/** A ponyfill/polyfill for the WHATWG [`WebSocketStream`][spec] API. * * `WebSocketStream` exposes a WebSocket as a pair of streams — * `{ readable, writable }` — with backpressure on the writable. It currently * ships only in Chromium. This package wraps a plain `WebSocket` to present the * same surface (`opened`, `closed`, `close()`), applying write backpressure by * polling `bufferedAmount` against a configurable high-water mark. * * Note: a ponyfill can only *approximate* backpressure via `bufferedAmount`; * the native API observes the real send buffer. When the native API is present, * prefer it via {@link openWebSocketStream}. * * [spec]: https://github.com/ricea/websocketstream-explainer */ /** The data yielded by the readable / accepted by the writable. */ export type WebSocketStreamData = Uint8Array | string; /** Resolved value of {@link WebSocketStreamLike.opened}. */ export interface WebSocketStreamOpenEvent { readable: ReadableStream; writable: WritableStream; extensions: string; protocol: string; } /** Resolved value of {@link WebSocketStreamLike.closed}. */ export interface WebSocketStreamCloseEvent { closeCode: number; reason: string; } /** Argument to {@link WebSocketStreamLike.close}. */ export interface WebSocketStreamCloseInfo { closeCode?: number; reason?: string; } /** The common shape implemented by both the native API and this ponyfill. */ export interface WebSocketStreamLike { readonly url: string; readonly opened: Promise; readonly closed: Promise; close(closeInfo?: WebSocketStreamCloseInfo): void; /** Ponyfill-only: resize the write-backpressure high-water mark at runtime * (see {@link WebSocketStream.setHighWaterMark}). Absent on the native API, * which sizes its own send buffer — callers should treat it as optional. */ setHighWaterMark?(bytes: number): void; } /** The slice of the `WebSocket` API this ponyfill relies on. Both the browser * `WebSocket` and Node's `ws` satisfy it. */ export interface WebSocketLike { binaryType: string; /** Absent on some server-side implementations; only used to report * {@link WebSocketStreamLike.url} for an adopted socket. */ readonly url?: string; readonly bufferedAmount: number; readonly readyState: number; readonly protocol: string; readonly extensions: string; send(data: string | ArrayBufferLike | ArrayBufferView): void; close(code?: number, reason?: string): void; onopen: ((ev: unknown) => void) | null; onmessage: ((ev: { data: unknown; }) => void) | null; onerror: ((ev: unknown) => void) | null; onclose: ((ev: { code?: number; reason?: string; }) => void) | null; } /** Constructor for a {@link WebSocketLike} (e.g. the global `WebSocket` or `ws`). */ export type WebSocketConstructor = new (url: string, protocols?: string | string[]) => WebSocketLike; export interface WebSocketStreamOptions { /** Subprotocols to advertise via `Sec-WebSocket-Protocol`. */ protocols?: string[]; /** Abort the connection. */ signal?: AbortSignal; /** Ponyfill-only: the `WebSocket` implementation to use. Defaults to * `globalThis.WebSocket`. Pass Node's `ws` when there is no global. */ webSocket?: WebSocketConstructor; /** Ponyfill-only: write backpressure kicks in once `bufferedAmount` exceeds * this many bytes. Defaults to 64 KiB. */ highWaterMark?: number; } /** A `WebSocketStream` implemented over a plain `WebSocket`. */ export declare class WebSocketStream implements WebSocketStreamLike { #private; readonly url: string; readonly opened: Promise; readonly closed: Promise; /** Dial `url`, or wrap a socket you already have (see {@link WebSocketStream.adopt}). */ constructor(source: string | WebSocketLike, options?: WebSocketStreamOptions); /** Wrap a socket that already exists — typically one a server accepted from an * HTTP upgrade (`Deno.upgradeWebSocket`, `ws`, ...), where there is no URL to * dial and the handshake (including subprotocol selection) is already done. * * Ownership transfers: this overwrites the socket's `onopen`/`onmessage`/ * `onerror`/`onclose` handlers, so adopt it before anything else reads from * it — messages delivered before adoption are dropped by the platform, not * buffered. A socket that is already open resolves {@link opened} without * waiting for an `onopen` that has already fired (or was never going to). * * `options.protocols` and `options.webSocket` are ignored; the socket exists. */ static adopt(ws: WebSocketLike, options?: Pick): WebSocketStream; close(closeInfo?: WebSocketStreamCloseInfo): void; /** The current write-backpressure high-water mark, in bytes. */ get highWaterMark(): number; /** Resize the write-backpressure high-water mark at runtime (bytes). * * Set this to roughly the bandwidth-delay product (RTT × estimated * throughput): large enough to keep the socket busy, small enough that * queued bytes can still be reprioritized rather than committed to the OS * send buffer. Takes effect immediately, including for an in-progress drain. * Clamped to a minimum of 1 byte. */ setHighWaterMark(bytes: number): void; } /** Open a `WebSocketStream`, preferring the native API when available and * falling back to the {@link WebSocketStream} ponyfill otherwise. Passing * `options.webSocket` forces the ponyfill (the native API can't use an injected * socket). */ export declare function openWebSocketStream(url: string | URL, options?: WebSocketStreamOptions): WebSocketStreamLike; /** Install {@link WebSocketStream} as the global `WebSocketStream` if the * platform doesn't ship one. Returns `true` if installed, `false` if a native * (or previously installed) implementation already existed. */ export declare function install(): boolean; export default WebSocketStream;