agentfootprint 7.24.0 → 7.25.0

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.
Files changed (71) hide show
  1. package/dist/adapters/hosting/agentcore.js +112 -1
  2. package/dist/adapters/hosting/agentcore.js.map +1 -1
  3. package/dist/esm/adapters/hosting/agentcore.d.ts +50 -1
  4. package/dist/esm/adapters/hosting/agentcore.js +110 -0
  5. package/dist/esm/adapters/hosting/agentcore.js.map +1 -1
  6. package/dist/esm/hosting/errors.d.ts +44 -2
  7. package/dist/esm/hosting/errors.js +63 -0
  8. package/dist/esm/hosting/errors.js.map +1 -1
  9. package/dist/esm/hosting/headers.d.ts +16 -0
  10. package/dist/esm/hosting/headers.js +24 -0
  11. package/dist/esm/hosting/headers.js.map +1 -0
  12. package/dist/esm/hosting/httpHost.d.ts +70 -4
  13. package/dist/esm/hosting/httpHost.js +211 -47
  14. package/dist/esm/hosting/httpHost.js.map +1 -1
  15. package/dist/esm/hosting/index.d.ts +23 -8
  16. package/dist/esm/hosting/index.js +21 -6
  17. package/dist/esm/hosting/index.js.map +1 -1
  18. package/dist/esm/hosting/nodeHost.d.ts +23 -0
  19. package/dist/esm/hosting/nodeHost.js +21 -1
  20. package/dist/esm/hosting/nodeHost.js.map +1 -1
  21. package/dist/esm/hosting/types.d.ts +212 -6
  22. package/dist/esm/hosting/types.js +7 -5
  23. package/dist/esm/hosting/types.js.map +1 -1
  24. package/dist/esm/hosting/webSocketConversation.d.ts +101 -0
  25. package/dist/esm/hosting/webSocketConversation.js +341 -0
  26. package/dist/esm/hosting/webSocketConversation.js.map +1 -0
  27. package/dist/esm/hosting/webSocketFrames.d.ts +164 -0
  28. package/dist/esm/hosting/webSocketFrames.js +284 -0
  29. package/dist/esm/hosting/webSocketFrames.js.map +1 -0
  30. package/dist/esm/hosting-providers.d.ts +7 -2
  31. package/dist/esm/hosting-providers.js +7 -2
  32. package/dist/esm/hosting-providers.js.map +1 -1
  33. package/dist/hosting/errors.js +66 -1
  34. package/dist/hosting/errors.js.map +1 -1
  35. package/dist/hosting/headers.js +28 -0
  36. package/dist/hosting/headers.js.map +1 -0
  37. package/dist/hosting/httpHost.js +211 -47
  38. package/dist/hosting/httpHost.js.map +1 -1
  39. package/dist/hosting/index.js +23 -6
  40. package/dist/hosting/index.js.map +1 -1
  41. package/dist/hosting/nodeHost.js +20 -0
  42. package/dist/hosting/nodeHost.js.map +1 -1
  43. package/dist/hosting/types.js +7 -5
  44. package/dist/hosting/types.js.map +1 -1
  45. package/dist/hosting/webSocketConversation.js +345 -0
  46. package/dist/hosting/webSocketConversation.js.map +1 -0
  47. package/dist/hosting/webSocketFrames.js +297 -0
  48. package/dist/hosting/webSocketFrames.js.map +1 -0
  49. package/dist/hosting-providers.js +8 -2
  50. package/dist/hosting-providers.js.map +1 -1
  51. package/dist/types/adapters/hosting/agentcore.d.ts +50 -1
  52. package/dist/types/adapters/hosting/agentcore.d.ts.map +1 -1
  53. package/dist/types/hosting/errors.d.ts +44 -2
  54. package/dist/types/hosting/errors.d.ts.map +1 -1
  55. package/dist/types/hosting/headers.d.ts +17 -0
  56. package/dist/types/hosting/headers.d.ts.map +1 -0
  57. package/dist/types/hosting/httpHost.d.ts +70 -4
  58. package/dist/types/hosting/httpHost.d.ts.map +1 -1
  59. package/dist/types/hosting/index.d.ts +23 -8
  60. package/dist/types/hosting/index.d.ts.map +1 -1
  61. package/dist/types/hosting/nodeHost.d.ts +23 -0
  62. package/dist/types/hosting/nodeHost.d.ts.map +1 -1
  63. package/dist/types/hosting/types.d.ts +212 -6
  64. package/dist/types/hosting/types.d.ts.map +1 -1
  65. package/dist/types/hosting/webSocketConversation.d.ts +102 -0
  66. package/dist/types/hosting/webSocketConversation.d.ts.map +1 -0
  67. package/dist/types/hosting/webSocketFrames.d.ts +165 -0
  68. package/dist/types/hosting/webSocketFrames.d.ts.map +1 -0
  69. package/dist/types/hosting-providers.d.ts +7 -2
  70. package/dist/types/hosting-providers.d.ts.map +1 -1
  71. package/package.json +1 -1
