cojson 0.13.18 → 0.13.20
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 +9 -0
- package/dist/PeerState.d.ts +1 -1
- package/dist/PeerState.d.ts.map +1 -1
- package/dist/PeerState.js +7 -36
- package/dist/PeerState.js.map +1 -1
- package/dist/coValueCore/coValueCore.d.ts +1 -1
- package/dist/coValueCore/coValueCore.d.ts.map +1 -1
- package/dist/coValueCore/coValueCore.js +11 -7
- package/dist/coValueCore/coValueCore.js.map +1 -1
- package/dist/localNode.d.ts.map +1 -1
- package/dist/localNode.js +10 -5
- package/dist/localNode.js.map +1 -1
- package/dist/streamUtils.d.ts +5 -5
- package/dist/streamUtils.d.ts.map +1 -1
- package/dist/streamUtils.js +5 -20
- package/dist/streamUtils.js.map +1 -1
- package/dist/sync.d.ts +6 -4
- package/dist/sync.d.ts.map +1 -1
- package/dist/sync.js +35 -19
- package/dist/sync.js.map +1 -1
- package/dist/tests/PeerState.test.js +0 -31
- package/dist/tests/PeerState.test.js.map +1 -1
- package/dist/tests/SyncStateManager.test.js +41 -6
- package/dist/tests/SyncStateManager.test.js.map +1 -1
- package/dist/tests/account.test.js +16 -0
- package/dist/tests/account.test.js.map +1 -1
- package/dist/tests/group.test.js.map +1 -1
- package/dist/tests/sync.auth.test.js +64 -15
- package/dist/tests/sync.auth.test.js.map +1 -1
- package/dist/tests/sync.load.test.js +2 -2
- package/dist/tests/sync.load.test.js.map +1 -1
- package/dist/tests/testUtils.d.ts +11 -2
- package/dist/tests/testUtils.d.ts.map +1 -1
- package/dist/tests/testUtils.js +27 -30
- package/dist/tests/testUtils.js.map +1 -1
- package/package.json +1 -1
- package/src/PeerState.ts +8 -40
- package/src/coValueCore/coValueCore.ts +14 -14
- package/src/localNode.ts +13 -6
- package/src/streamUtils.ts +7 -34
- package/src/sync.ts +51 -22
- package/src/tests/PeerState.test.ts +0 -37
- package/src/tests/SyncStateManager.test.ts +56 -6
- package/src/tests/account.test.ts +24 -0
- package/src/tests/group.test.ts +0 -1
- package/src/tests/sync.auth.test.ts +79 -21
- package/src/tests/sync.load.test.ts +3 -2
- package/src/tests/testUtils.ts +35 -34
package/package.json
CHANGED
package/src/PeerState.ts
CHANGED
|
@@ -23,15 +23,7 @@ export class PeerState {
|
|
|
23
23
|
});
|
|
24
24
|
|
|
25
25
|
this._knownStates = knownStates?.clone() ?? new PeerKnownStates();
|
|
26
|
-
|
|
27
|
-
// We assume that exchanges with storage peers are always successful
|
|
28
|
-
// hence we don't need to differentiate between knownStates and optimisticKnownStates
|
|
29
|
-
if (peer.role === "storage") {
|
|
30
|
-
this._optimisticKnownStates = "assumeInfallible";
|
|
31
|
-
} else {
|
|
32
|
-
this._optimisticKnownStates =
|
|
33
|
-
knownStates?.clone() ?? new PeerKnownStates();
|
|
34
|
-
}
|
|
26
|
+
this._optimisticKnownStates = knownStates?.clone() ?? new PeerKnownStates();
|
|
35
27
|
}
|
|
36
28
|
|
|
37
29
|
/**
|
|
@@ -52,13 +44,9 @@ export class PeerState {
|
|
|
52
44
|
* The main difference with knownState is that this is updated when the content is sent to the peer without
|
|
53
45
|
* waiting for any acknowledgement from the peer.
|
|
54
46
|
*/
|
|
55
|
-
readonly _optimisticKnownStates: PeerKnownStates
|
|
47
|
+
readonly _optimisticKnownStates: PeerKnownStates;
|
|
56
48
|
|
|
57
49
|
get optimisticKnownStates(): ReadonlyPeerKnownStates {
|
|
58
|
-
if (this._optimisticKnownStates === "assumeInfallible") {
|
|
59
|
-
return this.knownStates;
|
|
60
|
-
}
|
|
61
|
-
|
|
62
50
|
return this._optimisticKnownStates;
|
|
63
51
|
}
|
|
64
52
|
|
|
@@ -76,53 +64,33 @@ export class PeerState {
|
|
|
76
64
|
|
|
77
65
|
updateHeader(id: RawCoID, header: boolean) {
|
|
78
66
|
this._knownStates.updateHeader(id, header);
|
|
79
|
-
|
|
80
|
-
if (this._optimisticKnownStates !== "assumeInfallible") {
|
|
81
|
-
this._optimisticKnownStates.updateHeader(id, header);
|
|
82
|
-
}
|
|
67
|
+
this._optimisticKnownStates.updateHeader(id, header);
|
|
83
68
|
}
|
|
84
69
|
|
|
85
70
|
combineWith(id: RawCoID, value: CoValueKnownState) {
|
|
86
71
|
this._knownStates.combineWith(id, value);
|
|
87
|
-
|
|
88
|
-
if (this._optimisticKnownStates !== "assumeInfallible") {
|
|
89
|
-
this._optimisticKnownStates.combineWith(id, value);
|
|
90
|
-
}
|
|
72
|
+
this._optimisticKnownStates.combineWith(id, value);
|
|
91
73
|
}
|
|
92
74
|
|
|
93
75
|
combineOptimisticWith(id: RawCoID, value: CoValueKnownState) {
|
|
94
|
-
|
|
95
|
-
this._knownStates.combineWith(id, value);
|
|
96
|
-
} else {
|
|
97
|
-
this._optimisticKnownStates.combineWith(id, value);
|
|
98
|
-
}
|
|
76
|
+
this._optimisticKnownStates.combineWith(id, value);
|
|
99
77
|
}
|
|
100
78
|
|
|
101
79
|
updateSessionCounter(id: RawCoID, sessionId: SessionID, value: number) {
|
|
102
80
|
this._knownStates.updateSessionCounter(id, sessionId, value);
|
|
103
|
-
|
|
104
|
-
if (this._optimisticKnownStates !== "assumeInfallible") {
|
|
105
|
-
this._optimisticKnownStates.updateSessionCounter(id, sessionId, value);
|
|
106
|
-
}
|
|
81
|
+
this._optimisticKnownStates.updateSessionCounter(id, sessionId, value);
|
|
107
82
|
}
|
|
108
83
|
|
|
109
84
|
setKnownState(id: RawCoID, knownState: CoValueKnownState | "empty") {
|
|
110
85
|
this._knownStates.set(id, knownState);
|
|
111
|
-
|
|
112
|
-
if (this._optimisticKnownStates !== "assumeInfallible") {
|
|
113
|
-
this._optimisticKnownStates.set(id, knownState);
|
|
114
|
-
}
|
|
86
|
+
this._optimisticKnownStates.set(id, knownState);
|
|
115
87
|
}
|
|
116
88
|
|
|
117
89
|
setOptimisticKnownState(
|
|
118
90
|
id: RawCoID,
|
|
119
91
|
knownState: CoValueKnownState | "empty",
|
|
120
92
|
) {
|
|
121
|
-
|
|
122
|
-
this._knownStates.set(id, knownState);
|
|
123
|
-
} else {
|
|
124
|
-
this._optimisticKnownStates.set(id, knownState);
|
|
125
|
-
}
|
|
93
|
+
this._optimisticKnownStates.set(id, knownState);
|
|
126
94
|
}
|
|
127
95
|
|
|
128
96
|
get id() {
|
|
@@ -2,11 +2,7 @@ import { UpDownCounter, ValueType, metrics } from "@opentelemetry/api";
|
|
|
2
2
|
import { Result, err } from "neverthrow";
|
|
3
3
|
import { PeerState } from "../PeerState.js";
|
|
4
4
|
import { RawCoValue } from "../coValue.js";
|
|
5
|
-
import {
|
|
6
|
-
ControlledAccount,
|
|
7
|
-
ControlledAccountOrAgent,
|
|
8
|
-
RawAccountID,
|
|
9
|
-
} from "../coValues/account.js";
|
|
5
|
+
import { ControlledAccountOrAgent, RawAccountID } from "../coValues/account.js";
|
|
10
6
|
import { RawGroup } from "../coValues/group.js";
|
|
11
7
|
import { coreToCoValue } from "../coreToCoValue.js";
|
|
12
8
|
import {
|
|
@@ -656,15 +652,15 @@ export class CoValueCore {
|
|
|
656
652
|
a: Pick<DecryptedTransaction, "madeAt" | "txID">,
|
|
657
653
|
b: Pick<DecryptedTransaction, "madeAt" | "txID">,
|
|
658
654
|
) {
|
|
659
|
-
|
|
660
|
-
a.madeAt - b.madeAt
|
|
661
|
-
|
|
662
|
-
|
|
663
|
-
|
|
664
|
-
|
|
665
|
-
|
|
666
|
-
|
|
667
|
-
|
|
655
|
+
if (a.madeAt !== b.madeAt) {
|
|
656
|
+
return a.madeAt - b.madeAt;
|
|
657
|
+
}
|
|
658
|
+
|
|
659
|
+
if (a.txID.sessionID === b.txID.sessionID) {
|
|
660
|
+
return a.txID.txIndex - b.txID.txIndex;
|
|
661
|
+
}
|
|
662
|
+
|
|
663
|
+
return 0;
|
|
668
664
|
}
|
|
669
665
|
|
|
670
666
|
getCurrentReadKey(): {
|
|
@@ -1006,6 +1002,10 @@ export class CoValueCore {
|
|
|
1006
1002
|
const waitingForPeer = new Promise<void>((resolve) => {
|
|
1007
1003
|
const markNotFound = () => {
|
|
1008
1004
|
if (this.peers.get(peer.id)?.type === "pending") {
|
|
1005
|
+
logger.warn("Timeout waiting for peer to load coValue", {
|
|
1006
|
+
id: this.id,
|
|
1007
|
+
peerID: peer.id,
|
|
1008
|
+
});
|
|
1009
1009
|
this.markNotFoundInPeer(peer.id);
|
|
1010
1010
|
}
|
|
1011
1011
|
};
|
package/src/localNode.ts
CHANGED
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import { Result,
|
|
1
|
+
import { Result, err, ok } from "neverthrow";
|
|
2
2
|
import { CoID } from "./coValue.js";
|
|
3
3
|
import { RawCoValue } from "./coValue.js";
|
|
4
4
|
import {
|
|
@@ -223,10 +223,19 @@ export class LocalNode {
|
|
|
223
223
|
account.set("profile", profile.id, "trusting");
|
|
224
224
|
}
|
|
225
225
|
|
|
226
|
-
|
|
226
|
+
const profileId = account.get("profile");
|
|
227
|
+
|
|
228
|
+
if (!profileId) {
|
|
227
229
|
throw new Error("Must set account profile in initial migration");
|
|
228
230
|
}
|
|
229
231
|
|
|
232
|
+
if (node.syncManager.hasStoragePeers()) {
|
|
233
|
+
await Promise.all([
|
|
234
|
+
node.syncManager.waitForStorageSync(account.id),
|
|
235
|
+
node.syncManager.waitForStorageSync(profileId),
|
|
236
|
+
]);
|
|
237
|
+
}
|
|
238
|
+
|
|
230
239
|
return {
|
|
231
240
|
node,
|
|
232
241
|
accountID: account.id,
|
|
@@ -272,11 +281,9 @@ export class LocalNode {
|
|
|
272
281
|
if (!profileID) {
|
|
273
282
|
throw new Error("Account has no profile");
|
|
274
283
|
}
|
|
275
|
-
const profile = await node.load(profileID);
|
|
276
284
|
|
|
277
|
-
|
|
278
|
-
|
|
279
|
-
}
|
|
285
|
+
// Preload the profile
|
|
286
|
+
await node.load(profileID);
|
|
280
287
|
|
|
281
288
|
if (migration) {
|
|
282
289
|
await migration(account, node);
|
package/src/streamUtils.ts
CHANGED
|
@@ -6,23 +6,17 @@ export function connectedPeers(
|
|
|
6
6
|
peer1id: PeerID,
|
|
7
7
|
peer2id: PeerID,
|
|
8
8
|
{
|
|
9
|
-
trace = false,
|
|
10
9
|
peer1role = "client",
|
|
11
10
|
peer2role = "client",
|
|
12
11
|
crashOnClose = false,
|
|
13
12
|
}: {
|
|
14
|
-
trace?: boolean;
|
|
15
13
|
peer1role?: Peer["role"];
|
|
16
14
|
peer2role?: Peer["role"];
|
|
17
15
|
crashOnClose?: boolean;
|
|
18
16
|
} = {},
|
|
19
17
|
): [Peer, Peer] {
|
|
20
|
-
const [from1to2Rx, from1to2Tx] = newQueuePair(
|
|
21
|
-
|
|
22
|
-
);
|
|
23
|
-
const [from2to1Rx, from2to1Tx] = newQueuePair(
|
|
24
|
-
trace ? { traceAs: `${peer2id} -> ${peer1id}` } : undefined,
|
|
25
|
-
);
|
|
18
|
+
const [from1to2Rx, from1to2Tx] = newQueuePair();
|
|
19
|
+
const [from2to1Rx, from2to1Tx] = newQueuePair();
|
|
26
20
|
|
|
27
21
|
const peer2AsPeer: Peer = {
|
|
28
22
|
id: peer2id,
|
|
@@ -43,32 +37,11 @@ export function connectedPeers(
|
|
|
43
37
|
return [peer1AsPeer, peer2AsPeer];
|
|
44
38
|
}
|
|
45
39
|
|
|
46
|
-
export function newQueuePair(
|
|
47
|
-
|
|
48
|
-
|
|
40
|
+
export function newQueuePair(): [
|
|
41
|
+
AsyncIterable<SyncMessage>,
|
|
42
|
+
Channel<SyncMessage>,
|
|
43
|
+
] {
|
|
49
44
|
const channel = new Channel<SyncMessage>();
|
|
50
45
|
|
|
51
|
-
|
|
52
|
-
return [
|
|
53
|
-
(async function* () {
|
|
54
|
-
for await (const msg of channel) {
|
|
55
|
-
console.debug(
|
|
56
|
-
options.traceAs,
|
|
57
|
-
JSON.stringify(
|
|
58
|
-
msg,
|
|
59
|
-
(k, v) =>
|
|
60
|
-
k === "changes" || k === "encryptedChanges"
|
|
61
|
-
? v.slice(0, 20) + "..."
|
|
62
|
-
: v,
|
|
63
|
-
2,
|
|
64
|
-
),
|
|
65
|
-
);
|
|
66
|
-
yield msg;
|
|
67
|
-
}
|
|
68
|
-
})(),
|
|
69
|
-
channel,
|
|
70
|
-
];
|
|
71
|
-
} else {
|
|
72
|
-
return [channel.wrap(), channel];
|
|
73
|
-
}
|
|
46
|
+
return [channel.wrap(), channel];
|
|
74
47
|
}
|
package/src/sync.ts
CHANGED
|
@@ -159,6 +159,12 @@ export class SyncManager {
|
|
|
159
159
|
);
|
|
160
160
|
}
|
|
161
161
|
|
|
162
|
+
hasStoragePeers(): boolean {
|
|
163
|
+
return this.getPeers().some(
|
|
164
|
+
(peer) => peer.role === "storage" && !peer.closed,
|
|
165
|
+
);
|
|
166
|
+
}
|
|
167
|
+
|
|
162
168
|
handleSyncMessage(msg: SyncMessage, peer: PeerState) {
|
|
163
169
|
if (this.local.getCoValue(msg.id).isErroredInPeer(peer.id)) {
|
|
164
170
|
logger.warn(
|
|
@@ -393,10 +399,12 @@ export class SyncManager {
|
|
|
393
399
|
|
|
394
400
|
return;
|
|
395
401
|
} else {
|
|
396
|
-
//
|
|
397
|
-
|
|
398
|
-
|
|
399
|
-
|
|
402
|
+
// Syncronously updates the state loading is possible
|
|
403
|
+
coValue
|
|
404
|
+
.loadFromPeers(this.getServerAndStoragePeers(peer.id))
|
|
405
|
+
.catch((e) => {
|
|
406
|
+
logger.error("Error loading coValue in handleLoad", { err: e });
|
|
407
|
+
});
|
|
400
408
|
}
|
|
401
409
|
}
|
|
402
410
|
|
|
@@ -622,28 +630,24 @@ export class SyncManager {
|
|
|
622
630
|
|
|
623
631
|
handleUnsubscribe(_msg: DoneMessage) {}
|
|
624
632
|
|
|
625
|
-
requestedSyncs = new
|
|
626
|
-
|
|
627
|
-
|
|
628
|
-
|
|
633
|
+
requestedSyncs = new Set<RawCoID>();
|
|
634
|
+
requestCoValueSync(coValue: CoValueCore) {
|
|
635
|
+
if (this.requestedSyncs.has(coValue.id)) {
|
|
636
|
+
return;
|
|
637
|
+
}
|
|
629
638
|
|
|
630
|
-
|
|
631
|
-
|
|
632
|
-
|
|
633
|
-
|
|
634
|
-
|
|
635
|
-
this.requestedSyncs.delete(coValue.id);
|
|
636
|
-
this.syncCoValue(coValue);
|
|
637
|
-
resolve();
|
|
638
|
-
});
|
|
639
|
-
});
|
|
639
|
+
queueMicrotask(() => {
|
|
640
|
+
if (this.requestedSyncs.has(coValue.id)) {
|
|
641
|
+
this.syncCoValue(coValue);
|
|
642
|
+
}
|
|
643
|
+
});
|
|
640
644
|
|
|
641
|
-
|
|
642
|
-
return promise;
|
|
643
|
-
}
|
|
645
|
+
this.requestedSyncs.add(coValue.id);
|
|
644
646
|
}
|
|
645
647
|
|
|
646
648
|
async syncCoValue(coValue: CoValueCore) {
|
|
649
|
+
this.requestedSyncs.delete(coValue.id);
|
|
650
|
+
|
|
647
651
|
for (const peer of this.peersInPriorityOrder()) {
|
|
648
652
|
if (peer.closed) continue;
|
|
649
653
|
if (coValue.isErroredInPeer(peer.id)) continue;
|
|
@@ -674,6 +678,21 @@ export class SyncManager {
|
|
|
674
678
|
return true;
|
|
675
679
|
}
|
|
676
680
|
|
|
681
|
+
const peerState = this.peers[peerId];
|
|
682
|
+
|
|
683
|
+
// The peer has been closed, so it isn't possible to sync
|
|
684
|
+
if (!peerState || peerState.closed) {
|
|
685
|
+
return true;
|
|
686
|
+
}
|
|
687
|
+
|
|
688
|
+
// The client isn't subscribed to the coValue, so we won't sync it
|
|
689
|
+
if (
|
|
690
|
+
peerState.role === "client" &&
|
|
691
|
+
!peerState.optimisticKnownStates.has(id)
|
|
692
|
+
) {
|
|
693
|
+
return true;
|
|
694
|
+
}
|
|
695
|
+
|
|
677
696
|
return new Promise((resolve, reject) => {
|
|
678
697
|
const unsubscribe = this.syncState.subscribeToPeerUpdates(
|
|
679
698
|
peerId,
|
|
@@ -693,10 +712,20 @@ export class SyncManager {
|
|
|
693
712
|
});
|
|
694
713
|
}
|
|
695
714
|
|
|
715
|
+
async waitForStorageSync(id: RawCoID, timeout = 30_000) {
|
|
716
|
+
const peers = this.getPeers();
|
|
717
|
+
|
|
718
|
+
await Promise.all(
|
|
719
|
+
peers
|
|
720
|
+
.filter((peer) => peer.role === "storage")
|
|
721
|
+
.map((peer) => this.waitForSyncWithPeer(peer.id, id, timeout)),
|
|
722
|
+
);
|
|
723
|
+
}
|
|
724
|
+
|
|
696
725
|
async waitForSync(id: RawCoID, timeout = 30_000) {
|
|
697
726
|
const peers = this.getPeers();
|
|
698
727
|
|
|
699
|
-
|
|
728
|
+
await Promise.all(
|
|
700
729
|
peers.map((peer) => this.waitForSyncWithPeer(peer.id, id, timeout)),
|
|
701
730
|
);
|
|
702
731
|
}
|
|
@@ -174,9 +174,6 @@ describe("PeerState", () => {
|
|
|
174
174
|
test("should dispatch to both states", () => {
|
|
175
175
|
const { peerState } = setup();
|
|
176
176
|
const knownStatesSpy = vi.spyOn(peerState._knownStates, "set");
|
|
177
|
-
if (peerState._optimisticKnownStates === "assumeInfallible") {
|
|
178
|
-
throw new Error("Expected normal optimisticKnownStates");
|
|
179
|
-
}
|
|
180
177
|
|
|
181
178
|
const optimisticKnownStatesSpy = vi.spyOn(
|
|
182
179
|
peerState._optimisticKnownStates,
|
|
@@ -195,40 +192,6 @@ describe("PeerState", () => {
|
|
|
195
192
|
expect(optimisticKnownStatesSpy).toHaveBeenCalledWith("co_z1", state);
|
|
196
193
|
});
|
|
197
194
|
|
|
198
|
-
test("should use same reference for knownStates and optimisticKnownStates for storage peers", () => {
|
|
199
|
-
const mockStoragePeer: Peer = {
|
|
200
|
-
id: "test-storage-peer",
|
|
201
|
-
role: "storage",
|
|
202
|
-
priority: 1,
|
|
203
|
-
crashOnClose: false,
|
|
204
|
-
incoming: (async function* () {})(),
|
|
205
|
-
outgoing: {
|
|
206
|
-
push: vi.fn().mockResolvedValue(undefined),
|
|
207
|
-
close: vi.fn(),
|
|
208
|
-
},
|
|
209
|
-
};
|
|
210
|
-
const peerState = new PeerState(mockStoragePeer, undefined);
|
|
211
|
-
|
|
212
|
-
// Verify they are the same reference
|
|
213
|
-
expect(peerState.knownStates).toBe(peerState.optimisticKnownStates);
|
|
214
|
-
|
|
215
|
-
// Verify that dispatching only updates one state
|
|
216
|
-
const knownStatesSpy = vi.spyOn(peerState._knownStates, "set");
|
|
217
|
-
expect(peerState._optimisticKnownStates).toBe("assumeInfallible");
|
|
218
|
-
|
|
219
|
-
const state: CoValueKnownState = {
|
|
220
|
-
id: "co_z1",
|
|
221
|
-
header: false,
|
|
222
|
-
sessions: {},
|
|
223
|
-
};
|
|
224
|
-
|
|
225
|
-
peerState.setKnownState("co_z1", state);
|
|
226
|
-
|
|
227
|
-
// Only one dispatch should happen since they're the same reference
|
|
228
|
-
expect(knownStatesSpy).toHaveBeenCalledTimes(1);
|
|
229
|
-
expect(knownStatesSpy).toHaveBeenCalledWith("co_z1", state);
|
|
230
|
-
});
|
|
231
|
-
|
|
232
195
|
test("should use separate references for knownStates and optimisticKnownStates for non-storage peers", () => {
|
|
233
196
|
const { peerState } = setup(); // Uses a regular peer
|
|
234
197
|
|
|
@@ -70,12 +70,12 @@ describe("SyncStateManager", () => {
|
|
|
70
70
|
const map = group.createMap();
|
|
71
71
|
map.set("key1", "value1", "trusting");
|
|
72
72
|
|
|
73
|
-
const [
|
|
74
|
-
peer1role: "
|
|
75
|
-
peer2role: "
|
|
73
|
+
const [serverPeer] = connectedPeers("serverPeer", "unusedPeer", {
|
|
74
|
+
peer1role: "server",
|
|
75
|
+
peer2role: "client",
|
|
76
76
|
});
|
|
77
77
|
|
|
78
|
-
client.node.syncManager.addPeer(
|
|
78
|
+
client.node.syncManager.addPeer(serverPeer);
|
|
79
79
|
|
|
80
80
|
const subscriptionManager = client.node.syncManager.syncState;
|
|
81
81
|
|
|
@@ -86,7 +86,7 @@ describe("SyncStateManager", () => {
|
|
|
86
86
|
updateToJazzCloudSpy,
|
|
87
87
|
);
|
|
88
88
|
const unsubscribe2 = subscriptionManager.subscribeToPeerUpdates(
|
|
89
|
-
|
|
89
|
+
serverPeer.id,
|
|
90
90
|
updateToStorageSpy,
|
|
91
91
|
);
|
|
92
92
|
|
|
@@ -115,7 +115,7 @@ describe("SyncStateManager", () => {
|
|
|
115
115
|
);
|
|
116
116
|
|
|
117
117
|
expect(updateToStorageSpy).toHaveBeenLastCalledWith(
|
|
118
|
-
emptyKnownState(
|
|
118
|
+
emptyKnownState(group.core.id),
|
|
119
119
|
{ uploaded: false },
|
|
120
120
|
);
|
|
121
121
|
});
|
|
@@ -247,4 +247,54 @@ describe("SyncStateManager", () => {
|
|
|
247
247
|
),
|
|
248
248
|
).toEqual({ uploaded: true });
|
|
249
249
|
});
|
|
250
|
+
|
|
251
|
+
test("should skip closed peers", async () => {
|
|
252
|
+
const client = setupTestNode();
|
|
253
|
+
const { peerState } = client.connectToSyncServer();
|
|
254
|
+
|
|
255
|
+
peerState.gracefulShutdown();
|
|
256
|
+
|
|
257
|
+
const group = client.node.createGroup();
|
|
258
|
+
const map = group.createMap();
|
|
259
|
+
|
|
260
|
+
await expect(map.core.waitForSync()).resolves.toBeUndefined();
|
|
261
|
+
});
|
|
262
|
+
|
|
263
|
+
test("should skip client peers that are not subscribed to the coValue", async () => {
|
|
264
|
+
const server = setupTestNode({ isSyncServer: true });
|
|
265
|
+
const client = setupTestNode();
|
|
266
|
+
|
|
267
|
+
client.connectToSyncServer({
|
|
268
|
+
syncServer: server.node,
|
|
269
|
+
});
|
|
270
|
+
|
|
271
|
+
const group = server.node.createGroup();
|
|
272
|
+
const map = group.createMap();
|
|
273
|
+
|
|
274
|
+
await map.core.waitForSync();
|
|
275
|
+
|
|
276
|
+
expect(client.node.getCoValue(map.id).isAvailable()).toBe(false);
|
|
277
|
+
});
|
|
278
|
+
|
|
279
|
+
test("should wait for client peers that are subscribed to the coValue", async () => {
|
|
280
|
+
const server = setupTestNode({ isSyncServer: true });
|
|
281
|
+
const client = setupTestNode();
|
|
282
|
+
|
|
283
|
+
const { peerStateOnServer } = client.connectToSyncServer();
|
|
284
|
+
|
|
285
|
+
const group = server.node.createGroup();
|
|
286
|
+
const map = group.createMap();
|
|
287
|
+
map.set("key1", "value1", "trusting");
|
|
288
|
+
|
|
289
|
+
// Simulate the subscription to the coValue
|
|
290
|
+
peerStateOnServer.setKnownState(map.core.id, {
|
|
291
|
+
id: map.core.id,
|
|
292
|
+
header: true,
|
|
293
|
+
sessions: {},
|
|
294
|
+
});
|
|
295
|
+
|
|
296
|
+
await map.core.waitForSync();
|
|
297
|
+
|
|
298
|
+
expect(client.node.getCoValue(map.id).isAvailable()).toBe(true);
|
|
299
|
+
});
|
|
250
300
|
});
|
|
@@ -1,7 +1,9 @@
|
|
|
1
1
|
import { expect, test } from "vitest";
|
|
2
|
+
import { expectAccount } from "../coValues/account.js";
|
|
2
3
|
import { WasmCrypto } from "../crypto/WasmCrypto.js";
|
|
3
4
|
import { LocalNode } from "../localNode.js";
|
|
4
5
|
import { connectedPeers } from "../streamUtils.js";
|
|
6
|
+
import { createMockStoragePeer } from "./testUtils.js";
|
|
5
7
|
|
|
6
8
|
const Crypto = await WasmCrypto.create();
|
|
7
9
|
|
|
@@ -85,3 +87,25 @@ test("throws an error if the user tried to create an invite from an account", as
|
|
|
85
87
|
"Cannot create invite from an account",
|
|
86
88
|
);
|
|
87
89
|
});
|
|
90
|
+
|
|
91
|
+
test("wait for storage sync before resolving withNewlyCreatedAccount", async () => {
|
|
92
|
+
const { storage, peer } = createMockStoragePeer({
|
|
93
|
+
peerId: "account-node",
|
|
94
|
+
});
|
|
95
|
+
|
|
96
|
+
const { node, accountID } = await LocalNode.withNewlyCreatedAccount({
|
|
97
|
+
creationProps: { name: "Hermes Puggington" },
|
|
98
|
+
crypto: Crypto,
|
|
99
|
+
peersToLoadFrom: [peer],
|
|
100
|
+
});
|
|
101
|
+
|
|
102
|
+
const account = storage.getCoValue(accountID);
|
|
103
|
+
|
|
104
|
+
expect(account.isAvailable()).toBe(true);
|
|
105
|
+
|
|
106
|
+
const profile = storage.getCoValue(
|
|
107
|
+
expectAccount(account.getCurrentContent()).get("profile")!,
|
|
108
|
+
);
|
|
109
|
+
|
|
110
|
+
expect(profile.isAvailable()).toBe(true);
|
|
111
|
+
});
|
package/src/tests/group.test.ts
CHANGED
|
@@ -750,7 +750,6 @@ describe("extend with role mapping", () => {
|
|
|
750
750
|
const mapOnNode2 = await loadCoValueOrFail(node2.node, map.id);
|
|
751
751
|
|
|
752
752
|
expect(mapOnNode2.get("test")).toEqual("Written from the admin");
|
|
753
|
-
|
|
754
753
|
mapOnNode2.set("test", "Written from the inherited role");
|
|
755
754
|
expect(mapOnNode2.get("test")).toEqual("Written from the inherited role");
|
|
756
755
|
|