cojson 0.18.19 → 0.18.21

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 (56) hide show
  1. package/.turbo/turbo-build.log +1 -1
  2. package/CHANGELOG.md +18 -0
  3. package/dist/GarbageCollector.d.ts +2 -1
  4. package/dist/GarbageCollector.d.ts.map +1 -1
  5. package/dist/GarbageCollector.js +3 -2
  6. package/dist/GarbageCollector.js.map +1 -1
  7. package/dist/coValueCore/coValueCore.d.ts +28 -25
  8. package/dist/coValueCore/coValueCore.d.ts.map +1 -1
  9. package/dist/coValueCore/coValueCore.js +128 -90
  10. package/dist/coValueCore/coValueCore.js.map +1 -1
  11. package/dist/coValueCore/utils.d.ts +6 -0
  12. package/dist/coValueCore/utils.d.ts.map +1 -1
  13. package/dist/coValueCore/utils.js +53 -25
  14. package/dist/coValueCore/utils.js.map +1 -1
  15. package/dist/localNode.d.ts +5 -4
  16. package/dist/localNode.d.ts.map +1 -1
  17. package/dist/localNode.js +31 -37
  18. package/dist/localNode.js.map +1 -1
  19. package/dist/sync.d.ts.map +1 -1
  20. package/dist/sync.js +56 -69
  21. package/dist/sync.js.map +1 -1
  22. package/dist/tests/GarbageCollector.test.js +14 -0
  23. package/dist/tests/GarbageCollector.test.js.map +1 -1
  24. package/dist/tests/SyncStateManager.test.js +1 -1
  25. package/dist/tests/coValueCore.dependencies.test.d.ts +2 -0
  26. package/dist/tests/coValueCore.dependencies.test.d.ts.map +1 -0
  27. package/dist/tests/coValueCore.dependencies.test.js +55 -0
  28. package/dist/tests/coValueCore.dependencies.test.js.map +1 -0
  29. package/dist/tests/coValueCore.test.js +2 -2
  30. package/dist/tests/coValueCore.test.js.map +1 -1
  31. package/dist/tests/coValueCoreLoadingState.test.js +43 -62
  32. package/dist/tests/coValueCoreLoadingState.test.js.map +1 -1
  33. package/dist/tests/permissions.test.js +117 -117
  34. package/dist/tests/permissions.test.js.map +1 -1
  35. package/dist/tests/sync.load.test.js +238 -9
  36. package/dist/tests/sync.load.test.js.map +1 -1
  37. package/dist/tests/sync.peerReconciliation.test.js +7 -6
  38. package/dist/tests/sync.peerReconciliation.test.js.map +1 -1
  39. package/dist/tests/testUtils.d.ts.map +1 -1
  40. package/dist/tests/testUtils.js +2 -1
  41. package/dist/tests/testUtils.js.map +1 -1
  42. package/package.json +2 -2
  43. package/src/GarbageCollector.ts +5 -2
  44. package/src/coValueCore/coValueCore.ts +172 -118
  45. package/src/coValueCore/utils.ts +85 -31
  46. package/src/localNode.ts +43 -48
  47. package/src/sync.ts +63 -89
  48. package/src/tests/GarbageCollector.test.ts +20 -0
  49. package/src/tests/SyncStateManager.test.ts +1 -1
  50. package/src/tests/coValueCore.dependencies.test.ts +90 -0
  51. package/src/tests/coValueCore.test.ts +2 -2
  52. package/src/tests/coValueCoreLoadingState.test.ts +50 -66
  53. package/src/tests/permissions.test.ts +120 -123
  54. package/src/tests/sync.load.test.ts +308 -9
  55. package/src/tests/sync.peerReconciliation.test.ts +7 -6
  56. package/src/tests/testUtils.ts +5 -3
package/src/localNode.ts CHANGED
@@ -40,6 +40,7 @@ import { Peer, PeerID, SyncManager } from "./sync.js";
40
40
  import { accountOrAgentIDfromSessionID } from "./typeUtils/accountOrAgentIDfromSessionID.js";
41
41
  import { expectGroup } from "./typeUtils/expectGroup.js";
42
42
  import { canBeBranched } from "./coValueCore/branching.js";
43
+ import { connectedPeers } from "./streamUtils.js";
43
44
 