@@ -0,0 +1,341 @@
1
+ /**
2
+ * hosting/webSocketConversation — one upgraded socket, presented as a
3
+ * {@link HostConversation}.
4
+ *
5
+ * This is the whole adapter side of the conversation port: it takes the
6
+ * `'upgrade'` event a `node:http` server hands it, answers the handshake, and
7
+ * turns the frames flowing over the socket into the six-member port a handler
8
+ * sees. Everything protocol-shaped lives in `webSocketFrames.ts`; everything
9
+ * port-shaped lives in `types.ts`; this file is the join.
10
+ *
11
+ * ── The laws it keeps, all of them pinned by tests ───────────────────────────
12
+ * - **A path this door does not own is not touched.** `node:http` calls EVERY
13
+ * `'upgrade'` listener for every upgrade, exactly as it calls every
14
+ * `'request'` listener — so a caller's own protocol lives beside this one on
15
+ * the same socket. Whether an unclaimed path gets an answer is the caller's
16
+ * business on a server they own, and this door's only on a server it owns.
17
+ * - **Frames that arrive before the handler subscribes are held, up to a
18
+ * declared bound.** An `async` handler that awaits before calling `onFrame`
19
+ * would otherwise lose the far side's opening frame. The bound is in BYTES
20
+ * and overflow ends the conversation with a stated reason, because an
21
+ * unbounded queue somebody else fills is a way to kill this process, and an
22
+ * undeclared ceiling is the exact thing the declared-ceilings rule exists to
23
+ * forbid.
24
+ * - **`close()` ends every live conversation before the socket is released.**
25
+ * Measured, not assumed: an upgraded socket keeps `server.close()` waiting
26
+ * forever, so a door that let go of its conversations would hang the whole
27
+ * shutdown.
28
+ * - **The port's frame is the whole message.** A message the transport
29
+ * delivered in fragments counts against `maxFrameBytes` in total, so
30
+ * fragmentation cannot be used to walk around the ceiling.
31
+ *
32
+ * Pattern: Adapter. Role: outer ring, one transport.
33
+ */
34
+ import { ConversationClosedError, FrameTooLargeError } from './errors.js';
35
+ import { lowerCasedHeaders } from './headers.js';
36
+ import { CLOSE_CODE, decodeClose, decodeText, encodeClose, encodeFrame, encodeText, FrameProtocolError, FrameReader, isControlFrame, OPCODE, handshakeResponse, } from './webSocketFrames.js';
37
+ /** How long a politely-closed socket has to say goodbye before it is dropped. */
38
+ const CLOSE_GRACE_MS = 1_000;
39
+ export function conversationDoor(options) {
40
+ const live = new Set();
41
+ return {
42
+ get liveCount() {
43
+ return live.size;
44
+ },
45
+ handleUpgrade(request, socket) {
46
+ const url = request.url ?? '';
47
+ if (url.split('?')[0] !== options.path)
48
+ return false;
49
+ // From here the upgrade is ours, and every exit answers it.
50
+ socket.on('error', () => undefined);
51
+ if (!options.accepting()) {
52
+ refuse(socket, 503, `the '${options.hostName}' host is closed and is not taking new conversations`);
53
+ return true;
54
+ }
55
+ const headers = lowerCasedHeaders(request.headers);
56
+ const key = headers['sec-websocket-key'];
57
+ const upgrade = (headers.upgrade ?? '').toLowerCase();
58
+ if (upgrade !== 'websocket' || headers['sec-websocket-version'] !== '13' || !key) {
59
+ refuse(socket, 400, `the '${options.hostName}' host serves version-13 WebSocket conversations on ` +
60
+ `${options.path}, and this upgrade did not offer one`);
61
+ return true;
62
+ }
63
+ const facts = {
64
+ headers,
65
+ query: new URLSearchParams(url.split('?')[1] ?? ''),
66
+ };
67
+ const read = options.readConversation?.(facts) ?? {};
68
+ socket.write(handshakeResponse(key, read.protocol));
69
+ const conversation = openConversation(socket, {
70
+ hostName: options.hostName,
71
+ limits: options.limits,
72
+ ...(read.sessionId !== undefined && { sessionId: read.sessionId }),
73
+ headers: { ...headers, ...read.headers },
74
+ });
75
+ live.add(conversation);
76
+ void conversation.ended.finally(() => live.delete(conversation));
77
+ // A handler that throws ends THAT conversation and nothing else: one
78
+ // caller's bad frame is not an outage for everyone else on this door.
79
+ void (async () => {
80
+ try {
81
+ await options.handler(conversation.port);
82
+ }
83
+ catch (err) {
84
+ conversation.end(`the conversation handler threw: ${asMessage(err)}`);
85
+ }
86
+ })();
87
+ return true;
88
+ },
89
+ async closeAll(reason) {
90
+ const ending = [...live];
91
+ for (const conversation of ending)
92
+ conversation.end(reason);
93
+ // Waited on, not fired and forgotten: an upgraded socket that is still
94
+ // open keeps `server.close()` waiting forever, so "the door is closed"
95
+ // has to mean the sockets are actually gone.
96
+ await Promise.allSettled(ending.map((conversation) => conversation.ended));
97
+ },
98
+ };
99
+ }
100
+ /** Answer an upgrade we will not carry, in the one language a pre-101 socket speaks. */
101
+ function refuse(socket, status, message) {
102
+ const text = `[hosting] ${message}.`;
103
+ const reason = status === 503 ? 'Service Unavailable' : 'Bad Request';
104
+ socket.end(`HTTP/1.1 ${status} ${reason}\r\n` +
105
+ `content-type: text/plain\r\n` +
106
+ `content-length: ${Buffer.byteLength(text)}\r\n` +
107
+ `connection: close\r\n\r\n${text}`);
108
+ }
109
+ /**
110
+ * One socket, presented as a conversation.
111
+ *
112
+ * Written as a closure rather than a class because everything here is one
113
+ * socket's state and none of it is anybody else's business — there is nothing
114
+ * to subclass and nothing to inject.
115
+ */
116
+ function openConversation(socket, options) {
117
+ const { hostName, limits, sessionId } = options;
118
+ const maxFrameBytes = limits.maxFrameBytes;
119
+ const maxPendingBytes = limits.maxPendingBytes;
120
+ const reader = new FrameReader(maxFrameBytes);
121
+ const frameSubscribers = new Set();
122
+ const closeSubscribers = new Set();
123
+ /** Frames the far side sent before anybody was listening. Bounded (see below). */
124
+ let pending = [];
125
+ let pendingBytes = 0;
126
+ /** The message being assembled out of fragments, if any. */
127
+ let assembling = [];
128
+ let assemblingBytes = 0;
129
+ let assemblingText = false;
130
+ let ending;
131
+ let resolveEnded;
132
+ const ended = new Promise((resolve) => {
133
+ resolveEnded = resolve;
134
+ });
135
+ function finish(close, wireCode, wireReason) {
136
+ if (ending !== undefined)
137
+ return;
138
+ ending = close;
139
+ if (wireCode !== undefined && socket.writable) {
140
+ socket.write(encodeClose(wireCode, wireReason ?? close.reason));
141
+ }
142
+ // end() flushes what is queued and then sends FIN — the polite half of the
143
+ // close. The timer is the impolite half, for a peer that never answers.
144
+ socket.end();
145
+ const grace = setTimeout(() => socket.destroy(), CLOSE_GRACE_MS);
146
+ if (typeof grace.unref === 'function')
147
+ grace.unref();
148
+ socket.once('close', () => clearTimeout(grace));
149
+ const subscribers = [...closeSubscribers];
150
+ closeSubscribers.clear();
151
+ for (const cb of subscribers) {
152
+ try {
153
+ cb(close);
154
+ }
155
+ catch {
156
+ // A close subscriber that throws has nothing left to be told; the
157
+ // conversation is already over and there is nothing to protect.
158
+ }
159
+ }
160
+ resolveEnded();
161
+ }
162
+ function deliver(frame) {
163
+ if (ending !== undefined)
164
+ return;
165
+ if (frameSubscribers.size === 0) {
166
+ pendingBytes += Buffer.byteLength(frame, 'utf8');
167
+ if (maxPendingBytes !== undefined && pendingBytes > maxPendingBytes) {
168
+ pending = [];
169
+ finish({
170
+ by: 'host',
171
+ reason: `${pendingBytes} bytes arrived before onFrame(...) was subscribed, past the ` +
172
+ `declared maxPendingBytes of ${maxPendingBytes}`,
173
+ }, CLOSE_CODE.tooBig);
174
+ return;
175
+ }
176
+ pending.push(frame);
177
+ return;
178
+ }
179
+ for (const cb of [...frameSubscribers]) {
180
+ try {
181
+ cb(frame);
182
+ }
183
+ catch (err) {
184
+ // Feeding more frames to a consumer whose parser already threw is how a
185
+ // channel keeps looking healthy while nothing on it is being read.
186
+ finish({ by: 'host', reason: `a frame subscriber threw: ${asMessage(err)}` });
187
+ return;
188
+ }
189
+ }
190
+ }
191
+ function handleFrame(frame) {
192
+ if (ending !== undefined)
193
+ return;
194
+ if (isControlFrame(frame.opcode)) {
195
+ if (frame.opcode === OPCODE.ping) {
196
+ // Answered because the protocol says so, and because a peer's liveness
197
+ // check is not the same thing as this port inventing a heartbeat.
198
+ if (socket.writable)
199
+ socket.write(encodeFrame(OPCODE.pong, frame.payload));
200
+ return;
201
+ }
202
+ if (frame.opcode === OPCODE.pong)
203
+ return;
204
+ const said = decodeClose(frame.payload);
205
+ finish({ by: 'far-side', ...(said.reason !== undefined && { reason: said.reason }) }, said.code === CLOSE_CODE.noStatus ? CLOSE_CODE.normal : said.code, said.reason);
206
+ return;
207
+ }
208
+ if (frame.opcode === OPCODE.binary) {
209
+ // Refused by name rather than silently stringified: this port carries
210
+ // text, and binary is a capability nobody has shown evidence for yet.
211
+ finish({
212
+ by: 'host',
213
+ reason: 'a binary frame arrived, and this port carries text frames only — binary is a ' +
214
+ 'capability that will be minted when a consumer needs it, not guessed at now',
215
+ }, CLOSE_CODE.unsupportedData);
216
+ return;
217
+ }
218
+ if (frame.opcode === OPCODE.text) {
219
+ if (assemblingText) {
220
+ protocolFailure(new FrameProtocolError(CLOSE_CODE.protocolError, 'a new message started before the previous fragmented one finished'));
221
+ return;
222
+ }
223
+ assemblingText = true;
224
+ }
225
+ else if (!assemblingText) {
226
+ protocolFailure(new FrameProtocolError(CLOSE_CODE.protocolError, 'a continuation frame arrived with no message to continue'));
227
+ return;
228
+ }
229
+ assemblingBytes += frame.payload.length;
230
+ if (maxFrameBytes !== undefined && assemblingBytes > maxFrameBytes) {
231
+ // The ceiling is on the MESSAGE, so a peer cannot fragment its way past it.
232
+ protocolFailure(new FrameProtocolError(CLOSE_CODE.tooBig, `a fragmented message reached ${assemblingBytes} bytes, past the declared ` +
233
+ `maxFrameBytes of ${maxFrameBytes}`));
234
+ return;
235
+ }
236
+ assembling.push(frame.payload);
237
+ if (!frame.fin)
238
+ return;
239
+ const whole = assembling.length === 1 ? assembling[0] : Buffer.concat(assembling);
240
+ assembling = [];
241
+ assemblingBytes = 0;
242
+ assemblingText = false;
243
+ let text;
244
+ try {
245
+ text = decodeText(whole);
246
+ }
247
+ catch (err) {
248
+ protocolFailure(err);
249
+ return;
250
+ }
251
+ deliver(text);
252
+ }
253
+ function protocolFailure(err) {
254
+ const code = err instanceof FrameProtocolError ? err.closeCode : CLOSE_CODE.protocolError;
255
+ finish({ by: 'far-side', reason: asMessage(err) }, code, asMessage(err));
256
+ }
257
+ socket.on('data', (chunk) => {
258
+ if (ending !== undefined)
259
+ return;
260
+ let frames;
261
+ try {
262
+ frames = reader.push(chunk);
263
+ }
264
+ catch (err) {
265
+ protocolFailure(err);
266
+ return;
267
+ }
268
+ for (const frame of frames)
269
+ handleFrame(frame);
270
+ });
271
+ socket.on('error', (err) => {
272
+ finish({ by: 'transport', reason: err.message });
273
+ socket.destroy();
274
+ });
275
+ // BOTH halves, and `'end'` is the one that matters. An upgraded socket is
276
+ // half-open: when the far side vanishes, node delivers `'end'` and then waits
277
+ // for THIS side to end too — no `'error'`, and no `'close'` until we act. A
278
+ // door listening only for `'close'` would never fire onClose for a caller
279
+ // that walked away, and would hold a socket open that keeps every shutdown
280
+ // sharing this port waiting. Measured, not assumed.
281
+ socket.on('end', () => {
282
+ finish({ by: 'transport', reason: 'the connection ended without a close frame' });
283
+ });
284
+ socket.on('close', () => {
285
+ // No close frame from either side: the connection went away rather than
286
+ // ended, and those are different facts to whoever is watching.
287
+ finish({ by: 'transport', reason: 'the connection closed without a close frame' });
288
+ });
289
+ const port = {
290
+ ...(sessionId !== undefined && { sessionId }),
291
+ headers: options.headers,
292
+ send(frame) {
293
+ if (ending !== undefined)
294
+ throw new ConversationClosedError(hostName, sessionId);
295
+ const bytes = Buffer.byteLength(frame, 'utf8');
296
+ if (maxFrameBytes !== undefined && bytes > maxFrameBytes) {
297
+ throw new FrameTooLargeError(hostName, bytes, maxFrameBytes);
298
+ }
299
+ socket.write(encodeText(frame));
300
+ },
301
+ onFrame(cb) {
302
+ frameSubscribers.add(cb);
303
+ if (pending.length > 0) {
304
+ const held = pending;
305
+ pending = [];
306
+ pendingBytes = 0;
307
+ for (const frame of held)
308
+ cb(frame);
309
+ }
310
+ return () => {
311
+ frameSubscribers.delete(cb);
312
+ };
313
+ },
314
+ onClose(cb) {
315
+ // Already over? Say so now. A subscriber that arrives late is asking the
316
+ // same question as one that arrived early, and "never" is a wrong answer.
317
+ if (ending !== undefined) {
318
+ cb(ending);
319
+ return () => undefined;
320
+ }
321
+ closeSubscribers.add(cb);
322
+ return () => {
323
+ closeSubscribers.delete(cb);
324
+ };
325
+ },
326
+ close(reason) {
327
+ finish({ by: 'host', ...(reason !== undefined && { reason }) }, CLOSE_CODE.normal, reason);
328
+ },
329
+ };
330
+ return {
331
+ port,
332
+ ended,
333
+ end(reason) {
334
+ finish({ by: 'host', reason }, CLOSE_CODE.goingAway, reason);
335
+ },
336
+ };
337
+ }
338
+ function asMessage(err) {
339
+ return err instanceof Error ? err.message : String(err);
340
+ }
341
+ //# sourceMappingURL=webSocketConversation.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"webSocketConversation.js","sourceRoot":"","sources":["../../../src/hosting/webSocketConversation.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,164 @@
1
+ /**
2
+ * hosting/webSocketFrames — the frame codec the conversation door speaks, and
3
+ * nothing else.
4
+ *
5
+ * Pure bytes in, pure bytes out: no socket, no session, no handler, no port
6
+ * vocabulary. That is what makes it testable the only way protocol code is
7
+ * worth testing — against the byte sequences the specification itself
8
+ * publishes, rather than against a client we also wrote.
9
+ *
10
+ * ── Why this file exists instead of a dependency ─────────────────────────────
11
+ * The alternative was an optional peer dependency carrying a complete
12
+ * implementation. It was rejected for a reason that is about honesty rather
13
+ * than about lines of code: `capabilities` is declared at CONSTRUCTION and is
14
+ * static thereafter, so a host whose conversation door only works when an
15
+ * optional package happens to be installed can only either claim
16
+ * `'conversation'` and then refuse to do it — the library declaring a promise
17
+ * it cannot keep, which is the one thing the capability union forbids — or
18
+ * probe `node_modules` and let feature detection depend on install state, so a
19
+ * deployment that forgot the dependency silently gets a host that quietly does
20
+ * not do conversations. The shipped adapter honours what it declares, always,
21
+ * with nothing to install. That was worth these bytes.
22
+ *
23
+ * ── What is implemented, exactly ─────────────────────────────────────────────
24
+ * The server half of RFC 6455 for a text channel: the handshake, text frames,
25
+ * continuation frames, ping/pong, and close. Payloads are unmasked on the way
26
+ * in (client frames MUST be masked) and written unmasked on the way out (server
27
+ * frames MUST NOT be), which is the asymmetry the RFC requires.
28
+ *
29
+ * ── What is NOT, and how far the verification goes ───────────────────────────
30
+ * No extensions and no compression: `permessage-deflate` is negotiated, and
31
+ * this door never negotiates it, so the RSV bits must always be zero and a peer
32
+ * that sets one is refused. No binary frames — the port carries text, and
33
+ * binary is a capability nobody has minted evidence for. No client role.
34
+ *
35
+ * Verification is: the byte vectors published in RFC 6455 §5.7, which were
36
+ * authored by the specification and not by this repository, plus a live
37
+ * exchange against the platform's own WebSocket client where the runtime has
38
+ * one, plus the conversation conformance suite over a real socket. It is **not**
39
+ * run against the Autobahn test suite, and this file does not claim more
40
+ * coverage than the tests beside it actually execute.
41
+ *
42
+ * Pattern: pure codec. Role: innermost ring — imports nothing from this package.
43
+ */
44
+ /// <reference types="node" />
45
+ /// <reference types="node" />
46
+ /** The frame kinds this door speaks. Numbers, because the wire uses numbers. */
47
+ export declare const OPCODE: {
48
+ readonly continuation: 0;
49
+ readonly text: 1;
50
+ readonly binary: 2;
51
+ readonly close: 8;
52
+ readonly ping: 9;
53
+ readonly pong: 10;
54
+ };
55
+ /**
56
+ * Close codes this door produces. The RFC's own numbers, named — a bare `1009`
57
+ * in a branch is a fact nobody can check without opening the specification.
58
+ */
59
+ export declare const CLOSE_CODE: {
60
+ /** Ordinary end: the work is done. */
61
+ readonly normal: 1000;
62
+ /** The host is shutting down. */
63
+ readonly goingAway: 1001;
64
+ /** The peer broke the protocol. */
65
+ readonly protocolError: 1002;
66
+ /** Data this endpoint cannot accept — a binary frame, here. */
67
+ readonly unsupportedData: 1003;
68
+ /** Reserved: never sent on the wire, used only as "we were not told". */
69
+ readonly noStatus: 1005;
70
+ /** Text that is not valid UTF-8. */
71
+ readonly invalidPayload: 1007;
72
+ /** Past a declared ceiling. */
73
+ readonly tooBig: 1009;
74
+ };
75
+ /** One frame, as it came off the wire, already unmasked. */
76
+ export interface WebSocketFrame {
77
+ /** Last frame of its message? */
78
+ readonly fin: boolean;
79
+ /** One of {@link OPCODE}. */
80
+ readonly opcode: number;
81
+ /** The unmasked payload. */
82
+ readonly payload: Buffer;
83
+ }
84
+ /**
85
+ * A peer that broke the protocol, carrying the close code the RFC names for
86
+ * that breakage — so the caller does not have to re-derive which number means
87
+ * what at the point it has to write one.
88
+ */
89
+ export declare class FrameProtocolError extends Error {
90
+ readonly closeCode: number;
91
+ constructor(closeCode: number, message: string);
92
+ }
93
+ /**
94
+ * The `Sec-WebSocket-Accept` value for a client's `Sec-WebSocket-Key`:
95
+ * base64(sha1(key + GUID)). Deterministic, and pinned against the RFC's own
96
+ * worked example.
97
+ */
98
+ export declare function acceptKey(clientKey: string): string;
99
+ /**
100
+ * The 101 response, as bytes.
101
+ *
102
+ * `protocol` is echoed only when the caller selected one. Echoing a subprotocol
103
+ * the client did not offer would make the client fail the connection, so
104
+ * choosing one is the wire's job (it read the offer) and not this function's.
105
+ */
106
+ export declare function handshakeResponse(clientKey: string, protocol?: string): Buffer;
107
+ /**
108
+ * One frame, ready to write. **Never masked**: a server that masks its frames
109
+ * is a server the client closes on, per RFC 6455 §5.1.
110
+ */
111
+ export declare function encodeFrame(opcode: number, payload: Buffer, fin?: boolean): Buffer;
112
+ /** A text frame carrying one whole message. */
113
+ export declare function encodeText(text: string): Buffer;
114
+ /**
115
+ * A close frame carrying a code and, when there is one, a reason.
116
+ *
117
+ * The reason is truncated to 123 bytes because a control frame's payload may
118
+ * not exceed 125 and the code takes two of them. Truncating a REASON is safe in
119
+ * a way truncating a message never is: it is diagnostic prose about an ending
120
+ * that has already been decided.
121
+ */
122
+ export declare function encodeClose(code: number, reason?: string): Buffer;
123
+ /** What a close frame said, pulled apart. */
124
+ export interface CloseFramePayload {
125
+ /** The peer's code, or `noStatus` when it sent none — which is legal. */
126
+ readonly code: number;
127
+ /** The peer's words, when it sent any. */
128
+ readonly reason?: string;
129
+ }
130
+ /** Read a close frame's payload. An empty payload is "no status", never an error. */
131
+ export declare function decodeClose(payload: Buffer): CloseFramePayload;
132
+ /** Is this one of the three control opcodes? */
133
+ export declare function isControlFrame(opcode: number): boolean;
134
+ /**
135
+ * Turns a stream of TCP chunks into whole frames.
136
+ *
137
+ * Stateful by necessity — a frame arrives in as many pieces as the network
138
+ * feels like, and two frames arrive in one piece just as often. Every branch
139
+ * that gives up does so by throwing a {@link FrameProtocolError} carrying the
140
+ * code to close with, so the layer above never has to invent one.
141
+ */
142
+ export declare class FrameReader {
143
+ private buffered;
144
+ private readonly maxFrameBytes;
145
+ /**
146
+ * @param maxFrameBytes The declared ceiling, when there is one. Checked
147
+ * against the length in the HEADER — before the payload is buffered, so an
148
+ * announced 4GB frame costs nothing to refuse.
149
+ */
150
+ constructor(maxFrameBytes?: number);
151
+ /** Feed one chunk; get back every frame that completed. */
152
+ push(chunk: Buffer): WebSocketFrame[];
153
+ /** One frame, or `undefined` when the bytes for a whole one are not here yet. */
154
+ private readOne;
155
+ }
156
+ /**
157
+ * Decode a whole message's bytes as UTF-8, refusing anything that is not.
158
+ *
159
+ * Strict on purpose (RFC 6455 §8.1): a text frame that is not valid UTF-8 is a
160
+ * protocol violation, and the lenient alternative silently replaces the bad
161
+ * bytes with `U+FFFD` — handing the consumer a string the far side never sent
162
+ * and no way to know it happened.
163
+ */
164
+ export declare function decodeText(payload: Buffer): string;