@orkestrel/websocket 0.0.10 → 0.0.12

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -1 +1 @@
1
- {"version":3,"file":"index.cjs","names":["#emitter","#socket","#masked","#payload","#timeout","#signal","#dataListener","#closeListener","#errorListener","#abortListener","#handleData","#finish","#handleError","#readyState","#ingest","#write","#code","#reason","#encodeClose","#closeTimer","#destroyed","#detach","#buffer","#fail","#dispatch","#close","#messageOpcode","#fragments","#fragmentBytes","#decodeClose","#detached","#drain","#bytes"],"sources":["../../../src/server/constants.ts","../../../src/server/helpers.ts","../../../src/server/NodeWebSocket.ts","../../../src/server/factories.ts"],"sourcesContent":["import type { WebSocketReadyState } from './types.js'\n\n// The WebSocket wrapper's wire constants (AGENTS §5 constants file) — the RFC 6455\n// magic values the codec and the handshake are built on: the accept GUID, the\n// supported protocol version, the frame opcodes, the four ready states, and the\n// normal-closure status code. Every member is exported; the codec helpers and the\n// `NodeWebSocket` wrapper read them by name rather than re-spelling the bit values.\n\n/**\n * The RFC 6455 GUID concatenated to a client's `Sec-WebSocket-Key` before the SHA-1\n * hash that yields the `Sec-WebSocket-Accept` response value.\n *\n * @remarks\n * A fixed, spec-mandated constant (RFC 6455 §4.2.2) — read only by\n * {@link computeWebSocketAccept}.\n */\nexport const WEBSOCKET_GUID = '258EAFA5-E914-47DA-95CA-C5AB0DC85B11'\n\n/** The WebSocket protocol version this wrapper speaks (`Sec-WebSocket-Version: 13`). */\nexport const WEBSOCKET_VERSION = '13'\n\n/** Text frame opcode — a UTF-8 payload (RFC 6455 §5.6). */\nexport const WEBSOCKET_OPCODE_TEXT = 0x01\n\n/** Binary frame opcode — a raw byte payload (RFC 6455 §5.6). */\nexport const WEBSOCKET_OPCODE_BINARY = 0x02\n\n/** Continuation frame opcode — the next fragment of an open data message (RFC 6455 §5.4). */\nexport const WEBSOCKET_OPCODE_CONTINUATION = 0x00\n\n/** Close frame opcode — a control frame ending the connection (RFC 6455 §5.5.1). */\nexport const WEBSOCKET_OPCODE_CLOSE = 0x08\n\n/** Ping frame opcode — a control frame the peer must answer with a pong (RFC 6455 §5.5.2). */\nexport const WEBSOCKET_OPCODE_PING = 0x09\n\n/** Pong frame opcode — a control frame answering a ping (RFC 6455 §5.5.3). */\nexport const WEBSOCKET_OPCODE_PONG = 0x0a\n\n/** Ready state for a connecting WebSocket (before the handshake completes). */\nexport const WEBSOCKET_READY_CONNECTING: WebSocketReadyState = 0\n\n/** Ready state for an open WebSocket (the handshake completed; frames flow). */\nexport const WEBSOCKET_READY_OPEN: WebSocketReadyState = 1\n\n/** Ready state for a closing WebSocket (a close frame was sent or received). */\nexport const WEBSOCKET_READY_CLOSING: WebSocketReadyState = 2\n\n/** Ready state for a closed WebSocket (the socket ended). */\nexport const WEBSOCKET_READY_CLOSED: WebSocketReadyState = 3\n\n/** Normal-closure status code (RFC 6455 §7.4.1) — the default `close` code. */\nexport const WEBSOCKET_CLOSE_NORMAL = 1000\n\n/** Protocol-error status code (RFC 6455 §7.4.1) — a framing/state rule was violated. */\nexport const WEBSOCKET_CLOSE_PROTOCOL = 1002\n\n/** Unsupported-data status code (RFC 6455 §7.4.1) — the endpoint received a data type it cannot accept (e.g. binary on a text-only endpoint). */\nexport const WEBSOCKET_CLOSE_UNSUPPORTED = 1003\n\n/** Invalid-frame-payload-data status code (RFC 6455 §7.4.1) — e.g. non-UTF-8 text or an unparseable close reason. */\nexport const WEBSOCKET_CLOSE_INVALID = 1007\n\n/** Message-too-big status code (RFC 6455 §7.4.1) — a reassembled message exceeded the payload cap. */\nexport const WEBSOCKET_CLOSE_TOOBIG = 1009\n\n/** The default maximum inbound single-frame length AND reassembled-message total byte count (100 MiB — the `ws` package default). */\nexport const WEBSOCKET_MAX_PAYLOAD = 104_857_600\n\n/** The default close-handshake timeout in milliseconds — how long `close()` waits for the peer's echo before tearing the socket down. */\nexport const WEBSOCKET_CLOSE_TIMEOUT_MS = 30_000\n\n/** The post-`#fail` flush grace in milliseconds — how long a validation-breach close frame is given to flush through the socket's write buffer before the hard `destroy()` fallback fires (the normal path destroys sooner, on the `end()` flush callback). */\nexport const WEBSOCKET_FAIL_TIMEOUT_MS = 1_000\n\n/** The maximum control-frame payload length in bytes (RFC 6455 §5.5). */\nexport const WEBSOCKET_CONTROL_MAXLEN = 125\n\n/** The maximum UTF-8 close-reason length after the two-byte status code. */\nexport const WEBSOCKET_CLOSE_REASON_MAXLEN = WEBSOCKET_CONTROL_MAXLEN - 2\n","import type { WebSocketEncodeOptions, WebSocketFrame } from './types.js'\nimport { createHash, randomBytes } from 'node:crypto'\nimport { WEBSOCKET_GUID } from './constants.js'\n\n// The RFC 6455 codec and boundary guards — pure, exported, and exhaustively tested.\n// `computeWebSocketAccept` derives the handshake token; the `isWebSocket*` guards\n// validate upgrade/header invariants; `measureWebSocketFrame` and\n// `parseWebSocketFrame` decode the next frame incrementally; `encodeWebSocketFrame`\n// builds the inverse wire representation.\n//\n/**\n * Compute the `Sec-WebSocket-Accept` response value for an RFC 6455 upgrade.\n *\n * @remarks\n * The base64-encoded SHA-1 of the client's `Sec-WebSocket-Key` concatenated with the\n * fixed {@link WEBSOCKET_GUID} (RFC 6455 §4.2.2) — the proof the server understood the\n * handshake. Pure and deterministic.\n *\n * @param key - The client's `Sec-WebSocket-Key` header value\n * @returns The base64 accept token to send back as `Sec-WebSocket-Accept`\n */\nexport function computeWebSocketAccept(key: string): string {\n\treturn createHash('sha1')\n\t\t.update(key + WEBSOCKET_GUID)\n\t\t.digest('base64')\n}\n\n/**\n * Whether a value is a canonical RFC 6455 `Sec-WebSocket-Key`.\n *\n * @remarks\n * A valid key is exactly 16 random bytes encoded as 24 characters of base64, ending\n * in `==` (RFC 6455 §4.1). This predicate is suitable at an HTTP upgrade boundary:\n * malformed or non-canonical encodings return `false`; nothing is thrown.\n *\n * @param key - The proposed `Sec-WebSocket-Key` header value\n * @returns `true` when `key` is the canonical base64 encoding of 16 bytes\n *\n * @example\n * ```ts\n * const key = request.headers['sec-websocket-key']\n * if (typeof key !== 'string' || !isWebSocketKey(key)) socket.destroy()\n * ```\n */\nexport function isWebSocketKey(key: string): boolean {\n\tif (!/^[A-Za-z0-9+/]{22}==$/.test(key)) return false\n\treturn Buffer.from(key, 'base64').length === 16\n}\n\n/**\n * Whether a value is one valid WebSocket subprotocol token.\n *\n * @remarks\n * Subprotocols use the HTTP `token` grammar. Whitespace, separators, commas, and\n * control characters are rejected, preventing an untrusted value from injecting a\n * second handshake header.\n *\n * @param protocol - The negotiated subprotocol to validate\n * @returns `true` when `protocol` is one non-empty HTTP token\n *\n * @example\n * ```ts\n * if (!isWebSocketProtocol(protocol)) throw new RangeError('invalid protocol')\n * ```\n */\nexport function isWebSocketProtocol(protocol: string): boolean {\n\treturn /^[!#$%&'*+\\-.^_`|~0-9A-Za-z]+$/.test(protocol)\n}\n\n/**\n * Decode a single RFC 6455 frame from the front of a buffer.\n *\n * @remarks\n * Reads the FIN bit and opcode (byte 0), the mask bit and 7-bit payload length (byte\n * 1) — extended to a 16-bit length when the 7-bit field is `126`, or a 64-bit length\n * when it is `127` — the optional 4-byte mask key, then the payload, XOR-unmasking it\n * against the key when the mask bit is set (client→server frames MUST be masked, RFC\n * 6455 §5.3; an unmasked frame still decodes, leaving the payload as-is, so the caller\n * can enforce policy). Returns `undefined` the moment the buffer is too short for the\n * part it is up to (the length prefix, the mask, or the full payload) — the signal to\n * the caller to read more bytes and retry, exactly like {@link SSEParser} on a partial\n * line. `consumed` is the total bytes the frame occupied, so the caller slices the\n * remainder. Pure; never throws on a short buffer.\n *\n * @param buffer - The accumulation buffer to decode the next frame from\n * @returns The parsed {@link WebSocketFrame}, or `undefined` when the buffer is incomplete\n */\nexport function parseWebSocketFrame(buffer: Buffer): WebSocketFrame | undefined {\n\tif (buffer.length < 2) return undefined\n\n\tconst firstByte = buffer.readUInt8(0)\n\tconst secondByte = buffer.readUInt8(1)\n\n\tconst fin = (firstByte & 0x80) !== 0\n\tconst rsv = (firstByte & 0x70) >> 4\n\tconst opcode = firstByte & 0x0f\n\tconst masked = (secondByte & 0x80) !== 0\n\tlet length = secondByte & 0x7f\n\tlet offset = 2\n\n\tif (length === 126) {\n\t\tif (buffer.length < offset + 2) return undefined\n\t\tlength = buffer.readUInt16BE(offset)\n\t\toffset += 2\n\t} else if (length === 127) {\n\t\tif (buffer.length < offset + 8) return undefined\n\t\t// Split into two 32-bit reads — a payload past 2^53 is beyond any real frame,\n\t\t// and this keeps the arithmetic in safe-integer range.\n\t\tconst high = buffer.readUInt32BE(offset)\n\t\tconst low = buffer.readUInt32BE(offset + 4)\n\t\tlength = high * 0x1_0000_0000 + low\n\t\toffset += 8\n\t}\n\n\tlet mask: Buffer | undefined\n\tif (masked) {\n\t\tif (buffer.length < offset + 4) return undefined\n\t\tmask = buffer.subarray(offset, offset + 4)\n\t\toffset += 4\n\t}\n\n\tif (buffer.length < offset + length) return undefined\n\n\tconst payload = Buffer.alloc(length)\n\tbuffer.copy(payload, 0, offset, offset + length)\n\n\tif (mask !== undefined) {\n\t\tfor (let index = 0; index < length; index += 1) {\n\t\t\tpayload[index] = payload.readUInt8(index) ^ mask.readUInt8(index % 4)\n\t\t}\n\t}\n\n\treturn { fin, opcode, payload, consumed: offset + length, masked, rsv }\n}\n\n/**\n * Read the declared payload length off the front of a buffer, without buffering or\n * reading the payload itself.\n *\n * @remarks\n * Decodes only byte 1's 7-bit length field, extended by the 16-bit (`126`) or 64-bit\n * (`127`) form exactly like {@link parseWebSocketFrame} — but stops there, so a caller\n * can reject an over-cap frame the moment its length is known, before the payload\n * bytes have even arrived. Returns `undefined` until the length field itself is fully\n * buffered (mirrors the parser's incomplete-buffer contract). Pure; never throws.\n *\n * @param buffer - The accumulation buffer to read the next frame's length from\n * @returns The declared payload length, or `undefined` when the buffer is too short to know it yet\n *\n * @example\n * ```ts\n * const declared = measureWebSocketFrame(buffer)\n * if (declared !== undefined && declared > limit) fail(WEBSOCKET_CLOSE_TOOBIG)\n * ```\n */\nexport function measureWebSocketFrame(buffer: Buffer): number | undefined {\n\tif (buffer.length < 2) return undefined\n\n\tconst secondByte = buffer.readUInt8(1)\n\tlet length = secondByte & 0x7f\n\tconst offset = 2\n\n\tif (length === 126) {\n\t\tif (buffer.length < offset + 2) return undefined\n\t\tlength = buffer.readUInt16BE(offset)\n\t} else if (length === 127) {\n\t\tif (buffer.length < offset + 8) return undefined\n\t\tconst high = buffer.readUInt32BE(offset)\n\t\tconst low = buffer.readUInt32BE(offset + 4)\n\t\tlength = high * 0x1_0000_0000 + low\n\t}\n\n\treturn length\n}\n\n/**\n * Whether the next frame uses the shortest valid RFC 6455 payload-length encoding.\n *\n * @remarks\n * Returns `undefined` until the complete length prefix is buffered. The 16-bit form\n * is canonical only for lengths at least 126; the 64-bit form only for lengths at\n * least 65,536 and with its most-significant bit clear (RFC 6455 §5.2).\n *\n * @param buffer - The accumulation buffer containing the next frame header\n * @returns Its canonicality, or `undefined` while the length prefix is incomplete\n *\n * @example\n * ```ts\n * if (isWebSocketFrameCanonical(buffer) === false) fail(WEBSOCKET_CLOSE_PROTOCOL)\n * ```\n */\nexport function isWebSocketFrameCanonical(buffer: Buffer): boolean | undefined {\n\tif (buffer.length < 2) return undefined\n\n\tconst lengthCode = buffer.readUInt8(1) & 0x7f\n\tif (lengthCode < 126) return true\n\tif (lengthCode === 126) {\n\t\tif (buffer.length < 4) return undefined\n\t\treturn buffer.readUInt16BE(2) >= 126\n\t}\n\tif (buffer.length < 10) return undefined\n\tconst high = buffer.readUInt32BE(2)\n\tconst low = buffer.readUInt32BE(6)\n\tif ((high & 0x8000_0000) !== 0) return false\n\treturn high > 0 || low >= 65_536\n}\n\n/**\n * Decode a byte sequence as strict UTF-8, or signal it is malformed.\n *\n * @remarks\n * Wraps `TextDecoder('utf-8', { fatal: true })` in a try/catch so a malformed sequence\n * returns `undefined` instead of throwing (AGENTS §14 — a guard-adjacent coercer never\n * throws on bad input). Pure.\n *\n * @param bytes - The raw bytes to decode\n * @returns The decoded string, or `undefined` when `bytes` is not valid UTF-8\n *\n * @example\n * ```ts\n * const text = parseUTF8(payload)\n * if (text === undefined) fail(WEBSOCKET_CLOSE_INVALID)\n * ```\n */\nexport function parseUTF8(bytes: Buffer): string | undefined {\n\ttry {\n\t\treturn new TextDecoder('utf-8', { fatal: true }).decode(bytes)\n\t} catch {\n\t\treturn undefined\n\t}\n}\n\n/**\n * Whether a numeric value is a valid RFC 6455 close status code to RECEIVE (§7.4.1).\n *\n * @remarks\n * True for `1000`–`1003`, `1007`–`1014`, and the application range `3000`–`4999`; false\n * for anything below `1000`, the reserved-for-local-use-only codes `1004`–`1006` and\n * `1015`, and the unassigned `1016`–`2999` range. The `1012`–`1014` extension of the\n * strict RFC 6455 receivable set is a deliberate IANA-interop choice: those three codes\n * (Service Restart, Try Again Later, Bad Gateway) are IANA-registered in the WebSocket\n * Close Code Number Registry and accepted by the `ws` ecosystem and modern conformance\n * suites, so a peer sending one is not treated as a protocol violation. Pure predicate,\n * never throws.\n *\n * @param code - The close status code to validate\n * @returns `true` when `code` is a valid RFC 6455 close code\n *\n * @example\n * ```ts\n * if (!isCloseCode(code)) fail(WEBSOCKET_CLOSE_PROTOCOL)\n * ```\n */\nexport function isCloseCode(code: number): boolean {\n\tif (!Number.isInteger(code)) return false\n\tif (code >= 1000 && code <= 1003) return true\n\tif (code >= 1007 && code <= 1014) return true\n\tif (code >= 3000 && code <= 4999) return true\n\treturn false\n}\n\n/**\n * Encode a single RFC 6455 frame to its wire bytes — the inverse of\n * {@link parseWebSocketFrame}.\n *\n * @remarks\n * Builds a final (FIN-set) frame: byte 0 is `0x80 | opcode`; the payload length uses\n * the 7-bit form below 126, the `126` + 16-bit form below 65 536, or the `127` +\n * 64-bit form beyond; when `masked` is set the mask bit is set, a 4-byte key (supplied\n * via `options.mask`, else random) is written, and the payload is XOR-masked. Server→\n * client frames are unmasked (the default); pass `masked: true` to encode a CLIENT\n * frame (e.g. to feed the parser in a test). A `string` payload is encoded as UTF-8.\n * Returns one contiguous `Buffer` (header + payload), so the wrapper writes it with a\n * single `socket.write`. Pure.\n *\n * @param opcode - The frame opcode (a `WEBSOCKET_OPCODE_*` value)\n * @param payload - The payload, a `Buffer` or a UTF-8 `string`\n * @param options - Masking control ({@link WebSocketEncodeOptions}); defaults to unmasked\n * @returns The complete frame as wire bytes\n */\nexport function encodeWebSocketFrame(\n\topcode: number,\n\tpayload: Buffer | string,\n\toptions?: WebSocketEncodeOptions,\n): Buffer {\n\tif (!Number.isInteger(opcode) || opcode < 0 || opcode > 0x0f) {\n\t\tthrow new RangeError('opcode must be an integer between 0 and 15')\n\t}\n\tif (options?.mask !== undefined && options.mask.length !== 4) {\n\t\tthrow new RangeError('mask must contain exactly 4 bytes')\n\t}\n\tif (options?.mask !== undefined && options.masked !== true) {\n\t\tthrow new RangeError('mask requires masked: true')\n\t}\n\tconst body = typeof payload === 'string' ? Buffer.from(payload, 'utf-8') : payload\n\tconst length = body.length\n\tconst masked = options?.masked === true\n\tconst mask = masked ? (options?.mask ?? randomBytes(4)) : undefined\n\tconst maskBit = masked ? 0x80 : 0\n\n\t// The header size: 2 base bytes + the extended-length bytes (0 / 2 / 8) + the mask\n\t// key (0 / 4). The length prefix and the mask key write into this header.\n\tconst extended = length < 126 ? 0 : length < 65_536 ? 2 : 8\n\tconst header = Buffer.alloc(2 + extended + (mask !== undefined ? 4 : 0))\n\theader[0] = 0x80 | opcode\n\n\tif (length < 126) {\n\t\theader[1] = maskBit | length\n\t} else if (length < 65_536) {\n\t\theader[1] = maskBit | 126\n\t\theader.writeUInt16BE(length, 2)\n\t} else {\n\t\theader[1] = maskBit | 127\n\t\theader.writeUInt32BE(Math.floor(length / 0x1_0000_0000), 2)\n\t\theader.writeUInt32BE(length % 0x1_0000_0000, 6)\n\t}\n\n\tif (mask === undefined) return Buffer.concat([header, body])\n\n\tmask.copy(header, header.length - 4)\n\tconst maskedBody = Buffer.alloc(length)\n\tfor (let index = 0; index < length; index += 1) {\n\t\tmaskedBody[index] = body.readUInt8(index) ^ mask.readUInt8(index % 4)\n\t}\n\treturn Buffer.concat([header, maskedBody])\n}\n","import type { Duplex } from 'node:stream'\nimport type {\n\tNodeWebSocketEventMap,\n\tNodeWebSocketInterface,\n\tNodeWebSocketOptions,\n\tWebSocketReadyState,\n} from './types.js'\nimport type { EmitterInterface } from '@orkestrel/emitter'\nimport { Emitter } from '@orkestrel/emitter'\nimport {\n\tcomputeWebSocketAccept,\n\tencodeWebSocketFrame,\n\tisCloseCode,\n\tisWebSocketFrameCanonical,\n\tisWebSocketKey,\n\tisWebSocketProtocol,\n\tmeasureWebSocketFrame,\n\tparseUTF8,\n\tparseWebSocketFrame,\n} from './helpers.js'\nimport {\n\tWEBSOCKET_CLOSE_INVALID,\n\tWEBSOCKET_CLOSE_NORMAL,\n\tWEBSOCKET_CLOSE_PROTOCOL,\n\tWEBSOCKET_CLOSE_REASON_MAXLEN,\n\tWEBSOCKET_CLOSE_TIMEOUT_MS,\n\tWEBSOCKET_CLOSE_TOOBIG,\n\tWEBSOCKET_CLOSE_UNSUPPORTED,\n\tWEBSOCKET_CONTROL_MAXLEN,\n\tWEBSOCKET_FAIL_TIMEOUT_MS,\n\tWEBSOCKET_MAX_PAYLOAD,\n\tWEBSOCKET_OPCODE_BINARY,\n\tWEBSOCKET_OPCODE_CLOSE,\n\tWEBSOCKET_OPCODE_CONTINUATION,\n\tWEBSOCKET_OPCODE_PING,\n\tWEBSOCKET_OPCODE_PONG,\n\tWEBSOCKET_OPCODE_TEXT,\n\tWEBSOCKET_READY_CLOSED,\n\tWEBSOCKET_READY_CLOSING,\n\tWEBSOCKET_READY_CONNECTING,\n\tWEBSOCKET_READY_OPEN,\n} from './constants.js'\n\n/**\n * A server-native WebSocket over a raw upgraded `node:stream` Duplex — the lean\n * wrapper around the RFC 6455 wire protocol.\n *\n * @remarks\n * Created by `createNodeWebSocket`. When given a client `key` it runs in SERVER mode —\n * it writes the `101 Switching Protocols` handshake (`computeWebSocketAccept(key)`) and\n * emits `open`; given no key it runs in CLIENT mode (no handshake, frames masked). It\n * then listens on the socket's `data`, accumulating bytes in `#buffer` and decoding\n * every complete frame with {@link parseWebSocketFrame} (slicing `consumed` and\n * re-parsing the remainder): a TEXT frame — reassembling continuation fragments across\n * `fin: false` frames — decodes to UTF-8 and emits `message`; a PING is auto-answered\n * with a PONG and emits `ping`; a PONG emits `pong`; a CLOSE is echoed and ends the\n * socket, emitting `close`. `send` writes a text frame, `ping` a ping, `close` a close\n * frame; `destroy` tears down immediately. It owns a typed `#emitter` (AGENTS §13) that\n * isolates a throwing listener and routes the error to its own `error` handler (the `error`\n * option) — the socket never crashes. An underlying socket error emits the domain\n * `error` event and terminates the wrapper. The untyped socket `data` is narrowed to a\n * `Buffer` with a guard, never an assertion (AGENTS §14).\n */\nexport class NodeWebSocket implements NodeWebSocketInterface {\n\treadonly #emitter: Emitter<NodeWebSocketEventMap>\n\treadonly #socket: Duplex\n\treadonly #masked: boolean\n\treadonly #payload: number\n\treadonly #timeout: number\n\treadonly #signal: AbortSignal | undefined\n\treadonly #dataListener: (chunk: unknown) => void\n\treadonly #closeListener: () => void\n\treadonly #errorListener: (error: unknown) => void\n\treadonly #abortListener: () => void\n\t#buffer: Buffer = Buffer.alloc(0)\n\t#readyState: WebSocketReadyState = WEBSOCKET_READY_CONNECTING\n\t#code: number | undefined\n\t#reason: string | undefined\n\t#fragments: Buffer[] = []\n\t#messageOpcode: number | undefined\n\t#fragmentBytes = 0\n\t#closeTimer: ReturnType<typeof setTimeout> | undefined\n\t#destroyed = false\n\t#detached = false\n\n\tconstructor(options: NodeWebSocketOptions) {\n\t\tconst payload = options.payload ?? WEBSOCKET_MAX_PAYLOAD\n\t\tif (!Number.isSafeInteger(payload) || payload < 0) {\n\t\t\tthrow new RangeError('payload must be a non-negative safe integer')\n\t\t}\n\t\tconst timeout = options.timeout ?? WEBSOCKET_CLOSE_TIMEOUT_MS\n\t\tif (!Number.isSafeInteger(timeout) || timeout < 0) {\n\t\t\tthrow new RangeError('timeout must be a non-negative safe integer')\n\t\t}\n\t\tif (options.key !== undefined && !isWebSocketKey(options.key)) {\n\t\t\tthrow new RangeError('key must be the canonical base64 encoding of 16 bytes')\n\t\t}\n\t\tif (options.protocol !== undefined && !isWebSocketProtocol(options.protocol)) {\n\t\t\tthrow new RangeError('protocol must be a valid WebSocket subprotocol token')\n\t\t}\n\t\tif (options.protocol !== undefined && options.key === undefined) {\n\t\t\tthrow new RangeError('protocol requires a server key')\n\t\t}\n\n\t\tthis.#emitter = new Emitter({\n\t\t\t...(options.on === undefined ? {} : { on: options.on }),\n\t\t\t...(options.error === undefined ? {} : { error: options.error }),\n\t\t})\n\t\tthis.#socket = options.socket\n\t\t// Server mode is identified by a client key (it writes the handshake + sends\n\t\t// unmasked frames); without one this is a client (no handshake, masked frames).\n\t\tthis.#masked = options.key === undefined\n\t\tthis.#payload = payload\n\t\tthis.#timeout = timeout\n\t\tthis.#signal = options.signal\n\t\t// Retain each bound listener so terminal paths detach only this wrapper's callbacks.\n\t\tthis.#dataListener = this.#handleData.bind(this)\n\t\tthis.#closeListener = this.#finish.bind(this)\n\t\tthis.#errorListener = this.#handleError.bind(this)\n\t\tthis.#abortListener = this.destroy.bind(this)\n\n\t\tif (options.key !== undefined) {\n\t\t\tconst headers = [\n\t\t\t\t'HTTP/1.1 101 Switching Protocols',\n\t\t\t\t'Upgrade: websocket',\n\t\t\t\t'Connection: Upgrade',\n\t\t\t\t`Sec-WebSocket-Accept: ${computeWebSocketAccept(options.key)}`,\n\t\t\t]\n\t\t\tif (options.protocol !== undefined) {\n\t\t\t\theaders.push(`Sec-WebSocket-Protocol: ${options.protocol}`)\n\t\t\t}\n\t\t\tthis.#socket.write(`${headers.join('\\r\\n')}\\r\\n\\r\\n`)\n\t\t}\n\n\t\tthis.#readyState = WEBSOCKET_READY_OPEN\n\t\tthis.#socket.on('data', this.#dataListener)\n\t\tthis.#socket.on('close', this.#closeListener)\n\t\tthis.#socket.on('error', this.#errorListener)\n\t\tthis.#emitter.emit('open')\n\n\t\t// Replay any bytes buffered after the upgrade headers through the same ingest path\n\t\t// as `#handleData`, so the pre-buffer cap check applies uniformly (AGENTS §5 dedup).\n\t\tconst head = options.head\n\t\tif (head !== undefined && head.length > 0) {\n\t\t\tthis.#ingest(head)\n\t\t}\n\n\t\t// The external cancellation seam (composes with `@orkestrel/abort` /\n\t\t// `@orkestrel/timeout`'s native AbortSignals) — wired last so an already-aborted\n\t\t// signal tears the socket down only after the rest of construction has run. The\n\t\t// head-replay above can itself synchronously terminate the socket (a complete\n\t\t// CLOSE frame or an RFC violation routes through `#fail`/`#close` -> `#finish`),\n\t\t// which flushes the close frame GRACEFULLY via `#socket.end()`. In that case skip\n\t\t// the seam entirely: forcing `destroy()` would discard that flushing frame (the\n\t\t// loss `#fail` is engineered to avoid), and there is no live socket to attach to.\n\t\tif (this.#readyState !== WEBSOCKET_READY_CLOSED) {\n\t\t\tif (this.#signal?.aborted === true) {\n\t\t\t\tthis.destroy()\n\t\t\t} else {\n\t\t\t\tthis.#signal?.addEventListener('abort', this.#abortListener, { once: true })\n\t\t\t}\n\t\t}\n\t}\n\n\tget emitter(): EmitterInterface<NodeWebSocketEventMap> {\n\t\treturn this.#emitter\n\t}\n\n\tget readyState(): WebSocketReadyState {\n\t\treturn this.#readyState\n\t}\n\n\tsend(data: string): void {\n\t\tif (this.#readyState !== WEBSOCKET_READY_OPEN) return\n\t\tthis.#write(WEBSOCKET_OPCODE_TEXT, Buffer.from(data, 'utf-8'))\n\t}\n\n\tping(data?: string): void {\n\t\tif (this.#readyState !== WEBSOCKET_READY_OPEN) return\n\t\tif (data !== undefined && Buffer.byteLength(data, 'utf-8') > WEBSOCKET_CONTROL_MAXLEN) {\n\t\t\tthrow new RangeError('ping payload exceeds 125 bytes')\n\t\t}\n\t\tthis.#write(\n\t\t\tWEBSOCKET_OPCODE_PING,\n\t\t\tdata === undefined ? Buffer.alloc(0) : Buffer.from(data, 'utf-8'),\n\t\t)\n\t}\n\n\tclose(code?: number, reason?: string): void {\n\t\tif (\n\t\t\tthis.#readyState === WEBSOCKET_READY_CLOSING ||\n\t\t\tthis.#readyState === WEBSOCKET_READY_CLOSED\n\t\t) {\n\t\t\treturn\n\t\t}\n\t\tif (code !== undefined && !isCloseCode(code)) throw new RangeError('invalid close code')\n\t\tif (\n\t\t\treason !== undefined &&\n\t\t\tBuffer.byteLength(reason, 'utf-8') > WEBSOCKET_CLOSE_REASON_MAXLEN\n\t\t) {\n\t\t\tthrow new RangeError(`close reason exceeds ${WEBSOCKET_CLOSE_REASON_MAXLEN} bytes`)\n\t\t}\n\t\tthis.#readyState = WEBSOCKET_READY_CLOSING\n\t\tthis.#code = code ?? WEBSOCKET_CLOSE_NORMAL\n\t\tthis.#reason = reason === undefined || reason.length === 0 ? undefined : reason\n\t\tthis.#write(WEBSOCKET_OPCODE_CLOSE, this.#encodeClose(this.#code, this.#reason))\n\t\t// End the writable side after the close frame; the peer's echo (or the socket\n\t\t// `close`) drives the final state transition through `#finish`.\n\t\tthis.#socket.end()\n\t\tthis.#closeTimer = setTimeout(() => this.destroy(), this.#timeout)\n\t\tthis.#closeTimer.unref()\n\t}\n\n\tdestroy(): void {\n\t\tif (this.#destroyed) return\n\t\tthis.#destroyed = true\n\t\t// Detach before destroy so a destroy-time error reaches the terminal sink.\n\t\tthis.#detach()\n\t\tthis.#signal?.removeEventListener('abort', this.#abortListener)\n\t\t// `#finish` no-ops once already CLOSED (e.g. after `#fail` armed the hard-teardown\n\t\t// fallback), so the timer is cleared here unconditionally rather than relying on it.\n\t\tclearTimeout(this.#closeTimer)\n\t\tthis.#closeTimer = undefined\n\t\tif (!this.#socket.destroyed) this.#socket.destroy()\n\t\tthis.#finish()\n\t\tthis.#emitter.destroy()\n\t}\n\n\t// Decode every complete frame currently in the buffer, dispatching each and slicing\n\t// it off; stops when a partial frame remains (parse returns `undefined`).\n\t#drain(): void {\n\t\tfor (;;) {\n\t\t\tconst canonical = isWebSocketFrameCanonical(this.#buffer)\n\t\t\tif (canonical === false) {\n\t\t\t\tthis.#fail(WEBSOCKET_CLOSE_PROTOCOL)\n\t\t\t\treturn\n\t\t\t}\n\t\t\tconst declared = measureWebSocketFrame(this.#buffer)\n\t\t\tif (declared !== undefined && declared > this.#payload) {\n\t\t\t\tthis.#fail(WEBSOCKET_CLOSE_TOOBIG)\n\t\t\t\treturn\n\t\t\t}\n\t\t\tconst frame = parseWebSocketFrame(this.#buffer)\n\t\t\tif (frame === undefined) return\n\t\t\tthis.#buffer = this.#buffer.subarray(frame.consumed)\n\t\t\tthis.#dispatch(frame.fin, frame.opcode, frame.payload, frame.masked, frame.rsv)\n\t\t\tif (this.#readyState === WEBSOCKET_READY_CLOSED) return\n\t\t}\n\t}\n\n\t// Route one decoded frame through the RFC 6455 validation gauntlet, then the\n\t// fragmentation state machine. Any validity breach funnels through `#fail`, which\n\t// closes with the specified code and tears the socket down.\n\t#dispatch(fin: boolean, opcode: number, payload: Buffer, masked: boolean, rsv: number): void {\n\t\tif (rsv !== 0) {\n\t\t\tthis.#fail(WEBSOCKET_CLOSE_PROTOCOL)\n\t\t\treturn\n\t\t}\n\t\t// Server mode sends unmasked and requires masked input; client mode is the inverse.\n\t\tif (masked === this.#masked) {\n\t\t\tthis.#fail(WEBSOCKET_CLOSE_PROTOCOL)\n\t\t\treturn\n\t\t}\n\n\t\tif (\n\t\t\topcode === WEBSOCKET_OPCODE_CLOSE ||\n\t\t\topcode === WEBSOCKET_OPCODE_PING ||\n\t\t\topcode === WEBSOCKET_OPCODE_PONG\n\t\t) {\n\t\t\tif (!fin || payload.length > WEBSOCKET_CONTROL_MAXLEN) {\n\t\t\t\tthis.#fail(WEBSOCKET_CLOSE_PROTOCOL)\n\t\t\t\treturn\n\t\t\t}\n\t\t\tif (opcode === WEBSOCKET_OPCODE_PING) {\n\t\t\t\tthis.#write(WEBSOCKET_OPCODE_PONG, payload)\n\t\t\t\tthis.#emitter.emit('ping')\n\t\t\t\treturn\n\t\t\t}\n\t\t\tif (opcode === WEBSOCKET_OPCODE_PONG) {\n\t\t\t\tthis.#emitter.emit('pong')\n\t\t\t\treturn\n\t\t\t}\n\t\t\tthis.#close(payload)\n\t\t\treturn\n\t\t}\n\n\t\tif (opcode === WEBSOCKET_OPCODE_TEXT || opcode === WEBSOCKET_OPCODE_BINARY) {\n\t\t\tif (this.#messageOpcode !== undefined) {\n\t\t\t\tthis.#fail(WEBSOCKET_CLOSE_PROTOCOL)\n\t\t\t\treturn\n\t\t\t}\n\t\t\tthis.#messageOpcode = opcode\n\t\t} else if (opcode === WEBSOCKET_OPCODE_CONTINUATION) {\n\t\t\tif (this.#messageOpcode === undefined) {\n\t\t\t\tthis.#fail(WEBSOCKET_CLOSE_PROTOCOL)\n\t\t\t\treturn\n\t\t\t}\n\t\t} else {\n\t\t\t// Reserved data (0x3–0x7) or reserved control (0xB–0xF) opcodes.\n\t\t\tthis.#fail(WEBSOCKET_CLOSE_PROTOCOL)\n\t\t\treturn\n\t\t}\n\n\t\tthis.#fragments.push(payload)\n\t\tthis.#fragmentBytes += payload.length\n\t\tif (this.#fragmentBytes > this.#payload) {\n\t\t\tthis.#fail(WEBSOCKET_CLOSE_TOOBIG)\n\t\t\treturn\n\t\t}\n\t\tif (!fin) return\n\n\t\tif (this.#messageOpcode === WEBSOCKET_OPCODE_BINARY) {\n\t\t\tthis.#fail(WEBSOCKET_CLOSE_UNSUPPORTED)\n\t\t\treturn\n\t\t}\n\t\tconst text = parseUTF8(Buffer.concat(this.#fragments))\n\t\tif (text === undefined) {\n\t\t\tthis.#fail(WEBSOCKET_CLOSE_INVALID)\n\t\t\treturn\n\t\t}\n\t\tthis.#emitter.emit('message', text)\n\t\tthis.#messageOpcode = undefined\n\t\tthis.#fragments = []\n\t\tthis.#fragmentBytes = 0\n\t}\n\n\t// Handle a validated CLOSE frame: decode it (which itself may `#fail` on an invalid\n\t// code/reason), then — if still OPEN — echo the peer's payload verbatim and end.\n\t#close(payload: Buffer): void {\n\t\tconst valid = this.#decodeClose(payload)\n\t\tif (!valid) return\n\t\tif (this.#readyState === WEBSOCKET_READY_OPEN) {\n\t\t\t// Echo the peer's close frame before ending, per RFC 6455 §5.5.1.\n\t\t\tthis.#readyState = WEBSOCKET_READY_CLOSING\n\t\t\tthis.#write(WEBSOCKET_OPCODE_CLOSE, payload)\n\t\t}\n\t\t// The echo is queued; detach before `end()` can surface a socket error.\n\t\tthis.#detach()\n\t\tthis.#socket.end()\n\t\tthis.#finish()\n\t}\n\n\t// The single funnel for every RFC 6455 validation breach: close with `code`, `#detach`\n\t// the domain listeners (the connection is protocol-dead — RFC 6455 permits discarding\n\t// further input after sending close, and this also stops a post-fail socket `error`\n\t// emitting AFTER the terminal `close` event), write the close frame, then flush + half\n\t// -close via `end()` (never a synchronous `destroy()`, which can discard the buffered\n\t// close frame and leave the peer seeing 1006 instead of the intended code) before\n\t// finishing. The hard-teardown fallback is armed AFTER `#finish` so `#finish`'s\n\t// `clearTimeout` cannot kill it; the normal path destroys the moment the write buffer\n\t// flushes (the `end()` callback), the unref'd timer is only the malicious-peer backstop.\n\t#fail(code: number, reason?: string): void {\n\t\tif (\n\t\t\tthis.#readyState === WEBSOCKET_READY_CLOSING ||\n\t\t\tthis.#readyState === WEBSOCKET_READY_CLOSED\n\t\t) {\n\t\t\treturn\n\t\t}\n\t\tthis.#code = code\n\t\tthis.#reason = reason\n\t\tthis.#readyState = WEBSOCKET_READY_CLOSING\n\t\tthis.#detach()\n\t\tthis.#write(WEBSOCKET_OPCODE_CLOSE, this.#encodeClose(code, reason))\n\t\tthis.#socket.end(() => {\n\t\t\tif (!this.#socket.destroyed) this.#socket.destroy()\n\t\t\t// The normal flush path destroyed the socket already — clear the unref'd\n\t\t\t// fallback timer below so it doesn't linger `WEBSOCKET_FAIL_TIMEOUT_MS` holding\n\t\t\t// its closure alive for no reason.\n\t\t\tclearTimeout(this.#closeTimer)\n\t\t\tthis.#closeTimer = undefined\n\t\t})\n\t\tthis.#messageOpcode = undefined\n\t\tthis.#fragments = []\n\t\tthis.#fragmentBytes = 0\n\t\tthis.#finish()\n\t\tthis.#closeTimer = setTimeout(() => {\n\t\t\tif (!this.#socket.destroyed) this.#socket.destroy()\n\t\t}, WEBSOCKET_FAIL_TIMEOUT_MS)\n\t\tthis.#closeTimer.unref()\n\t}\n\n\t// Drop only this wrapper's domain listeners and arm one durable terminal error sink.\n\t#detach(): void {\n\t\tif (this.#detached) return\n\t\tthis.#detached = true\n\t\tthis.#socket.off('data', this.#dataListener)\n\t\tthis.#socket.off('close', this.#closeListener)\n\t\tthis.#socket.off('error', this.#errorListener)\n\t\t// Keep a terminal socket safe from late peer errors after the domain listener is gone.\n\t\tthis.#socket.on('error', () => undefined)\n\t}\n\n\t// Write one frame to the socket — masked in client mode, unmasked in server mode.\n\t// A destroyed socket silently drops the write (the lifecycle is already ending).\n\t#write(opcode: number, payload: Buffer): void {\n\t\tif (this.#socket.destroyed) return\n\t\tthis.#socket.write(encodeWebSocketFrame(opcode, payload, { masked: this.#masked }))\n\t}\n\n\t// Build a close-frame payload: the 2-byte big-endian code, then the optional UTF-8\n\t// reason. An undefined code yields an empty payload (a bare close).\n\t#encodeClose(code: number | undefined, reason: string | undefined): Buffer {\n\t\tif (code === undefined) return Buffer.alloc(0)\n\t\tconst text = reason === undefined ? Buffer.alloc(0) : Buffer.from(reason, 'utf-8')\n\t\tconst payload = Buffer.alloc(2 + text.length)\n\t\tpayload.writeUInt16BE(code, 0)\n\t\ttext.copy(payload, 2)\n\t\treturn payload\n\t}\n\n\t// Validate and read a peer close-frame payload into `#code` / `#reason` (RFC 6455\n\t// §7.4.1). A bare close (0 bytes) is valid with no code/reason. A single stray byte\n\t// is a protocol error. 2+ bytes carry a code (must be a receivable close code) and\n\t// an optional UTF-8 reason. Returns `false` when a breach routed through `#fail`\n\t// (the caller must not also echo).\n\t#decodeClose(payload: Buffer): boolean {\n\t\tif (payload.length === 0) {\n\t\t\tthis.#code = undefined\n\t\t\tthis.#reason = undefined\n\t\t\treturn true\n\t\t}\n\t\tif (payload.length === 1) {\n\t\t\tthis.#fail(WEBSOCKET_CLOSE_PROTOCOL)\n\t\t\treturn false\n\t\t}\n\t\tconst code = payload.readUInt16BE(0)\n\t\tif (!isCloseCode(code)) {\n\t\t\tthis.#fail(WEBSOCKET_CLOSE_PROTOCOL)\n\t\t\treturn false\n\t\t}\n\t\tif (payload.length === 2) {\n\t\t\tthis.#code = code\n\t\t\tthis.#reason = undefined\n\t\t\treturn true\n\t\t}\n\t\tconst reason = parseUTF8(payload.subarray(2))\n\t\tif (reason === undefined) {\n\t\t\tthis.#fail(WEBSOCKET_CLOSE_INVALID)\n\t\t\treturn false\n\t\t}\n\t\tthis.#code = code\n\t\tthis.#reason = reason.length === 0 ? undefined : reason\n\t\treturn true\n\t}\n\n\t// Transition to CLOSED once (idempotent), clear the close-handshake timer, and emit\n\t// the final `close` with the last known code/reason.\n\t#finish(): void {\n\t\tif (this.#readyState === WEBSOCKET_READY_CLOSED) return\n\t\tthis.#detach()\n\t\tclearTimeout(this.#closeTimer)\n\t\tthis.#closeTimer = undefined\n\t\tthis.#signal?.removeEventListener('abort', this.#abortListener)\n\t\tthis.#readyState = WEBSOCKET_READY_CLOSED\n\t\tthis.#emitter.emit('close', this.#code, this.#reason)\n\t}\n\n\t// Append `bytes` to the accumulation buffer, then drain every complete frame. `#drain`\n\t// preflights canonical encoding and the declared payload cap on EACH iteration, so\n\t// every coalesced frame receives the same validation. Shared by `#handleData` and head replay.\n\t#ingest(bytes: Buffer): void {\n\t\tthis.#buffer = Buffer.concat([this.#buffer, bytes])\n\t\tthis.#drain()\n\t}\n\n\t#handleData(chunk: unknown): void {\n\t\tif (this.#readyState === WEBSOCKET_READY_CLOSED) return\n\t\tconst bytes = this.#bytes(chunk)\n\t\tif (bytes === undefined) return\n\t\tthis.#ingest(bytes)\n\t}\n\n\t#handleError(error: unknown): void {\n\t\tthis.#emitter.emit('error', error)\n\t\tthis.destroy()\n\t}\n\n\t// Narrow an untyped socket `data` chunk to a `Buffer` (AGENTS §14) — a `node:net`\n\t// socket without an explicit encoding yields Buffers, but the listener parameter is\n\t// `unknown`, so it crosses through this guard, never an assertion. A non-Buffer\n\t// chunk (a string from a mis-encoded socket) is normalized; anything else is dropped.\n\t#bytes(chunk: unknown): Buffer | undefined {\n\t\tif (Buffer.isBuffer(chunk)) return chunk\n\t\tif (typeof chunk === 'string') return Buffer.from(chunk, 'utf-8')\n\t\treturn undefined\n\t}\n}\n","import type { NodeWebSocketInterface, NodeWebSocketOptions } from './types.js'\nimport { NodeWebSocket } from './NodeWebSocket.js'\n\n/**\n * Create a server-native WebSocket over a raw upgraded `node:stream` Duplex socket.\n *\n * @remarks\n * The construction entry point for the {@link NodeWebSocketInterface} (AGENTS §8). Pass\n * the upgraded `socket` plus the client's `Sec-WebSocket-Key` as `key` to run in SERVER\n * mode — the wrapper writes the `101 Switching Protocols` handshake and sends unmasked\n * frames; omit `key` for CLIENT mode (no handshake, masked frames). This is the\n * lean-native handle; it speaks only the WebSocket wire protocol — an MCP transport (the\n * later chunk) is built ON it. It is the WebSocket counterpart to\n * `createSQLiteDatabase` / `createIndexedDBDatabase`.\n *\n * @param options - The {@link NodeWebSocketOptions} (`socket`, optional `key` / `head` /\n * `protocol` / `on`)\n * @returns A typed {@link NodeWebSocketInterface}\n *\n * @example\n * ```ts\n * import { createNodeWebSocket } from '@src/server'\n *\n * // In a node:http 'upgrade' handler — server mode, identified by the client key:\n * server.on('upgrade', (request, socket, head) => {\n * \tconst ws = createNodeWebSocket({\n * \t\tsocket,\n * \t\tkey: request.headers['sec-websocket-key'],\n * \t\thead,\n * \t\ton: { message: (text) => ws.send(`echo: ${text}`) },\n * \t})\n * })\n * ```\n */\nexport function createNodeWebSocket(options: NodeWebSocketOptions): NodeWebSocketInterface {\n\treturn new NodeWebSocket(options)\n}\n"],"mappings":"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"}
1
+ {"version":3,"file":"index.cjs","names":[],"sources":["../../../src/server/constants.ts","../../../src/server/errors.ts","../../../src/server/helpers.ts","../../../src/server/parsers.ts","../../../src/server/NodeWebSocket.ts","../../../src/server/factories.ts"],"sourcesContent":["import type { WebSocketReadyState } from './types.js'\n\n// The WebSocket wrapper's wire constants — the RFC 6455\n// magic values the codec and the handshake are built on: the accept GUID, the\n// supported protocol version, the frame opcodes, the ready states, and the\n// normal-closure status code. Every member is exported; the codec helpers and the\n// `NodeWebSocket` wrapper read them by name rather than re-spelling the bit values.\n\n/**\n * Names the accept GUID concatenated to a client's `Sec-WebSocket-Key` before the accept\n * hash, '258EAFA5-E914-47DA-95CA-C5AB0DC85B11'.\n *\n * @remarks\n * The base64-encoded SHA-1 of that concatenation is the `Sec-WebSocket-Accept` response\n * value. A fixed, spec-mandated constant (RFC 6455 §4.2.2) — read only by\n * {@link computeWebSocketAccept}.\n */\nexport const WEBSOCKET_GUID = '258EAFA5-E914-47DA-95CA-C5AB0DC85B11'\n\n/**\n * Names the supported protocol version, '13'.\n *\n * @remarks\n * The value this wrapper speaks, carried by the `Sec-WebSocket-Version` handshake header.\n */\nexport const WEBSOCKET_VERSION = '13'\n\n/**\n * Names the text frame opcode, 0x01.\n *\n * @remarks\n * A UTF-8 payload (RFC 6455 §5.6).\n */\nexport const WEBSOCKET_OPCODE_TEXT = 0x01\n\n/**\n * Names the binary frame opcode, 0x02.\n *\n * @remarks\n * A raw byte payload (RFC 6455 §5.6).\n */\nexport const WEBSOCKET_OPCODE_BINARY = 0x02\n\n/**\n * Names the continuation frame opcode, 0x00.\n *\n * @remarks\n * The next fragment of an open data message (RFC 6455 §5.4).\n */\nexport const WEBSOCKET_OPCODE_CONTINUATION = 0x00\n\n/**\n * Names the close frame opcode, 0x08.\n *\n * @remarks\n * A control frame ending the connection (RFC 6455 §5.5.1).\n */\nexport const WEBSOCKET_OPCODE_CLOSE = 0x08\n\n/**\n * Names the ping frame opcode, 0x09.\n *\n * @remarks\n * A control frame the peer must answer with a pong (RFC 6455 §5.5.2).\n */\nexport const WEBSOCKET_OPCODE_PING = 0x09\n\n/**\n * Names the pong frame opcode, 0x0a.\n *\n * @remarks\n * A control frame answering a ping (RFC 6455 §5.5.3).\n */\nexport const WEBSOCKET_OPCODE_PONG = 0x0a\n\n/**\n * Names the connecting ready state, 0.\n *\n * @remarks\n * The state a WebSocket holds before its handshake completes.\n */\nexport const WEBSOCKET_READY_CONNECTING: WebSocketReadyState = 0\n\n/**\n * Names the open ready state, 1.\n *\n * @remarks\n * The state a WebSocket holds after the handshake completes and while frames flow.\n */\nexport const WEBSOCKET_READY_OPEN: WebSocketReadyState = 1\n\n/**\n * Names the closing ready state, 2.\n *\n * @remarks\n * The state a WebSocket holds after a close frame is sent or received.\n */\nexport const WEBSOCKET_READY_CLOSING: WebSocketReadyState = 2\n\n/**\n * Names the closed ready state, 3.\n *\n * @remarks\n * The state a WebSocket holds after the socket ends.\n */\nexport const WEBSOCKET_READY_CLOSED: WebSocketReadyState = 3\n\n/**\n * Names the normal-closure status code, 1000.\n *\n * @remarks\n * The default `close` code (RFC 6455 §7.4.1).\n */\nexport const WEBSOCKET_CLOSE_NORMAL = 1000\n\n/**\n * Names the protocol-error status code, 1002.\n *\n * @remarks\n * Sent when a framing or state rule was violated (RFC 6455 §7.4.1).\n */\nexport const WEBSOCKET_CLOSE_PROTOCOL = 1002\n\n/**\n * Names the unsupported-data status code, 1003.\n *\n * @remarks\n * Sent when the endpoint received a data type it cannot accept (RFC 6455 §7.4.1), for\n * example binary on a text-only endpoint.\n */\nexport const WEBSOCKET_CLOSE_UNSUPPORTED = 1003\n\n/**\n * Names the invalid-frame-payload-data status code, 1007.\n *\n * @remarks\n * Sent for non-UTF-8 text or an unparseable close reason (RFC 6455 §7.4.1).\n */\nexport const WEBSOCKET_CLOSE_INVALID = 1007\n\n/**\n * Names the message-too-big status code, 1009.\n *\n * @remarks\n * Sent when a reassembled message exceeded the payload cap (RFC 6455 §7.4.1).\n */\nexport const WEBSOCKET_CLOSE_TOO_BIG = 1009\n\n/**\n * Names the default cap on both an inbound frame's declared length and a reassembled\n * message's total byte count, 104,857,600 bytes (100 MiB).\n *\n * @remarks\n * The same value the `ws` package defaults to. Either breach closes\n * {@link WEBSOCKET_CLOSE_TOO_BIG}.\n */\nexport const WEBSOCKET_MAX_PAYLOAD = 104_857_600\n\n/**\n * Names the default close-handshake timeout, 30,000 milliseconds — how long `close` waits\n * for the peer's echo.\n *\n * @remarks\n * After it expires the wrapper tears the socket down, so a silent peer cannot leak the\n * handle open.\n */\nexport const WEBSOCKET_CLOSE_TIMEOUT_MS = 30_000\n\n/**\n * Names the flush grace, 1,000 milliseconds, a validation-breach close frame is given\n * before the hard teardown fallback destroys the socket.\n *\n * @remarks\n * Armed after `#fail` writes the close frame, so the frame drains through the socket's\n * write buffer rather than being discarded. The normal path destroys sooner, on the\n * `end()` flush callback.\n */\nexport const WEBSOCKET_FAIL_TIMEOUT_MS = 1_000\n\n/**\n * Names the maximum control-frame payload length, 125 bytes.\n *\n * @remarks\n * The cap RFC 6455 §5.5 sets on every control frame's payload.\n */\nexport const WEBSOCKET_CONTROL_MAX_LENGTH = 125\n\n/**\n * Names the maximum UTF-8 close-reason length after the two-byte status code, 123.\n *\n * @remarks\n * What is left of {@link WEBSOCKET_CONTROL_MAX_LENGTH} after the close frame's status\n * code.\n */\nexport const WEBSOCKET_CLOSE_REASON_MAX_LENGTH = WEBSOCKET_CONTROL_MAX_LENGTH - 2\n","import type { WebSocketErrorCode } from './types.js'\n\n// Errors for the WebSocket wrapper. A single `WebSocketError` carries a\n// machine-readable `code` naming the subject that was refused, so a `catch` branches\n// on `error.code` rather than parsing a message. Every refusal is a caller-supplied\n// value the RFC 6455 wire protocol cannot carry — a malformed option, an over-cap\n// control payload, an unsendable close code, an unrepresentable frame header — and\n// each throws before it writes a byte: an OPTION before the wrapper assumes ownership\n// of the socket, a LIMIT and a CLOSE without writing a frame or moving `readyState`,\n// and a FRAME out of the pure encoder, which touches no socket. A peer's protocol\n// violation is not an error: it closes the connection with the matching\n// `WEBSOCKET_CLOSE_*` status code instead.\n\n/**\n * Represents an error the WebSocket wrapper throws for a refused caller-supplied value,\n * carrying a machine-readable `code` and an optional `context`.\n *\n * @remarks\n * The `code` is a {@link WebSocketErrorCode}; the `context` record holds the refused\n * value under a key naming it: an `'OPTION'` carries the offending option (`payload`,\n * `timeout`, `key`, or `protocol`), a `'LIMIT'` carries `size` and the `limit` it\n * exceeded, a `'CLOSE'` carries the refused close `code`, and a `'FRAME'` carries\n * `opcode` or the mask's `size`. Narrow a caught value with {@link isWebSocketError}.\n *\n * @example\n * ```ts\n * import { createNodeWebSocket, isWebSocketError } from '@src/server'\n *\n * try {\n * \tcreateNodeWebSocket({ socket, key: 'not-base64' })\n * } catch (error) {\n * \tif (isWebSocketError(error) && error.code === 'OPTION') socket.destroy()\n * }\n * ```\n */\nexport class WebSocketError extends Error {\n\treadonly code: WebSocketErrorCode\n\treadonly context?: Readonly<Record<string, unknown>>\n\n\t/**\n\t * Creates a WebSocket error carrying a machine-readable code.\n\t *\n\t * @param code - The machine-readable {@link WebSocketErrorCode} a `catch` branches on\n\t * @param message - The human-readable description, carried as the `Error` message\n\t * @param context - The refused value keyed by name; omitted leaves `context` `undefined`\n\t */\n\tconstructor(\n\t\tcode: WebSocketErrorCode,\n\t\tmessage: string,\n\t\tcontext?: Readonly<Record<string, unknown>>,\n\t) {\n\t\tsuper(message)\n\t\tthis.name = 'WebSocketError'\n\t\tthis.code = code\n\t\tif (context !== undefined) this.context = context\n\t}\n}\n\n/**\n * Checks whether a caught value is a {@link WebSocketError}, narrowing it so a `catch` can\n * branch on `error.code`.\n *\n * @param value - The value to test (typically a `catch` binding)\n * @returns True if `value` is a `WebSocketError`; false otherwise\n *\n * @example\n * ```ts\n * import { isWebSocketError } from '@src/server'\n *\n * try {\n * \tws.close(1000.5)\n * } catch (error) {\n * \tif (isWebSocketError(error) && error.code === 'CLOSE') ws.close()\n * }\n * ```\n */\nexport function isWebSocketError(value: unknown): value is WebSocketError {\n\treturn value instanceof WebSocketError\n}\n","import type { WebSocketEncodeOptions } from './types.js'\nimport { createHash, randomBytes } from 'node:crypto'\nimport { WEBSOCKET_GUID } from './constants.js'\nimport { WebSocketError } from './errors.js'\n\n// The RFC 6455 codec helpers and boundary predicates — pure, exported, and exhaustively\n// tested. `computeWebSocketAccept` derives the handshake token; `measureWebSocketFrame`\n// reads a frame's declared payload length off the header alone and\n// `matchesWebSocketCanonical` reads whether that length uses the shortest valid encoding;\n// `encodeWebSocketFrame` builds the wire representation and refuses an unrepresentable\n// frame header with a `FRAME`-coded `WebSocketError`; `isWebSocketKey`,\n// `isWebSocketProtocol`, and `isCloseCode` are total predicates over caller-supplied\n// handshake and wire values. The coercers (`parseWebSocketFrame`, `parseUTF8`) live in\n// `parsers.ts` — this file keeps every pure read and predicate that coerces nothing.\n\n/**\n * Computes the `Sec-WebSocket-Accept` response value for an RFC 6455 upgrade.\n *\n * @remarks\n * The base64-encoded SHA-1 of the client's `Sec-WebSocket-Key` concatenated with the\n * fixed {@link WEBSOCKET_GUID} (RFC 6455 §4.2.2) — the proof the server understood the\n * handshake. Pure and deterministic.\n *\n * @param key - The client's `Sec-WebSocket-Key` header value\n * @returns The base64 accept token to send back as `Sec-WebSocket-Accept`\n */\nexport function computeWebSocketAccept(key: string): string {\n\treturn createHash('sha1')\n\t\t.update(key + WEBSOCKET_GUID)\n\t\t.digest('base64')\n}\n\n/**\n * Reads the declared payload length off the front of a buffer without buffering or\n * reading the payload itself, answering `undefined` until the length field is complete.\n *\n * @remarks\n * Decodes only byte 1's 7-bit length field, extended by the 16-bit (`126`) or 64-bit\n * (`127`) form exactly like `parseWebSocketFrame` — but stops there, so a caller\n * can reject an over-cap frame the moment its length is known, before the payload\n * bytes have even arrived. The incomplete-buffer contract mirrors the parser's. Pure;\n * never throws.\n *\n * @param buffer - The accumulation buffer to read the next frame's length from\n * @returns The declared payload length, or `undefined` when the buffer is too short to know it yet\n *\n * @example\n * ```ts\n * const declared = measureWebSocketFrame(buffer)\n * if (declared !== undefined && declared > limit) fail(WEBSOCKET_CLOSE_TOO_BIG)\n * ```\n */\nexport function measureWebSocketFrame(buffer: Buffer): number | undefined {\n\tif (buffer.length < 2) return undefined\n\n\tconst secondByte = buffer.readUInt8(1)\n\tlet length = secondByte & 0x7f\n\tconst offset = 2\n\n\tif (length === 126) {\n\t\tif (buffer.length < offset + 2) return undefined\n\t\tlength = buffer.readUInt16BE(offset)\n\t} else if (length === 127) {\n\t\tif (buffer.length < offset + 8) return undefined\n\t\tconst high = buffer.readUInt32BE(offset)\n\t\tconst low = buffer.readUInt32BE(offset + 4)\n\t\tlength = high * 0x1_0000_0000 + low\n\t}\n\n\treturn length\n}\n\n/**\n * Checks whether the next frame uses the shortest valid RFC 6455 payload-length\n * encoding, answering `undefined` until its length prefix is complete.\n *\n * @remarks\n * The 16-bit form is canonical only for lengths at least 126; the 64-bit form only for\n * lengths at least 65,536 and with its most-significant bit clear (RFC 6455 §5.2). Reads\n * the same length prefix as {@link measureWebSocketFrame}, under the same\n * incomplete-buffer contract. Pure; never throws.\n *\n * @param buffer - The accumulation buffer containing the next frame header\n * @returns Its canonicality, or `undefined` while the length prefix is incomplete\n *\n * @example\n * ```ts\n * if (matchesWebSocketCanonical(buffer) === false) fail(WEBSOCKET_CLOSE_PROTOCOL)\n * ```\n */\nexport function matchesWebSocketCanonical(buffer: Buffer): boolean | undefined {\n\tif (buffer.length < 2) return undefined\n\n\tconst lengthCode = buffer.readUInt8(1) & 0x7f\n\tif (lengthCode < 126) return true\n\tif (lengthCode === 126) {\n\t\tif (buffer.length < 4) return undefined\n\t\treturn buffer.readUInt16BE(2) >= 126\n\t}\n\tif (buffer.length < 10) return undefined\n\tconst high = buffer.readUInt32BE(2)\n\tconst low = buffer.readUInt32BE(6)\n\tif ((high & 0x8000_0000) !== 0) return false\n\treturn high > 0 || low >= 65_536\n}\n\n/**\n * Encodes a single RFC 6455 frame to its wire bytes — the inverse of\n * `parseWebSocketFrame`.\n *\n * @remarks\n * Builds a final (FIN-set) frame: byte 0 is `0x80 | opcode`; the payload length uses\n * the 7-bit form below 126, the `126` + 16-bit form below 65 536, or the `127` +\n * 64-bit form beyond; when `masked` is set the mask bit is set, a 4-byte key (supplied\n * through `options.mask`, else random) is written, and the payload is XOR-masked. Server→\n * client frames are unmasked (the default); pass `masked: true` to encode a client\n * frame (for example to feed the parser in a test). A `string` payload is encoded as\n * UTF-8. Returns one contiguous `Buffer` (header + payload), so the wrapper writes it\n * with a single `socket.write`. Pure.\n *\n * @param opcode - The frame opcode (a `WEBSOCKET_OPCODE_*` value)\n * @param payload - The payload, a `Buffer` or a UTF-8 `string`\n * @param options - Masking control ({@link WebSocketEncodeOptions}); defaults to unmasked\n * @returns The complete frame as wire bytes\n * @throws A {@link WebSocketError} coded `FRAME` when `opcode` is outside the four-bit wire field, when `options.mask` is not 4 bytes, or when `options.mask` is supplied without `masked: true`\n */\nexport function encodeWebSocketFrame(\n\topcode: number,\n\tpayload: Buffer | string,\n\toptions?: WebSocketEncodeOptions,\n): Buffer {\n\tif (!Number.isInteger(opcode) || opcode < 0 || opcode > 0x0f) {\n\t\tthrow new WebSocketError('FRAME', 'opcode must be an integer between 0 and 15', { opcode })\n\t}\n\tif (options?.mask !== undefined && options.mask.length !== 4) {\n\t\tthrow new WebSocketError('FRAME', 'mask must contain exactly 4 bytes', {\n\t\t\tsize: options.mask.length,\n\t\t})\n\t}\n\tif (options?.mask !== undefined && options.masked !== true) {\n\t\tthrow new WebSocketError('FRAME', 'mask requires masked: true')\n\t}\n\tconst body = typeof payload === 'string' ? Buffer.from(payload, 'utf-8') : payload\n\tconst length = body.length\n\tconst masked = options?.masked === true\n\tconst mask = masked ? (options?.mask ?? randomBytes(4)) : undefined\n\tconst maskBit = masked ? 0x80 : 0\n\n\t// The header size: 2 base bytes + the extended-length bytes (0 / 2 / 8) + the mask\n\t// key (0 / 4). The length prefix and the mask key write into this header.\n\tconst extended = length < 126 ? 0 : length < 65_536 ? 2 : 8\n\tconst header = Buffer.alloc(2 + extended + (mask !== undefined ? 4 : 0))\n\theader[0] = 0x80 | opcode\n\n\tif (length < 126) {\n\t\theader[1] = maskBit | length\n\t} else if (length < 65_536) {\n\t\theader[1] = maskBit | 126\n\t\theader.writeUInt16BE(length, 2)\n\t} else {\n\t\theader[1] = maskBit | 127\n\t\theader.writeUInt32BE(Math.floor(length / 0x1_0000_0000), 2)\n\t\theader.writeUInt32BE(length % 0x1_0000_0000, 6)\n\t}\n\n\tif (mask === undefined) return Buffer.concat([header, body])\n\n\tmask.copy(header, header.length - 4)\n\tconst maskedBody = Buffer.alloc(length)\n\tfor (let index = 0; index < length; index += 1) {\n\t\tmaskedBody[index] = body.readUInt8(index) ^ mask.readUInt8(index % 4)\n\t}\n\treturn Buffer.concat([header, maskedBody])\n}\n\n/**\n * Checks whether a value is a canonical RFC 6455 `Sec-WebSocket-Key`.\n *\n * @remarks\n * A valid key is exactly 16 random bytes encoded as 24 characters of base64, ending\n * in `==` (RFC 6455 §4.1). This predicate is suitable at an HTTP upgrade boundary:\n * malformed or non-canonical encodings return `false`; nothing is thrown.\n *\n * @param key - The proposed `Sec-WebSocket-Key` header value\n * @returns True if `key` is the canonical base64 encoding of 16 bytes; false otherwise\n *\n * @example\n * ```ts\n * const key = request.headers['sec-websocket-key']\n * if (typeof key !== 'string' || !isWebSocketKey(key)) socket.destroy()\n * ```\n */\nexport function isWebSocketKey(key: string): boolean {\n\tif (!/^[A-Za-z0-9+/]{22}==$/.test(key)) return false\n\treturn Buffer.from(key, 'base64').length === 16\n}\n\n/**\n * Checks whether a value is one valid WebSocket subprotocol token.\n *\n * @remarks\n * Subprotocols use the HTTP `token` grammar. Whitespace, separators, commas, and\n * control characters are rejected, preventing an untrusted value from injecting a\n * second handshake header.\n *\n * @param protocol - The negotiated subprotocol to validate\n * @returns True if `protocol` is one non-empty HTTP token; false otherwise\n *\n * @example\n * ```ts\n * if (!isWebSocketProtocol(protocol)) socket.destroy()\n * ```\n */\nexport function isWebSocketProtocol(protocol: string): boolean {\n\treturn /^[!#$%&'*+\\-.^_`|~0-9A-Za-z]+$/.test(protocol)\n}\n\n/**\n * Checks whether a numeric value is a close status code an RFC 6455 endpoint may\n * receive (§7.4.1).\n *\n * @remarks\n * True for `1000`–`1003`, `1007`–`1014`, and the application range `3000`–`4999`; false\n * for anything below `1000`, the reserved-for-local-use-only codes `1004`–`1006` and\n * `1015`, and the unassigned `1016`–`2999` range. The `1012`–`1014` extension of the\n * strict RFC 6455 receivable set is a deliberate IANA-interop choice: those three codes\n * (Service Restart, Try Again Later, Bad Gateway) are IANA-registered in the WebSocket\n * Close Code Number Registry and accepted by the `ws` ecosystem and modern conformance\n * suites, so a peer sending one is not treated as a protocol violation. Pure predicate,\n * never throws.\n *\n * @param code - The close status code to validate\n * @returns True if `code` is a valid RFC 6455 close code; false otherwise\n *\n * @example\n * ```ts\n * if (!isCloseCode(code)) fail(WEBSOCKET_CLOSE_PROTOCOL)\n * ```\n */\nexport function isCloseCode(code: number): boolean {\n\tif (!Number.isInteger(code)) return false\n\tif (code >= 1000 && code <= 1003) return true\n\tif (code >= 1007 && code <= 1014) return true\n\tif (code >= 3000 && code <= 4999) return true\n\treturn false\n}\n","import type { WebSocketFrame } from './types.js'\n\n// The RFC 6455 coercers — pure reads off a wire buffer that answer `undefined` while\n// the bytes they need are still incomplete, so the caller accumulates and retries.\n// A coercion producing `T | undefined` is a `parse*`, never a guard: it answers about\n// bytes that may not be there yet, where a guard is total. The pure reads and\n// predicates that coerce nothing — `measureWebSocketFrame`, `matchesWebSocketCanonical`,\n// `isWebSocketKey`, `isWebSocketProtocol`, `isCloseCode` — live in `helpers.ts`.\n\n/**\n * Decodes a single RFC 6455 frame from the front of a buffer, answering `undefined`\n * while the buffer is incomplete so the caller accumulates and retries.\n *\n * @remarks\n * Reads the FIN bit and opcode (byte 0), the mask bit and 7-bit payload length (byte\n * 1) — extended to a 16-bit length when the 7-bit field is `126`, or a 64-bit length\n * when it is `127` — the optional 4-byte mask key, then the payload, XOR-unmasking it\n * against the key when the mask bit is set (client→server frames must be masked, RFC\n * 6455 §5.3; an unmasked frame still decodes, leaving the payload as-is, so the caller\n * can enforce policy). The incomplete answer comes the moment the buffer is too short\n * for the part it is up to: the length prefix, the mask, or the full payload.\n * `consumed` is the total bytes the frame occupied, so the caller slices the remainder.\n * Pure; never throws on a short buffer.\n *\n * @param buffer - The accumulation buffer to decode the next frame from\n * @returns The parsed {@link WebSocketFrame}, or `undefined` when the buffer is incomplete\n *\n * @example\n * ```ts\n * const frame = parseWebSocketFrame(buffer)\n * if (frame === undefined) return // incomplete — wait for more bytes\n * ```\n */\nexport function parseWebSocketFrame(buffer: Buffer): WebSocketFrame | undefined {\n\tif (buffer.length < 2) return undefined\n\n\tconst firstByte = buffer.readUInt8(0)\n\tconst secondByte = buffer.readUInt8(1)\n\n\tconst fin = (firstByte & 0x80) !== 0\n\tconst rsv = (firstByte & 0x70) >> 4\n\tconst opcode = firstByte & 0x0f\n\tconst masked = (secondByte & 0x80) !== 0\n\tlet length = secondByte & 0x7f\n\tlet offset = 2\n\n\tif (length === 126) {\n\t\tif (buffer.length < offset + 2) return undefined\n\t\tlength = buffer.readUInt16BE(offset)\n\t\toffset += 2\n\t} else if (length === 127) {\n\t\tif (buffer.length < offset + 8) return undefined\n\t\t// Split into two 32-bit reads — a payload past 2^53 is beyond any real frame,\n\t\t// and this keeps the arithmetic in safe-integer range.\n\t\tconst high = buffer.readUInt32BE(offset)\n\t\tconst low = buffer.readUInt32BE(offset + 4)\n\t\tlength = high * 0x1_0000_0000 + low\n\t\toffset += 8\n\t}\n\n\tlet mask: Buffer | undefined\n\tif (masked) {\n\t\tif (buffer.length < offset + 4) return undefined\n\t\tmask = buffer.subarray(offset, offset + 4)\n\t\toffset += 4\n\t}\n\n\tif (buffer.length < offset + length) return undefined\n\n\tconst payload = Buffer.alloc(length)\n\tbuffer.copy(payload, 0, offset, offset + length)\n\n\tif (mask !== undefined) {\n\t\tfor (let index = 0; index < length; index += 1) {\n\t\t\tpayload[index] = payload.readUInt8(index) ^ mask.readUInt8(index % 4)\n\t\t}\n\t}\n\n\treturn { fin, opcode, payload, consumed: offset + length, masked, rsv }\n}\n\n/**\n * Decodes a byte sequence as strict UTF-8, answering `undefined` when the sequence is\n * malformed.\n *\n * @remarks\n * Wraps `TextDecoder('utf-8', { fatal: true })` in a try/catch, so a malformed sequence\n * returns rather than throwing — a guard-adjacent coercer never throws on bad input.\n * Pure.\n *\n * @param bytes - The raw bytes to decode\n * @returns The decoded string, or `undefined` when `bytes` is not valid UTF-8\n *\n * @example\n * ```ts\n * const text = parseUTF8(payload)\n * if (text === undefined) fail(WEBSOCKET_CLOSE_INVALID)\n * ```\n */\nexport function parseUTF8(bytes: Buffer): string | undefined {\n\ttry {\n\t\treturn new TextDecoder('utf-8', { fatal: true }).decode(bytes)\n\t} catch {\n\t\treturn undefined\n\t}\n}\n","import type { Duplex } from 'node:stream'\nimport type {\n\tNodeWebSocketEventMap,\n\tNodeWebSocketInterface,\n\tNodeWebSocketOptions,\n\tWebSocketReadyState,\n} from './types.js'\nimport type { EmitterInterface } from '@orkestrel/emitter'\nimport { Emitter } from '@orkestrel/emitter'\nimport {\n\tcomputeWebSocketAccept,\n\tencodeWebSocketFrame,\n\tisCloseCode,\n\tisWebSocketKey,\n\tisWebSocketProtocol,\n\tmatchesWebSocketCanonical,\n\tmeasureWebSocketFrame,\n} from './helpers.js'\nimport { parseUTF8, parseWebSocketFrame } from './parsers.js'\nimport { WebSocketError } from './errors.js'\nimport {\n\tWEBSOCKET_CLOSE_INVALID,\n\tWEBSOCKET_CLOSE_NORMAL,\n\tWEBSOCKET_CLOSE_PROTOCOL,\n\tWEBSOCKET_CLOSE_REASON_MAX_LENGTH,\n\tWEBSOCKET_CLOSE_TIMEOUT_MS,\n\tWEBSOCKET_CLOSE_TOO_BIG,\n\tWEBSOCKET_CLOSE_UNSUPPORTED,\n\tWEBSOCKET_CONTROL_MAX_LENGTH,\n\tWEBSOCKET_FAIL_TIMEOUT_MS,\n\tWEBSOCKET_MAX_PAYLOAD,\n\tWEBSOCKET_OPCODE_BINARY,\n\tWEBSOCKET_OPCODE_CLOSE,\n\tWEBSOCKET_OPCODE_CONTINUATION,\n\tWEBSOCKET_OPCODE_PING,\n\tWEBSOCKET_OPCODE_PONG,\n\tWEBSOCKET_OPCODE_TEXT,\n\tWEBSOCKET_READY_CLOSED,\n\tWEBSOCKET_READY_CLOSING,\n\tWEBSOCKET_READY_CONNECTING,\n\tWEBSOCKET_READY_OPEN,\n} from './constants.js'\n\n/**\n * Implements the wrapper contract over a raw upgraded `node:stream` Duplex socket,\n * driving the RFC 6455 handshake, the frame codec, auto-pong, and the close handshake,\n * and surfacing every event on an owned `emitter`.\n *\n * @remarks\n * Created by `createNodeWebSocket`. When given a client `key` it runs in server mode —\n * it writes the `101 Switching Protocols` handshake (`computeWebSocketAccept(key)`) and\n * emits `open`; given no key it runs in client mode (no handshake, frames masked). It\n * then listens on the socket's `data`, accumulating bytes in `#buffer` and decoding\n * every complete frame with {@link parseWebSocketFrame} (slicing `consumed` and\n * re-parsing the remainder): a text frame — reassembling continuation fragments across\n * `fin: false` frames — decodes to UTF-8 and emits `message`; a ping is auto-answered\n * with a pong and emits `ping`; a pong emits `pong`; a close frame is echoed and ends the\n * socket, emitting `close`. `send` writes a text frame, `ping` a ping, `close` a close\n * frame; `destroy` tears down immediately. It owns a typed `#emitter` by composition, and\n * the emitter isolates a throwing listener and routes the error to its own `error` handler\n * (the `error` option) — the socket never crashes. An underlying socket error emits the\n * domain `error` event and terminates the wrapper. The untyped socket `data` is narrowed\n * to a `Buffer` with a guard, never an assertion.\n *\n * @example\n * ```ts\n * import { NodeWebSocket } from '@src/server'\n *\n * // In a node:http 'upgrade' handler, over the socket the server already handed over:\n * const key = request.headers['sec-websocket-key']\n * if (typeof key !== 'string') {\n * \tsocket.destroy()\n * \treturn\n * }\n * const ws = new NodeWebSocket({ socket, key, head })\n * ws.emitter.on('message', (text) => ws.send(`echo: ${text}`))\n * ```\n */\nexport class NodeWebSocket implements NodeWebSocketInterface {\n\treadonly #emitter: Emitter<NodeWebSocketEventMap>\n\treadonly #socket: Duplex\n\treadonly #masked: boolean\n\treadonly #payload: number\n\treadonly #timeout: number\n\treadonly #signal: AbortSignal | undefined\n\treadonly #dataListener: (chunk: unknown) => void\n\treadonly #closeListener: () => void\n\treadonly #errorListener: (error: unknown) => void\n\treadonly #abortListener: () => void\n\t#buffer: Buffer = Buffer.alloc(0)\n\t#readyState: WebSocketReadyState = WEBSOCKET_READY_CONNECTING\n\t#code: number | undefined\n\t#reason: string | undefined\n\t#fragments: Buffer[] = []\n\t#messageOpcode: number | undefined\n\t#fragmentBytes = 0\n\t#closeTimer: ReturnType<typeof setTimeout> | undefined\n\t#destroyed = false\n\t#detached = false\n\n\t/**\n\t * Creates a WebSocket wrapper over an already-upgraded Duplex socket.\n\t *\n\t * @remarks\n\t * `key` selects the mode: present runs server mode and writes the `101 Switching\n\t * Protocols` handshake, omitted runs client mode and masks every outgoing frame.\n\t * {@link NodeWebSocketOptions} describes every member.\n\t *\n\t * @param options - The {@link NodeWebSocketOptions} the wrapper is built from\n\t * @throws A {@link WebSocketError} coded `OPTION` when `payload`, `timeout`, `key`, or `protocol` is refused, or when `protocol` is supplied without a server `key`, thrown before the wrapper writes to or assumes ownership of the `socket`\n\t */\n\tconstructor(options: NodeWebSocketOptions) {\n\t\tconst payload = options.payload ?? WEBSOCKET_MAX_PAYLOAD\n\t\tif (!Number.isSafeInteger(payload) || payload < 0) {\n\t\t\tthrow new WebSocketError('OPTION', 'payload must be a non-negative safe integer', {\n\t\t\t\tpayload,\n\t\t\t})\n\t\t}\n\t\tconst timeout = options.timeout ?? WEBSOCKET_CLOSE_TIMEOUT_MS\n\t\tif (!Number.isSafeInteger(timeout) || timeout < 0) {\n\t\t\tthrow new WebSocketError('OPTION', 'timeout must be a non-negative safe integer', {\n\t\t\t\ttimeout,\n\t\t\t})\n\t\t}\n\t\tif (options.key !== undefined && !isWebSocketKey(options.key)) {\n\t\t\tthrow new WebSocketError('OPTION', 'key must be the canonical base64 encoding of 16 bytes', {\n\t\t\t\tkey: options.key,\n\t\t\t})\n\t\t}\n\t\tif (options.protocol !== undefined && !isWebSocketProtocol(options.protocol)) {\n\t\t\tthrow new WebSocketError('OPTION', 'protocol must be a valid WebSocket subprotocol token', {\n\t\t\t\tprotocol: options.protocol,\n\t\t\t})\n\t\t}\n\t\tif (options.protocol !== undefined && options.key === undefined) {\n\t\t\tthrow new WebSocketError('OPTION', 'protocol requires a server key', {\n\t\t\t\tprotocol: options.protocol,\n\t\t\t})\n\t\t}\n\n\t\tthis.#emitter = new Emitter({\n\t\t\t...(options.on === undefined ? {} : { on: options.on }),\n\t\t\t...(options.error === undefined ? {} : { error: options.error }),\n\t\t})\n\t\tthis.#socket = options.socket\n\t\t// Server mode is identified by a client key (it writes the handshake + sends\n\t\t// unmasked frames); without one this is a client (no handshake, masked frames).\n\t\tthis.#masked = options.key === undefined\n\t\tthis.#payload = payload\n\t\tthis.#timeout = timeout\n\t\tthis.#signal = options.signal\n\t\t// Retain each bound listener so terminal paths detach only this wrapper's callbacks.\n\t\tthis.#dataListener = this.#handleData.bind(this)\n\t\tthis.#closeListener = this.#finish.bind(this)\n\t\tthis.#errorListener = this.#handleError.bind(this)\n\t\tthis.#abortListener = this.destroy.bind(this)\n\n\t\tif (options.key !== undefined) {\n\t\t\tconst headers = [\n\t\t\t\t'HTTP/1.1 101 Switching Protocols',\n\t\t\t\t'Upgrade: websocket',\n\t\t\t\t'Connection: Upgrade',\n\t\t\t\t`Sec-WebSocket-Accept: ${computeWebSocketAccept(options.key)}`,\n\t\t\t]\n\t\t\tif (options.protocol !== undefined) {\n\t\t\t\theaders.push(`Sec-WebSocket-Protocol: ${options.protocol}`)\n\t\t\t}\n\t\t\tthis.#socket.write(`${headers.join('\\r\\n')}\\r\\n\\r\\n`)\n\t\t}\n\n\t\tthis.#readyState = WEBSOCKET_READY_OPEN\n\t\tthis.#socket.on('data', this.#dataListener)\n\t\tthis.#socket.on('close', this.#closeListener)\n\t\tthis.#socket.on('error', this.#errorListener)\n\t\tthis.#emitter.emit('open')\n\n\t\t// Replay any bytes buffered after the upgrade headers through the same ingest path\n\t\t// as `#handleData`, so the pre-buffer cap check applies uniformly through one route.\n\t\tconst head = options.head\n\t\tif (head !== undefined && head.length > 0) {\n\t\t\tthis.#ingest(head)\n\t\t}\n\n\t\t// The external cancellation seam (composes with `@orkestrel/abort` /\n\t\t// `@orkestrel/timeout`'s native AbortSignals) — wired last so an already-aborted\n\t\t// signal tears the socket down only after the rest of construction has run. The\n\t\t// preceding head replay can itself synchronously terminate the socket (a complete\n\t\t// close frame or an RFC violation routes through `#fail`/`#close` -> `#finish`),\n\t\t// which flushes the close frame gracefully through `#socket.end()`. In that case skip\n\t\t// the seam entirely: forcing `destroy()` would discard that flushing frame (the\n\t\t// loss `#fail` is engineered to avoid), and there is no live socket to attach to.\n\t\tif (this.#readyState !== WEBSOCKET_READY_CLOSED) {\n\t\t\tif (this.#signal?.aborted === true) {\n\t\t\t\tthis.destroy()\n\t\t\t} else {\n\t\t\t\tthis.#signal?.addEventListener('abort', this.#abortListener, { once: true })\n\t\t\t}\n\t\t}\n\t}\n\n\tget emitter(): EmitterInterface<NodeWebSocketEventMap> {\n\t\treturn this.#emitter\n\t}\n\n\tget readyState(): WebSocketReadyState {\n\t\treturn this.#readyState\n\t}\n\n\tsend(message: string): void {\n\t\tif (this.#readyState !== WEBSOCKET_READY_OPEN) return\n\t\tthis.#write(WEBSOCKET_OPCODE_TEXT, Buffer.from(message, 'utf-8'))\n\t}\n\n\tping(payload?: string): void {\n\t\tif (this.#readyState !== WEBSOCKET_READY_OPEN) return\n\t\tconst size = payload === undefined ? 0 : Buffer.byteLength(payload, 'utf-8')\n\t\tif (size > WEBSOCKET_CONTROL_MAX_LENGTH) {\n\t\t\tthrow new WebSocketError(\n\t\t\t\t'LIMIT',\n\t\t\t\t`ping payload exceeds ${WEBSOCKET_CONTROL_MAX_LENGTH} bytes`,\n\t\t\t\t{ size, limit: WEBSOCKET_CONTROL_MAX_LENGTH },\n\t\t\t)\n\t\t}\n\t\tthis.#write(\n\t\t\tWEBSOCKET_OPCODE_PING,\n\t\t\tpayload === undefined ? Buffer.alloc(0) : Buffer.from(payload, 'utf-8'),\n\t\t)\n\t}\n\n\tclose(code?: number, reason?: string): void {\n\t\tif (\n\t\t\tthis.#readyState === WEBSOCKET_READY_CLOSING ||\n\t\t\tthis.#readyState === WEBSOCKET_READY_CLOSED\n\t\t) {\n\t\t\treturn\n\t\t}\n\t\tif (code !== undefined && !isCloseCode(code)) {\n\t\t\tthrow new WebSocketError('CLOSE', 'invalid close code', { code })\n\t\t}\n\t\tconst size = reason === undefined ? 0 : Buffer.byteLength(reason, 'utf-8')\n\t\tif (size > WEBSOCKET_CLOSE_REASON_MAX_LENGTH) {\n\t\t\tthrow new WebSocketError(\n\t\t\t\t'LIMIT',\n\t\t\t\t`close reason exceeds ${WEBSOCKET_CLOSE_REASON_MAX_LENGTH} bytes`,\n\t\t\t\t{ size, limit: WEBSOCKET_CLOSE_REASON_MAX_LENGTH },\n\t\t\t)\n\t\t}\n\t\tthis.#readyState = WEBSOCKET_READY_CLOSING\n\t\tthis.#code = code ?? WEBSOCKET_CLOSE_NORMAL\n\t\tthis.#reason = reason === undefined || reason.length === 0 ? undefined : reason\n\t\tthis.#write(WEBSOCKET_OPCODE_CLOSE, this.#encodeClose(this.#code, this.#reason))\n\t\t// End the writable side after the close frame; the peer's echo (or the socket\n\t\t// `close`) drives the final state transition through `#finish`.\n\t\tthis.#socket.end()\n\t\tthis.#closeTimer = setTimeout(() => this.destroy(), this.#timeout)\n\t\tthis.#closeTimer.unref()\n\t}\n\n\tdestroy(): void {\n\t\tif (this.#destroyed) return\n\t\tthis.#destroyed = true\n\t\t// Detach before destroy so a destroy-time error reaches the terminal sink.\n\t\tthis.#detach()\n\t\tthis.#signal?.removeEventListener('abort', this.#abortListener)\n\t\t// `#finish` no-ops after the state is already closed (for example after `#fail` armed\n\t\t// the hard-teardown fallback), so the timer is cleared here unconditionally rather\n\t\t// than relying on it.\n\t\tclearTimeout(this.#closeTimer)\n\t\tthis.#closeTimer = undefined\n\t\tif (!this.#socket.destroyed) this.#socket.destroy()\n\t\tthis.#finish()\n\t\tthis.#emitter.destroy()\n\t}\n\n\t// Decode every complete frame in the buffer, dispatching each and slicing\n\t// it off; stops when a partial frame remains (parse returns `undefined`).\n\t#drain(): void {\n\t\tfor (;;) {\n\t\t\tconst canonical = matchesWebSocketCanonical(this.#buffer)\n\t\t\tif (canonical === false) {\n\t\t\t\tthis.#fail(WEBSOCKET_CLOSE_PROTOCOL)\n\t\t\t\treturn\n\t\t\t}\n\t\t\tconst declared = measureWebSocketFrame(this.#buffer)\n\t\t\tif (declared !== undefined && declared > this.#payload) {\n\t\t\t\tthis.#fail(WEBSOCKET_CLOSE_TOO_BIG)\n\t\t\t\treturn\n\t\t\t}\n\t\t\tconst frame = parseWebSocketFrame(this.#buffer)\n\t\t\tif (frame === undefined) return\n\t\t\tthis.#buffer = this.#buffer.subarray(frame.consumed)\n\t\t\tthis.#dispatch(frame.fin, frame.opcode, frame.payload, frame.masked, frame.rsv)\n\t\t\tif (this.#readyState === WEBSOCKET_READY_CLOSED) return\n\t\t}\n\t}\n\n\t// Route one decoded frame through the RFC 6455 validation gauntlet, then the\n\t// fragmentation state machine. Any validity breach funnels through `#fail`, which\n\t// closes with the specified code and tears the socket down.\n\t#dispatch(fin: boolean, opcode: number, payload: Buffer, masked: boolean, rsv: number): void {\n\t\tif (rsv !== 0) {\n\t\t\tthis.#fail(WEBSOCKET_CLOSE_PROTOCOL)\n\t\t\treturn\n\t\t}\n\t\t// Server mode sends unmasked and requires masked input; client mode is the inverse.\n\t\tif (masked === this.#masked) {\n\t\t\tthis.#fail(WEBSOCKET_CLOSE_PROTOCOL)\n\t\t\treturn\n\t\t}\n\n\t\tif (\n\t\t\topcode === WEBSOCKET_OPCODE_CLOSE ||\n\t\t\topcode === WEBSOCKET_OPCODE_PING ||\n\t\t\topcode === WEBSOCKET_OPCODE_PONG\n\t\t) {\n\t\t\tif (!fin || payload.length > WEBSOCKET_CONTROL_MAX_LENGTH) {\n\t\t\t\tthis.#fail(WEBSOCKET_CLOSE_PROTOCOL)\n\t\t\t\treturn\n\t\t\t}\n\t\t\tif (opcode === WEBSOCKET_OPCODE_PING) {\n\t\t\t\tthis.#write(WEBSOCKET_OPCODE_PONG, payload)\n\t\t\t\tthis.#emitter.emit('ping')\n\t\t\t\treturn\n\t\t\t}\n\t\t\tif (opcode === WEBSOCKET_OPCODE_PONG) {\n\t\t\t\tthis.#emitter.emit('pong')\n\t\t\t\treturn\n\t\t\t}\n\t\t\tthis.#close(payload)\n\t\t\treturn\n\t\t}\n\n\t\tif (opcode === WEBSOCKET_OPCODE_TEXT || opcode === WEBSOCKET_OPCODE_BINARY) {\n\t\t\tif (this.#messageOpcode !== undefined) {\n\t\t\t\tthis.#fail(WEBSOCKET_CLOSE_PROTOCOL)\n\t\t\t\treturn\n\t\t\t}\n\t\t\tthis.#messageOpcode = opcode\n\t\t} else if (opcode === WEBSOCKET_OPCODE_CONTINUATION) {\n\t\t\tif (this.#messageOpcode === undefined) {\n\t\t\t\tthis.#fail(WEBSOCKET_CLOSE_PROTOCOL)\n\t\t\t\treturn\n\t\t\t}\n\t\t} else {\n\t\t\t// Reserved data (0x3–0x7) or reserved control (0xB–0xF) opcodes.\n\t\t\tthis.#fail(WEBSOCKET_CLOSE_PROTOCOL)\n\t\t\treturn\n\t\t}\n\n\t\tthis.#fragments.push(payload)\n\t\tthis.#fragmentBytes += payload.length\n\t\tif (this.#fragmentBytes > this.#payload) {\n\t\t\tthis.#fail(WEBSOCKET_CLOSE_TOO_BIG)\n\t\t\treturn\n\t\t}\n\t\tif (!fin) return\n\n\t\tif (this.#messageOpcode === WEBSOCKET_OPCODE_BINARY) {\n\t\t\tthis.#fail(WEBSOCKET_CLOSE_UNSUPPORTED)\n\t\t\treturn\n\t\t}\n\t\tconst text = parseUTF8(Buffer.concat(this.#fragments))\n\t\tif (text === undefined) {\n\t\t\tthis.#fail(WEBSOCKET_CLOSE_INVALID)\n\t\t\treturn\n\t\t}\n\t\tthis.#emitter.emit('message', text)\n\t\tthis.#messageOpcode = undefined\n\t\tthis.#fragments = []\n\t\tthis.#fragmentBytes = 0\n\t}\n\n\t// Handle a validated close frame: decode it (which itself may `#fail` on an invalid\n\t// code/reason), then — if the socket is still open — echo the peer's payload verbatim\n\t// and end.\n\t#close(payload: Buffer): void {\n\t\tconst valid = this.#decodeClose(payload)\n\t\tif (!valid) return\n\t\tif (this.#readyState === WEBSOCKET_READY_OPEN) {\n\t\t\t// Echo the peer's close frame before ending, per RFC 6455 §5.5.1.\n\t\t\tthis.#readyState = WEBSOCKET_READY_CLOSING\n\t\t\tthis.#write(WEBSOCKET_OPCODE_CLOSE, payload)\n\t\t}\n\t\t// The echo is queued; detach before `end()` can surface a socket error.\n\t\tthis.#detach()\n\t\tthis.#socket.end()\n\t\tthis.#finish()\n\t}\n\n\t// The single funnel for every RFC 6455 validation breach: close with `code`, `#detach`\n\t// the domain listeners (the connection is protocol-dead — RFC 6455 permits discarding\n\t// further input after sending close, and this also stops a post-fail socket `error`\n\t// emitting after the terminal `close` event), write the close frame, then flush + half\n\t// -close through `end()` (never a synchronous `destroy()`, which can discard the buffered\n\t// close frame and leave the peer seeing 1006 instead of the intended code) before\n\t// finishing. The hard-teardown fallback is armed after `#finish` so `#finish`'s\n\t// `clearTimeout` cannot kill it; the normal path destroys the moment the write buffer\n\t// flushes (the `end()` callback), the unref'd timer is only the malicious-peer backstop.\n\t#fail(code: number, reason?: string): void {\n\t\tif (\n\t\t\tthis.#readyState === WEBSOCKET_READY_CLOSING ||\n\t\t\tthis.#readyState === WEBSOCKET_READY_CLOSED\n\t\t) {\n\t\t\treturn\n\t\t}\n\t\tthis.#code = code\n\t\tthis.#reason = reason\n\t\tthis.#readyState = WEBSOCKET_READY_CLOSING\n\t\tthis.#detach()\n\t\tthis.#write(WEBSOCKET_OPCODE_CLOSE, this.#encodeClose(code, reason))\n\t\tthis.#socket.end(() => {\n\t\t\tif (!this.#socket.destroyed) this.#socket.destroy()\n\t\t\t// The normal flush path destroyed the socket already — clear the unref'd fallback\n\t\t\t// timer this method arms after `#finish`, so it doesn't linger\n\t\t\t// `WEBSOCKET_FAIL_TIMEOUT_MS` holding its closure alive for no reason.\n\t\t\tclearTimeout(this.#closeTimer)\n\t\t\tthis.#closeTimer = undefined\n\t\t})\n\t\tthis.#messageOpcode = undefined\n\t\tthis.#fragments = []\n\t\tthis.#fragmentBytes = 0\n\t\tthis.#finish()\n\t\tthis.#closeTimer = setTimeout(() => {\n\t\t\tif (!this.#socket.destroyed) this.#socket.destroy()\n\t\t}, WEBSOCKET_FAIL_TIMEOUT_MS)\n\t\tthis.#closeTimer.unref()\n\t}\n\n\t// Drop only this wrapper's domain listeners and arm one durable terminal error sink.\n\t#detach(): void {\n\t\tif (this.#detached) return\n\t\tthis.#detached = true\n\t\tthis.#socket.off('data', this.#dataListener)\n\t\tthis.#socket.off('close', this.#closeListener)\n\t\tthis.#socket.off('error', this.#errorListener)\n\t\t// Keep a terminal socket safe from late peer errors after the domain listener is gone.\n\t\tthis.#socket.on('error', () => undefined)\n\t}\n\n\t// Write one frame to the socket — masked in client mode, unmasked in server mode.\n\t// A destroyed socket silently drops the write (the lifecycle is already ending).\n\t#write(opcode: number, payload: Buffer): void {\n\t\tif (this.#socket.destroyed) return\n\t\tthis.#socket.write(encodeWebSocketFrame(opcode, payload, { masked: this.#masked }))\n\t}\n\n\t// Build a close-frame payload: the 2-byte big-endian code, then the optional UTF-8\n\t// reason. An undefined code yields an empty payload (a bare close).\n\t#encodeClose(code: number | undefined, reason: string | undefined): Buffer {\n\t\tif (code === undefined) return Buffer.alloc(0)\n\t\tconst text = reason === undefined ? Buffer.alloc(0) : Buffer.from(reason, 'utf-8')\n\t\tconst payload = Buffer.alloc(2 + text.length)\n\t\tpayload.writeUInt16BE(code, 0)\n\t\ttext.copy(payload, 2)\n\t\treturn payload\n\t}\n\n\t// Validate and read a peer close-frame payload into `#code` / `#reason` (RFC 6455\n\t// §7.4.1). A bare close (0 bytes) is valid with no code/reason. A single stray byte\n\t// is a protocol error. 2+ bytes carry a code (must be a receivable close code) and\n\t// an optional UTF-8 reason. Returns `false` when a breach routed through `#fail`\n\t// (the caller must not also echo).\n\t#decodeClose(payload: Buffer): boolean {\n\t\tif (payload.length === 0) {\n\t\t\tthis.#code = undefined\n\t\t\tthis.#reason = undefined\n\t\t\treturn true\n\t\t}\n\t\tif (payload.length === 1) {\n\t\t\tthis.#fail(WEBSOCKET_CLOSE_PROTOCOL)\n\t\t\treturn false\n\t\t}\n\t\tconst code = payload.readUInt16BE(0)\n\t\tif (!isCloseCode(code)) {\n\t\t\tthis.#fail(WEBSOCKET_CLOSE_PROTOCOL)\n\t\t\treturn false\n\t\t}\n\t\tif (payload.length === 2) {\n\t\t\tthis.#code = code\n\t\t\tthis.#reason = undefined\n\t\t\treturn true\n\t\t}\n\t\tconst reason = parseUTF8(payload.subarray(2))\n\t\tif (reason === undefined) {\n\t\t\tthis.#fail(WEBSOCKET_CLOSE_INVALID)\n\t\t\treturn false\n\t\t}\n\t\tthis.#code = code\n\t\tthis.#reason = reason.length === 0 ? undefined : reason\n\t\treturn true\n\t}\n\n\t// Transition to the closed state one time only (idempotent), clear the close-handshake timer, and emit\n\t// the final `close` with the last known code/reason.\n\t#finish(): void {\n\t\tif (this.#readyState === WEBSOCKET_READY_CLOSED) return\n\t\tthis.#detach()\n\t\tclearTimeout(this.#closeTimer)\n\t\tthis.#closeTimer = undefined\n\t\tthis.#signal?.removeEventListener('abort', this.#abortListener)\n\t\tthis.#readyState = WEBSOCKET_READY_CLOSED\n\t\tthis.#emitter.emit('close', this.#code, this.#reason)\n\t}\n\n\t// Append `bytes` to the accumulation buffer, then drain every complete frame. `#drain`\n\t// preflights canonical encoding and the declared payload cap on each iteration, so\n\t// every coalesced frame receives the same validation. Shared by `#handleData` and head replay.\n\t#ingest(bytes: Buffer): void {\n\t\tthis.#buffer = Buffer.concat([this.#buffer, bytes])\n\t\tthis.#drain()\n\t}\n\n\t#handleData(chunk: unknown): void {\n\t\tif (this.#readyState === WEBSOCKET_READY_CLOSED) return\n\t\tconst bytes = this.#bytes(chunk)\n\t\tif (bytes === undefined) return\n\t\tthis.#ingest(bytes)\n\t}\n\n\t#handleError(error: unknown): void {\n\t\tthis.#emitter.emit('error', error)\n\t\tthis.destroy()\n\t}\n\n\t// Narrow an untyped socket `data` chunk to a `Buffer` with a guard — a `node:net`\n\t// socket without an explicit encoding yields Buffers, but the listener parameter is\n\t// `unknown`, so it crosses through this guard, never an assertion. A non-Buffer\n\t// chunk (a string from a mis-encoded socket) is normalized; anything else is dropped.\n\t#bytes(chunk: unknown): Buffer | undefined {\n\t\tif (Buffer.isBuffer(chunk)) return chunk\n\t\tif (typeof chunk === 'string') return Buffer.from(chunk, 'utf-8')\n\t\treturn undefined\n\t}\n}\n","import type { NodeWebSocketInterface, NodeWebSocketOptions } from './types.js'\nimport { NodeWebSocket } from './NodeWebSocket.js'\n\n/**\n * Creates a server-native WebSocket over a raw upgraded `node:stream` Duplex socket —\n * server mode when a `key` is given, client mode otherwise.\n *\n * @remarks\n * The construction entry point for the {@link NodeWebSocketInterface}. In server mode the\n * wrapper writes the `101 Switching Protocols` handshake and sends unmasked frames; in\n * client mode it writes no handshake and masks every outgoing frame. This is the\n * lean-native handle: it speaks the WebSocket wire protocol and nothing above it, so a\n * message transport is built on it rather than into it.\n *\n * @param options - The {@link NodeWebSocketOptions} (`socket`, optional `key` / `head` /\n * `protocol` / `on`)\n * @returns A typed {@link NodeWebSocketInterface}\n * @throws A `WebSocketError` coded `OPTION` when `payload`, `timeout`, `key`, or `protocol` is refused, thrown before the wrapper writes to or assumes ownership of the `socket`\n *\n * @example Accept an upgrade and echo messages (server mode)\n * ```ts\n * import { createNodeWebSocket } from '@orkestrel/websocket'\n *\n * server.on('upgrade', (request, socket, head) => {\n * \tconst key = request.headers['sec-websocket-key']\n * \tif (typeof key !== 'string') {\n * \t\tsocket.destroy()\n * \t\treturn\n * \t}\n * \tconst ws = createNodeWebSocket({\n * \t\tsocket,\n * \t\tkey,\n * \t\thead, // any bytes already buffered after the upgrade headers\n * \t\ton: { message: (text) => ws.send(`echo: ${text}`) }, // wired before the first frame arrives\n * \t})\n * \tws.emitter.on('message', (text) => log('echoed', text)) // a second observer of the same event\n * \tws.emitter.on('close', (code, reason) => log('closed', code, reason))\n * })\n * ```\n */\nexport function createNodeWebSocket(options: NodeWebSocketOptions): NodeWebSocketInterface {\n\treturn new NodeWebSocket(options)\n}\n"],"mappings":";;;;;;;;;;;;;AAiBA,IAAa,iBAAiB;;;;;;;AAQ9B,IAAa,oBAAoB;;;;;;;AAQjC,IAAa,wBAAwB;;;;;;;AAQrC,IAAa,0BAA0B;;;;;;;AAQvC,IAAa,gCAAgC;;;;;;;AAQ7C,IAAa,yBAAyB;;;;;;;AAQtC,IAAa,wBAAwB;;;;;;;AAQrC,IAAa,wBAAwB;;;;;;;AAQrC,IAAa,6BAAkD;;;;;;;AAQ/D,IAAa,uBAA4C;;;;;;;AAQzD,IAAa,0BAA+C;;;;;;;AAQ5D,IAAa,yBAA8C;;;;;;;AAQ3D,IAAa,yBAAyB;;;;;;;AAQtC,IAAa,2BAA2B;;;;;;;;AASxC,IAAa,8BAA8B;;;;;;;AAQ3C,IAAa,0BAA0B;;;;;;;AAQvC,IAAa,0BAA0B;;;;;;;;;AAUvC,IAAa,wBAAwB;;;;;;;;;AAUrC,IAAa,6BAA6B;;;;;;;;;;AAW1C,IAAa,4BAA4B;;;;;;;AAQzC,IAAa,+BAA+B;;;;;;;;AAS5C,IAAa,oCAAoC;;;;;;;;;;;;;;;;;;;;;;;;;AC/JjD,IAAa,iBAAb,cAAoC,MAAM;CACzC;CACA;;;;;;;;CASA,YACC,MACA,SACA,SACC;EACD,MAAM,OAAO;EACb,KAAK,OAAO;EACZ,KAAK,OAAO;EACZ,IAAI,YAAY,KAAA,GAAW,KAAK,UAAU;CAC3C;AACD;;;;;;;;;;;;;;;;;;;AAoBA,SAAgB,iBAAiB,OAAyC;CACzE,OAAO,iBAAiB;AACzB;;;;;;;;;;;;;;ACpDA,SAAgB,uBAAuB,KAAqB;CAC3D,QAAA,GAAO,YAAA,WAAA,CAAW,MAAM,CAAC,CACvB,OAAO,MAAM,cAAc,CAAC,CAC5B,OAAO,QAAQ;AAClB;;;;;;;;;;;;;;;;;;;;;AAsBA,SAAgB,sBAAsB,QAAoC;CACzE,IAAI,OAAO,SAAS,GAAG,OAAO,KAAA;CAG9B,IAAI,SADe,OAAO,UAAU,CACvB,IAAa;CAC1B,MAAM,SAAS;CAEf,IAAI,WAAW,KAAK;EACnB,IAAI,OAAO,SAAS,GAAY,OAAO,KAAA;EACvC,SAAS,OAAO,aAAa,MAAM;CACpC,OAAO,IAAI,WAAW,KAAK;EAC1B,IAAI,OAAO,SAAS,IAAY,OAAO,KAAA;EACvC,MAAM,OAAO,OAAO,aAAa,MAAM;EACvC,MAAM,MAAM,OAAO,aAAa,CAAU;EAC1C,SAAS,OAAO,aAAgB;CACjC;CAEA,OAAO;AACR;;;;;;;;;;;;;;;;;;;AAoBA,SAAgB,0BAA0B,QAAqC;CAC9E,IAAI,OAAO,SAAS,GAAG,OAAO,KAAA;CAE9B,MAAM,aAAa,OAAO,UAAU,CAAC,IAAI;CACzC,IAAI,aAAa,KAAK,OAAO;CAC7B,IAAI,eAAe,KAAK;EACvB,IAAI,OAAO,SAAS,GAAG,OAAO,KAAA;EAC9B,OAAO,OAAO,aAAa,CAAC,KAAK;CAClC;CACA,IAAI,OAAO,SAAS,IAAI,OAAO,KAAA;CAC/B,MAAM,OAAO,OAAO,aAAa,CAAC;CAClC,MAAM,MAAM,OAAO,aAAa,CAAC;CACjC,KAAK,OAAO,gBAAiB,GAAG,OAAO;CACvC,OAAO,OAAO,KAAK,OAAO;AAC3B;;;;;;;;;;;;;;;;;;;;;AAsBA,SAAgB,qBACf,QACA,SACA,SACS;CACT,IAAI,CAAC,OAAO,UAAU,MAAM,KAAK,SAAS,KAAK,SAAS,IACvD,MAAM,IAAI,eAAe,SAAS,8CAA8C,EAAE,OAAO,CAAC;CAE3F,IAAI,SAAS,SAAS,KAAA,KAAa,QAAQ,KAAK,WAAW,GAC1D,MAAM,IAAI,eAAe,SAAS,qCAAqC,EACtE,MAAM,QAAQ,KAAK,OACpB,CAAC;CAEF,IAAI,SAAS,SAAS,KAAA,KAAa,QAAQ,WAAW,MACrD,MAAM,IAAI,eAAe,SAAS,4BAA4B;CAE/D,MAAM,OAAO,OAAO,YAAY,WAAW,OAAO,KAAK,SAAS,OAAO,IAAI;CAC3E,MAAM,SAAS,KAAK;CACpB,MAAM,SAAS,SAAS,WAAW;CACnC,MAAM,OAAO,SAAU,SAAS,SAAA,GAAQ,YAAA,YAAA,CAAY,CAAC,IAAK,KAAA;CAC1D,MAAM,UAAU,SAAS,MAAO;CAIhC,MAAM,WAAW,SAAS,MAAM,IAAI,SAAS,QAAS,IAAI;CAC1D,MAAM,SAAS,OAAO,MAAM,IAAI,YAAY,SAAS,KAAA,IAAY,IAAI,EAAE;CACvE,OAAO,KAAK,MAAO;CAEnB,IAAI,SAAS,KACZ,OAAO,KAAK,UAAU;MAChB,IAAI,SAAS,OAAQ;EAC3B,OAAO,KAAK,UAAU;EACtB,OAAO,cAAc,QAAQ,CAAC;CAC/B,OAAO;EACN,OAAO,KAAK,UAAU;EACtB,OAAO,cAAc,KAAK,MAAM,SAAS,UAAa,GAAG,CAAC;EAC1D,OAAO,cAAc,SAAS,YAAe,CAAC;CAC/C;CAEA,IAAI,SAAS,KAAA,GAAW,OAAO,OAAO,OAAO,CAAC,QAAQ,IAAI,CAAC;CAE3D,KAAK,KAAK,QAAQ,OAAO,SAAS,CAAC;CACnC,MAAM,aAAa,OAAO,MAAM,MAAM;CACtC,KAAK,IAAI,QAAQ,GAAG,QAAQ,QAAQ,SAAS,GAC5C,WAAW,SAAS,KAAK,UAAU,KAAK,IAAI,KAAK,UAAU,QAAQ,CAAC;CAErE,OAAO,OAAO,OAAO,CAAC,QAAQ,UAAU,CAAC;AAC1C;;;;;;;;;;;;;;;;;;AAmBA,SAAgB,eAAe,KAAsB;CACpD,IAAI,CAAC,wBAAwB,KAAK,GAAG,GAAG,OAAO;CAC/C,OAAO,OAAO,KAAK,KAAK,QAAQ,CAAC,CAAC,WAAW;AAC9C;;;;;;;;;;;;;;;;;AAkBA,SAAgB,oBAAoB,UAA2B;CAC9D,OAAO,iCAAiC,KAAK,QAAQ;AACtD;;;;;;;;;;;;;;;;;;;;;;;AAwBA,SAAgB,YAAY,MAAuB;CAClD,IAAI,CAAC,OAAO,UAAU,IAAI,GAAG,OAAO;CACpC,IAAI,QAAQ,OAAQ,QAAQ,MAAM,OAAO;CACzC,IAAI,QAAQ,QAAQ,QAAQ,MAAM,OAAO;CACzC,IAAI,QAAQ,OAAQ,QAAQ,MAAM,OAAO;CACzC,OAAO;AACR;;;;;;;;;;;;;;;;;;;;;;;;;;;ACpNA,SAAgB,oBAAoB,QAA4C;CAC/E,IAAI,OAAO,SAAS,GAAG,OAAO,KAAA;CAE9B,MAAM,YAAY,OAAO,UAAU,CAAC;CACpC,MAAM,aAAa,OAAO,UAAU,CAAC;CAErC,MAAM,OAAO,YAAY,SAAU;CACnC,MAAM,OAAO,YAAY,QAAS;CAClC,MAAM,SAAS,YAAY;CAC3B,MAAM,UAAU,aAAa,SAAU;CACvC,IAAI,SAAS,aAAa;CAC1B,IAAI,SAAS;CAEb,IAAI,WAAW,KAAK;EACnB,IAAI,OAAO,SAAS,SAAS,GAAG,OAAO,KAAA;EACvC,SAAS,OAAO,aAAa,MAAM;EACnC,UAAU;CACX,OAAO,IAAI,WAAW,KAAK;EAC1B,IAAI,OAAO,SAAS,SAAS,GAAG,OAAO,KAAA;EAGvC,MAAM,OAAO,OAAO,aAAa,MAAM;EACvC,MAAM,MAAM,OAAO,aAAa,SAAS,CAAC;EAC1C,SAAS,OAAO,aAAgB;EAChC,UAAU;CACX;CAEA,IAAI;CACJ,IAAI,QAAQ;EACX,IAAI,OAAO,SAAS,SAAS,GAAG,OAAO,KAAA;EACvC,OAAO,OAAO,SAAS,QAAQ,SAAS,CAAC;EACzC,UAAU;CACX;CAEA,IAAI,OAAO,SAAS,SAAS,QAAQ,OAAO,KAAA;CAE5C,MAAM,UAAU,OAAO,MAAM,MAAM;CACnC,OAAO,KAAK,SAAS,GAAG,QAAQ,SAAS,MAAM;CAE/C,IAAI,SAAS,KAAA,GACZ,KAAK,IAAI,QAAQ,GAAG,QAAQ,QAAQ,SAAS,GAC5C,QAAQ,SAAS,QAAQ,UAAU,KAAK,IAAI,KAAK,UAAU,QAAQ,CAAC;CAItE,OAAO;EAAE;EAAK;EAAQ;EAAS,UAAU,SAAS;EAAQ;EAAQ;CAAI;AACvE;;;;;;;;;;;;;;;;;;;AAoBA,SAAgB,UAAU,OAAmC;CAC5D,IAAI;EACH,OAAO,IAAI,YAAY,SAAS,EAAE,OAAO,KAAK,CAAC,CAAC,CAAC,OAAO,KAAK;CAC9D,QAAQ;EACP;CACD;AACD;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;AC3BA,IAAa,gBAAb,MAA6D;CAC5D;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA,UAAkB,OAAO,MAAM,CAAC;CAChC,cAAA;CACA;CACA;CACA,aAAuB,CAAC;CACxB;CACA,iBAAiB;CACjB;CACA,aAAa;CACb,YAAY;;;;;;;;;;;;CAaZ,YAAY,SAA+B;EAC1C,MAAM,UAAU,QAAQ,WAAA;EACxB,IAAI,CAAC,OAAO,cAAc,OAAO,KAAK,UAAU,GAC/C,MAAM,IAAI,eAAe,UAAU,+CAA+C,EACjF,QACD,CAAC;EAEF,MAAM,UAAU,QAAQ,WAAA;EACxB,IAAI,CAAC,OAAO,cAAc,OAAO,KAAK,UAAU,GAC/C,MAAM,IAAI,eAAe,UAAU,+CAA+C,EACjF,QACD,CAAC;EAEF,IAAI,QAAQ,QAAQ,KAAA,KAAa,CAAC,eAAe,QAAQ,GAAG,GAC3D,MAAM,IAAI,eAAe,UAAU,yDAAyD,EAC3F,KAAK,QAAQ,IACd,CAAC;EAEF,IAAI,QAAQ,aAAa,KAAA,KAAa,CAAC,oBAAoB,QAAQ,QAAQ,GAC1E,MAAM,IAAI,eAAe,UAAU,wDAAwD,EAC1F,UAAU,QAAQ,SACnB,CAAC;EAEF,IAAI,QAAQ,aAAa,KAAA,KAAa,QAAQ,QAAQ,KAAA,GACrD,MAAM,IAAI,eAAe,UAAU,kCAAkC,EACpE,UAAU,QAAQ,SACnB,CAAC;EAGF,KAAK,WAAW,IAAI,mBAAA,QAAQ;GAC3B,GAAI,QAAQ,OAAO,KAAA,IAAY,CAAC,IAAI,EAAE,IAAI,QAAQ,GAAG;GACrD,GAAI,QAAQ,UAAU,KAAA,IAAY,CAAC,IAAI,EAAE,OAAO,QAAQ,MAAM;EAC/D,CAAC;EACD,KAAK,UAAU,QAAQ;EAGvB,KAAK,UAAU,QAAQ,QAAQ,KAAA;EAC/B,KAAK,WAAW;EAChB,KAAK,WAAW;EAChB,KAAK,UAAU,QAAQ;EAEvB,KAAK,gBAAgB,KAAK,YAAY,KAAK,IAAI;EAC/C,KAAK,iBAAiB,KAAK,QAAQ,KAAK,IAAI;EAC5C,KAAK,iBAAiB,KAAK,aAAa,KAAK,IAAI;EACjD,KAAK,iBAAiB,KAAK,QAAQ,KAAK,IAAI;EAE5C,IAAI,QAAQ,QAAQ,KAAA,GAAW;GAC9B,MAAM,UAAU;IACf;IACA;IACA;IACA,yBAAyB,uBAAuB,QAAQ,GAAG;GAC5D;GACA,IAAI,QAAQ,aAAa,KAAA,GACxB,QAAQ,KAAK,2BAA2B,QAAQ,UAAU;GAE3D,KAAK,QAAQ,MAAM,GAAG,QAAQ,KAAK,MAAM,EAAE,SAAS;EACrD;EAEA,KAAK,cAAA;EACL,KAAK,QAAQ,GAAG,QAAQ,KAAK,aAAa;EAC1C,KAAK,QAAQ,GAAG,SAAS,KAAK,cAAc;EAC5C,KAAK,QAAQ,GAAG,SAAS,KAAK,cAAc;EAC5C,KAAK,SAAS,KAAK,MAAM;EAIzB,MAAM,OAAO,QAAQ;EACrB,IAAI,SAAS,KAAA,KAAa,KAAK,SAAS,GACvC,KAAK,QAAQ,IAAI;EAWlB,IAAI,KAAK,gBAAA,GAAwC;GAChD,IAAI,KAAK,SAAS,YAAY,MAC7B,KAAK,QAAQ;QAEb,KAAK,SAAS,iBAAiB,SAAS,KAAK,gBAAgB,EAAE,MAAM,KAAK,CAAC;EAE7E;CACD;CAEA,IAAI,UAAmD;EACtD,OAAO,KAAK;CACb;CAEA,IAAI,aAAkC;EACrC,OAAO,KAAK;CACb;CAEA,KAAK,SAAuB;EAC3B,IAAI,KAAK,gBAAA,GAAsC;EAC/C,KAAK,OAAA,GAA8B,OAAO,KAAK,SAAS,OAAO,CAAC;CACjE;CAEA,KAAK,SAAwB;EAC5B,IAAI,KAAK,gBAAA,GAAsC;EAC/C,MAAM,OAAO,YAAY,KAAA,IAAY,IAAI,OAAO,WAAW,SAAS,OAAO;EAC3E,IAAI,OAAA,KACH,MAAM,IAAI,eACT,SACA,kCACA;GAAE;GAAM,OAAA;EAAoC,CAC7C;EAED,KAAK,OAAA,GAEJ,YAAY,KAAA,IAAY,OAAO,MAAM,CAAC,IAAI,OAAO,KAAK,SAAS,OAAO,CACvE;CACD;CAEA,MAAM,MAAe,QAAuB;EAC3C,IACC,KAAK,gBAAA,KACL,KAAK,gBAAA,GAEL;EAED,IAAI,SAAS,KAAA,KAAa,CAAC,YAAY,IAAI,GAC1C,MAAM,IAAI,eAAe,SAAS,sBAAsB,EAAE,KAAK,CAAC;EAEjE,MAAM,OAAO,WAAW,KAAA,IAAY,IAAI,OAAO,WAAW,QAAQ,OAAO;EACzE,IAAI,OAAA,KACH,MAAM,IAAI,eACT,SACA,kCACA;GAAE;GAAM,OAAA;EAAyC,CAClD;EAED,KAAK,cAAA;EACL,KAAK,QAAQ,QAAA;EACb,KAAK,UAAU,WAAW,KAAA,KAAa,OAAO,WAAW,IAAI,KAAA,IAAY;EACzE,KAAK,OAAA,GAA+B,KAAK,aAAa,KAAK,OAAO,KAAK,OAAO,CAAC;EAG/E,KAAK,QAAQ,IAAI;EACjB,KAAK,cAAc,iBAAiB,KAAK,QAAQ,GAAG,KAAK,QAAQ;EACjE,KAAK,YAAY,MAAM;CACxB;CAEA,UAAgB;EACf,IAAI,KAAK,YAAY;EACrB,KAAK,aAAa;EAElB,KAAK,QAAQ;EACb,KAAK,SAAS,oBAAoB,SAAS,KAAK,cAAc;EAI9D,aAAa,KAAK,WAAW;EAC7B,KAAK,cAAc,KAAA;EACnB,IAAI,CAAC,KAAK,QAAQ,WAAW,KAAK,QAAQ,QAAQ;EAClD,KAAK,QAAQ;EACb,KAAK,SAAS,QAAQ;CACvB;CAIA,SAAe;EACd,SAAS;GAER,IADkB,0BAA0B,KAAK,OAC7C,MAAc,OAAO;IACxB,KAAK,MAAM,wBAAwB;IACnC;GACD;GACA,MAAM,WAAW,sBAAsB,KAAK,OAAO;GACnD,IAAI,aAAa,KAAA,KAAa,WAAW,KAAK,UAAU;IACvD,KAAK,MAAM,uBAAuB;IAClC;GACD;GACA,MAAM,QAAQ,oBAAoB,KAAK,OAAO;GAC9C,IAAI,UAAU,KAAA,GAAW;GACzB,KAAK,UAAU,KAAK,QAAQ,SAAS,MAAM,QAAQ;GACnD,KAAK,UAAU,MAAM,KAAK,MAAM,QAAQ,MAAM,SAAS,MAAM,QAAQ,MAAM,GAAG;GAC9E,IAAI,KAAK,gBAAA,GAAwC;EAClD;CACD;CAKA,UAAU,KAAc,QAAgB,SAAiB,QAAiB,KAAmB;EAC5F,IAAI,QAAQ,GAAG;GACd,KAAK,MAAM,wBAAwB;GACnC;EACD;EAEA,IAAI,WAAW,KAAK,SAAS;GAC5B,KAAK,MAAM,wBAAwB;GACnC;EACD;EAEA,IACC,WAAA,KACA,WAAA,KACA,WAAA,IACC;GACD,IAAI,CAAC,OAAO,QAAQ,SAAA,KAAuC;IAC1D,KAAK,MAAM,wBAAwB;IACnC;GACD;GACA,IAAI,WAAA,GAAkC;IACrC,KAAK,OAAA,IAA8B,OAAO;IAC1C,KAAK,SAAS,KAAK,MAAM;IACzB;GACD;GACA,IAAI,WAAA,IAAkC;IACrC,KAAK,SAAS,KAAK,MAAM;IACzB;GACD;GACA,KAAK,OAAO,OAAO;GACnB;EACD;EAEA,IAAI,WAAA,KAAoC,WAAA,GAAoC;GAC3E,IAAI,KAAK,mBAAmB,KAAA,GAAW;IACtC,KAAK,MAAM,wBAAwB;IACnC;GACD;GACA,KAAK,iBAAiB;EACvB,OAAO,IAAI,WAAA,GACN;OAAA,KAAK,mBAAmB,KAAA,GAAW;IACtC,KAAK,MAAM,wBAAwB;IACnC;GACD;SACM;GAEN,KAAK,MAAM,wBAAwB;GACnC;EACD;EAEA,KAAK,WAAW,KAAK,OAAO;EAC5B,KAAK,kBAAkB,QAAQ;EAC/B,IAAI,KAAK,iBAAiB,KAAK,UAAU;GACxC,KAAK,MAAM,uBAAuB;GAClC;EACD;EACA,IAAI,CAAC,KAAK;EAEV,IAAI,KAAK,mBAAA,GAA4C;GACpD,KAAK,MAAM,2BAA2B;GACtC;EACD;EACA,MAAM,OAAO,UAAU,OAAO,OAAO,KAAK,UAAU,CAAC;EACrD,IAAI,SAAS,KAAA,GAAW;GACvB,KAAK,MAAM,uBAAuB;GAClC;EACD;EACA,KAAK,SAAS,KAAK,WAAW,IAAI;EAClC,KAAK,iBAAiB,KAAA;EACtB,KAAK,aAAa,CAAC;EACnB,KAAK,iBAAiB;CACvB;CAKA,OAAO,SAAuB;EAE7B,IAAI,CADU,KAAK,aAAa,OAC3B,GAAO;EACZ,IAAI,KAAK,gBAAA,GAAsC;GAE9C,KAAK,cAAA;GACL,KAAK,OAAA,GAA+B,OAAO;EAC5C;EAEA,KAAK,QAAQ;EACb,KAAK,QAAQ,IAAI;EACjB,KAAK,QAAQ;CACd;CAWA,MAAM,MAAc,QAAuB;EAC1C,IACC,KAAK,gBAAA,KACL,KAAK,gBAAA,GAEL;EAED,KAAK,QAAQ;EACb,KAAK,UAAU;EACf,KAAK,cAAA;EACL,KAAK,QAAQ;EACb,KAAK,OAAA,GAA+B,KAAK,aAAa,MAAM,MAAM,CAAC;EACnE,KAAK,QAAQ,UAAU;GACtB,IAAI,CAAC,KAAK,QAAQ,WAAW,KAAK,QAAQ,QAAQ;GAIlD,aAAa,KAAK,WAAW;GAC7B,KAAK,cAAc,KAAA;EACpB,CAAC;EACD,KAAK,iBAAiB,KAAA;EACtB,KAAK,aAAa,CAAC;EACnB,KAAK,iBAAiB;EACtB,KAAK,QAAQ;EACb,KAAK,cAAc,iBAAiB;GACnC,IAAI,CAAC,KAAK,QAAQ,WAAW,KAAK,QAAQ,QAAQ;EACnD,GAAG,yBAAyB;EAC5B,KAAK,YAAY,MAAM;CACxB;CAGA,UAAgB;EACf,IAAI,KAAK,WAAW;EACpB,KAAK,YAAY;EACjB,KAAK,QAAQ,IAAI,QAAQ,KAAK,aAAa;EAC3C,KAAK,QAAQ,IAAI,SAAS,KAAK,cAAc;EAC7C,KAAK,QAAQ,IAAI,SAAS,KAAK,cAAc;EAE7C,KAAK,QAAQ,GAAG,eAAe,KAAA,CAAS;CACzC;CAIA,OAAO,QAAgB,SAAuB;EAC7C,IAAI,KAAK,QAAQ,WAAW;EAC5B,KAAK,QAAQ,MAAM,qBAAqB,QAAQ,SAAS,EAAE,QAAQ,KAAK,QAAQ,CAAC,CAAC;CACnF;CAIA,aAAa,MAA0B,QAAoC;EAC1E,IAAI,SAAS,KAAA,GAAW,OAAO,OAAO,MAAM,CAAC;EAC7C,MAAM,OAAO,WAAW,KAAA,IAAY,OAAO,MAAM,CAAC,IAAI,OAAO,KAAK,QAAQ,OAAO;EACjF,MAAM,UAAU,OAAO,MAAM,IAAI,KAAK,MAAM;EAC5C,QAAQ,cAAc,MAAM,CAAC;EAC7B,KAAK,KAAK,SAAS,CAAC;EACpB,OAAO;CACR;CAOA,aAAa,SAA0B;EACtC,IAAI,QAAQ,WAAW,GAAG;GACzB,KAAK,QAAQ,KAAA;GACb,KAAK,UAAU,KAAA;GACf,OAAO;EACR;EACA,IAAI,QAAQ,WAAW,GAAG;GACzB,KAAK,MAAM,wBAAwB;GACnC,OAAO;EACR;EACA,MAAM,OAAO,QAAQ,aAAa,CAAC;EACnC,IAAI,CAAC,YAAY,IAAI,GAAG;GACvB,KAAK,MAAM,wBAAwB;GACnC,OAAO;EACR;EACA,IAAI,QAAQ,WAAW,GAAG;GACzB,KAAK,QAAQ;GACb,KAAK,UAAU,KAAA;GACf,OAAO;EACR;EACA,MAAM,SAAS,UAAU,QAAQ,SAAS,CAAC,CAAC;EAC5C,IAAI,WAAW,KAAA,GAAW;GACzB,KAAK,MAAM,uBAAuB;GAClC,OAAO;EACR;EACA,KAAK,QAAQ;EACb,KAAK,UAAU,OAAO,WAAW,IAAI,KAAA,IAAY;EACjD,OAAO;CACR;CAIA,UAAgB;EACf,IAAI,KAAK,gBAAA,GAAwC;EACjD,KAAK,QAAQ;EACb,aAAa,KAAK,WAAW;EAC7B,KAAK,cAAc,KAAA;EACnB,KAAK,SAAS,oBAAoB,SAAS,KAAK,cAAc;EAC9D,KAAK,cAAA;EACL,KAAK,SAAS,KAAK,SAAS,KAAK,OAAO,KAAK,OAAO;CACrD;CAKA,QAAQ,OAAqB;EAC5B,KAAK,UAAU,OAAO,OAAO,CAAC,KAAK,SAAS,KAAK,CAAC;EAClD,KAAK,OAAO;CACb;CAEA,YAAY,OAAsB;EACjC,IAAI,KAAK,gBAAA,GAAwC;EACjD,MAAM,QAAQ,KAAK,OAAO,KAAK;EAC/B,IAAI,UAAU,KAAA,GAAW;EACzB,KAAK,QAAQ,KAAK;CACnB;CAEA,aAAa,OAAsB;EAClC,KAAK,SAAS,KAAK,SAAS,KAAK;EACjC,KAAK,QAAQ;CACd;CAMA,OAAO,OAAoC;EAC1C,IAAI,OAAO,SAAS,KAAK,GAAG,OAAO;EACnC,IAAI,OAAO,UAAU,UAAU,OAAO,OAAO,KAAK,OAAO,OAAO;CAEjE;AACD;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;AC7eA,SAAgB,oBAAoB,SAAuD;CAC1F,OAAO,IAAI,cAAc,OAAO;AACjC"}