44
45
  /** A `LocalNode` represents a local view of a set of loaded `CoValue`s, from the perspective of a particular account (or primitive cryptographic agent).
45
46
 
@@ -81,12 +82,15 @@ export class LocalNode {
81
82
  this.crypto = crypto;
82
83
  }
83
84
 
84
- enableGarbageCollector() {
85
+ enableGarbageCollector(opts?: { garbageCollectGroups?: boolean }) {
85
86
  if (this.garbageCollector) {
86
87
  return;
87
88
  }
88
89
 
89
- this.garbageCollector = new GarbageCollector(this.coValues);
90
+ this.garbageCollector = new GarbageCollector(
91
+ this.coValues,
92
+ opts?.garbageCollectGroups ?? false,
93
+ );
90
94
  }
91
95
 
92
96
  setStorage(storage: StorageAPI) {
@@ -99,7 +103,13 @@ export class LocalNode {
99
103
  }
100
104
 
101
105
  hasCoValue(id: RawCoID) {
102
- return this.coValues.has(id);
106
+ const coValue = this.coValues.get(id);
107
+
108
+ if (!coValue) {
109
+ return false;
110
+ }
111
+
112
+ return coValue.loadingState !== "unknown";
103
113
  }
104
114
 
105
115
  getCoValue(id: RawCoID) {
@@ -119,16 +129,6 @@ export class LocalNode {
119
129
  return this.coValues.values();
120
130
  }
121
131
 
122
- private putCoValue(
123
- id: RawCoID,
124
- verified: VerifiedState,
125
- { forceOverwrite = false }: { forceOverwrite?: boolean } = {},
126
- ): AvailableCoValueCore {
127
- const entry = this.getCoValue(id);
128
- entry.internalMarkMagicallyAvailable(verified, { forceOverwrite });
129
- return entry as AvailableCoValueCore;
130
- }
131
-
132
132
  internalDeleteCoValue(id: RawCoID) {
133
133
  this.coValues.delete(id);
134
134
  }
@@ -371,10 +371,13 @@ export class LocalNode {
371
371
 
372
372
  const id = idforHeader(header, this.crypto);
373
373
 
374
- const coValue = this.putCoValue(
375
- id,
376
- new VerifiedState(id, this.crypto, header),
377
- );
374
+ const coValue = this.getCoValue(id);
375
+
376
+ coValue.provideHeader(header);
377
+
378
+ if (!coValue.hasVerifiedContent()) {
379
+ throw new Error("CoValue not available after providing header");
380
+ }
378
381
 
379
382
  this.garbageCollector?.trackCoValueAccess(coValue);
380
383
  this.syncManager.syncHeader(coValue.verified);
@@ -624,11 +627,13 @@ export class LocalNode {
624
627
  return;
625
628
  }
626
629
 
630
+ const groupCoreAsDifferentAgent = await this.loadCoValueAsDifferentAgent(
631
+ group.id,
632
+ inviteAgentSecret,
633
+ );
634
+
627
635
  const groupAsInvite = expectGroup(
628
- this.loadCoValueAsDifferentAgent(
629
- group.id,
630
- inviteAgentSecret,
631
- ).getCurrentContent(),
636
+ groupCoreAsDifferentAgent.getCurrentContent(),
632
637
  );
633
638
 
634
639
  groupAsInvite.addMemberInternal(
@@ -755,7 +760,7 @@ export class LocalNode {
755
760
  return group;
756
761
  }
757
762
 
758
- loadCoValueAsDifferentAgent(
763
+ async loadCoValueAsDifferentAgent(
759
764
  id: RawCoID,
760
765
  secret: AgentSecret,
761
766
  accountId?: RawAccountID | AgentID,
@@ -768,44 +773,34 @@ export class LocalNode {
768
773
  this.crypto,
769
774
  );
770
775
 
771
- newNode.cloneVerifiedStateFrom(this, id);
776
+ await newNode.loadVerifiedStateFrom(this, id);
772
777
 
773
778
  return newNode.expectCoValueLoaded(id);
774
779
  }
775
780
 
776
781
  /** @internal */
