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.
@@ -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
- ): 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 [
51
- Stream.fromQueue(queue).pipe(
52
- Stream.tap((msg) =>
53
- Console.debug(
54
- options.traceAs,
55
- JSON.stringify(
56
- msg,
57
- (k, v) =>
58
- k === "changes" || k === "encryptedChanges"
59
- ? v.slice(0, 20) + "..."
60
- : v,
61
- 2,
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
- queue,
67
- ];
68
- } else {
69
- return [Stream.fromQueue(queue), queue];
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 class DisconnectedError extends Data.TaggedError("DisconnectedError")<{
60
- message: string;
61
- }> {}
58
+ export type DisconnectedError = "Disconnected";
62
59
 
63
- export class PingTimeoutError extends Error {
64
- readonly _tag = "PingTimeoutError";
65
- }
60
+ export type PingTimeoutError = "PingTimeout";
66
61
 
67
- export type IncomingSyncStream = Stream.Stream<
68
- SyncMessage,
69
- DisconnectedError | PingTimeoutError
62
+ export type IncomingSyncStream = AsyncIterable<
63
+ SyncMessage | DisconnectedError | PingTimeoutError
70
64
  >;
71
- export type OutgoingSyncQueue = Queue.Enqueue<SyncMessage>;
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
- Effect.runPromise(
148
- Queue.offer(peer.outgoing, {
149
- action: "load",
150
- id: id,
151
- header: false,
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
- await this.trySendToPeer(peer, {
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
- await this.trySendToPeer(peer, {
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
- await this.trySendToPeer(peer, {
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
- await this.trySendToPeer(peer, piece);
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
- void Effect.runPromise(
369
- peerState.incoming.pipe(
370
- Stream.ensuring(
371
- Effect.sync(() => {
372
- console.log("Peer disconnected:", peer.id);
373
- delete this.peers[peer.id];
374
- }),
375
- ),
376
- Stream.runForEach((msg) =>
377
- Effect.tryPromise({
378
- try: () => this.handleSyncMessage(msg, peerState),
379
- catch: (e) =>
380
- new Error(
381
- `Error reading from peer ${
382
- peer.id
383
- }, handling msg\n\n${JSON.stringify(
384
- msg,
385
- (k, v) =>
386
- k === "changes" ||
387
- k === "encryptedChanges"
388
- ? v.slice(0, 20) + "..."
389
- : v,
390
- )}`,
391
- { cause: e },
392
- ),
393
- }).pipe(
394
- Effect.timeoutFail({
395
- duration: 10000,
396
- onTimeout: () =>
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 Effect.runPromise(Queue.offer(peer.outgoing, msg));
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 && peer.role === "server",
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 back", e);
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
- await this.trySendToPeer(peer, {
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
- await this.trySendToPeer(peer, {
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] = await Effect.runPromise(
57
- connectedPeers("node1", "node2", {
58
- trace: true,
59
- peer1role: "server",
60
- peer2role: "client",
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