cojson-transport-ws 0.13.27 → 0.13.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,4 +1,4 @@
1
1
 
2
- > cojson-transport-ws@0.13.27 build /home/runner/_work/jazz/jazz/packages/cojson-transport-ws
2
+ > cojson-transport-ws@0.13.28 build /home/runner/_work/jazz/jazz/packages/cojson-transport-ws
3
3
  > rm -rf ./dist && tsc --sourceMap --outDir dist
4
4
 
package/CHANGELOG.md CHANGED
@@ -1,5 +1,13 @@
1
1
  # cojson-transport-nodejs-ws
2
2
 
3
+ ## 0.13.28
4
+
5
+ ### Patch Changes
6
+
7
+ - 422dbc4: Add waitUntilConnected and subscribe APIs on WebSocketPeerWithReconnection
8
+ - Updated dependencies [e7ccb2c]
9
+ - cojson@0.13.28
10
+
3
11
  ## 0.13.27
4
12
 
5
13
  ### Patch Changes
@@ -1,21 +1,31 @@
1
1
  import { type Peer } from "cojson";
2
+ import type { AnyWebSocketConstructor } from "./types.js";
2
3
  export declare class WebSocketPeerWithReconnection {
3
4
  private peer;
4
5
  private reconnectionTimeout;
5
6
  private addPeer;
6
7
  private removePeer;
8
+ private WebSocketConstructor;
9
+ private pingTimeout;
7
10
  constructor(opts: {
8
11
  peer: string;
9
12
  reconnectionTimeout: number | undefined;
10
13
  addPeer: (peer: Peer) => void;
11
14
  removePeer: (peer: Peer) => void;
15
+ WebSocketConstructor?: AnyWebSocketConstructor;
16
+ pingTimeout?: number;
12
17
  });
13
- state: "disabled" | "enabled";
18
+ enabled: boolean;
19
+ closed: boolean;
14
20
  currentPeer: Peer | undefined;
15
- unsubscribeNetworkChange: (() => void) | undefined;
21
+ private unsubscribeNetworkChange;
16
22
  onNetworkChange(callback: (connected: boolean) => void): () => void;
17
- waitForOnline(timeout: number): Promise<void>;
23
+ private waitForOnline;
18
24
  reconnectionAttempts: number;
25
+ onConnectionChangeListeners: Set<(connected: boolean) => void>;
26
+ waitUntilConnected: () => Promise<void>;
27
+ subscribe: (listener: (connected: boolean) => void) => void;
28
+ unsubscribe: (listener: (connected: boolean) => void) => void;
19
29
  startConnection: () => Promise<void>;
20
30
  enable: () => void;
21
31
  disable: () => void;
@@ -1 +1 @@
1
- {"version":3,"file":"WebSocketPeerWithReconnection.d.ts","sourceRoot":"","sources":["../src/WebSocketPeerWithReconnection.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,KAAK,IAAI,EAAU,MAAM,QAAQ,CAAC;AAG3C,qBAAa,6BAA6B;IACxC,OAAO,CAAC,IAAI,CAAS;IACrB,OAAO,CAAC,mBAAmB,CAAS;IACpC,OAAO,CAAC,OAAO,CAAuB;IACtC,OAAO,CAAC,UAAU,CAAuB;gBAE7B,IAAI,EAAE;QAChB,IAAI,EAAE,MAAM,CAAC;QACb,mBAAmB,EAAE,MAAM,GAAG,SAAS,CAAC;QACxC,OAAO,EAAE,CAAC,IAAI,EAAE,IAAI,KAAK,IAAI,CAAC;QAC9B,UAAU,EAAE,CAAC,IAAI,EAAE,IAAI,KAAK,IAAI,CAAC;KAClC;IAOD,KAAK,EAAE,UAAU,GAAG,SAAS,CAAc;IAC3C,WAAW,EAAE,IAAI,GAAG,SAAS,CAAa;IAC1C,wBAAwB,EAAE,CAAC,MAAM,IAAI,CAAC,GAAG,SAAS,CAAa;IAI/D,eAAe,CAAC,QAAQ,EAAE,CAAC,SAAS,EAAE,OAAO,KAAK,IAAI,GAAG,MAAM,IAAI;IAKnE,aAAa,CAAC,OAAO,EAAE,MAAM;IAkB7B,oBAAoB,SAAK;IAEzB,eAAe,sBAgCb;IAEF,MAAM,aAKJ;IAEF,OAAO,aAaL;CACH"}
1
+ {"version":3,"file":"WebSocketPeerWithReconnection.d.ts","sourceRoot":"","sources":["../src/WebSocketPeerWithReconnection.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,KAAK,IAAI,EAAU,MAAM,QAAQ,CAAC;AAE3C,OAAO,KAAK,EAAE,uBAAuB,EAAE,MAAM,YAAY,CAAC;AAE1D,qBAAa,6BAA6B;IACxC,OAAO,CAAC,IAAI,CAAS;IACrB,OAAO,CAAC,mBAAmB,CAAS;IACpC,OAAO,CAAC,OAAO,CAAuB;IACtC,OAAO,CAAC,UAAU,CAAuB;IACzC,OAAO,CAAC,oBAAoB,CAA0B;IACtD,OAAO,CAAC,WAAW,CAAS;gBAEhB,IAAI,EAAE;QAChB,IAAI,EAAE,MAAM,CAAC;QACb,mBAAmB,EAAE,MAAM,GAAG,SAAS,CAAC;QACxC,OAAO,EAAE,CAAC,IAAI,EAAE,IAAI,KAAK,IAAI,CAAC;QAC9B,UAAU,EAAE,CAAC,IAAI,EAAE,IAAI,KAAK,IAAI,CAAC;QACjC,oBAAoB,CAAC,EAAE,uBAAuB,CAAC;QAC/C,WAAW,CAAC,EAAE,MAAM,CAAC;KACtB;IASD,OAAO,UAAS;IAChB,MAAM,UAAQ;IAEd,WAAW,EAAE,IAAI,GAAG,SAAS,CAAa;IAC1C,OAAO,CAAC,wBAAwB,CAAuC;IAIvE,eAAe,CAAC,QAAQ,EAAE,CAAC,SAAS,EAAE,OAAO,KAAK,IAAI,GAAG,MAAM,IAAI;IAKnE,OAAO,CAAC,aAAa;IAkBrB,oBAAoB,SAAK;IAEzB,2BAA2B,kBAAuB,OAAO,KAAK,IAAI,EAAI;IAEtE,kBAAkB,sBAahB;IAEF,SAAS,aAAc,CAAC,SAAS,EAAE,OAAO,KAAK,IAAI,UAGjD;IAEF,WAAW,aAAc,CAAC,SAAS,EAAE,OAAO,KAAK,IAAI,UAEnD;IAEF,eAAe,sBA4Cb;IAEF,MAAM,aAKJ;IAEF,OAAO,aAcL;CACH"}
@@ -2,28 +2,62 @@ import { logger } from "cojson";
2
2
  import { createWebSocketPeer } from "./createWebSocketPeer.js";
3
3
  export class WebSocketPeerWithReconnection {
4
4
  constructor(opts) {
5
- this.state = "disabled";
5
+ this.enabled = false;
6
+ this.closed = true;
6
7
  this.currentPeer = undefined;
7
8
  this.unsubscribeNetworkChange = undefined;
8
9
  this.reconnectionAttempts = 0;
10
+ this.onConnectionChangeListeners = new Set();
11
+ this.waitUntilConnected = async () => {
12
+ if (this.closed) {
13
+ return new Promise((resolve) => {
14
+ const listener = (connected) => {
15
+ if (connected) {
16
+ resolve();
17
+ this.onConnectionChangeListeners.delete(listener);
18
+ }
19
+ };
20
+ this.onConnectionChangeListeners.add(listener);
21
+ });
22
+ }
23
+ };
24
+ this.subscribe = (listener) => {
25
+ this.onConnectionChangeListeners.add(listener);
26
+ listener(!this.closed);
27
+ };
28
+ this.unsubscribe = (listener) => {
29
+ this.onConnectionChangeListeners.delete(listener);
30
+ };
9
31
  this.startConnection = async () => {
10
- if (this.state !== "enabled")
32
+ if (!this.enabled)
11
33
  return;
12
34
  if (this.currentPeer) {
13
35
  this.removePeer(this.currentPeer);
36
+ this.currentPeer.outgoing.close();
14
37
  this.reconnectionAttempts++;
15
38
  const timeout = this.reconnectionTimeout * this.reconnectionAttempts;
16
39
  logger.debug(`Websocket disconnected, trying to reconnect in ${timeout}ms`);
17
40
  await this.waitForOnline(timeout);
18
41
  }
19
- if (this.state !== "enabled")
42
+ if (!this.enabled)
20
43
  return;
21
44
  this.currentPeer = createWebSocketPeer({
22
- websocket: new WebSocket(this.peer),
45
+ websocket: new this.WebSocketConstructor(this.peer),
46
+ pingTimeout: this.pingTimeout,
23
47
  id: this.peer,
24
48
  role: "server",
25
- onClose: this.startConnection,
49
+ onClose: () => {
50
+ this.closed = true;
51
+ for (const listener of this.onConnectionChangeListeners) {
52
+ listener(false);
53
+ }
54
+ this.startConnection();
55
+ },
26
56
  onSuccess: () => {
57
+ this.closed = false;
58
+ for (const listener of this.onConnectionChangeListeners) {
59
+ listener(true);
60
+ }
27
61
  logger.debug("Websocket connection successful");
28
62
  this.reconnectionAttempts = 0;
29
63
  },
@@ -31,20 +65,21 @@ export class WebSocketPeerWithReconnection {
31
65
  this.addPeer(this.currentPeer);
32
66
  };
33
67
  this.enable = () => {
34
- if (this.state === "enabled")
68
+ if (this.enabled)
35
69
  return;
36
- this.state = "enabled";
70
+ this.enabled = true;
37
71
  this.startConnection();
38
72
  };
39
73
  this.disable = () => {
40
- if (this.state === "disabled")
74
+ if (!this.enabled)
41
75
  return;
42
- this.state = "disabled";
76
+ this.enabled = false;
43
77
  this.reconnectionAttempts = 0;
44
78
  this.unsubscribeNetworkChange?.();
45
79
  this.unsubscribeNetworkChange = undefined;
46
80
  if (this.currentPeer) {
47
81
  this.removePeer(this.currentPeer);
82
+ this.currentPeer.outgoing.close();
48
83
  this.currentPeer = undefined;
49
84
  }
50
85
  };
@@ -52,6 +87,8 @@ export class WebSocketPeerWithReconnection {
52
87
  this.reconnectionTimeout = opts.reconnectionTimeout || 500;
53
88
  this.addPeer = opts.addPeer;
54
89
  this.removePeer = opts.removePeer;
90
+ this.WebSocketConstructor = opts.WebSocketConstructor || WebSocket;
91
+ this.pingTimeout = opts.pingTimeout || 10000;
55
92
  }
56
93
  // Basic implementation for environments that don't support network change events (e.g. Node.js)
57
94
  // Needs to be extended to handle platform specific APIs
@@ -68,7 +105,7 @@ export class WebSocketPeerWithReconnection {
68
105
  });
69
106
  function handleTimeoutOrOnline() {
70
107
  clearTimeout(timer);
71
- unsubscribeNetworkChange();
108
+ unsubscribeNetworkChange?.();
72
109
  resolve();
73
110
  }
74
111
  const timer = setTimeout(handleTimeoutOrOnline, timeout);
@@ -1 +1 @@
1
- {"version":3,"file":"WebSocketPeerWithReconnection.js","sourceRoot":"","sources":["../src/WebSocketPeerWithReconnection.ts"],"names":[],"mappings":"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"}
1
+ {"version":3,"file":"WebSocketPeerWithReconnection.js","sourceRoot":"","sources":["../src/WebSocketPeerWithReconnection.ts"],"names":[],"mappings":"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"}
@@ -101,5 +101,183 @@ describe("WebSocketPeerWithReconnection", () => {
101
101
  await waitFor(() => expect(peer.reconnectionAttempts).toBe(0));
102
102
  peer.disable();
103
103
  });
104
+ describe("waitUntilConnected", () => {
105
+ test("should wait until connected before resolving", async () => {
106
+ const addPeer = vi.fn();
107
+ const removePeer = vi.fn();
108
+ const peer = new WebSocketPeerWithReconnection({
109
+ peer: syncServerUrl,
110
+ reconnectionTimeout: 100,
111
+ addPeer,
112
+ removePeer,
113
+ });
114
+ // Start waiting for connection before enabling
115
+ const waitPromise = peer.waitUntilConnected();
116
+ // Enable the peer after a short delay
117
+ setTimeout(() => peer.enable(), 100);
118
+ // Wait for connection to be established
119
+ await waitPromise;
120
+ expect(addPeer).toHaveBeenCalledTimes(1);
121
+ expect(peer.closed).toBe(false);
122
+ peer.disable();
123
+ });
124
+ test("should resolve immediately if already connected", async () => {
125
+ const addPeer = vi.fn();
126
+ const removePeer = vi.fn();
127
+ const peer = new WebSocketPeerWithReconnection({
128
+ peer: syncServerUrl,
129
+ reconnectionTimeout: 100,
130
+ addPeer,
131
+ removePeer,
132
+ });
133
+ // Enable the peer first
134
+ peer.enable();
135
+ // Wait for initial connection
136
+ await new Promise((resolve) => setTimeout(resolve, 100));
137
+ // Now wait for connection again
138
+ const waitPromise = peer.waitUntilConnected();
139
+ // Should resolve immediately since we're already connected
140
+ await waitPromise;
141
+ expect(addPeer).toHaveBeenCalledTimes(1);
142
+ expect(peer.closed).toBe(false);
143
+ peer.disable();
144
+ });
145
+ test("should work when connection is lost and regained", async () => {
146
+ const addPeer = vi.fn();
147
+ const removePeer = vi.fn();
148
+ const peer = new WebSocketPeerWithReconnection({
149
+ peer: syncServerUrl,
150
+ reconnectionTimeout: 100,
151
+ addPeer,
152
+ removePeer,
153
+ });
154
+ peer.enable();
155
+ // Wait for initial connection
156
+ await new Promise((resolve) => setTimeout(resolve, 100));
157
+ // Close server to simulate connection loss
158
+ server.close();
159
+ // Wait for disconnect to be detected
160
+ await new Promise((resolve) => setTimeout(resolve, 200));
161
+ // Start server again
162
+ server = await startSyncServer(server.port);
163
+ // Wait for the waitUntilConnected promise to resolve
164
+ await peer.waitUntilConnected();
165
+ // Verify that we have a new connection
166
+ expect(addPeer).toHaveBeenCalledTimes(3); // Once for initial, once for reconnection
167
+ expect(peer.closed).toBe(false);
168
+ peer.disable();
169
+ });
170
+ });
171
+ describe("subscribe", () => {
172
+ test("should notify subscribers of initial connection state", async () => {
173
+ const addPeer = vi.fn();
174
+ const removePeer = vi.fn();
175
+ const listener = vi.fn();
176
+ const peer = new WebSocketPeerWithReconnection({
177
+ peer: syncServerUrl,
178
+ reconnectionTimeout: 100,
179
+ addPeer,
180
+ removePeer,
181
+ });
182
+ // Subscribe before enabling
183
+ peer.subscribe(listener);
184
+ // Initial state should be disconnected
185
+ expect(listener).toHaveBeenCalledWith(false);
186
+ // Enable the peer
187
+ peer.enable();
188
+ // Wait for connection
189
+ await new Promise((resolve) => setTimeout(resolve, 100));
190
+ // Should notify of connected state
191
+ expect(listener).toHaveBeenCalledWith(true);
192
+ peer.disable();
193
+ });
194
+ test("should notify subscribers of connection state changes", async () => {
195
+ const addPeer = vi.fn();
196
+ const removePeer = vi.fn();
197
+ const listener = vi.fn();
198
+ const peer = new WebSocketPeerWithReconnection({
199
+ peer: syncServerUrl,
200
+ reconnectionTimeout: 100,
201
+ addPeer,
202
+ removePeer,
203
+ });
204
+ peer.enable();
205
+ peer.subscribe(listener);
206
+ // Wait for initial connection
207
+ await new Promise((resolve) => setTimeout(resolve, 100));
208
+ // Should notify of connected state
209
+ expect(listener).toHaveBeenCalledWith(true);
210
+ // Close server to simulate disconnect
211
+ server.close();
212
+ // Wait for disconnect to be detected
213
+ await new Promise((resolve) => setTimeout(resolve, 200));
214
+ // Should notify of disconnected state
215
+ expect(listener).toHaveBeenCalledWith(false);
216
+ peer.disable();
217
+ });
218
+ test("should not notify unsubscribed listeners", async () => {
219
+ const addPeer = vi.fn();
220
+ const removePeer = vi.fn();
221
+ const listener1 = vi.fn();
222
+ const listener2 = vi.fn();
223
+ const peer = new WebSocketPeerWithReconnection({
224
+ peer: syncServerUrl,
225
+ reconnectionTimeout: 100,
226
+ addPeer,
227
+ removePeer,
228
+ });
229
+ peer.enable();
230
+ peer.subscribe(listener1);
231
+ peer.subscribe(listener2);
232
+ // Wait for initial connection
233
+ await new Promise((resolve) => setTimeout(resolve, 100));
234
+ // Both listeners should be notified of connection
235
+ expect(listener1).toHaveBeenCalledWith(true);
236
+ expect(listener2).toHaveBeenCalledWith(true);
237
+ // Unsubscribe listener1
238
+ peer.unsubscribe(listener1);
239
+ listener1.mockClear();
240
+ listener2.mockClear();
241
+ // Close server to simulate disconnect
242
+ server.close();
243
+ // Wait for disconnect to be detected
244
+ await new Promise((resolve) => setTimeout(resolve, 200));
245
+ // Only listener2 should be notified of disconnect
246
+ expect(listener1).not.toHaveBeenCalled();
247
+ expect(listener2).toHaveBeenCalledWith(false);
248
+ peer.disable();
249
+ });
250
+ test("should handle multiple subscribers correctly", async () => {
251
+ const addPeer = vi.fn();
252
+ const removePeer = vi.fn();
253
+ const listeners = Array.from({ length: 3 }, () => vi.fn());
254
+ const peer = new WebSocketPeerWithReconnection({
255
+ peer: syncServerUrl,
256
+ reconnectionTimeout: 100,
257
+ addPeer,
258
+ removePeer,
259
+ });
260
+ peer.enable();
261
+ // Subscribe all listeners
262
+ for (const listener of listeners) {
263
+ peer.subscribe(listener);
264
+ }
265
+ // Wait for initial connection
266
+ await new Promise((resolve) => setTimeout(resolve, 100));
267
+ // All listeners should be notified of connection
268
+ for (const listener of listeners) {
269
+ expect(listener).toHaveBeenCalledWith(true);
270
+ }
271
+ // Close server to simulate disconnect
272
+ server.close();
273
+ // Wait for disconnect to be detected
274
+ await new Promise((resolve) => setTimeout(resolve, 200));
275
+ // All listeners should be notified of disconnect
276
+ for (const listener of listeners) {
277
+ expect(listener).toHaveBeenCalledWith(false);
278
+ }
279
+ peer.disable();
280
+ });
281
+ });
104
282
  });
105
283
  //# sourceMappingURL=WebSocketPeerWithReconnection.test.js.map
@@ -1 +1 @@
1
- {"version":3,"file":"WebSocketPeerWithReconnection.test.js","sourceRoot":"","sources":["../../src/tests/WebSocketPeerWithReconnection.test.ts"],"names":[],"mappings":"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"}
1
+ {"version":3,"file":"WebSocketPeerWithReconnection.test.js","sourceRoot":"","sources":["../../src/tests/WebSocketPeerWithReconnection.test.ts"],"names":[],"mappings":"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"}
package/package.json CHANGED
@@ -1,12 +1,12 @@
1
1
  {
2
2
  "name": "cojson-transport-ws",
3
3
  "type": "module",
4
- "version": "0.13.27",
4
+ "version": "0.13.28",
5
5
  "main": "dist/index.js",
6
6
  "types": "dist/index.d.ts",
7
7
  "license": "MIT",
8
8
  "dependencies": {
9
- "cojson": "0.13.27"
9
+ "cojson": "0.13.28"
10
10
  },
11
11
  "devDependencies": {
12
12
  "typescript": "5.6.2",
@@ -1,27 +1,36 @@
1
1
  import { type Peer, logger } from "cojson";
2
2
  import { createWebSocketPeer } from "./createWebSocketPeer.js";
3
+ import type { AnyWebSocketConstructor } from "./types.js";
3
4
 
4
5
  export class WebSocketPeerWithReconnection {
5
6
  private peer: string;
6
7
  private reconnectionTimeout: number;
7
8
  private addPeer: (peer: Peer) => void;
8
9
  private removePeer: (peer: Peer) => void;
10
+ private WebSocketConstructor: AnyWebSocketConstructor;
11
+ private pingTimeout: number;
9
12
 
10
13
  constructor(opts: {
11
14
  peer: string;
12
15
  reconnectionTimeout: number | undefined;
13
16
  addPeer: (peer: Peer) => void;
14
17
  removePeer: (peer: Peer) => void;
18
+ WebSocketConstructor?: AnyWebSocketConstructor;
19
+ pingTimeout?: number;
15
20
  }) {
16
21
  this.peer = opts.peer;
17
22
  this.reconnectionTimeout = opts.reconnectionTimeout || 500;
18
23
  this.addPeer = opts.addPeer;
19
24
  this.removePeer = opts.removePeer;
25
+ this.WebSocketConstructor = opts.WebSocketConstructor || WebSocket;
26
+ this.pingTimeout = opts.pingTimeout || 10_000;
20
27
  }
21
28
 
22
- state: "disabled" | "enabled" = "disabled";
29
+ enabled = false;
30
+ closed = true;
31
+
23
32
  currentPeer: Peer | undefined = undefined;
24
- unsubscribeNetworkChange: (() => void) | undefined = undefined;
33
+ private unsubscribeNetworkChange: (() => void) | undefined = undefined;
25
34
 
26
35
  // Basic implementation for environments that don't support network change events (e.g. Node.js)
27
36
  // Needs to be extended to handle platform specific APIs
@@ -30,7 +39,7 @@ export class WebSocketPeerWithReconnection {
30
39
  return () => {};
31
40
  }
32
41
 
33
- waitForOnline(timeout: number) {
42
+ private waitForOnline(timeout: number) {
34
43
  return new Promise<void>((resolve) => {
35
44
  const unsubscribeNetworkChange = this.onNetworkChange((connected) => {
36
45
  if (connected) {
@@ -40,7 +49,7 @@ export class WebSocketPeerWithReconnection {
40
49
 
41
50
  function handleTimeoutOrOnline() {
42
51
  clearTimeout(timer);
43
- unsubscribeNetworkChange();
52
+ unsubscribeNetworkChange?.();
44
53
  resolve();
45
54
  }
46
55
 
@@ -50,11 +59,38 @@ export class WebSocketPeerWithReconnection {
50
59
 
51
60
  reconnectionAttempts = 0;
52
61
 
62
+ onConnectionChangeListeners = new Set<(connected: boolean) => void>();
63
+
64
+ waitUntilConnected = async () => {
65
+ if (this.closed) {
66
+ return new Promise<void>((resolve) => {
67
+ const listener = (connected: boolean) => {
68
+ if (connected) {
69
+ resolve();
70
+ this.onConnectionChangeListeners.delete(listener);
71
+ }
72
+ };
73
+
74
+ this.onConnectionChangeListeners.add(listener);
75
+ });
76
+ }
77
+ };
78
+
79
+ subscribe = (listener: (connected: boolean) => void) => {
80
+ this.onConnectionChangeListeners.add(listener);
81
+ listener(!this.closed);
82
+ };
83
+
84
+ unsubscribe = (listener: (connected: boolean) => void) => {
85
+ this.onConnectionChangeListeners.delete(listener);
86
+ };
87
+
53
88
  startConnection = async () => {
54
- if (this.state !== "enabled") return;
89
+ if (!this.enabled) return;
55
90
 
56
91
  if (this.currentPeer) {
57
92
  this.removePeer(this.currentPeer);
93
+ this.currentPeer.outgoing.close();
58
94
 
59
95
  this.reconnectionAttempts++;
60
96
 
@@ -67,14 +103,25 @@ export class WebSocketPeerWithReconnection {
67
103
  await this.waitForOnline(timeout);
68
104
  }
69
105
 
70
- if (this.state !== "enabled") return;
106
+ if (!this.enabled) return;
71
107
 
72
108
  this.currentPeer = createWebSocketPeer({
73
- websocket: new WebSocket(this.peer),
109
+ websocket: new this.WebSocketConstructor(this.peer),
110
+ pingTimeout: this.pingTimeout,
74
111
  id: this.peer,
75
112
  role: "server",
76
- onClose: this.startConnection,
113
+ onClose: () => {
114
+ this.closed = true;
115
+ for (const listener of this.onConnectionChangeListeners) {
116
+ listener(false);
117
+ }
118
+ this.startConnection();
119
+ },
77
120
  onSuccess: () => {
121
+ this.closed = false;
122
+ for (const listener of this.onConnectionChangeListeners) {
123
+ listener(true);
124
+ }
78
125
  logger.debug("Websocket connection successful");
79
126
 
80
127
  this.reconnectionAttempts = 0;
@@ -85,16 +132,16 @@ export class WebSocketPeerWithReconnection {
85
132
  };
86
133
 
87
134
  enable = () => {
88
- if (this.state === "enabled") return;
135
+ if (this.enabled) return;
89
136
 
90
- this.state = "enabled";
137
+ this.enabled = true;
91
138
  this.startConnection();
92
139
  };
93
140
 
94
141
  disable = () => {
95
- if (this.state === "disabled") return;
142
+ if (!this.enabled) return;
96
143
 
97
- this.state = "disabled";
144
+ this.enabled = false;
98
145
 
99
146
  this.reconnectionAttempts = 0;
100
147
  this.unsubscribeNetworkChange?.();
@@ -102,6 +149,7 @@ export class WebSocketPeerWithReconnection {
102
149
 
103
150
  if (this.currentPeer) {
104
151
  this.removePeer(this.currentPeer);
152
+ this.currentPeer.outgoing.close();
105
153
  this.currentPeer = undefined;
106
154
  }
107
155
  };
@@ -140,4 +140,245 @@ describe("WebSocketPeerWithReconnection", () => {
140
140
 
141
141
  peer.disable();
142
142
  });
143
+
144
+ describe("waitUntilConnected", () => {
145
+ test("should wait until connected before resolving", async () => {
146
+ const addPeer = vi.fn();
147
+ const removePeer = vi.fn();
148
+
149
+ const peer = new WebSocketPeerWithReconnection({
150
+ peer: syncServerUrl,
151
+ reconnectionTimeout: 100,
152
+ addPeer,
153
+ removePeer,
154
+ });
155
+
156
+ // Start waiting for connection before enabling
157
+ const waitPromise = peer.waitUntilConnected();
158
+
159
+ // Enable the peer after a short delay
160
+ setTimeout(() => peer.enable(), 100);
161
+
162
+ // Wait for connection to be established
163
+ await waitPromise;
164
+
165
+ expect(addPeer).toHaveBeenCalledTimes(1);
166
+ expect(peer.closed).toBe(false);
167
+
168
+ peer.disable();
169
+ });
170
+
171
+ test("should resolve immediately if already connected", async () => {
172
+ const addPeer = vi.fn();
173
+ const removePeer = vi.fn();
174
+
175
+ const peer = new WebSocketPeerWithReconnection({
176
+ peer: syncServerUrl,
177
+ reconnectionTimeout: 100,
178
+ addPeer,
179
+ removePeer,
180
+ });
181
+
182
+ // Enable the peer first
183
+ peer.enable();
184
+
185
+ // Wait for initial connection
186
+ await new Promise((resolve) => setTimeout(resolve, 100));
187
+
188
+ // Now wait for connection again
189
+ const waitPromise = peer.waitUntilConnected();
190
+
191
+ // Should resolve immediately since we're already connected
192
+ await waitPromise;
193
+
194
+ expect(addPeer).toHaveBeenCalledTimes(1);
195
+ expect(peer.closed).toBe(false);
196
+
197
+ peer.disable();
198
+ });
199
+
200
+ test("should work when connection is lost and regained", async () => {
201
+ const addPeer = vi.fn();
202
+ const removePeer = vi.fn();
203
+
204
+ const peer = new WebSocketPeerWithReconnection({
205
+ peer: syncServerUrl,
206
+ reconnectionTimeout: 100,
207
+ addPeer,
208
+ removePeer,
209
+ });
210
+
211
+ peer.enable();
212
+
213
+ // Wait for initial connection
214
+ await new Promise((resolve) => setTimeout(resolve, 100));
215
+
216
+ // Close server to simulate connection loss
217
+ server.close();
218
+
219
+ // Wait for disconnect to be detected
220
+ await new Promise((resolve) => setTimeout(resolve, 200));
221
+
222
+ // Start server again
223
+ server = await startSyncServer(server.port);
224
+
225
+ // Wait for the waitUntilConnected promise to resolve
226
+ await peer.waitUntilConnected();
227
+
228
+ // Verify that we have a new connection
229
+ expect(addPeer).toHaveBeenCalledTimes(3); // Once for initial, once for reconnection
230
+ expect(peer.closed).toBe(false);
231
+
232
+ peer.disable();
233
+ });
234
+ });
235
+
236
+ describe("subscribe", () => {
237
+ test("should notify subscribers of initial connection state", async () => {
238
+ const addPeer = vi.fn();
239
+ const removePeer = vi.fn();
240
+ const listener = vi.fn();
241
+
242
+ const peer = new WebSocketPeerWithReconnection({
243
+ peer: syncServerUrl,
244
+ reconnectionTimeout: 100,
245
+ addPeer,
246
+ removePeer,
247
+ });
248
+
249
+ // Subscribe before enabling
250
+ peer.subscribe(listener);
251
+
252
+ // Initial state should be disconnected
253
+ expect(listener).toHaveBeenCalledWith(false);
254
+
255
+ // Enable the peer
256
+ peer.enable();
257
+
258
+ // Wait for connection
259
+ await new Promise((resolve) => setTimeout(resolve, 100));
260
+
261
+ // Should notify of connected state
262
+ expect(listener).toHaveBeenCalledWith(true);
263
+
264
+ peer.disable();
265
+ });
266
+
267
+ test("should notify subscribers of connection state changes", async () => {
268
+ const addPeer = vi.fn();
269
+ const removePeer = vi.fn();
270
+ const listener = vi.fn();
271
+
272
+ const peer = new WebSocketPeerWithReconnection({
273
+ peer: syncServerUrl,
274
+ reconnectionTimeout: 100,
275
+ addPeer,
276
+ removePeer,
277
+ });
278
+
279
+ peer.enable();
280
+ peer.subscribe(listener);
281
+
282
+ // Wait for initial connection
283
+ await new Promise((resolve) => setTimeout(resolve, 100));
284
+
285
+ // Should notify of connected state
286
+ expect(listener).toHaveBeenCalledWith(true);
287
+
288
+ // Close server to simulate disconnect
289
+ server.close();
290
+
291
+ // Wait for disconnect to be detected
292
+ await new Promise((resolve) => setTimeout(resolve, 200));
293
+
294
+ // Should notify of disconnected state
295
+ expect(listener).toHaveBeenCalledWith(false);
296
+
297
+ peer.disable();
298
+ });
299
+
300
+ test("should not notify unsubscribed listeners", async () => {
301
+ const addPeer = vi.fn();
302
+ const removePeer = vi.fn();
303
+ const listener1 = vi.fn();
304
+ const listener2 = vi.fn();
305
+
306
+ const peer = new WebSocketPeerWithReconnection({
307
+ peer: syncServerUrl,
308
+ reconnectionTimeout: 100,
309
+ addPeer,
310
+ removePeer,
311
+ });
312
+
313
+ peer.enable();
314
+ peer.subscribe(listener1);
315
+ peer.subscribe(listener2);
316
+
317
+ // Wait for initial connection
318
+ await new Promise((resolve) => setTimeout(resolve, 100));
319
+
320
+ // Both listeners should be notified of connection
321
+ expect(listener1).toHaveBeenCalledWith(true);
322
+ expect(listener2).toHaveBeenCalledWith(true);
323
+
324
+ // Unsubscribe listener1
325
+ peer.unsubscribe(listener1);
326
+
327
+ listener1.mockClear();
328
+ listener2.mockClear();
329
+
330
+ // Close server to simulate disconnect
331
+ server.close();
332
+
333
+ // Wait for disconnect to be detected
334
+ await new Promise((resolve) => setTimeout(resolve, 200));
335
+
336
+ // Only listener2 should be notified of disconnect
337
+ expect(listener1).not.toHaveBeenCalled();
338
+ expect(listener2).toHaveBeenCalledWith(false);
339
+
340
+ peer.disable();
341
+ });
342
+
343
+ test("should handle multiple subscribers correctly", async () => {
344
+ const addPeer = vi.fn();
345
+ const removePeer = vi.fn();
346
+ const listeners = Array.from({ length: 3 }, () => vi.fn());
347
+
348
+ const peer = new WebSocketPeerWithReconnection({
349
+ peer: syncServerUrl,
350
+ reconnectionTimeout: 100,
351
+ addPeer,
352
+ removePeer,
353
+ });
354
+
355
+ peer.enable();
356
+
357
+ // Subscribe all listeners
358
+ for (const listener of listeners) {
359
+ peer.subscribe(listener);
360
+ }
361
+
362
+ // Wait for initial connection
363
+ await new Promise((resolve) => setTimeout(resolve, 100));
364
+
365
+ // All listeners should be notified of connection
366
+ for (const listener of listeners) {
367
+ expect(listener).toHaveBeenCalledWith(true);
368
+ }
369
+
370
+ // Close server to simulate disconnect
371
+ server.close();
372
+
373
+ // Wait for disconnect to be detected
374
+ await new Promise((resolve) => setTimeout(resolve, 200));
375
+
376
+ // All listeners should be notified of disconnect
377
+ for (const listener of listeners) {
378
+ expect(listener).toHaveBeenCalledWith(false);
379
+ }
380
+
381
+ peer.disable();
382
+ });
383
+ });
143
384
  });