cojson 0.7.18 → 0.7.26
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-lint.log +0 -4
- package/.turbo/turbo-test.log +262 -336
- 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 -57
- 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 +295 -215
- package/dist/storage/index.js.map +1 -1
- package/dist/streamUtils.js +34 -30
- package/dist/streamUtils.js.map +1 -1
- package/dist/sync.js +48 -40
- 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 +85 -105
- package/src/storage/chunksAndKnownStates.ts +3 -4
- package/src/storage/index.ts +413 -310
- package/src/streamUtils.ts +47 -43
- package/src/sync.ts +68 -76
- package/src/tests/account.test.ts +3 -4
- 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,54 +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
|
-
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
|
|
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,
|
|
60
|
+
),
|
|
61
|
+
);
|
|
62
|
+
yield msg;
|
|
63
|
+
}
|
|
64
|
+
})(),
|
|
65
|
+
channel,
|
|
66
|
+
];
|
|
67
|
+
} else {
|
|
68
|
+
return [channel.wrap(), channel];
|
|
69
|
+
}
|
|
66
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 { Effect, Queue, Stream } from "effect";
|
|
7
6
|
|
|
8
7
|
export type CoValueKnownState = {
|
|
9
8
|
id: RawCoID;
|
|
@@ -56,22 +55,17 @@ export type DoneMessage = {
|
|
|
56
55
|
|
|
57
56
|
export type PeerID = string;
|
|
58
57
|
|
|
59
|
-
export
|
|
60
|
-
readonly _tag = "DisconnectedError";
|
|
61
|
-
constructor(public message: string) {
|
|
62
|
-
super(message);
|
|
63
|
-
}
|
|
64
|
-
}
|
|
58
|
+
export type DisconnectedError = "Disconnected";
|
|
65
59
|
|
|
66
|
-
export
|
|
67
|
-
readonly _tag = "PingTimeoutError";
|
|
68
|
-
}
|
|
60
|
+
export type PingTimeoutError = "PingTimeout";
|
|
69
61
|
|
|
70
|
-
export type IncomingSyncStream =
|
|
71
|
-
SyncMessage
|
|
72
|
-
DisconnectedError | PingTimeoutError
|
|
62
|
+
export type IncomingSyncStream = AsyncIterable<
|
|
63
|
+
SyncMessage | DisconnectedError | PingTimeoutError
|
|
73
64
|
>;
|
|
74
|
-
export type OutgoingSyncQueue =
|
|
65
|
+
export type OutgoingSyncQueue = {
|
|
66
|
+
push: (msg: SyncMessage) => Promise<unknown>;
|
|
67
|
+
close: () => void;
|
|
68
|
+
};
|
|
75
69
|
|
|
76
70
|
export interface Peer {
|
|
77
71
|
id: PeerID;
|
|
@@ -147,15 +141,11 @@ export class SyncManager {
|
|
|
147
141
|
|
|
148
142
|
for (const peer of eligiblePeers) {
|
|
149
143
|
// console.log("loading", id, "from", peer.id);
|
|
150
|
-
|
|
151
|
-
|
|
152
|
-
|
|
153
|
-
|
|
154
|
-
|
|
155
|
-
sessions: {},
|
|
156
|
-
}),
|
|
157
|
-
).catch((e) => {
|
|
158
|
-
console.error("Error writing to peer", e);
|
|
144
|
+
await peer.outgoing.push({
|
|
145
|
+
action: "load",
|
|
146
|
+
id: id,
|
|
147
|
+
header: false,
|
|
148
|
+
sessions: {},
|
|
159
149
|
});
|
|
160
150
|
|
|
161
151
|
const coValueEntry = this.local.coValues[id];
|
|
@@ -232,11 +222,13 @@ export class SyncManager {
|
|
|
232
222
|
}
|
|
233
223
|
|
|
234
224
|
if (entry.state === "loading") {
|
|
235
|
-
|
|
225
|
+
this.trySendToPeer(peer, {
|
|
236
226
|
action: "load",
|
|
237
227
|
id,
|
|
238
228
|
header: false,
|
|
239
229
|
sessions: {},
|
|
230
|
+
}).catch((e) => {
|
|
231
|
+
console.error("Error sending load", e);
|
|
240
232
|
});
|
|
241
233
|
return;
|
|
242
234
|
}
|
|
@@ -249,9 +241,11 @@ export class SyncManager {
|
|
|
249
241
|
|
|
250
242
|
if (!peer.toldKnownState.has(id)) {
|
|
251
243
|
peer.toldKnownState.add(id);
|
|
252
|
-
|
|
244
|
+
this.trySendToPeer(peer, {
|
|
253
245
|
action: "load",
|
|
254
246
|
...coValue.knownState(),
|
|
247
|
+
}).catch((e) => {
|
|
248
|
+
console.error("Error sending load", e);
|
|
255
249
|
});
|
|
256
250
|
}
|
|
257
251
|
}
|
|
@@ -276,10 +270,12 @@ export class SyncManager {
|
|
|
276
270
|
);
|
|
277
271
|
|
|
278
272
|
if (!peer.toldKnownState.has(id)) {
|
|
279
|
-
|
|
273
|
+
this.trySendToPeer(peer, {
|
|
280
274
|
action: "known",
|
|
281
275
|
asDependencyOf,
|
|
282
276
|
...coValue.knownState(),
|
|
277
|
+
}).catch((e) => {
|
|
278
|
+
console.error("Error sending known state", e);
|
|
283
279
|
});
|
|
284
280
|
|
|
285
281
|
peer.toldKnownState.add(id);
|
|
@@ -314,7 +310,9 @@ export class SyncManager {
|
|
|
314
310
|
// } header: ${!!piece.header}`,
|
|
315
311
|
// // Object.values(piece.new).map((s) => s.newTransactions)
|
|
316
312
|
// );
|
|
317
|
-
|
|
313
|
+
this.trySendToPeer(peer, piece).catch((e) => {
|
|
314
|
+
console.error("Error sending content piece", e);
|
|
315
|
+
});
|
|
318
316
|
if (performance.now() - lastYield > 10) {
|
|
319
317
|
await new Promise<void>((resolve) => {
|
|
320
318
|
setTimeout(resolve, 0);
|
|
@@ -368,55 +366,39 @@ export class SyncManager {
|
|
|
368
366
|
void initialSync();
|
|
369
367
|
}
|
|
370
368
|
|
|
371
|
-
|
|
372
|
-
peerState.incoming
|
|
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
|
-
|
|
398
|
-
|
|
399
|
-
|
|
400
|
-
new Error("Took >10s to process message"),
|
|
401
|
-
}),
|
|
402
|
-
),
|
|
403
|
-
),
|
|
404
|
-
Effect.catchAll((e) =>
|
|
405
|
-
Effect.logError(
|
|
406
|
-
"Error in peer",
|
|
407
|
-
peer.id,
|
|
408
|
-
e.message,
|
|
409
|
-
typeof e.cause === "object" &&
|
|
410
|
-
e.cause instanceof Error &&
|
|
411
|
-
e.cause.message,
|
|
412
|
-
),
|
|
413
|
-
),
|
|
414
|
-
),
|
|
415
|
-
);
|
|
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
|
+
});
|
|
416
398
|
}
|
|
417
399
|
|
|
418
400
|
trySendToPeer(peer: PeerState, msg: SyncMessage) {
|
|
419
|
-
return
|
|
401
|
+
return peer.outgoing.push(msg);
|
|
420
402
|
}
|
|
421
403
|
|
|
422
404
|
async handleLoad(msg: LoadMessage, peer: PeerState) {
|
|
@@ -442,7 +424,7 @@ export class SyncManager {
|
|
|
442
424
|
header: false,
|
|
443
425
|
sessions: {},
|
|
444
426
|
}).catch((e) => {
|
|
445
|
-
console.error("Error sending known state
|
|
427
|
+
console.error("Error sending known state", e);
|
|
446
428
|
});
|
|
447
429
|
}
|
|
448
430
|
return;
|
|
@@ -473,11 +455,13 @@ export class SyncManager {
|
|
|
473
455
|
peer.optimisticKnownStates[msg.id] = knownStateIn(msg);
|
|
474
456
|
peer.toldKnownState.add(msg.id);
|
|
475
457
|
|
|
476
|
-
|
|
458
|
+
this.trySendToPeer(peer, {
|
|
477
459
|
action: "known",
|
|
478
460
|
id: msg.id,
|
|
479
461
|
header: false,
|
|
480
462
|
sessions: {},
|
|
463
|
+
}).catch((e) => {
|
|
464
|
+
console.error("Error sending known state back", e);
|
|
481
465
|
});
|
|
482
466
|
|
|
483
467
|
return;
|
|
@@ -687,10 +671,12 @@ export class SyncManager {
|
|
|
687
671
|
await this.syncCoValue(coValue);
|
|
688
672
|
|
|
689
673
|
if (invalidStateAssumed) {
|
|
690
|
-
|
|
674
|
+
this.trySendToPeer(peer, {
|
|
691
675
|
action: "known",
|
|
692
676
|
isCorrection: true,
|
|
693
677
|
...coValue.knownState(),
|
|
678
|
+
}).catch((e) => {
|
|
679
|
+
console.error("Error sending known state correction", e);
|
|
694
680
|
});
|
|
695
681
|
}
|
|
696
682
|
}
|
|
@@ -758,6 +744,12 @@ export class SyncManager {
|
|
|
758
744
|
}
|
|
759
745
|
}
|
|
760
746
|
}
|
|
747
|
+
|
|
748
|
+
gracefulShutdown() {
|
|
749
|
+
for (const peer of Object.values(this.peers)) {
|
|
750
|
+
peer.outgoing.close();
|
|
751
|
+
}
|
|
752
|
+
}
|
|
761
753
|
}
|
|
762
754
|
|
|
763
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,13 @@ 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] =
|
|
55
|
+
const [node1asPeer, node2asPeer] = connectedPeers("node1", "node2", {
|
|
57
56
|
trace: true,
|
|
58
57
|
peer1role: "server",
|
|
59
58
|
peer2role: "client",
|
|
60
|
-
})
|
|
59
|
+
})
|
|
61
60
|
|
|
62
|
-
console.log("After connected peers")
|
|
61
|
+
console.log("After connected peers");
|
|
63
62
|
|
|
64
63
|
node.syncManager.addPeer(node2asPeer);
|
|
65
64
|
|