cojson 0.16.5 → 0.16.6

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 (117) hide show
  1. package/.turbo/turbo-build.log +1 -1
  2. package/CHANGELOG.md +10 -0
  3. package/dist/GarbageCollector.d.ts +12 -0
  4. package/dist/GarbageCollector.d.ts.map +1 -0
  5. package/dist/GarbageCollector.js +37 -0
  6. package/dist/GarbageCollector.js.map +1 -0
  7. package/dist/coValue.d.ts +1 -1
  8. package/dist/coValueContentMessage.d.ts +1 -0
  9. package/dist/coValueContentMessage.d.ts.map +1 -1
  10. package/dist/coValueContentMessage.js +11 -3
  11. package/dist/coValueContentMessage.js.map +1 -1
  12. package/dist/coValueCore/coValueCore.d.ts +2 -1
  13. package/dist/coValueCore/coValueCore.d.ts.map +1 -1
  14. package/dist/coValueCore/coValueCore.js +15 -0
  15. package/dist/coValueCore/coValueCore.js.map +1 -1
  16. package/dist/coValueCore/utils.d.ts.map +1 -1
  17. package/dist/coValueCore/utils.js.map +1 -1
  18. package/dist/coValueCore/verifiedState.d.ts +1 -0
  19. package/dist/coValueCore/verifiedState.d.ts.map +1 -1
  20. package/dist/coValueCore/verifiedState.js.map +1 -1
  21. package/dist/coValues/coMap.d.ts +3 -3
  22. package/dist/coValues/coPlainText.d.ts +1 -0
  23. package/dist/coValues/coPlainText.d.ts.map +1 -1
  24. package/dist/coValues/coPlainText.js +27 -8
  25. package/dist/coValues/coPlainText.js.map +1 -1
  26. package/dist/coValues/coStream.d.ts +2 -2
  27. package/dist/coValues/group.d.ts +1 -1
  28. package/dist/config.d.ts +10 -1
  29. package/dist/config.d.ts.map +1 -1
  30. package/dist/config.js +16 -1
  31. package/dist/config.js.map +1 -1
  32. package/dist/exports.d.ts +11 -4
  33. package/dist/exports.d.ts.map +1 -1
  34. package/dist/exports.js +8 -3
  35. package/dist/exports.js.map +1 -1
  36. package/dist/localNode.d.ts +3 -0
  37. package/dist/localNode.d.ts.map +1 -1
  38. package/dist/localNode.js +11 -0
  39. package/dist/localNode.js.map +1 -1
  40. package/dist/queue/LocalTransactionsSyncQueue.js +1 -1
  41. package/dist/queue/LocalTransactionsSyncQueue.js.map +1 -1
  42. package/dist/queue/StoreQueue.d.ts +7 -1
  43. package/dist/queue/StoreQueue.d.ts.map +1 -1
  44. package/dist/queue/StoreQueue.js +35 -13
  45. package/dist/queue/StoreQueue.js.map +1 -1
  46. package/dist/storage/sqlite/client.d.ts +4 -4
  47. package/dist/storage/sqlite/client.d.ts.map +1 -1
  48. package/dist/storage/sqlite/client.js +13 -4
  49. package/dist/storage/sqlite/client.js.map +1 -1
  50. package/dist/storage/sqliteAsync/client.d.ts +3 -3
  51. package/dist/storage/sqliteAsync/client.d.ts.map +1 -1
  52. package/dist/storage/sqliteAsync/client.js +12 -3
  53. package/dist/storage/sqliteAsync/client.js.map +1 -1
  54. package/dist/storage/storageAsync.d.ts.map +1 -1
  55. package/dist/storage/storageAsync.js +2 -7
  56. package/dist/storage/storageAsync.js.map +1 -1
  57. package/dist/storage/storageSync.d.ts.map +1 -1
  58. package/dist/storage/storageSync.js +2 -7
  59. package/dist/storage/storageSync.js.map +1 -1
  60. package/dist/storage/types.d.ts +2 -2
  61. package/dist/storage/types.d.ts.map +1 -1
  62. package/dist/sync.d.ts.map +1 -1
  63. package/dist/sync.js +17 -3
  64. package/dist/sync.js.map +1 -1
  65. package/dist/tests/GarbageCollector.test.d.ts +2 -0
  66. package/dist/tests/GarbageCollector.test.d.ts.map +1 -0
  67. package/dist/tests/GarbageCollector.test.js +85 -0
  68. package/dist/tests/GarbageCollector.test.js.map +1 -0
  69. package/dist/tests/coPlainText.test.js +142 -4
  70. package/dist/tests/coPlainText.test.js.map +1 -1
  71. package/dist/tests/coStream.test.js +3 -3
  72. package/dist/tests/coStream.test.js.map +1 -1
  73. package/dist/tests/sync.garbageCollection.test.d.ts +2 -0
  74. package/dist/tests/sync.garbageCollection.test.d.ts.map +1 -0
  75. package/dist/tests/sync.garbageCollection.test.js +133 -0
  76. package/dist/tests/sync.garbageCollection.test.js.map +1 -0
  77. package/dist/tests/sync.mesh.test.js +48 -34
  78. package/dist/tests/sync.mesh.test.js.map +1 -1
  79. package/dist/tests/sync.storage.test.js +31 -21
  80. package/dist/tests/sync.storage.test.js.map +1 -1
  81. package/dist/tests/sync.storageAsync.test.js +76 -29
  82. package/dist/tests/sync.storageAsync.test.js.map +1 -1
  83. package/dist/tests/testStorage.d.ts +1 -0
  84. package/dist/tests/testStorage.d.ts.map +1 -1
  85. package/dist/tests/testStorage.js +1 -1
  86. package/dist/tests/testStorage.js.map +1 -1
  87. package/dist/tests/testUtils.d.ts +1 -0
  88. package/dist/tests/testUtils.d.ts.map +1 -1
  89. package/dist/tests/testUtils.js +1 -0
  90. package/dist/tests/testUtils.js.map +1 -1
  91. package/package.json +1 -1
  92. package/src/GarbageCollector.ts +48 -0
  93. package/src/coValueContentMessage.ts +16 -3
  94. package/src/coValueCore/coValueCore.ts +27 -10
  95. package/src/coValueCore/utils.ts +1 -0
  96. package/src/coValueCore/verifiedState.ts +1 -0
  97. package/src/coValues/coPlainText.ts +40 -8
  98. package/src/config.ts +20 -1
  99. package/src/exports.ts +13 -5
  100. package/src/localNode.ts +15 -1
  101. package/src/queue/LocalTransactionsSyncQueue.ts +1 -1
  102. package/src/queue/StoreQueue.ts +45 -12
  103. package/src/storage/sqlite/client.ts +24 -10
  104. package/src/storage/sqliteAsync/client.ts +26 -5
  105. package/src/storage/storageAsync.ts +5 -9
  106. package/src/storage/storageSync.ts +2 -9
  107. package/src/storage/types.ts +7 -4
  108. package/src/sync.ts +19 -3
  109. package/src/tests/GarbageCollector.test.ts +127 -0
  110. package/src/tests/coPlainText.test.ts +176 -4
  111. package/src/tests/coStream.test.ts +7 -3
  112. package/src/tests/sync.garbageCollection.test.ts +178 -0
  113. package/src/tests/sync.mesh.test.ts +49 -34
  114. package/src/tests/sync.storage.test.ts +31 -21
  115. package/src/tests/sync.storageAsync.test.ts +81 -29
  116. package/src/tests/testStorage.ts +11 -3
  117. package/src/tests/testUtils.ts +4 -1
