@orkestrel/websocket 0.0.10 → 0.0.11
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.
- package/README.md +8 -3
- package/dist/src/server/index.cjs +317 -205
- package/dist/src/server/index.cjs.map +1 -1
- package/dist/src/server/index.d.cts +202 -109
- package/dist/src/server/index.d.ts +202 -109
- package/dist/src/server/index.js +312 -202
- package/dist/src/server/index.js.map +1 -1
- package/package.json +19 -15
|
@@ -4,7 +4,7 @@ import { EmitterHooks } from '@orkestrel/emitter';
|
|
|
4
4
|
import { EmitterInterface } from '@orkestrel/emitter';
|
|
5
5
|
|
|
6
6
|
/**
|
|
7
|
-
*
|
|
7
|
+
* Computes the `Sec-WebSocket-Accept` response value for an RFC 6455 upgrade.
|
|
8
8
|
*
|
|
9
9
|
* @remarks
|
|
10
10
|
* The base64-encoded SHA-1 of the client's `Sec-WebSocket-Key` concatenated with the
|
|
@@ -17,10 +17,10 @@ import { EmitterInterface } from '@orkestrel/emitter';
|
|
|
17
17
|
export declare function computeWebSocketAccept(key: string): string;
|
|
18
18
|
|
|
19
19
|
/**
|
|
20
|
-
*
|
|
20
|
+
* Creates a server-native WebSocket over a raw upgraded `node:stream` Duplex socket.
|
|
21
21
|
*
|
|
22
22
|
* @remarks
|
|
23
|
-
* The construction entry point for the {@link NodeWebSocketInterface}
|
|
23
|
+
* The construction entry point for the {@link NodeWebSocketInterface}. Pass
|
|
24
24
|
* the upgraded `socket` plus the client's `Sec-WebSocket-Key` as `key` to run in SERVER
|
|
25
25
|
* mode — the wrapper writes the `101 Switching Protocols` handshake and sends unmasked
|
|
26
26
|
* frames; omit `key` for CLIENT mode (no handshake, masked frames). This is the
|
|
@@ -31,6 +31,7 @@ export declare function computeWebSocketAccept(key: string): string;
|
|
|
31
31
|
* @param options - The {@link NodeWebSocketOptions} (`socket`, optional `key` / `head` /
|
|
32
32
|
* `protocol` / `on`)
|
|
33
33
|
* @returns A typed {@link NodeWebSocketInterface}
|
|
34
|
+
* @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`
|
|
34
35
|
*
|
|
35
36
|
* @example
|
|
36
37
|
* ```ts
|
|
@@ -38,9 +39,14 @@ export declare function computeWebSocketAccept(key: string): string;
|
|
|
38
39
|
*
|
|
39
40
|
* // In a node:http 'upgrade' handler — server mode, identified by the client key:
|
|
40
41
|
* server.on('upgrade', (request, socket, head) => {
|
|
42
|
+
* const key = request.headers['sec-websocket-key']
|
|
43
|
+
* if (typeof key !== 'string') {
|
|
44
|
+
* socket.destroy()
|
|
45
|
+
* return
|
|
46
|
+
* }
|
|
41
47
|
* const ws = createNodeWebSocket({
|
|
42
48
|
* socket,
|
|
43
|
-
* key
|
|
49
|
+
* key, // present => server mode + 101 handshake
|
|
44
50
|
* head,
|
|
45
51
|
* on: { message: (text) => ws.send(`echo: ${text}`) },
|
|
46
52
|
* })
|
|
@@ -50,28 +56,29 @@ export declare function computeWebSocketAccept(key: string): string;
|
|
|
50
56
|
export declare function createNodeWebSocket(options: NodeWebSocketOptions): NodeWebSocketInterface;
|
|
51
57
|
|
|
52
58
|
/**
|
|
53
|
-
*
|
|
54
|
-
*
|
|
59
|
+
* Encodes a single RFC 6455 frame to its wire bytes — the inverse of
|
|
60
|
+
* `parseWebSocketFrame`.
|
|
55
61
|
*
|
|
56
62
|
* @remarks
|
|
57
63
|
* Builds a final (FIN-set) frame: byte 0 is `0x80 | opcode`; the payload length uses
|
|
58
64
|
* the 7-bit form below 126, the `126` + 16-bit form below 65 536, or the `127` +
|
|
59
65
|
* 64-bit form beyond; when `masked` is set the mask bit is set, a 4-byte key (supplied
|
|
60
|
-
*
|
|
66
|
+
* through `options.mask`, else random) is written, and the payload is XOR-masked. Server→
|
|
61
67
|
* client frames are unmasked (the default); pass `masked: true` to encode a CLIENT
|
|
62
|
-
* frame (
|
|
63
|
-
* Returns one contiguous `Buffer` (header + payload), so the wrapper writes it
|
|
64
|
-
* single `socket.write`. Pure.
|
|
68
|
+
* frame (for example to feed the parser in a test). A `string` payload is encoded as
|
|
69
|
+
* UTF-8. Returns one contiguous `Buffer` (header + payload), so the wrapper writes it
|
|
70
|
+
* with a single `socket.write`. Pure.
|
|
65
71
|
*
|
|
66
72
|
* @param opcode - The frame opcode (a `WEBSOCKET_OPCODE_*` value)
|
|
67
73
|
* @param payload - The payload, a `Buffer` or a UTF-8 `string`
|
|
68
74
|
* @param options - Masking control ({@link WebSocketEncodeOptions}); defaults to unmasked
|
|
69
75
|
* @returns The complete frame as wire bytes
|
|
76
|
+
* @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`
|
|
70
77
|
*/
|
|
71
78
|
export declare function encodeWebSocketFrame(opcode: number, payload: Buffer | string, options?: WebSocketEncodeOptions): Buffer;
|
|
72
79
|
|
|
73
80
|
/**
|
|
74
|
-
*
|
|
81
|
+
* Checks whether a numeric value is a valid RFC 6455 close status code to RECEIVE (§7.4.1).
|
|
75
82
|
*
|
|
76
83
|
* @remarks
|
|
77
84
|
* True for `1000`–`1003`, `1007`–`1014`, and the application range `3000`–`4999`; false
|
|
@@ -84,7 +91,7 @@ export declare function encodeWebSocketFrame(opcode: number, payload: Buffer | s
|
|
|
84
91
|
* never throws.
|
|
85
92
|
*
|
|
86
93
|
* @param code - The close status code to validate
|
|
87
|
-
* @returns
|
|
94
|
+
* @returns True if `code` is a valid RFC 6455 close code; false otherwise
|
|
88
95
|
*
|
|
89
96
|
* @example
|
|
90
97
|
* ```ts
|
|
@@ -94,25 +101,26 @@ export declare function encodeWebSocketFrame(opcode: number, payload: Buffer | s
|
|
|
94
101
|
export declare function isCloseCode(code: number): boolean;
|
|
95
102
|
|
|
96
103
|
/**
|
|
97
|
-
*
|
|
98
|
-
*
|
|
99
|
-
* @remarks
|
|
100
|
-
* Returns `undefined` until the complete length prefix is buffered. The 16-bit form
|
|
101
|
-
* is canonical only for lengths at least 126; the 64-bit form only for lengths at
|
|
102
|
-
* least 65,536 and with its most-significant bit clear (RFC 6455 §5.2).
|
|
104
|
+
* Checks whether a value is a {@link WebSocketError}.
|
|
103
105
|
*
|
|
104
|
-
* @param
|
|
105
|
-
* @returns
|
|
106
|
+
* @param value - The value to test (typically a `catch` binding)
|
|
107
|
+
* @returns True if `value` is a `WebSocketError`; false otherwise
|
|
106
108
|
*
|
|
107
109
|
* @example
|
|
108
110
|
* ```ts
|
|
109
|
-
*
|
|
111
|
+
* import { isWebSocketError } from '@src/server'
|
|
112
|
+
*
|
|
113
|
+
* try {
|
|
114
|
+
* ws.close(1000.5)
|
|
115
|
+
* } catch (error) {
|
|
116
|
+
* if (isWebSocketError(error) && error.code === 'CLOSE') ws.close()
|
|
117
|
+
* }
|
|
110
118
|
* ```
|
|
111
119
|
*/
|
|
112
|
-
export declare function
|
|
120
|
+
export declare function isWebSocketError(value: unknown): value is WebSocketError;
|
|
113
121
|
|
|
114
122
|
/**
|
|
115
|
-
*
|
|
123
|
+
* Checks whether a value is a canonical RFC 6455 `Sec-WebSocket-Key`.
|
|
116
124
|
*
|
|
117
125
|
* @remarks
|
|
118
126
|
* A valid key is exactly 16 random bytes encoded as 24 characters of base64, ending
|
|
@@ -120,7 +128,7 @@ export declare function isWebSocketFrameCanonical(buffer: Buffer): boolean | und
|
|
|
120
128
|
* malformed or non-canonical encodings return `false`; nothing is thrown.
|
|
121
129
|
*
|
|
122
130
|
* @param key - The proposed `Sec-WebSocket-Key` header value
|
|
123
|
-
* @returns
|
|
131
|
+
* @returns True if `key` is the canonical base64 encoding of 16 bytes; false otherwise
|
|
124
132
|
*
|
|
125
133
|
* @example
|
|
126
134
|
* ```ts
|
|
@@ -131,7 +139,7 @@ export declare function isWebSocketFrameCanonical(buffer: Buffer): boolean | und
|
|
|
131
139
|
export declare function isWebSocketKey(key: string): boolean;
|
|
132
140
|
|
|
133
141
|
/**
|
|
134
|
-
*
|
|
142
|
+
* Checks whether a value is one valid WebSocket subprotocol token.
|
|
135
143
|
*
|
|
136
144
|
* @remarks
|
|
137
145
|
* Subprotocols use the HTTP `token` grammar. Whitespace, separators, commas, and
|
|
@@ -139,22 +147,42 @@ export declare function isWebSocketKey(key: string): boolean;
|
|
|
139
147
|
* second handshake header.
|
|
140
148
|
*
|
|
141
149
|
* @param protocol - The negotiated subprotocol to validate
|
|
142
|
-
* @returns
|
|
150
|
+
* @returns True if `protocol` is one non-empty HTTP token; false otherwise
|
|
143
151
|
*
|
|
144
152
|
* @example
|
|
145
153
|
* ```ts
|
|
146
|
-
* if (!isWebSocketProtocol(protocol))
|
|
154
|
+
* if (!isWebSocketProtocol(protocol)) socket.destroy()
|
|
147
155
|
* ```
|
|
148
156
|
*/
|
|
149
157
|
export declare function isWebSocketProtocol(protocol: string): boolean;
|
|
150
158
|
|
|
151
159
|
/**
|
|
152
|
-
*
|
|
160
|
+
* Checks whether the next frame uses the shortest valid RFC 6455 payload-length encoding.
|
|
161
|
+
*
|
|
162
|
+
* @remarks
|
|
163
|
+
* Returns `undefined` until the complete length prefix is buffered. The 16-bit form
|
|
164
|
+
* is canonical only for lengths at least 126; the 64-bit form only for lengths at
|
|
165
|
+
* least 65,536 and with its most-significant bit clear (RFC 6455 §5.2). Reads the same
|
|
166
|
+
* length prefix as {@link measureWebSocketFrame}, under the same incomplete-buffer
|
|
167
|
+
* contract. Pure; never throws.
|
|
168
|
+
*
|
|
169
|
+
* @param buffer - The accumulation buffer containing the next frame header
|
|
170
|
+
* @returns Its canonicality, or `undefined` while the length prefix is incomplete
|
|
171
|
+
*
|
|
172
|
+
* @example
|
|
173
|
+
* ```ts
|
|
174
|
+
* if (matchesWebSocketCanonical(buffer) === false) fail(WEBSOCKET_CLOSE_PROTOCOL)
|
|
175
|
+
* ```
|
|
176
|
+
*/
|
|
177
|
+
export declare function matchesWebSocketCanonical(buffer: Buffer): boolean | undefined;
|
|
178
|
+
|
|
179
|
+
/**
|
|
180
|
+
* Reads the declared payload length off the front of a buffer, without buffering or
|
|
153
181
|
* reading the payload itself.
|
|
154
182
|
*
|
|
155
183
|
* @remarks
|
|
156
184
|
* Decodes only byte 1's 7-bit length field, extended by the 16-bit (`126`) or 64-bit
|
|
157
|
-
* (`127`) form exactly like
|
|
185
|
+
* (`127`) form exactly like `parseWebSocketFrame` — but stops there, so a caller
|
|
158
186
|
* can reject an over-cap frame the moment its length is known, before the payload
|
|
159
187
|
* bytes have even arrived. Returns `undefined` until the length field itself is fully
|
|
160
188
|
* buffered (mirrors the parser's incomplete-buffer contract). Pure; never throws.
|
|
@@ -165,13 +193,13 @@ export declare function isWebSocketProtocol(protocol: string): boolean;
|
|
|
165
193
|
* @example
|
|
166
194
|
* ```ts
|
|
167
195
|
* const declared = measureWebSocketFrame(buffer)
|
|
168
|
-
* if (declared !== undefined && declared > limit) fail(
|
|
196
|
+
* if (declared !== undefined && declared > limit) fail(WEBSOCKET_CLOSE_TOO_BIG)
|
|
169
197
|
* ```
|
|
170
198
|
*/
|
|
171
199
|
export declare function measureWebSocketFrame(buffer: Buffer): number | undefined;
|
|
172
200
|
|
|
173
201
|
/**
|
|
174
|
-
*
|
|
202
|
+
* Represents a server-native WebSocket over a raw upgraded `node:stream` Duplex — the lean
|
|
175
203
|
* wrapper around the RFC 6455 wire protocol.
|
|
176
204
|
*
|
|
177
205
|
* @remarks
|
|
@@ -184,34 +212,59 @@ export declare function measureWebSocketFrame(buffer: Buffer): number | undefine
|
|
|
184
212
|
* `fin: false` frames — decodes to UTF-8 and emits `message`; a PING is auto-answered
|
|
185
213
|
* with a PONG and emits `ping`; a PONG emits `pong`; a CLOSE is echoed and ends the
|
|
186
214
|
* socket, emitting `close`. `send` writes a text frame, `ping` a ping, `close` a close
|
|
187
|
-
* frame; `destroy` tears down immediately. It owns a typed `#emitter`
|
|
188
|
-
* isolates a throwing listener and routes the error to its own `error` handler
|
|
189
|
-
* option) — the socket never crashes. An underlying socket error emits the
|
|
190
|
-
* `error` event and terminates the wrapper. The untyped socket `data` is narrowed
|
|
191
|
-
* `Buffer` with a guard, never an assertion
|
|
215
|
+
* frame; `destroy` tears down immediately. It owns a typed `#emitter` by composition, and
|
|
216
|
+
* the emitter isolates a throwing listener and routes the error to its own `error` handler
|
|
217
|
+
* (the `error` option) — the socket never crashes. An underlying socket error emits the
|
|
218
|
+
* domain `error` event and terminates the wrapper. The untyped socket `data` is narrowed
|
|
219
|
+
* to a `Buffer` with a guard, never an assertion.
|
|
220
|
+
*
|
|
221
|
+
* @example
|
|
222
|
+
* ```ts
|
|
223
|
+
* import { NodeWebSocket } from '@src/server'
|
|
224
|
+
*
|
|
225
|
+
* // In a node:http 'upgrade' handler, over the socket the server already handed over:
|
|
226
|
+
* const key = request.headers['sec-websocket-key']
|
|
227
|
+
* if (typeof key !== 'string') {
|
|
228
|
+
* socket.destroy()
|
|
229
|
+
* return
|
|
230
|
+
* }
|
|
231
|
+
* const ws = new NodeWebSocket({ socket, key, head })
|
|
232
|
+
* ws.emitter.on('message', (text) => ws.send(`echo: ${text}`))
|
|
233
|
+
* ```
|
|
192
234
|
*/
|
|
193
235
|
export declare class NodeWebSocket implements NodeWebSocketInterface {
|
|
194
236
|
#private;
|
|
237
|
+
/**
|
|
238
|
+
* Creates a WebSocket wrapper over an already-upgraded Duplex socket.
|
|
239
|
+
*
|
|
240
|
+
* @remarks
|
|
241
|
+
* `key` selects the mode: present runs SERVER mode and writes the `101 Switching
|
|
242
|
+
* Protocols` handshake, omitted runs CLIENT mode and masks every outgoing frame.
|
|
243
|
+
* {@link NodeWebSocketOptions} describes every member.
|
|
244
|
+
*
|
|
245
|
+
* @param options - The {@link NodeWebSocketOptions} the wrapper is built from
|
|
246
|
+
* @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`
|
|
247
|
+
*/
|
|
195
248
|
constructor(options: NodeWebSocketOptions);
|
|
196
249
|
get emitter(): EmitterInterface<NodeWebSocketEventMap>;
|
|
197
250
|
get readyState(): WebSocketReadyState;
|
|
198
|
-
send(
|
|
199
|
-
ping(
|
|
251
|
+
send(message: string): void;
|
|
252
|
+
ping(payload?: string): void;
|
|
200
253
|
close(code?: number, reason?: string): void;
|
|
201
254
|
destroy(): void;
|
|
202
255
|
}
|
|
203
256
|
|
|
204
257
|
/**
|
|
205
|
-
*
|
|
258
|
+
* Represents the event map of a {@link NodeWebSocketInterface}.
|
|
206
259
|
*
|
|
207
260
|
* @remarks
|
|
208
261
|
* `open` — the handshake completed and the socket is ready. `message` — a text frame
|
|
209
|
-
* arrived (its decoded UTF-8 string). `close` — the connection ended
|
|
210
|
-
*
|
|
211
|
-
* event and then terminates the wrapper).
|
|
212
|
-
* (a ping is auto-answered with a pong).
|
|
213
|
-
* Listener isolation is the emitter's
|
|
214
|
-
*
|
|
262
|
+
* arrived (its decoded UTF-8 string). `close` — the connection ended, carrying the
|
|
263
|
+
* labeled `[code, reason]` tuple (each `undefined` when the peer sent none). `error` —
|
|
264
|
+
* the underlying socket faulted (a DOMAIN event and then terminates the wrapper).
|
|
265
|
+
* `ping` / `pong` — a control frame arrived (a ping is auto-answered with a pong).
|
|
266
|
+
* Listener isolation is the emitter's: a listener throw is routed to the emitter's
|
|
267
|
+
* `error` handler (the `error` option), never onto this map, so a buggy observer
|
|
215
268
|
* never breaks the socket.
|
|
216
269
|
*/
|
|
217
270
|
export declare type NodeWebSocketEventMap = {
|
|
@@ -224,7 +277,7 @@ export declare type NodeWebSocketEventMap = {
|
|
|
224
277
|
};
|
|
225
278
|
|
|
226
279
|
/**
|
|
227
|
-
*
|
|
280
|
+
* Represents a server-native WebSocket over a raw upgraded socket — the behavioral contract.
|
|
228
281
|
*
|
|
229
282
|
* @remarks
|
|
230
283
|
* Created by `createNodeWebSocket`. In server mode it writes the RFC 6455 handshake
|
|
@@ -234,20 +287,24 @@ export declare type NodeWebSocketEventMap = {
|
|
|
234
287
|
* frame is echoed and ends the socket, emitting `close`. `send` writes a text frame;
|
|
235
288
|
* `ping` writes a ping; `close` writes a close frame (the 2-byte code + optional
|
|
236
289
|
* reason); `destroy` tears the socket down immediately. `readyState` tracks the
|
|
237
|
-
* lifecycle. It owns a typed `emitter`
|
|
290
|
+
* lifecycle. It owns a typed `emitter` by composition and never throws on a faulty
|
|
238
291
|
* listener — the emitter routes it to its `error` handler (the `error` option).
|
|
292
|
+
* `ping` throws a `LIMIT`-coded `WebSocketError` when its UTF-8 payload exceeds
|
|
293
|
+
* `WEBSOCKET_CONTROL_MAX_LENGTH`; `close` throws a `CLOSE`-coded one for a status code
|
|
294
|
+
* `isCloseCode` refuses and a `LIMIT`-coded one for a reason past
|
|
295
|
+
* `WEBSOCKET_CLOSE_REASON_MAX_LENGTH`, in each case without changing `readyState`.
|
|
239
296
|
*/
|
|
240
297
|
export declare interface NodeWebSocketInterface {
|
|
241
298
|
readonly emitter: EmitterInterface<NodeWebSocketEventMap>;
|
|
242
299
|
readonly readyState: WebSocketReadyState;
|
|
243
|
-
send(
|
|
244
|
-
ping(
|
|
300
|
+
send(message: string): void;
|
|
301
|
+
ping(payload?: string): void;
|
|
245
302
|
close(code?: number, reason?: string): void;
|
|
246
303
|
destroy(): void;
|
|
247
304
|
}
|
|
248
305
|
|
|
249
306
|
/**
|
|
250
|
-
*
|
|
307
|
+
* Represents the options for `createNodeWebSocket`.
|
|
251
308
|
*
|
|
252
309
|
* @remarks
|
|
253
310
|
* `socket` is the upgraded `node:stream` Duplex (the raw TCP stream after the HTTP
|
|
@@ -256,8 +313,8 @@ export declare interface NodeWebSocketInterface {
|
|
|
256
313
|
* frames; omit it for CLIENT mode — no handshake is written and frames are MASKED (RFC
|
|
257
314
|
* 6455 §5.3). `head` is any bytes buffered after the upgrade headers (replayed through
|
|
258
315
|
* the parser). `protocol` is a negotiated subprotocol to echo in the handshake. `on`
|
|
259
|
-
* wires initial listeners at construction
|
|
260
|
-
* emitter's listener-error handler
|
|
316
|
+
* wires initial listeners at construction — the reserved `on` option; `error` is the
|
|
317
|
+
* emitter's listener-error handler, where a listener throw routes. `payload` caps
|
|
261
318
|
* both a single inbound frame's declared length AND the total bytes of a reassembled
|
|
262
319
|
* fragmented message (default `WEBSOCKET_MAX_PAYLOAD`) — a breach closes 1009. `timeout`
|
|
263
320
|
* is how long the wrapper waits, after sending a close frame, for the peer's echo before
|
|
@@ -265,7 +322,8 @@ export declare interface NodeWebSocketInterface {
|
|
|
265
322
|
* is the external cancellation seam — on abort the socket destroys; composes with the
|
|
266
323
|
* line's `@orkestrel/abort` and `@orkestrel/timeout` primitives, which expose native
|
|
267
324
|
* `AbortSignal`s. An already-aborted signal tears the socket down immediately after
|
|
268
|
-
* construction.
|
|
325
|
+
* construction. A refused member throws an `OPTION`-coded `WebSocketError` before the
|
|
326
|
+
* wrapper writes to or assumes ownership of the `socket`.
|
|
269
327
|
*/
|
|
270
328
|
export declare interface NodeWebSocketOptions {
|
|
271
329
|
readonly socket: Duplex;
|
|
@@ -273,7 +331,7 @@ export declare interface NodeWebSocketOptions {
|
|
|
273
331
|
readonly head?: Buffer;
|
|
274
332
|
readonly protocol?: string;
|
|
275
333
|
readonly on?: EmitterHooks<NodeWebSocketEventMap>;
|
|
276
|
-
/**
|
|
334
|
+
/** Holds the emitter's listener-error handler — a listener throw routes here, not to a domain event. */
|
|
277
335
|
readonly error?: EmitterErrorHandler;
|
|
278
336
|
readonly payload?: number;
|
|
279
337
|
readonly timeout?: number;
|
|
@@ -281,12 +339,12 @@ export declare interface NodeWebSocketOptions {
|
|
|
281
339
|
}
|
|
282
340
|
|
|
283
341
|
/**
|
|
284
|
-
*
|
|
342
|
+
* Decodes a byte sequence as strict UTF-8, or signals it is malformed.
|
|
285
343
|
*
|
|
286
344
|
* @remarks
|
|
287
345
|
* Wraps `TextDecoder('utf-8', { fatal: true })` in a try/catch so a malformed sequence
|
|
288
|
-
* returns `undefined` instead of throwing
|
|
289
|
-
*
|
|
346
|
+
* returns `undefined` instead of throwing — a guard-adjacent coercer never throws on bad
|
|
347
|
+
* input. Pure.
|
|
290
348
|
*
|
|
291
349
|
* @param bytes - The raw bytes to decode
|
|
292
350
|
* @returns The decoded string, or `undefined` when `bytes` is not valid UTF-8
|
|
@@ -300,7 +358,7 @@ export declare interface NodeWebSocketOptions {
|
|
|
300
358
|
export declare function parseUTF8(bytes: Buffer): string | undefined;
|
|
301
359
|
|
|
302
360
|
/**
|
|
303
|
-
*
|
|
361
|
+
* Decodes a single RFC 6455 frame from the front of a buffer.
|
|
304
362
|
*
|
|
305
363
|
* @remarks
|
|
306
364
|
* Reads the FIN bit and opcode (byte 0), the mask bit and 7-bit payload length (byte
|
|
@@ -310,44 +368,49 @@ export declare function parseUTF8(bytes: Buffer): string | undefined;
|
|
|
310
368
|
* 6455 §5.3; an unmasked frame still decodes, leaving the payload as-is, so the caller
|
|
311
369
|
* can enforce policy). Returns `undefined` the moment the buffer is too short for the
|
|
312
370
|
* part it is up to (the length prefix, the mask, or the full payload) — the signal to
|
|
313
|
-
* the caller to read more bytes and retry
|
|
314
|
-
*
|
|
315
|
-
* remainder. Pure; never throws on a short buffer.
|
|
371
|
+
* the caller to read more bytes and retry. `consumed` is the total bytes the frame
|
|
372
|
+
* occupied, so the caller slices the remainder. Pure; never throws on a short buffer.
|
|
316
373
|
*
|
|
317
374
|
* @param buffer - The accumulation buffer to decode the next frame from
|
|
318
375
|
* @returns The parsed {@link WebSocketFrame}, or `undefined` when the buffer is incomplete
|
|
376
|
+
*
|
|
377
|
+
* @example
|
|
378
|
+
* ```ts
|
|
379
|
+
* const frame = parseWebSocketFrame(buffer)
|
|
380
|
+
* if (frame === undefined) return // incomplete — wait for more bytes
|
|
381
|
+
* ```
|
|
319
382
|
*/
|
|
320
383
|
export declare function parseWebSocketFrame(buffer: Buffer): WebSocketFrame | undefined;
|
|
321
384
|
|
|
322
|
-
/**
|
|
385
|
+
/** Names the invalid-frame-payload-data status code (RFC 6455 §7.4.1) — for example non-UTF-8 text or an unparseable close reason. */
|
|
323
386
|
export declare const WEBSOCKET_CLOSE_INVALID = 1007;
|
|
324
387
|
|
|
325
|
-
/**
|
|
388
|
+
/** Names the normal-closure status code (RFC 6455 §7.4.1) — the default `close` code. */
|
|
326
389
|
export declare const WEBSOCKET_CLOSE_NORMAL = 1000;
|
|
327
390
|
|
|
328
|
-
/**
|
|
391
|
+
/** Names the protocol-error status code (RFC 6455 §7.4.1) — a framing/state rule was violated. */
|
|
329
392
|
export declare const WEBSOCKET_CLOSE_PROTOCOL = 1002;
|
|
330
393
|
|
|
331
|
-
/**
|
|
332
|
-
export declare const
|
|
394
|
+
/** Names the maximum UTF-8 close-reason length after the two-byte status code. */
|
|
395
|
+
export declare const WEBSOCKET_CLOSE_REASON_MAX_LENGTH: number;
|
|
333
396
|
|
|
334
|
-
/**
|
|
397
|
+
/** Names the default close-handshake timeout in milliseconds — how long `close()` waits for the peer's echo before tearing the socket down. */
|
|
335
398
|
export declare const WEBSOCKET_CLOSE_TIMEOUT_MS = 30000;
|
|
336
399
|
|
|
337
|
-
/**
|
|
338
|
-
export declare const
|
|
400
|
+
/** Names the message-too-big status code (RFC 6455 §7.4.1) — a reassembled message exceeded the payload cap. */
|
|
401
|
+
export declare const WEBSOCKET_CLOSE_TOO_BIG = 1009;
|
|
339
402
|
|
|
340
|
-
/**
|
|
403
|
+
/** Names the unsupported-data status code (RFC 6455 §7.4.1) — the endpoint received a data type it cannot accept, for example binary on a text-only endpoint. */
|
|
341
404
|
export declare const WEBSOCKET_CLOSE_UNSUPPORTED = 1003;
|
|
342
405
|
|
|
343
|
-
/**
|
|
344
|
-
export declare const
|
|
406
|
+
/** Names the maximum control-frame payload length in bytes (RFC 6455 §5.5). */
|
|
407
|
+
export declare const WEBSOCKET_CONTROL_MAX_LENGTH = 125;
|
|
345
408
|
|
|
346
|
-
/**
|
|
409
|
+
/** Names 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). */
|
|
347
410
|
export declare const WEBSOCKET_FAIL_TIMEOUT_MS = 1000;
|
|
348
411
|
|
|
349
412
|
/**
|
|
350
|
-
*
|
|
413
|
+
* Names the RFC 6455 GUID concatenated to a client's `Sec-WebSocket-Key` before the SHA-1
|
|
351
414
|
* hash that yields the `Sec-WebSocket-Accept` response value.
|
|
352
415
|
*
|
|
353
416
|
* @remarks
|
|
@@ -356,59 +419,44 @@ export declare const WEBSOCKET_FAIL_TIMEOUT_MS = 1000;
|
|
|
356
419
|
*/
|
|
357
420
|
export declare const WEBSOCKET_GUID = "258EAFA5-E914-47DA-95CA-C5AB0DC85B11";
|
|
358
421
|
|
|
359
|
-
/**
|
|
422
|
+
/** Names the default maximum inbound single-frame length AND reassembled-message total byte count (100 MiB — the `ws` package default). */
|
|
360
423
|
export declare const WEBSOCKET_MAX_PAYLOAD = 104857600;
|
|
361
424
|
|
|
362
|
-
/**
|
|
425
|
+
/** Names the binary frame opcode — a raw byte payload (RFC 6455 §5.6). */
|
|
363
426
|
export declare const WEBSOCKET_OPCODE_BINARY = 2;
|
|
364
427
|
|
|
365
|
-
/**
|
|
428
|
+
/** Names the close frame opcode — a control frame ending the connection (RFC 6455 §5.5.1). */
|
|
366
429
|
export declare const WEBSOCKET_OPCODE_CLOSE = 8;
|
|
367
430
|
|
|
368
|
-
/**
|
|
431
|
+
/** Names the continuation frame opcode — the next fragment of an open data message (RFC 6455 §5.4). */
|
|
369
432
|
export declare const WEBSOCKET_OPCODE_CONTINUATION = 0;
|
|
370
433
|
|
|
371
|
-
/**
|
|
434
|
+
/** Names the ping frame opcode — a control frame the peer must answer with a pong (RFC 6455 §5.5.2). */
|
|
372
435
|
export declare const WEBSOCKET_OPCODE_PING = 9;
|
|
373
436
|
|
|
374
|
-
/**
|
|
437
|
+
/** Names the pong frame opcode — a control frame answering a ping (RFC 6455 §5.5.3). */
|
|
375
438
|
export declare const WEBSOCKET_OPCODE_PONG = 10;
|
|
376
439
|
|
|
377
|
-
/**
|
|
440
|
+
/** Names the text frame opcode — a UTF-8 payload (RFC 6455 §5.6). */
|
|
378
441
|
export declare const WEBSOCKET_OPCODE_TEXT = 1;
|
|
379
442
|
|
|
380
|
-
/**
|
|
443
|
+
/** Names the ready state for a closed WebSocket (the socket ended). */
|
|
381
444
|
export declare const WEBSOCKET_READY_CLOSED: WebSocketReadyState;
|
|
382
445
|
|
|
383
|
-
/**
|
|
446
|
+
/** Names the ready state for a closing WebSocket (a close frame was sent or received). */
|
|
384
447
|
export declare const WEBSOCKET_READY_CLOSING: WebSocketReadyState;
|
|
385
448
|
|
|
386
|
-
/**
|
|
449
|
+
/** Names the ready state for a connecting WebSocket (before the handshake completes). */
|
|
387
450
|
export declare const WEBSOCKET_READY_CONNECTING: WebSocketReadyState;
|
|
388
451
|
|
|
389
|
-
/**
|
|
452
|
+
/** Names the ready state for an open WebSocket (the handshake completed; frames flow). */
|
|
390
453
|
export declare const WEBSOCKET_READY_OPEN: WebSocketReadyState;
|
|
391
454
|
|
|
392
|
-
/**
|
|
455
|
+
/** Names the WebSocket protocol version this wrapper speaks (`Sec-WebSocket-Version: 13`). */
|
|
393
456
|
export declare const WEBSOCKET_VERSION = "13";
|
|
394
457
|
|
|
395
458
|
/**
|
|
396
|
-
*
|
|
397
|
-
*
|
|
398
|
-
* @remarks
|
|
399
|
-
* `code` is the RFC 6455 close status code (undefined when the peer closed with no
|
|
400
|
-
* payload); `reason` is the optional UTF-8 reason text (undefined when empty).
|
|
401
|
-
*/
|
|
402
|
-
export declare interface WebSocketClose {
|
|
403
|
-
readonly code: number | undefined;
|
|
404
|
-
readonly reason: string | undefined;
|
|
405
|
-
}
|
|
406
|
-
|
|
407
|
-
/** A WebSocket close status code (RFC 6455 §7.4) — e.g. `WEBSOCKET_CLOSE_NORMAL` (1000). */
|
|
408
|
-
export declare type WebSocketCloseCode = number;
|
|
409
|
-
|
|
410
|
-
/**
|
|
411
|
-
* Options for {@link encodeWebSocketFrame} — how a frame is masked on the wire.
|
|
459
|
+
* Represents the options for {@link encodeWebSocketFrame} — how a frame is masked on the wire.
|
|
412
460
|
*
|
|
413
461
|
* @remarks
|
|
414
462
|
* `masked` toggles the mask bit (server→client frames are NOT masked, the default;
|
|
@@ -422,7 +470,57 @@ export declare interface WebSocketEncodeOptions {
|
|
|
422
470
|
}
|
|
423
471
|
|
|
424
472
|
/**
|
|
425
|
-
*
|
|
473
|
+
* Represents an error thrown by the WebSocket wrapper for a refused caller-supplied value.
|
|
474
|
+
*
|
|
475
|
+
* @remarks
|
|
476
|
+
* Carries a {@link WebSocketErrorCode} and an optional `context` record holding the
|
|
477
|
+
* refused value under a key naming it: an `'OPTION'` carries the offending option
|
|
478
|
+
* (`payload`, `timeout`, `key`, or `protocol`), a `'LIMIT'` carries `size` and the
|
|
479
|
+
* `limit` it exceeded, a `'CLOSE'` carries the refused close `code`, and a `'FRAME'`
|
|
480
|
+
* carries `opcode` or the mask's `size`. Narrow a caught value with
|
|
481
|
+
* {@link isWebSocketError}.
|
|
482
|
+
*
|
|
483
|
+
* @example
|
|
484
|
+
* ```ts
|
|
485
|
+
* import { createNodeWebSocket, isWebSocketError } from '@src/server'
|
|
486
|
+
*
|
|
487
|
+
* try {
|
|
488
|
+
* createNodeWebSocket({ socket, key: 'not-base64' })
|
|
489
|
+
* } catch (error) {
|
|
490
|
+
* if (isWebSocketError(error) && error.code === 'OPTION') socket.destroy()
|
|
491
|
+
* }
|
|
492
|
+
* ```
|
|
493
|
+
*/
|
|
494
|
+
export declare class WebSocketError extends Error {
|
|
495
|
+
readonly code: WebSocketErrorCode;
|
|
496
|
+
readonly context?: Readonly<Record<string, unknown>>;
|
|
497
|
+
/**
|
|
498
|
+
* Creates a WebSocket error carrying a machine-readable code.
|
|
499
|
+
*
|
|
500
|
+
* @param code - The machine-readable {@link WebSocketErrorCode} a `catch` branches on
|
|
501
|
+
* @param message - The human-readable description, carried as the `Error` message
|
|
502
|
+
* @param context - The refused value keyed by name; omitted leaves `context` `undefined`
|
|
503
|
+
*/
|
|
504
|
+
constructor(code: WebSocketErrorCode, message: string, context?: Readonly<Record<string, unknown>>);
|
|
505
|
+
}
|
|
506
|
+
|
|
507
|
+
/**
|
|
508
|
+
* Represents the subject an {@link import('./errors.js').WebSocketError} names as refused.
|
|
509
|
+
*
|
|
510
|
+
* @remarks
|
|
511
|
+
* `OPTION` — a {@link NodeWebSocketOptions} member was refused at construction
|
|
512
|
+
* (`payload`, `timeout`, `key`, `protocol`, or a `protocol` given without a server
|
|
513
|
+
* `key`). `LIMIT` — an outbound control-frame payload exceeded its RFC 6455 §5.5 cap
|
|
514
|
+
* (a `ping` payload past `WEBSOCKET_CONTROL_MAX_LENGTH`, a `close` reason past
|
|
515
|
+
* `WEBSOCKET_CLOSE_REASON_MAX_LENGTH`). `CLOSE` — a close status code `isCloseCode` refuses
|
|
516
|
+
* was passed to `close`. `FRAME` — an `encodeWebSocketFrame` frame-header argument was
|
|
517
|
+
* refused (an opcode outside the four-bit wire field, a mask that is not 4 bytes, or a
|
|
518
|
+
* mask supplied without `masked: true`).
|
|
519
|
+
*/
|
|
520
|
+
export declare type WebSocketErrorCode = 'OPTION' | 'LIMIT' | 'CLOSE' | 'FRAME';
|
|
521
|
+
|
|
522
|
+
/**
|
|
523
|
+
* Represents a parsed RFC 6455 frame — the structured result of decoding one frame off the wire.
|
|
426
524
|
*
|
|
427
525
|
* @remarks
|
|
428
526
|
* `fin` is the final-fragment bit (false for a continued fragment); `opcode`
|
|
@@ -444,13 +542,8 @@ export declare interface WebSocketFrame {
|
|
|
444
542
|
readonly rsv: number;
|
|
445
543
|
}
|
|
446
544
|
|
|
447
|
-
/** A decoded text message received from, or to send to, a WebSocket peer. */
|
|
448
|
-
export declare interface WebSocketMessage {
|
|
449
|
-
readonly data: string;
|
|
450
|
-
}
|
|
451
|
-
|
|
452
545
|
/**
|
|
453
|
-
*
|
|
546
|
+
* Represents a WebSocket ready state — the four browser-compatible lifecycle values.
|
|
454
547
|
*
|
|
455
548
|
* @remarks
|
|
456
549
|
* `0` connecting, `1` open, `2` closing, `3` closed — the same numbering the DOM
|