cojson-transport-ws 0.8.13 → 0.8.17

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/src/index.ts CHANGED
@@ -1,209 +1,216 @@
1
1
  import {
2
- DisconnectedError,
3
- Peer,
4
- PingTimeoutError,
5
- SyncMessage,
6
- cojsonInternals,
2
+ DisconnectedError,
3
+ Peer,
4
+ PingTimeoutError,
5
+ SyncMessage,
6
+ cojsonInternals,
7
7
  } from "cojson";
8
- import { AnyWebSocket } from "./types.js";
9
8
  import { BatchedOutgoingMessages } from "./BatchedOutgoingMessages.js";
10
9
  import { deserializeMessages } from "./serialization.js";
10
+ import { AnyWebSocket } from "./types.js";
11
11
 
12
12
  export const BUFFER_LIMIT = 100_000;
13
13
  export const BUFFER_LIMIT_POLLING_INTERVAL = 10;
14
14
 
15
15
  export type CreateWebSocketPeerOpts = {
16
- id: string;
17
- websocket: AnyWebSocket;
18
- role: Peer["role"];
19
- expectPings?: boolean;
20
- batchingByDefault?: boolean;
21
- onClose?: () => void;
16
+ id: string;
17
+ websocket: AnyWebSocket;
18
+ role: Peer["role"];
19
+ expectPings?: boolean;
20
+ batchingByDefault?: boolean;
21
+ onClose?: () => void;
22
22
  };
23
23
 
24
24
  function createPingTimeoutListener(enabled: boolean, callback: () => void) {
25
- if (!enabled) {
26
- return {
27
- reset() {},
28
- clear() {}
29
- }
30
- }
31
-
32
- let pingTimeout: ReturnType<typeof setTimeout> | null = null;
33
-
25
+ if (!enabled) {
34
26
  return {
35
- reset() {
36
- pingTimeout && clearTimeout(pingTimeout);
37
- pingTimeout = setTimeout(() => {
38
- callback();
39
- }, 10_000);
40
- },
41
- clear() {
42
- pingTimeout && clearTimeout(pingTimeout);
43
- }
27
+ reset() {},
28
+ clear() {},
44
29
  };
30
+ }
31
+
32
+ let pingTimeout: ReturnType<typeof setTimeout> | null = null;
33
+
34
+ return {
35
+ reset() {
36
+ pingTimeout && clearTimeout(pingTimeout);
37
+ pingTimeout = setTimeout(() => {
38
+ callback();
39
+ }, 10_000);
40
+ },
41
+ clear() {
42
+ pingTimeout && clearTimeout(pingTimeout);
43
+ },
44
+ };
45
45
  }
46
46
 
47
47
  function waitForWebSocketOpen(websocket: AnyWebSocket) {
48
- return new Promise<void>((resolve) => {
49
- if (websocket.readyState === 1) {
50
- resolve();
51
- } else {
52
- websocket.addEventListener("open", resolve, { once: true });
53
- }
54
- });
48
+ return new Promise<void>((resolve) => {
49
+ if (websocket.readyState === 1) {
50
+ resolve();
51
+ } else {
52
+ websocket.addEventListener("open", resolve, { once: true });
53
+ }
54
+ });
55
55
  }
56
56
 
