v2.4.0-beta.1. remove circleci, cleaning in ws client

This commit is contained in:
tiagosiebler
2022-09-14 23:12:44 +01:00
parent 04fd7989dc
commit d1ed7971ad
9 changed files with 296 additions and 264 deletions

View File

@@ -1,31 +0,0 @@
version: 2.1
jobs:
test:
docker:
- image: cimg/node:15.1
steps:
- checkout
- restore_cache:
# See the configuration reference documentation for more details on using restore_cache and save_cache steps
# https://circleci.com/docs/2.0/configuration-reference/?section=reference#save_cache
keys:
- node-deps-v1-{{ .Branch }}-{{checksum "package-lock.json"}}
- run:
name: install packages
command: npm ci --ignore-scripts
- save_cache:
key: node-deps-v1-{{ .Branch }}-{{checksum "package-lock.json"}}
paths:
- ~/.npm
- run:
name: Run Build
command: npm run build
- run:
name: Run Tests
command: npm run test
workflows:
integrationtests:
jobs:
- test

View File

@@ -1,6 +1,6 @@
{ {
"name": "bybit-api", "name": "bybit-api",
"version": "2.3.2", "version": "2.4.0-beta.1",
"description": "Node.js connector for Bybit's REST APIs and WebSockets, with TypeScript & integration tests.", "description": "Node.js connector for Bybit's REST APIs and WebSockets, with TypeScript & integration tests.",
"main": "lib/index.js", "main": "lib/index.js",
"types": "lib/index.d.ts", "types": "lib/index.d.ts",

View File

@@ -1,3 +1,4 @@
export * from './response'; export * from './response';
export * from './request'; export * from './request';
export * from './shared'; export * from './shared';
export * from './websockets';

View File

@@ -1,3 +1,14 @@
import { InverseClient } from '../inverse-client';
import { LinearClient } from '../linear-client';
import { SpotClient } from '../spot-client';
import { SpotClientV3 } from '../spot-client-v3';
export type RESTClient =
| InverseClient
| LinearClient
| SpotClient
| SpotClientV3;
export type numberInString = string; export type numberInString = string;
export type OrderSide = 'Buy' | 'Sell'; export type OrderSide = 'Buy' | 'Sell';

107
src/types/websockets.ts Normal file
View File

@@ -0,0 +1,107 @@
import { RestClientOptions } from '../util';
export type APIMarket = 'inverse' | 'linear' | 'spot' | 'v3';
// Same as inverse futures
export type WsPublicInverseTopic =
| 'orderBookL2_25'
| 'orderBookL2_200'
| 'trade'
| 'insurance'
| 'instrument_info'
| 'klineV2';
export type WsPublicUSDTPerpTopic =
| 'orderBookL2_25'
| 'orderBookL2_200'
| 'trade'
| 'insurance'
| 'instrument_info'
| 'kline';
export type WsPublicSpotV1Topic =
| 'trade'
| 'realtimes'
| 'kline'
| 'depth'
| 'mergedDepth'
| 'diffDepth';
export type WsPublicSpotV2Topic =
| 'depth'
| 'kline'
| 'trade'
| 'bookTicker'
| 'realtimes';
export type WsPublicTopics =
| WsPublicInverseTopic
| WsPublicUSDTPerpTopic
| WsPublicSpotV1Topic
| WsPublicSpotV2Topic
| string;
// Same as inverse futures
export type WsPrivateInverseTopic =
| 'position'
| 'execution'
| 'order'
| 'stop_order';
export type WsPrivateUSDTPerpTopic =
| 'position'
| 'execution'
| 'order'
| 'stop_order'
| 'wallet';
export type WsPrivateSpotTopic =
| 'outboundAccountInfo'
| 'executionReport'
| 'ticketInfo';
export type WsPrivateTopic =
| WsPrivateInverseTopic
| WsPrivateUSDTPerpTopic
| WsPrivateSpotTopic
| string;
export type WsTopic = WsPublicTopics | WsPrivateTopic;
/** This is used to differentiate between each of the available websocket streams (as bybit has multiple websockets) */
export type WsKey =
| 'inverse'
| 'linearPrivate'
| 'linearPublic'
| 'spotPrivate'
| 'spotPublic';
export interface WSClientConfigurableOptions {
key?: string;
secret?: string;
livenet?: boolean;
/**
* The API group this client should connect to.
*
* For the V3 APIs use `v3` as the market (spot/unified margin/usdc/account asset/copy trading)
*/
market: APIMarket;
pongTimeout?: number;
pingInterval?: number;
reconnectTimeout?: number;
restOptions?: RestClientOptions;
requestOptions?: any;
wsUrl?: string;
/** If true, fetch server time before trying to authenticate (disabled by default) */
fetchTimeOffsetBeforeAuth?: boolean;
}
export interface WebsocketClientOptions extends WSClientConfigurableOptions {
livenet: boolean;
market: APIMarket;
pongTimeout: number;
pingInterval: number;
reconnectTimeout: number;
}

View File

@@ -1,16 +1,35 @@
import WebSocket from 'isomorphic-ws'; import WebSocket from 'isomorphic-ws';
import { WsConnectionState } from '../websocket-client';
import { DefaultLogger } from './logger'; import { DefaultLogger } from './logger';
export enum WsConnectionStateEnum {
INITIAL = 0,
CONNECTING = 1,
CONNECTED = 2,
CLOSING = 3,
RECONNECTING = 4,
}
/** A "topic" is always a string */
type WsTopic = string; type WsTopic = string;
/**
* A "Set" is used to ensure we only subscribe to a topic once (tracking a list of unique topics we're expected to be connected to)
* TODO: do any WS topics allow parameters? If so, we need a way to track those (see FTX implementation)
*/
type WsTopicList = Set<WsTopic>; type WsTopicList = Set<WsTopic>;
interface WsStoredState { interface WsStoredState {
/** The currently active websocket connection */
ws?: WebSocket; ws?: WebSocket;
connectionState?: WsConnectionState; /** The current lifecycle state of the connection (enum) */
connectionState?: WsConnectionStateEnum;
/** A timer that will send an upstream heartbeat (ping) when it expires */
activePingTimer?: ReturnType<typeof setTimeout> | undefined; activePingTimer?: ReturnType<typeof setTimeout> | undefined;
/** A timer tracking that an upstream heartbeat was sent, expecting a reply before it expires */
activePongTimer?: ReturnType<typeof setTimeout> | undefined; activePongTimer?: ReturnType<typeof setTimeout> | undefined;
/**
* All the topics we are expected to be subscribed to (and we automatically resubscribe to if the connection drops)
*/
subscribedTopics: WsTopicList; subscribedTopics: WsTopicList;
} }
@@ -23,6 +42,9 @@ export default class WsStore {
this.wsState = {}; this.wsState = {};
} }
/** Get WS stored state for key, optionally create if missing */
get(key: string, createIfMissing?: true): WsStoredState;
get(key: string, createIfMissing?: false): WsStoredState | undefined;
get(key: string, createIfMissing?: boolean): WsStoredState | undefined { get(key: string, createIfMissing?: boolean): WsStoredState | undefined {
if (this.wsState[key]) { if (this.wsState[key]) {
return this.wsState[key]; return this.wsState[key];
@@ -46,7 +68,7 @@ export default class WsStore {
} }
this.wsState[key] = { this.wsState[key] = {
subscribedTopics: new Set(), subscribedTopics: new Set(),
connectionState: WsConnectionState.READY_STATE_INITIAL, connectionState: WsConnectionStateEnum.INITIAL,
}; };
return this.get(key); return this.get(key);
} }
@@ -94,22 +116,22 @@ export default class WsStore {
); );
} }
getConnectionState(key: string): WsConnectionState { getConnectionState(key: string): WsConnectionStateEnum {
return this.get(key, true)!.connectionState!; return this.get(key, true)!.connectionState!;
} }
setConnectionState(key: string, state: WsConnectionState) { setConnectionState(key: string, state: WsConnectionStateEnum) {
this.get(key, true)!.connectionState = state; this.get(key, true)!.connectionState = state;
} }
isConnectionState(key: string, state: WsConnectionState): boolean { isConnectionState(key: string, state: WsConnectionStateEnum): boolean {
return this.getConnectionState(key) === state; return this.getConnectionState(key) === state;
} }
/* subscribed topics */ /* subscribed topics */
getTopics(key: string): WsTopicList { getTopics(key: string): WsTopicList {
return this.get(key, true)!.subscribedTopics; return this.get(key, true).subscribedTopics;
} }
getTopicsByKey(): Record<string, WsTopicList> { getTopicsByKey(): Record<string, WsTopicList> {

View File

@@ -2,3 +2,4 @@ export * from './BaseRestClient';
export * from './requestUtils'; export * from './requestUtils';
export * from './WsStore'; export * from './WsStore';
export * from './logger'; export * from './logger';
export * from './websocket-util';

View File

@@ -0,0 +1,40 @@
import { WsKey } from '../types';
export const wsKeyInverse = 'inverse';
export const wsKeyLinearPrivate = 'linearPrivate';
export const wsKeyLinearPublic = 'linearPublic';
export const wsKeySpotPrivate = 'spotPrivate';
export const wsKeySpotPublic = 'spotPublic';
export function getLinearWsKeyForTopic(topic: string): WsKey {
const privateLinearTopics = [
'position',
'execution',
'order',
'stop_order',
'wallet',
];
if (privateLinearTopics.includes(topic)) {
return wsKeyLinearPrivate;
}
return wsKeyLinearPublic;
}
export function getSpotWsKeyForTopic(topic: string): WsKey {
const privateLinearTopics = [
'position',
'execution',
'order',
'stop_order',
'outboundAccountInfo',
'executionReport',
'ticketInfo',
];
if (privateLinearTopics.includes(topic)) {
return wsKeySpotPrivate;
}
return wsKeySpotPublic;
}

View File

@@ -3,17 +3,35 @@ import WebSocket from 'isomorphic-ws';
import { InverseClient } from './inverse-client'; import { InverseClient } from './inverse-client';
import { LinearClient } from './linear-client'; import { LinearClient } from './linear-client';
import { DefaultLogger } from './util/logger'; import { SpotClientV3 } from './spot-client-v3';
import { SpotClient } from './spot-client'; import { SpotClient } from './spot-client';
import { KlineInterval } from './types/shared';
import { DefaultLogger } from './util/logger';
import {
APIMarket,
KlineInterval,
RESTClient,
WebsocketClientOptions,
WSClientConfigurableOptions,
WsKey,
WsTopic,
} from './types';
import { signMessage } from './util/node-support'; import { signMessage } from './util/node-support';
import WsStore from './util/WsStore';
import { import {
serializeParams, serializeParams,
isWsPong, isWsPong,
RestClientOptions, getLinearWsKeyForTopic,
} from './util/requestUtils'; getSpotWsKeyForTopic,
wsKeyInverse,
import WsStore from './util/WsStore'; wsKeyLinearPrivate,
wsKeyLinearPublic,
wsKeySpotPrivate,
wsKeySpotPublic,
WsConnectionStateEnum,
} from './util';
const inverseEndpoints = { const inverseEndpoints = {
livenet: 'wss://stream.bybit.com/realtime', livenet: 'wss://stream.bybit.com/realtime',
@@ -48,169 +66,6 @@ const spotEndpoints = {
const loggerCategory = { category: 'bybit-ws' }; const loggerCategory = { category: 'bybit-ws' };
const READY_STATE_INITIAL = 0;
const READY_STATE_CONNECTING = 1;
const READY_STATE_CONNECTED = 2;
const READY_STATE_CLOSING = 3;
const READY_STATE_RECONNECTING = 4;
export enum WsConnectionState {
READY_STATE_INITIAL,
READY_STATE_CONNECTING,
READY_STATE_CONNECTED,
READY_STATE_CLOSING,
READY_STATE_RECONNECTING,
}
export type APIMarket = 'inverse' | 'linear' | 'spot';
// Same as inverse futures
export type WsPublicInverseTopic =
| 'orderBookL2_25'
| 'orderBookL2_200'
| 'trade'
| 'insurance'
| 'instrument_info'
| 'klineV2';
export type WsPublicUSDTPerpTopic =
| 'orderBookL2_25'
| 'orderBookL2_200'
| 'trade'
| 'insurance'
| 'instrument_info'
| 'kline';
export type WsPublicSpotV1Topic =
| 'trade'
| 'realtimes'
| 'kline'
| 'depth'
| 'mergedDepth'
| 'diffDepth';
export type WsPublicSpotV2Topic =
| 'depth'
| 'kline'
| 'trade'
| 'bookTicker'
| 'realtimes';
export type WsPublicTopics =
| WsPublicInverseTopic
| WsPublicUSDTPerpTopic
| WsPublicSpotV1Topic
| WsPublicSpotV2Topic
| string;
// Same as inverse futures
export type WsPrivateInverseTopic =
| 'position'
| 'execution'
| 'order'
| 'stop_order';
export type WsPrivateUSDTPerpTopic =
| 'position'
| 'execution'
| 'order'
| 'stop_order'
| 'wallet';
export type WsPrivateSpotTopic =
| 'outboundAccountInfo'
| 'executionReport'
| 'ticketInfo';
export type WsPrivateTopic =
| WsPrivateInverseTopic
| WsPrivateUSDTPerpTopic
| WsPrivateSpotTopic
| string;
export type WsTopic = WsPublicTopics | WsPrivateTopic;
export interface WSClientConfigurableOptions {
key?: string;
secret?: string;
livenet?: boolean;
// defaults to inverse.
/**
* @deprecated Use the property { market: 'linear' } instead
*/
linear?: boolean;
market?: APIMarket;
pongTimeout?: number;
pingInterval?: number;
reconnectTimeout?: number;
restOptions?: RestClientOptions;
requestOptions?: any;
wsUrl?: string;
/** If true, fetch server time before trying to authenticate (disabled by default) */
fetchTimeOffsetBeforeAuth?: boolean;
}
export interface WebsocketClientOptions extends WSClientConfigurableOptions {
livenet: boolean;
/**
* @deprecated Use the property { market: 'linear' } instead
*/
linear?: boolean;
market?: APIMarket;
pongTimeout: number;
pingInterval: number;
reconnectTimeout: number;
}
export const wsKeyInverse = 'inverse';
export const wsKeyLinearPrivate = 'linearPrivate';
export const wsKeyLinearPublic = 'linearPublic';
export const wsKeySpotPrivate = 'spotPrivate';
export const wsKeySpotPublic = 'spotPublic';
// This is used to differentiate between each of the available websocket streams (as bybit has multiple websockets)
export type WsKey =
| 'inverse'
| 'linearPrivate'
| 'linearPublic'
| 'spotPrivate'
| 'spotPublic';
const getLinearWsKeyForTopic = (topic: string): WsKey => {
const privateLinearTopics = [
'position',
'execution',
'order',
'stop_order',
'wallet',
];
if (privateLinearTopics.includes(topic)) {
return wsKeyLinearPrivate;
}
return wsKeyLinearPublic;
};
const getSpotWsKeyForTopic = (topic: string): WsKey => {
const privateLinearTopics = [
'position',
'execution',
'order',
'stop_order',
'outboundAccountInfo',
'executionReport',
'ticketInfo',
];
if (privateLinearTopics.includes(topic)) {
return wsKeySpotPrivate;
}
return wsKeySpotPublic;
};
export declare interface WebsocketClient { export declare interface WebsocketClient {
on( on(
event: 'open' | 'reconnected', event: 'open' | 'reconnected',
@@ -223,16 +78,10 @@ export declare interface WebsocketClient {
on(event: 'reconnect' | 'close', listener: ({ wsKey: WsKey }) => void): this; on(event: 'reconnect' | 'close', listener: ({ wsKey: WsKey }) => void): this;
} }
function resolveMarket(options: WSClientConfigurableOptions): APIMarket {
if (options.linear) {
return 'linear';
}
return 'inverse';
}
export class WebsocketClient extends EventEmitter { export class WebsocketClient extends EventEmitter {
private logger: typeof DefaultLogger; private logger: typeof DefaultLogger;
private restClient: InverseClient | LinearClient | SpotClient; /** Purely used */
private restClient: RESTClient;
private options: WebsocketClientOptions; private options: WebsocketClientOptions;
private wsStore: WsStore; private wsStore: WsStore;
@@ -254,11 +103,15 @@ export class WebsocketClient extends EventEmitter {
...options, ...options,
}; };
if (!this.options.market) { if (this.isV3()) {
this.options.market = resolveMarket(this.options); this.restClient = new SpotClientV3(
} undefined,
undefined,
if (this.isLinear()) { this.isLivenet(),
this.options.restOptions,
this.options.requestOptions
);
} else if (this.isLinear()) {
this.restClient = new LinearClient( this.restClient = new LinearClient(
undefined, undefined,
undefined, undefined,
@@ -299,7 +152,12 @@ export class WebsocketClient extends EventEmitter {
} }
public isInverse(): boolean { public isInverse(): boolean {
return !this.isLinear() && !this.isSpot(); return this.options.market === 'inverse';
}
/** USDC, spot v3, unified margin, account asset */
public isV3(): boolean {
return this.options.market === 'v3';
} }
/** /**
@@ -314,14 +172,22 @@ export class WebsocketClient extends EventEmitter {
// attempt to send subscription topic per websocket // attempt to send subscription topic per websocket
this.wsStore.getKeys().forEach((wsKey: WsKey) => { this.wsStore.getKeys().forEach((wsKey: WsKey) => {
// if connected, send subscription request // if connected, send subscription request
if (this.wsStore.isConnectionState(wsKey, READY_STATE_CONNECTED)) { if (
this.wsStore.isConnectionState(wsKey, WsConnectionStateEnum.CONNECTED)
) {
return this.requestSubscribeTopics(wsKey, topics); return this.requestSubscribeTopics(wsKey, topics);
} }
// start connection process if it hasn't yet begun. Topics are automatically subscribed to on-connect // start connection process if it hasn't yet begun. Topics are automatically subscribed to on-connect
if ( if (
!this.wsStore.isConnectionState(wsKey, READY_STATE_CONNECTING) && !this.wsStore.isConnectionState(
!this.wsStore.isConnectionState(wsKey, READY_STATE_RECONNECTING) wsKey,
WsConnectionStateEnum.CONNECTING
) &&
!this.wsStore.isConnectionState(
wsKey,
WsConnectionStateEnum.RECONNECTING
)
) { ) {
return this.connect(wsKey); return this.connect(wsKey);
} }
@@ -339,7 +205,9 @@ export class WebsocketClient extends EventEmitter {
this.wsStore.getKeys().forEach((wsKey: WsKey) => { this.wsStore.getKeys().forEach((wsKey: WsKey) => {
// unsubscribe request only necessary if active connection exists // unsubscribe request only necessary if active connection exists
if (this.wsStore.isConnectionState(wsKey, READY_STATE_CONNECTED)) { if (
this.wsStore.isConnectionState(wsKey, WsConnectionStateEnum.CONNECTED)
) {
this.requestUnsubscribeTopics(wsKey, topics); this.requestUnsubscribeTopics(wsKey, topics);
} }
}); });
@@ -347,14 +215,14 @@ export class WebsocketClient extends EventEmitter {
public close(wsKey: WsKey) { public close(wsKey: WsKey) {
this.logger.info('Closing connection', { ...loggerCategory, wsKey }); this.logger.info('Closing connection', { ...loggerCategory, wsKey });
this.setWsState(wsKey, READY_STATE_CLOSING); this.setWsState(wsKey, WsConnectionStateEnum.CLOSING);
this.clearTimers(wsKey); this.clearTimers(wsKey);
this.getWs(wsKey)?.close(); this.getWs(wsKey)?.close();
} }
/** /**
* Request connection of all dependent websockets, instead of waiting for automatic connection by library * Request connection of all dependent (public & private) websockets, instead of waiting for automatic connection by library
*/ */
public connectAll(): Promise<WebSocket | undefined>[] | undefined { public connectAll(): Promise<WebSocket | undefined>[] | undefined {
if (this.isInverse()) { if (this.isInverse()) {
@@ -411,7 +279,9 @@ export class WebsocketClient extends EventEmitter {
return this.wsStore.getWs(wsKey); return this.wsStore.getWs(wsKey);
} }
if (this.wsStore.isConnectionState(wsKey, READY_STATE_CONNECTING)) { if (
this.wsStore.isConnectionState(wsKey, WsConnectionStateEnum.CONNECTING)
) {
this.logger.error( this.logger.error(
'Refused to connect to ws, connection attempt already active', 'Refused to connect to ws, connection attempt already active',
{ ...loggerCategory, wsKey } { ...loggerCategory, wsKey }
@@ -421,9 +291,9 @@ export class WebsocketClient extends EventEmitter {
if ( if (
!this.wsStore.getConnectionState(wsKey) || !this.wsStore.getConnectionState(wsKey) ||
this.wsStore.isConnectionState(wsKey, READY_STATE_INITIAL) this.wsStore.isConnectionState(wsKey, WsConnectionStateEnum.INITIAL)
) { ) {
this.setWsState(wsKey, READY_STATE_CONNECTING); this.setWsState(wsKey, WsConnectionStateEnum.CONNECTING);
} }
const authParams = await this.getAuthParams(wsKey); const authParams = await this.getAuthParams(wsKey);
@@ -481,19 +351,23 @@ export class WebsocketClient extends EventEmitter {
? await this.restClient.fetchTimeOffset() ? await this.restClient.fetchTimeOffset()
: 0; : 0;
const params: any = { const signatureExpires = Date.now() + timeOffset + 5000;
api_key: this.options.key,
expires: Date.now() + timeOffset + 5000,
};
params.signature = await signMessage( const signature = await signMessage(
'GET/realtime' + params.expires, 'GET/realtime' + signatureExpires,
secret secret
); );
return '?' + serializeParams(params);
const authParams = {
api_key: this.options.key,
expires: signatureExpires,
signature,
};
return '?' + serializeParams(authParams);
} else if (!key || !secret) { } else if (!key || !secret) {
this.logger.warning( this.logger.warning(
'Connot authenticate websocket, either api or private keys missing.', 'Cannot authenticate websocket, either api or private keys missing.',
{ ...loggerCategory, wsKey } { ...loggerCategory, wsKey }
); );
} else { } else {
@@ -508,8 +382,11 @@ export class WebsocketClient extends EventEmitter {
private reconnectWithDelay(wsKey: WsKey, connectionDelayMs: number) { private reconnectWithDelay(wsKey: WsKey, connectionDelayMs: number) {
this.clearTimers(wsKey); this.clearTimers(wsKey);
if (this.wsStore.getConnectionState(wsKey) !== READY_STATE_CONNECTING) { if (
this.setWsState(wsKey, READY_STATE_RECONNECTING); this.wsStore.getConnectionState(wsKey) !==
WsConnectionStateEnum.CONNECTING
) {
this.setWsState(wsKey, WsConnectionStateEnum.RECONNECTING);
} }
setTimeout(() => { setTimeout(() => {
@@ -635,7 +512,9 @@ export class WebsocketClient extends EventEmitter {
} }
private onWsOpen(event, wsKey: WsKey) { private onWsOpen(event, wsKey: WsKey) {
if (this.wsStore.isConnectionState(wsKey, READY_STATE_CONNECTING)) { if (
this.wsStore.isConnectionState(wsKey, WsConnectionStateEnum.CONNECTING)
) {
this.logger.info('Websocket connected', { this.logger.info('Websocket connected', {
...loggerCategory, ...loggerCategory,
wsKey, wsKey,
@@ -645,13 +524,13 @@ export class WebsocketClient extends EventEmitter {
}); });
this.emit('open', { wsKey, event }); this.emit('open', { wsKey, event });
} else if ( } else if (
this.wsStore.isConnectionState(wsKey, READY_STATE_RECONNECTING) this.wsStore.isConnectionState(wsKey, WsConnectionStateEnum.RECONNECTING)
) { ) {
this.logger.info('Websocket reconnected', { ...loggerCategory, wsKey }); this.logger.info('Websocket reconnected', { ...loggerCategory, wsKey });
this.emit('reconnected', { wsKey, event }); this.emit('reconnected', { wsKey, event });
} }
this.setWsState(wsKey, READY_STATE_CONNECTED); this.setWsState(wsKey, WsConnectionStateEnum.CONNECTED);
// TODO: persistence not working yet for spot topics // TODO: persistence not working yet for spot topics
if (wsKey !== 'spotPublic' && wsKey !== 'spotPrivate') { if (wsKey !== 'spotPublic' && wsKey !== 'spotPrivate') {
@@ -670,18 +549,20 @@ export class WebsocketClient extends EventEmitter {
this.clearPongTimer(wsKey); this.clearPongTimer(wsKey);
const msg = JSON.parse((event && event.data) || event); const msg = JSON.parse((event && event.data) || event);
if ('success' in msg || msg?.pong) { if (msg['success'] || msg?.pong) {
this.onWsMessageResponse(msg, wsKey); return this.onWsMessageResponse(msg, wsKey);
} else if (msg.topic) { }
this.onWsMessageUpdate(msg);
} else { if (msg.topic) {
return this.emit('update', msg);
}
this.logger.warning('Got unhandled ws message', { this.logger.warning('Got unhandled ws message', {
...loggerCategory, ...loggerCategory,
message: msg, message: msg,
event, event,
wsKey, wsKey,
}); });
}
} catch (e) { } catch (e) {
this.logger.error('Failed to parse ws event message', { this.logger.error('Failed to parse ws event message', {
...loggerCategory, ...loggerCategory,
@@ -694,7 +575,9 @@ export class WebsocketClient extends EventEmitter {
private onWsError(error: any, wsKey: WsKey) { private onWsError(error: any, wsKey: WsKey) {
this.parseWsError('Websocket error', error, wsKey); this.parseWsError('Websocket error', error, wsKey);
if (this.wsStore.isConnectionState(wsKey, READY_STATE_CONNECTED)) { if (
this.wsStore.isConnectionState(wsKey, WsConnectionStateEnum.CONNECTED)
) {
this.emit('error', error); this.emit('error', error);
} }
} }
@@ -705,11 +588,13 @@ export class WebsocketClient extends EventEmitter {
wsKey, wsKey,
}); });
if (this.wsStore.getConnectionState(wsKey) !== READY_STATE_CLOSING) { if (
this.wsStore.getConnectionState(wsKey) !== WsConnectionStateEnum.CLOSING
) {
this.reconnectWithDelay(wsKey, this.options.reconnectTimeout!); this.reconnectWithDelay(wsKey, this.options.reconnectTimeout!);
this.emit('reconnect', { wsKey }); this.emit('reconnect', { wsKey });
} else { } else {
this.setWsState(wsKey, READY_STATE_INITIAL); this.setWsState(wsKey, WsConnectionStateEnum.INITIAL);
this.emit('close', { wsKey }); this.emit('close', { wsKey });
} }
} }
@@ -722,15 +607,11 @@ export class WebsocketClient extends EventEmitter {
} }
} }
private onWsMessageUpdate(message: any) {
this.emit('update', message);
}
private getWs(wsKey: string) { private getWs(wsKey: string) {
return this.wsStore.getWs(wsKey); return this.wsStore.getWs(wsKey);
} }
private setWsState(wsKey: WsKey, state: WsConnectionState) { private setWsState(wsKey: WsKey, state: WsConnectionStateEnum) {
this.wsStore.setConnectionState(wsKey, state); this.wsStore.setConnectionState(wsKey, state);
} }
@@ -740,7 +621,7 @@ export class WebsocketClient extends EventEmitter {
} }
const networkKey = this.isLivenet() ? 'livenet' : 'testnet'; const networkKey = this.isLivenet() ? 'livenet' : 'testnet';
// TODO: reptitive // TODO: repetitive
if (this.isLinear() || wsKey.startsWith('linear')) { if (this.isLinear() || wsKey.startsWith('linear')) {
if (wsKey === wsKeyLinearPublic) { if (wsKey === wsKeyLinearPublic) {
return linearEndpoints.public[networkKey]; return linearEndpoints.public[networkKey];