UNPKG

kucoin-universal-sdk

Version:
154 lines 6.74 kB
"use strict"; Object.defineProperty(exports, "__esModule", { value: true }); exports.DefaultWsService = void 0; const constant_1 = require("../../model/constant"); const default_transport_1 = require("./default_transport"); const default_ws_callback_1 = require("./default_ws_callback"); const default_ws_client_1 = require("./default_ws_client"); const default_ws_token_provider_1 = require("./default_ws_token_provider"); const model_1 = require("../../model"); const sub_1 = require("../util/sub"); const common_1 = require("../../model/common"); const crypto_1 = require("crypto"); const events_1 = require("events"); const common_2 = require("../../common"); /** * DefaultWsService implements the WebSocket service interface for handling real-time data communication. * It manages WebSocket connections, subscriptions, and message handling with automatic reconnection support. */ class DefaultWsService { constructor(option, domain, privateChannel, versionString) { if (!option.webSocketClientOption) { throw new Error('WebSocketClientOption is undefined'); } this.wsOption = option.webSocketClientOption; this.privateChannel = privateChannel; this.tokenTransport = new default_transport_1.DefaultTransport(option, versionString); this.topicManager = new default_ws_callback_1.TopicManager(); this.eventEmitter = new events_1.EventEmitter(); this.eventEmitter.on('event', (event, msg) => { if (this.wsOption.eventCallback) { try { this.wsOption.eventCallback(event, msg); } catch (e) { common_2.logger.error(`call event callback error, event: ${event}`, e); } } }); this.client = new default_ws_client_1.WebSocketClient(new default_ws_token_provider_1.DefaultWsTokenProvider(this.tokenTransport, domain, privateChannel), this.wsOption); this.client.on('event', (event, msg) => { this.eventEmitter.emit('event', event, msg); }); this.client.on('message', (message) => { this.processMessages(message); }); this.client.on('reconnected', () => { this.recovery(); }); } start() { return this.client.start(); } stop() { return Promise.all([this.tokenTransport.close(), this.client.stop()]).then(); } subscribe(prefix, args, callback) { // Create subscription info with prefix, args, and callback const subInfo = new sub_1.SubInfo(prefix, args || [], callback); const subId = subInfo.toId(); // Get callback manager for the prefix and attempt to add subscription const callbackManager = this.topicManager.getCallbackManager(prefix); const created = callbackManager.add(subInfo); // Check if already subscribed if (!created) { common_2.logger.warn(`Already subscribed: ${subId}`); return Promise.reject(new Error('Already subscribed')); } // Create subscription message const subEvent = new common_1.WsMessage(); subEvent.id = subId; subEvent.type = constant_1.MessageType.SubscribeMessage; subEvent.topic = subInfo.subTopic(); subEvent.privateChannel = this.privateChannel; subEvent.response = true; return this.client .write(subEvent, this.wsOption.writeTimeout) .then(() => { common_2.logger.info(`subscribed id: ${subId}`); return subId; }) .catch((err) => { // Clean up on failure const callbackManager = this.topicManager.getCallbackManager(subInfo.prefix); callbackManager.remove(subId); common_2.logger.error(`subscribe id: ${subId}, error`, err); throw err; }); } unsubscribe(id) { return new Promise((resolve, reject) => { const subInfo = sub_1.SubInfo.fromId(id); const callbackManager = this.topicManager.getCallbackManager(subInfo.prefix); const subEvent = new common_1.WsMessage(); subEvent.id = (0, crypto_1.randomUUID)().toString(); subEvent.type = constant_1.MessageType.UnsubscribeMessage; subEvent.topic = subInfo.subTopic(); subEvent.privateChannel = this.privateChannel; subEvent.response = true; this.client .write(subEvent, this.wsOption.writeTimeout) .then(() => { callbackManager.remove(id); common_2.logger.info(`unsubscribe id: ${id}`); resolve(); }) .catch((e) => { common_2.logger.error(`unsubscribe id: ${id}, error`, e); reject(e); }); }); } processMessages(message) { const callbackManager = this.topicManager.getCallbackManager(message.topic); if (!callbackManager) { common_2.logger.warn(`Unknown topic: ${message.topic}`); return; } const callback = callbackManager.get(message.topic); if (!callback) { common_2.logger.warn(`Unknown callback for topic: ${message.topic}`); return; } try { callback.onMessage(message); } catch (err) { common_2.logger.error('Error processing callback', err); this.eventEmitter.emit('event', model_1.WebSocketEvent.EventCallbackError, String(err)); } } recovery() { common_2.logger.info('WebSocket client reconnected, resubscribe...'); const oldTopicManager = this.topicManager; this.topicManager = new default_ws_callback_1.TopicManager(); oldTopicManager.range((key, value) => { for (const sub of value.getSubInfo()) { if (sub.callback) { this.subscribe(sub.prefix, sub.args, sub.callback) .then((id) => { common_2.logger.info(`Resubscribe success, id:${id}`); this.eventEmitter.emit('event', model_1.WebSocketEvent.EventReSubscribeOK, id); }) .catch((err) => { common_2.logger.info(`Resubscribe error, id:${sub.toId()}, err:${err}`); this.eventEmitter.emit('event', model_1.WebSocketEvent.EventReSubscribeError, err.toString()); }); } } return true; }); } } exports.DefaultWsService = DefaultWsService; //# sourceMappingURL=default_ws_service.js.map