import memd from 'memd'; import { WebSocketProvider } from './WsTransportIsomorphic' import { TTransport } from './ITransport'; import { MessageBasedTransport } from './MessageBasedTransport'; import { RpcSubscription } from '../RpcSubscription'; export class WsTransport implements TTransport.Transport { private ws: WsTransportSingleton; public id: string constructor(private options: TTransport.Options.Ws) { this.id = this.options.url; this.ws = WsTransportSingleton.create(this.options.url, {}); } async request (req: TTransport.Request) async request (req: TTransport.Request[]) async request (req: TTransport.Request | TTransport.Request[]) { return this.ws.request(req); } async subscribe (req: TTransport.Request): Promise> { return this.ws.subscribe(req); } async unsubscribe (req: TTransport.Request & { method: 'eth_unsubscribe', params: [number] }) { return this.ws.unsubscribe(req); } } class WsTransportSingleton extends MessageBasedTransport { @memd.deco.memoize() public static create (url: string, options) { return new WsTransportSingleton({ url, ...options }); } constructor(private options: TTransport.Options.Ws) { super(options); } protected async send(message: string) { let ws = await this.connect(); ws.send(message); } @memd.deco.memoize({ perInstance: true }) private async connect() { const { url, clientConfig } = this.options const WebSocket = await WebSocketProvider.get(); const ws = new WebSocket(url, clientConfig ?? {}); const onMessage = ({ data }) => { this.onMessage(data); this.emit('message', data); }; const onClose = () => { //wsCache.delete(url); memd.fn.clearMemoized(this.connect, this.options.url); ws.removeEventListener('close', onClose) ws.removeEventListener('open', onConnect) ws.removeEventListener('message', onMessage) this.emit('close'); }; const onConnect = () => { this.emit('connect'); } ws.addEventListener('close', onClose); ws.addEventListener('message', onMessage); ws.addEventListener('open', onConnect); if (ws.readyState === WebSocket.CONNECTING) { await new Promise((resolve, reject) => { ws.onopen = resolve ws.onerror = reject }) } return ws; } }