cojson-transport-ws 0.13.25 → 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.
- package/.turbo/turbo-build.log +1 -1
- package/CHANGELOG.md +15 -0
- package/dist/WebSocketPeerWithReconnection.d.ts +13 -3
- package/dist/WebSocketPeerWithReconnection.d.ts.map +1 -1
- package/dist/WebSocketPeerWithReconnection.js +47 -10
- package/dist/WebSocketPeerWithReconnection.js.map +1 -1
- package/dist/tests/WebSocketPeerWithReconnection.test.js +178 -0
- package/dist/tests/WebSocketPeerWithReconnection.test.js.map +1 -1
- package/package.json +2 -2
- package/src/WebSocketPeerWithReconnection.ts +60 -12
- package/src/tests/WebSocketPeerWithReconnection.test.ts +241 -0
package/.turbo/turbo-build.log
CHANGED
package/CHANGELOG.md
CHANGED
|
@@ -1,5 +1,20 @@
|
|
|
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
|
+
|
|
11
|
+
## 0.13.27
|
|
12
|
+
|
|
13
|
+
### Patch Changes
|
|
14
|
+
|
|
15
|
+
- Updated dependencies [6357052]
|
|
16
|
+
- cojson@0.13.27
|
|
17
|
+
|
|
3
18
|
## 0.13.25
|
|
4
19
|
|
|
5
20
|
### 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
|
-
|
|
18
|
+
enabled: boolean;
|
|
19
|
+
closed: boolean;
|
|
14
20
|
currentPeer: Peer | undefined;
|
|
15
|
-
unsubscribeNetworkChange
|
|
21
|
+
private unsubscribeNetworkChange;
|
|
16
22
|
onNetworkChange(callback: (connected: boolean) => void): () => void;
|
|
17
|
-
waitForOnline
|
|
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;
|
|
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.
|
|
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.
|
|
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.
|
|
42
|
+
if (!this.enabled)
|
|
20
43
|
return;
|
|
21
44
|
this.currentPeer = createWebSocketPeer({
|
|
22
|
-
websocket: new
|
|
45
|
+
websocket: new this.WebSocketConstructor(this.peer),
|
|
46
|
+
pingTimeout: this.pingTimeout,
|
|
23
47
|
id: this.peer,
|
|
24
48
|
role: "server",
|
|
25
|
-
onClose:
|
|
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.
|
|
68
|
+
if (this.enabled)
|
|
35
69
|
return;
|
|
36
|
-
this.
|
|
70
|
+
this.enabled = true;
|
|
37
71
|
this.startConnection();
|
|
38
72
|
};
|
|
39
73
|
this.disable = () => {
|
|
40
|
-
if (this.
|
|
74
|
+
if (!this.enabled)
|
|
41
75
|
return;
|
|
42
|
-
this.
|
|
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":"AAAA,OAAO,EAAa,MAAM,EAAE,MAAM,QAAQ,CAAC;AAC3C,OAAO,EAAE,mBAAmB,EAAE,MAAM,0BAA0B,CAAC;
|
|
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.
|
|
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.
|
|
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
|
-
|
|
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.
|
|
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.
|
|
106
|
+
if (!this.enabled) return;
|
|
71
107
|
|
|
72
108
|
this.currentPeer = createWebSocketPeer({
|
|
73
|
-
websocket: new
|
|
109
|
+
websocket: new this.WebSocketConstructor(this.peer),
|
|
110
|
+
pingTimeout: this.pingTimeout,
|
|
74
111
|
id: this.peer,
|
|
75
112
|
role: "server",
|
|
76
|
-
onClose:
|
|
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.
|
|
135
|
+
if (this.enabled) return;
|
|
89
136
|
|
|
90
|
-
this.
|
|
137
|
+
this.enabled = true;
|
|
91
138
|
this.startConnection();
|
|
92
139
|
};
|
|
93
140
|
|
|
94
141
|
disable = () => {
|
|
95
|
-
if (this.
|
|
142
|
+
if (!this.enabled) return;
|
|
96
143
|
|
|
97
|
-
this.
|
|
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
|
});
|