import WebSocket from 'ws'; import type { Server, IncomingMessage, ClientRequest } from 'node:http'; interface WsListenerMap { 'close': (this: WebSocket, code: number, reason: Buffer) => void; 'error': (this: WebSocket, error: Error) => void; 'upgrade': (this: WebSocket, request: IncomingMessage) => void; 'message': (this: WebSocket, data: WebSocket.RawData, isBinary: boolean) => void; 'open': (this: WebSocket) => void; 'ping': (this: WebSocket, data: Buffer) => void; 'pong': (this: WebSocket, data: Buffer) => void; 'redirect': (this: WebSocket, url: string, request: ClientRequest) => void; 'unexpected-response': (this: WebSocket, request: ClientRequest, response: IncomingMessage) => void; } type WsAddListenerMap = { [K in keyof WsListenerMap]: OmitThisParameter; }; export interface InjectWSOptions { /** Extra request headers. `host` defaults to 'localhost'. */ headers?: Record; } /** * Thenable builder returned by `injectWS`. Listener registrations are queued * pre-connect and replayed onto the real `ws.WebSocket` BEFORE the synthetic * upgrade is emitted, closing the race window RFC 6455 permits between the * 101 response and the first server-sent frame. * * Awaiting the chain (or calling `.connect()`) triggers the upgrade and * resolves with the connected `WebSocket`. Idempotent — re-awaiting or * re-calling `.connect()` returns the same `Promise`. * * Listener-attachment methods mirror `ws.WebSocket`'s typed event overloads, * so consumers get the same listener-argument typing they'd get on the live * socket. */ export interface WebSocketChain extends PromiseLike { on(event: K, listener: WsListenerMap[K]): this; on(event: string | symbol, listener: (this: WebSocket, ...args: any[]) => void): this; once(event: K, listener: WsListenerMap[K]): this; once(event: string | symbol, listener: (this: WebSocket, ...args: any[]) => void): this; addListener(event: K, listener: WsAddListenerMap[K]): this; addListener(event: string | symbol, listener: (...args: any[]) => void): this; prependListener(event: string | symbol, listener: (...args: any[]) => void): this; prependOnceListener(event: string | symbol, listener: (...args: any[]) => void): this; /** Trigger the synthetic upgrade. Idempotent. */ connect(): Promise; /** * Race-safe async iterable over incoming messages. Queues 'message' / * 'close' / 'error' listeners on the chain (same pre-handshake guarantee * as `.on(...)`), terminates on 'close', throws on 'error'. Iteration * itself triggers `.connect()` if it hasn't been called already. * * Message data type is `Buffer` — accurate for the default 'nodebuffer' * binaryType the chain configures. * * With a `transform` argument: each chunk is mapped through `transform` * before yielding. Useful for per-frame decode (e.g. CBOR, JSON, * protobuf) so consumers can write `for await (const frame of * chain.toIterable(decodeFrame))` instead of wrapping with an outer * async generator. */ toIterable(): AsyncIterable; toIterable(transform: (chunk: Buffer) => T | Promise): AsyncIterable; then(onFulfilled?: ((value: WebSocket) => TResult1 | PromiseLike) | null, onRejected?: ((reason: unknown) => TResult2 | PromiseLike) | null): Promise; catch(onRejected?: ((reason: unknown) => TResult | PromiseLike) | null): Promise; finally(onFinally?: (() => void) | null): Promise; } export declare class WebSocketChainImpl implements WebSocketChain { #private; constructor(server: Server, url: string, options: InjectWSOptions); on(event: string | symbol, listener: (...args: any[]) => void): this; once(event: string | symbol, listener: (...args: any[]) => void): this; addListener(event: string | symbol, listener: (...args: any[]) => void): this; prependListener(event: string | symbol, listener: (...args: any[]) => void): this; prependOnceListener(event: string | symbol, listener: (...args: any[]) => void): this; connect(): Promise; toIterable(transform?: (chunk: Buffer) => T | Promise): AsyncIterable; then(onFulfilled?: ((value: WebSocket) => TResult1 | PromiseLike) | null, onRejected?: ((reason: unknown) => TResult2 | PromiseLike) | null): Promise; catch(onRejected?: ((reason: unknown) => TResult | PromiseLike) | null): Promise; finally(onFinally?: (() => void) | null): Promise; } export {};