cojson-transport-ws 0.7.17 → 0.7.22

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,8 +1,8 @@
1
1
 
2
- > cojson-transport-ws@0.7.14 build /Users/anselm/jazz/jazz/packages/cojson-transport-ws
2
+ > cojson-transport-ws@0.7.18 build /Users/anselm/jazz/jazz/packages/cojson-transport-ws
3
3
  > npm run lint && rm -rf ./dist && tsc --sourceMap --outDir dist
4
4
 
5
5
 
6
- > cojson-transport-ws@0.7.14 lint
6
+ > cojson-transport-ws@0.7.18 lint
7
7
  > eslint . --ext ts,tsx
8
8
 
@@ -0,0 +1,4 @@
1
+
2
+ > cojson-transport-ws@0.7.18 lint /Users/anselm/jazz/jazz/packages/cojson-transport-ws
3
+ > eslint . --ext ts,tsx
4
+
package/CHANGELOG.md CHANGED
@@ -1,5 +1,18 @@
1
1
  # cojson-transport-nodejs-ws
2
2
 
3
+ ## 0.7.22
4
+
5
+ ### Patch Changes
6
+
7
+ - Increase disconnect timeout for now
8
+
9
+ ## 0.7.18
10
+
11
+ ### Patch Changes
12
+
13
+ - Updated dependencies
14
+ - cojson@0.7.18
15
+
3
16
  ## 0.7.17
4
17
 
5
18
  ### Patch Changes
package/dist/index.js CHANGED
@@ -1,59 +1,46 @@
1
1
  import { DisconnectedError, PingTimeoutError } from "cojson";
