@drift-labs/common
Version:
Common functions for Drift
96 lines (95 loc) • 4.05 kB
TypeScript
/// <reference types="ws" />
import { Subject, Subscription } from 'rxjs';
import WebSocket from 'isomorphic-ws';
type WebSocketMessage<T = Record<string, unknown>> = T;
type WebSocketSubscriptionProps<T = Record<string, unknown>> = {
wsUrl: string;
subscriptionId: string;
subscribeMessage: string;
unsubscribeMessage: string;
onError: (err?: any) => void;
onMessage: (message: WebSocketMessage<T>) => void;
messageFilter: (message: WebSocketMessage<T>) => boolean;
errorMessageFilter: (message: WebSocketMessage<T>) => boolean;
onClose?: () => void;
enableHeartbeatMonitoring?: boolean;
};
type WebSocketSubscriptionState<T = Record<string, unknown>> = WebSocketSubscriptionProps<T> & {
hasSentSubscribeMessage?: boolean;
subjectSubscription?: Subscription;
};
type WebSocketUrl = string;
type SubscriptionId = string;
declare enum WebSocketConnectionState {
CONNECTING = 0,
CONNECTED = 1,
DISCONNECTING = 2,
DISCONNECTED = 3
}
type IMultiplexWebSocket<T = Record<string, unknown>> = {
wsUrl: WebSocketUrl;
webSocket: WebSocket;
customConnectionState: WebSocketConnectionState;
subject: Subject<WebSocketMessage<T>>;
subscriptions: Map<SubscriptionId, Omit<WebSocketSubscriptionProps<T>, 'wsUrl' | 'subscriptionId'>>;
};
/**
* MultiplexWebSocket allows for multiple subscriptions to a single websocket of the same URL, improving efficiency and reducing the number of open connections.
*
* This implementation assumes the following:
* - All websocket streams are treated equally - reconnection attempts are performed at the same standards
* - All messages returned are in the `WebSocketMessage` format
* - A single instance of the websocket manager is created for each websocket URL - this means all subscriptions to the same websocket URL will share the same websocket instance
*
* Internal implementation details:
* - The websocket is closed when the number of subscriptions is 0
* - The websocket will be refreshed (new instance) when it disconnects unexpectedly or errors, until it reaches the maximum number of reconnect attempts
*/
export declare class MultiplexWebSocket<T = Record<string, unknown>> implements IMultiplexWebSocket<T> {
#private;
/**
* A lookup of all websockets by their URL.
*/
private static URL_TO_WEBSOCKETS_LOOKUP;
/**
* A lookup from websocket URL to all subscription IDs for that URL.
*/
private static URL_TO_SUBSCRIPTION_IDS_LOOKUP;
wsUrl: WebSocketUrl;
customConnectionState: WebSocketConnectionState;
subject: Subject<WebSocketMessage<T>>;
subscriptions: Map<SubscriptionId, Omit<WebSocketSubscriptionState<T>, 'wsUrl' | 'subscriptionId'>>;
private reconnectionManager;
private closeTimeout;
private heartbeatTimeout;
private heartbeatMonitoringEnabled;
private constructor();
/**
* Creates a new virtual websocket subscription. If an existing websocket for the given URL exists, the subscription will be added to the existing websocket.
* Returns a function that can be called to unsubscribe from the subscription.
*/
static createWebSocketSubscription<T = Record<string, unknown>>(props: WebSocketSubscriptionProps<T>): {
unsubscribe: () => void;
};
private static handleNewSubForNewWsUrl;
private static handleNewSubForExistingWsUrl;
get webSocket(): WebSocket;
/**
* Setting the WebSocket instance will automatically add event handlers to the WebSocket instance.
* When the WebSocket is connected, all existing subscriptions will be subscribed to.
*/
set webSocket(webSocket: WebSocket);
private subscribeToWebSocket;
private subscribe;
private unsubscribe;
private scheduleDelayedClose;
private cancelDelayedClose;
private close;
private startHeartbeatMonitoring;
private stopHeartbeatMonitoring;
private resetHeartbeatTimeout;
private isHeartbeatMessage;
private handleHeartbeat;
private refreshWebSocket;
}
export {};