@@ -1,10 +1,21 @@
1
- import { afterEach, expect, test, vi } from "vitest";
1
+ import { afterEach, beforeEach, expect, test, vi } from "vitest";
2
2
  import { expectPlainText } from "../coValue.js";
3
+ import { setMaxRecommendedTxSize } from "../config.js";
3
4
  import { WasmCrypto } from "../crypto/WasmCrypto.js";
4
- import { nodeWithRandomAgentAndSessionID } from "./testUtils.js";
5
+ import {
6
+ SyncMessagesLog,
7
+ loadCoValueOrFail,
8
+ nodeWithRandomAgentAndSessionID,
9
+ setupTestNode,
10
+ } from "./testUtils.js";
5
11
 
6
12
  const Crypto = await WasmCrypto.create();
7
13
 
14
+ beforeEach(() => {
15
+ setMaxRecommendedTxSize(100 * 1024);
16
+ SyncMessagesLog.clear();
17
+ });
18
+
8
19
  afterEach(() => void vi.unstubAllGlobals());
9
20
 
10
21
  test("Empty CoPlainText works", () => {
@@ -185,8 +196,8 @@ test("insertBefore and insertAfter work as expected", () => {
185
196
  expect(content.toString()).toEqual("hey");
186
197
 
187
198
  // Insert '!' at start
188
- content.insertBefore(0, "!", "trusting"); // "!hey"
189
- expect(content.toString()).toEqual("!hey");
199
+ content.insertBefore(0, "!?", "trusting"); // "!?hey"
200
+ expect(content.toString()).toEqual("!?hey");
190
201
  });
191
202
 
192
203
  test("Can delete a single grapheme", () => {
@@ -286,3 +297,164 @@ test("Splits into and from grapheme string arrays", () => {
286
297
  const text = content.fromGraphemes(graphemes);
287
298
  expect(text).toEqual("👋 안녕!");
288
299
  });
300
+
301
+ test("chunks transactions when when the chars are longer than MAX_RECOMMENDED_TX_SIZE", async () => {
302
+ setMaxRecommendedTxSize(5);
303
+
304
+ const client = setupTestNode();
305
+ const { storage } = client.addStorage();
306
+
307
+ const coValue = client.node.createCoValue({
308
+ type: "coplaintext",
309
+ ruleset: { type: "unsafeAllowAll" },
310
+ meta: null,
311
+ ...Crypto.createdNowUnique(),
312
+ });
313
+
314
+ const content = expectPlainText(coValue.getCurrentContent());
315
+
316
+ content.insertAfter(
317
+ content.entries().length,
318
+ "I'm writing you to test that coplaintext",
319
+ "trusting",
320
+ );
321
+ content.insertAfter(
322
+ content.entries().length,
323
+ " chunks transactions when when the chars are longer than MAX_RECOMMENDED_TX_SIZE.",
324
+ "trusting",
325
+ );
326
+ content.insertAfter(
327
+ content.entries().length,
328
+ "This is required because when a user paste 1Mb of text, we can split it in multiple websocket messages.",
329
+ "trusting",
330
+ );
331
+
332
+ content.insertBefore(0, "Dear reader,\n", "trusting");
333
+
334
+ expect(content.toString()).toMatchInlineSnapshot(
335
+ `"Dear reader,\nI'm writing you to test that coplaintext chunks transactions when when the chars are longer than MAX_RECOMMENDED_TX_SIZE.This is required because when a user paste 1Mb of text, we can split it in multiple websocket messages."`,
336
+ );
337
+
338
+ await coValue.waitForSync();
339
+
340
+ client.restart();
341
+ client.addStorage({
342
+ storage,
343
+ });
344
+
345
+ const loaded = await loadCoValueOrFail(client.node, content.id);
346
+ await loaded.core.waitForSync();
347
+
348
+ expect(loaded.toString()).toEqual(content.toString());
349
+
350
+ expect(
351
+ SyncMessagesLog.getMessages({
352
+ CoPlainText: coValue,
353
+ }),
354
+ ).toMatchInlineSnapshot(`
355
+ [
356
+ "client -> storage | CONTENT CoPlainText header: true new: ",
357
+ "client -> storage | CONTENT CoPlainText header: false new: After: 0 New: 1",
358
+ "client -> storage | CONTENT CoPlainText header: false new: After: 1 New: 1",
359
+ "client -> storage | CONTENT CoPlainText header: false new: After: 2 New: 1",
360
+ "client -> storage | CONTENT CoPlainText header: false new: After: 3 New: 1",
361
+ "client -> storage | CONTENT CoPlainText header: false new: After: 4 New: 1",
362
+ "client -> storage | CONTENT CoPlainText header: false new: After: 5 New: 1",
363
+ "client -> storage | CONTENT CoPlainText header: false new: After: 6 New: 1",
364
+ "client -> storage | CONTENT CoPlainText header: false new: After: 7 New: 1",
365
+ "client -> storage | CONTENT CoPlainText header: false new: After: 8 New: 1",
366
+ "client -> storage | CONTENT CoPlainText header: false new: After: 9 New: 1",
367
+ "client -> storage | CONTENT CoPlainText header: false new: After: 10 New: 1",
368
+ "client -> storage | CONTENT CoPlainText header: false new: After: 11 New: 1",
369
+ "client -> storage | CONTENT CoPlainText header: false new: After: 12 New: 1",
370
+ "client -> storage | CONTENT CoPlainText header: false new: After: 13 New: 1",
371
+ "client -> storage | CONTENT CoPlainText header: false new: After: 14 New: 1",
372
+ "client -> storage | CONTENT CoPlainText header: false new: After: 15 New: 1",
373
+ "client -> storage | CONTENT CoPlainText header: false new: After: 16 New: 1",
374
+ "client -> storage | CONTENT CoPlainText header: false new: After: 17 New: 1",
375
+ "client -> storage | CONTENT CoPlainText header: false new: After: 18 New: 1",
376
+ "client -> storage | CONTENT CoPlainText header: false new: After: 19 New: 1",
377
+ "client -> storage | CONTENT CoPlainText header: false new: After: 20 New: 1",
378
+ "client -> storage | CONTENT CoPlainText header: false new: After: 21 New: 1",
379
+ "client -> storage | CONTENT CoPlainText header: false new: After: 22 New: 1",
380
+ "client -> storage | CONTENT CoPlainText header: false new: After: 23 New: 1",
381
+ "client -> storage | CONTENT CoPlainText header: false new: After: 24 New: 1",
382
+ "client -> storage | CONTENT CoPlainText header: false new: After: 25 New: 1",
383
+ "client -> storage | CONTENT CoPlainText header: false new: After: 26 New: 1",
384
+ "client -> storage | CONTENT CoPlainText header: false new: After: 27 New: 1",
385
+ "client -> storage | CONTENT CoPlainText header: false new: After: 28 New: 1",
386
+ "client -> storage | CONTENT CoPlainText header: false new: After: 29 New: 1",
387
+ "client -> storage | CONTENT CoPlainText header: false new: After: 30 New: 1",
388
+ "client -> storage | CONTENT CoPlainText header: false new: After: 31 New: 1",
389
+ "client -> storage | CONTENT CoPlainText header: false new: After: 32 New: 1",
390
+ "client -> storage | CONTENT CoPlainText header: false new: After: 33 New: 1",
391
+ "client -> storage | CONTENT CoPlainText header: false new: After: 34 New: 1",
392
+ "client -> storage | CONTENT CoPlainText header: false new: After: 35 New: 1",
393
+ "client -> storage | CONTENT CoPlainText header: false new: After: 36 New: 1",
394
+ "client -> storage | CONTENT CoPlainText header: false new: After: 37 New: 1",
395
+ "client -> storage | CONTENT CoPlainText header: false new: After: 38 New: 1",
396
+ "client -> storage | CONTENT CoPlainText header: false new: After: 39 New: 1",
397
+ "client -> storage | CONTENT CoPlainText header: false new: After: 40 New: 1",
398
+ "client -> storage | CONTENT CoPlainText header: false new: After: 41 New: 1",
399
+ "client -> storage | CONTENT CoPlainText header: false new: After: 42 New: 1",
400
+ "client -> storage | CONTENT CoPlainText header: false new: After: 43 New: 1",
401
+ "client -> storage | CONTENT CoPlainText header: false new: After: 44 New: 1",
402
+ "client -> storage | CONTENT CoPlainText header: false new: After: 45 New: 1",
403
+ "client -> storage | CONTENT CoPlainText header: false new: After: 46 New: 1",
404
+ "client -> storage | CONTENT CoPlainText header: false new: After: 47 New: 1",
405
+ "client -> storage | CONTENT CoPlainText header: false new: After: 48 New: 1",
406
+ "client -> storage | CONTENT CoPlainText header: false new: After: 49 New: 1",
407
+ "client -> storage | LOAD CoPlainText sessions: empty",
408
+ "storage -> client | CONTENT CoPlainText header: true new: After: 0 New: 1 expectContentUntil: header/50",
409
+ "storage -> client | CONTENT CoPlainText header: true new: After: 1 New: 1",
410
+ "storage -> client | CONTENT CoPlainText header: true new: After: 2 New: 1",
411
+ "storage -> client | CONTENT CoPlainText header: true new: After: 3 New: 1",
412
+ "storage -> client | CONTENT CoPlainText header: true new: After: 4 New: 1",
413
+ "storage -> client | CONTENT CoPlainText header: true new: After: 5 New: 1",
414
+ "storage -> client | CONTENT CoPlainText header: true new: After: 6 New: 1",
415
+ "storage -> client | CONTENT CoPlainText header: true new: After: 7 New: 1",
416
+ "storage -> client | CONTENT CoPlainText header: true new: After: 8 New: 1",
417
+ "storage -> client | CONTENT CoPlainText header: true new: After: 9 New: 1",
418
+ "storage -> client | CONTENT CoPlainText header: true new: After: 10 New: 1",
419
+ "storage -> client | CONTENT CoPlainText header: true new: After: 11 New: 1",
420
+ "storage -> client | CONTENT CoPlainText header: true new: After: 12 New: 1",
421
+ "storage -> client | CONTENT CoPlainText header: true new: After: 13 New: 1",
422
+ "storage -> client | CONTENT CoPlainText header: true new: After: 14 New: 1",
423
+ "storage -> client | CONTENT CoPlainText header: true new: After: 15 New: 1",
424
+ "storage -> client | CONTENT CoPlainText header: true new: After: 16 New: 1",
425
+ "storage -> client | CONTENT CoPlainText header: true new: After: 17 New: 1",
426
+ "storage -> client | CONTENT CoPlainText header: true new: After: 18 New: 1",
427
+ "storage -> client | CONTENT CoPlainText header: true new: After: 19 New: 1",
428
+ "storage -> client | CONTENT CoPlainText header: true new: After: 20 New: 1",
429
+ "storage -> client | CONTENT CoPlainText header: true new: After: 21 New: 1",
430
+ "storage -> client | CONTENT CoPlainText header: true new: After: 22 New: 1",
431
+ "storage -> client | CONTENT CoPlainText header: true new: After: 23 New: 1",
432
+ "storage -> client | CONTENT CoPlainText header: true new: After: 24 New: 1",
433
+ "storage -> client | CONTENT CoPlainText header: true new: After: 25 New: 1",
434
+ "storage -> client | CONTENT CoPlainText header: true new: After: 26 New: 1",
435
+ "storage -> client | CONTENT CoPlainText header: true new: After: 27 New: 1",
436
+ "storage -> client | CONTENT CoPlainText header: true new: After: 28 New: 1",
437
+ "storage -> client | CONTENT CoPlainText header: true new: After: 29 New: 1",
438
+ "storage -> client | CONTENT CoPlainText header: true new: After: 30 New: 1",
439
+ "storage -> client | CONTENT CoPlainText header: true new: After: 31 New: 1",
440
+ "storage -> client | CONTENT CoPlainText header: true new: After: 32 New: 1",
441
+ "storage -> client | CONTENT CoPlainText header: true new: After: 33 New: 1",
442
+ "storage -> client | CONTENT CoPlainText header: true new: After: 34 New: 1",
443
+ "storage -> client | CONTENT CoPlainText header: true new: After: 35 New: 1",
444
+ "storage -> client | CONTENT CoPlainText header: true new: After: 36 New: 1",
445
+ "storage -> client | CONTENT CoPlainText header: true new: After: 37 New: 1",
446
+ "storage -> client | CONTENT CoPlainText header: true new: After: 38 New: 1",
447
+ "storage -> client | CONTENT CoPlainText header: true new: After: 39 New: 1",
448
+ "storage -> client | CONTENT CoPlainText header: true new: After: 40 New: 1",
449
+ "storage -> client | CONTENT CoPlainText header: true new: After: 41 New: 1",
450
+ "storage -> client | CONTENT CoPlainText header: true new: After: 42 New: 1",
451
+ "storage -> client | CONTENT CoPlainText header: true new: After: 43 New: 1",
452
+ "storage -> client | CONTENT CoPlainText header: true new: After: 44 New: 1",
453
+ "storage -> client | CONTENT CoPlainText header: true new: After: 45 New: 1",
454
+ "storage -> client | CONTENT CoPlainText header: true new: After: 46 New: 1",
455
+ "storage -> client | CONTENT CoPlainText header: true new: After: 47 New: 1",
456
+ "storage -> client | CONTENT CoPlainText header: true new: After: 48 New: 1",
457
+ "storage -> client | CONTENT CoPlainText header: true new: After: 49 New: 1",
458
+ ]
459
+ `);
460
+ });
@@ -6,7 +6,7 @@ import {
6
6
  RawBinaryCoStream,
7
7
  RawCoStreamView,
8
8
  } from "../coValues/coStream.js";
9
- import { MAX_RECOMMENDED_TX_SIZE } from "../config.js";
9
+ import { TRANSACTION_CONFIG } from "../config.js";
10
10
  import { WasmCrypto } from "../crypto/WasmCrypto.js";
11
11
  import { SessionID } from "../ids.js";
12
12
  import {
@@ -153,7 +153,9 @@ test("When adding large transactions (small fraction of MAX_RECOMMENDED_TX_SIZE)
153
153
  );
154
154
 
155
155
  for (let i = 0; i < 10; i++) {
156
- const chunk = new Uint8Array(MAX_RECOMMENDED_TX_SIZE / 3 + 100);
156
+ const chunk = new Uint8Array(
157
+ TRANSACTION_CONFIG.MAX_RECOMMENDED_TX_SIZE / 3 + 100,
158
+ );
157
159
 
158
160
  content.pushBinaryStreamChunk(chunk, "trusting");
159
161
  }
@@ -226,7 +228,9 @@ test("When adding large transactions (bigger than MAX_RECOMMENDED_TX_SIZE), we s
226
228
  "trusting",
227
229
  );
228
230
 
229
- const chunk = new Uint8Array(MAX_RECOMMENDED_TX_SIZE + 100);
231
+ const chunk = new Uint8Array(
232
+ TRANSACTION_CONFIG.MAX_RECOMMENDED_TX_SIZE + 100,
233
+ );
230
234
 
231
235
  for (let i = 0; i < 3; i++) {
232
236
  content.pushBinaryStreamChunk(chunk, "trusting");
@@ -0,0 +1,178 @@
1
+ import { assert, beforeEach, describe, expect, test, vi } from "vitest";
2
+
3
+ import { setGarbageCollectorMaxAge } from "../config";
4
+ import { emptyKnownState } from "../exports";
5
+ import {
6
+ SyncMessagesLog,
7
+ TEST_NODE_CONFIG,
8
+ loadCoValueOrFail,
9
+ setupTestNode,
10
+ waitFor,
11
+ } from "./testUtils";
12
+
13
+ // We want to simulate a real world communication that happens asynchronously
14
+ TEST_NODE_CONFIG.withAsyncPeers = true;
15
+
16
+ beforeEach(() => {
17
+ // We want to test what happens when the garbage collector kicks in and removes a coValue
18
+ // We set the max age to -1 to make it remove everything
19
+ setGarbageCollectorMaxAge(-1);
20
+ });
21
+
22
+ describe("sync after the garbage collector has run", () => {
23
+ let jazzCloud: ReturnType<typeof setupTestNode>;
24
+
25
+ beforeEach(async () => {
26
+ SyncMessagesLog.clear();
27
+ jazzCloud = setupTestNode({
28
+ isSyncServer: true,
29
+ });
30
+ jazzCloud.addStorage({
31
+ ourName: "server",
32
+ });
33
+ jazzCloud.node.enableGarbageCollector();
34
+ });
35
+
36
+ test("loading a coValue from the sync server that was removed by the garbage collector", async () => {
37
+ const client = setupTestNode();
38
+
39
+ client.connectToSyncServer();
40
+
41
+ const group = jazzCloud.node.createGroup();
42
+ const map = group.createMap();
43
+ map.set("hello", "world", "trusting");
44
+
45
+ await map.core.waitForSync();
46
+
47
+ // force the garbage collector to run
48
+ jazzCloud.node.garbageCollector?.collect();
49
+
50
+ SyncMessagesLog.clear();
51
+
52
+ const mapOnClient = await loadCoValueOrFail(client.node, map.id);
53
+ expect(mapOnClient.get("hello")).toEqual("world");
54
+
55
+ expect(
56
+ SyncMessagesLog.getMessages({
57
+ Group: group.core,
58
+ Map: map.core,
59
+ }),
60
+ ).toMatchInlineSnapshot(`
61
+ [
62
+ "client -> server | LOAD Map sessions: empty",
63
+ "server -> storage | LOAD Map sessions: empty",
64
+ "storage -> server | CONTENT Group header: true new: After: 0 New: 3",
65
+ "storage -> server | CONTENT Map header: true new: After: 0 New: 1",
66
+ "server -> client | CONTENT Group header: true new: After: 0 New: 3",
67
+ "server -> client | CONTENT Map header: true new: After: 0 New: 1",
68
+ "client -> server | KNOWN Group sessions: header/3",
69
+ "client -> server | KNOWN Map sessions: header/1",
70
+ ]
71
+ `);
72
+ });
73
+
74
+ test("updating a coValue that was removed by the garbage collector", async () => {
75
+ const client = setupTestNode();
76
+
77
+ client.connectToSyncServer();
78
+
79
+ const group = jazzCloud.node.createGroup();
80
+ group.addMember("everyone", "writer");
81
+ const map = group.createMap();
82
+ map.set("hello", "world", "trusting");
83
+
84
+ const mapOnClient = await loadCoValueOrFail(client.node, map.id);
85
+ expect(mapOnClient.get("hello")).toEqual("world");
86
+
87
+ // force the garbage collector to run
88
+ jazzCloud.node.garbageCollector?.collect();
89
+ SyncMessagesLog.clear();
90
+
91
+ mapOnClient.set("hello", "updated", "trusting");
92
+
93
+ await mapOnClient.core.waitForSync();
94
+
95
+ const mapOnServer = await loadCoValueOrFail(jazzCloud.node, map.id);
96
+
97
+ expect(mapOnServer.get("hello")).toEqual("updated");
98
+
99
+ expect(
100
+ SyncMessagesLog.getMessages({
101
+ Group: group.core,
102
+ Map: map.core,
103
+ }),
104
+ ).toMatchInlineSnapshot(`
105
+ [
106
+ "client -> server | CONTENT Map header: false new: After: 0 New: 1",
107
+ "server -> storage | LOAD Map sessions: empty",
108
+ "storage -> server | CONTENT Group header: true new: After: 0 New: 5",
109
+ "storage -> server | CONTENT Map header: true new: After: 0 New: 1",
110
+ "server -> client | KNOWN Map sessions: header/2",
111
+ "server -> storage | CONTENT Map header: false new: After: 0 New: 1",
112
+ ]
113
+ `);
114
+ });
115
+
116
+ test("syncing a coValue that was removed by the garbage collector", async () => {
117
+ const edge = setupTestNode();
118
+ edge.addStorage({
119
+ ourName: "edge",
120
+ });
121
+ edge.connectToSyncServer({
122
+ syncServer: jazzCloud.node,
123
+ syncServerName: "server",
124
+ ourName: "edge",
125
+ });
126
+ edge.node.enableGarbageCollector();
127
+ const client = setupTestNode();
128
+
129
+ client.connectToSyncServer({
130
+ syncServer: edge.node,
131
+ syncServerName: "edge",
132
+ });
133
+
134
+ const group = edge.node.createGroup();
135
+ group.addMember("everyone", "writer");
136
+
137
+ await group.core.waitForSync();
138
+
139
+ const map = group.createMap();
140
+
141
+ map.set("hello", "updated", "trusting");
142
+
143
+ // force the garbage collector to run before the transaction is synced
144
+ edge.node.garbageCollector?.collect();
145
+ expect(edge.node.getCoValue(map.id).isAvailable()).toBe(false);
146
+
147
+ SyncMessagesLog.clear();
148
+
149
+ // The storage should work even after the coValue is unmounted, so the load should be successful
150
+ const mapOnClient = await loadCoValueOrFail(client.node, map.id);
151
+ expect(mapOnClient.get("hello")).toEqual("updated");
152
+
153
+ expect(
154
+ SyncMessagesLog.getMessages({
155
+ Group: group.core,
156
+ Map: map.core,
157
+ }),
158
+ ).toMatchInlineSnapshot(`
159
+ [
160
+ "client -> edge | LOAD Map sessions: empty",
161
+ "edge -> storage | CONTENT Map header: true new: After: 0 New: 1",
162
+ "edge -> server | CONTENT Map header: true new: After: 0 New: 1",
163
+ "edge -> storage | LOAD Map sessions: empty",
164
+ "storage -> edge | CONTENT Group header: true new: After: 0 New: 5",
165
+ "storage -> edge | CONTENT Map header: true new: After: 0 New: 1",
166
+ "edge -> server | CONTENT Map header: true new: ",
167
+ "edge -> client | CONTENT Group header: true new: After: 0 New: 5",
168
+ "edge -> client | CONTENT Map header: true new: After: 0 New: 1",
169
+ "server -> edge | KNOWN Map sessions: header/1",
170
+ "server -> storage | CONTENT Map header: true new: After: 0 New: 1",
171
+ "server -> edge | KNOWN Map sessions: header/1",
172
+ "server -> storage | CONTENT Map header: true new: ",
173
+ "client -> edge | KNOWN Group sessions: header/5",
174
+ "client -> edge | KNOWN Map sessions: header/1",
175
+ ]
176
+ `);
177
+ });
178
+ });
@@ -1,6 +1,7 @@
1
1
  import { beforeEach, describe, expect, test, vi } from "vitest";
2
2
 
3
3
  import { expectMap } from "../coValue";
4
+ import { setMaxRecommendedTxSize } from "../config";
4
5
  import {
5
6
  SyncMessagesLog,
6
7
  TEST_NODE_CONFIG,
@@ -14,6 +15,10 @@ import {
14
15
  // We want to simulate a real world communication that happens asynchronously
15
16
  TEST_NODE_CONFIG.withAsyncPeers = true;
16
17
 
18
+ beforeEach(() => {
19
+ setMaxRecommendedTxSize(100 * 1024);
20
+ });
21
+
17
22
  function setupMesh() {
18
23
  const coreServer = setupTestNode();
19
24
 
@@ -254,6 +259,9 @@ describe("multiple clients syncing with the a cloud-like server mesh", () => {
254
259
 
255
260
  // Forcefully delete the coValue from the edge (simulating some data loss)
256
261
  mesh.edgeItaly.node.internalDeleteCoValue(map.id);
262
+ mesh.edgeItaly.addStorage({
263
+ ourName: "edge-italy",
264
+ });
257
265
 
258
266
  mapOnClient.set("fromClient", "updated", "trusting");
259
267
  mapOnCoreServer.set("fromServer", "updated", "trusting");
@@ -483,6 +491,7 @@ describe("multiple clients syncing with the a cloud-like server mesh", () => {
483
491
  });
484
492
 
485
493
  test("large coValue streaming from an edge to the core server and a client at the same time", async () => {
494
+ setMaxRecommendedTxSize(1000);
486
495
  const edge = setupTestNode();
487
496
 
488
497
  const { storage } = edge.addStorage({
@@ -494,12 +503,9 @@ describe("multiple clients syncing with the a cloud-like server mesh", () => {
494
503
 
495
504
  const largeMap = group.createMap();
496
505
 
497
- // Generate a large amount of data (about 100MB)
498
- const dataSize = 1 * 200 * 1024;
499
- const chunkSize = 1024; // 1KB chunks
500
- const chunks = dataSize / chunkSize;
506
+ const chunks = 100;
501
507
 
502
- const value = Buffer.alloc(chunkSize, `value$`).toString("base64");
508
+ const value = "1".repeat(10);
503
509
 
504
510
  for (let i = 0; i < chunks; i++) {
505
511
  const key = `key${i}`;
@@ -516,9 +522,11 @@ describe("multiple clients syncing with the a cloud-like server mesh", () => {
516
522
  ).toMatchInlineSnapshot(`
517
523
  [
518
524
  "edge -> storage | CONTENT Group header: true new: After: 0 New: 5",
519
- "edge -> storage | CONTENT Map header: true new: After: 0 New: 73",
520
- "edge -> storage | CONTENT Map header: false new: After: 73 New: 73",
521
- "edge -> storage | CONTENT Map header: false new: After: 146 New: 54",
525
+ "edge -> storage | CONTENT Map header: true new: After: 0 New: 20",
526
+ "edge -> storage | CONTENT Map header: false new: After: 20 New: 21",
527
+ "edge -> storage | CONTENT Map header: false new: After: 41 New: 21",
528
+ "edge -> storage | CONTENT Map header: false new: After: 62 New: 21",
529
+ "edge -> storage | CONTENT Map header: false new: After: 83 New: 17",
522
530
  ]
523
531
  `);
524
532
 
@@ -565,11 +573,11 @@ describe("multiple clients syncing with the a cloud-like server mesh", () => {
565
573
  "edge -> storage | LOAD Map sessions: empty",
566
574
  "storage -> edge | CONTENT Group header: true new: After: 0 New: 5",
567
575
  "edge -> core | LOAD Group sessions: header/5",
568
- "storage -> edge | CONTENT Map header: true new: After: 0 New: 73 expectContentUntil: header/200",
569
- "edge -> core | LOAD Map sessions: header/200",
576
+ "storage -> edge | CONTENT Map header: true new: After: 0 New: 41 expectContentUntil: header/100",
577
+ "edge -> core | LOAD Map sessions: header/100",
570
578
  "edge -> client | CONTENT Group header: true new: After: 0 New: 5",
571
- "edge -> client | CONTENT Map header: true new: expectContentUntil: header/200",
572
- "edge -> client | CONTENT Map header: false new: After: 0 New: 73",
579
+ "edge -> client | CONTENT Map header: true new: expectContentUntil: header/100",
580
+ "edge -> client | CONTENT Map header: false new: After: 0 New: 41",
573
581
  "core -> storage | LOAD Group sessions: empty",
574
582
  "storage -> core | KNOWN Group sessions: empty",
575
583
  "core -> edge | KNOWN Group sessions: empty",
@@ -579,32 +587,39 @@ describe("multiple clients syncing with the a cloud-like server mesh", () => {
579
587
  "client -> edge | KNOWN Group sessions: header/5",
580
588
  "client -> storage | CONTENT Group header: true new: After: 0 New: 5",
581
589
  "client -> edge | KNOWN Map sessions: header/0",
582
- "client -> storage | CONTENT Map header: true new: expectContentUntil: header/200",
583
- "client -> edge | KNOWN Map sessions: header/73",
584
- "client -> storage | CONTENT Map header: false new: After: 0 New: 73",
585
- "storage -> edge | CONTENT Map header: true new: After: 73 New: 73",
586
- "edge -> client | CONTENT Map header: false new: After: 73 New: 73",
590
+ "client -> storage | CONTENT Map header: true new: expectContentUntil: header/100",
591
+ "client -> edge | KNOWN Map sessions: header/41",
592
+ "client -> storage | CONTENT Map header: false new: After: 0 New: 41",
593
+ "storage -> edge | CONTENT Map header: true new: After: 41 New: 21",
594
+ "edge -> client | CONTENT Map header: false new: After: 41 New: 21",
587
595
  "edge -> core | CONTENT Group header: true new: After: 0 New: 5",
588
- "edge -> core | CONTENT Map header: true new: expectContentUntil: header/200",
589
- "edge -> core | CONTENT Map header: false new: After: 0 New: 73",
590
- "edge -> core | CONTENT Map header: false new: After: 73 New: 73",
591
- "client -> edge | KNOWN Map sessions: header/146",
592
- "client -> storage | CONTENT Map header: false new: After: 73 New: 73",
593
- "storage -> edge | CONTENT Map header: true new: After: 146 New: 54",
594
- "edge -> core | CONTENT Map header: false new: After: 146 New: 54",
595
- "edge -> client | CONTENT Map header: false new: After: 146 New: 54",
596
+ "edge -> core | CONTENT Map header: true new: expectContentUntil: header/100",
597
+ "edge -> core | CONTENT Map header: false new: After: 0 New: 41",
598
+ "edge -> core | CONTENT Map header: false new: After: 41 New: 21",
599
+ "client -> edge | KNOWN Map sessions: header/62",
600
+ "client -> storage | CONTENT Map header: false new: After: 41 New: 21",
601
+ "storage -> edge | CONTENT Map header: true new: After: 62 New: 21",
602
+ "edge -> core | CONTENT Map header: false new: After: 62 New: 21",
603
+ "edge -> client | CONTENT Map header: false new: After: 62 New: 21",
596
604
  "core -> edge | KNOWN Group sessions: header/5",
597
605
  "core -> storage | CONTENT Group header: true new: After: 0 New: 5",
598
606
  "core -> edge | KNOWN Map sessions: header/0",
599
- "core -> storage | CONTENT Map header: true new: expectContentUntil: header/200",
600
- "core -> edge | KNOWN Map sessions: header/73",
601
- "core -> storage | CONTENT Map header: false new: After: 0 New: 73",
602
- "core -> edge | KNOWN Map sessions: header/146",
603
- "core -> storage | CONTENT Map header: false new: After: 73 New: 73",
604
- "core -> edge | KNOWN Map sessions: header/200",
605
- "core -> storage | CONTENT Map header: false new: After: 146 New: 54",
606
- "client -> edge | KNOWN Map sessions: header/200",
607
- "client -> storage | CONTENT Map header: false new: After: 146 New: 54",
607
+ "core -> storage | CONTENT Map header: true new: expectContentUntil: header/100",
608
+ "core -> edge | KNOWN Map sessions: header/41",
609
+ "core -> storage | CONTENT Map header: false new: After: 0 New: 41",
610
+ "core -> edge | KNOWN Map sessions: header/62",
611
+ "core -> storage | CONTENT Map header: false new: After: 41 New: 21",
612
+ "core -> edge | KNOWN Map sessions: header/83",
613
+ "core -> storage | CONTENT Map header: false new: After: 62 New: 21",
614
+ "client -> edge | KNOWN Map sessions: header/83",
615
+ "client -> storage | CONTENT Map header: false new: After: 62 New: 21",
616
+ "storage -> edge | CONTENT Map header: true new: After: 83 New: 17",
617
+ "edge -> core | CONTENT Map header: false new: After: 83 New: 17",
618
+ "edge -> client | CONTENT Map header: false new: After: 83 New: 17",
619
+ "core -> edge | KNOWN Map sessions: header/100",
620
+ "core -> storage | CONTENT Map header: false new: After: 83 New: 17",
621
+ "client -> edge | KNOWN Map sessions: header/100",
622
+ "client -> storage | CONTENT Map header: false new: After: 83 New: 17",
608
623
  ]
609
624
  `);
610
625
 
@@ -8,6 +8,7 @@ import {
8
8
  vi,
9
9
  } from "vitest";
10
10
 
11
+ import { setMaxRecommendedTxSize } from "../config";
11
12
  import { emptyKnownState } from "../exports";
12
13
  import {
13
14
  SyncMessagesLog,
@@ -27,6 +28,7 @@ describe("client with storage syncs with server", () => {
27
28
 
28
29
  beforeEach(async () => {
29
30
  SyncMessagesLog.clear();
31
+ setMaxRecommendedTxSize(100 * 1024);
30
32
  jazzCloud = setupTestNode({
31
33
  isSyncServer: true,
32
34
  });
@@ -244,6 +246,7 @@ describe("client syncs with a server with storage", () => {
244
246
  });
245
247
 
246
248
  test("loading a large coValue from storage", async () => {
249
+ setMaxRecommendedTxSize(1000);
247
250
  const client = setupTestNode();
248
251
 
249
252
  client.connectToSyncServer({
@@ -260,11 +263,9 @@ describe("client syncs with a server with storage", () => {
260
263
  const largeMap = group.createMap();
261
264
 
262
265
  // Generate a large amount of data (about 100MB)
263
- const dataSize = 1 * 200 * 1024;
264
- const chunkSize = 1024; // 1KB chunks
265
- const chunks = dataSize / chunkSize;
266
+ const chunks = 100;
266
267
 
267
- const value = Buffer.alloc(chunkSize, `value$`).toString("base64");
268
+ const value = "1".repeat(10);
268
269
 
269
270
  for (let i = 0; i < chunks; i++) {
270
271
  const key = `key${i}`;
@@ -289,20 +290,28 @@ describe("client syncs with a server with storage", () => {
289
290
  [
290
291
  "client -> storage | CONTENT Group header: true new: After: 0 New: 5",
291
292
  "client -> server | CONTENT Group header: true new: After: 0 New: 5",
292
- "client -> storage | CONTENT Map header: true new: After: 0 New: 73",
293
- "client -> server | CONTENT Map header: true new: After: 0 New: 73",
294
- "client -> storage | CONTENT Map header: false new: After: 73 New: 73",
295
- "client -> server | CONTENT Map header: false new: After: 73 New: 73",
296
- "client -> storage | CONTENT Map header: false new: After: 146 New: 54",
297
- "client -> server | CONTENT Map header: false new: After: 146 New: 54",
293
+ "client -> storage | CONTENT Map header: true new: After: 0 New: 20",
294
+ "client -> server | CONTENT Map header: true new: After: 0 New: 20",
295
+ "client -> storage | CONTENT Map header: false new: After: 20 New: 21",
296
+ "client -> server | CONTENT Map header: false new: After: 20 New: 21",
297
+ "client -> storage | CONTENT Map header: false new: After: 41 New: 21",
298
+ "client -> server | CONTENT Map header: false new: After: 41 New: 21",
299
+ "client -> storage | CONTENT Map header: false new: After: 62 New: 21",
300
+ "client -> server | CONTENT Map header: false new: After: 62 New: 21",
301
+ "client -> storage | CONTENT Map header: false new: After: 83 New: 17",
302
+ "client -> server | CONTENT Map header: false new: After: 83 New: 17",
298
303
  "server -> client | KNOWN Group sessions: header/5",
299
304
  "server -> storage | CONTENT Group header: true new: After: 0 New: 5",
300
- "server -> client | KNOWN Map sessions: header/73",
301
- "server -> storage | CONTENT Map header: true new: After: 0 New: 73",
302
- "server -> client | KNOWN Map sessions: header/146",
303
- "server -> storage | CONTENT Map header: false new: After: 73 New: 73",
304
- "server -> client | KNOWN Map sessions: header/200",
305
- "server -> storage | CONTENT Map header: false new: After: 146 New: 54",
305
+ "server -> client | KNOWN Map sessions: header/20",
306
+ "server -> storage | CONTENT Map header: true new: After: 0 New: 20",
307
+ "server -> client | KNOWN Map sessions: header/41",
308
+ "server -> storage | CONTENT Map header: false new: After: 20 New: 21",
309
+ "server -> client | KNOWN Map sessions: header/62",
310
+ "server -> storage | CONTENT Map header: false new: After: 41 New: 21",
311
+ "server -> client | KNOWN Map sessions: header/83",
312
+ "server -> storage | CONTENT Map header: false new: After: 62 New: 21",
313
+ "server -> client | KNOWN Map sessions: header/100",
314
+ "server -> storage | CONTENT Map header: false new: After: 83 New: 17",
306
315
  ]
307
316
  `);
308
317
 
@@ -355,12 +364,13 @@ describe("client syncs with a server with storage", () => {
355
364
  "client -> storage | LOAD Map sessions: empty",
356
365
  "storage -> client | CONTENT Group header: true new: After: 0 New: 5",
357
366
  "client -> server | LOAD Group sessions: header/5",
358
- "storage -> client | CONTENT Map header: true new: After: 0 New: 73 expectContentUntil: header/200",
359
- "client -> server | LOAD Map sessions: header/200",
367
+ "storage -> client | CONTENT Map header: true new: After: 0 New: 41 expectContentUntil: header/100",
368
+ "client -> server | LOAD Map sessions: header/100",
360
369
  "server -> client | KNOWN Group sessions: header/5",
361
- "server -> client | KNOWN Map sessions: header/200",
362
- "storage -> client | CONTENT Map header: true new: After: 73 New: 73",
363
- "storage -> client | CONTENT Map header: true new: After: 146 New: 54",
370
+ "server -> client | KNOWN Map sessions: header/100",
371
+ "storage -> client | CONTENT Map header: true new: After: 41 New: 21",
372
+ "storage -> client | CONTENT Map header: true new: After: 62 New: 21",
373
+ "storage -> client | CONTENT Map header: true new: After: 83 New: 17",
364
374
  ]
365
375
  `);
366
376
  });