/** * wsServer.ts — a minimal RFC 6455 WebSocket server, hand-rolled. * * The package ships three runtime dependencies and adding `ws` for one loopback * socket is not worth it, so this implements exactly the slice the bridge * needs: the upgrade handshake, text frames, ping/pong and close. Binary frames * are accepted and dropped; no extensions or subprotocols are negotiated. * * It binds the loopback interface only — never 0.0.0.0 — and refuses any * upgrade carrying a browser Origin, which is the first half of the defence * against cross-site WebSocket hijacking (the token check in bridge.ts is the * other half, because a non-browser client can forge any header it likes). */ import { type IncomingMessage } from 'http'; export interface WsConnection { id: number; send(text: string): void; close(code?: number, reason?: string): void; readonly closed: boolean; /** Per-connection state the owner attaches (auth status, timers). */ meta: Record; } export interface WsServerHandlers { onConnection(conn: WsConnection, req: IncomingMessage): void; onMessage(conn: WsConnection, text: string): void; onClose(conn: WsConnection): void; } /** * An Origin header proves a browser made the request. The nwjs game page loads * from file:// and sends either no Origin or "null"/"file://", so any http(s) * origin is a web page reaching for us and is refused outright. */ export declare function originAllowed(origin: string | undefined): boolean; export declare function acceptKey(key: string): string; /** Encode one server-to-client frame (never masked, per the RFC). */ export declare function encodeFrame(payload: Buffer, opcode?: number): Buffer; export interface DecodedFrame { fin: boolean; opcode: number; payload: Buffer; /** Total bytes consumed from the input buffer. */ size: number; } /** * Try to decode one frame off the front of `buf`. Returns null when the buffer * does not yet hold a complete frame, so the caller keeps accumulating. Throws * when the frame is malformed or oversized — the caller must then close. */ export declare function decodeFrame(buf: Buffer): DecodedFrame | null; export interface WsServer { readonly port: number; close(): Promise; connections(): WsConnection[]; } /** * Start a loopback WebSocket server. Resolves once it is accepting * connections; rejects if the port cannot be bound. */ export declare function startWsServer(port: number, handlers: WsServerHandlers): Promise;