okx-v5-ws
Version:
This is a non-official OKX V5 websocket SDK for nodejs.
180 lines • 6.32 kB
JavaScript
"use strict";
var __importDefault = (this && this.__importDefault) || function (mod) {
return (mod && mod.__esModule) ? mod : { "default": mod };
};
Object.defineProperty(exports, "__esModule", { value: true });
exports.WSConnector = void 0;
const websocket_1 = __importDefault(require("websocket"));
const WebSocketClient = websocket_1.default.client;
const util_1 = require("./util");
const events_1 = __importDefault(require("events"));
/**
* Handle WS connection level logic
*/
class WSConnector {
#serverBaseUrl;
// connection state
#connectState = 'closed';
/* reconnect state data */
#reconnectionAttempts = 0;
#reconnectionMaxAttempts = Infinity;
/* WS connection instances */
#connection = null;
#client = new WebSocketClient();
// after-connected handler
#afterConnected;
/* states & timers for handle ping pong */
#pingTimer = null;
#waitPongTimer = null;
#lastMessageReceiveTimestamp;
// events connector
#eventEmitter = new events_1.default();
/**
* constructor
*/
constructor({ serverBaseUrl, afterConnected }) {
this.#serverBaseUrl = serverBaseUrl;
this.#afterConnected = afterConnected;
this.#lastMessageReceiveTimestamp = Date.now();
this.#client.on('connectFailed', function (error) {
console.error('Connect Error: ' + error.toString());
});
this.#client.on('connect', (connection) => {
this.#connectState = 'connected';
this.#connection = connection;
this.#reconnectionAttempts = 0;
console.log('WebSocket Client Connected');
connection.on('error', (error) => {
console.error('Connection Error: ' + error.toString());
this.#eventEmitter.emit('error', error);
});
connection.on('close', (code, desc) => {
this.#eventEmitter.emit('close', code, desc);
// reset ping pong state
clearInterval(this.#pingTimer);
clearInterval(this.#waitPongTimer);
console.log(`Connection Closed. code=${code}, desc=${desc}`);
this.#connectState = 'closed';
if (code !== 1000) {
this.reconnect();
}
else {
this.#eventEmitter.emit('closed', code, desc);
}
});
connection.on('message', (message) => {
// reset ping pong state
this.#lastMessageReceiveTimestamp = Date.now();
clearTimeout(this.#pingTimer);
clearTimeout(this.#waitPongTimer);
this.#pingTimer = setTimeout(this.#ping, 15000);
if (message.type === 'utf8') {
this.#eventEmitter.emit('message', message.utf8Data);
}
});
this.#eventEmitter.emit('connect');
});
}
get event() {
return this.#eventEmitter;
}
get connected() {
return this.#connectState === 'connected';
}
get connection() {
return this.#connection;
}
/**
* connect to server
*/
async connect() {
if (this.#connectState === 'connected') {
return true;
}
// connect if not already connecting
if (this.#connectState !== 'connecting') {
this.#client.connect(this.#serverBaseUrl);
}
this.#connectState = 'connecting';
return new Promise((resolve, reject) => {
const connected = () => {
this.#client.removeListener('connectFailed', connectFailed);
resolve(true);
};
const connectFailed = (error) => {
this.#client.removeListener('connect', connected);
reject(error);
};
this.#client.once('connect', connected);
this.#client.once('connectFailed', connectFailed);
})
.then(this.#afterConnected)
.then(() => true);
}
/**
* do reconnect
*/
async reconnect() {
if (this.#connectState === 'connected' || this.#connectState === 'reconnecting') {
return;
}
this.#eventEmitter.emit('reconnect');
this.#connectState = 'reconnecting';
return new Promise((resolve, reject) => {
this.#client.once('connect', () => {
resolve();
});
(async () => {
while (this.#connectState === 'reconnecting' && this.#reconnectionAttempts < this.#reconnectionMaxAttempts) {
await (0, util_1.sleep)(5000);
this.#reconnectionAttempts++;
await this.connect();
}
if (this.#connectState === 'reconnecting') {
reject(new Error('Reconnet fail after all attempts'));
}
})();
});
}
/**
* send message to server
*
* @param data
* @returns
*/
async send(data) {
console.debug('send: ', data);
return new Promise((resolve, reject) => {
this.#connection?.send(data, (err) => {
if (err) {
reject(err);
}
else {
resolve();
}
});
});
}
/**
* close connection
*/
close = () => {
this.#connection?.close(1000);
};
/**
* send ping and wait pong or disconnect
*/
#ping = async () => {
if (this.#connectState === 'connected') {
clearTimeout(this.#waitPongTimer);
await this.send('ping');
this.#waitPongTimer = setTimeout(() => {
if (this.#connectState === 'connected' && Date.now() - this.#lastMessageReceiveTimestamp >= 15000) {
this.#connection?.close(1001);
}
}, 15000);
}
};
}
exports.WSConnector = WSConnector;
//# sourceMappingURL=WSConnector.js.map