UNPKG

@renegade-fi/core

Version:
114 lines (107 loc) 4.34 kB
import type { RenegadeConfig } from "../createConfig.js"; import { type AuthType, RelayerWebsocket, type RelayerWebsocketParams } from "./websocket.js"; export type WebsocketWaiterParams = { config: RenegadeConfig; topic: string; authType: AuthType; messageHandler: (message: any) => any | undefined; prefetch?: () => Promise<any | undefined>; timeout?: number; }; /** * A lightweight method which resolves when a short-lived websocket connection is closed. * * The method will open the websocket connection, subscribe to the given topic * (sending a subscription message in the format expected by the relayer), * and close it when the message handler returns a value, resolving the promise with the value. * * The message handler should return undefined *only* if the message is not relevant to the waiter. * If the message satisfies the waiter's criteria but no return value is needed, the handler * should return null. * * The message handler is also responsible for parsing the message, to give it control over e.g. how * bigint values are parsed. * * Additionally, the method accepts an async `prefetch` function which can be used ahead of the websocket * connection being opened to fetch a value which will be returned immediately if it is not undefined. * * If the timeout is reached, the promise will reject. * * Because this method is intended for short-lived websocket connections, it does not support reconnecting to the server. * If the connection is closed, the method will throw an error. */ export async function websocketWaiter<T>(params: WebsocketWaiterParams): Promise<T> { return new Promise((resolve, reject) => { let promiseSettled = false; const wsParams: RelayerWebsocketParams = { config: params.config, topic: params.topic, authType: params.authType, onmessage: function (this: WebSocket, event: MessageEvent) { try { const result = params.messageHandler(event.data); if (result !== undefined) { promiseSettled = true; this.close(); resolve(result); } } catch (error) { promiseSettled = true; this.close(); reject(error); } }, oncloseCallback: () => { if (!promiseSettled) { promiseSettled = true; reject(new Error("Websocket connection closed")); } }, onerrorCallback: (error: Event | Error) => { if (!promiseSettled) { promiseSettled = true; reject(error); } }, }; const ws = new RelayerWebsocket(wsParams); ws.connect().catch((error) => reject(error)); if (params.timeout) { setTimeout(() => { if (!promiseSettled) { promiseSettled = true; ws.close(); reject(new Error("Websocket connection timed out")); } }, params.timeout); } if (params.prefetch) { params .prefetch() .then((result) => { if (result) { promiseSettled = true; try { ws.close(); } catch (_) { // If the WS is not open, this will throw an error. // This is fine, so we can ignore it. } resolve(result); } }) .catch((error) => { if (!promiseSettled) { promiseSettled = true; try { ws.close(); } catch (_) { // If the WS is not open, this will throw an error. // This is fine, so we can ignore it. } reject(error); } }); } }); }