hume 0.10.4-beta.2 → 0.10.4-beta.3
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/api/resources/empathicVoice/resources/chat/client/Socket.js +3 -9
- package/core/websocket/ws.d.ts +30 -0
- package/core/websocket/ws.js +126 -25
- package/dist/api/resources/empathicVoice/resources/chat/client/Socket.js +3 -9
- package/dist/core/websocket/ws.d.ts +30 -0
- package/dist/core/websocket/ws.js +126 -25
- package/package.json +1 -1
|
@@ -75,15 +75,9 @@ class ChatSocket {
|
|
|
75
75
|
(_b = (_a = this.eventHandlers).close) === null || _b === void 0 ? void 0 : _b.call(_a, event);
|
|
76
76
|
};
|
|
77
77
|
this.handleError = (event) => {
|
|
78
|
-
var _a, _b
|
|
79
|
-
|
|
80
|
-
|
|
81
|
-
(_c = (_b = this.eventHandlers).error) === null || _c === void 0 ? void 0 : _c.call(_b, new Error(message));
|
|
82
|
-
}
|
|
83
|
-
catch (_g) {
|
|
84
|
-
const message = (_d = event.message) !== null && _d !== void 0 ? _d : `core.ReconnectingWebSocket error (unable to stringify)`;
|
|
85
|
-
(_f = (_e = this.eventHandlers).error) === null || _f === void 0 ? void 0 : _f.call(_e, new Error(message));
|
|
86
|
-
}
|
|
78
|
+
var _a, _b;
|
|
79
|
+
// Create and dispatch a new Error object using the message from the standardized event.
|
|
80
|
+
(_b = (_a = this.eventHandlers).error) === null || _b === void 0 ? void 0 : _b.call(_a, new Error(event.message));
|
|
87
81
|
};
|
|
88
82
|
this.socket = socket;
|
|
89
83
|
this.socket.addEventListener("open", this.handleOpen);
|
package/core/websocket/ws.d.ts
CHANGED
|
@@ -121,6 +121,15 @@ export declare class ReconnectingWebSocket {
|
|
|
121
121
|
private _getNextDelay;
|
|
122
122
|
private _wait;
|
|
123
123
|
private _getNextUrl;
|
|
124
|
+
/**
|
|
125
|
+
* Handles the final "tear down" process when connection attempts must permanently stop
|
|
126
|
+
* (e.g., max retries reached, fatal configuration error).
|
|
127
|
+
* Releases locks, disables reconnection, dispatches final error and close events.
|
|
128
|
+
*
|
|
129
|
+
* @param error - The specific Error instance indicating the reason for termination.
|
|
130
|
+
* @param closeReason - The reason string to use for the final CloseEvent.
|
|
131
|
+
*/
|
|
132
|
+
private _shutdown;
|
|
124
133
|
private _connect;
|
|
125
134
|
private _handleTimeout;
|
|
126
135
|
private _disconnect;
|
|
@@ -128,7 +137,28 @@ export declare class ReconnectingWebSocket {
|
|
|
128
137
|
private _callEventListener;
|
|
129
138
|
private _handleOpen;
|
|
130
139
|
private _handleMessage;
|
|
140
|
+
/**
|
|
141
|
+
* Adapts a raw error or event input into a standardized `Events.ErrorEvent`.
|
|
142
|
+
* This utility is called by `_handleError` to ensure it operates on a
|
|
143
|
+
* consistent error structure.
|
|
144
|
+
* @param rawErrorOrEventInput - The raw error data (e.g., Error instance, DOM Event, Events.ErrorEvent).
|
|
145
|
+
* @returns A standardized `Events.ErrorEvent` containing an underlying `Error` instance.
|
|
146
|
+
*/
|
|
147
|
+
private _adaptError;
|
|
148
|
+
/**
|
|
149
|
+
* Core handler for all errors. It first standardizes the error input using `_adaptError`,
|
|
150
|
+
* then manages disconnection, dispatches the standardized error event, and initiates reconnection.
|
|
151
|
+
* @param rawErrorOrEventInput - The error data from any internal source.
|
|
152
|
+
*/
|
|
131
153
|
private _handleError;
|
|
154
|
+
/**
|
|
155
|
+
* Handles WebSocket close events, either native or synthesized internally.
|
|
156
|
+
*
|
|
157
|
+
* Adapts the event if necessary, manages state, dispatches standardized close events,
|
|
158
|
+
* and initiates reconnection if appropriate.
|
|
159
|
+
*
|
|
160
|
+
* @param eventInfo - Either an internal Events.CloseEvent or a native DOM CloseEvent.
|
|
161
|
+
*/
|
|
132
162
|
private _handleClose;
|
|
133
163
|
private _removeListeners;
|
|
134
164
|
private _addListeners;
|
package/core/websocket/ws.js
CHANGED
|
@@ -38,6 +38,7 @@ exports.ReconnectingWebSocket = void 0;
|
|
|
38
38
|
const runtime_1 = require("../runtime");
|
|
39
39
|
const Events = __importStar(require("./events"));
|
|
40
40
|
const ws_1 = require("ws");
|
|
41
|
+
const ABNORMAL_CLOSURE_CODE = 1006;
|
|
41
42
|
const getGlobalWebSocket = () => {
|
|
42
43
|
if (typeof WebSocket !== "undefined") {
|
|
43
44
|
// @ts-ignore
|
|
@@ -115,33 +116,70 @@ class ReconnectingWebSocket {
|
|
|
115
116
|
}
|
|
116
117
|
this._listeners.message.forEach((listener) => this._callEventListener(event, listener));
|
|
117
118
|
};
|
|
118
|
-
|
|
119
|
-
|
|
120
|
-
|
|
121
|
-
|
|
122
|
-
|
|
123
|
-
|
|
119
|
+
/**
|
|
120
|
+
* Core handler for all errors. It first standardizes the error input using `_adaptError`,
|
|
121
|
+
* then manages disconnection, dispatches the standardized error event, and initiates reconnection.
|
|
122
|
+
* @param rawErrorOrEventInput - The error data from any internal source.
|
|
123
|
+
*/
|
|
124
|
+
this._handleError = (rawErrorOrEventInput) => {
|
|
125
|
+
this._debug("Raw error/event received by _handleError, passing to _adaptError:", rawErrorOrEventInput);
|
|
126
|
+
const adaptedError = this._adaptError(rawErrorOrEventInput);
|
|
127
|
+
const { error, message } = adaptedError;
|
|
128
|
+
// Close with abnormal closure code so _handleClose will reconnect if there's a socket
|
|
129
|
+
this._disconnect(ABNORMAL_CLOSURE_CODE, message);
|
|
130
|
+
this._debug("Processed error. Dispatching Event:", message, error);
|
|
131
|
+
if (this.onerror)
|
|
132
|
+
this.onerror(adaptedError);
|
|
124
133
|
this._debug("exec error listeners");
|
|
125
|
-
this._listeners.error.forEach((listener) => this._callEventListener(
|
|
126
|
-
|
|
134
|
+
this._listeners.error.forEach((listener) => this._callEventListener(adaptedError, listener));
|
|
135
|
+
// If we never even got a ws instance, _disconnect returned early so we need to explicitly retry here
|
|
136
|
+
if (!this._ws && this._shouldReconnect) {
|
|
137
|
+
this._debug("Error before socket created; scheduling initial reconnect.");
|
|
138
|
+
this._connect();
|
|
139
|
+
}
|
|
127
140
|
};
|
|
141
|
+
/**
|
|
142
|
+
* Handles WebSocket close events, either native or synthesized internally.
|
|
143
|
+
*
|
|
144
|
+
* Adapts the event if necessary, manages state, dispatches standardized close events,
|
|
145
|
+
* and initiates reconnection if appropriate.
|
|
146
|
+
*
|
|
147
|
+
* @param eventInfo - Either an internal Events.CloseEvent or a native DOM CloseEvent.
|
|
148
|
+
*/
|
|
128
149
|
this._handleClose = (event) => {
|
|
129
|
-
this._debug("close event");
|
|
150
|
+
this._debug("Raw close event received by _handleClose:", event);
|
|
151
|
+
// Adapt event if event is an instance of the (browser) native CloseEvent
|
|
152
|
+
const isNativeCloseEvent = typeof CloseEvent !== "undefined" && event instanceof CloseEvent;
|
|
153
|
+
const adaptedEvent = isNativeCloseEvent
|
|
154
|
+
? new Events.CloseEvent(event.code, event.reason, this)
|
|
155
|
+
: event;
|
|
156
|
+
// Clean up state
|
|
130
157
|
this._clearTimeouts();
|
|
131
|
-
|
|
158
|
+
this._ws = undefined;
|
|
159
|
+
this._connectLock = false;
|
|
160
|
+
// Determine reconnection intent
|
|
161
|
+
if (this._closeCalled) {
|
|
132
162
|
this._shouldReconnect = false;
|
|
163
|
+
this._debug("Reconnection stopped: RWS.close() was called.");
|
|
133
164
|
}
|
|
134
|
-
if (
|
|
135
|
-
this.
|
|
136
|
-
|
|
137
|
-
if (this.onclose) {
|
|
138
|
-
this.onclose(event);
|
|
165
|
+
else if (adaptedEvent.code === 1000) {
|
|
166
|
+
this._shouldReconnect = false;
|
|
167
|
+
this._debug("Reconnection stopped: Received close code 1000 (intentional server close).");
|
|
139
168
|
}
|
|
140
|
-
|
|
169
|
+
// Dispatch event to listeners
|
|
170
|
+
if (this.onclose)
|
|
171
|
+
this.onclose(adaptedEvent);
|
|
172
|
+
this._listeners.close.forEach((listener) => this._callEventListener(adaptedEvent, listener));
|
|
173
|
+
// Conditionally attempt reconnection
|
|
174
|
+
if (this._shouldReconnect)
|
|
175
|
+
this._connect();
|
|
141
176
|
};
|
|
142
177
|
this._url = url;
|
|
143
178
|
this._protocols = protocols;
|
|
144
179
|
this._options = options;
|
|
180
|
+
if (!isWebSocket(this._options.WebSocket)) {
|
|
181
|
+
throw Error("No valid WebSocket class provided");
|
|
182
|
+
}
|
|
145
183
|
if (this._options.startClosed) {
|
|
146
184
|
this._shouldReconnect = false;
|
|
147
185
|
}
|
|
@@ -267,8 +305,8 @@ class ReconnectingWebSocket {
|
|
|
267
305
|
this._connect();
|
|
268
306
|
}
|
|
269
307
|
else {
|
|
308
|
+
// If the socket is already connected then disconnect. The socket will conditionally reconnected in _handleClose
|
|
270
309
|
this._disconnect(code, reason);
|
|
271
|
-
this._connect();
|
|
272
310
|
}
|
|
273
311
|
}
|
|
274
312
|
/**
|
|
@@ -356,35 +394,73 @@ class ReconnectingWebSocket {
|
|
|
356
394
|
}
|
|
357
395
|
throw Error("Invalid URL");
|
|
358
396
|
}
|
|
397
|
+
/**
|
|
398
|
+
* Handles the final "tear down" process when connection attempts must permanently stop
|
|
399
|
+
* (e.g., max retries reached, fatal configuration error).
|
|
400
|
+
* Releases locks, disables reconnection, dispatches final error and close events.
|
|
401
|
+
*
|
|
402
|
+
* @param error - The specific Error instance indicating the reason for termination.
|
|
403
|
+
* @param closeReason - The reason string to use for the final CloseEvent.
|
|
404
|
+
*/
|
|
405
|
+
_shutdown(error, closeReason) {
|
|
406
|
+
this._debug("Terminating connection attempts. Reason:", error.message);
|
|
407
|
+
// Ensure state prevents further attempts
|
|
408
|
+
this._connectLock = false;
|
|
409
|
+
this._shouldReconnect = false;
|
|
410
|
+
// Dispatch error event
|
|
411
|
+
const errorEvent = this._adaptError(error);
|
|
412
|
+
if (this.onerror)
|
|
413
|
+
this.onerror(errorEvent);
|
|
414
|
+
this._listeners.error.forEach((listener) => this._callEventListener(errorEvent, listener));
|
|
415
|
+
// Dispatch close event
|
|
416
|
+
const closeEvent = new Events.CloseEvent(1000, closeReason, this);
|
|
417
|
+
if (this.onclose)
|
|
418
|
+
this.onclose(closeEvent);
|
|
419
|
+
this._listeners.close.forEach((listener) => this._callEventListener(closeEvent, listener));
|
|
420
|
+
}
|
|
359
421
|
_connect() {
|
|
360
|
-
|
|
422
|
+
// Check locks and intent
|
|
423
|
+
if (this._connectLock) {
|
|
424
|
+
this._debug("Connection attempt already in progress.");
|
|
425
|
+
return;
|
|
426
|
+
}
|
|
427
|
+
if (!this._shouldReconnect) {
|
|
428
|
+
this._debug("Reconnection disabled, skipping connect attempt.");
|
|
361
429
|
return;
|
|
362
430
|
}
|
|
431
|
+
// Set lock for this attempt
|
|
363
432
|
this._connectLock = true;
|
|
364
433
|
const { maxRetries = DEFAULT.maxRetries, connectionTimeout = DEFAULT.connectionTimeout, WebSocket = getGlobalWebSocket(), } = this._options;
|
|
434
|
+
// Max retries check
|
|
365
435
|
if (this._retryCount >= maxRetries) {
|
|
366
|
-
this.
|
|
436
|
+
this._shutdown(new Error(`Max retries (${maxRetries}) reached. Giving up.`), "Max retries reached");
|
|
367
437
|
return;
|
|
368
438
|
}
|
|
439
|
+
// Prepare connection attempt
|
|
369
440
|
this._retryCount++;
|
|
370
441
|
this._debug("connect", this._retryCount);
|
|
371
442
|
this._removeListeners();
|
|
372
|
-
|
|
373
|
-
throw Error("No valid WebSocket class provided");
|
|
374
|
-
}
|
|
443
|
+
// Connection attempt
|
|
375
444
|
this._wait()
|
|
376
445
|
.then(() => this._getNextUrl(this._url))
|
|
377
446
|
.then((url) => {
|
|
378
|
-
//
|
|
447
|
+
// Close could be called before creating the ws
|
|
379
448
|
if (this._closeCalled) {
|
|
449
|
+
this._connectLock = false;
|
|
450
|
+
this._debug("Connection cancelled: ReconnectingWebSocket.close() called during setup.");
|
|
380
451
|
return;
|
|
381
452
|
}
|
|
382
453
|
this._debug("connect", { url, protocols: this._protocols });
|
|
383
454
|
this._ws = this._protocols ? new WebSocket(url, this._protocols) : new WebSocket(url);
|
|
384
455
|
this._ws.binaryType = this._binaryType;
|
|
385
|
-
this._connectLock = false;
|
|
386
456
|
this._addListeners();
|
|
457
|
+
this._connectLock = false;
|
|
387
458
|
this._connectTimeout = setTimeout(() => this._handleTimeout(), connectionTimeout);
|
|
459
|
+
})
|
|
460
|
+
.catch((error) => {
|
|
461
|
+
this._debug("Connection setup failed:", error);
|
|
462
|
+
this._connectLock = false;
|
|
463
|
+
this._handleError(error);
|
|
388
464
|
});
|
|
389
465
|
}
|
|
390
466
|
_handleTimeout() {
|
|
@@ -419,6 +495,32 @@ class ReconnectingWebSocket {
|
|
|
419
495
|
listener(event);
|
|
420
496
|
}
|
|
421
497
|
}
|
|
498
|
+
/**
|
|
499
|
+
* Adapts a raw error or event input into a standardized `Events.ErrorEvent`.
|
|
500
|
+
* This utility is called by `_handleError` to ensure it operates on a
|
|
501
|
+
* consistent error structure.
|
|
502
|
+
* @param rawErrorOrEventInput - The raw error data (e.g., Error instance, DOM Event, Events.ErrorEvent).
|
|
503
|
+
* @returns A standardized `Events.ErrorEvent` containing an underlying `Error` instance.
|
|
504
|
+
*/
|
|
505
|
+
_adaptError(rawErrorOrEventInput) {
|
|
506
|
+
this._debug("Adapting raw error/event via _adaptError:", rawErrorOrEventInput);
|
|
507
|
+
let underlyingError;
|
|
508
|
+
if (rawErrorOrEventInput instanceof Events.ErrorEvent && rawErrorOrEventInput.error instanceof Error) {
|
|
509
|
+
return rawErrorOrEventInput;
|
|
510
|
+
}
|
|
511
|
+
if (rawErrorOrEventInput instanceof Error) {
|
|
512
|
+
underlyingError = rawErrorOrEventInput;
|
|
513
|
+
}
|
|
514
|
+
else if (typeof Event !== "undefined" &&
|
|
515
|
+
rawErrorOrEventInput instanceof Event &&
|
|
516
|
+
rawErrorOrEventInput.type === "error") {
|
|
517
|
+
underlyingError = new Error("WebSocket low-level error occurred (see browser/runtime console for native event details).");
|
|
518
|
+
}
|
|
519
|
+
else {
|
|
520
|
+
underlyingError = new Error(`Unknown WebSocket error. Raw data: ${String(rawErrorOrEventInput !== null && rawErrorOrEventInput !== void 0 ? rawErrorOrEventInput : "undefined")}`);
|
|
521
|
+
}
|
|
522
|
+
return new Events.ErrorEvent(underlyingError, this);
|
|
523
|
+
}
|
|
422
524
|
_removeListeners() {
|
|
423
525
|
if (!this._ws) {
|
|
424
526
|
return;
|
|
@@ -438,7 +540,6 @@ class ReconnectingWebSocket {
|
|
|
438
540
|
this._ws.addEventListener("open", this._handleOpen);
|
|
439
541
|
this._ws.addEventListener("close", this._handleClose);
|
|
440
542
|
this._ws.addEventListener("message", this._handleMessage);
|
|
441
|
-
// @ts-ignore
|
|
442
543
|
this._ws.addEventListener("error", this._handleError);
|
|
443
544
|
}
|
|
444
545
|
_clearTimeouts() {
|
|
@@ -75,15 +75,9 @@ class ChatSocket {
|
|
|
75
75
|
(_b = (_a = this.eventHandlers).close) === null || _b === void 0 ? void 0 : _b.call(_a, event);
|
|
76
76
|
};
|
|
77
77
|
this.handleError = (event) => {
|
|
78
|
-
var _a, _b
|
|
79
|
-
|
|
80
|
-
|
|
81
|
-
(_c = (_b = this.eventHandlers).error) === null || _c === void 0 ? void 0 : _c.call(_b, new Error(message));
|
|
82
|
-
}
|
|
83
|
-
catch (_g) {
|
|
84
|
-
const message = (_d = event.message) !== null && _d !== void 0 ? _d : `core.ReconnectingWebSocket error (unable to stringify)`;
|
|
85
|
-
(_f = (_e = this.eventHandlers).error) === null || _f === void 0 ? void 0 : _f.call(_e, new Error(message));
|
|
86
|
-
}
|
|
78
|
+
var _a, _b;
|
|
79
|
+
// Create and dispatch a new Error object using the message from the standardized event.
|
|
80
|
+
(_b = (_a = this.eventHandlers).error) === null || _b === void 0 ? void 0 : _b.call(_a, new Error(event.message));
|
|
87
81
|
};
|
|
88
82
|
this.socket = socket;
|
|
89
83
|
this.socket.addEventListener("open", this.handleOpen);
|
|
@@ -121,6 +121,15 @@ export declare class ReconnectingWebSocket {
|
|
|
121
121
|
private _getNextDelay;
|
|
122
122
|
private _wait;
|
|
123
123
|
private _getNextUrl;
|
|
124
|
+
/**
|
|
125
|
+
* Handles the final "tear down" process when connection attempts must permanently stop
|
|
126
|
+
* (e.g., max retries reached, fatal configuration error).
|
|
127
|
+
* Releases locks, disables reconnection, dispatches final error and close events.
|
|
128
|
+
*
|
|
129
|
+
* @param error - The specific Error instance indicating the reason for termination.
|
|
130
|
+
* @param closeReason - The reason string to use for the final CloseEvent.
|
|
131
|
+
*/
|
|
132
|
+
private _shutdown;
|
|
124
133
|
private _connect;
|
|
125
134
|
private _handleTimeout;
|
|
126
135
|
private _disconnect;
|
|
@@ -128,7 +137,28 @@ export declare class ReconnectingWebSocket {
|
|
|
128
137
|
private _callEventListener;
|
|
129
138
|
private _handleOpen;
|
|
130
139
|
private _handleMessage;
|
|
140
|
+
/**
|
|
141
|
+
* Adapts a raw error or event input into a standardized `Events.ErrorEvent`.
|
|
142
|
+
* This utility is called by `_handleError` to ensure it operates on a
|
|
143
|
+
* consistent error structure.
|
|
144
|
+
* @param rawErrorOrEventInput - The raw error data (e.g., Error instance, DOM Event, Events.ErrorEvent).
|
|
145
|
+
* @returns A standardized `Events.ErrorEvent` containing an underlying `Error` instance.
|
|
146
|
+
*/
|
|
147
|
+
private _adaptError;
|
|
148
|
+
/**
|
|
149
|
+
* Core handler for all errors. It first standardizes the error input using `_adaptError`,
|
|
150
|
+
* then manages disconnection, dispatches the standardized error event, and initiates reconnection.
|
|
151
|
+
* @param rawErrorOrEventInput - The error data from any internal source.
|
|
152
|
+
*/
|
|
131
153
|
private _handleError;
|
|
154
|
+
/**
|
|
155
|
+
* Handles WebSocket close events, either native or synthesized internally.
|
|
156
|
+
*
|
|
157
|
+
* Adapts the event if necessary, manages state, dispatches standardized close events,
|
|
158
|
+
* and initiates reconnection if appropriate.
|
|
159
|
+
*
|
|
160
|
+
* @param eventInfo - Either an internal Events.CloseEvent or a native DOM CloseEvent.
|
|
161
|
+
*/
|
|
132
162
|
private _handleClose;
|
|
133
163
|
private _removeListeners;
|
|
134
164
|
private _addListeners;
|
|
@@ -38,6 +38,7 @@ exports.ReconnectingWebSocket = void 0;
|
|
|
38
38
|
const runtime_1 = require("../runtime");
|
|
39
39
|
const Events = __importStar(require("./events"));
|
|
40
40
|
const ws_1 = require("ws");
|
|
41
|
+
const ABNORMAL_CLOSURE_CODE = 1006;
|
|
41
42
|
const getGlobalWebSocket = () => {
|
|
42
43
|
if (typeof WebSocket !== "undefined") {
|
|
43
44
|
// @ts-ignore
|
|
@@ -115,33 +116,70 @@ class ReconnectingWebSocket {
|
|
|
115
116
|
}
|
|
116
117
|
this._listeners.message.forEach((listener) => this._callEventListener(event, listener));
|
|
117
118
|
};
|
|
118
|
-
|
|
119
|
-
|
|
120
|
-
|
|
121
|
-
|
|
122
|
-
|
|
123
|
-
|
|
119
|
+
/**
|
|
120
|
+
* Core handler for all errors. It first standardizes the error input using `_adaptError`,
|
|
121
|
+
* then manages disconnection, dispatches the standardized error event, and initiates reconnection.
|
|
122
|
+
* @param rawErrorOrEventInput - The error data from any internal source.
|
|
123
|
+
*/
|
|
124
|
+
this._handleError = (rawErrorOrEventInput) => {
|
|
125
|
+
this._debug("Raw error/event received by _handleError, passing to _adaptError:", rawErrorOrEventInput);
|
|
126
|
+
const adaptedError = this._adaptError(rawErrorOrEventInput);
|
|
127
|
+
const { error, message } = adaptedError;
|
|
128
|
+
// Close with abnormal closure code so _handleClose will reconnect if there's a socket
|
|
129
|
+
this._disconnect(ABNORMAL_CLOSURE_CODE, message);
|
|
130
|
+
this._debug("Processed error. Dispatching Event:", message, error);
|
|
131
|
+
if (this.onerror)
|
|
132
|
+
this.onerror(adaptedError);
|
|
124
133
|
this._debug("exec error listeners");
|
|
125
|
-
this._listeners.error.forEach((listener) => this._callEventListener(
|
|
126
|
-
|
|
134
|
+
this._listeners.error.forEach((listener) => this._callEventListener(adaptedError, listener));
|
|
135
|
+
// If we never even got a ws instance, _disconnect returned early so we need to explicitly retry here
|
|
136
|
+
if (!this._ws && this._shouldReconnect) {
|
|
137
|
+
this._debug("Error before socket created; scheduling initial reconnect.");
|
|
138
|
+
this._connect();
|
|
139
|
+
}
|
|
127
140
|
};
|
|
141
|
+
/**
|
|
142
|
+
* Handles WebSocket close events, either native or synthesized internally.
|
|
143
|
+
*
|
|
144
|
+
* Adapts the event if necessary, manages state, dispatches standardized close events,
|
|
145
|
+
* and initiates reconnection if appropriate.
|
|
146
|
+
*
|
|
147
|
+
* @param eventInfo - Either an internal Events.CloseEvent or a native DOM CloseEvent.
|
|
148
|
+
*/
|
|
128
149
|
this._handleClose = (event) => {
|
|
129
|
-
this._debug("close event");
|
|
150
|
+
this._debug("Raw close event received by _handleClose:", event);
|
|
151
|
+
// Adapt event if event is an instance of the (browser) native CloseEvent
|
|
152
|
+
const isNativeCloseEvent = typeof CloseEvent !== "undefined" && event instanceof CloseEvent;
|
|
153
|
+
const adaptedEvent = isNativeCloseEvent
|
|
154
|
+
? new Events.CloseEvent(event.code, event.reason, this)
|
|
155
|
+
: event;
|
|
156
|
+
// Clean up state
|
|
130
157
|
this._clearTimeouts();
|
|
131
|
-
|
|
158
|
+
this._ws = undefined;
|
|
159
|
+
this._connectLock = false;
|
|
160
|
+
// Determine reconnection intent
|
|
161
|
+
if (this._closeCalled) {
|
|
132
162
|
this._shouldReconnect = false;
|
|
163
|
+
this._debug("Reconnection stopped: RWS.close() was called.");
|
|
133
164
|
}
|
|
134
|
-
if (
|
|
135
|
-
this.
|
|
136
|
-
|
|
137
|
-
if (this.onclose) {
|
|
138
|
-
this.onclose(event);
|
|
165
|
+
else if (adaptedEvent.code === 1000) {
|
|
166
|
+
this._shouldReconnect = false;
|
|
167
|
+
this._debug("Reconnection stopped: Received close code 1000 (intentional server close).");
|
|
139
168
|
}
|
|
140
|
-
|
|
169
|
+
// Dispatch event to listeners
|
|
170
|
+
if (this.onclose)
|
|
171
|
+
this.onclose(adaptedEvent);
|
|
172
|
+
this._listeners.close.forEach((listener) => this._callEventListener(adaptedEvent, listener));
|
|
173
|
+
// Conditionally attempt reconnection
|
|
174
|
+
if (this._shouldReconnect)
|
|
175
|
+
this._connect();
|
|
141
176
|
};
|
|
142
177
|
this._url = url;
|
|
143
178
|
this._protocols = protocols;
|
|
144
179
|
this._options = options;
|
|
180
|
+
if (!isWebSocket(this._options.WebSocket)) {
|
|
181
|
+
throw Error("No valid WebSocket class provided");
|
|
182
|
+
}
|
|
145
183
|
if (this._options.startClosed) {
|
|
146
184
|
this._shouldReconnect = false;
|
|
147
185
|
}
|
|
@@ -267,8 +305,8 @@ class ReconnectingWebSocket {
|
|
|
267
305
|
this._connect();
|
|
268
306
|
}
|
|
269
307
|
else {
|
|
308
|
+
// If the socket is already connected then disconnect. The socket will conditionally reconnected in _handleClose
|
|
270
309
|
this._disconnect(code, reason);
|
|
271
|
-
this._connect();
|
|
272
310
|
}
|
|
273
311
|
}
|
|
274
312
|
/**
|
|
@@ -356,35 +394,73 @@ class ReconnectingWebSocket {
|
|
|
356
394
|
}
|
|
357
395
|
throw Error("Invalid URL");
|
|
358
396
|
}
|
|
397
|
+
/**
|
|
398
|
+
* Handles the final "tear down" process when connection attempts must permanently stop
|
|
399
|
+
* (e.g., max retries reached, fatal configuration error).
|
|
400
|
+
* Releases locks, disables reconnection, dispatches final error and close events.
|
|
401
|
+
*
|
|
402
|
+
* @param error - The specific Error instance indicating the reason for termination.
|
|
403
|
+
* @param closeReason - The reason string to use for the final CloseEvent.
|
|
404
|
+
*/
|
|
405
|
+
_shutdown(error, closeReason) {
|
|
406
|
+
this._debug("Terminating connection attempts. Reason:", error.message);
|
|
407
|
+
// Ensure state prevents further attempts
|
|
408
|
+
this._connectLock = false;
|
|
409
|
+
this._shouldReconnect = false;
|
|
410
|
+
// Dispatch error event
|
|
411
|
+
const errorEvent = this._adaptError(error);
|
|
412
|
+
if (this.onerror)
|
|
413
|
+
this.onerror(errorEvent);
|
|
414
|
+
this._listeners.error.forEach((listener) => this._callEventListener(errorEvent, listener));
|
|
415
|
+
// Dispatch close event
|
|
416
|
+
const closeEvent = new Events.CloseEvent(1000, closeReason, this);
|
|
417
|
+
if (this.onclose)
|
|
418
|
+
this.onclose(closeEvent);
|
|
419
|
+
this._listeners.close.forEach((listener) => this._callEventListener(closeEvent, listener));
|
|
420
|
+
}
|
|
359
421
|
_connect() {
|
|
360
|
-
|
|
422
|
+
// Check locks and intent
|
|
423
|
+
if (this._connectLock) {
|
|
424
|
+
this._debug("Connection attempt already in progress.");
|
|
425
|
+
return;
|
|
426
|
+
}
|
|
427
|
+
if (!this._shouldReconnect) {
|
|
428
|
+
this._debug("Reconnection disabled, skipping connect attempt.");
|
|
361
429
|
return;
|
|
362
430
|
}
|
|
431
|
+
// Set lock for this attempt
|
|
363
432
|
this._connectLock = true;
|
|
364
433
|
const { maxRetries = DEFAULT.maxRetries, connectionTimeout = DEFAULT.connectionTimeout, WebSocket = getGlobalWebSocket(), } = this._options;
|
|
434
|
+
// Max retries check
|
|
365
435
|
if (this._retryCount >= maxRetries) {
|
|
366
|
-
this.
|
|
436
|
+
this._shutdown(new Error(`Max retries (${maxRetries}) reached. Giving up.`), "Max retries reached");
|
|
367
437
|
return;
|
|
368
438
|
}
|
|
439
|
+
// Prepare connection attempt
|
|
369
440
|
this._retryCount++;
|
|
370
441
|
this._debug("connect", this._retryCount);
|
|
371
442
|
this._removeListeners();
|
|
372
|
-
|
|
373
|
-
throw Error("No valid WebSocket class provided");
|
|
374
|
-
}
|
|
443
|
+
// Connection attempt
|
|
375
444
|
this._wait()
|
|
376
445
|
.then(() => this._getNextUrl(this._url))
|
|
377
446
|
.then((url) => {
|
|
378
|
-
//
|
|
447
|
+
// Close could be called before creating the ws
|
|
379
448
|
if (this._closeCalled) {
|
|
449
|
+
this._connectLock = false;
|
|
450
|
+
this._debug("Connection cancelled: ReconnectingWebSocket.close() called during setup.");
|
|
380
451
|
return;
|
|
381
452
|
}
|
|
382
453
|
this._debug("connect", { url, protocols: this._protocols });
|
|
383
454
|
this._ws = this._protocols ? new WebSocket(url, this._protocols) : new WebSocket(url);
|
|
384
455
|
this._ws.binaryType = this._binaryType;
|
|
385
|
-
this._connectLock = false;
|
|
386
456
|
this._addListeners();
|
|
457
|
+
this._connectLock = false;
|
|
387
458
|
this._connectTimeout = setTimeout(() => this._handleTimeout(), connectionTimeout);
|
|
459
|
+
})
|
|
460
|
+
.catch((error) => {
|
|
461
|
+
this._debug("Connection setup failed:", error);
|
|
462
|
+
this._connectLock = false;
|
|
463
|
+
this._handleError(error);
|
|
388
464
|
});
|
|
389
465
|
}
|
|
390
466
|
_handleTimeout() {
|
|
@@ -419,6 +495,32 @@ class ReconnectingWebSocket {
|
|
|
419
495
|
listener(event);
|
|
420
496
|
}
|
|
421
497
|
}
|
|
498
|
+
/**
|
|
499
|
+
* Adapts a raw error or event input into a standardized `Events.ErrorEvent`.
|
|
500
|
+
* This utility is called by `_handleError` to ensure it operates on a
|
|
501
|
+
* consistent error structure.
|
|
502
|
+
* @param rawErrorOrEventInput - The raw error data (e.g., Error instance, DOM Event, Events.ErrorEvent).
|
|
503
|
+
* @returns A standardized `Events.ErrorEvent` containing an underlying `Error` instance.
|
|
504
|
+
*/
|
|
505
|
+
_adaptError(rawErrorOrEventInput) {
|
|
506
|
+
this._debug("Adapting raw error/event via _adaptError:", rawErrorOrEventInput);
|
|
507
|
+
let underlyingError;
|
|
508
|
+
if (rawErrorOrEventInput instanceof Events.ErrorEvent && rawErrorOrEventInput.error instanceof Error) {
|
|
509
|
+
return rawErrorOrEventInput;
|
|
510
|
+
}
|
|
511
|
+
if (rawErrorOrEventInput instanceof Error) {
|
|
512
|
+
underlyingError = rawErrorOrEventInput;
|
|
513
|
+
}
|
|
514
|
+
else if (typeof Event !== "undefined" &&
|
|
515
|
+
rawErrorOrEventInput instanceof Event &&
|
|
516
|
+
rawErrorOrEventInput.type === "error") {
|
|
517
|
+
underlyingError = new Error("WebSocket low-level error occurred (see browser/runtime console for native event details).");
|
|
518
|
+
}
|
|
519
|
+
else {
|
|
520
|
+
underlyingError = new Error(`Unknown WebSocket error. Raw data: ${String(rawErrorOrEventInput !== null && rawErrorOrEventInput !== void 0 ? rawErrorOrEventInput : "undefined")}`);
|
|
521
|
+
}
|
|
522
|
+
return new Events.ErrorEvent(underlyingError, this);
|
|
523
|
+
}
|
|
422
524
|
_removeListeners() {
|
|
423
525
|
if (!this._ws) {
|
|
424
526
|
return;
|
|
@@ -438,7 +540,6 @@ class ReconnectingWebSocket {
|
|
|
438
540
|
this._ws.addEventListener("open", this._handleOpen);
|
|
439
541
|
this._ws.addEventListener("close", this._handleClose);
|
|
440
542
|
this._ws.addEventListener("message", this._handleMessage);
|
|
441
|
-
// @ts-ignore
|
|
442
543
|
this._ws.addEventListener("error", this._handleError);
|
|
443
544
|
}
|
|
444
545
|
_clearTimeouts() {
|