cojson 0.18.37 → 0.18.38

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 (65) hide show
  1. package/.turbo/turbo-build.log +1 -1
  2. package/CHANGELOG.md +9 -0
  3. package/dist/coValueCore/SessionMap.d.ts +5 -3
  4. package/dist/coValueCore/SessionMap.d.ts.map +1 -1
  5. package/dist/coValueCore/SessionMap.js +19 -9
  6. package/dist/coValueCore/SessionMap.js.map +1 -1
  7. package/dist/coValueCore/coValueCore.d.ts +5 -2
  8. package/dist/coValueCore/coValueCore.d.ts.map +1 -1
  9. package/dist/coValueCore/coValueCore.js +24 -4
  10. package/dist/coValueCore/coValueCore.js.map +1 -1
  11. package/dist/coValueCore/verifiedState.d.ts +1 -3
  12. package/dist/coValueCore/verifiedState.d.ts.map +1 -1
  13. package/dist/coValueCore/verifiedState.js +10 -14
  14. package/dist/coValueCore/verifiedState.js.map +1 -1
  15. package/dist/exports.d.ts +3 -3
  16. package/dist/exports.d.ts.map +1 -1
  17. package/dist/exports.js +3 -3
  18. package/dist/exports.js.map +1 -1
  19. package/dist/permissions.d.ts +0 -1
  20. package/dist/permissions.d.ts.map +1 -1
  21. package/dist/permissions.js +34 -58
  22. package/dist/permissions.js.map +1 -1
  23. package/dist/queue/LocalTransactionsSyncQueue.d.ts.map +1 -1
  24. package/dist/queue/LocalTransactionsSyncQueue.js +1 -18
  25. package/dist/queue/LocalTransactionsSyncQueue.js.map +1 -1
  26. package/dist/sync.d.ts.map +1 -1
  27. package/dist/sync.js +8 -0
  28. package/dist/sync.js.map +1 -1
  29. package/dist/tests/StorageApiAsync.test.js +8 -8
  30. package/dist/tests/coValueCore.isStreaming.test.js +30 -40
  31. package/dist/tests/coValueCore.isStreaming.test.js.map +1 -1
  32. package/dist/tests/coValueCore.newContentSince.test.js +242 -132
  33. package/dist/tests/coValueCore.newContentSince.test.js.map +1 -1
  34. package/dist/tests/sync.auth.test.js +6 -6
  35. package/dist/tests/sync.load.test.js +11 -75
  36. package/dist/tests/sync.load.test.js.map +1 -1
  37. package/dist/tests/sync.mesh.test.js +2 -2
  38. package/dist/tests/sync.storage.test.js +22 -48
  39. package/dist/tests/sync.storage.test.js.map +1 -1
  40. package/dist/tests/sync.storageAsync.test.js +121 -71
  41. package/dist/tests/sync.storageAsync.test.js.map +1 -1
  42. package/dist/tests/sync.upload.test.js +11 -75
  43. package/dist/tests/sync.upload.test.js.map +1 -1
  44. package/dist/tests/testUtils.d.ts +4 -1
  45. package/dist/tests/testUtils.d.ts.map +1 -1
  46. package/dist/tests/testUtils.js +11 -0
  47. package/dist/tests/testUtils.js.map +1 -1
  48. package/package.json +3 -3
  49. package/src/coValueCore/SessionMap.ts +25 -15
  50. package/src/coValueCore/coValueCore.ts +32 -2
  51. package/src/coValueCore/verifiedState.ts +11 -20
  52. package/src/exports.ts +3 -2
  53. package/src/permissions.ts +34 -66
  54. package/src/queue/LocalTransactionsSyncQueue.ts +1 -20
  55. package/src/sync.ts +9 -0
  56. package/src/tests/StorageApiAsync.test.ts +8 -8
  57. package/src/tests/coValueCore.isStreaming.test.ts +84 -91
  58. package/src/tests/coValueCore.newContentSince.test.ts +246 -141
  59. package/src/tests/sync.auth.test.ts +6 -6
  60. package/src/tests/sync.load.test.ts +11 -79
  61. package/src/tests/sync.mesh.test.ts +2 -2
  62. package/src/tests/sync.storage.test.ts +22 -51
  63. package/src/tests/sync.storageAsync.test.ts +159 -76
  64. package/src/tests/sync.upload.test.ts +11 -78
  65. package/src/tests/testUtils.ts +16 -0
