UNPKG

@serptech/api

Version:

Library for work with SERP API

184 lines (146 loc) 4.32 kB
import EventEmitter from "events"; import debounce from "lodash.debounce"; interface EventEmitterInterface { on: (event: string | symbol, listener: (...args: any[]) => void) => any; emit: (event: string | symbol, ...args: any[]) => any; } interface SocketClientInterface { send(msg: string): void; onopen(event: any): void; onmessage(event: any): void; close(): void; onclose(data?: any): void; terminate(): void; } interface SerpWsApiV1SettingsInterface { token?: string; version?: number | string; endpoint?: string; apiEndpoints: { v1: string }; SocketClient: new (endpoint: string) => SocketClientInterface; } export interface SerpWsApiV1Interface extends EventEmitterInterface { setToken(token: string): void; setEndpoint(endpoint: string): void; connect(): void; disconnect(): void; terminate(): void; } class SerpWsApiV1 extends EventEmitter implements SerpWsApiV1Interface { private SocketClient: new (endpoint: string) => SocketClientInterface; socket?: SocketClientInterface; private isSocketOpen = false; private token?: string; private endpoint: string; private reconnectTimeout?: ReturnType<typeof setTimeout>; private pingTimeout?: ReturnType<typeof setTimeout>; private pingBackOffDelay = 1000; private reconnectTime = 30000; constructor({ token, SocketClient, apiEndpoints, endpoint, }: SerpWsApiV1SettingsInterface) { super(); this.token = token; this.endpoint = endpoint || apiEndpoints.v1; this.SocketClient = SocketClient; } private heartbeat(): void { if (!this.socket) return; if (this.reconnectTimeout) { clearTimeout(this.reconnectTimeout); } if (this.pingTimeout) { clearTimeout(this.pingTimeout); } this.socket.send(JSON.stringify({ action: "PING" })); /** * Если ответа нет в течение this.pingBackOffDelay * шлём еще один запрос и так до достижения this.reconnectTime */ this.pingTimeout = setTimeout(() => { this.heartbeat(); this.pingBackOffDelay = this.pingBackOffDelay * 2; }, this.pingBackOffDelay); this.reconnectTimeout = setTimeout(() => { this.disconnect(); this.connect(); }, this.reconnectTime); } private debouncedHeartbeat(): () => void { return debounce(this.heartbeat, 5000); } private resetPing(): void { if (this.reconnectTimeout) { clearTimeout(this.reconnectTimeout); } if (this.pingTimeout) { clearTimeout(this.pingTimeout); } this.pingBackOffDelay = 1000; } setToken(token: string): void { this.token = token; if (this.isSocketOpen) { this.disconnect(); } } setEndpoint(endpoint: string): void { this.endpoint = endpoint; if (this.isSocketOpen) { this.disconnect(); } } connect(): void { this.socket = new this.SocketClient(this.endpoint); this.socket.onopen = (): void => { if (!this.socket) return; this.isSocketOpen = true; this.resetPing(); this.socket.send( JSON.stringify({ action: "AUTH", data: { token: this.token, }, }) ); this.socket.onmessage = ({ data } = { data: "" }): void => { try { const parsedData = JSON.parse(data); if (parsedData.auth === "ok") { this.debouncedHeartbeat(); return; } if (parsedData.PING) { this.resetPing(); this.debouncedHeartbeat(); return; } this.emit("message", parsedData); } catch (error) { this.emit("error", error); } }; this.socket.onclose = (data): void => { this.resetPing(); this.emit("disconnect", data); this.isSocketOpen = false; }; this.emit("connect"); }; } disconnect(): void { if (this.socket) { this.socket.close(); } } terminate(): void { if (this.socket) { this.socket.terminate(); } } } export { SerpWsApiV1 };