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.
@@ -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
- ): Effect.Effect<[Peer, Peer]> {
17
- return Effect.gen(function* () {
18
- const [from1to2Rx, from1to2Tx] = yield* newQueuePair(
19
- trace ? { traceAs: `${peer1id} -> ${peer2id}` } : undefined,
20
- );
21
- const [from2to1Rx, from2to1Tx] = yield* newQueuePair(
22
- trace ? { traceAs: `${peer2id} -> ${peer1id}` } : undefined,
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
- const peer2AsPeer: Peer = {
26
- id: peer2id,
27
- incoming: from2to1Rx,
28
- outgoing: from1to2Tx,
29
- role: peer2role,
30
- };
25
+ const peer2AsPeer: Peer = {
26
+ id: peer2id,
27
+ incoming: from2to1Rx,
28
+ outgoing: from1to2Tx,
29
+ role: peer2role,
30
+ };
31
31
 
32
- const peer1AsPeer: Peer = {
33
- id: peer1id,
34
- incoming: from1to2Rx,
35
- outgoing: from2to1Tx,
36
- role: peer1role,
37
- };
32
+ const peer1AsPeer: Peer = {
33
+ id: peer1id,
34
+ incoming: from1to2Rx,
35
+ outgoing: from2to1Tx,
36
+ role: peer1role,
37
+ };
38
38
 
39
- return [peer1AsPeer, peer2AsPeer];
40
- });
39
+ return [peer1AsPeer, peer2AsPeer];
41
40
  }
42
41
 
43
42
  export function newQueuePair(
44
43
  options: { traceAs?: string } = {},
45
- ): Effect.Effect<[Stream.Stream<SyncMessage>, Queue.Enqueue<SyncMessage>]> {
46
- return Effect.gen(function* () {
47
- const queue = yield* Queue.unbounded<SyncMessage>();
44
+ ): [AsyncIterable<SyncMessage>, Channel<SyncMessage>] {
45
+ const channel = new Channel<SyncMessage>();
48
46
 
49
- if (options.traceAs) {
50
- return [Stream.fromQueue(queue).pipe(Stream.tap((msg) => Console.debug(
51
- options.traceAs,
52
- JSON.stringify(
53
- msg,
54
- (k, v) =>
55
- k === "changes" ||
56
- k === "encryptedChanges"
57
- ? v.slice(0, 20) + "..."
58
- : v,
59
- 2,
60
- ),
61
- ))), queue];
62
- } else {
63
- return [Stream.fromQueue(queue), queue];
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 class DisconnectedError extends Error {
60
- readonly _tag = "DisconnectedError";
61
- constructor(public message: string) {
62
- super(message);
63
- }
64
- }
58
+ export type DisconnectedError = "Disconnected";
65
59
 
66
- export class PingTimeoutError extends Error {
67
- readonly _tag = "PingTimeoutError";
68
- }
60
+ export type PingTimeoutError = "PingTimeout";
69
61
 
70
- export type IncomingSyncStream = Stream.Stream<
71
- SyncMessage,
72
- DisconnectedError | PingTimeoutError
62
+ export type IncomingSyncStream = AsyncIterable<
63
+ SyncMessage | DisconnectedError | PingTimeoutError
73
64
  >;
74
- export type OutgoingSyncQueue = Queue.Enqueue<SyncMessage>;
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
- Effect.runPromise(
151
- Queue.offer(peer.outgoing, {
152
- action: "load",
153
- id: id,
154
- header: false,
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
- await this.trySendToPeer(peer, {
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
- await this.trySendToPeer(peer, {
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
- await this.trySendToPeer(peer, {
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
- await this.trySendToPeer(peer, piece);
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
- void Effect.runPromise(
372
- peerState.incoming.pipe(
373
- Stream.ensuring(
374
- Effect.sync(() => {
375
- console.log("Peer disconnected:", peer.id);
376
- delete this.peers[peer.id];
377
- }),
378
- ),
379
- Stream.runForEach((msg) =>
380
- Effect.tryPromise({
381
- try: () => this.handleSyncMessage(msg, peerState),
382
- catch: (e) =>
383
- new Error(
384
- `Error reading from peer ${
385
- peer.id
386
- }, handling msg\n\n${JSON.stringify(
387
- msg,
388
- (k, v) =>
389
- k === "changes" ||
390
- k === "encryptedChanges"
391
- ? v.slice(0, 20) + "..."
392
- : v,
393
- )}`,
394
- { cause: e },
395
- ),
396
- }).pipe(
397
- Effect.timeoutFail({
398
- duration: 10000,
399
- onTimeout: () =>
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 Effect.runPromise(Queue.offer(peer.outgoing, msg));
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 back", e);
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
- await this.trySendToPeer(peer, {
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
- await this.trySendToPeer(peer, {
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] = await Effect.runPromise(connectedPeers("node1", "node2", {
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