cojson-transport-ws 0.8.13 → 0.8.16

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,211 @@
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);
124
-
125
- function handleClose() {
126
- incoming
127
- .push("Disconnected")
128
- .catch((e) =>
129
- console.error("Error while pushing disconnect msg", e),
130
- );
131
- emitClosedEvent();
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
+ );
148
+
149
+ function handleIncomingMsg(event: { data: unknown }) {
150
+ const result = deserializeMessages(event.data);
151
+
152
+ if (!result.ok) {
153
+ console.error(
154
+ "Error while deserializing messages",
155
+ event.data,
156
+ result.error,
157
+ );
158
+ return;
132
159
  }
133
160
 
134
- websocket.addEventListener("close", handleClose);
135
-
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
- }
156
-
157
- const { messages } = result;
161
+ const { messages } = result;
158
162
 
159
- if (messages.length > 1) {
160
- // If more than one message is received, the other peer supports batching
161
- outgoingMessages.setBatchingEnabled(true);
162
- }
163
+ if (messages.length > 1) {
164
+ // If more than one message is received, the other peer supports batching
165
+ outgoingMessages.setBatchingEnabled(true);
166
+ }
163
167
 
164
- pingTimeout.reset();
168
+ pingTimeout.reset();
165
169
 
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
- }
170
+ for (const msg of messages) {
171
+ if (msg && "action" in msg) {
172
+ incoming
173
+ .push(msg)
174
+ .catch((e) => console.error("Error while pushing incoming msg", e));
175
+ }
175
176
  }
177
+ }
176
178
 
177
- websocket.addEventListener("message", handleIncomingMsg);
179
+ websocket.addEventListener("message", handleIncomingMsg);
178
180
 
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
- }
181
+ return {
182
+ id,
183
+ incoming,
184
+ outgoing: {
185
+ push: outgoingMessages.sendMessage,
186
+ close() {
187
+ console.log("Trying to close", id, websocket.readyState);
188
+ outgoingMessages.close();
189
+
190
+ websocket.removeEventListener("message", handleIncomingMsg);
191
+ websocket.removeEventListener("close", handleClose);
192
+ pingTimeout.clear();
193
+ emitClosedEvent();
194
+
195
+ if (websocket.readyState === 0) {
196
+ websocket.addEventListener(
197
+ "open",
198
+ function handleClose() {
199
+ websocket.close();
204
200
  },
205
- },
206
- role,
207
- crashOnClose: false,
208
- };
201
+ { once: true },
202
+ );
203
+ } else if (websocket.readyState == 1) {
204
+ websocket.close();
205
+ }
206
+ },
207
+ },
208
+ role,
209
+ crashOnClose: false,
210
+ };
209
211
  }
@@ -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
  }