777
- cloneVerifiedStateFrom(otherNode: LocalNode, id: RawCoID) {
778
- const coValuesIdsToCopy = [id];
782
+ async loadVerifiedStateFrom(otherNode: LocalNode, id: RawCoID) {
783
+ const connection = connectedPeers("source-" + id, "target-" + id, {
784
+ peer1role: "server",
785
+ peer2role: "client",
786
+ });
779
787
 
780
- // Scan all the dependencies and add them to the list
781
- for (let i = 0; i < coValuesIdsToCopy.length; i++) {
782
- const coValueID = coValuesIdsToCopy[i]!;
783
- const coValue = otherNode.getCoValue(coValueID);
788
+ this.syncManager.addPeer(connection[0], true);
789
+ otherNode.syncManager.addPeer(connection[1], true);
784
790
 
785
- if (!coValue.isAvailable()) {
786
- continue;
787
- }
791
+ const coValue = this.getCoValue(id);
788
792
 
789
- for (const dep of coValue.getDependedOnCoValues()) {
790
- coValuesIdsToCopy.push(dep);
791
- }
793
+ const peerState = this.syncManager.peers[connection[0].id];
794
+
795
+ if (!peerState) {
796
+ throw new Error("Peer state not found");
792
797
  }
793
798
 
794
- // Copy the coValue all the dependencies by following the dependency order
795
- while (coValuesIdsToCopy.length > 0) {
796
- const coValueID = coValuesIdsToCopy.pop()!;
797
- const coValue = otherNode.getCoValue(coValueID);
799
+ coValue.loadFromPeers([peerState]);
798
800
 
799
- if (!coValue.isAvailable()) {
800
- continue;
801
- }
801
+ await coValue.waitForAvailable();
802
802
 
803
- if (this.coValues.get(coValueID)?.isAvailable()) {
804
- continue;
805
- }
806
-
807
- this.putCoValue(coValueID, coValue.verified);
808
- }
803
+ peerState.gracefulShutdown();
809
804
  }
810
805
 
811
806
  /**
package/src/sync.ts CHANGED
@@ -8,7 +8,6 @@ import {
8
8
  knownStateFromContent,
9
9
  } from "./coValueContentMessage.js";
10
10
  import { CoValueCore } from "./coValueCore/coValueCore.js";
11
- import { getDependedOnCoValuesFromRawData } from "./coValueCore/utils.js";
12
11
  import { CoValueHeader, Transaction } from "./coValueCore/verifiedState.js";
13
12
  import { Signature } from "./crypto/crypto.js";
14
13
  import { RawCoID, SessionID, isRawCoID } from "./ids.js";
@@ -17,8 +16,6 @@ import { logger } from "./logger.js";
17
16
  import { CoValuePriority } from "./priority.js";
18
17
  import { IncomingMessagesQueue } from "./queue/IncomingMessagesQueue.js";
19
18
  import { LocalTransactionsSyncQueue } from "./queue/LocalTransactionsSyncQueue.js";
20
- import { accountOrAgentIDfromSessionID } from "./typeUtils/accountOrAgentIDfromSessionID.js";
21
- import { isAccountID } from "./typeUtils/isAccountID.js";
22
19
 
23
20
  export type CoValueKnownState = {
24
21
  id: RawCoID;
@@ -463,7 +460,6 @@ export class SyncManager {
463
460
 
464
461
  const handleLoadResult = () => {
465
462
  if (coValue.isAvailable()) {
466
- this.sendNewContent(msg.id, peer);
467
463
  return;
468
464
  }
469
465
 
@@ -524,8 +520,42 @@ export class SyncManager {
524
520
  ? "import"
525
521
  : peer?.role;
526
522
 
523
+ coValue.addDependenciesFromContentMessage(msg);
524
+
525
+ // If some of the dependencies are missing, we wait for them to be available
526
+ // before handling the new content
527
+ // This must happen even if the dependencies are not related to this content
528
+ // but the content we've got before
529
+ if (!this.skipVerify && coValue.hasMissingDependencies()) {
530
+ coValue.addNewContentToQueue(msg, from);
531
+
532
+ for (const dependency of coValue.missingDependencies) {
533
+ const dependencyCoValue = this.local.getCoValue(dependency);
534
+ if (!dependencyCoValue.hasVerifiedContent()) {
535
+ const peers = this.getServerPeers(dependency);
536
+
537
+ // If the peer that sent the new content is a client, we can assume that they are in possession of the dependency
538
+ if (peer?.role === "client") {
539
+ peers.push(peer);
540
+ }
541
+
542
+ dependencyCoValue.load(peers);
543
+ }
544
+ }
545
+
546
+ return;
547
+ }
548
+
549
+ /**
550
+ * Check if we have the CoValue in memory
551
+ */
527
552
  if (!coValue.hasVerifiedContent()) {
553
+ /**
554
+ * The peer has assumed we already have the CoValue
555
+ */
528
556
  if (!msg.header) {
557
+ // We check if the covalue was in memory and has been garbage collected
558
+ // In that case we should have it tracked in the storage
529
559
  const storageKnownState = this.local.storage?.getKnownState(msg.id);
530
560
 
531
561
  if (storageKnownState?.header) {
@@ -542,6 +572,7 @@ export class SyncManager {
542
572
  return;
543
573
  }
544
574
 
575
+ // The peer assumption is not correct, so we ask for the full CoValue
545
576
  if (peer) {
546
577
  this.trySendToPeer(peer, {
547
578
  action: "known",
@@ -551,6 +582,8 @@ export class SyncManager {
551
582
  sessions: {},
552
583
  });
553
584
  } else {
585
+ // The wrong assumption has been made by storage or import, we don't have a recovery mechanism
586
+ // Should never happen
554
587
  logger.error(
555
588
  "Received new content with no header on a missing CoValue",
556
589
  {
@@ -561,43 +594,15 @@ export class SyncManager {
561
594
  return;
562
595
  }
563
596
 
564
- const sessionIDs = Object.keys(msg.new) as SessionID[];
565
- const transactions = Object.values(msg.new).map(
566
- (content) => content.newTransactions,
567
- );
568
-
569
- // If we'll be performing transaction verification, ensure all the dependencies available.
570
- if (!this.skipVerify) {
571
- for (const dependency of getDependedOnCoValuesFromRawData(
572
- msg.id,
573
- msg.header,
574
- sessionIDs,
575
- transactions,
576
- )) {
577
- const dependencyCoValue = this.local.getCoValue(dependency);
578
-
579
- if (!dependencyCoValue.hasVerifiedContent()) {
580
- coValue.markMissingDependency(dependency);
581
-
582
- const peers = this.getServerPeers(dependencyCoValue.id);
583
-
584
- // if the peer that sent the content is a client, we add it to the list of peers
585
- // to also ask them for the dependency
586
- if (peer?.role === "client") {
587
- peers.push(peer);
588
- }
589
-
590
- dependencyCoValue.load(peers);
591
- } else if (!dependencyCoValue.isAvailable()) {
592
- coValue.markMissingDependency(dependency);
593
- }
594
- }
595
- }
597
+ const previousState = coValue.loadingState;
596
598
 
599
+ /**
600
+ * We are getting the full CoValue, so we can instantiate it
601
+ */
597
602
  const success = coValue.provideHeader(
598
603
  msg.header,
599
- peer?.id ?? "storage",
600
604
  msg.expectContentUntil,
605
+ this.skipVerify,
601
606
  );
602
607
 
603
608
  if (!success) {
@@ -608,6 +613,7 @@ export class SyncManager {
608
613
  return;
609
614
  }
610
615
 
616
+ coValue.markFoundInPeer(peer?.id ?? "storage", previousState);
611
617
  peer?.updateHeader(msg.id, true);
612
618
 
613
619
  if (msg.expectContentUntil) {
@@ -619,6 +625,7 @@ export class SyncManager {
619
625
  }
620
626
  }
621
627
 
628
+ // At this point the CoValue must be in memory, if not we have a bug
622
629
  if (!coValue.hasVerifiedContent()) {
623
630
  throw new Error(
624
631
  "Unreachable: CoValue should always have a verified state at this point",
@@ -635,6 +642,9 @@ export class SyncManager {
635
642
  new: {},
636
643
  };
637
644
 
645
+ /**
646
+ * The coValue is in memory, load the transactions from the content message
647
+ */
638
648
  for (const [sessionID, newContentForSession] of Object.entries(msg.new) as [
639
649
  SessionID,
640
650
  SessionNewContent,
@@ -659,57 +669,8 @@ export class SyncManager {
659
669
  continue;
660
670
  }
661
671
 
662
- // If we'll be performing transaction verification, ensure the account is available.
663
- if (!this.skipVerify) {
664
- const accountId = accountOrAgentIDfromSessionID(sessionID);
665
-
666
- if (isAccountID(accountId)) {
667
- const account = this.local.getCoValue(accountId);
668
-
669
- // We can't verify the transaction without the account, so we delay the session content handling until the account is available
670
- if (!account.isAvailable()) {
671
- // This covers the case where we are getting a new session on an already loaded coValue
672
- // where we need to load the account to get their public key
673
- if (!coValue.missingDependencies.has(accountId)) {
674
- const peers = this.getServerPeers(account.id);
675
-
676
- if (peer?.role === "client") {
677
- // if the peer that sent the content is a client, we add it to the list of peers
678
- // to also ask them for the dependency
679
- peers.push(peer);
680
- }
681
-
682
- account.load(peers);
683
- }
684
-
685
- // We need to wait for the account to be available before we can verify the transaction
686
- // Currently doing this by delaying the handleNewContent for the session to when we have the account
687
- //
688
- // This is not the best solution, because the knownState is not updated and the ACK response will be given
689
- // by excluding the session.
690
- // This is good enough implementation for now because the only case for the account to be missing are out-of-order
691
- // dependencies push, so the gap should be short lived.
692
- //
693
- // When we are going to have sharded-peers we should revisit this, and store unverified sessions that are considered as part of the
694
- // knwonState, but not actively used until they can be verified.
695
- void account.waitForAvailable().then(() => {
696
- this.handleNewContent(
697
- {
698
- action: "content",
699
- id: coValue.id,
700
- new: {
701
- [sessionID]: newContentForSession,
702
- },
703
- priority: msg.priority,
704
- },
705
- from,
706
- );
707
- });
708
- continue;
709
- }
710
- }
711
- }
712
-
672
+ // TODO: Handle invalid signatures in the middle of streaming
673
+ // This could cause a situation where we are unable to load a chunk, and ask for a correction for all the subsequent chunks
713
674
  const result = coValue.tryAddTransactions(
714
675
  sessionID,
715
676
  newTransactions,
@@ -725,6 +686,7 @@ export class SyncManager {
725
686
  id: msg.id,
726
687
  err: result.error,
727
688
  });
689
+ // TODO Mark only the session as errored, not the whole coValue
728
690
  coValue.markErrored(peer.id, result.error);
729
691
  } else {
730
692
  logger.error("Failed to add transactions from storage", {
@@ -740,7 +702,10 @@ export class SyncManager {
740
702
  }
741
703
 
742
704
  // The new content for this session has been verified, so we can store it
743
- contentToStore.new[sessionID] = newContentForSession;
705
+ if (result.value) {
706
+ contentToStore.new[sessionID] = newContentForSession;
707
+ }
708
+
744
709
  peer?.updateSessionCounter(
745
710
  msg.id,
746
711
  sessionID,
@@ -749,6 +714,9 @@ export class SyncManager {
749
714
  );
750
715
  }
751
716
 
717
+ /**
718
+ * Check if we lack some transactions to be able to load the new content
719
+ */
752
720
  if (invalidStateAssumed) {
753
721
  if (peer) {
754
722
  this.trySendToPeer(peer, {
@@ -784,6 +752,9 @@ export class SyncManager {
784
752
 
785
753
  const syncedPeers = [];
786
754
 
755
+ /**
756
+ * Store the content and propagate it to the server peers and the subscribed client peers
757
+ */
787
758
  const hasNewContent =
788
759
  contentToStore.header || Object.keys(contentToStore.new).length > 0;
789
760
 
@@ -824,6 +795,9 @@ export class SyncManager {
824
795
  }
825
796
  }
826
797
 
798
+ /**
799
+ * Send an update to all the sync state listeners
800
+ */
827
801
  for (const peer of syncedPeers) {
828
802
  this.syncState.triggerUpdate(peer.id, coValue.id);
829
803
  }
@@ -69,6 +69,26 @@ describe("garbage collector", () => {
69
69
  test("coValues are not garbage collected if they are a group or account", async () => {
70
70
  const client = await setupTestAccount();
71
71
 
72
+ client.addStorage({
73
+ ourName: "client",
74
+ });
75
+ client.node.enableGarbageCollector({
76
+ garbageCollectGroups: true,
77
+ });
78
+
79
+ const group = client.node.createGroup();
80
+
81
+ await new Promise((resolve) => setTimeout(resolve, 10));
82
+
83
+ client.node.garbageCollector?.collect();
84
+
85
+ expect(client.node.getCoValue(group.id).isAvailable()).toBe(false);
86
+ expect(client.node.getCoValue(client.accountID).isAvailable()).toBe(false);
87
+ });
88
+
89
+ test("group or account coValues are garbage collected if garbageCollectGroups is true", async () => {
90
+ const client = await setupTestAccount();
91
+
72
92
  client.addStorage({
73
93
  ourName: "client",
74
94
  });
@@ -333,9 +333,9 @@ describe("SyncStateManager", () => {
333
333
  [
334
334
  "server -> client | CONTENT Map header: true new: After: 0 New: 1",
335
335
  "client -> server | LOAD Group sessions: empty",
336
- "client -> server | KNOWN Map sessions: header/1",
337
336
  "server -> client | CONTENT Group header: true new: After: 0 New: 3",
338
337
  "client -> server | KNOWN Group sessions: header/3",
338
+ "client -> server | KNOWN Map sessions: header/1",
339
339
  ]
340
340
  `);
341
341
  });
@@ -0,0 +1,90 @@
1
+ import { beforeEach, expect, test } from "vitest";
2
+ import {
3
+ loadCoValueOrFail,
4
+ setupTestAccount,
5
+ setupTestNode,
6
+ } from "./testUtils.js";
7
+
8
+ beforeEach(() => {
9
+ setupTestNode({ isSyncServer: true });
10
+ });
11
+
12
+ test("should track the group dependency when creating a new coValue", () => {
13
+ const client = setupTestNode();
14
+
15
+ const group = client.node.createGroup();
16
+ const map = group.createMap();
17
+
18
+ expect(map.core.getDependedOnCoValues()).toEqual(new Set([group.core.id]));
19
+ });
20
+
21
+ test("should track the source dependency when creating a new branch", () => {
22
+ const client = setupTestNode();
23
+
24
+ const group = client.node.createGroup();
25
+ const map = group.createMap();
26
+
27
+ const branch = map.core.createBranch("feature-branch", group.id);
28
+
29
+ expect(branch.getDependedOnCoValues()).toEqual(
30
+ new Set([map.core.id, group.core.id]),
31
+ );
32
+ });
33
+
34
+ test("should track the parent group dependency when extending a group", () => {
35
+ const client = setupTestNode();
36
+
37
+ const group = client.node.createGroup();
38
+ const parentGroup = client.node.createGroup();
39
+
40
+ group.extend(parentGroup);
41
+
42
+ expect(group.core.getDependedOnCoValues()).toEqual(
43
+ new Set([parentGroup.core.id]),
44
+ );
45
+ });
46
+
47
+ test("should track the account dependency when syncing an account session", async () => {
48
+ const sourceClient = await setupTestAccount({
49
+ connected: true,
50
+ });
51
+ const targetClient = await setupTestAccount({
52
+ connected: true,
53
+ });
54
+
55
+ const group = sourceClient.node.createGroup();
56
+ group.addMember("everyone", "reader");
57
+ const map = group.createMap();
58
+
59
+ map.set("hello", "world");
60
+
61
+ await map.core.waitForSync();
62
+
63
+ const loadedMap = await loadCoValueOrFail(targetClient.node, map.id);
64
+
65
+ expect(loadedMap.core.getDependedOnCoValues()).toEqual(
66
+ new Set([group.core.id, sourceClient.accountID]),
67
+ );
68
+ });
69
+
70
+ test("should track the account dependency when syncing a group extension", async () => {
71
+ const sourceClient = await setupTestAccount({
72
+ connected: true,
73
+ });
74
+ const targetClient = await setupTestAccount({
75
+ connected: true,
76
+ });
77
+
78
+ const group = sourceClient.node.createGroup();
79
+ const parentGroup = sourceClient.node.createGroup();
80
+
81
+ group.extend(parentGroup);
82
+
83
+ await group.core.waitForSync();
84
+
85
+ const loadedGroup = await loadCoValueOrFail(targetClient.node, group.id);
86
+
87
+ expect(loadedGroup.core.getDependedOnCoValues()).toEqual(
88
+ new Set([parentGroup.core.id, sourceClient.accountID]),
89
+ );
90
+ });
@@ -539,7 +539,7 @@ describe("markErrored and isErroredInPeer", () => {
539
539
 
540
540
  expect(coValue.isAvailable()).toBe(false);
541
541
 
542
- const success = coValue.provideHeader(header, "peerId");
542
+ const success = coValue.provideHeader(header);
543
543
  expect(success).toBe(true);
544
544
  expect(coValue.isAvailable()).toBe(true);
545
545
  });
@@ -559,7 +559,7 @@ describe("markErrored and isErroredInPeer", () => {
559
559
 
560
560
  expect(coValue.isAvailable()).toBe(false);
561
561
 
562
- const success = coValue.provideHeader(header, "peerId");
562
+ const success = coValue.provideHeader(header);
563
563
  expect(success).toBe(false);
564
564
  expect(coValue.isAvailable()).toBe(false);
565
565
  });