UNPKG

okx-v5-ws

Version:

This is a non-official OKX V5 websocket SDK for nodejs.

180 lines 6.32 kB
"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