UNPKG

masto

Version:

Mastodon API client for JavaScript, TypeScript, Node.js, browsers

82 lines (81 loc) 2.63 kB
import WebSocket from "isomorphic-ws"; import { createPromiseWithResolvers, ExponentialBackoff, } from "../../utils/index.js"; import { MastoWebSocketError } from "../errors/index.js"; import { waitForClose, waitForOpen } from "./wait-for-events.js"; export class WebSocketConnectorImpl { props; logger; ws; killed = false; queue = []; backoff; constructor(props, logger) { this.props = props; this.logger = logger; this.backoff = new ExponentialBackoff({ maxAttempts: this.props.maxAttempts, }); this.spawn(); } acquire() { if (this.killed) { throw new MastoWebSocketError("WebSocket closed"); } if (this.ws) { return Promise.resolve(this.ws); } const promiseWithResolvers = createPromiseWithResolvers(); this.queue.push(promiseWithResolvers); return promiseWithResolvers.promise; } async *[Symbol.asyncIterator]() { while (!this.killed) { yield await this.acquire(); } } kill() { this.killed = true; this.ws?.close(); this.backoff.clear(); for (const { reject } of this.queue) { reject(new MastoWebSocketError("WebSocket closed")); } this.queue = []; } async spawn() { while (!this.killed) { try { await this.backoff.sleep(); } catch { break; } try { this.logger?.log("info", "Connecting to WebSocket..."); { const ctor = (this.props.implementation ?? WebSocket); const ws = new ctor(...this.props.constructorParameters); await waitForOpen(ws); this.ws = ws; } this.logger?.log("info", "Connected to WebSocket"); for (const { resolve } of this.queue) { resolve(this.ws); } this.queue = []; await waitForClose(this.ws); this.logger?.log("info", "WebSocket closed"); this.backoff.clear(); } catch (error) { this.logger?.log("error", "WebSocket error:", error); } this.ws = undefined; } for (const { reject } of this.queue) { reject(new MastoWebSocketError(`Failed to connect to WebSocket after ${this.props.maxAttempts} attempts`)); } this.queue = []; } }