@iam4x/reconnecting-websocket 1.3.0 → 1.4.1
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/dist/index.d.ts +1 -0
- package/dist/index.js +71 -12
- package/package.json +1 -1
package/dist/index.d.ts
CHANGED
|
@@ -34,6 +34,7 @@ export declare class ReconnectingWebSocket {
|
|
|
34
34
|
constructor(url: string, options?: ReconnectOptions);
|
|
35
35
|
connect(): void;
|
|
36
36
|
emit(event: EventType, payload: any): void;
|
|
37
|
+
private runWithFinalizer;
|
|
37
38
|
scheduleReconnect(): void;
|
|
38
39
|
startHealthCheck(): void;
|
|
39
40
|
stopHealthCheck(): void;
|
package/dist/index.js
CHANGED
|
@@ -82,18 +82,21 @@ export class ReconnectingWebSocket {
|
|
|
82
82
|
this.openFn = (event) => {
|
|
83
83
|
// Only process if event is from the current socket
|
|
84
84
|
if (event.target === currentWs && this.ws === currentWs) {
|
|
85
|
+
const isReconnect = this.wasConnected;
|
|
85
86
|
this.clearTimers();
|
|
86
87
|
this.retryCount = 0;
|
|
87
|
-
this.emit("open", event);
|
|
88
|
-
// Emit reconnect event if this was a reconnection after a disconnection
|
|
89
|
-
if (this.wasConnected) {
|
|
90
|
-
this.emit("reconnect", event);
|
|
91
|
-
}
|
|
92
88
|
this.wasConnected = true;
|
|
93
89
|
this.startHealthCheck();
|
|
94
90
|
this.startInactivityTimer();
|
|
95
|
-
|
|
96
|
-
|
|
91
|
+
this.runWithFinalizer(() => {
|
|
92
|
+
this.emit("open", event);
|
|
93
|
+
if (isReconnect) {
|
|
94
|
+
this.emit("reconnect", event);
|
|
95
|
+
}
|
|
96
|
+
}, () => {
|
|
97
|
+
// Flush queued messages even if a listener throws during open/reconnect.
|
|
98
|
+
this.flushMessageQueue();
|
|
99
|
+
});
|
|
97
100
|
}
|
|
98
101
|
};
|
|
99
102
|
this.msgFn = (event) => {
|
|
@@ -106,12 +109,16 @@ export class ReconnectingWebSocket {
|
|
|
106
109
|
this.closeFn = (event) => {
|
|
107
110
|
// Only process if event is from the current socket
|
|
108
111
|
if (event.target === currentWs && this.ws === currentWs) {
|
|
112
|
+
const shouldReconnect = !this.forcedClose;
|
|
109
113
|
this.stopHealthCheck();
|
|
110
114
|
this.stopInactivityTimer();
|
|
111
|
-
this.
|
|
112
|
-
|
|
113
|
-
|
|
114
|
-
|
|
115
|
+
this.runWithFinalizer(() => {
|
|
116
|
+
this.emit("close", { code: event.code, reason: event.reason });
|
|
117
|
+
}, () => {
|
|
118
|
+
if (shouldReconnect) {
|
|
119
|
+
this.scheduleReconnect();
|
|
120
|
+
}
|
|
121
|
+
});
|
|
115
122
|
}
|
|
116
123
|
};
|
|
117
124
|
this.errorFn = (event) => {
|
|
@@ -131,6 +138,33 @@ export class ReconnectingWebSocket {
|
|
|
131
138
|
listener(payload);
|
|
132
139
|
}
|
|
133
140
|
}
|
|
141
|
+
runWithFinalizer(action, finalizer) {
|
|
142
|
+
let didThrow = false;
|
|
143
|
+
let thrown;
|
|
144
|
+
try {
|
|
145
|
+
action();
|
|
146
|
+
}
|
|
147
|
+
catch (error) {
|
|
148
|
+
didThrow = true;
|
|
149
|
+
thrown = error;
|
|
150
|
+
}
|
|
151
|
+
finally {
|
|
152
|
+
if (finalizer) {
|
|
153
|
+
try {
|
|
154
|
+
finalizer();
|
|
155
|
+
}
|
|
156
|
+
catch (error) {
|
|
157
|
+
if (!didThrow) {
|
|
158
|
+
didThrow = true;
|
|
159
|
+
thrown = error;
|
|
160
|
+
}
|
|
161
|
+
}
|
|
162
|
+
}
|
|
163
|
+
}
|
|
164
|
+
if (didThrow) {
|
|
165
|
+
throw thrown;
|
|
166
|
+
}
|
|
167
|
+
}
|
|
134
168
|
scheduleReconnect() {
|
|
135
169
|
// Clear any existing reconnect timeout first to prevent multiple reconnects
|
|
136
170
|
if (this.reconnectTimeout) {
|
|
@@ -180,9 +214,34 @@ export class ReconnectingWebSocket {
|
|
|
180
214
|
if (this.forcedClose) {
|
|
181
215
|
return;
|
|
182
216
|
}
|
|
183
|
-
|
|
217
|
+
const shouldReconnect = !this.forcedClose;
|
|
218
|
+
// Proactively trigger reconnection due to inactivity
|
|
219
|
+
// Don't rely on the close event as it may never fire on a stalled connection
|
|
184
220
|
if (this.ws) {
|
|
221
|
+
// Stop health check to prevent it from also triggering reconnection
|
|
222
|
+
this.stopHealthCheck();
|
|
223
|
+
// Remove event listeners to prevent any late events from interfering
|
|
224
|
+
if (this.openFn)
|
|
225
|
+
this.ws.removeEventListener("open", this.openFn);
|
|
226
|
+
if (this.msgFn)
|
|
227
|
+
this.ws.removeEventListener("message", this.msgFn);
|
|
228
|
+
if (this.closeFn)
|
|
229
|
+
this.ws.removeEventListener("close", this.closeFn);
|
|
230
|
+
if (this.errorFn)
|
|
231
|
+
this.ws.removeEventListener("error", this.errorFn);
|
|
232
|
+
// Try to close the socket (may hang on stalled connections, but we don't wait)
|
|
185
233
|
this.ws.close();
|
|
234
|
+
// Clear the socket reference
|
|
235
|
+
this.ws = undefined;
|
|
236
|
+
this.runWithFinalizer(() => {
|
|
237
|
+
// Emit close event to listeners with a special code indicating inactivity timeout.
|
|
238
|
+
this.emit("close", { code: 4000, reason: "Inactivity timeout" });
|
|
239
|
+
}, () => {
|
|
240
|
+
if (shouldReconnect) {
|
|
241
|
+
// Schedule reconnection directly without waiting for close event.
|
|
242
|
+
this.scheduleReconnect();
|
|
243
|
+
}
|
|
244
|
+
});
|
|
186
245
|
}
|
|
187
246
|
}, this.options.watchingInactivityTimeout);
|
|
188
247
|
}
|