import { Transport } from '@hawk.so/core'; import { CatcherMessage } from '../types'; import { CatcherMessageType } from '@hawk.so/types'; /** * Custom WebSocket wrapper class * * @copyright CodeX */ export default class Socket implements Transport { /** * Socket connection endpoint */ private readonly url; /** * External handler for socket message */ private readonly onMessage; /** * External handler for socket opening */ private readonly onOpen; /** * External handler for socket close */ private readonly onClose; /** * Queue of events collected while socket is not connected. * They will be sent once the connection is established. */ private eventsQueue; /** * Websocket instance */ private ws; /** * Page hide event handler reference (for removal) */ private pageHideHandler; /** * Timer that closes an idle connection after no errors have been sent * for connectionIdleMs milliseconds. */ private connectionIdleTimer; /** * How long (ms) to keep the connection open after the last error was sent. * Errors often come in bursts, so holding the socket briefly avoids * the overhead of opening a new connection for each one. */ private readonly connectionIdleMs; /** * Deduplicates concurrent openConnection() calls — all callers share the * same in-flight Promise so only one WebSocket is ever created at a time. */ private readonly initOnce; /** * Creates new Socket instance. Setup initial socket params. * * @param options — constructor options for catcher initialization */ constructor({ collectorEndpoint, onMessage, onClose, onOpen, connectionIdleMs, }: { collectorEndpoint: any; onMessage?: ((_message: MessageEvent) => void) | undefined; onClose?: (() => void) | undefined; onOpen?: (() => void) | undefined; connectionIdleMs?: number | undefined; }); /** * Send an event to the Collector * * @param message - event data in Hawk Format */ send(message: CatcherMessage): Promise; /** * Setup window event listeners */ private setupListeners; /** * Remove window event listeners */ private destroyListeners; /** * Create new WebSocket connection and setup socket event listeners. * Always call initOnce() instead — it deduplicates concurrent calls. */ private openConnection; /** * Closes socket connection and cancels any pending idle timer */ private close; /** * Closes the WebSocket and nulls all event handlers before releasing the reference. * Without this, the old connection stays open and its onclose/onerror * handlers keep firing, causing duplicate reconnect attempts and log noise. */ private closeAndDetachSocket; /** * Resets the idle close timer. * Called after each successful send so the connection stays open * for connectionIdleMs after the last error in a burst. */ private resetIdleTimer; /** * Sends all queued events directly via the WebSocket. * Bypasses send() intentionally — send() always enqueues first, * so calling it here would cause infinite recursion. */ private sendQueue; }