57
- function createOutgoingMessagesManager(websocket: AnyWebSocket, batchingByDefault: boolean) {
58
- const outgoingMessages = new BatchedOutgoingMessages((messages) => {
59
- if (websocket.readyState === 1) {
60
- websocket.send(messages);
61
- }
62
- });
63
-
64
- let batchingEnabled = batchingByDefault;
57
+ function createOutgoingMessagesManager(
58
+ websocket: AnyWebSocket,
59
+ batchingByDefault: boolean,
60
+ ) {
61
+ const outgoingMessages = new BatchedOutgoingMessages((messages) => {
62
+ if (websocket.readyState === 1) {
63
+ websocket.send(messages);
64
+ }
65
+ });
65
66
 
66
- async function sendMessage(msg: SyncMessage) {
67
- if (websocket.readyState !== 1) {
68
- await waitForWebSocketOpen(websocket);
69
- }
67
+ let batchingEnabled = batchingByDefault;
70
68
 
71
- while (
72
- websocket.bufferedAmount > BUFFER_LIMIT &&
73
- websocket.readyState === 1
74
- ) {
75
- await new Promise<void>((resolve) =>
76
- setTimeout(resolve, BUFFER_LIMIT_POLLING_INTERVAL),
77
- );
78
- }
69
+ async function sendMessage(msg: SyncMessage) {
70
+ if (websocket.readyState !== 1) {
71
+ await waitForWebSocketOpen(websocket);
72
+ }
79
73
 
80
- if (websocket.readyState !== 1) {
81
- return;
82
- }
74
+ while (
75
+ websocket.bufferedAmount > BUFFER_LIMIT &&
76
+ websocket.readyState === 1
77
+ ) {
78
+ await new Promise<void>((resolve) =>
79
+ setTimeout(resolve, BUFFER_LIMIT_POLLING_INTERVAL),
80
+ );
81
+ }
83
82
 
84
- if (!batchingEnabled) {
85
- websocket.send(JSON.stringify(msg));
86
- } else {
87
- outgoingMessages.push(msg);
88
- }
83
+ if (websocket.readyState !== 1) {
84
+ return;
89
85
  }
90
86
 
91
- return {
92
- sendMessage,
93
- setBatchingEnabled(enabled: boolean) {
94
- batchingEnabled = enabled;
95
- },
96
- close() {
97
- outgoingMessages.close();
98
- },
99
- };
87
+ if (!batchingEnabled) {
88
+ websocket.send(JSON.stringify(msg));
89
+ } else {
90
+ outgoingMessages.push(msg);
91
+ }
92
+ }
93
+
94
+ return {
95
+ sendMessage,
96
+ setBatchingEnabled(enabled: boolean) {
97
+ batchingEnabled = enabled;
98
+ },
99
+ close() {
100
+ outgoingMessages.close();
101
+ },
102
+ };
100
103
  }
101
104
 
102
105
  function createClosedEventEmitter(callback = () => {}) {
103
- let disconnected = false;
106
+ let disconnected = false;
104
107
 
105
- return () => {
106
- if (disconnected) return;
107
- disconnected = true;
108
- callback();
109
- }
108
+ return () => {
109
+ if (disconnected) return;
110
+ disconnected = true;
111
+ callback();
112
+ };
110
113
  }
111
114
 
112
115
  export function createWebSocketPeer({
113
- id,
114
- websocket,
115
- role,
116
- expectPings = true,
117
- batchingByDefault = true,
118
- onClose,
116
+ id,
117
+ websocket,
118
+ role,
119
+ expectPings = true,
120
+ batchingByDefault = true,
121
+ onClose,
119
122
  }: CreateWebSocketPeerOpts): Peer {
120
- const incoming = new cojsonInternals.Channel<
121
- SyncMessage | DisconnectedError | PingTimeoutError
122
- >();
123
- const emitClosedEvent = createClosedEventEmitter(onClose);
123
+ const incoming = new cojsonInternals.Channel<
124
+ SyncMessage | DisconnectedError | PingTimeoutError
125
+ >();
126
+ const emitClosedEvent = createClosedEventEmitter(onClose);
127
+
128
+ function handleClose() {
129
+ incoming
130
+ .push("Disconnected")
131
+ .catch((e) => console.error("Error while pushing disconnect msg", e));
132
+ emitClosedEvent();
133
+ }
134
+
135
+ websocket.addEventListener("close", handleClose);
136
+
137
+ const pingTimeout = createPingTimeoutListener(expectPings, () => {
138
+ incoming
139
+ .push("PingTimeout")
140
+ .catch((e) => console.error("Error while pushing ping timeout", e));
141
+ emitClosedEvent();
142
+ });
143
+
144
+ const outgoingMessages = createOutgoingMessagesManager(
145
+ websocket,
146
+ batchingByDefault,
147
+ );
124
148
 
125
- function handleClose() {
126
- incoming
127
- .push("Disconnected")
128
- .catch((e) =>
129
- console.error("Error while pushing disconnect msg", e),
130
- );
131
- emitClosedEvent();
149
+ function handleIncomingMsg(event: { data: unknown }) {
150
+ if (event.data === "") {
151
+ console.log("client", id, "sent empty message");
152
+ return;
132
153
  }
133
154
 
134
- websocket.addEventListener("close", handleClose);
155
+ const result = deserializeMessages(event.data);
135
156
 
136
- const pingTimeout = createPingTimeoutListener(expectPings, () => {
137
- incoming
138
- .push("PingTimeout")
139
- .catch((e) => console.error("Error while pushing ping timeout", e));
140
- emitClosedEvent();
141
- });
142
-
143
- const outgoingMessages = createOutgoingMessagesManager(websocket, batchingByDefault);
144
-
145
- function handleIncomingMsg(event: { data: unknown }) {
146
- const result = deserializeMessages(event.data);
147
-
148
- if (!result.ok) {
149
- console.error(
150
- "Error while deserializing messages",
151
- event.data,
152
- result.error,
153
- );
154
- return;
155
- }
157
+ if (!result.ok) {
158
+ console.error(
159
+ "Error while deserializing messages",
160
+ event.data,
161
+ result.error,
162
+ );
163
+ return;
164
+ }
156
165
 
157
- const { messages } = result;
166
+ const { messages } = result;
158
167
 
159
- if (messages.length > 1) {
160
- // If more than one message is received, the other peer supports batching
161
- outgoingMessages.setBatchingEnabled(true);
162
- }
168
+ if (messages.length > 1) {
169
+ // If more than one message is received, the other peer supports batching
170
+ outgoingMessages.setBatchingEnabled(true);
171
+ }
163
172
 
164
- pingTimeout.reset();
173
+ pingTimeout.reset();
165
174
 
166
- for (const msg of messages) {
167
- if (msg && "action" in msg) {
168
- incoming
169
- .push(msg)
170
- .catch((e) =>
171
- console.error("Error while pushing incoming msg", e),
172
- );
173
- }
174
- }
175
+ for (const msg of messages) {
176
+ if (msg && "action" in msg) {
177
+ incoming
178
+ .push(msg)
179
+ .catch((e) => console.error("Error while pushing incoming msg", e));
180
+ }
175
181
  }
182
+ }
176
183
 
177
- websocket.addEventListener("message", handleIncomingMsg);
184
+ websocket.addEventListener("message", handleIncomingMsg);
178
185
 
179
- return {
180
- id,
181
- incoming,
182
- outgoing: {
183
- push: outgoingMessages.sendMessage,
184
- close() {
185
- console.log("Trying to close", id, websocket.readyState);
186
- outgoingMessages.close();
187
-
188
- websocket.removeEventListener("message", handleIncomingMsg);
189
- websocket.removeEventListener("close", handleClose);
190
- pingTimeout.clear();
191
- emitClosedEvent();
192
-
193
- if (websocket.readyState === 0) {
194
- websocket.addEventListener(
195
- "open",
196
- function handleClose() {
197
- websocket.close();
198
- },
199
- { once: true },
200
- );
201
- } else if (websocket.readyState == 1) {
202
- websocket.close();
203
- }
186
+ return {
187
+ id,
188
+ incoming,
189
+ outgoing: {
190
+ push: outgoingMessages.sendMessage,
191
+ close() {
192
+ console.log("Trying to close", id, websocket.readyState);
193
+ outgoingMessages.close();
194
+
195
+ websocket.removeEventListener("message", handleIncomingMsg);
196
+ websocket.removeEventListener("close", handleClose);
197
+ pingTimeout.clear();
198
+ emitClosedEvent();
199
+
200
+ if (websocket.readyState === 0) {
201
+ websocket.addEventListener(
202
+ "open",
203
+ function handleClose() {
204
+ websocket.close();
204
205
  },
205
- },
206
- role,
207
- crashOnClose: false,
208
- };
206
+ { once: true },
207
+ );
208
+ } else if (websocket.readyState == 1) {
209
+ websocket.close();
210
+ }
211
+ },
212
+ },
213
+ role,
214
+ crashOnClose: false,
215
+ };
209
216
  }
@@ -2,30 +2,32 @@ import { SyncMessage } from "cojson";
2
2
  import { PingMsg } from "./types.js";
3
3
 
4
4
  export function addMessageToBacklog(backlog: string, message: SyncMessage) {
5
- if (!backlog) {
6
- return JSON.stringify(message);
7
- }
8
- return `${backlog}\n${JSON.stringify(message)}`;
5
+ if (!backlog) {
6
+ return JSON.stringify(message);
7
+ }
8
+ return `${backlog}\n${JSON.stringify(message)}`;
9
9
  }
10
10
 
11
- export function deserializeMessages(messages: unknown) {
12
- if (typeof messages !== "string") {
13
- return {
14
- ok: false,
15
- error: new Error("Expected a string"),
16
- } as const;
17
- }
11
+ export function deserializeMessages(messages: unknown) {
12
+ if (typeof messages !== "string") {
13
+ return {
14
+ ok: false,
15
+ error: new Error("Expected a string"),
16
+ } as const;
17
+ }
18
18
 
19
- try {
20
- return {
21
- ok: true,
22
- messages: messages.split("\n").map((msg) => JSON.parse(msg)) as SyncMessage[] | PingMsg[],
23
- } as const;
24
- } catch (e) {
25
- console.error("Error while deserializing messages", e);
26
- return {
27
- ok: false,
28
- error: e,
29
- } as const;
30
- }
19
+ try {
20
+ return {
21
+ ok: true,
22
+ messages: messages.split("\n").map((msg) => JSON.parse(msg)) as
23
+ | SyncMessage[]
24
+ | PingMsg[],
25
+ } as const;
26
+ } catch (e) {
27
+ console.error("Error while deserializing messages", e);
28
+ return {
29
+ ok: false,
30
+ error: e,
31
+ } as const;
32
+ }
31
33
  }