cojson 0.7.23 → 0.7.28
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 +2 -2
- package/.turbo/turbo-test.log +264 -339
- package/CHANGELOG.md +12 -0
- package/dist/coValueCore.js +8 -1
- package/dist/coValueCore.js.map +1 -1
- package/dist/index.js +4 -4
- package/dist/index.js.map +1 -1
- package/dist/localNode.js +3 -0
- package/dist/localNode.js.map +1 -1
- package/dist/storage/FileSystem.js +48 -62
- package/dist/storage/FileSystem.js.map +1 -1
- package/dist/storage/chunksAndKnownStates.js +2 -3
- package/dist/storage/chunksAndKnownStates.js.map +1 -1
- package/dist/storage/index.js +285 -310
- package/dist/storage/index.js.map +1 -1
- package/dist/streamUtils.js +34 -32
- package/dist/streamUtils.js.map +1 -1
- package/dist/sync.js +49 -36
- package/dist/sync.js.map +1 -1
- package/dist/tests/account.test.js +2 -3
- package/dist/tests/account.test.js.map +1 -1
- package/dist/tests/sync.test.js +183 -182
- package/dist/tests/sync.test.js.map +1 -1
- package/package.json +4 -4
- package/src/coValueCore.ts +8 -1
- package/src/index.ts +5 -10
- package/src/localNode.ts +4 -0
- package/src/storage/FileSystem.ts +83 -110
- package/src/storage/chunksAndKnownStates.ts +3 -4
- package/src/storage/index.ts +391 -491
- package/src/streamUtils.ts +46 -48
- package/src/sync.ts +69 -74
- package/src/tests/account.test.ts +5 -8
- package/src/tests/sync.test.ts +731 -798
package/src/streamUtils.ts
CHANGED
|
@@ -1,5 +1,6 @@
|
|
|
1
|
-
import { Console, Effect, Queue, Stream } from "effect";
|
|
2
1
|
import { Peer, PeerID, SyncMessage } from "./sync.js";
|
|
2
|
+
import { Channel } from "queueable";
|
|
3
|
+
export { Channel } from "queueable";
|
|
3
4
|
|
|
4
5
|
export function connectedPeers(
|
|
5
6
|
peer1id: PeerID,
|
|
@@ -13,60 +14,57 @@ export function connectedPeers(
|
|
|
13
14
|
peer1role?: Peer["role"];
|
|
14
15
|
peer2role?: Peer["role"];
|
|
15
16
|
} = {},
|
|
16
|
-
):
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
);
|
|
17
|
+
): [Peer, Peer] {
|
|
18
|
+
const [from1to2Rx, from1to2Tx] = newQueuePair(
|
|
19
|
+
trace ? { traceAs: `${peer1id} -> ${peer2id}` } : undefined,
|
|
20
|
+
);
|
|
21
|
+
const [from2to1Rx, from2to1Tx] = newQueuePair(
|
|
22
|
+
trace ? { traceAs: `${peer2id} -> ${peer1id}` } : undefined,
|
|
23
|
+
);
|
|
24
24
|
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
25
|
+
const peer2AsPeer: Peer = {
|
|
26
|
+
id: peer2id,
|
|
27
|
+
incoming: from2to1Rx,
|
|
28
|
+
outgoing: from1to2Tx,
|
|
29
|
+
role: peer2role,
|
|
30
|
+
};
|
|
31
31
|
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
32
|
+
const peer1AsPeer: Peer = {
|
|
33
|
+
id: peer1id,
|
|
34
|
+
incoming: from1to2Rx,
|
|
35
|
+
outgoing: from2to1Tx,
|
|
36
|
+
role: peer1role,
|
|
37
|
+
};
|
|
38
38
|
|
|
39
|
-
|
|
40
|
-
});
|
|
39
|
+
return [peer1AsPeer, peer2AsPeer];
|
|
41
40
|
}
|
|
42
41
|
|
|
43
42
|
export function newQueuePair(
|
|
44
43
|
options: { traceAs?: string } = {},
|
|
45
|
-
):
|
|
46
|
-
|
|
47
|
-
const queue = yield* Queue.unbounded<SyncMessage>();
|
|
44
|
+
): [AsyncIterable<SyncMessage>, Channel<SyncMessage>] {
|
|
45
|
+
const channel = new Channel<SyncMessage>();
|
|
48
46
|
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
),
|
|
47
|
+
if (options.traceAs) {
|
|
48
|
+
return [
|
|
49
|
+
(async function* () {
|
|
50
|
+
for await (const msg of channel) {
|
|
51
|
+
console.debug(
|
|
52
|
+
options.traceAs,
|
|
53
|
+
JSON.stringify(
|
|
54
|
+
msg,
|
|
55
|
+
(k, v) =>
|
|
56
|
+
k === "changes" || k === "encryptedChanges"
|
|
57
|
+
? v.slice(0, 20) + "..."
|
|
58
|
+
: v,
|
|
59
|
+
2,
|
|
63
60
|
),
|
|
64
|
-
)
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
|
|
61
|
+
);
|
|
62
|
+
yield msg;
|
|
63
|
+
}
|
|
64
|
+
})(),
|
|
65
|
+
channel,
|
|
66
|
+
];
|
|
67
|
+
} else {
|
|
68
|
+
return [channel.wrap(), channel];
|
|
69
|
+
}
|
|
72
70
|
}
|
package/src/sync.ts
CHANGED
|
@@ -3,7 +3,6 @@ import { CoValueHeader, Transaction } from "./coValueCore.js";
|
|
|
3
3
|
import { CoValueCore } from "./coValueCore.js";
|
|
4
4
|
import { LocalNode, newLoadingState } from "./localNode.js";
|
|
5
5
|
import { RawCoID, SessionID } from "./ids.js";
|
|
6
|
-
import { Data, Effect, Queue, Stream } from "effect";
|
|
7
6
|
|
|
8
7
|
export type CoValueKnownState = {
|
|
9
8
|
id: RawCoID;
|
|
@@ -56,19 +55,17 @@ export type DoneMessage = {
|
|
|
56
55
|
|
|
57
56
|
export type PeerID = string;
|
|
58
57
|
|
|
59
|
-
export
|
|
60
|
-
message: string;
|
|
61
|
-
}> {}
|
|
58
|
+
export type DisconnectedError = "Disconnected";
|
|
62
59
|
|
|
63
|
-
export
|
|
64
|
-
readonly _tag = "PingTimeoutError";
|
|
65
|
-
}
|
|
60
|
+
export type PingTimeoutError = "PingTimeout";
|
|
66
61
|
|
|
67
|
-
export type IncomingSyncStream =
|
|
68
|
-
SyncMessage
|
|
69
|
-
DisconnectedError | PingTimeoutError
|
|
62
|
+
export type IncomingSyncStream = AsyncIterable<
|
|
63
|
+
SyncMessage | DisconnectedError | PingTimeoutError
|
|
70
64
|
>;
|
|
71
|
-
export type OutgoingSyncQueue =
|
|
65
|
+
export type OutgoingSyncQueue = {
|
|
66
|
+
push: (msg: SyncMessage) => Promise<unknown>;
|
|
67
|
+
close: () => void;
|
|
68
|
+
};
|
|
72
69
|
|
|
73
70
|
export interface Peer {
|
|
74
71
|
id: PeerID;
|
|
@@ -144,15 +141,11 @@ export class SyncManager {
|
|
|
144
141
|
|
|
145
142
|
for (const peer of eligiblePeers) {
|
|
146
143
|
// console.log("loading", id, "from", peer.id);
|
|
147
|
-
|
|
148
|
-
|
|
149
|
-
|
|
150
|
-
|
|
151
|
-
|
|
152
|
-
sessions: {},
|
|
153
|
-
}),
|
|
154
|
-
).catch((e) => {
|
|
155
|
-
console.error("Error writing to peer", e);
|
|
144
|
+
await peer.outgoing.push({
|
|
145
|
+
action: "load",
|
|
146
|
+
id: id,
|
|
147
|
+
header: false,
|
|
148
|
+
sessions: {},
|
|
156
149
|
});
|
|
157
150
|
|
|
158
151
|
const coValueEntry = this.local.coValues[id];
|
|
@@ -229,11 +222,13 @@ export class SyncManager {
|
|
|
229
222
|
}
|
|
230
223
|
|
|
231
224
|
if (entry.state === "loading") {
|
|
232
|
-
|
|
225
|
+
this.trySendToPeer(peer, {
|
|
233
226
|
action: "load",
|
|
234
227
|
id,
|
|
235
228
|
header: false,
|
|
236
229
|
sessions: {},
|
|
230
|
+
}).catch((e) => {
|
|
231
|
+
console.error("Error sending load", e);
|
|
237
232
|
});
|
|
238
233
|
return;
|
|
239
234
|
}
|
|
@@ -246,9 +241,11 @@ export class SyncManager {
|
|
|
246
241
|
|
|
247
242
|
if (!peer.toldKnownState.has(id)) {
|
|
248
243
|
peer.toldKnownState.add(id);
|
|
249
|
-
|
|
244
|
+
this.trySendToPeer(peer, {
|
|
250
245
|
action: "load",
|
|
251
246
|
...coValue.knownState(),
|
|
247
|
+
}).catch((e) => {
|
|
248
|
+
console.error("Error sending load", e);
|
|
252
249
|
});
|
|
253
250
|
}
|
|
254
251
|
}
|
|
@@ -273,10 +270,12 @@ export class SyncManager {
|
|
|
273
270
|
);
|
|
274
271
|
|
|
275
272
|
if (!peer.toldKnownState.has(id)) {
|
|
276
|
-
|
|
273
|
+
this.trySendToPeer(peer, {
|
|
277
274
|
action: "known",
|
|
278
275
|
asDependencyOf,
|
|
279
276
|
...coValue.knownState(),
|
|
277
|
+
}).catch((e) => {
|
|
278
|
+
console.error("Error sending known state", e);
|
|
280
279
|
});
|
|
281
280
|
|
|
282
281
|
peer.toldKnownState.add(id);
|
|
@@ -311,7 +310,9 @@ export class SyncManager {
|
|
|
311
310
|
// } header: ${!!piece.header}`,
|
|
312
311
|
// // Object.values(piece.new).map((s) => s.newTransactions)
|
|
313
312
|
// );
|
|
314
|
-
|
|
313
|
+
this.trySendToPeer(peer, piece).catch((e) => {
|
|
314
|
+
console.error("Error sending content piece", e);
|
|
315
|
+
});
|
|
315
316
|
if (performance.now() - lastYield > 10) {
|
|
316
317
|
await new Promise<void>((resolve) => {
|
|
317
318
|
setTimeout(resolve, 0);
|
|
@@ -365,55 +366,39 @@ export class SyncManager {
|
|
|
365
366
|
void initialSync();
|
|
366
367
|
}
|
|
367
368
|
|
|
368
|
-
|
|
369
|
-
peerState.incoming
|
|
370
|
-
|
|
371
|
-
|
|
372
|
-
|
|
373
|
-
|
|
374
|
-
|
|
375
|
-
|
|
376
|
-
|
|
377
|
-
|
|
378
|
-
|
|
379
|
-
|
|
380
|
-
|
|
381
|
-
|
|
382
|
-
|
|
383
|
-
|
|
384
|
-
|
|
385
|
-
|
|
386
|
-
|
|
387
|
-
|
|
388
|
-
|
|
389
|
-
|
|
390
|
-
|
|
391
|
-
|
|
392
|
-
|
|
393
|
-
|
|
394
|
-
|
|
395
|
-
|
|
396
|
-
|
|
397
|
-
new Error("Took >10s to process message"),
|
|
398
|
-
}),
|
|
399
|
-
),
|
|
400
|
-
),
|
|
401
|
-
Effect.catchAll((e) =>
|
|
402
|
-
Effect.logError(
|
|
403
|
-
"Error in peer",
|
|
404
|
-
peer.id,
|
|
405
|
-
e.message,
|
|
406
|
-
typeof e.cause === "object" &&
|
|
407
|
-
e.cause instanceof Error &&
|
|
408
|
-
e.cause.message,
|
|
409
|
-
),
|
|
410
|
-
),
|
|
411
|
-
),
|
|
412
|
-
);
|
|
369
|
+
const processMessages = async () => {
|
|
370
|
+
for await (const msg of peerState.incoming) {
|
|
371
|
+
if (msg === "Disconnected") {
|
|
372
|
+
return;
|
|
373
|
+
}
|
|
374
|
+
if (msg === "PingTimeout") {
|
|
375
|
+
console.error("Ping timeout from peer", peer.id);
|
|
376
|
+
return;
|
|
377
|
+
}
|
|
378
|
+
try {
|
|
379
|
+
await this.handleSyncMessage(msg, peerState);
|
|
380
|
+
} catch (e) {
|
|
381
|
+
throw new Error(
|
|
382
|
+
`Error reading from peer ${
|
|
383
|
+
peer.id
|
|
384
|
+
}, handling msg\n\n${JSON.stringify(msg, (k, v) =>
|
|
385
|
+
k === "changes" || k === "encryptedChanges"
|
|
386
|
+
? v.slice(0, 20) + "..."
|
|
387
|
+
: v,
|
|
388
|
+
)}`,
|
|
389
|
+
{ cause: e },
|
|
390
|
+
);
|
|
391
|
+
}
|
|
392
|
+
}
|
|
393
|
+
};
|
|
394
|
+
|
|
395
|
+
processMessages().catch((e) => {
|
|
396
|
+
console.error("Error processing messages from peer", peer.id, e);
|
|
397
|
+
});
|
|
413
398
|
}
|
|
414
399
|
|
|
415
400
|
trySendToPeer(peer: PeerState, msg: SyncMessage) {
|
|
416
|
-
return
|
|
401
|
+
return peer.outgoing.push(msg);
|
|
417
402
|
}
|
|
418
403
|
|
|
419
404
|
async handleLoad(msg: LoadMessage, peer: PeerState) {
|
|
@@ -426,7 +411,7 @@ export class SyncManager {
|
|
|
426
411
|
// special case: we should be able to solve this much more neatly
|
|
427
412
|
// with an explicit state machine in the future
|
|
428
413
|
const eligiblePeers = this.peersInPriorityOrder().filter(
|
|
429
|
-
(other) => other.id !== peer.id &&
|
|
414
|
+
(other) => other.id !== peer.id && other.role === "server",
|
|
430
415
|
);
|
|
431
416
|
if (eligiblePeers.length === 0) {
|
|
432
417
|
if (msg.header || Object.keys(msg.sessions).length > 0) {
|
|
@@ -439,7 +424,7 @@ export class SyncManager {
|
|
|
439
424
|
header: false,
|
|
440
425
|
sessions: {},
|
|
441
426
|
}).catch((e) => {
|
|
442
|
-
console.error("Error sending known state
|
|
427
|
+
console.error("Error sending known state", e);
|
|
443
428
|
});
|
|
444
429
|
}
|
|
445
430
|
return;
|
|
@@ -470,11 +455,13 @@ export class SyncManager {
|
|
|
470
455
|
peer.optimisticKnownStates[msg.id] = knownStateIn(msg);
|
|
471
456
|
peer.toldKnownState.add(msg.id);
|
|
472
457
|
|
|
473
|
-
|
|
458
|
+
this.trySendToPeer(peer, {
|
|
474
459
|
action: "known",
|
|
475
460
|
id: msg.id,
|
|
476
461
|
header: false,
|
|
477
462
|
sessions: {},
|
|
463
|
+
}).catch((e) => {
|
|
464
|
+
console.error("Error sending known state back", e);
|
|
478
465
|
});
|
|
479
466
|
|
|
480
467
|
return;
|
|
@@ -684,10 +671,12 @@ export class SyncManager {
|
|
|
684
671
|
await this.syncCoValue(coValue);
|
|
685
672
|
|
|
686
673
|
if (invalidStateAssumed) {
|
|
687
|
-
|
|
674
|
+
this.trySendToPeer(peer, {
|
|
688
675
|
action: "known",
|
|
689
676
|
isCorrection: true,
|
|
690
677
|
...coValue.knownState(),
|
|
678
|
+
}).catch((e) => {
|
|
679
|
+
console.error("Error sending known state correction", e);
|
|
691
680
|
});
|
|
692
681
|
}
|
|
693
682
|
}
|
|
@@ -755,6 +744,12 @@ export class SyncManager {
|
|
|
755
744
|
}
|
|
756
745
|
}
|
|
757
746
|
}
|
|
747
|
+
|
|
748
|
+
gracefulShutdown() {
|
|
749
|
+
for (const peer of Object.values(this.peers)) {
|
|
750
|
+
peer.outgoing.close();
|
|
751
|
+
}
|
|
752
|
+
}
|
|
758
753
|
}
|
|
759
754
|
|
|
760
755
|
function knownStateIn(msg: LoadMessage | KnownStateMessage) {
|
|
@@ -3,7 +3,6 @@ import { newRandomSessionID } from "../coValueCore.js";
|
|
|
3
3
|
import { LocalNode } from "../localNode.js";
|
|
4
4
|
import { connectedPeers } from "../streamUtils.js";
|
|
5
5
|
import { WasmCrypto } from "../crypto/WasmCrypto.js";
|
|
6
|
-
import { Effect } from "effect";
|
|
7
6
|
|
|
8
7
|
const Crypto = await WasmCrypto.create();
|
|
9
8
|
|
|
@@ -53,13 +52,11 @@ test("Can create account with one node, and then load it on another", async () =
|
|
|
53
52
|
map.set("foo", "bar", "private");
|
|
54
53
|
expect(map.get("foo")).toEqual("bar");
|
|
55
54
|
|
|
56
|
-
const [node1asPeer, node2asPeer] =
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
}),
|
|
62
|
-
);
|
|
55
|
+
const [node1asPeer, node2asPeer] = connectedPeers("node1", "node2", {
|
|
56
|
+
trace: true,
|
|
57
|
+
peer1role: "server",
|
|
58
|
+
peer2role: "client",
|
|
59
|
+
})
|
|
63
60
|
|
|
64
61
|
console.log("After connected peers");
|
|
65
62
|
|