@aurox/persistent-websocket-connection
Advanced tools
Comparing version 0.5.0 to 0.6.0
@@ -1,9 +0,6 @@ | ||
"use strict"; | ||
Object.defineProperty(exports, "__esModule", { value: true }); | ||
exports.DEFAULT_PERSISTENT_WEBSOCKET_CONNECTION_KEEP_ALIVE_INTERVAL = exports.DEFAULT_PERSISTENT_WEBSOCKET_CONNECTION_PING_PONG_TOLERANCE_TIMEOUT = exports.DEFAULT_PERSISTENT_WEBSOCKET_CONNECTION_PING_PONG_INTERVAL = exports.DEFAULT_PERSISTENT_WEBSOCKET_CONNECTION_CONNECTION_CHECK_INTERVAL = exports.DEFAULT_PERSISTENT_WEBSOCKET_CONNECTION_RECONNECT_TIMEOUT = void 0; | ||
exports.DEFAULT_PERSISTENT_WEBSOCKET_CONNECTION_RECONNECT_TIMEOUT = 2000; | ||
exports.DEFAULT_PERSISTENT_WEBSOCKET_CONNECTION_CONNECTION_CHECK_INTERVAL = 500; | ||
exports.DEFAULT_PERSISTENT_WEBSOCKET_CONNECTION_PING_PONG_INTERVAL = 5000; | ||
exports.DEFAULT_PERSISTENT_WEBSOCKET_CONNECTION_PING_PONG_TOLERANCE_TIMEOUT = 2000; | ||
exports.DEFAULT_PERSISTENT_WEBSOCKET_CONNECTION_KEEP_ALIVE_INTERVAL = 5000; | ||
export const DEFAULT_PERSISTENT_WEBSOCKET_CONNECTION_RECONNECT_TIMEOUT = 2000; | ||
export const DEFAULT_PERSISTENT_WEBSOCKET_CONNECTION_CONNECTION_CHECK_INTERVAL = 500; | ||
export const DEFAULT_PERSISTENT_WEBSOCKET_CONNECTION_PING_PONG_INTERVAL = 5000; | ||
export const DEFAULT_PERSISTENT_WEBSOCKET_CONNECTION_PING_PONG_TOLERANCE_TIMEOUT = 2000; | ||
export const DEFAULT_PERSISTENT_WEBSOCKET_CONNECTION_KEEP_ALIVE_INTERVAL = 5000; | ||
//# sourceMappingURL=constants.js.map |
@@ -1,2 +0,2 @@ | ||
import { PersistentWebsocketConnectionOptions } from './types'; | ||
import { PersistentWebsocketConnectionOptions } from './types.js'; | ||
type DeepRequired<T> = T extends (...args: any[]) => any ? T : T extends object ? { | ||
@@ -3,0 +3,0 @@ [P in keyof T]-?: DeepRequired<T[P]>; |
@@ -1,5 +0,2 @@ | ||
"use strict"; | ||
Object.defineProperty(exports, "__esModule", { value: true }); | ||
exports.getOptionsWithDefaults = void 0; | ||
const constants_1 = require("./constants"); | ||
import { DEFAULT_PERSISTENT_WEBSOCKET_CONNECTION_RECONNECT_TIMEOUT, DEFAULT_PERSISTENT_WEBSOCKET_CONNECTION_CONNECTION_CHECK_INTERVAL, DEFAULT_PERSISTENT_WEBSOCKET_CONNECTION_PING_PONG_INTERVAL, DEFAULT_PERSISTENT_WEBSOCKET_CONNECTION_PING_PONG_TOLERANCE_TIMEOUT, DEFAULT_PERSISTENT_WEBSOCKET_CONNECTION_KEEP_ALIVE_INTERVAL, } from './constants.js'; | ||
function defaultDoPing(send) { | ||
@@ -20,6 +17,6 @@ send('ping'); | ||
} | ||
function getOptionsWithDefaults(options) { | ||
export function getOptionsWithDefaults(options) { | ||
return { | ||
url: options.url, | ||
reconnectTimeout: options.reconnectTimeout || constants_1.DEFAULT_PERSISTENT_WEBSOCKET_CONNECTION_RECONNECT_TIMEOUT, | ||
reconnectTimeout: options.reconnectTimeout || DEFAULT_PERSISTENT_WEBSOCKET_CONNECTION_RECONNECT_TIMEOUT, | ||
reconnectBackoff: { | ||
@@ -29,9 +26,9 @@ enabled: (options.reconnectBackoff && options.reconnectBackoff.enabled) || false, | ||
backoffIncrement: (options.reconnectBackoff && options.reconnectBackoff.backoffIncrement) || | ||
constants_1.DEFAULT_PERSISTENT_WEBSOCKET_CONNECTION_RECONNECT_TIMEOUT, | ||
DEFAULT_PERSISTENT_WEBSOCKET_CONNECTION_RECONNECT_TIMEOUT, | ||
maxBackedOffTimeout: (options.reconnectBackoff && options.reconnectBackoff.maxBackedOffTimeout) || | ||
constants_1.DEFAULT_PERSISTENT_WEBSOCKET_CONNECTION_RECONNECT_TIMEOUT * 10, | ||
DEFAULT_PERSISTENT_WEBSOCKET_CONNECTION_RECONNECT_TIMEOUT * 10, | ||
backoffIncrementResetTimeout: (options.reconnectBackoff && options.reconnectBackoff.backoffIncrementResetTimeout) || | ||
constants_1.DEFAULT_PERSISTENT_WEBSOCKET_CONNECTION_RECONNECT_TIMEOUT * 10, | ||
DEFAULT_PERSISTENT_WEBSOCKET_CONNECTION_RECONNECT_TIMEOUT * 10, | ||
}, | ||
connectionCheckInterval: options.connectionCheckInterval || constants_1.DEFAULT_PERSISTENT_WEBSOCKET_CONNECTION_CONNECTION_CHECK_INTERVAL, | ||
connectionCheckInterval: options.connectionCheckInterval || DEFAULT_PERSISTENT_WEBSOCKET_CONNECTION_CONNECTION_CHECK_INTERVAL, | ||
pingPong: { | ||
@@ -43,4 +40,4 @@ enabled: (options.pingPong && options.pingPong.enabled) || false, | ||
forwardHeartbeatMessage: (options.pingPong && options.pingPong.forwardHeartbeatMessage) || false, | ||
interval: (options.pingPong && options.pingPong.interval) || constants_1.DEFAULT_PERSISTENT_WEBSOCKET_CONNECTION_PING_PONG_INTERVAL, | ||
toleranceTimeout: (options.pingPong && options.pingPong.toleranceTimeout) || constants_1.DEFAULT_PERSISTENT_WEBSOCKET_CONNECTION_PING_PONG_TOLERANCE_TIMEOUT, | ||
interval: (options.pingPong && options.pingPong.interval) || DEFAULT_PERSISTENT_WEBSOCKET_CONNECTION_PING_PONG_INTERVAL, | ||
toleranceTimeout: (options.pingPong && options.pingPong.toleranceTimeout) || DEFAULT_PERSISTENT_WEBSOCKET_CONNECTION_PING_PONG_TOLERANCE_TIMEOUT, | ||
heartbeatDebounce: (options.pingPong && options.pingPong.heartbeatDebounce) || null, | ||
@@ -53,3 +50,3 @@ doPing: (options.pingPong && options.pingPong.doPing) || defaultDoPing, | ||
enabled: (options.keepAlive && options.keepAlive.enabled) || false, | ||
interval: (options.keepAlive && options.keepAlive.interval) || constants_1.DEFAULT_PERSISTENT_WEBSOCKET_CONNECTION_KEEP_ALIVE_INTERVAL, | ||
interval: (options.keepAlive && options.keepAlive.interval) || DEFAULT_PERSISTENT_WEBSOCKET_CONNECTION_KEEP_ALIVE_INTERVAL, | ||
doKeepAlive: (options.keepAlive && options.keepAlive.doKeepAlive) || defaultDoKeepAlive, | ||
@@ -70,3 +67,2 @@ }, | ||
} | ||
exports.getOptionsWithDefaults = getOptionsWithDefaults; | ||
//# sourceMappingURL=defaults.js.map |
@@ -1,5 +0,5 @@ | ||
export * from './constants'; | ||
export * from './types'; | ||
import { PersistentWebsocketConnection, PersistentWebsocketConnectionEvents } from './PersistentWebsocketConnection'; | ||
export * from './constants.js'; | ||
export * from './types.js'; | ||
import { PersistentWebsocketConnection, PersistentWebsocketConnectionEvents } from './PersistentWebsocketConnection.js'; | ||
export { PersistentWebsocketConnectionEvents }; | ||
export default PersistentWebsocketConnection; |
@@ -1,8 +0,5 @@ | ||
"use strict"; | ||
Object.defineProperty(exports, "__esModule", { value: true }); | ||
const tslib_1 = require("tslib"); | ||
tslib_1.__exportStar(require("./constants"), exports); | ||
tslib_1.__exportStar(require("./types"), exports); | ||
const PersistentWebsocketConnection_1 = require("./PersistentWebsocketConnection"); | ||
exports.default = PersistentWebsocketConnection_1.PersistentWebsocketConnection; | ||
export * from './constants.js'; | ||
export * from './types.js'; | ||
import { PersistentWebsocketConnection } from './PersistentWebsocketConnection.js'; | ||
export default PersistentWebsocketConnection; | ||
//# sourceMappingURL=index.js.map |
import { TypedEmitter } from 'tiny-typed-emitter'; | ||
import { PersistentWebsocketConnectionOptions, ConnectionLostReason } from './types'; | ||
import { PersistentWebsocketConnectionOptions, ConnectionLostReason } from './types.js'; | ||
export interface PersistentWebsocketConnectionEvents { | ||
@@ -4,0 +4,0 @@ 'connecting': () => void; |
@@ -1,84 +0,126 @@ | ||
"use strict"; | ||
Object.defineProperty(exports, "__esModule", { value: true }); | ||
exports.PersistentWebsocketConnection = void 0; | ||
const tslib_1 = require("tslib"); | ||
const isomorphic_ws_1 = tslib_1.__importDefault(require("isomorphic-ws")); | ||
const tiny_typed_emitter_1 = require("tiny-typed-emitter"); | ||
const defaults_1 = require("./defaults"); | ||
class PersistentWebsocketConnection extends tiny_typed_emitter_1.TypedEmitter { | ||
import WebSocket from 'isomorphic-ws'; | ||
import { TypedEmitter } from 'tiny-typed-emitter'; | ||
import { getOptionsWithDefaults } from './defaults.js'; | ||
export class PersistentWebsocketConnection extends TypedEmitter { | ||
connection = null; | ||
logger; | ||
url; | ||
reconnectTimeout; | ||
connectionCheckInterval; | ||
agent; | ||
pingPongOptions; | ||
keepAliveOptions; | ||
backoffOptions; | ||
attemptingToEstablishConnection = false; | ||
closedExternally = false; | ||
closedDuoToPongOrHeartbeatTimeout = false; | ||
initialConnectionEstablished = false; | ||
reconnecting = false; | ||
reconnectingForcefully = false; | ||
reconnectingTries = 0; | ||
reconnectBackoffIncrement = 0; | ||
onInitialConnectionEstablished; | ||
onReconnect; | ||
onData; | ||
onReconnecting; | ||
onConnecting; | ||
onConnectionLost; | ||
queuedDataToSendOnReconnect = []; | ||
reconnectTimer = null; | ||
reconnectDelayTimer = null; | ||
reconnectBackoffIncrementResetTimer = null; | ||
checkConnectionTimer = null; | ||
pingPongTimer = null; | ||
pingPongToleranceTimer = null; | ||
keepAliveTimer = null; | ||
get isConnected() { | ||
var _a, _b; | ||
return ((_a = this.connection) === null || _a === void 0 ? void 0 : _a.readyState) === ((_b = this.connection) === null || _b === void 0 ? void 0 : _b.OPEN); | ||
return this.connection?.readyState === this.connection?.OPEN; | ||
} | ||
get isConnecting() { | ||
var _a, _b; | ||
return ((_a = this.connection) === null || _a === void 0 ? void 0 : _a.readyState) === ((_b = this.connection) === null || _b === void 0 ? void 0 : _b.CONNECTING); | ||
return this.connection?.readyState === this.connection?.CONNECTING; | ||
} | ||
constructor(options) { | ||
super(); | ||
this.connection = null; | ||
this.attemptingToEstablishConnection = false; | ||
this.closedExternally = false; | ||
this.closedDuoToPongOrHeartbeatTimeout = false; | ||
this.initialConnectionEstablished = false; | ||
this.reconnecting = false; | ||
this.reconnectingForcefully = false; | ||
this.reconnectingTries = 0; | ||
const optionsWithDefaults = getOptionsWithDefaults(options); | ||
this.logger = optionsWithDefaults.logger; | ||
this.url = optionsWithDefaults.url; | ||
this.reconnectTimeout = optionsWithDefaults.reconnectTimeout; | ||
this.connectionCheckInterval = optionsWithDefaults.connectionCheckInterval; | ||
this.agent = optionsWithDefaults.agent; | ||
this.pingPongOptions = optionsWithDefaults.pingPong; | ||
this.keepAliveOptions = optionsWithDefaults.keepAlive; | ||
this.backoffOptions = optionsWithDefaults.reconnectBackoff; | ||
this.onInitialConnectionEstablished = optionsWithDefaults.onInitialConnectionEstablished; | ||
this.onReconnect = optionsWithDefaults.onReconnect; | ||
this.onData = optionsWithDefaults.onData; | ||
this.onReconnecting = optionsWithDefaults.onReconnecting; | ||
this.onConnecting = optionsWithDefaults.onConnecting; | ||
this.onConnectionLost = optionsWithDefaults.onConnectionLost; | ||
this.connect(); | ||
this.checkConnection(); | ||
} | ||
send = (data) => { | ||
if (this.isConnected) { | ||
this.connection?.send(data); | ||
} | ||
}; | ||
sendOrQueue = (data) => { | ||
if (this.isConnected) { | ||
this.connection?.send(data); | ||
} | ||
else { | ||
this.queuedDataToSendOnReconnect.push(data); | ||
} | ||
}; | ||
stop = () => { | ||
if (this.closedExternally) { | ||
return; | ||
} | ||
clearTimeout(this.reconnectTimer); | ||
clearTimeout(this.reconnectDelayTimer); | ||
clearTimeout(this.reconnectBackoffIncrementResetTimer); | ||
clearTimeout(this.checkConnectionTimer); | ||
clearTimeout(this.pingPongTimer); | ||
clearTimeout(this.pingPongToleranceTimer); | ||
clearTimeout(this.keepAliveTimer); | ||
this.closedExternally = true; | ||
this.closeConnection(); | ||
this.logger.debug(`Connection to ${this.url} closed gracefully.`); | ||
this.emit('stopped'); | ||
}; | ||
forceReconnect = () => { | ||
if (this.closedExternally) { | ||
return; | ||
} | ||
this.logger.debug(`Attempting to forcefully reconnect to ${this.url}.`); | ||
clearTimeout(this.pingPongTimer); | ||
clearTimeout(this.pingPongToleranceTimer); | ||
clearTimeout(this.keepAliveTimer); | ||
// When forcefully reconnecting, we must reset the backoff state since it implies an urgent requirement | ||
this.resetBackoffIncrement(); | ||
this.reconnectingForcefully = true; | ||
this.closeConnection(); | ||
}; | ||
resetBackoffIncrement = () => { | ||
this.reconnectBackoffIncrement = 0; | ||
this.queuedDataToSendOnReconnect = []; | ||
this.reconnectTimer = null; | ||
this.reconnectDelayTimer = null; | ||
this.reconnectBackoffIncrementResetTimer = null; | ||
this.checkConnectionTimer = null; | ||
this.pingPongTimer = null; | ||
this.pingPongToleranceTimer = null; | ||
this.keepAliveTimer = null; | ||
this.send = (data) => { | ||
var _a; | ||
if (this.isConnected) { | ||
(_a = this.connection) === null || _a === void 0 ? void 0 : _a.send(data); | ||
} | ||
}; | ||
this.sendOrQueue = (data) => { | ||
var _a; | ||
if (this.isConnected) { | ||
(_a = this.connection) === null || _a === void 0 ? void 0 : _a.send(data); | ||
} | ||
else { | ||
this.queuedDataToSendOnReconnect.push(data); | ||
} | ||
}; | ||
this.stop = () => { | ||
if (this.closedExternally) { | ||
return; | ||
} | ||
clearTimeout(this.reconnectTimer); | ||
clearTimeout(this.reconnectDelayTimer); | ||
clearTimeout(this.reconnectBackoffIncrementResetTimer); | ||
clearTimeout(this.checkConnectionTimer); | ||
clearTimeout(this.pingPongTimer); | ||
clearTimeout(this.pingPongToleranceTimer); | ||
clearTimeout(this.keepAliveTimer); | ||
this.closedExternally = true; | ||
this.closeConnection(); | ||
this.logger.debug(`Connection to ${this.url} closed gracefully.`); | ||
this.emit('stopped'); | ||
}; | ||
this.forceReconnect = () => { | ||
if (this.closedExternally) { | ||
return; | ||
} | ||
this.logger.debug(`Attempting to forcefully reconnect to ${this.url}.`); | ||
clearTimeout(this.pingPongTimer); | ||
clearTimeout(this.pingPongToleranceTimer); | ||
clearTimeout(this.keepAliveTimer); | ||
// When forcefully reconnecting, we must reset the backoff state since it implies an urgent requirement | ||
this.resetBackoffIncrement(); | ||
this.reconnectingForcefully = true; | ||
this.closeConnection(); | ||
}; | ||
this.resetBackoffIncrement = () => { | ||
this.reconnectBackoffIncrement = 0; | ||
}; | ||
this.reconnect = () => { | ||
}; | ||
reconnect = () => { | ||
if (this.closedExternally || this.isConnected) { | ||
this.reconnectingTries = 0; | ||
this.reconnecting = false; | ||
return; | ||
} | ||
this.reconnecting = true; | ||
this.reconnectingTries += 1; | ||
this.onReconnecting(this.reconnectingTries); | ||
this.emit('reconnecting', this.reconnectingTries); | ||
if (!this.attemptingToEstablishConnection) { | ||
this.logger.debug(`Attempting to reconnect to ${this.url}.`); | ||
this.connect(); | ||
} | ||
else { | ||
this.logger.debug(`Attempted to reconnect to ${this.url}, but the previous attempt is still unresolved.`); | ||
} | ||
clearTimeout(this.reconnectTimer); | ||
this.reconnectTimer = setTimeout(() => { | ||
if (this.closedExternally || this.isConnected) { | ||
@@ -89,256 +131,217 @@ this.reconnectingTries = 0; | ||
} | ||
this.reconnecting = true; | ||
this.reconnectingTries += 1; | ||
this.onReconnecting(this.reconnectingTries); | ||
this.emit('reconnecting', this.reconnectingTries); | ||
if (!this.attemptingToEstablishConnection) { | ||
this.logger.debug(`Attempting to reconnect to ${this.url}.`); | ||
this.connect(); | ||
this.logger.debug(`Reconnection to ${this.url} timed out after ${this.reconnectTimeout}ms, retrying...`); | ||
this.reconnectWithDelay(); | ||
}, this.reconnectTimeout); | ||
}; | ||
reconnectWithDelay = () => { | ||
clearTimeout(this.reconnectTimer); | ||
clearTimeout(this.reconnectDelayTimer); | ||
clearTimeout(this.reconnectBackoffIncrementResetTimer); | ||
if (this.closedExternally || this.isConnected) { | ||
this.reconnectingTries = 0; | ||
this.reconnecting = false; | ||
return; | ||
} | ||
this.reconnecting = true; | ||
let delay = 0; | ||
if (this.backoffOptions.enabled) { | ||
this.reconnectBackoffIncrement += 1; | ||
const incrementDiff = this.reconnectBackoffIncrement - this.backoffOptions.backoffOffset; | ||
const increment = incrementDiff <= 0 ? 0 : Math.round(incrementDiff); | ||
delay = this.backoffOptions.backoffIncrement * increment; | ||
if (delay > this.backoffOptions.maxBackedOffTimeout) { | ||
delay = this.backoffOptions.maxBackedOffTimeout; | ||
} | ||
else { | ||
this.logger.debug(`Attempted to reconnect to ${this.url}, but the previous attempt is still unresolved.`); | ||
} | ||
clearTimeout(this.reconnectTimer); | ||
this.reconnectTimer = setTimeout(() => { | ||
if (this.closedExternally || this.isConnected) { | ||
this.reconnectingTries = 0; | ||
this.reconnecting = false; | ||
return; | ||
} | ||
this.logger.debug(`Reconnection to ${this.url} timed out after ${this.reconnectTimeout}ms, retrying...`); | ||
this.reconnectWithDelay(); | ||
}, this.reconnectTimeout); | ||
}; | ||
this.reconnectWithDelay = () => { | ||
clearTimeout(this.reconnectTimer); | ||
clearTimeout(this.reconnectDelayTimer); | ||
clearTimeout(this.reconnectBackoffIncrementResetTimer); | ||
if (this.closedExternally || this.isConnected) { | ||
this.reconnectingTries = 0; | ||
this.reconnecting = false; | ||
if (delay > 0) { | ||
this.logger.debug(`Will try to reconnect to ${this.url} after ${delay}ms.`); | ||
clearTimeout(this.reconnectDelayTimer); | ||
this.reconnectDelayTimer = setTimeout(this.reconnect, delay); | ||
return; | ||
} | ||
this.reconnecting = true; | ||
let delay = 0; | ||
if (this.backoffOptions.enabled) { | ||
this.reconnectBackoffIncrement += 1; | ||
const incrementDiff = this.reconnectBackoffIncrement - this.backoffOptions.backoffOffset; | ||
const increment = incrementDiff <= 0 ? 0 : Math.round(incrementDiff); | ||
delay = this.backoffOptions.backoffIncrement * increment; | ||
if (delay > this.backoffOptions.maxBackedOffTimeout) { | ||
delay = this.backoffOptions.maxBackedOffTimeout; | ||
} | ||
this.reconnect(); | ||
}; | ||
closeConnection = () => { | ||
try { | ||
this.connection?.close(); | ||
} | ||
catch { | ||
// Ignore errors here, failure to close is not very problematic | ||
} | ||
}; | ||
handleConnectionLost = (reason, error) => { | ||
let preventDefault = false; | ||
this.onConnectionLost({ | ||
reason, | ||
error, | ||
preventDefault: () => { | ||
preventDefault = true; | ||
}, | ||
}); | ||
if (!preventDefault) { | ||
this.emit('connection-lost', reason, error); | ||
this.reconnectWithDelay(); | ||
} | ||
}; | ||
handleNoPong = () => { | ||
if (this.pingPongOptions.heartbeatOnly) { | ||
const debounce = this.pingPongOptions.heartbeatDebounce ? this.pingPongOptions.heartbeatDebounce : 0; | ||
const tolerance = this.pingPongOptions.toleranceTimeout + debounce; | ||
this.logger.error(`Connection to ${this.url}: Heartbeat was not received for ${tolerance}ms, it will be reset.`); | ||
} | ||
else { | ||
const tolerance = this.pingPongOptions.toleranceTimeout; | ||
this.logger.error(`Connection to ${this.url}: Ping was not answered by a pong for ${tolerance}ms, it will be reset.`); | ||
} | ||
clearTimeout(this.pingPongTimer); | ||
this.closedDuoToPongOrHeartbeatTimeout = true; | ||
this.closeConnection(); | ||
}; | ||
ping = (intervalOverride) => { | ||
this.pingPongTimer = setTimeout(() => { | ||
if (this.isConnected) { | ||
this.pingPongToleranceTimer = setTimeout(this.handleNoPong, this.pingPongOptions.toleranceTimeout); | ||
if (!this.pingPongOptions.heartbeatOnly) { | ||
this.pingPongOptions.doPing(this.send); | ||
} | ||
if (delay > 0) { | ||
this.logger.debug(`Will try to reconnect to ${this.url} after ${delay}ms.`); | ||
clearTimeout(this.reconnectDelayTimer); | ||
this.reconnectDelayTimer = setTimeout(this.reconnect, delay); | ||
return; | ||
} | ||
} | ||
this.reconnect(); | ||
}; | ||
this.closeConnection = () => { | ||
var _a; | ||
try { | ||
(_a = this.connection) === null || _a === void 0 ? void 0 : _a.close(); | ||
}, intervalOverride || this.pingPongOptions.interval); | ||
}; | ||
keepAlive = () => { | ||
this.keepAliveTimer = setTimeout(() => { | ||
if (this.isConnected) { | ||
this.keepAliveOptions.doKeepAlive(this.send); | ||
this.keepAlive(); | ||
} | ||
catch (_b) { | ||
// Ignore errors here, failure to close is not very problematic | ||
}, this.keepAliveOptions.interval); | ||
}; | ||
handleOpen = () => { | ||
this.attemptingToEstablishConnection = false; | ||
this.reconnectingForcefully = false; | ||
this.closedDuoToPongOrHeartbeatTimeout = false; | ||
this.emit('connected'); | ||
if (!this.initialConnectionEstablished) { | ||
this.logger.debug(`Connection to ${this.url} was successfully established.`); | ||
this.onInitialConnectionEstablished(this.send); | ||
this.initialConnectionEstablished = true; | ||
} | ||
else { | ||
clearTimeout(this.reconnectBackoffIncrementResetTimer); | ||
this.reconnectBackoffIncrementResetTimer = setTimeout(this.resetBackoffIncrement, this.backoffOptions.backoffIncrementResetTimeout); | ||
this.logger.debug(`Successfully reconnected to ${this.url}.`); | ||
this.onReconnect(this.send); | ||
this.emit('reconnected'); | ||
} | ||
if (this.queuedDataToSendOnReconnect.length > 0) { | ||
const queue = this.queuedDataToSendOnReconnect; | ||
this.queuedDataToSendOnReconnect = []; | ||
this.logger.debug(`Attempting to send ${queue.length} queued item(s) to ${this.url}.`); | ||
for (const data of queue) { | ||
this.connection?.send(data); | ||
} | ||
}; | ||
this.handleConnectionLost = (reason, error) => { | ||
let preventDefault = false; | ||
this.onConnectionLost({ | ||
reason, | ||
error, | ||
preventDefault: () => { | ||
preventDefault = true; | ||
}, | ||
}); | ||
if (!preventDefault) { | ||
this.emit('connection-lost', reason, error); | ||
this.reconnectWithDelay(); | ||
} | ||
if (this.pingPongOptions.enabled) { | ||
this.ping(); | ||
} | ||
if (this.keepAliveOptions.enabled) { | ||
this.keepAlive(); | ||
} | ||
}; | ||
handleClose = () => { | ||
this.attemptingToEstablishConnection = false; | ||
clearTimeout(this.pingPongTimer); | ||
clearTimeout(this.pingPongToleranceTimer); | ||
clearTimeout(this.keepAliveTimer); | ||
if (!this.closedExternally && !this.reconnecting) { | ||
if (this.closedDuoToPongOrHeartbeatTimeout) { | ||
this.closedDuoToPongOrHeartbeatTimeout = false; | ||
this.handleConnectionLost('pong-or-heartbeat-timeout'); | ||
} | ||
}; | ||
this.handleNoPong = () => { | ||
if (this.pingPongOptions.heartbeatOnly) { | ||
const debounce = this.pingPongOptions.heartbeatDebounce ? this.pingPongOptions.heartbeatDebounce : 0; | ||
const tolerance = this.pingPongOptions.toleranceTimeout + debounce; | ||
this.logger.error(`Connection to ${this.url}: Heartbeat was not received for ${tolerance}ms, it will be reset.`); | ||
} | ||
else { | ||
const tolerance = this.pingPongOptions.toleranceTimeout; | ||
this.logger.error(`Connection to ${this.url}: Ping was not answered by a pong for ${tolerance}ms, it will be reset.`); | ||
this.handleConnectionLost('closed-unexpectedly'); | ||
} | ||
clearTimeout(this.pingPongTimer); | ||
this.closedDuoToPongOrHeartbeatTimeout = true; | ||
this.closeConnection(); | ||
}; | ||
this.ping = (intervalOverride) => { | ||
this.pingPongTimer = setTimeout(() => { | ||
if (this.isConnected) { | ||
this.pingPongToleranceTimer = setTimeout(this.handleNoPong, this.pingPongOptions.toleranceTimeout); | ||
if (!this.pingPongOptions.heartbeatOnly) { | ||
this.pingPongOptions.doPing(this.send); | ||
} | ||
} | ||
}; | ||
handleError = (event) => { | ||
this.attemptingToEstablishConnection = false; | ||
clearTimeout(this.pingPongTimer); | ||
clearTimeout(this.pingPongToleranceTimer); | ||
clearTimeout(this.keepAliveTimer); | ||
if (!this.closedExternally && !this.reconnecting) { | ||
this.logger.error(`Connection to ${this.url} encountered an error.`); | ||
this.handleConnectionLost('error', (event && event.message) || undefined); | ||
} | ||
}; | ||
handleMessage = (event) => { | ||
if (this.pingPongOptions.enabled) { | ||
if (this.pingPongOptions.doCheckHeartbeat(event)) { | ||
this.emit('heartbeat'); | ||
clearTimeout(this.pingPongTimer); | ||
clearTimeout(this.pingPongToleranceTimer); | ||
if (this.pingPongOptions.heartbeatDebounce !== null) { | ||
this.ping(this.pingPongOptions.heartbeatDebounce); | ||
} | ||
}, intervalOverride || this.pingPongOptions.interval); | ||
}; | ||
this.keepAlive = () => { | ||
this.keepAliveTimer = setTimeout(() => { | ||
if (this.isConnected) { | ||
this.keepAliveOptions.doKeepAlive(this.send); | ||
this.keepAlive(); | ||
else { | ||
this.ping(); | ||
} | ||
}, this.keepAliveOptions.interval); | ||
}; | ||
this.handleOpen = () => { | ||
var _a; | ||
this.attemptingToEstablishConnection = false; | ||
this.reconnectingForcefully = false; | ||
this.closedDuoToPongOrHeartbeatTimeout = false; | ||
this.emit('connected'); | ||
if (!this.initialConnectionEstablished) { | ||
this.logger.debug(`Connection to ${this.url} was successfully established.`); | ||
this.onInitialConnectionEstablished(this.send); | ||
this.initialConnectionEstablished = true; | ||
if (!this.pingPongOptions.forwardHeartbeatMessage) { | ||
return; | ||
} | ||
} | ||
else { | ||
clearTimeout(this.reconnectBackoffIncrementResetTimer); | ||
this.reconnectBackoffIncrementResetTimer = setTimeout(this.resetBackoffIncrement, this.backoffOptions.backoffIncrementResetTimeout); | ||
this.logger.debug(`Successfully reconnected to ${this.url}.`); | ||
this.onReconnect(this.send); | ||
this.emit('reconnected'); | ||
} | ||
if (this.queuedDataToSendOnReconnect.length > 0) { | ||
const queue = this.queuedDataToSendOnReconnect; | ||
this.queuedDataToSendOnReconnect = []; | ||
this.logger.debug(`Attempting to send ${queue.length} queued item(s) to ${this.url}.`); | ||
for (const data of queue) { | ||
(_a = this.connection) === null || _a === void 0 ? void 0 : _a.send(data); | ||
else if (!this.pingPongOptions.heartbeatOnly && this.pingPongOptions.doCheckPong(event)) { | ||
this.emit('pong'); | ||
clearTimeout(this.pingPongTimer); | ||
clearTimeout(this.pingPongToleranceTimer); | ||
this.ping(); | ||
if (!this.pingPongOptions.forwardPongMessage) { | ||
return; | ||
} | ||
} | ||
if (this.pingPongOptions.enabled) { | ||
else if (!this.pingPongOptions.disableDebounceOnMessage) { | ||
clearTimeout(this.pingPongTimer); | ||
clearTimeout(this.pingPongToleranceTimer); | ||
this.ping(); | ||
} | ||
if (this.keepAliveOptions.enabled) { | ||
this.keepAlive(); | ||
} | ||
const data = event.data === 'string' ? event.data : event.data.toString(); | ||
this.onData(data); | ||
this.emit('data', data); | ||
}; | ||
checkConnection = () => { | ||
if (this.closedExternally) { | ||
return; | ||
} | ||
if (this.initialConnectionEstablished && !this.reconnecting && !this.isConnected) { | ||
if (this.reconnectingForcefully) { | ||
this.logger.debug(`Connection to ${this.url} was forcefully closed.`); | ||
} | ||
}; | ||
this.handleClose = () => { | ||
this.attemptingToEstablishConnection = false; | ||
clearTimeout(this.pingPongTimer); | ||
clearTimeout(this.pingPongToleranceTimer); | ||
clearTimeout(this.keepAliveTimer); | ||
if (!this.closedExternally && !this.reconnecting) { | ||
if (this.closedDuoToPongOrHeartbeatTimeout) { | ||
this.closedDuoToPongOrHeartbeatTimeout = false; | ||
this.handleConnectionLost('pong-or-heartbeat-timeout'); | ||
} | ||
else { | ||
this.handleConnectionLost('closed-unexpectedly'); | ||
} | ||
else { | ||
this.logger.error(`Connection to ${this.url} was unexpectedly lost.`); | ||
} | ||
}; | ||
this.handleError = (event) => { | ||
this.attemptingToEstablishConnection = false; | ||
clearTimeout(this.pingPongTimer); | ||
clearTimeout(this.pingPongToleranceTimer); | ||
clearTimeout(this.keepAliveTimer); | ||
if (!this.closedExternally && !this.reconnecting) { | ||
this.logger.error(`Connection to ${this.url} encountered an error.`); | ||
this.handleConnectionLost('error', (event && event.message) || undefined); | ||
if (this.closedDuoToPongOrHeartbeatTimeout) { | ||
this.closedDuoToPongOrHeartbeatTimeout = false; | ||
this.handleConnectionLost('pong-or-heartbeat-timeout'); | ||
} | ||
}; | ||
this.handleMessage = (event) => { | ||
if (this.pingPongOptions.enabled) { | ||
if (this.pingPongOptions.doCheckHeartbeat(event)) { | ||
this.emit('heartbeat'); | ||
clearTimeout(this.pingPongTimer); | ||
clearTimeout(this.pingPongToleranceTimer); | ||
if (this.pingPongOptions.heartbeatDebounce !== null) { | ||
this.ping(this.pingPongOptions.heartbeatDebounce); | ||
} | ||
else { | ||
this.ping(); | ||
} | ||
if (!this.pingPongOptions.forwardHeartbeatMessage) { | ||
return; | ||
} | ||
} | ||
else if (!this.pingPongOptions.heartbeatOnly && this.pingPongOptions.doCheckPong(event)) { | ||
this.emit('pong'); | ||
clearTimeout(this.pingPongTimer); | ||
clearTimeout(this.pingPongToleranceTimer); | ||
this.ping(); | ||
if (!this.pingPongOptions.forwardPongMessage) { | ||
return; | ||
} | ||
} | ||
else if (!this.pingPongOptions.disableDebounceOnMessage) { | ||
clearTimeout(this.pingPongTimer); | ||
clearTimeout(this.pingPongToleranceTimer); | ||
this.ping(); | ||
} | ||
else { | ||
this.handleConnectionLost('closed-unexpectedly'); | ||
} | ||
const data = event.data === 'string' ? event.data : event.data.toString(); | ||
this.onData(data); | ||
this.emit('data', data); | ||
}; | ||
this.checkConnection = () => { | ||
if (this.closedExternally) { | ||
return; | ||
} | ||
if (this.initialConnectionEstablished && !this.reconnecting && !this.isConnected) { | ||
if (this.reconnectingForcefully) { | ||
this.logger.debug(`Connection to ${this.url} was forcefully closed.`); | ||
} | ||
else { | ||
this.logger.error(`Connection to ${this.url} was unexpectedly lost.`); | ||
} | ||
clearTimeout(this.pingPongTimer); | ||
clearTimeout(this.pingPongToleranceTimer); | ||
clearTimeout(this.keepAliveTimer); | ||
if (this.closedDuoToPongOrHeartbeatTimeout) { | ||
this.closedDuoToPongOrHeartbeatTimeout = false; | ||
this.handleConnectionLost('pong-or-heartbeat-timeout'); | ||
} | ||
else { | ||
this.handleConnectionLost('closed-unexpectedly'); | ||
} | ||
} | ||
this.checkConnectionTimer = setTimeout(this.checkConnection, this.connectionCheckInterval); | ||
}; | ||
this.connect = () => { | ||
this.closeConnection(); | ||
this.attemptingToEstablishConnection = true; | ||
this.logger.debug(`Attempting to connect to ${this.url}.`); | ||
this.connection = new isomorphic_ws_1.default(this.url, { agent: this.agent }); | ||
this.onConnecting(this.connection); | ||
this.emit('connecting'); | ||
this.connection.onopen = this.handleOpen; | ||
this.connection.onmessage = this.handleMessage; | ||
this.connection.onclose = this.handleClose; | ||
this.connection.onerror = this.handleError; | ||
}; | ||
const optionsWithDefaults = (0, defaults_1.getOptionsWithDefaults)(options); | ||
this.logger = optionsWithDefaults.logger; | ||
this.url = optionsWithDefaults.url; | ||
this.reconnectTimeout = optionsWithDefaults.reconnectTimeout; | ||
this.connectionCheckInterval = optionsWithDefaults.connectionCheckInterval; | ||
this.agent = optionsWithDefaults.agent; | ||
this.pingPongOptions = optionsWithDefaults.pingPong; | ||
this.keepAliveOptions = optionsWithDefaults.keepAlive; | ||
this.backoffOptions = optionsWithDefaults.reconnectBackoff; | ||
this.onInitialConnectionEstablished = optionsWithDefaults.onInitialConnectionEstablished; | ||
this.onReconnect = optionsWithDefaults.onReconnect; | ||
this.onData = optionsWithDefaults.onData; | ||
this.onReconnecting = optionsWithDefaults.onReconnecting; | ||
this.onConnecting = optionsWithDefaults.onConnecting; | ||
this.onConnectionLost = optionsWithDefaults.onConnectionLost; | ||
this.connect(); | ||
this.checkConnection(); | ||
} | ||
} | ||
this.checkConnectionTimer = setTimeout(this.checkConnection, this.connectionCheckInterval); | ||
}; | ||
connect = () => { | ||
this.closeConnection(); | ||
this.attemptingToEstablishConnection = true; | ||
this.logger.debug(`Attempting to connect to ${this.url}.`); | ||
this.connection = new WebSocket(this.url, { agent: this.agent }); | ||
this.onConnecting(this.connection); | ||
this.emit('connecting'); | ||
this.connection.onopen = this.handleOpen; | ||
this.connection.onmessage = this.handleMessage; | ||
this.connection.onclose = this.handleClose; | ||
this.connection.onerror = this.handleError; | ||
}; | ||
} | ||
exports.PersistentWebsocketConnection = PersistentWebsocketConnection; | ||
//# sourceMappingURL=PersistentWebsocketConnection.js.map |
@@ -1,2 +0,2 @@ | ||
/// <reference types="node" /> | ||
/// <reference types="node" resolution-mode="require"/> | ||
import WebSocket, { MessageEvent } from 'ws'; | ||
@@ -3,0 +3,0 @@ import type { Agent } from 'http'; |
@@ -1,3 +0,2 @@ | ||
"use strict"; | ||
Object.defineProperty(exports, "__esModule", { value: true }); | ||
export {}; | ||
//# sourceMappingURL=types.js.map |
{ | ||
"name": "@aurox/persistent-websocket-connection", | ||
"version": "0.5.0", | ||
"version": "0.6.0", | ||
"description": "Aurox Persistent Websocket Connection", | ||
"main": "dist/index.js", | ||
"types": "dist/index.d.ts", | ||
"type": "module", | ||
"scripts": { | ||
@@ -20,20 +21,20 @@ "prepublishOnly": "npm run build", | ||
"dependencies": { | ||
"@types/node": "^18.11.18", | ||
"@types/ws": "^8.5.3", | ||
"@types/node": "^20.3.0", | ||
"@types/ws": "^8.5.5", | ||
"isomorphic-ws": "^5.0.0", | ||
"tiny-typed-emitter": "^2.1.0", | ||
"tslib": "^2.4.1", | ||
"ws": "^8.11.0" | ||
"tslib": "^2.5.3", | ||
"ws": "^8.13.0" | ||
}, | ||
"devDependencies": { | ||
"@typescript-eslint/eslint-plugin": "^5.47.1", | ||
"@typescript-eslint/parser": "^5.47.1", | ||
"eslint": "^8.30.0", | ||
"eslint-config-prettier": "^8.5.0", | ||
"@typescript-eslint/eslint-plugin": "^5.59.9", | ||
"@typescript-eslint/parser": "^5.59.9", | ||
"eslint": "^8.42.0", | ||
"eslint-config-prettier": "^8.8.0", | ||
"eslint-plugin-prettier": "^4.2.1", | ||
"prettier": "^2.8.1", | ||
"rimraf": "^3.0.2", | ||
"prettier": "^2.8.8", | ||
"rimraf": "^5.0.1", | ||
"ts-node": "^10.9.1", | ||
"typescript": "^4.9.4" | ||
"typescript": "^5.1.3" | ||
} | ||
} |
import { MessageEvent } from 'ws'; | ||
import { PersistentWebsocketConnectionOptions } from './types'; | ||
import { PersistentWebsocketConnectionOptions } from './types.js'; | ||
import { | ||
@@ -10,3 +10,3 @@ DEFAULT_PERSISTENT_WEBSOCKET_CONNECTION_RECONNECT_TIMEOUT, | ||
DEFAULT_PERSISTENT_WEBSOCKET_CONNECTION_KEEP_ALIVE_INTERVAL, | ||
} from './constants'; | ||
} from './constants.js'; | ||
@@ -13,0 +13,0 @@ function defaultDoPing(send: (data: any) => void) { |
@@ -1,5 +0,5 @@ | ||
export * from './constants'; | ||
export * from './types'; | ||
export * from './constants.js'; | ||
export * from './types.js'; | ||
import { PersistentWebsocketConnection, PersistentWebsocketConnectionEvents } from './PersistentWebsocketConnection'; | ||
import { PersistentWebsocketConnection, PersistentWebsocketConnectionEvents } from './PersistentWebsocketConnection.js'; | ||
@@ -6,0 +6,0 @@ export { PersistentWebsocketConnectionEvents }; |
@@ -5,3 +5,3 @@ import WebSocket, { MessageEvent, ErrorEvent } from 'isomorphic-ws'; | ||
import { getOptionsWithDefaults } from './defaults'; | ||
import { getOptionsWithDefaults } from './defaults.js'; | ||
import { | ||
@@ -15,3 +15,3 @@ PersistentWebsocketConnectionPingPongOptions, | ||
PersistentWebsocketConnectionKeepAlive, | ||
} from './types'; | ||
} from './types.js'; | ||
@@ -18,0 +18,0 @@ export interface PersistentWebsocketConnectionEvents { |
Sorry, the diff of this file is not supported yet
Sorry, the diff of this file is not supported yet
Sorry, the diff of this file is not supported yet
Sorry, the diff of this file is not supported yet
License Policy Violation
LicenseThis package is not allowed per your license policy. Review the package's license to ensure compliance.
Found 1 instance in 1 package
License Policy Violation
LicenseThis package is not allowed per your license policy. Review the package's license to ensure compliance.
Found 1 instance in 1 package
New author
Supply chain riskA new npm collaborator published a version of the package for the first time. New collaborators are usually benign additions to a project, but do indicate a change to the security surface area of a package.
Found 1 instance in 1 package
0
Yes
62858
1051
+ Added@types/node@20.17.6(transitive)
+ Addedundici-types@6.19.8(transitive)
- Removed@types/node@18.19.64(transitive)
- Removedundici-types@5.26.5(transitive)
Updated@types/node@^20.3.0
Updated@types/ws@^8.5.5
Updatedtslib@^2.5.3
Updatedws@^8.13.0