@@ -18,7 +18,6 @@ import {
18
18
  getParentGroupId,
19
19
  } from "./ids.js";
20
20
  import { JsonValue } from "./jsonValue.js";
21
- import { logger } from "./logger.js";
22
21
  import { expectGroup } from "./typeUtils/expectGroup.js";
23
22
 
24
23
  export type PermissionsDef =
@@ -71,23 +70,6 @@ function canAdmin(role: Role | undefined): boolean {
71
70
  return role === "admin" || role === "manager";
72
71
  }
73
72
 
74
- let logPermissionErrors = true;
75
-
76
- export function disablePermissionErrors() {
77
- logPermissionErrors = false;
78
- }
79
-
80
- function logPermissionError(
81
- message: string,
82
- attributes?: Record<string, JsonValue>,
83
- ) {
84
- if (logPermissionErrors === false) {
85
- return;
86
- }
87
-
88
- logger.debug("Permission error: " + message, attributes);
89
- }
90
-
91
73
  export function determineValidTransactions(coValue: CoValueCore): void {
92
74
  if (!coValue.isAvailable()) {
93
75
  throw new Error("determineValidTransactions CoValue is not available");
@@ -129,7 +111,10 @@ export function determineValidTransactions(coValue: CoValueCore): void {
129
111
  );
130
112
 
131
113
  if (!effectiveTransactor) {
132
- tx.markInvalid();
114
+ tx.markInvalid("Transactor not found in group", {
115
+ transactor: tx.author,
116
+ group: groupAtTime.toJSON(),
117
+ });
133
118
  continue;
134
119
  }
135
120
 
@@ -157,7 +142,10 @@ export function determineValidTransactions(coValue: CoValueCore): void {
157
142
  transactorRoleAtTxTime !== "writer" &&
158
143
  transactorRoleAtTxTime !== "writeOnly"
159
144
  ) {
160
- tx.markInvalid();
145
+ tx.markInvalid("Transactor has no write permissions", {
146
+ transactor: tx.author,
147
+ transactorRole: transactorRoleAtTxTime ?? "undefined",
148
+ });
161
149
  continue;
162
150
  }
163
151
 
@@ -263,10 +251,9 @@ function determineValidTransactionsForGroup(
263
251
  transaction.markValid();
264
252
  continue;
265
253
  } else {
266
- logPermissionError(
254
+ transaction.markInvalid(
267
255
  "Only admins can make private transactions in groups",
268
256
  );
269
- transaction.markInvalid();
270
257
  continue;
271
258
  }
272
259
  }
@@ -286,21 +273,18 @@ function determineValidTransactionsForGroup(
286
273
  | MapOpPayload<`child_${CoID<RawGroup>}`, CoID<RawGroup>>;
287
274
 
288
275
  if (changes.length !== 1) {
289
- logPermissionError("Group transaction must have exactly one change");
290
- transaction.markInvalid();
276
+ transaction.markInvalid("Group transaction must have exactly one change");
291
277
  continue;
292
278
  }
293
279
 
294
280
  if (change.op !== "set") {
295
- logPermissionError("Group transaction must set a role or readKey");
296
- transaction.markInvalid();
281
+ transaction.markInvalid("Group transaction must set a role or readKey");
297
282
  continue;
298
283
  }
299
284
 
300
285
  if (change.key === "readKey") {
301
286
  if (!canAdmin(transactorRole)) {
302
- logPermissionError("Only admins can set readKeys");
303
- transaction.markInvalid();
287
+ transaction.markInvalid("Only admins can set readKeys");
304
288
  continue;
305
289
  }
306
290
 
@@ -308,8 +292,7 @@ function determineValidTransactionsForGroup(
308
292
  continue;
309
293
  } else if (change.key === "profile") {
310
294
  if (!canAdmin(transactorRole)) {
311
- logPermissionError("Only admins can set profile");
312
- transaction.markInvalid();
295
+ transaction.markInvalid("Only admins can set profile");
313
296
  continue;
314
297
  }
315
298
 
@@ -317,7 +300,7 @@ function determineValidTransactionsForGroup(
317
300
  continue;
318
301
  } else if (change.key === "root") {
319
302
  if (!canAdmin(transactorRole)) {
320
- logPermissionError("Only admins can set root");
303
+ transaction.markInvalid("Only admins can set root");
321
304
  continue;
322
305
  }
323
306
 
@@ -337,8 +320,7 @@ function determineValidTransactionsForGroup(
337
320
  transactorRole !== "writeOnlyInvite" &&
338
321
  !isOwnWriteKeyRevelation(change.key, transactor, writeOnlyKeys)
339
322
  ) {
340
- logPermissionError("Only admins and managers can reveal keys");
341
- transaction.markInvalid();
323
+ transaction.markInvalid("Only admins and managers can reveal keys");
342
324
  continue;
343
325
  }
344
326
 
@@ -346,10 +328,9 @@ function determineValidTransactionsForGroup(
346
328
  continue;
347
329
  } else if (isParentExtension(change.key)) {
348
330
  if (!canAdmin(transactorRole)) {
349
- logPermissionError(
331
+ transaction.markInvalid(
350
332
  "Only admins and managers can set parent extensions",
351
333
  );
352
- transaction.markInvalid();
353
334
  continue;
354
335
  }
355
336
 
@@ -361,16 +342,14 @@ function determineValidTransactionsForGroup(
361
342
  );
362
343
 
363
344
  if (!parentGroupCore.isGroup()) {
364
- logPermissionError("Parent group is not a group");
365
- transaction.markInvalid();
345
+ transaction.markInvalid("Parent group is not a group");
366
346
  continue;
367
347
  }
368
348
 
369
349
  const parentGroup = expectGroup(parentGroupCore.getCurrentContent());
370
350
 
371
351
  if (isSelfExtension(coValue, parentGroup)) {
372
- logPermissionError("Parent group is a circular dependency");
373
- transaction.markInvalid();
352
+ transaction.markInvalid("Parent group is a circular dependency");
374
353
  continue;
375
354
  }
376
355
 
@@ -385,8 +364,7 @@ function determineValidTransactionsForGroup(
385
364
  transaction.markValid();
386
365
  continue;
387
366
  } else if (isChildExtension(change.key)) {
388
- logPermissionError("Child extensions are not allowed anymore");
389
- transaction.markInvalid();
367
+ transaction.markInvalid("Child extensions are not allowed anymore");
390
368
  continue;
391
369
  } else if (isWriteKeyForMember(change.key)) {
392
370
  const memberKey = getAccountOrAgentFromWriteKeyForMember(change.key);
@@ -397,8 +375,7 @@ function determineValidTransactionsForGroup(
397
375
  transactorRole !== "writeOnlyInvite" &&
398
376
  memberKey !== transactor
399
377
  ) {
400
- logPermissionError("Only admins and managers can set writeKeys");
401
- transaction.markInvalid();
378
+ transaction.markInvalid("Only admins and managers can set writeKeys");
402
379
  continue;
403
380
  }
404
381
 
@@ -413,10 +390,9 @@ function determineValidTransactionsForGroup(
413
390
  * blocking them from accessing the group.ß
414
391
  */
415
392
  if (writeKeys.has(change.key) && !canAdmin(transactorRole)) {
416
- logPermissionError(
393
+ transaction.markInvalid(
417
394
  "Write key already exists and can't be overridden by invite",
418
395
  );
419
- transaction.markInvalid();
420
396
  continue;
421
397
  }
422
398
 
@@ -442,8 +418,7 @@ function determineValidTransactionsForGroup(
442
418
  assignedRole !== "readerInvite" &&
443
419
  assignedRole !== "writeOnlyInvite"
444
420
  ) {
445
- logPermissionError("Group transaction must set a valid role");
446
- transaction.markInvalid();
421
+ transaction.markInvalid("Group transaction must set a valid role");
447
422
  continue;
448
423
  }
449
424
 
@@ -456,10 +431,9 @@ function determineValidTransactionsForGroup(
456
431
  assignedRole === "revoked"
457
432
  )
458
433
  ) {
459
- logPermissionError(
434
+ transaction.markInvalid(
460
435
  "Everyone can only be set to reader, writer, writeOnly or revoked",
461
436
  );
462
- transaction.markInvalid();
463
437
  continue;
464
438
  }
465
439
 
@@ -474,11 +448,6 @@ function determineValidTransactionsForGroup(
474
448
  transaction.markValid();
475
449
  }
476
450
 
477
- function markTransactionAsInvalid(message: string) {
478
- logPermissionError(message);
479
- transaction.markInvalid();
480
- }
481
-
482
451
  // is first self promotion to admin
483
452
  if (
484
453
  transactorRole === undefined &&
@@ -511,7 +480,7 @@ function determineValidTransactionsForGroup(
511
480
  assignedRole !== "admin" &&
512
481
  affectedMember !== transactor
513
482
  ) {
514
- markTransactionAsInvalid("Admins can't demote admins.");
483
+ transaction.markInvalid("Admins can't demote admins.");
515
484
  continue;
516
485
  }
517
486
 
@@ -527,20 +496,20 @@ function determineValidTransactionsForGroup(
527
496
  */
528
497
  if (transactorRole === "manager") {
529
498
  if (affectedMemberRole === "admin") {
530
- markTransactionAsInvalid("Managers can't demote admins.");
499
+ transaction.markInvalid("Managers can't demote admins.");
531
500
  continue;
532
501
  }
533
502
  if (change.value === "admin") {
534
- markTransactionAsInvalid("Managers can't promote to admin.");
503
+ transaction.markInvalid("Managers can't promote to admin.");
535
504
  continue;
536
505
  }
537
506
 
538
507
  if (change.value === "adminInvite") {
539
- markTransactionAsInvalid("Managers can't invite admins.");
508
+ transaction.markInvalid("Managers can't invite admins.");
540
509
  continue;
541
510
  }
542
511
  if (change.value === "managerInvite") {
543
- markTransactionAsInvalid("Managers can't invite managers.");
512
+ transaction.markInvalid("Managers can't invite managers.");
544
513
  continue;
545
514
  }
546
515
 
@@ -550,32 +519,31 @@ function determineValidTransactionsForGroup(
550
519
 
551
520
  if (transactorRole === "adminInvite") {
552
521
  if (change.value !== "admin") {
553
- logPermissionError("AdminInvites can only create admins.");
554
- transaction.markInvalid();
522
+ transaction.markInvalid("AdminInvites can only create admins.");
555
523
  continue;
556
524
  }
557
525
  } else if (transactorRole === "managerInvite") {
558
526
  if (change.value !== "manager") {
559
- markTransactionAsInvalid("managerInvite can only create managers.");
527
+ transaction.markInvalid("managerInvite can only create managers.");
560
528
  continue;
561
529
  }
562
530
  } else if (transactorRole === "writerInvite") {
563
531
  if (change.value !== "writer") {
564
- markTransactionAsInvalid("WriterInvites can only create writers.");
532
+ transaction.markInvalid("WriterInvites can only create writers.");
565
533
  continue;
566
534
  }
567
535
  } else if (transactorRole === "readerInvite") {
568
536
  if (change.value !== "reader") {
569
- markTransactionAsInvalid("ReaderInvites can only create reader.");
537
+ transaction.markInvalid("ReaderInvites can only create reader.");
570
538
  continue;
571
539
  }
572
540
  } else if (transactorRole === "writeOnlyInvite") {
573
541
  if (change.value !== "writeOnly") {
574
- markTransactionAsInvalid("WriteOnlyInvites can only create writeOnly.");
542
+ transaction.markInvalid("WriteOnlyInvites can only create writeOnly.");
575
543
  continue;
576
544
  }
577
545
  } else {
578
- markTransactionAsInvalid(
546
+ transaction.markInvalid(
579
547
  "Group transaction must be made by current admin, manager, or invite",
580
548
  );
581
549
  continue;
@@ -50,33 +50,14 @@ export class LocalTransactionsSyncQueue {
50
50
  coValue: VerifiedState,
51
51
  knownStateBefore: CoValueKnownState,
52
52
  ) {
53
- const content = coValue.newContentSince(knownStateBefore, {
54
- skipExpectContentUntil: true, // we need to calculate the streaming header considering the current batch
55
- });
53
+ const content = coValue.newContentSince(knownStateBefore);
56
54
 
57
55
  if (!content) {
58
56
  return;
59
57
  }
60
58
 
61
- let firstChunk = this.firstChunks.get(coValue.id);
62
-
63
59
  for (const piece of content) {
64
60
  this.batch.push(piece);
65
-
66
- // Check if the local content updates are in streaming, if so we need to add the info to the first chunk
67
- if (firstChunk) {
68
- if (!firstChunk.expectContentUntil) {
69
- firstChunk.expectContentUntil =
70
- knownStateFromContent(firstChunk).sessions;
71
- }
72
- combineKnownStateSessions(
73
- firstChunk.expectContentUntil,
74
- knownStateFromContent(piece).sessions,
75
- );
76
- } else {
77
- firstChunk = piece;
78
- this.firstChunks.set(coValue.id, firstChunk);
79
- }
80
61
  }
81
62
  }
82
63
 
package/src/sync.ts CHANGED
@@ -484,6 +484,15 @@ export class SyncManager {
484
484
  ? "import"
485
485
  : peer?.role;
486
486
 
487
+ // TODO: We can't handle client-to-client streaming until we
488
+ // handle the streaming state reset on disconnection
489
+ if (peer?.role === "client" && msg.expectContentUntil) {
490
+ msg = {
491
+ ...msg,
492
+ expectContentUntil: undefined,
493
+ };
494
+ }
495
+
487
496
  coValue.addDependenciesFromContentMessage(msg);
488
497
 
489
498
  // If some of the dependencies are missing, we wait for them to be available
@@ -539,16 +539,16 @@ describe("StorageApiAsync", () => {
539
539
  }),
540
540
  ).toMatchInlineSnapshot(`
541
541
  [
542
- "test -> test-storage | CONTENT Core header: false new: After: 1 New: 1 expectContentUntil: header/3",
543
- "test -> test-storage | CONTENT Core2 header: false new: After: 1 New: 1 expectContentUntil: header/3",
542
+ "test -> test-storage | CONTENT Core header: false new: After: 1 New: 1",
543
+ "test -> test-storage | CONTENT Core2 header: false new: After: 1 New: 1",
544
544
  "test -> test-storage | CONTENT Core header: false new: After: 2 New: 1",
545
545
  "test -> test-storage | CONTENT Core2 header: false new: After: 2 New: 1",
546
546
  "test-storage -> test | KNOWN CORRECTION Core sessions: empty",
547
547
  "test -> test-storage | CONTENT Core header: true new: After: 0 New: 3",
548
548
  "test-storage -> test | KNOWN CORRECTION Core2 sessions: empty",
549
549
  "test -> test-storage | CONTENT Core2 header: true new: After: 0 New: 3",
550
- "test -> test-storage | CONTENT Core header: false new: After: 3 New: 1 expectContentUntil: header/5",
551
- "test -> test-storage | CONTENT Core2 header: false new: After: 3 New: 1 expectContentUntil: header/5",
550
+ "test -> test-storage | CONTENT Core header: false new: After: 3 New: 1",
551
+ "test -> test-storage | CONTENT Core2 header: false new: After: 3 New: 1",
552
552
  "test -> test-storage | CONTENT Core header: false new: After: 4 New: 1",
553
553
  "test -> test-storage | CONTENT Core2 header: false new: After: 4 New: 1",
554
554
  ]
@@ -606,14 +606,14 @@ describe("StorageApiAsync", () => {
606
606
  }),
607
607
  ).toMatchInlineSnapshot(`
608
608
  [
609
- "test -> test-storage | CONTENT Core header: false new: After: 1 New: 1 expectContentUntil: header/3",
610
- "test -> test-storage | CONTENT Core2 header: false new: After: 1 New: 1 expectContentUntil: header/3",
609
+ "test -> test-storage | CONTENT Core header: false new: After: 1 New: 1",
610
+ "test -> test-storage | CONTENT Core2 header: false new: After: 1 New: 1",
611
611
  "test -> test-storage | CONTENT Core header: false new: After: 2 New: 1",
612
612
  "test -> test-storage | CONTENT Core2 header: false new: After: 2 New: 1",
613
613
  "test-storage -> test | KNOWN CORRECTION Core sessions: empty",
614
614
  "test -> test-storage | CONTENT Core header: true new: After: 0 New: 3",
615
- "test -> test-storage | CONTENT Core header: false new: After: 3 New: 1 expectContentUntil: header/5",
616
- "test -> test-storage | CONTENT Core2 header: false new: After: 3 New: 1 expectContentUntil: header/5",
615
+ "test -> test-storage | CONTENT Core header: false new: After: 3 New: 1",
616
+ "test -> test-storage | CONTENT Core2 header: false new: After: 3 New: 1",
617
617
  "test -> test-storage | CONTENT Core header: false new: After: 4 New: 1",
618
618
  "test -> test-storage | CONTENT Core2 header: false new: After: 4 New: 1",
619
619
  ]
@@ -2,6 +2,7 @@ import { assert, beforeEach, describe, expect, test } from "vitest";
2
2
  import {
3
3
  SyncMessagesLog,
4
4
  TEST_NODE_CONFIG,
5
+ fillCoMapWithLargeData,
5
6
  loadCoValueOrFail,
6
7
  setupTestAccount,
7
8
  setupTestNode,
@@ -49,19 +50,7 @@ describe("isStreaming", () => {
49
50
  await group.core.waitForSync();
50
51
  client.disconnect();
51
52
 
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
- }
53
+ const map = fillCoMapWithLargeData(group.createMap());
65
54
 
66
55
  const newSession = client.spawnNewSession();
67
56
 
@@ -101,16 +90,7 @@ describe("isStreaming", () => {
101
90
  client.disconnect();
102
91
 
103
92
  // 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
- }
93
+ fillCoMapWithLargeData(map);
114
94
 
115
95
  const newSession = client.spawnNewSession();
116
96
 
@@ -134,41 +114,81 @@ describe("isStreaming", () => {
134
114
  expect(mapInNewSession.core.isStreaming()).toBe(false);
135
115
  });
136
116
 
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
- });
117
+ // TODO: We can't handle client-to-client streaming until we
118
+ // handle the streaming state reset on disconnection
119
+ // Otherwise the other client might wait for a content that will never be sent
120
+ test.fails(
121
+ "streaming a large value between two clients should be streaming until all chunks are sent",
122
+ async () => {
123
+ const client = setupTestNode();
124
+ client.connectToSyncServer({
125
+ ourName: "initialClient",
126
+ });
127
+ const streamingClient = client.spawnNewSession();
128
+ streamingClient.connectToSyncServer({
129
+ ourName: "streamingClient",
130
+ });
131
+
132
+ const group = client.node.createGroup();
133
+
134
+ await group.core.waitForSync();
135
+ client.disconnect();
136
+
137
+ const map = fillCoMapWithLargeData(group.createMap());
138
+
139
+ const loadingClient = client.spawnNewSession();
140
+ loadingClient.connectToSyncServer({
141
+ ourName: "loadingClient",
142
+ });
143
+
144
+ await loadCoValueOrFail(loadingClient.node, group.id);
145
+
146
+ const content = map.core.verified.newContentSince(undefined);
147
+ assert(content);
148
+ const lastChunk = content.pop();
149
+ assert(lastChunk);
150
+
151
+ for (const chunk of content) {
152
+ streamingClient.node.syncManager.handleNewContent(chunk, "import");
153
+ }
154
+
155
+ await streamingClient.node.syncManager.waitForAllCoValuesSync();
156
+
157
+ const mapInLoadingClient = await loadCoValueOrFail(
158
+ loadingClient.node,
159
+ map.id,
160
+ );
146
161
 
147
- const group = client.node.createGroup();
162
+ expect(mapInLoadingClient.core.isStreaming()).toBe(true);
148
163
 
149
- await group.core.waitForSync();
150
- client.disconnect();
164
+ streamingClient.node.syncManager.handleNewContent(lastChunk, "import");
151
165
 
152
- const map = group.createMap();
166
+ await waitFor(() => {
167
+ expect(mapInLoadingClient.core.knownState()).toEqual(
168
+ map.core.knownState(),
169
+ );
170
+ });
153
171
 
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;
172
+ expect(mapInLoadingClient.core.isStreaming()).toBe(false);
173
+ },
174
+ );
158
175
 
159
- const value = Buffer.alloc(chunkSize, `value$`).toString("base64");
176
+ test("should be false when getting streaming content that is already in the known state", async () => {
177
+ const client = setupTestNode({
178
+ connected: true,
179
+ });
160
180
 
161
- for (let i = 0; i < chunks; i++) {
162
- const key = `key${i}`;
163
- map.set(key, value, "trusting");
164
- }
181
+ const group = client.node.createGroup();
165
182
 
166
- const loadingClient = client.spawnNewSession();
167
- loadingClient.connectToSyncServer({
168
- ourName: "loadingClient",
169
- });
183
+ await group.core.waitForSync();
184
+
185
+ const map = fillCoMapWithLargeData(group.createMap());
170
186
 
171
- await loadCoValueOrFail(loadingClient.node, group.id);
187
+ await map.core.waitForSync();
188
+ const newSession = client.spawnNewSession();
189
+
190
+ const mapInNewSession1 = await loadCoValueOrFail(newSession.node, map.id);
191
+ await mapInNewSession1.core.waitForFullStreaming();
172
192
 
173
193
  const content = map.core.verified.newContentSince(undefined);
174
194
  assert(content);
@@ -176,57 +196,28 @@ describe("isStreaming", () => {
176
196
  assert(lastChunk);
177
197
 
178
198
  for (const chunk of content) {
179
- streamingClient.node.syncManager.handleNewContent(chunk, "import");
199
+ newSession.node.syncManager.handleNewContent(chunk, "import");
180
200
  }
181
201
 
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
- });
202
+ const mapInNewSession = await loadCoValueOrFail(newSession.node, map.id);
198
203
 
199
- expect(mapInLoadingClient.core.isStreaming()).toBe(false);
204
+ expect(mapInNewSession.core.isStreaming()).toBe(false);
200
205
  });
201
206
 
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
- });
207
+ test("streaming state from clients should be ignored", async () => {
208
+ const client = setupTestNode();
206
209
 
207
210
  const group = client.node.createGroup();
208
211
 
209
212
  await group.core.waitForSync();
210
213
 
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
- }
214
+ const map = fillCoMapWithLargeData(group.createMap());
224
215
 
225
216
  await map.core.waitForSync();
226
217
  const newSession = client.spawnNewSession();
227
-
228
- const mapInNewSession1 = await loadCoValueOrFail(newSession.node, map.id);
229
- await mapInNewSession1.core.waitForFullStreaming();
218
+ newSession.connectToSyncServer({
219
+ ourName: "streamingClient",
220
+ });
230
221
 
231
222
  const content = map.core.verified.newContentSince(undefined);
232
223
  assert(content);
@@ -237,9 +228,11 @@ describe("isStreaming", () => {
237
228
  newSession.node.syncManager.handleNewContent(chunk, "import");
238
229
  }
239
230
 
240
- const mapInNewSession = await loadCoValueOrFail(newSession.node, map.id);
241
-
242
- expect(mapInNewSession.core.isStreaming()).toBe(false);
231
+ const mapOnServer = jazzCloud.node.getCoValue(map.id);
232
+ expect(mapOnServer.isStreaming()).toBe(false);
233
+ expect(mapOnServer.knownState()).toEqual(
234
+ mapOnServer.knownStateWithStreaming(),
235
+ );
243
236
  });
244
237
 
245
238
  test("should be false when getting streaming content that's not really streaming", async () => {