2
- import { Either, Stream, Queue, Effect, Exit } from "effect";
2
+ import { Stream, Queue, Effect, Console } from "effect";
3
+ const g = globalThis;
3
4
  export function createWebSocketPeer(options) {
4
5
  return Effect.gen(function* () {
5
6
  const ws = options.websocket;
6
- const incoming = yield* Queue.unbounded();
7
+ const ws_ = ws;
7
8
  const outgoing = yield* Queue.unbounded();
8
- ws.addEventListener("close", (event) => {
9
- void Effect.runPromiseExit(Queue.offer(incoming, Either.left(new DisconnectedError(`${event.code}: ${event.reason}`)))).then((e) => {
10
- if (Exit.isFailure(e) && !Exit.isInterrupted(e)) {
11
- console.warn("Failed closing ws", e);
12
- }
13
- });
14
- });
15
- let pingTimeout;
16
- ws.addEventListener("message", (event) => {
17
- const msg = JSON.parse(event.data);
18
- if (pingTimeout) {
19
- clearTimeout(pingTimeout);
20
- }
21
- pingTimeout = setTimeout(() => {
22
- console.debug("Ping timeout");
23
- void Effect.runPromise(Queue.offer(incoming, Either.left(new PingTimeoutError())));
24
- try {
25
- ws.close();
26
- }
27
- catch (e) {
28
- console.error("Error while trying to close ws on ping timeout", e);
29
- }
30
- }, 2500);
31
- if (msg.type === "ping") {
32
- // eslint-disable-next-line @typescript-eslint/no-explicit-any
33
- globalThis.jazzPings =
34
- // eslint-disable-next-line @typescript-eslint/no-explicit-any
35
- globalThis.jazzPings || [];
36
- // eslint-disable-next-line @typescript-eslint/no-explicit-any
37
- globalThis.jazzPings.push({
9
+ const closed = once(ws, "close").pipe(Effect.flatMap((event) => new DisconnectedError({
10
+ message: `${event.code}: ${event.reason}`,
11
+ })), Stream.fromEffect);
12
+ const isSyncMessage = (msg) => {
13
+ // eslint-disable-next-line @typescript-eslint/no-explicit-any
14
+ if (msg?.type === "ping") {
15
+ const ping = msg;
16
+ g.jazzPings || (g.jazzPings = []);
17
+ g.jazzPings.push({
38
18
  received: Date.now(),
39
- sent: msg.time,
40
- dc: msg.dc,
19
+ sent: ping.time,
20
+ dc: ping.dc,
41
21
  });
42
- return;
43
- }
44
- else {
45
- void Effect.runPromise(Queue.offer(incoming, Either.right(msg)));
22
+ return false;
46
23
  }
47
- });
48
- ws.addEventListener("open", () => {
49
- void Stream.fromQueue(outgoing).pipe(Stream.runForEach((msg) => Effect.sync(() => ws.send(JSON.stringify(msg)))), Effect.runPromise);
50
- });
24
+ return true;
25
+ };
26
+ yield* Effect.forkDaemon(Effect.gen(function* () {
27
+ yield* once(ws, "open");
28
+ yield* Queue.take(outgoing).pipe(Effect.andThen((message) => ws.send(JSON.stringify(message))), Effect.forever);
29
+ }));
30
+ const messages = Stream.fromEventListener(ws_, "message").pipe(Stream.timeoutFail(() => new PingTimeoutError(), "10 seconds"), Stream.tapError((_e) => Console.warn("Ping timeout").pipe(Effect.andThen(Effect.try(() => ws.close())), Effect.catchAll((e) => Console.error("Error while trying to close ws on ping timeout", e)))), Stream.mergeLeft(closed), Stream.map((_) => JSON.parse(_.data)), Stream.filter(isSyncMessage), Stream.buffer({ capacity: "unbounded" }), Stream.onDone(() => Queue.shutdown(outgoing)));
51
31
  return {
52
32
  id: options.id,
53
- incoming: Stream.fromQueue(incoming, { shutdown: true }).pipe(Stream.mapEffect((either) => either)),
33
+ incoming: messages,
54
34
  outgoing,
55
35
  role: options.role,
56
36
  };
57
37
  });
58
38
  }
39
+ const once = (ws, event) => Effect.async((register) => {
40
+ const cb = (msg) => {
41
+ ws.removeEventListener(event, cb);
42
+ register(Effect.succeed(msg));
43
+ };
44
+ ws.addEventListener(event, cb);
45
+ });
59
46
  //# sourceMappingURL=index.js.map
package/dist/index.js.map CHANGED
@@ -1 +1 @@
1
- {"version":3,"file":"index.js","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"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"}
1
+ {"version":3,"file":"index.js","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"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"}
package/package.json CHANGED
@@ -1,14 +1,14 @@
1
1
  {
2
2
  "name": "cojson-transport-ws",
3
3
  "type": "module",
4
- "version": "0.7.17",
4
+ "version": "0.7.22",
5
5
  "main": "dist/index.js",
6
6
  "types": "src/index.ts",
7
7
  "license": "MIT",
8
8
  "dependencies": {
9
- "effect": "^3.1.5",
9
+ "effect": "^3.5.2",
10
10
  "typescript": "^5.1.6",
11
- "cojson": "0.7.17"
11
+ "cojson": "0.7.18"
12
12
  },
13
13
  "devDependencies": {
14
14
  "@types/ws": "^8.5.5"
package/src/index.ts CHANGED
@@ -1,20 +1,37 @@
1
1
  import { DisconnectedError, Peer, PingTimeoutError, SyncMessage } from "cojson";
2
- import { Either, Stream, Queue, Effect, Exit } from "effect";
2
+ import { Stream, Queue, Effect, Console } from "effect";
3
+
4
+ interface WebsocketEvents {
5
+ close: { code: number; reason: string };
6
+ message: { data: unknown };
7
+ open: void;
8
+ }
9
+ interface PingMsg {
10
+ time: number;
11
+ dc: string;
12
+ }
3
13
 
4
14
  interface AnyWebSocket {
5
- addEventListener(
6
- type: "close",
7
- listener: (event: { code: number; reason: string }) => void,
15
+ addEventListener<K extends keyof WebsocketEvents>(
16
+ type: K,
17
+ listener: (event: WebsocketEvents[K]) => void,
8
18
  ): void;
9
- addEventListener(
10
- type: "message",
11
- listener: (event: { data: string | unknown }) => void,
19
+ removeEventListener<K extends keyof WebsocketEvents>(
20
+ type: K,
21
+ listener: (event: WebsocketEvents[K]) => void,
12
22
  ): void;
13
- addEventListener(type: "open", listener: () => void): void;
14
23
  close(): void;
15
24
  send(data: string): void;
16
25
  }
17
26
 
27
+ const g: typeof globalThis & {
28
+ jazzPings?: {
29
+ received: number;
30
+ sent: number;
31
+ dc: string;
32
+ }[];
33
+ } = globalThis;
34
+
18
35
  export function createWebSocketPeer(options: {
19
36
  id: string;
20
37
  websocket: AnyWebSocket;
@@ -22,87 +39,81 @@ export function createWebSocketPeer(options: {
22
39
  }): Effect.Effect<Peer> {
23
40
  return Effect.gen(function* () {
24
41
  const ws = options.websocket;
42
+ const ws_ = ws as unknown as Stream.EventListener<WebsocketEvents["message"]>;
25
43
 
26
- const incoming =
27
- yield* Queue.unbounded<
28
- Either.Either<SyncMessage, DisconnectedError | PingTimeoutError>
29
- >();
30
44
  const outgoing = yield* Queue.unbounded<SyncMessage>();
31
45
 
32
- ws.addEventListener("close", (event) => {
33
- void Effect.runPromiseExit(
34
- Queue.offer(
35
- incoming,
36
- Either.left(
37
- new DisconnectedError(`${event.code}: ${event.reason}`),
38
- ),
39
- ),
40
- ).then((e) => {
41
- if (Exit.isFailure(e) && !Exit.isInterrupted(e)) {
42
- console.warn("Failed closing ws", e);
43
- }
44
- });
45
- });
46
-
47
- let pingTimeout: ReturnType<typeof setTimeout> | undefined;
48
-
49
- ws.addEventListener("message", (event) => {
50
- const msg = JSON.parse(event.data as string);
51
-
52
- if (pingTimeout) {
53
- clearTimeout(pingTimeout);
54
- }
55
-
56
- pingTimeout = setTimeout(() => {
57
- console.debug("Ping timeout");
58
- void Effect.runPromise(
59
- Queue.offer(incoming, Either.left(new PingTimeoutError())),
60
- );
61
- try {
62
- ws.close();
63
- } catch (e) {
64
- console.error(
65
- "Error while trying to close ws on ping timeout",
66
- e,
67
- );
68
- }
69
- }, 2500);
46
+ const closed = once(ws, "close").pipe(
47
+ Effect.flatMap(
48
+ (event) =>
49
+ new DisconnectedError({
50
+ message: `${event.code}: ${event.reason}`,
51
+ }),
52
+ ),
53
+ Stream.fromEffect,
54
+ );
70
55
 
71
- if (msg.type === "ping") {
72
- // eslint-disable-next-line @typescript-eslint/no-explicit-any
73
- (globalThis as any).jazzPings =
74
- // eslint-disable-next-line @typescript-eslint/no-explicit-any
75
- (globalThis as any).jazzPings || [];
76
- // eslint-disable-next-line @typescript-eslint/no-explicit-any
77
- (globalThis as any).jazzPings.push({
56
+ const isSyncMessage = (msg: unknown): msg is SyncMessage => {
57
+ // eslint-disable-next-line @typescript-eslint/no-explicit-any
58
+ if ((msg as any)?.type === "ping") {
59
+ const ping = msg as PingMsg;
60
+ g.jazzPings ||= [];
61
+ g.jazzPings.push({
78
62
  received: Date.now(),
79
- sent: msg.time,
80
- dc: msg.dc,
63
+ sent: ping.time,
64
+ dc: ping.dc,
81
65
  });
82
- return;
83
- } else {
84
- void Effect.runPromise(
85
- Queue.offer(incoming, Either.right(msg)),
86
- );
66
+ return false;
87
67
  }
88
- });
68
+ return true;
69
+ };
89
70
 
90
- ws.addEventListener("open", () => {
91
- void Stream.fromQueue(outgoing).pipe(
92
- Stream.runForEach((msg) =>
93
- Effect.sync(() => ws.send(JSON.stringify(msg))),
94
- ),
95
- Effect.runPromise,
71
+ yield* Effect.forkDaemon(Effect.gen(function* () {
72
+ yield* once(ws, "open");
73
+ yield* Queue.take(outgoing).pipe(
74
+ Effect.andThen((message) => ws.send(JSON.stringify(message))),
75
+ Effect.forever,
96
76
  );
97
- });
77
+ }));
78
+
79
+ type E = WebsocketEvents["message"];
80
+ const messages = Stream.fromEventListener<E>(ws_, "message").pipe(
81
+ Stream.timeoutFail(() => new PingTimeoutError(), "10 seconds"),
82
+ Stream.tapError((_e) =>
83
+ Console.warn("Ping timeout").pipe(
84
+ Effect.andThen(Effect.try(() => ws.close())),
85
+ Effect.catchAll((e) =>
86
+ Console.error(
87
+ "Error while trying to close ws on ping timeout",
88
+ e,
89
+ ),
90
+ ),
91
+ ),
92
+ ),
93
+ Stream.mergeLeft(closed),
94
+ Stream.map((_) => JSON.parse(_.data as string)),
95
+ Stream.filter(isSyncMessage),
96
+ Stream.buffer({ capacity: "unbounded" }),
97
+ Stream.onDone(() => Queue.shutdown(outgoing)),
98
+ );
98
99
 
99
100
  return {
100
101
  id: options.id,
101
- incoming: Stream.fromQueue(incoming, { shutdown: true }).pipe(
102
- Stream.mapEffect((either) => either),
103
- ),
102
+ incoming: messages,
104
103
  outgoing,
105
104
  role: options.role,
106
105
  };
107
106
  });
108
107
  }
108
+
109
+ const once = <Event extends keyof WebsocketEvents>(
110
+ ws: AnyWebSocket,
111
+ event: Event,
112
+ ) =>
113
+ Effect.async<WebsocketEvents[Event]>((register) => {
114
+ const cb = (msg: WebsocketEvents[Event]) => {
115
+ ws.removeEventListener(event, cb);
116
+ register(Effect.succeed(msg));
117
+ };
118
+ ws.addEventListener(event, cb);
119
+ });