cojson 0.18.28 → 0.18.30
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.
- package/.turbo/turbo-build.log +1 -1
- package/CHANGELOG.md +16 -0
- package/dist/PeerState.d.ts +23 -14
- package/dist/PeerState.d.ts.map +1 -1
- package/dist/PeerState.js +74 -23
- package/dist/PeerState.js.map +1 -1
- package/dist/SyncStateManager.d.ts +3 -3
- package/dist/SyncStateManager.d.ts.map +1 -1
- package/dist/SyncStateManager.js +18 -44
- package/dist/SyncStateManager.js.map +1 -1
- package/dist/coValueContentMessage.d.ts.map +1 -1
- package/dist/coValueContentMessage.js +2 -1
- package/dist/coValueContentMessage.js.map +1 -1
- package/dist/coValueCore/PeerKnownState.d.ts +21 -0
- package/dist/coValueCore/PeerKnownState.d.ts.map +1 -0
- package/dist/coValueCore/PeerKnownState.js +52 -0
- package/dist/coValueCore/PeerKnownState.js.map +1 -0
- package/dist/coValueCore/coValueCore.d.ts +39 -8
- package/dist/coValueCore/coValueCore.d.ts.map +1 -1
- package/dist/coValueCore/coValueCore.js +139 -40
- package/dist/coValueCore/coValueCore.js.map +1 -1
- package/dist/coValueCore/decryptTransactionChangesAndMeta.d.ts.map +1 -1
- package/dist/coValueCore/decryptTransactionChangesAndMeta.js +0 -5
- package/dist/coValueCore/decryptTransactionChangesAndMeta.js.map +1 -1
- package/dist/coValueCore/verifiedState.d.ts +0 -14
- package/dist/coValueCore/verifiedState.d.ts.map +1 -1
- package/dist/coValueCore/verifiedState.js +2 -32
- package/dist/coValueCore/verifiedState.js.map +1 -1
- package/dist/coValues/coList.d.ts +3 -4
- package/dist/coValues/coList.d.ts.map +1 -1
- package/dist/coValues/coList.js +4 -4
- package/dist/coValues/coList.js.map +1 -1
- package/dist/coValues/coMap.d.ts +3 -4
- package/dist/coValues/coMap.d.ts.map +1 -1
- package/dist/coValues/coMap.js +5 -4
- package/dist/coValues/coMap.js.map +1 -1
- package/dist/coValues/coStream.d.ts +3 -3
- package/dist/coValues/coStream.d.ts.map +1 -1
- package/dist/coValues/coStream.js +3 -4
- package/dist/coValues/coStream.js.map +1 -1
- package/dist/coValues/group.d.ts +3 -3
- package/dist/coValues/group.d.ts.map +1 -1
- package/dist/coValues/group.js +74 -52
- package/dist/coValues/group.js.map +1 -1
- package/dist/exports.d.ts +2 -2
- package/dist/exports.d.ts.map +1 -1
- package/dist/exports.js +2 -2
- package/dist/exports.js.map +1 -1
- package/dist/localNode.d.ts.map +1 -1
- package/dist/localNode.js +7 -5
- package/dist/localNode.js.map +1 -1
- package/dist/permissions.d.ts +5 -1
- package/dist/permissions.d.ts.map +1 -1
- package/dist/permissions.js +173 -109
- package/dist/permissions.js.map +1 -1
- package/dist/sync.d.ts.map +1 -1
- package/dist/sync.js +33 -44
- package/dist/sync.js.map +1 -1
- package/dist/tests/PeerKnownState.test.d.ts +2 -0
- package/dist/tests/PeerKnownState.test.d.ts.map +1 -0
- package/dist/tests/PeerKnownState.test.js +342 -0
- package/dist/tests/PeerKnownState.test.js.map +1 -0
- package/dist/tests/PeerState.test.js +17 -16
- package/dist/tests/PeerState.test.js.map +1 -1
- package/dist/tests/StorageApiAsync.test.js +12 -12
- package/dist/tests/StorageApiAsync.test.js.map +1 -1
- package/dist/tests/StorageApiSync.test.js +11 -11
- package/dist/tests/StorageApiSync.test.js.map +1 -1
- package/dist/tests/SyncStateManager.test.js +16 -21
- package/dist/tests/SyncStateManager.test.js.map +1 -1
- package/dist/tests/coValueCore.dependencies.test.js +59 -0
- package/dist/tests/coValueCore.dependencies.test.js.map +1 -1
- package/dist/tests/coValueCore.test.js +41 -21
- package/dist/tests/coValueCore.test.js.map +1 -1
- package/dist/tests/group.addMember.test.js +266 -219
- package/dist/tests/group.addMember.test.js.map +1 -1
- package/dist/tests/group.inheritance.test.js +12 -0
- package/dist/tests/group.inheritance.test.js.map +1 -1
- package/dist/tests/group.invite.test.js +77 -0
- package/dist/tests/group.invite.test.js.map +1 -1
- package/dist/tests/group.removeMember.test.js +64 -7
- package/dist/tests/group.removeMember.test.js.map +1 -1
- package/dist/tests/group.roleOf.test.js +14 -4
- package/dist/tests/group.roleOf.test.js.map +1 -1
- package/dist/tests/permissions.test.js +51 -202
- package/dist/tests/permissions.test.js.map +1 -1
- package/dist/tests/sync.content.test.js +2 -2
- package/dist/tests/sync.content.test.js.map +1 -1
- package/dist/tests/sync.invite.test.js +6 -6
- package/dist/tests/sync.load.test.js +22 -22
- package/dist/tests/sync.mesh.test.js +9 -9
- package/dist/tests/sync.storage.test.js +13 -7
- package/dist/tests/sync.storage.test.js.map +1 -1
- package/dist/tests/sync.storageAsync.test.js +3 -3
- package/dist/tests/sync.test.js +13 -33
- package/dist/tests/sync.test.js.map +1 -1
- package/dist/tests/sync.upload.test.js +2 -2
- package/package.json +3 -3
- package/src/PeerState.ts +86 -34
- package/src/SyncStateManager.ts +25 -60
- package/src/coValueContentMessage.ts +3 -1
- package/src/coValueCore/PeerKnownState.ts +74 -0
- package/src/coValueCore/coValueCore.ts +180 -49
- package/src/coValueCore/decryptTransactionChangesAndMeta.ts +0 -6
- package/src/coValueCore/verifiedState.ts +2 -37
- package/src/coValues/coList.ts +7 -7
- package/src/coValues/coMap.ts +9 -7
- package/src/coValues/coStream.ts +6 -5
- package/src/coValues/group.ts +99 -60
- package/src/exports.ts +2 -1
- package/src/localNode.ts +7 -5
- package/src/permissions.ts +204 -123
- package/src/sync.ts +37 -53
- package/src/tests/PeerKnownState.test.ts +426 -0
- package/src/tests/PeerState.test.ts +24 -24
- package/src/tests/StorageApiAsync.test.ts +12 -12
- package/src/tests/StorageApiSync.test.ts +11 -11
- package/src/tests/SyncStateManager.test.ts +23 -53
- package/src/tests/coValueCore.dependencies.test.ts +87 -0
- package/src/tests/coValueCore.test.ts +64 -22
- package/src/tests/group.addMember.test.ts +384 -345
- package/src/tests/group.inheritance.test.ts +33 -0
- package/src/tests/group.invite.test.ts +117 -0
- package/src/tests/group.removeMember.test.ts +95 -9
- package/src/tests/group.roleOf.test.ts +16 -4
- package/src/tests/permissions.test.ts +56 -295
- package/src/tests/sync.content.test.ts +2 -2
- package/src/tests/sync.invite.test.ts +6 -6
- package/src/tests/sync.load.test.ts +22 -22
- package/src/tests/sync.mesh.test.ts +9 -9
- package/src/tests/sync.storage.test.ts +13 -8
- package/src/tests/sync.storageAsync.test.ts +3 -3
- package/src/tests/sync.test.ts +21 -50
- package/src/tests/sync.upload.test.ts +2 -2
- package/dist/PeerKnownStates.d.ts +0 -19
- package/dist/PeerKnownStates.d.ts.map +0 -1
- package/dist/PeerKnownStates.js +0 -64
- package/dist/PeerKnownStates.js.map +0 -1
- package/dist/tests/PeerKnownStates.test.d.ts +0 -2
- package/dist/tests/PeerKnownStates.test.d.ts.map +0 -1
- package/dist/tests/PeerKnownStates.test.js +0 -77
- package/dist/tests/PeerKnownStates.test.js.map +0 -1
- package/src/PeerKnownStates.ts +0 -93
- package/src/tests/PeerKnownStates.test.ts +0 -99
package/src/sync.ts
CHANGED
|
@@ -229,7 +229,7 @@ export class SyncManager {
|
|
|
229
229
|
}
|
|
230
230
|
|
|
231
231
|
const newContentPieces = coValue.verified.newContentSince(
|
|
232
|
-
peer.
|
|
232
|
+
peer.getOptimisticKnownState(id),
|
|
233
233
|
);
|
|
234
234
|
|
|
235
235
|
if (newContentPieces) {
|
|
@@ -306,8 +306,8 @@ export class SyncManager {
|
|
|
306
306
|
}
|
|
307
307
|
|
|
308
308
|
// Fill the missing known states with empty known states
|
|
309
|
-
if (!peer.
|
|
310
|
-
peer.
|
|
309
|
+
if (!peer.getKnownState(coValue.id)) {
|
|
310
|
+
peer.setKnownState(coValue.id, "empty");
|
|
311
311
|
}
|
|
312
312
|
}
|
|
313
313
|
|
|
@@ -342,20 +342,18 @@ export class SyncManager {
|
|
|
342
342
|
addPeer(peer: Peer, skipReconciliation: boolean = false) {
|
|
343
343
|
const prevPeer = this.peers[peer.id];
|
|
344
344
|
|
|
345
|
-
|
|
346
|
-
prevPeer.
|
|
347
|
-
|
|
345
|
+
const peerState = prevPeer
|
|
346
|
+
? prevPeer.newPeerStateFrom(peer)
|
|
347
|
+
: new PeerState(peer, undefined);
|
|
348
348
|
|
|
349
|
-
const peerState = new PeerState(peer, prevPeer?.knownStates);
|
|
350
349
|
this.peers[peer.id] = peerState;
|
|
351
350
|
|
|
352
351
|
this.peersCounter.add(1, { role: peer.role });
|
|
353
352
|
|
|
354
|
-
const unsubscribeFromKnownStatesUpdates =
|
|
355
|
-
(id) => {
|
|
356
|
-
this.syncState.triggerUpdate(peer.id, id);
|
|
357
|
-
}
|
|
358
|
-
);
|
|
353
|
+
const unsubscribeFromKnownStatesUpdates =
|
|
354
|
+
peerState.subscribeToKnownStatesUpdates((id, knownState) => {
|
|
355
|
+
this.syncState.triggerUpdate(peer.id, id, knownState.value());
|
|
356
|
+
});
|
|
359
357
|
|
|
360
358
|
if (!skipReconciliation && peerState.role === "server") {
|
|
361
359
|
void this.startPeerReconciliation(peerState);
|
|
@@ -450,7 +448,7 @@ export class SyncManager {
|
|
|
450
448
|
|
|
451
449
|
// The header is a boolean value that tells us if the other peer do have information about the header.
|
|
452
450
|
// If it's false in this point it means that the coValue is unavailable on the other peer.
|
|
453
|
-
const availableOnPeer = peer.
|
|
451
|
+
const availableOnPeer = peer.getOptimisticKnownState(msg.id)?.header;
|
|
454
452
|
|
|
455
453
|
if (!availableOnPeer) {
|
|
456
454
|
coValue.markNotFoundInPeer(peer.id);
|
|
@@ -598,7 +596,7 @@ export class SyncManager {
|
|
|
598
596
|
|
|
599
597
|
let invalidStateAssumed = false;
|
|
600
598
|
|
|
601
|
-
const
|
|
599
|
+
const validNewContent: NewContentMessage = {
|
|
602
600
|
action: "content",
|
|
603
601
|
id: msg.id,
|
|
604
602
|
priority: msg.priority,
|
|
@@ -667,13 +665,12 @@ export class SyncManager {
|
|
|
667
665
|
|
|
668
666
|
// The new content for this session has been verified, so we can store it
|
|
669
667
|
if (result.value) {
|
|
670
|
-
|
|
668
|
+
validNewContent.new[sessionID] = newContentForSession;
|
|
671
669
|
}
|
|
670
|
+
}
|
|
672
671
|
|
|
673
|
-
|
|
674
|
-
|
|
675
|
-
newContentForSession.newTransactions.length;
|
|
676
|
-
peer?.updateSessionCounter(msg.id, sessionID, transactionsCount);
|
|
672
|
+
if (peer) {
|
|
673
|
+
peer.combineWith(msg.id, knownStateFromContent(validNewContent));
|
|
677
674
|
}
|
|
678
675
|
|
|
679
676
|
/**
|
|
@@ -718,10 +715,10 @@ export class SyncManager {
|
|
|
718
715
|
* Store the content and propagate it to the server peers and the subscribed client peers
|
|
719
716
|
*/
|
|
720
717
|
const hasNewContent =
|
|
721
|
-
|
|
718
|
+
validNewContent.header || Object.keys(validNewContent.new).length > 0;
|
|
722
719
|
|
|
723
720
|
if (from !== "storage" && hasNewContent) {
|
|
724
|
-
this.storeContent(
|
|
721
|
+
this.storeContent(validNewContent);
|
|
725
722
|
}
|
|
726
723
|
|
|
727
724
|
for (const peer of this.getPeers(coValue.id)) {
|
|
@@ -729,11 +726,13 @@ export class SyncManager {
|
|
|
729
726
|
* We sync the content against the source peer if it is a client or server peers
|
|
730
727
|
* to upload any content that is available on the current node and not on the source peer.
|
|
731
728
|
*/
|
|
732
|
-
if (peer.closed)
|
|
733
|
-
|
|
729
|
+
if (peer.closed || coValue.isErroredInPeer(peer.id)) {
|
|
730
|
+
peer.emitCoValueChange(coValue.id);
|
|
731
|
+
continue;
|
|
732
|
+
}
|
|
734
733
|
|
|
735
734
|
// We directly forward the new content to peers that have an active subscription
|
|
736
|
-
if (peer.
|
|
735
|
+
if (peer.isCoValueSubscribedToPeer(coValue.id)) {
|
|
737
736
|
this.sendNewContent(coValue.id, peer);
|
|
738
737
|
syncedPeers.push(peer);
|
|
739
738
|
} else if (
|
|
@@ -756,13 +755,6 @@ export class SyncManager {
|
|
|
756
755
|
}
|
|
757
756
|
}
|
|
758
757
|
}
|
|
759
|
-
|
|
760
|
-
/**
|
|
761
|
-
* Send an update to all the sync state listeners
|
|
762
|
-
*/
|
|
763
|
-
for (const peer of syncedPeers) {
|
|
764
|
-
this.syncState.triggerUpdate(peer.id, coValue.id);
|
|
765
|
-
}
|
|
766
758
|
}
|
|
767
759
|
|
|
768
760
|
handleCorrection(msg: KnownStateMessage, peer: PeerState) {
|
|
@@ -786,27 +778,25 @@ export class SyncManager {
|
|
|
786
778
|
const contentKnownState = knownStateFromContent(content);
|
|
787
779
|
|
|
788
780
|
for (const peer of this.getPeers(coValue.id)) {
|
|
789
|
-
if (peer.closed) continue;
|
|
790
|
-
if (coValue.isErroredInPeer(peer.id)) continue;
|
|
791
|
-
|
|
792
781
|
// Only subscribed CoValues are synced to clients
|
|
793
782
|
if (
|
|
794
783
|
peer.role === "client" &&
|
|
795
|
-
!peer.
|
|
784
|
+
!peer.isCoValueSubscribedToPeer(coValue.id)
|
|
796
785
|
) {
|
|
797
786
|
continue;
|
|
798
787
|
}
|
|
799
788
|
|
|
789
|
+
if (peer.closed || coValue.isErroredInPeer(peer.id)) {
|
|
790
|
+
peer.emitCoValueChange(content.id);
|
|
791
|
+
continue;
|
|
792
|
+
}
|
|
793
|
+
|
|
800
794
|
// We assume that the peer already knows anything before this content
|
|
801
795
|
// Any eventual reconciliation will be handled through the known state messages exchange
|
|
802
796
|
this.trySendToPeer(peer, content);
|
|
803
797
|
peer.combineOptimisticWith(coValue.id, contentKnownState);
|
|
804
798
|
peer.trackToldKnownState(coValue.id);
|
|
805
799
|
}
|
|
806
|
-
|
|
807
|
-
for (const peer of this.getPeers(coValue.id)) {
|
|
808
|
-
this.syncState.triggerUpdate(peer.id, coValue.id);
|
|
809
|
-
}
|
|
810
800
|
}
|
|
811
801
|
|
|
812
802
|
private storeContent(content: NewContentMessage) {
|
|
@@ -837,15 +827,6 @@ export class SyncManager {
|
|
|
837
827
|
}
|
|
838
828
|
|
|
839
829
|
waitForSyncWithPeer(peerId: PeerID, id: RawCoID, timeout: number) {
|
|
840
|
-
const { syncState } = this;
|
|
841
|
-
const currentSyncState = syncState.getCurrentSyncState(peerId, id);
|
|
842
|
-
|
|
843
|
-
const isTheConditionAlreadyMet = currentSyncState.uploaded;
|
|
844
|
-
|
|
845
|
-
if (isTheConditionAlreadyMet) {
|
|
846
|
-
return;
|
|
847
|
-
}
|
|
848
|
-
|
|
849
830
|
const peerState = this.peers[peerId];
|
|
850
831
|
|
|
851
832
|
// The peer has been closed and is not persistent, so it isn't possible to sync
|
|
@@ -853,11 +834,14 @@ export class SyncManager {
|
|
|
853
834
|
return;
|
|
854
835
|
}
|
|
855
836
|
|
|
856
|
-
|
|
857
|
-
|
|
858
|
-
|
|
859
|
-
|
|
860
|
-
|
|
837
|
+
if (peerState.isCoValueSubscribedToPeer(id)) {
|
|
838
|
+
const isAlreadySynced = this.syncState.isSynced(peerState, id);
|
|
839
|
+
|
|
840
|
+
if (isAlreadySynced) {
|
|
841
|
+
return;
|
|
842
|
+
}
|
|
843
|
+
} else if (peerState.role === "client") {
|
|
844
|
+
// The client isn't subscribed to the coValue, so we won't sync it
|
|
861
845
|
return;
|
|
862
846
|
}
|
|
863
847
|
|
|
@@ -0,0 +1,426 @@
|
|
|
1
|
+
import { describe, expect, test } from "vitest";
|
|
2
|
+
import { PeerKnownState } from "../coValueCore/PeerKnownState.js";
|
|
3
|
+
import type { CoValueKnownState } from "../knownState.js";
|
|
4
|
+
import { cloneKnownState } from "../knownState.js";
|
|
5
|
+
import { RawCoID, SessionID } from "../ids.js";
|
|
6
|
+
import type { PeerID } from "../sync.js";
|
|
7
|
+
|
|
8
|
+
describe("PeerKnownState", () => {
|
|
9
|
+
const testId = "co_ztest123" as RawCoID;
|
|
10
|
+
const testPeerId = "peer123" as PeerID;
|
|
11
|
+
const session1 = "session1_session_z123" as SessionID;
|
|
12
|
+
const session2 = "session2_session_z456" as SessionID;
|
|
13
|
+
|
|
14
|
+
describe("constructor", () => {
|
|
15
|
+
test("should initialize with correct peerId and empty knownState", () => {
|
|
16
|
+
const peerKnownState = new PeerKnownState(testId, testPeerId);
|
|
17
|
+
|
|
18
|
+
expect(peerKnownState.peerId).toBe(testPeerId);
|
|
19
|
+
expect(peerKnownState.value()).toEqual({
|
|
20
|
+
id: testId,
|
|
21
|
+
header: false,
|
|
22
|
+
sessions: {},
|
|
23
|
+
});
|
|
24
|
+
expect(peerKnownState.optimisticValue()).toEqual({
|
|
25
|
+
id: testId,
|
|
26
|
+
header: false,
|
|
27
|
+
sessions: {},
|
|
28
|
+
});
|
|
29
|
+
});
|
|
30
|
+
|
|
31
|
+
test("should maintain knownState reference", () => {
|
|
32
|
+
const peerKnownState = new PeerKnownState(testId, testPeerId);
|
|
33
|
+
const initialKnownState = peerKnownState.value();
|
|
34
|
+
|
|
35
|
+
// Verify that the reference is preserved by checking that modifications
|
|
36
|
+
// to the returned object affect subsequent calls to value()
|
|
37
|
+
const state1 = peerKnownState.value();
|
|
38
|
+
const state2 = peerKnownState.value();
|
|
39
|
+
expect(state1).toBe(state2); // Same reference
|
|
40
|
+
expect(state1).toBe(initialKnownState); // Same as initial reference
|
|
41
|
+
});
|
|
42
|
+
});
|
|
43
|
+
|
|
44
|
+
describe("updateHeader", () => {
|
|
45
|
+
test("should update header in knownState when no optimistic state exists", () => {
|
|
46
|
+
const peerKnownState = new PeerKnownState(testId, testPeerId);
|
|
47
|
+
const initialKnownState = peerKnownState.value();
|
|
48
|
+
|
|
49
|
+
peerKnownState.updateHeader(true);
|
|
50
|
+
|
|
51
|
+
expect(peerKnownState.value().header).toBe(true);
|
|
52
|
+
expect(peerKnownState.value()).toBe(initialKnownState); // Reference preserved
|
|
53
|
+
});
|
|
54
|
+
|
|
55
|
+
test("should update header in both knownState and optimisticKnownState", () => {
|
|
56
|
+
const peerKnownState = new PeerKnownState(testId, testPeerId);
|
|
57
|
+
|
|
58
|
+
// Create optimistic state first
|
|
59
|
+
peerKnownState.combineOptimisticWith({
|
|
60
|
+
id: testId,
|
|
61
|
+
header: false,
|
|
62
|
+
sessions: { [session1]: 5 },
|
|
63
|
+
});
|
|
64
|
+
|
|
65
|
+
const knownStateRef = peerKnownState.value();
|
|
66
|
+
const optimisticStateRef = peerKnownState.optimisticValue();
|
|
67
|
+
|
|
68
|
+
peerKnownState.updateHeader(true);
|
|
69
|
+
|
|
70
|
+
expect(peerKnownState.value().header).toBe(true);
|
|
71
|
+
expect(peerKnownState.optimisticValue().header).toBe(true);
|
|
72
|
+
expect(peerKnownState.value()).toBe(knownStateRef); // Reference preserved
|
|
73
|
+
expect(peerKnownState.optimisticValue()).toBe(optimisticStateRef); // Reference preserved
|
|
74
|
+
});
|
|
75
|
+
|
|
76
|
+
test("should update header to false", () => {
|
|
77
|
+
const peerKnownState = new PeerKnownState(testId, testPeerId);
|
|
78
|
+
peerKnownState.updateHeader(true);
|
|
79
|
+
|
|
80
|
+
peerKnownState.updateHeader(false);
|
|
81
|
+
|
|
82
|
+
expect(peerKnownState.value().header).toBe(false);
|
|
83
|
+
expect(peerKnownState.optimisticValue().header).toBe(false);
|
|
84
|
+
});
|
|
85
|
+
});
|
|
86
|
+
|
|
87
|
+
describe("combineWith", () => {
|
|
88
|
+
test("should combine sessions and header correctly", () => {
|
|
89
|
+
const peerKnownState = new PeerKnownState(testId, testPeerId);
|
|
90
|
+
const initialKnownState = peerKnownState.value();
|
|
91
|
+
|
|
92
|
+
const toCombine: CoValueKnownState = {
|
|
93
|
+
id: testId,
|
|
94
|
+
header: true,
|
|
95
|
+
sessions: { [session1]: 5, [session2]: 10 },
|
|
96
|
+
};
|
|
97
|
+
|
|
98
|
+
peerKnownState.combineWith(toCombine);
|
|
99
|
+
|
|
100
|
+
expect(peerKnownState.value()).toEqual({
|
|
101
|
+
id: testId,
|
|
102
|
+
header: true,
|
|
103
|
+
sessions: { [session1]: 5, [session2]: 10 },
|
|
104
|
+
});
|
|
105
|
+
expect(peerKnownState.value()).toBe(initialKnownState); // Reference preserved
|
|
106
|
+
});
|
|
107
|
+
|
|
108
|
+
test("should update existing sessions with higher values", () => {
|
|
109
|
+
const peerKnownState = new PeerKnownState(testId, testPeerId);
|
|
110
|
+
const initialKnownState = peerKnownState.value();
|
|
111
|
+
|
|
112
|
+
// Set initial state
|
|
113
|
+
peerKnownState.combineWith({
|
|
114
|
+
id: testId,
|
|
115
|
+
header: false,
|
|
116
|
+
sessions: { [session1]: 3, [session2]: 5 },
|
|
117
|
+
});
|
|
118
|
+
|
|
119
|
+
// Combine with higher values for session1, lower for session2
|
|
120
|
+
peerKnownState.combineWith({
|
|
121
|
+
id: testId,
|
|
122
|
+
header: true,
|
|
123
|
+
sessions: { [session1]: 7, [session2]: 2 },
|
|
124
|
+
});
|
|
125
|
+
|
|
126
|
+
expect(peerKnownState.value().sessions[session1]).toBe(7); // Updated to higher value
|
|
127
|
+
expect(peerKnownState.value().sessions[session2]).toBe(5); // Kept higher existing value
|
|
128
|
+
expect(peerKnownState.value()).toBe(initialKnownState); // Reference preserved
|
|
129
|
+
});
|
|
130
|
+
|
|
131
|
+
test("should update optimisticKnownState when it exists", () => {
|
|
132
|
+
const peerKnownState = new PeerKnownState(testId, testPeerId);
|
|
133
|
+
|
|
134
|
+
// Create optimistic state
|
|
135
|
+
peerKnownState.combineOptimisticWith({
|
|
136
|
+
id: testId,
|
|
137
|
+
header: false,
|
|
138
|
+
sessions: { [session1]: 3 },
|
|
139
|
+
});
|
|
140
|
+
|
|
141
|
+
const optimisticStateRef = peerKnownState.optimisticValue();
|
|
142
|
+
|
|
143
|
+
peerKnownState.combineWith({
|
|
144
|
+
id: testId,
|
|
145
|
+
header: true,
|
|
146
|
+
sessions: { [session2]: 10 },
|
|
147
|
+
});
|
|
148
|
+
|
|
149
|
+
expect(peerKnownState.optimisticValue().sessions[session1]).toBe(3); // From optimistic state
|
|
150
|
+
expect(peerKnownState.optimisticValue().sessions[session2]).toBe(10); // Combined
|
|
151
|
+
expect(peerKnownState.optimisticValue().header).toBe(true); // Combined header
|
|
152
|
+
expect(peerKnownState.optimisticValue()).toBe(optimisticStateRef); // Reference preserved
|
|
153
|
+
});
|
|
154
|
+
});
|
|
155
|
+
|
|
156
|
+
describe("combineOptimisticWith", () => {
|
|
157
|
+
test("should create optimisticKnownState when none exists", () => {
|
|
158
|
+
const peerKnownState = new PeerKnownState(testId, testPeerId);
|
|
159
|
+
const initialKnownState = peerKnownState.value();
|
|
160
|
+
|
|
161
|
+
const toCombine: CoValueKnownState = {
|
|
162
|
+
id: testId,
|
|
163
|
+
header: true,
|
|
164
|
+
sessions: { [session1]: 5 },
|
|
165
|
+
};
|
|
166
|
+
|
|
167
|
+
peerKnownState.combineOptimisticWith(toCombine);
|
|
168
|
+
|
|
169
|
+
expect(peerKnownState.optimisticValue()).toEqual({
|
|
170
|
+
id: testId,
|
|
171
|
+
header: true,
|
|
172
|
+
sessions: { [session1]: 5 },
|
|
173
|
+
});
|
|
174
|
+
// Should be a different object from knownState
|
|
175
|
+
expect(peerKnownState.optimisticValue()).not.toBe(peerKnownState.value());
|
|
176
|
+
// Should not affect the known state
|
|
177
|
+
expect(peerKnownState.value()).toEqual(initialKnownState);
|
|
178
|
+
});
|
|
179
|
+
|
|
180
|
+
test("should combine with existing optimisticKnownState", () => {
|
|
181
|
+
const peerKnownState = new PeerKnownState(testId, testPeerId);
|
|
182
|
+
|
|
183
|
+
// Create initial optimistic state
|
|
184
|
+
peerKnownState.combineOptimisticWith({
|
|
185
|
+
id: testId,
|
|
186
|
+
header: false,
|
|
187
|
+
sessions: { [session1]: 3 },
|
|
188
|
+
});
|
|
189
|
+
|
|
190
|
+
const optimisticStateRef = peerKnownState.optimisticValue();
|
|
191
|
+
|
|
192
|
+
// Combine with additional state
|
|
193
|
+
peerKnownState.combineOptimisticWith({
|
|
194
|
+
id: testId,
|
|
195
|
+
header: true,
|
|
196
|
+
sessions: { [session2]: 7 },
|
|
197
|
+
});
|
|
198
|
+
|
|
199
|
+
expect(peerKnownState.optimisticValue()).toEqual({
|
|
200
|
+
id: testId,
|
|
201
|
+
header: true,
|
|
202
|
+
sessions: { [session1]: 3, [session2]: 7 },
|
|
203
|
+
});
|
|
204
|
+
expect(peerKnownState.optimisticValue()).toBe(optimisticStateRef); // Reference preserved
|
|
205
|
+
});
|
|
206
|
+
|
|
207
|
+
test("should preserve knownState when combining optimistic", () => {
|
|
208
|
+
const peerKnownState = new PeerKnownState(testId, testPeerId);
|
|
209
|
+
const initialKnownState = peerKnownState.value();
|
|
210
|
+
|
|
211
|
+
peerKnownState.combineOptimisticWith({
|
|
212
|
+
id: testId,
|
|
213
|
+
header: true,
|
|
214
|
+
sessions: { [session1]: 10 },
|
|
215
|
+
});
|
|
216
|
+
|
|
217
|
+
expect(peerKnownState.value()).toBe(initialKnownState); // Reference preserved
|
|
218
|
+
expect(peerKnownState.value().sessions).toEqual({}); // Known state unchanged
|
|
219
|
+
});
|
|
220
|
+
});
|
|
221
|
+
|
|
222
|
+
describe("set", () => {
|
|
223
|
+
test("should set knownState with provided CoValueKnownState", () => {
|
|
224
|
+
const peerKnownState = new PeerKnownState(testId, testPeerId);
|
|
225
|
+
const initialKnownState = peerKnownState.value();
|
|
226
|
+
|
|
227
|
+
// Create optimistic state first
|
|
228
|
+
peerKnownState.combineOptimisticWith({
|
|
229
|
+
id: testId,
|
|
230
|
+
header: false,
|
|
231
|
+
sessions: { [session1]: 5 },
|
|
232
|
+
});
|
|
233
|
+
|
|
234
|
+
const newState: CoValueKnownState = {
|
|
235
|
+
id: testId,
|
|
236
|
+
header: true,
|
|
237
|
+
sessions: { [session1]: 10, [session2]: 20 },
|
|
238
|
+
};
|
|
239
|
+
|
|
240
|
+
peerKnownState.set(newState);
|
|
241
|
+
|
|
242
|
+
expect(peerKnownState.value()).toEqual(newState);
|
|
243
|
+
expect(peerKnownState.value()).toBe(initialKnownState); // Reference preserved
|
|
244
|
+
expect(peerKnownState.optimisticValue()).toBe(peerKnownState.value()); // Should return knownState when optimistic is cleared
|
|
245
|
+
});
|
|
246
|
+
|
|
247
|
+
test("should handle shallow copy of sessions in set", () => {
|
|
248
|
+
const peerKnownState = new PeerKnownState(testId, testPeerId);
|
|
249
|
+
const initialKnownState = peerKnownState.value();
|
|
250
|
+
|
|
251
|
+
const sessions = { [session1]: 5, [session2]: 10 };
|
|
252
|
+
const newState: CoValueKnownState = {
|
|
253
|
+
id: testId,
|
|
254
|
+
header: true,
|
|
255
|
+
sessions,
|
|
256
|
+
};
|
|
257
|
+
|
|
258
|
+
peerKnownState.set(newState);
|
|
259
|
+
|
|
260
|
+
expect(peerKnownState.value().sessions).toEqual(sessions);
|
|
261
|
+
expect(peerKnownState.value().sessions).not.toBe(sessions); // Should be a copy
|
|
262
|
+
expect(peerKnownState.value()).toBe(initialKnownState); // Reference preserved
|
|
263
|
+
});
|
|
264
|
+
|
|
265
|
+
test("should set empty state when payload is 'empty'", () => {
|
|
266
|
+
const peerKnownState = new PeerKnownState(testId, testPeerId);
|
|
267
|
+
|
|
268
|
+
// Set some initial state
|
|
269
|
+
peerKnownState.combineWith({
|
|
270
|
+
id: testId,
|
|
271
|
+
header: true,
|
|
272
|
+
sessions: { [session1]: 5 },
|
|
273
|
+
});
|
|
274
|
+
|
|
275
|
+
// Create optimistic state
|
|
276
|
+
peerKnownState.combineOptimisticWith({
|
|
277
|
+
id: testId,
|
|
278
|
+
header: false,
|
|
279
|
+
sessions: { [session2]: 10 },
|
|
280
|
+
});
|
|
281
|
+
|
|
282
|
+
const initialKnownState = peerKnownState.value();
|
|
283
|
+
peerKnownState.set("empty");
|
|
284
|
+
|
|
285
|
+
expect(peerKnownState.value()).toEqual({
|
|
286
|
+
id: testId,
|
|
287
|
+
header: false,
|
|
288
|
+
sessions: {},
|
|
289
|
+
});
|
|
290
|
+
expect(peerKnownState.value()).toBe(initialKnownState); // Reference preserved
|
|
291
|
+
expect(peerKnownState.optimisticValue()).toBe(peerKnownState.value()); // Should return knownState when optimistic is cleared
|
|
292
|
+
});
|
|
293
|
+
|
|
294
|
+
test("should clear optimisticKnownState on set", () => {
|
|
295
|
+
const peerKnownState = new PeerKnownState(testId, testPeerId);
|
|
296
|
+
|
|
297
|
+
// Create optimistic state
|
|
298
|
+
peerKnownState.combineOptimisticWith({
|
|
299
|
+
id: testId,
|
|
300
|
+
header: true,
|
|
301
|
+
sessions: { [session1]: 5 },
|
|
302
|
+
});
|
|
303
|
+
|
|
304
|
+
expect(peerKnownState.optimisticValue()).toBeDefined();
|
|
305
|
+
expect(peerKnownState.optimisticValue()).not.toBe(peerKnownState.value());
|
|
306
|
+
|
|
307
|
+
peerKnownState.set("empty");
|
|
308
|
+
|
|
309
|
+
expect(peerKnownState.optimisticValue()).toBe(peerKnownState.value()); // Should return knownState when optimistic is cleared
|
|
310
|
+
});
|
|
311
|
+
});
|
|
312
|
+
|
|
313
|
+
describe("value", () => {
|
|
314
|
+
test("should return the current knownState", () => {
|
|
315
|
+
const peerKnownState = new PeerKnownState(testId, testPeerId);
|
|
316
|
+
const state1 = peerKnownState.value();
|
|
317
|
+
const state2 = peerKnownState.value();
|
|
318
|
+
|
|
319
|
+
expect(state1).toBe(state2); // Same reference
|
|
320
|
+
expect(state1).toEqual({
|
|
321
|
+
id: testId,
|
|
322
|
+
header: false,
|
|
323
|
+
sessions: {},
|
|
324
|
+
});
|
|
325
|
+
});
|
|
326
|
+
|
|
327
|
+
test("should return updated knownState after modifications", () => {
|
|
328
|
+
const peerKnownState = new PeerKnownState(testId, testPeerId);
|
|
329
|
+
|
|
330
|
+
peerKnownState.combineWith({
|
|
331
|
+
id: testId,
|
|
332
|
+
header: true,
|
|
333
|
+
sessions: { [session1]: 5 },
|
|
334
|
+
});
|
|
335
|
+
|
|
336
|
+
expect(peerKnownState.value()).toEqual({
|
|
337
|
+
id: testId,
|
|
338
|
+
header: true,
|
|
339
|
+
sessions: { [session1]: 5 },
|
|
340
|
+
});
|
|
341
|
+
});
|
|
342
|
+
});
|
|
343
|
+
|
|
344
|
+
describe("optimisticValue", () => {
|
|
345
|
+
test("should return knownState when no optimistic state exists", () => {
|
|
346
|
+
const peerKnownState = new PeerKnownState(testId, testPeerId);
|
|
347
|
+
|
|
348
|
+
expect(peerKnownState.optimisticValue()).toBe(peerKnownState.value());
|
|
349
|
+
});
|
|
350
|
+
|
|
351
|
+
test("should return optimisticKnownState when it exists", () => {
|
|
352
|
+
const peerKnownState = new PeerKnownState(testId, testPeerId);
|
|
353
|
+
|
|
354
|
+
peerKnownState.combineOptimisticWith({
|
|
355
|
+
id: testId,
|
|
356
|
+
header: true,
|
|
357
|
+
sessions: { [session1]: 5 },
|
|
358
|
+
});
|
|
359
|
+
|
|
360
|
+
const optimisticState = peerKnownState.optimisticValue();
|
|
361
|
+
expect(optimisticState).not.toBe(peerKnownState.value());
|
|
362
|
+
expect(optimisticState).toEqual({
|
|
363
|
+
id: testId,
|
|
364
|
+
header: true,
|
|
365
|
+
sessions: { [session1]: 5 },
|
|
366
|
+
});
|
|
367
|
+
});
|
|
368
|
+
|
|
369
|
+
test("should maintain reference consistency", () => {
|
|
370
|
+
const peerKnownState = new PeerKnownState(testId, testPeerId);
|
|
371
|
+
|
|
372
|
+
peerKnownState.combineOptimisticWith({
|
|
373
|
+
id: testId,
|
|
374
|
+
header: true,
|
|
375
|
+
sessions: { [session1]: 5 },
|
|
376
|
+
});
|
|
377
|
+
|
|
378
|
+
const optimisticState1 = peerKnownState.optimisticValue();
|
|
379
|
+
const optimisticState2 = peerKnownState.optimisticValue();
|
|
380
|
+
|
|
381
|
+
expect(optimisticState1).toBe(optimisticState2); // Same reference
|
|
382
|
+
});
|
|
383
|
+
});
|
|
384
|
+
|
|
385
|
+
describe("integration scenarios", () => {
|
|
386
|
+
test("should handle complex workflow maintaining references", () => {
|
|
387
|
+
const peerKnownState = new PeerKnownState(testId, testPeerId);
|
|
388
|
+
const originalKnownState = peerKnownState.value();
|
|
389
|
+
|
|
390
|
+
// Initial state setup
|
|
391
|
+
peerKnownState.updateHeader(true);
|
|
392
|
+
expect(peerKnownState.value()).toBe(originalKnownState);
|
|
393
|
+
|
|
394
|
+
// Combine with some sessions
|
|
395
|
+
peerKnownState.combineWith({
|
|
396
|
+
id: testId,
|
|
397
|
+
header: false,
|
|
398
|
+
sessions: { [session1]: 3, [session2]: 7 },
|
|
399
|
+
});
|
|
400
|
+
expect(peerKnownState.value()).toBe(originalKnownState);
|
|
401
|
+
|
|
402
|
+
// Create optimistic state
|
|
403
|
+
peerKnownState.combineOptimisticWith({
|
|
404
|
+
id: testId,
|
|
405
|
+
header: true,
|
|
406
|
+
sessions: { [session2]: 5 },
|
|
407
|
+
});
|
|
408
|
+
|
|
409
|
+
const originalOptimisticState = peerKnownState.optimisticValue();
|
|
410
|
+
|
|
411
|
+
// Update header should affect both states
|
|
412
|
+
peerKnownState.updateHeader(false);
|
|
413
|
+
expect(peerKnownState.value()).toBe(originalKnownState);
|
|
414
|
+
expect(peerKnownState.optimisticValue()).toBe(originalOptimisticState);
|
|
415
|
+
|
|
416
|
+
// Set should clear optimistic and update known
|
|
417
|
+
peerKnownState.set({
|
|
418
|
+
id: testId,
|
|
419
|
+
header: true,
|
|
420
|
+
sessions: { [session1]: 20 },
|
|
421
|
+
});
|
|
422
|
+
expect(peerKnownState.value()).toBe(originalKnownState);
|
|
423
|
+
expect(peerKnownState.optimisticValue()).toBe(peerKnownState.value()); // Should return knownState when optimistic is cleared
|
|
424
|
+
});
|
|
425
|
+
});
|
|
426
|
+
});
|
|
@@ -44,7 +44,7 @@ describe("PeerState", () => {
|
|
|
44
44
|
expect(peerState.closed).toBe(true);
|
|
45
45
|
});
|
|
46
46
|
|
|
47
|
-
test("should clone the knownStates
|
|
47
|
+
test("should clone the knownStates and reset the optimistic known state when using newPeerStateFrom", () => {
|
|
48
48
|
const { peerState, mockPeer } = setup();
|
|
49
49
|
peerState.setKnownState("co_z1", {
|
|
50
50
|
id: "co_z1",
|
|
@@ -52,31 +52,40 @@ describe("PeerState", () => {
|
|
|
52
52
|
sessions: {},
|
|
53
53
|
});
|
|
54
54
|
|
|
55
|
-
|
|
55
|
+
peerState.combineOptimisticWith("co_z1", {
|
|
56
|
+
id: "co_z1",
|
|
57
|
+
header: true,
|
|
58
|
+
sessions: {
|
|
59
|
+
"session-1": 1,
|
|
60
|
+
} as KnownStateSessions,
|
|
61
|
+
});
|
|
62
|
+
|
|
63
|
+
const newPeerState = peerState.newPeerStateFrom(mockPeer);
|
|
56
64
|
|
|
57
|
-
expect(newPeerState.
|
|
58
|
-
|
|
65
|
+
expect(newPeerState.getKnownState("co_z1")).toEqual(
|
|
66
|
+
peerState.getKnownState("co_z1"),
|
|
67
|
+
);
|
|
68
|
+
expect(newPeerState.getKnownState("co_z1")).not.toBe(
|
|
69
|
+
peerState.getKnownState("co_z1"),
|
|
70
|
+
);
|
|
71
|
+
expect(newPeerState.getOptimisticKnownState("co_z1")).toBe(
|
|
72
|
+
newPeerState.getKnownState("co_z1"),
|
|
73
|
+
);
|
|
59
74
|
});
|
|
60
75
|
|
|
61
76
|
test("should dispatch to both states", () => {
|
|
62
77
|
const { peerState } = setup();
|
|
63
|
-
const knownStatesSpy = vi.spyOn(peerState._knownStates, "set");
|
|
64
|
-
|
|
65
|
-
const optimisticKnownStatesSpy = vi.spyOn(
|
|
66
|
-
peerState._optimisticKnownStates,
|
|
67
|
-
"set",
|
|
68
|
-
);
|
|
69
78
|
|
|
70
79
|
const state: CoValueKnownState = {
|
|
71
80
|
id: "co_z1",
|
|
72
|
-
header:
|
|
81
|
+
header: true,
|
|
73
82
|
sessions: {},
|
|
74
83
|
};
|
|
75
84
|
|
|
76
85
|
peerState.setKnownState("co_z1", state);
|
|
77
86
|
|
|
78
|
-
expect(
|
|
79
|
-
expect(
|
|
87
|
+
expect(peerState.getKnownState("co_z1")).toEqual(state);
|
|
88
|
+
expect(peerState.getOptimisticKnownState("co_z1")).toEqual(state);
|
|
80
89
|
});
|
|
81
90
|
|
|
82
91
|
test("dispatching an optimistic update should not affect the known states", () => {
|
|
@@ -100,16 +109,7 @@ describe("PeerState", () => {
|
|
|
100
109
|
|
|
101
110
|
peerState.combineOptimisticWith("co_z1", optimisticState);
|
|
102
111
|
|
|
103
|
-
expect(peerState.
|
|
104
|
-
expect(peerState.
|
|
105
|
-
optimisticState,
|
|
106
|
-
);
|
|
107
|
-
});
|
|
108
|
-
|
|
109
|
-
test("should use separate references for knownStates and optimisticKnownStates for non-storage peers", () => {
|
|
110
|
-
const { peerState } = setup(); // Uses a regular peer
|
|
111
|
-
|
|
112
|
-
// Verify they are different references
|
|
113
|
-
expect(peerState.knownStates).not.toBe(peerState.optimisticKnownStates);
|
|
112
|
+
expect(peerState.getKnownState("co_z1")).not.toEqual(optimisticState);
|
|
113
|
+
expect(peerState.getOptimisticKnownState("co_z1")).toEqual(optimisticState);
|
|
114
114
|
});
|
|
115
115
|
});
|