ventiws 1.0.0-alpha

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/dist/index.mjs ADDED
@@ -0,0 +1,2977 @@
1
+ import { createRequire } from "node:module";
2
+ import { STATUS_CODES, createServer, request } from "node:http";
3
+ import { existsSync } from "node:fs";
4
+ import { arch, platform } from "node:process";
5
+ import { dirname, join } from "node:path";
6
+ import { fileURLToPath } from "node:url";
7
+ import { inspect } from "node:util";
8
+ import { createHash, randomBytes } from "node:crypto";
9
+ import { request as request$1 } from "node:https";
10
+ import { Duplex } from "node:stream";
11
+ //#region src/binding/addon-error.ts
12
+ function unloadableAddonMessage(path, platform, arch) {
13
+ return `ventiws: the native addon at ${path} was found but could not be loaded, so it was built for a different ${platform}-${arch} or against a different libc. In a checkout the fix is "pnpm build:binding"; in an install, a package built for this platform. The loader's own error is the cause.`;
14
+ }
15
+ function missingAddonMessage(root, platform, arch) {
16
+ const target = `${platform}-${arch}`;
17
+ if (root === void 0) return `ventiws: the package layout is broken -- there is no package.json above the native addon, so the artifact for ${target} cannot be located. Reinstalling ventiws should restore it.`;
18
+ return `ventiws: no native addon for ${target} was found under ${root}. An install ships the addon for one platform, so a ${target} machine needs ventiws built for ${target}, which needs the Zig toolchain (zig 0.16.0). If you are working in a checkout, the command is "pnpm build:binding".`;
19
+ }
20
+ //#endregion
21
+ //#region src/binding/load.ts
22
+ const require = createRequire(import.meta.url);
23
+ const candidatePaths = [[
24
+ "zig-out",
25
+ "lib",
26
+ "ventiws.node"
27
+ ], ["dist", "ventiws.node"]];
28
+ function findPackageRoot(start) {
29
+ let current = start;
30
+ while (current !== void 0) {
31
+ if (existsSync(join(current, "package.json"))) return current;
32
+ const parent = dirname(current);
33
+ current = parent === current ? void 0 : parent;
34
+ }
35
+ }
36
+ function resolveAddonPath() {
37
+ const root = findPackageRoot(dirname(fileURLToPath(import.meta.url)));
38
+ for (const parts of candidatePaths) {
39
+ if (root === void 0) continue;
40
+ const candidate = join(root, ...parts);
41
+ if (existsSync(candidate)) return candidate;
42
+ }
43
+ throw new Error(missingAddonMessage(root, platform, arch));
44
+ }
45
+ let addon;
46
+ function loadAddon() {
47
+ if (addon !== void 0) return addon;
48
+ const path = resolveAddonPath();
49
+ try {
50
+ addon = require(path);
51
+ } catch (cause) {
52
+ throw Object.assign(new Error(unloadableAddonMessage(path, platform, arch)), { cause });
53
+ }
54
+ return addon;
55
+ }
56
+ //#endregion
57
+ //#region src/compat/events/registry.ts
58
+ function createRegistry() {
59
+ return {};
60
+ }
61
+ function listenerCount$1(registry, event) {
62
+ return registry[event]?.length ?? 0;
63
+ }
64
+ function subscribe(registry, event, handler) {
65
+ const bucket = registry[event] ?? [];
66
+ return {
67
+ ...registry,
68
+ [event]: [...bucket, handler]
69
+ };
70
+ }
71
+ function prepend(registry, event, handler) {
72
+ const bucket = registry[event] ?? [];
73
+ return {
74
+ ...registry,
75
+ [event]: [handler, ...bucket]
76
+ };
77
+ }
78
+ function unsubscribeMatching(registry, event, matches) {
79
+ const bucket = registry[event];
80
+ if (bucket === void 0) return registry;
81
+ let index = bucket.length - 1;
82
+ while (index >= 0) {
83
+ if (matches(bucket[index])) return {
84
+ ...registry,
85
+ [event]: bucket.toSpliced(index, 1)
86
+ };
87
+ index -= 1;
88
+ }
89
+ return registry;
90
+ }
91
+ function removeAll(registry, event) {
92
+ if (event === void 0) return {};
93
+ return {
94
+ ...registry,
95
+ [event]: []
96
+ };
97
+ }
98
+ function eventNames(registry) {
99
+ const names = [];
100
+ for (const key of Object.keys(registry)) if ((registry[key]?.length ?? 0) > 0) names.push(key);
101
+ return names;
102
+ }
103
+ function dispatchWith(registry, target, event, ...args) {
104
+ const bucket = registry[event];
105
+ if (bucket === void 0) return 0;
106
+ for (let index = 0; index < bucket.length; index += 1) Reflect.apply(bucket[index], target, args);
107
+ return bucket.length;
108
+ }
109
+ //#endregion
110
+ //#region src/compat/errors.ts
111
+ const STATUS_ERROR_CODES = {
112
+ closed: "ERR_SOCKET_CLOSED",
113
+ "invalid-handle": "ERR_INVALID_HANDLE",
114
+ "payload-too-large": "ERR_MAX_PAYLOAD",
115
+ "invalid-close-code": "ERR_INVALID_CLOSE_CODE",
116
+ "invalid-close-reason": "ERR_INVALID_CLOSE_REASON",
117
+ "protocol-error": "ERR_PROTOCOL",
118
+ "policy-violation": "ERR_POLICY_VIOLATION"
119
+ };
120
+ function createError(code, message, constructor = Error) {
121
+ return Object.assign(new constructor(message), { code });
122
+ }
123
+ function createStatusError(status, message) {
124
+ return createError(STATUS_ERROR_CODES[status], message);
125
+ }
126
+ //#endregion
127
+ //#region src/compat/events/limits.ts
128
+ function warnOnOverflow(state, event) {
129
+ if (state.maxListeners <= 0) return;
130
+ const count = listenerCount$1(state.listeners, event);
131
+ if (count <= state.maxListeners) return;
132
+ const name = String(event);
133
+ if (state.warned.has(name)) return;
134
+ state.warned.add(name);
135
+ process.emitWarning(createOverflowWarning(count, name, state.maxListeners));
136
+ }
137
+ function forgetWarning(state, event) {
138
+ if (event === void 0) {
139
+ state.warned.clear();
140
+ return;
141
+ }
142
+ state.warned.delete(String(event));
143
+ }
144
+ function createOverflowWarning(count, event, limit) {
145
+ const warning = /* @__PURE__ */ new Error(`Possible EventEmitter memory leak detected. ${count} ${event} listeners added to the emitter. MaxListeners is ${limit}. Use emitter.setMaxListeners() to increase limit`);
146
+ warning.name = "MaxListenersExceededWarning";
147
+ return warning;
148
+ }
149
+ function assertListenerLimit(count) {
150
+ if (typeof count !== "number") throw createError("ERR_INVALID_OPTION", "The \"setMaxListeners\" argument must be of type number. Received " + String(count), TypeError);
151
+ if (count < 0 || Number.isNaN(count)) throw createError("ERR_INVALID_OPTION", `The value of "setMaxListeners" is out of range. It must be >= 0. Received ${count}`, RangeError);
152
+ return count;
153
+ }
154
+ //#endregion
155
+ //#region src/compat/events/tags.ts
156
+ const MAX_WRAPPER_DEPTH = 3;
157
+ const DOM_WRAPPER = Symbol("ventiws.domWrapper");
158
+ function asTagged(entry) {
159
+ return entry;
160
+ }
161
+ function originalOf(entry) {
162
+ let current = entry;
163
+ for (let depth = 0; depth < MAX_WRAPPER_DEPTH; depth += 1) {
164
+ if (typeof current !== "function") return current;
165
+ const next = asTagged(current).listener;
166
+ if (next === void 0) return current;
167
+ current = next;
168
+ }
169
+ return current;
170
+ }
171
+ function isDomEntry(entry) {
172
+ const tagged = asTagged(entry);
173
+ if (tagged[DOM_WRAPPER] === true) return true;
174
+ const inner = tagged.listener;
175
+ if (inner === void 0) return false;
176
+ return asTagged(inner)[DOM_WRAPPER] === true;
177
+ }
178
+ function listenerView(entry) {
179
+ const inner = asTagged(entry).listener;
180
+ if (inner === void 0) return entry;
181
+ if (isDomEntry(entry)) return asTagged(inner)[DOM_WRAPPER] === true ? inner : entry;
182
+ return inner;
183
+ }
184
+ function removeTagged(state, event, handler) {
185
+ state.listeners = unsubscribeMatching(state.listeners, event, (entry) => entry === handler || !isDomEntry(entry) && originalOf(entry) === handler);
186
+ }
187
+ function onceWrapper(state, event, raw) {
188
+ const wrapper = (...args) => {
189
+ state.listeners = unsubscribeMatching(state.listeners, event, (entry) => entry === wrapper);
190
+ Reflect.apply(raw, state.target, args);
191
+ };
192
+ return Object.assign(wrapper, { listener: raw });
193
+ }
194
+ function onceEvent(state, event, handler) {
195
+ state.listeners = subscribe(state.listeners, event, onceWrapper(state, event, handler));
196
+ }
197
+ //#endregion
198
+ //#region src/compat/events/emitter.ts
199
+ const ERROR_EVENT = "error";
200
+ function emitEvent(state, event, ...args) {
201
+ if (event === ERROR_EVENT && listenerCount$1(state.listeners, event) === 0) {
202
+ const failure = args[0];
203
+ if (failure instanceof Error) throw failure;
204
+ const wrapped = /* @__PURE__ */ new Error(`Unhandled error. (${inspect(failure)})`);
205
+ Object.assign(wrapped, { context: failure });
206
+ throw wrapped;
207
+ }
208
+ return dispatchWith(state.listeners, state.target, event, ...args) > 0;
209
+ }
210
+ function createEmitter(state) {
211
+ const addListener = (event, handler) => {
212
+ state.listeners = subscribe(state.listeners, event, handler);
213
+ warnOnOverflow(state, event);
214
+ return state.target;
215
+ };
216
+ const once = (event, handler) => {
217
+ state.listeners = subscribe(state.listeners, event, onceWrapper(state, event, handler));
218
+ warnOnOverflow(state, event);
219
+ return state.target;
220
+ };
221
+ const prependListener = (event, handler) => {
222
+ state.listeners = prepend(state.listeners, event, handler);
223
+ warnOnOverflow(state, event);
224
+ return state.target;
225
+ };
226
+ const prependOnceListener = (event, handler) => {
227
+ state.listeners = prepend(state.listeners, event, onceWrapper(state, event, handler));
228
+ warnOnOverflow(state, event);
229
+ return state.target;
230
+ };
231
+ const removeListener = (event, handler) => {
232
+ removeTagged(state, event, handler);
233
+ return state.target;
234
+ };
235
+ const emit = (event, ...args) => emitEvent(state, event, ...args);
236
+ return {
237
+ on: addListener,
238
+ addListener,
239
+ once,
240
+ prependListener,
241
+ prependOnceListener,
242
+ off: removeListener,
243
+ removeListener,
244
+ removeAllListeners: (event) => {
245
+ state.listeners = removeAll(state.listeners, event);
246
+ forgetWarning(state, event);
247
+ return state.target;
248
+ },
249
+ emit,
250
+ listeners: (event) => {
251
+ return (state.listeners[event] ?? []).map((entry) => listenerView(entry));
252
+ },
253
+ rawListeners: (event) => [...state.listeners[event] ?? []],
254
+ eventNames: () => eventNames(state.listeners),
255
+ listenerCount: (event) => listenerCount$1(state.listeners, event),
256
+ getMaxListeners: () => state.maxListeners,
257
+ setMaxListeners: (count) => {
258
+ state.maxListeners = assertListenerLimit(count);
259
+ state.warned.clear();
260
+ return state.target;
261
+ }
262
+ };
263
+ }
264
+ //#endregion
265
+ //#region src/compat/server/clients.ts
266
+ function clientCount(state) {
267
+ return state.clients?.size ?? 0;
268
+ }
269
+ function trackClient(state, socket) {
270
+ const clients = state.clients;
271
+ if (clients === void 0) return;
272
+ clients.add(socket);
273
+ socket.once("close", () => {
274
+ clients.delete(socket);
275
+ if (state.shouldEmitClose && clients.size === 0) process.nextTick(() => emitClose(state));
276
+ });
277
+ }
278
+ //#endregion
279
+ //#region src/compat/server/close.ts
280
+ function emitClose(state) {
281
+ if (state.lifecycle === "closed") return;
282
+ state.lifecycle = "closed";
283
+ emitEvent(state, "close");
284
+ }
285
+ function emitCloseAgain(state) {
286
+ emitEvent(state, "close");
287
+ }
288
+ function detachServer(state) {
289
+ if (state.removeListeners !== null) state.removeListeners();
290
+ state.removeListeners = null;
291
+ state.server = null;
292
+ }
293
+ function addressOf(state) {
294
+ if (state.normalizedOptions.noServer) throw createError("ERR_INVALID_STATE", "The server is operating in \"noServer\" mode");
295
+ if (state.server === null) return null;
296
+ return state.server.address();
297
+ }
298
+ function closeWebSocketServer(state, callback) {
299
+ if (state.lifecycle === "closed") {
300
+ if (callback) onceEvent(state, "close", () => {
301
+ callback(createError("ERR_SOCKET_CLOSED", "The server is not running"));
302
+ });
303
+ process.nextTick(() => {
304
+ emitCloseAgain(state);
305
+ });
306
+ return;
307
+ }
308
+ if (callback) onceEvent(state, "close", () => {
309
+ callback();
310
+ });
311
+ if (state.lifecycle === "closing") return;
312
+ state.lifecycle = "closing";
313
+ if (state.normalizedOptions.noServer || state.normalizedOptions.server !== null) {
314
+ detachServer(state);
315
+ if (!state.normalizedOptions.clientTracking || clientCount(state) === 0) {
316
+ process.nextTick(() => {
317
+ emitClose(state);
318
+ });
319
+ return;
320
+ }
321
+ state.shouldEmitClose = true;
322
+ return;
323
+ }
324
+ const httpServer = state.server;
325
+ detachServer(state);
326
+ httpServer?.close(() => {
327
+ emitClose(state);
328
+ });
329
+ }
330
+ //#endregion
331
+ //#region src/compat/options/shared.ts
332
+ const DEFAULT_MAX_PAYLOAD = 104857600;
333
+ const DEFAULT_MAX_FRAGMENTS = 16384;
334
+ const DEFAULT_MAX_BUFFERED_CHUNKS = 262144;
335
+ const DEFAULT_CLOSE_TIMEOUT = 3e4;
336
+ const SUBPROTOCOL_PATTERN = /^[!#$%&'*+\-.0-9A-Z^_`|a-z~]+$/;
337
+ function isProtocolToken(value) {
338
+ return typeof value === "string" && SUBPROTOCOL_PATTERN.test(value);
339
+ }
340
+ function invalidOption(message, constructor = TypeError) {
341
+ throw createError("ERR_INVALID_OPTION", message, constructor);
342
+ }
343
+ function normalizeProtocols$1(protocols) {
344
+ if (protocols === void 0) return [];
345
+ const list = typeof protocols === "string" ? [protocols] : protocols;
346
+ const candidates = Array.isArray(list) ? list : [list];
347
+ const seen = /* @__PURE__ */ new Set();
348
+ const result = [];
349
+ for (const protocol of candidates) {
350
+ if (typeof protocol !== "string" || !SUBPROTOCOL_PATTERN.test(protocol) || seen.has(protocol)) invalidOption("An invalid or duplicated subprotocol was specified", SyntaxError);
351
+ seen.add(protocol);
352
+ result.push(protocol);
353
+ }
354
+ return result;
355
+ }
356
+ function parseProtocolHeader(header) {
357
+ return normalizeProtocols$1(header.split(",").map((protocol) => protocol.trim()));
358
+ }
359
+ function normalizePerMessageDeflate(value, fallback) {
360
+ if (value !== void 0 && !value) return false;
361
+ const resolved = value ?? fallback;
362
+ if (!resolved) return false;
363
+ const options = resolved === true ? {} : resolved;
364
+ return {
365
+ serverNoContextTakeover: options.serverNoContextTakeover,
366
+ clientNoContextTakeover: options.clientNoContextTakeover,
367
+ serverMaxWindowBits: options.serverMaxWindowBits,
368
+ clientMaxWindowBits: options.clientMaxWindowBits,
369
+ threshold: options.threshold ?? 1024,
370
+ concurrencyLimit: options.concurrencyLimit ?? 10,
371
+ zlibDeflateOptions: options.zlibDeflateOptions,
372
+ zlibInflateOptions: options.zlibInflateOptions
373
+ };
374
+ }
375
+ function closeTimeoutOf(source) {
376
+ const raw = source.closeTimeout;
377
+ if (raw === void 0) return DEFAULT_CLOSE_TIMEOUT;
378
+ if (typeof raw !== "number" || !Number.isFinite(raw) || raw < 0) invalidOption(`The closeTimeout option must be a non-negative number (received ${String(raw)})`, RangeError);
379
+ return raw;
380
+ }
381
+ //#endregion
382
+ //#region src/compat/server/handshake.ts
383
+ const KEY_PATTERN = /^[+/0-9A-Za-z]{22}==$/;
384
+ const GUID$1 = "258EAFA5-E914-47DA-95CA-C5AB0DC85B11";
385
+ const STATUS_MIN = 100;
386
+ const STATUS_MAX = 599;
387
+ function hasControlCharacters(value) {
388
+ return value.includes("\0") || value.includes("\r") || value.includes("\n");
389
+ }
390
+ function socketAccept(key) {
391
+ return createHash("sha1").update(key + GUID$1).digest("base64");
392
+ }
393
+ function requestHeader(request, name) {
394
+ const value = request.headers[name];
395
+ return Array.isArray(value) ? value[0] : value;
396
+ }
397
+ function isSecure(request) {
398
+ const socket = request.socket;
399
+ return Boolean(socket.authorized || socket.encrypted);
400
+ }
401
+ function parseProtocols(state, request, socket) {
402
+ const header = request.headers["sec-websocket-protocol"];
403
+ if (header === void 0) return [];
404
+ try {
405
+ return parseProtocolHeader(Array.isArray(header) ? header.join(",") : header);
406
+ } catch {
407
+ abortOrEmit(state, request, socket, 400, "Invalid Sec-WebSocket-Protocol header");
408
+ return;
409
+ }
410
+ }
411
+ function selectProtocol(state, protocols, request) {
412
+ if (protocols.length === 0) return false;
413
+ const offered = new Set(protocols);
414
+ const handleProtocols = state.options.handleProtocols;
415
+ const selected = handleProtocols ? handleProtocols(offered, request) : protocols[0] ?? false;
416
+ if (!isProtocolToken(selected)) return false;
417
+ return selected;
418
+ }
419
+ function abortOrEmit(state, request, socket, code, message, headers) {
420
+ if (listenerCount$1(state.listeners, "wsClientError") > 0) {
421
+ emitEvent(state, "wsClientError", createError("ERR_PROTOCOL", message), socket, request);
422
+ return;
423
+ }
424
+ abortHandshake(socket, code, message, headers);
425
+ }
426
+ function abortHandshake(socket, code, message, headers) {
427
+ const status = Number.isInteger(code) && code >= STATUS_MIN && code <= STATUS_MAX ? code : 500;
428
+ const body = message || STATUS_CODES[status] || "";
429
+ const merged = {
430
+ Connection: "close",
431
+ "Content-Type": "text/html",
432
+ "Content-Length": Buffer.byteLength(body),
433
+ ...sanitizeHeaders(headers)
434
+ };
435
+ const lines = Object.keys(merged).map((name) => `${name}: ${String(merged[name])}`).join("\r\n");
436
+ socket.once("finish", () => {
437
+ socket.destroy();
438
+ });
439
+ socket.end(`HTTP/1.1 ${status} ${STATUS_CODES[status] ?? ""}\r\n${lines}\r\n\r\n${body}`);
440
+ }
441
+ function sanitizeHeaders(headers) {
442
+ const safe = {};
443
+ for (const [name, value] of Object.entries(headers ?? {})) {
444
+ if (hasControlCharacters(name)) continue;
445
+ if ((Array.isArray(value) ? value : [value]).some((entry) => entry !== void 0 && hasControlCharacters(String(entry)))) continue;
446
+ safe[name] = value;
447
+ }
448
+ return safe;
449
+ }
450
+ //#endregion
451
+ //#region src/binding/codec-status.ts
452
+ const CODEC_ROLE = {
453
+ client: 0,
454
+ server: 1
455
+ };
456
+ const CODEC_OUTCOME = {
457
+ backpressure: 1,
458
+ failed: 2,
459
+ staleHandle: 3
460
+ };
461
+ const CODEC_KINDS = [
462
+ "text",
463
+ "binary",
464
+ "ping",
465
+ "pong",
466
+ "close",
467
+ "rejected",
468
+ "continuation"
469
+ ];
470
+ const CODEC_FAILURES = [
471
+ "protocolError",
472
+ "expectedFin",
473
+ "expectedMask",
474
+ "invalidCloseCode",
475
+ "invalidControlPayloadLength",
476
+ "invalidOpcode",
477
+ "invalidUtf8",
478
+ "unexpectedMask",
479
+ "unexpectedRsv1",
480
+ "unexpectedRsv2or3",
481
+ "tooManyBufferedParts",
482
+ "unsupportedDataPayloadLength",
483
+ "unsupportedMessageLength",
484
+ "invalidCompressedData",
485
+ "invalidMask"
486
+ ];
487
+ function decodeOutcome(value) {
488
+ if (value >= 0) return {
489
+ kind: "consumed",
490
+ bytes: value
491
+ };
492
+ const ordinal = -value;
493
+ if (ordinal === CODEC_OUTCOME.backpressure) return { kind: "backpressure" };
494
+ if (ordinal === CODEC_OUTCOME.failed) return { kind: "failed" };
495
+ return { kind: "stale-handle" };
496
+ }
497
+ //#endregion
498
+ //#region src/binding/codec-limits.ts
499
+ function codecLimits() {
500
+ return loadAddon().engineLimits();
501
+ }
502
+ //#endregion
503
+ //#region src/binding/errors.ts
504
+ const NATIVE_ERROR_CODES = {
505
+ UnknownServer: "ERR_INVALID_HANDLE",
506
+ InvalidServerState: "ERR_INVALID_STATE",
507
+ ServerNotClosed: "ERR_INVALID_STATE",
508
+ EventsPending: "ERR_INVALID_STATE",
509
+ EnvCleanupUnavailable: "ERR_INVALID_STATE",
510
+ InvalidHost: "ERR_INVALID_OPTION",
511
+ InvalidPort: "ERR_INVALID_OPTION",
512
+ InvalidBacklog: "ERR_INVALID_OPTION",
513
+ InvalidPath: "ERR_INVALID_OPTION",
514
+ InvalidConnectionCapacity: "ERR_INVALID_OPTION",
515
+ InvalidMessageCapacity: "ERR_INVALID_OPTION",
516
+ InvalidFrameCapacity: "ERR_INVALID_OPTION",
517
+ ThreadsafeFunctionUnavailable: "ERR_PROTOCOL",
518
+ CapacityExhausted: "ERR_INVALID_STATE",
519
+ ServerCapacityExhausted: "ERR_INVALID_STATE",
520
+ EngineWorkerMissing: "ERR_INVALID_STATE",
521
+ AddressInUse: "ERR_INVALID_STATE",
522
+ SystemResources: "ERR_INVALID_STATE",
523
+ ThreadQuotaExceeded: "ERR_INVALID_STATE",
524
+ OutOfMemory: "ERR_INVALID_STATE"
525
+ };
526
+ function nativeError(error) {
527
+ const name = error instanceof Error ? error.message : String(error);
528
+ const code = NATIVE_ERROR_CODES[name] ?? "ERR_PROTOCOL";
529
+ return Object.assign(new Error(name), { code });
530
+ }
531
+ function guardError(message, code, constructor = RangeError) {
532
+ return Object.assign(new constructor(message), { code });
533
+ }
534
+ function callNative(call) {
535
+ try {
536
+ return call();
537
+ } catch (error) {
538
+ throw nativeError(error);
539
+ }
540
+ }
541
+ //#endregion
542
+ //#region src/binding/codec-create.ts
543
+ function createCodec(role, options) {
544
+ const limits = codecLimits();
545
+ assertCeiling("maxPayload", options.maxPayload, limits.maxPayloadBytes);
546
+ assertCeiling("maxFragments", options.maxFragments, limits.maxFragments);
547
+ const addon = loadAddon();
548
+ return callNative(() => addon.codecCreate(role, options.validateUtf8 ? 1 : 0, options.maxPayload, options.maxFragments, options.permessageDeflate ? 1 : 0));
549
+ }
550
+ function assertCeiling(name, value, ceiling) {
551
+ if (Number.isSafeInteger(value) && value >= 0 && value <= ceiling) return;
552
+ throw createError("ERR_INVALID_OPTION", `ventiws: ${name} must be an integer in [0, ${ceiling}] (received ${String(value)})`, RangeError);
553
+ }
554
+ //#endregion
555
+ //#region src/binding/codec-encode.ts
556
+ function encodeCodecFrame(handle, kind, fin, payload, compress, maskFrame = true, mask = EMPTY$3) {
557
+ const addon = loadAddon();
558
+ return callNative(() => addon.codecEncode(handle, kind, fin ? 1 : 0, payload, compress ? 1 : 0, maskFrame ? 1 : 0, mask));
559
+ }
560
+ const EMPTY$3 = /* @__PURE__ */ new Uint8Array(0);
561
+ function codecOutbound(handle) {
562
+ const addon = loadAddon();
563
+ return callNative(() => addon.codecOutbound(handle));
564
+ }
565
+ //#endregion
566
+ //#region src/binding/codec-state.ts
567
+ function codecFailureCode(handle) {
568
+ const addon = loadAddon();
569
+ return callNative(() => addon.codecFailureCode(handle));
570
+ }
571
+ function codecFailure(handle) {
572
+ const addon = loadAddon();
573
+ const ordinal = callNative(() => addon.codecFailure(handle));
574
+ return CODEC_FAILURES[ordinal - 1] ?? null;
575
+ }
576
+ //#endregion
577
+ //#region src/binding/codec.ts
578
+ function destroyCodec(handle) {
579
+ const addon = loadAddon();
580
+ callNative(() => addon.codecDestroy(handle));
581
+ }
582
+ function feedCodec(handle, bytes) {
583
+ const addon = loadAddon();
584
+ return decodeOutcome(callNative(() => addon.codecFeed(handle, bytes)));
585
+ }
586
+ function codecFeedResume(handle) {
587
+ const addon = loadAddon();
588
+ return callNative(() => addon.codecResume(handle));
589
+ }
590
+ function pendingCodecEvents(handle) {
591
+ const addon = loadAddon();
592
+ return callNative(() => addon.codecPending(handle));
593
+ }
594
+ function selectCodecEvent(handle) {
595
+ const addon = loadAddon();
596
+ return callNative(() => addon.codecSelect(handle));
597
+ }
598
+ function selectedCodecEvent(handle) {
599
+ const addon = loadAddon();
600
+ const event = callNative(() => addon.codecEvent(handle));
601
+ if (event === null) return null;
602
+ return {
603
+ kind: CODEC_KINDS[event[0]] ?? "rejected",
604
+ code: event[1],
605
+ payload: event[2]
606
+ };
607
+ }
608
+ function takeCodecEvent(handle) {
609
+ const addon = loadAddon();
610
+ callNative(() => addon.codecTake(handle));
611
+ }
612
+ function codecFragmentEnds(handle) {
613
+ const addon = loadAddon();
614
+ return callNative(() => addon.codecFragments(handle));
615
+ }
616
+ //#endregion
617
+ //#region src/compat/socket/codec-handle.ts
618
+ function codecOf(state) {
619
+ return state.codec;
620
+ }
621
+ function openCodec(state, role) {
622
+ if (state.codec !== null) return state.codec;
623
+ const handle = createCodec(role, {
624
+ maxPayload: state.maxPayload,
625
+ maxFragments: state.maxFragments,
626
+ validateUtf8: state.validateUtf8,
627
+ permessageDeflate: state.compressible
628
+ });
629
+ state.codec = handle;
630
+ return handle;
631
+ }
632
+ function closeCodec(state) {
633
+ if (state.codec === null) return;
634
+ destroyCodec(state.codec);
635
+ state.codec = null;
636
+ }
637
+ function isWritable(state) {
638
+ return state.codec !== null && state.transport !== null && !state.transport.writableEnded;
639
+ }
640
+ function noTransportError() {
641
+ return createError("ERR_INVALID_STATE", "ventiws: the socket has no transport to write to");
642
+ }
643
+ //#endregion
644
+ //#region src/compat/socket/codec-deferral.ts
645
+ function isDeliveryPaused(state) {
646
+ return state.deliveryPaused;
647
+ }
648
+ function pauseUntilNextTick(state, resume) {
649
+ if (state.deliveryPaused) return true;
650
+ state.deliveryPaused = true;
651
+ setImmediate(() => {
652
+ state.deliveryPaused = false;
653
+ resume();
654
+ });
655
+ return true;
656
+ }
657
+ //#endregion
658
+ //#region src/binding/native.ts
659
+ const NATIVE_SOCKET_STATUSES = [
660
+ "ok",
661
+ "closing",
662
+ "closed",
663
+ "backpressure",
664
+ "invalidHandle",
665
+ "payloadTooLarge",
666
+ "invalidCloseCode",
667
+ "invalidCloseReason",
668
+ "protocolError",
669
+ "policyViolation"
670
+ ];
671
+ //#endregion
672
+ //#region src/binding/handle.ts
673
+ const MAX_HANDLE_BIG = 18446744073709551615n;
674
+ function assertConnectionHandle(handle) {
675
+ if (typeof handle !== "bigint" || handle < 0n || handle > MAX_HANDLE_BIG) throw guardError(`ventiws: connection handle out of range: ${String(handle)}`, "ERR_INVALID_HANDLE");
676
+ }
677
+ //#endregion
678
+ //#region src/binding/server.ts
679
+ const MAX_SERVER_HANDLE = 2 ** 40 - 1;
680
+ function assertServerHandle(handle) {
681
+ if (!Number.isSafeInteger(handle) || handle < 0 || handle > MAX_SERVER_HANDLE) throw guardError(`ventiws: server handle must be a uint40, got ${handle}`, "ERR_INVALID_HANDLE");
682
+ }
683
+ function engineLimits() {
684
+ return loadAddon().engineLimits();
685
+ }
686
+ //#endregion
687
+ //#region src/binding/socket.ts
688
+ const MAX_UINT16 = 65535;
689
+ const ENGINE_STATUS_BY_NATIVE = {
690
+ ok: "ok",
691
+ closing: "closing",
692
+ closed: "closed",
693
+ backpressure: "backpressure",
694
+ invalidHandle: "invalid-handle",
695
+ payloadTooLarge: "payload-too-large",
696
+ invalidCloseCode: "invalid-close-code",
697
+ invalidCloseReason: "invalid-close-reason",
698
+ protocolError: "protocol-error",
699
+ policyViolation: "policy-violation"
700
+ };
701
+ function assertPayload(data) {
702
+ if (!(data instanceof Uint8Array)) throw guardError("ventiws: socket payload must be a Uint8Array", "ERR_INVALID_OPTION", TypeError);
703
+ }
704
+ function statusFromOrdinal(ordinal) {
705
+ const status = NATIVE_SOCKET_STATUSES[ordinal];
706
+ if (status === void 0) throw guardError(`ventiws: unknown native socket status ${ordinal}`, "ERR_PROTOCOL", RangeError);
707
+ return ENGINE_STATUS_BY_NATIVE[status];
708
+ }
709
+ function assertCloseCode$1(code) {
710
+ if (!Number.isInteger(code) || code < 0 || code > MAX_UINT16) throw guardError(`ventiws: close code must be a uint16, got ${code}`, "ERR_INVALID_CLOSE_CODE");
711
+ }
712
+ function sendSocket(server, connection, data, binary = false) {
713
+ assertServerHandle(server);
714
+ assertConnectionHandle(connection);
715
+ assertPayload(data);
716
+ const addon = loadAddon();
717
+ return statusFromOrdinal(callNative(() => addon.sendSocket(server, connection, data, binary)));
718
+ }
719
+ function closeSocket(server, connection, code, reason) {
720
+ assertServerHandle(server);
721
+ assertConnectionHandle(connection);
722
+ assertCloseCode$1(code);
723
+ assertPayload(reason);
724
+ const addon = loadAddon();
725
+ return statusFromOrdinal(callNative(() => addon.closeSocket(server, connection, code, reason)));
726
+ }
727
+ function pauseSocket(server, connection) {
728
+ assertServerHandle(server);
729
+ assertConnectionHandle(connection);
730
+ const addon = loadAddon();
731
+ return statusFromOrdinal(callNative(() => addon.pauseSocket(server, connection)));
732
+ }
733
+ function resumeSocket(server, connection) {
734
+ assertServerHandle(server);
735
+ assertConnectionHandle(connection);
736
+ const addon = loadAddon();
737
+ return statusFromOrdinal(callNative(() => addon.resumeSocket(server, connection)));
738
+ }
739
+ function socketBufferedAmount(server, connection) {
740
+ assertServerHandle(server);
741
+ assertConnectionHandle(connection);
742
+ const addon = loadAddon();
743
+ return callNative(() => addon.socketBufferedAmount(server, connection));
744
+ }
745
+ //#endregion
746
+ //#region src/compat/socket/queued.ts
747
+ function queuedBytes(state) {
748
+ if (state.transport === null) return 0;
749
+ return state.transport.writableLength;
750
+ }
751
+ //#endregion
752
+ //#region src/compat/socket/payload.ts
753
+ const READY_STATE_NAMES = [
754
+ "CONNECTING",
755
+ "OPEN",
756
+ "CLOSING",
757
+ "CLOSED"
758
+ ];
759
+ function toPayload(data) {
760
+ if (typeof data === "number") return {
761
+ bytes: Buffer.from(String(data), "utf8"),
762
+ binary: false
763
+ };
764
+ if (typeof data === "string") return data.length === 0 ? {
765
+ bytes: Buffer.alloc(0),
766
+ binary: false
767
+ } : {
768
+ bytes: Buffer.from(data, "utf8"),
769
+ binary: false
770
+ };
771
+ if (!data) return {
772
+ bytes: Buffer.alloc(0),
773
+ binary: true
774
+ };
775
+ return {
776
+ bytes: toBytes(data),
777
+ binary: true
778
+ };
779
+ }
780
+ function toBytes(data) {
781
+ if (ArrayBuffer.isView(data)) return Buffer.from(data.buffer, data.byteOffset, data.byteLength);
782
+ if (data instanceof ArrayBuffer) return Buffer.from(data);
783
+ return Buffer.from(data);
784
+ }
785
+ function notOpenError(readyState) {
786
+ return createError("ERR_SOCKET_NOT_OPEN", `WebSocket is not open: readyState ${readyState} (${READY_STATE_NAMES[readyState] ?? "UNKNOWN"})`);
787
+ }
788
+ function statusError(status) {
789
+ return createStatusError(status, `ventiws: socket operation failed with status "${status}"`);
790
+ }
791
+ function bufferedAmountOf(state) {
792
+ if (state.attachment === null) return queuedBytes(state);
793
+ return socketBufferedAmount(state.attachment.server, state.attachment.connection);
794
+ }
795
+ function defer(callback, error) {
796
+ if (typeof callback !== "function") return;
797
+ process.nextTick(callback, error);
798
+ }
799
+ //#endregion
800
+ //#region src/compat/socket/codec-outbound.ts
801
+ function ordinalOf(kind) {
802
+ return CODEC_KINDS.indexOf(kind);
803
+ }
804
+ function maskFor(state, mask) {
805
+ if (state.isServer || !mask || state.generateMask === null) return NO_MASK;
806
+ state.generateMask(state.maskScratch);
807
+ return state.maskScratch;
808
+ }
809
+ const NO_MASK = /* @__PURE__ */ new Uint8Array(0);
810
+ function writeFrame(state, kind, payload, fin = true, compress = false, mask = true) {
811
+ const handle = state.codec;
812
+ if (handle === null) return "closed";
813
+ if (!isWritable(state)) return state.transport?.writableEnded === true ? "closed" : "closing";
814
+ const length = encodeCodecFrame(handle, ordinalOf(kind), fin, payload, compress, mask, maskFor(state, mask));
815
+ if (length < 0) return encodeFailure(-length);
816
+ const framed = codecOutbound(handle);
817
+ if (state.transport === null) return "closed";
818
+ state.transport.write(framed.subarray(0, length));
819
+ return "ok";
820
+ }
821
+ function writePong(state, payload) {
822
+ writeFrame(state, "pong", payload);
823
+ }
824
+ function writeCloseFrame(state, code, reason) {
825
+ if (state.closeFrameSent) return;
826
+ if (writeFrame(state, "close", closePayload(code, reason)) !== "ok") return;
827
+ state.closeFrameSent = true;
828
+ }
829
+ function closePayload(code, reason) {
830
+ if (code === void 0) return Buffer.alloc(0);
831
+ const payload = Buffer.alloc(2 + reason.length);
832
+ payload.writeUInt16BE(code, 0);
833
+ reason.copy(payload, 2);
834
+ return payload;
835
+ }
836
+ function encodeFailure(ordinal) {
837
+ switch (ordinal) {
838
+ case 1: return "protocol-error";
839
+ case 2: return "payload-too-large";
840
+ case 3: return "protocol-error";
841
+ case 4: return "invalid-handle";
842
+ default: return "protocol-error";
843
+ }
844
+ }
845
+ function frameError(status) {
846
+ if (status === "closed" || status === "closing") return noTransportError();
847
+ if (status === "backpressure") return createError("ERR_BACKPRESSURE", "ventiws: the codec's event queue is full");
848
+ return statusError(status);
849
+ }
850
+ const CLOSE_NO_STATUS = 1005;
851
+ const CLOSE_ABNORMAL$1 = 1006;
852
+ function isValidControlPayload(payload) {
853
+ return Buffer.byteLength(payload) <= 125;
854
+ }
855
+ function isValidStatusCode(code) {
856
+ if (code >= 3e3 && code <= 4999) return true;
857
+ return code >= 1e3 && code <= 1014 && code !== 1004 && code !== 1005 && code !== 1006;
858
+ }
859
+ //#endregion
860
+ //#region src/compat/socket/close-code.ts
861
+ function closeCodeOf(code) {
862
+ if (code === void 0) return void 0;
863
+ return Math.trunc(assertCloseCode(code));
864
+ }
865
+ function assertCloseCode(code) {
866
+ if (typeof code !== "number" || !isValidStatusCode(code)) throw createError("ERR_INVALID_CLOSE_CODE", "First argument must be a valid error code number", TypeError);
867
+ return code;
868
+ }
869
+ //#endregion
870
+ //#region src/compat/socket/close-reason.ts
871
+ const EMPTY$2 = Buffer.alloc(0);
872
+ function toCloseReason(reason) {
873
+ if (reason === void 0) return EMPTY$2;
874
+ if (reason === null) return EMPTY$2;
875
+ if (!hasReasonData(reason)) return EMPTY$2;
876
+ assertReasonLength(Buffer.byteLength(reason));
877
+ if (typeof reason === "string") return Buffer.from(reason, "utf8");
878
+ if (reason instanceof Uint8Array) return Buffer.from(reason);
879
+ throw createError("ERR_INVALID_OPTION", "Second argument must be a string or a Uint8Array", TypeError);
880
+ }
881
+ function hasReasonData(reason) {
882
+ const length = reason.length;
883
+ return typeof length === "number" && length > 0;
884
+ }
885
+ function assertReasonLength(length) {
886
+ if (length <= 123) return;
887
+ throw createError("ERR_INVALID_CLOSE_REASON", "The message must not be greater than 123 bytes", RangeError);
888
+ }
889
+ //#endregion
890
+ //#region src/compat/socket/close-failure.ts
891
+ function closeFailure(status) {
892
+ switch (status) {
893
+ case "backpressure": return createError("ERR_BACKPRESSURE", "ventiws: the outbound staging ring is full");
894
+ case "invalid-handle": return createError("ERR_INVALID_HANDLE", "ventiws: the connection handle is stale");
895
+ case "ok":
896
+ case "closing":
897
+ case "closed": return createError("ERR_INVALID_STATE", "ventiws: the connection is already closing");
898
+ case "payload-too-large":
899
+ case "invalid-close-code":
900
+ case "invalid-close-reason":
901
+ case "protocol-error":
902
+ case "policy-violation": return statusError(status);
903
+ }
904
+ }
905
+ //#endregion
906
+ //#region src/compat/socket/state.ts
907
+ const SOCKET_BRAND = Symbol("ventiws.socket");
908
+ const SOCKET_STATE = Symbol("ventiws.socket.state");
909
+ function brandSocket(socket, state) {
910
+ Object.defineProperties(socket, {
911
+ [SOCKET_BRAND]: { value: true },
912
+ [SOCKET_STATE]: { value: state }
913
+ });
914
+ }
915
+ function isSocket(value) {
916
+ if (typeof value !== "object" || value === null) return false;
917
+ return value[SOCKET_BRAND] === true;
918
+ }
919
+ function socketStateOf(socket) {
920
+ if (typeof socket !== "object" || socket === null) return void 0;
921
+ return socket[SOCKET_STATE];
922
+ }
923
+ function createSocketState() {
924
+ return {
925
+ url: "",
926
+ protocol: "",
927
+ extensions: "",
928
+ binaryType: "nodebuffer",
929
+ readyState: 0,
930
+ isPaused: false,
931
+ isServer: true,
932
+ closeCode: 1006,
933
+ closeReason: Buffer.alloc(0),
934
+ closeFrameSent: false,
935
+ closeFrameReceived: false,
936
+ fragmentsOpen: false,
937
+ errorEmitted: false,
938
+ attachment: null,
939
+ transport: null,
940
+ codec: null,
941
+ closeTimer: null,
942
+ closeTimeout: DEFAULT_CLOSE_TIMEOUT,
943
+ allowSynchronousEvents: true,
944
+ deliveryPaused: false,
945
+ pendingInput: [],
946
+ maxBufferedChunks: DEFAULT_MAX_BUFFERED_CHUNKS,
947
+ validateUtf8: true,
948
+ maxPayload: DEFAULT_MAX_PAYLOAD,
949
+ maxFragments: DEFAULT_MAX_FRAGMENTS,
950
+ cancelHandshake: null,
951
+ pendingSend: null,
952
+ compressible: false,
953
+ threshold: 0,
954
+ autoPong: true,
955
+ generateMask: null,
956
+ maskScratch: Buffer.alloc(4),
957
+ listeners: createRegistry(),
958
+ maxListeners: 10,
959
+ warned: /* @__PURE__ */ new Set(),
960
+ target: void 0
961
+ };
962
+ }
963
+ //#endregion
964
+ //#region src/compat/socket/lifecycle.ts
965
+ const EMPTY$1 = Buffer.alloc(0);
966
+ function closeFrameWritten(socket) {
967
+ return socketStateOf(socket)?.closeFrameSent === true;
968
+ }
969
+ function finishConnection(state, code, reason) {
970
+ if (state.readyState === 3) return;
971
+ if (state.closeTimer !== null) {
972
+ clearTimeout(state.closeTimer);
973
+ state.closeTimer = null;
974
+ }
975
+ state.readyState = 3;
976
+ state.closeCode = code;
977
+ state.closeReason = reason;
978
+ emitEvent(state, "close", code, reason);
979
+ }
980
+ function failConnection(state, error) {
981
+ if (state.readyState === 3) return;
982
+ if (!state.errorEmitted) {
983
+ state.errorEmitted = true;
984
+ state.readyState = 2;
985
+ try {
986
+ emitEvent(state, "error", error);
987
+ } finally {
988
+ finishConnection(state, CLOSE_ABNORMAL$1, EMPTY$1);
989
+ }
990
+ return;
991
+ }
992
+ finishConnection(state, CLOSE_ABNORMAL$1, EMPTY$1);
993
+ }
994
+ function reportWithoutClosing(state, error) {
995
+ if (state.errorEmitted) return;
996
+ if (state.readyState === 3) return;
997
+ state.errorEmitted = true;
998
+ emitEvent(state, "error", error);
999
+ }
1000
+ function closeConnection(state, code, reason) {
1001
+ if (state.readyState === 3) return;
1002
+ if (state.readyState === 0) {
1003
+ failConnection(state, createError("ERR_INVALID_STATE", "WebSocket was closed before the connection was established"));
1004
+ return;
1005
+ }
1006
+ if (state.readyState === 2) return;
1007
+ state.readyState = 2;
1008
+ const closeCode = closeCodeOf(code);
1009
+ const closeReason = toCloseReason(reason);
1010
+ if (state.codec !== null) {
1011
+ closeFramed(state, closeCode, closeReason);
1012
+ armCloseTimeout(state, state.closeTimeout);
1013
+ return;
1014
+ }
1015
+ if (state.attachment === null) {
1016
+ closeUnattached(state);
1017
+ return;
1018
+ }
1019
+ const status = closeSocket(state.attachment.server, state.attachment.connection, closeCode ?? 1e3, closeReason);
1020
+ if (status === "ok") {
1021
+ state.closeFrameSent = true;
1022
+ return;
1023
+ }
1024
+ if (status === "closing" || status === "closed") {
1025
+ finishConnection(state, CLOSE_ABNORMAL$1, EMPTY$1);
1026
+ return;
1027
+ }
1028
+ failConnection(state, closeFailure(status));
1029
+ }
1030
+ function closeUnattached(state) {
1031
+ state.cancelHandshake?.();
1032
+ if (state.transport === null) {
1033
+ finishConnection(state, CLOSE_ABNORMAL$1, EMPTY$1);
1034
+ return;
1035
+ }
1036
+ state.transport.destroy();
1037
+ }
1038
+ //#endregion
1039
+ //#region src/compat/socket/codec-close.ts
1040
+ const CLOSE_ABNORMAL = 1006;
1041
+ function closeFramed(state, code, reason) {
1042
+ writeCloseFrame(state, code, reason);
1043
+ }
1044
+ function refuseFramed(state, refusal) {
1045
+ if (state.readyState === 3) return;
1046
+ state.readyState = 2;
1047
+ writeCloseFrame(state, refusal.closeCode, Buffer.from(refusal.reason, "utf8"));
1048
+ try {
1049
+ if (!state.errorEmitted) {
1050
+ state.errorEmitted = true;
1051
+ emitEvent(state, "error", createError(refusal.code, refusal.message, refusal.ctor));
1052
+ }
1053
+ } finally {
1054
+ finishConnection(state, refusal.closeCode, Buffer.from(refusal.reason, "utf8"));
1055
+ closeCodec(state);
1056
+ state.transport?.end();
1057
+ }
1058
+ }
1059
+ function armCloseTimeout(state, milliseconds) {
1060
+ clearCloseTimeout(state);
1061
+ state.closeTimer = setTimeout(() => {
1062
+ state.closeTimer = null;
1063
+ if (state.readyState !== 2) return;
1064
+ state.transport?.destroy();
1065
+ finishConnection(state, CLOSE_ABNORMAL, Buffer.alloc(0));
1066
+ }, Math.max(milliseconds, 0));
1067
+ state.closeTimer.unref?.();
1068
+ }
1069
+ function clearCloseTimeout(state) {
1070
+ if (state.closeTimer === null) return;
1071
+ clearTimeout(state.closeTimer);
1072
+ state.closeTimer = null;
1073
+ }
1074
+ //#endregion
1075
+ //#region src/compat/socket/refusal-table.ts
1076
+ const REFUSALS = {
1077
+ expectedFin: refused(1002, "WS_ERR_EXPECTED_FIN", "protocol error", "FIN must be set"),
1078
+ expectedMask: refused(1002, "WS_ERR_EXPECTED_MASK", "protocol error", "MASK must be set"),
1079
+ invalidCloseCode: refused(1002, "WS_ERR_INVALID_CLOSE_CODE", "protocol error", "invalid status code"),
1080
+ invalidControlPayloadLength: refused(1002, "WS_ERR_INVALID_CONTROL_PAYLOAD_LENGTH", "protocol error", "invalid payload length"),
1081
+ invalidOpcode: refused(1002, "WS_ERR_INVALID_OPCODE", "protocol error", "invalid opcode"),
1082
+ unexpectedMask: refused(1002, "WS_ERR_UNEXPECTED_MASK", "protocol error", "MASK must be clear"),
1083
+ unexpectedRsv1: refused(1002, "WS_ERR_UNEXPECTED_RSV_1", "protocol error", "RSV1 must be clear"),
1084
+ unexpectedRsv2or3: refused(1002, "WS_ERR_UNEXPECTED_RSV_2_3", "protocol error", "RSV2 and RSV3 must be clear"),
1085
+ unsupportedDataPayloadLength: {
1086
+ closeCode: 1009,
1087
+ code: "WS_ERR_UNSUPPORTED_DATA_PAYLOAD_LENGTH",
1088
+ reason: "message too big",
1089
+ message: "Unsupported WebSocket frame: payload length > 2^53 - 1",
1090
+ ctor: RangeError
1091
+ },
1092
+ invalidUtf8: {
1093
+ closeCode: 1007,
1094
+ code: "WS_ERR_INVALID_UTF8",
1095
+ reason: "invalid payload",
1096
+ message: "Invalid WebSocket frame: invalid UTF-8 sequence",
1097
+ ctor: Error
1098
+ },
1099
+ invalidCompressedData: {
1100
+ closeCode: 1007,
1101
+ code: "ERR_INVALID_COMPRESSED_DATA",
1102
+ reason: "invalid payload",
1103
+ message: "Invalid WebSocket frame: invalid compressed data",
1104
+ ctor: Error
1105
+ },
1106
+ tooManyBufferedParts: {
1107
+ closeCode: 1008,
1108
+ code: "WS_ERR_TOO_MANY_BUFFERED_PARTS",
1109
+ reason: "too many message fragments",
1110
+ message: "Too many message fragments",
1111
+ ctor: RangeError
1112
+ },
1113
+ unsupportedMessageLength: {
1114
+ closeCode: 1009,
1115
+ code: "WS_ERR_UNSUPPORTED_MESSAGE_LENGTH",
1116
+ reason: "message too big",
1117
+ message: "Max payload size exceeded",
1118
+ ctor: RangeError
1119
+ },
1120
+ invalidMask: {
1121
+ closeCode: 1002,
1122
+ code: "ERR_INVALID_OPTION",
1123
+ reason: "protocol error",
1124
+ message: "ventiws: generateMask must fill all four bytes of the masking key",
1125
+ ctor: RangeError
1126
+ },
1127
+ protocolError: {
1128
+ closeCode: 1002,
1129
+ code: "ERR_PROTOCOL",
1130
+ reason: "protocol error",
1131
+ message: "Invalid WebSocket frame",
1132
+ ctor: RangeError
1133
+ }
1134
+ };
1135
+ function refused(closeCode, code, reason, detail) {
1136
+ return {
1137
+ closeCode,
1138
+ code,
1139
+ reason,
1140
+ message: `Invalid WebSocket frame: ${detail}`,
1141
+ ctor: RangeError
1142
+ };
1143
+ }
1144
+ //#endregion
1145
+ //#region src/compat/socket/codec-refusal.ts
1146
+ function refusalOf(failure) {
1147
+ return REFUSALS[failure] ?? REFUSALS.protocolError;
1148
+ }
1149
+ function failureByCode(code) {
1150
+ switch (code) {
1151
+ case 1007: return "invalidUtf8";
1152
+ case 1008: return "tooManyBufferedParts";
1153
+ case 1009: return "unsupportedMessageLength";
1154
+ default: return "protocolError";
1155
+ }
1156
+ }
1157
+ function refuseByCodec(state, failure) {
1158
+ refuseFramed(state, refusalOf(failure));
1159
+ }
1160
+ //#endregion
1161
+ //#region src/compat/socket/payload-shape.ts
1162
+ function shapeBinary(binaryType, payload, ends) {
1163
+ switch (binaryType) {
1164
+ case "arraybuffer": return slicedArrayBuffer(payload);
1165
+ case "blob": return new Blob([new Uint8Array(payload)]);
1166
+ case "fragments": return sliceFragments(payload, ends);
1167
+ case "nodebuffer": return payload;
1168
+ }
1169
+ }
1170
+ function slicedArrayBuffer(payload) {
1171
+ return payload.buffer.slice(payload.byteOffset, payload.byteOffset + payload.byteLength);
1172
+ }
1173
+ function sliceFragments(payload, ends) {
1174
+ if (ends === null || ends.length === 0) return [payload];
1175
+ const pieces = [];
1176
+ let start = 0;
1177
+ for (const end of ends) {
1178
+ const at = Math.min(end, payload.length);
1179
+ if (at > start) pieces.push(payload.subarray(start, at));
1180
+ start = at;
1181
+ }
1182
+ if (start < payload.length) pieces.push(payload.subarray(start));
1183
+ return pieces.length > 0 ? pieces : [payload];
1184
+ }
1185
+ //#endregion
1186
+ //#region src/compat/socket/codec-peer-close.ts
1187
+ function closeFromPeer(state, event) {
1188
+ if (state.readyState !== 2) state.readyState = 2;
1189
+ state.closeFrameReceived = true;
1190
+ if (!state.closeFrameSent) writeCloseFrame(state, event.code === 0 ? void 0 : event.code, event.payload);
1191
+ finishConnection(state, event.code === 0 ? CLOSE_NO_STATUS : event.code, event.payload);
1192
+ closeCodec(state);
1193
+ state.transport?.end();
1194
+ }
1195
+ //#endregion
1196
+ //#region src/compat/socket/codec-events.ts
1197
+ function deliver(state, resume) {
1198
+ drain(state, false, resume);
1199
+ }
1200
+ function drainReleased(state, resume) {
1201
+ drain(state, true, resume);
1202
+ }
1203
+ function drain(state, released, resume) {
1204
+ const handle = codecOf(state);
1205
+ if (handle === null) return;
1206
+ if (isDeliveryPaused(state)) return;
1207
+ let honoured = released;
1208
+ while (pendingCodecEvents(handle) > 0 && selectCodecEvent(handle)) {
1209
+ const event = selectedCodecEvent(handle);
1210
+ if (!honoured && state.allowSynchronousEvents === false && isDeferrable(event)) {
1211
+ pauseUntilNextTick(state, resume);
1212
+ return;
1213
+ }
1214
+ honoured = false;
1215
+ const ends = codecFragmentEnds(handle);
1216
+ takeCodecEvent(handle);
1217
+ if (event === null) continue;
1218
+ dispatch(state, handle, event, ends);
1219
+ if (state.readyState === 3) return;
1220
+ }
1221
+ }
1222
+ function isDeferrable(event) {
1223
+ if (event === null) return false;
1224
+ return event.kind === "text" || event.kind === "binary" || event.kind === "ping" || event.kind === "pong";
1225
+ }
1226
+ function dispatch(state, handle, event, ends) {
1227
+ switch (event.kind) {
1228
+ case "text":
1229
+ emitEvent(state, "message", event.payload, false);
1230
+ return;
1231
+ case "binary":
1232
+ emitBinary(state, event, ends);
1233
+ return;
1234
+ case "ping":
1235
+ if (state.autoPong) writePong(state, event.payload);
1236
+ emitEvent(state, "ping", event.payload);
1237
+ return;
1238
+ case "pong":
1239
+ emitEvent(state, "pong", event.payload);
1240
+ return;
1241
+ case "close":
1242
+ closeFromPeer(state, event);
1243
+ return;
1244
+ case "rejected":
1245
+ refuseByCodec(state, codecFailure(handle) ?? failureByCode(codecFailureCode(handle)));
1246
+ return;
1247
+ }
1248
+ }
1249
+ function emitBinary(state, event, ends) {
1250
+ emitEvent(state, "message", shapeBinary(state.binaryType, event.payload, ends), true);
1251
+ }
1252
+ //#endregion
1253
+ //#region src/compat/socket/codec-inbound.ts
1254
+ function driveInbound(state, transport, pending) {
1255
+ if (pending !== void 0 && pending.length > 0) ingest(state, pending);
1256
+ transport.on("data", (chunk) => {
1257
+ ingest(state, chunk);
1258
+ });
1259
+ }
1260
+ function ingest(state, chunk) {
1261
+ const handle = codecOf(state);
1262
+ if (handle === null) return;
1263
+ if (state.pendingInput.length > 0) {
1264
+ enqueue(state, chunk);
1265
+ return;
1266
+ }
1267
+ consume(state, handle, chunk);
1268
+ }
1269
+ function enqueue(state, chunk) {
1270
+ if (state.maxBufferedChunks > 0 && state.pendingInput.length >= state.maxBufferedChunks) refuseByCodec(state, "tooManyBufferedParts");
1271
+ else state.pendingInput.push(chunk);
1272
+ }
1273
+ function consume(state, handle, chunk) {
1274
+ let rest = chunk;
1275
+ while (rest.length > 0) {
1276
+ const outcome = feedCodec(handle, rest);
1277
+ try {
1278
+ deliver(state, () => {
1279
+ guardedResume(state);
1280
+ });
1281
+ } catch (error) {
1282
+ failOnThrow(state, error);
1283
+ throw error;
1284
+ }
1285
+ if (outcome.kind === "failed") {
1286
+ refuseByCodec(state, codecFailure(handle) ?? failureByCode(codecFailureCode(handle)));
1287
+ return;
1288
+ }
1289
+ if (outcome.kind === "stale-handle") return;
1290
+ const consumed = outcome.kind === "consumed" ? outcome.bytes : codecFeedResume(handle);
1291
+ if (consumed <= 0) return;
1292
+ rest = rest.subarray(consumed);
1293
+ if (outcome.kind === "consumed") return;
1294
+ if (isDeliveryPaused(state)) {
1295
+ enqueue(state, rest);
1296
+ return;
1297
+ }
1298
+ }
1299
+ }
1300
+ function failOnThrow(state, error) {
1301
+ if (state.readyState === 3) return;
1302
+ try {
1303
+ failConnection(state, error instanceof Error ? error : createError("ERR_PROTOCOL", String(error)));
1304
+ } finally {
1305
+ closeCodec(state);
1306
+ state.transport?.destroy();
1307
+ }
1308
+ }
1309
+ function guardedResume(state) {
1310
+ try {
1311
+ resume(state);
1312
+ } catch (error) {
1313
+ failOnThrow(state, error);
1314
+ throw error;
1315
+ }
1316
+ }
1317
+ function resume(state) {
1318
+ drainReleased(state, () => {
1319
+ guardedResume(state);
1320
+ });
1321
+ const queued = state.pendingInput;
1322
+ const handle = codecOf(state);
1323
+ state.pendingInput = [];
1324
+ if (handle === null) return;
1325
+ for (const [index, chunk] of queued.entries()) {
1326
+ if (codecOf(state) === null) return;
1327
+ consume(state, handle, chunk);
1328
+ if (!isDeliveryPaused(state)) continue;
1329
+ state.pendingInput = state.pendingInput.concat(queued.slice(index + 1));
1330
+ return;
1331
+ }
1332
+ }
1333
+ //#endregion
1334
+ //#region src/compat/socket/transport.ts
1335
+ const EMPTY = Buffer.alloc(0);
1336
+ function failTransport(state, error) {
1337
+ if (state.readyState === 3) return;
1338
+ state.readyState = 2;
1339
+ emitEvent(state, "error", error);
1340
+ }
1341
+ function terminateConnection(state) {
1342
+ if (state.readyState === 3) return;
1343
+ if (state.readyState === 0) {
1344
+ failConnection(state, createError("ERR_INVALID_STATE", "WebSocket was closed before the connection was established"));
1345
+ return;
1346
+ }
1347
+ state.readyState = 2;
1348
+ if (state.transport !== null) {
1349
+ state.transport.destroy();
1350
+ return;
1351
+ }
1352
+ finishConnection(state, CLOSE_ABNORMAL$1, EMPTY);
1353
+ }
1354
+ //#endregion
1355
+ //#region src/compat/socket/attach.ts
1356
+ function attachSocket(socket, transport, role, pending) {
1357
+ const state = socketStateOf(socket);
1358
+ if (state === void 0) throw createError("ERR_INVALID_HANDLE", "ventiws: attachSocket requires a ventiws socket record");
1359
+ if (!transport.readable || !transport.writable) {
1360
+ const gone = createError("ERR_INVALID_STATE", "ventiws: the transport was closed before the connection was established");
1361
+ process.nextTick(() => {
1362
+ failConnection(state, gone);
1363
+ });
1364
+ transport.destroy();
1365
+ return;
1366
+ }
1367
+ state.transport = transport;
1368
+ openCodec(state, role ?? CODEC_ROLE.server);
1369
+ transport.on("end", () => {
1370
+ if (state.readyState !== 3) state.readyState = 2;
1371
+ transport.end();
1372
+ });
1373
+ transport.on("error", (error) => {
1374
+ try {
1375
+ failTransport(state, asCodedError(error));
1376
+ } finally {
1377
+ transport.destroy();
1378
+ }
1379
+ });
1380
+ transport.on("close", () => {
1381
+ closeCodec(state);
1382
+ finishConnection(state, state.closeCode, state.closeReason);
1383
+ });
1384
+ openSocket(state);
1385
+ driveInbound(state, transport, pending);
1386
+ }
1387
+ function openSocket(state) {
1388
+ if (state.readyState === 1) return;
1389
+ state.readyState = 1;
1390
+ emitEvent(state, "open");
1391
+ }
1392
+ function asCodedError(error) {
1393
+ if (error instanceof Error) return error;
1394
+ return createError("ERR_PROTOCOL", `ventiws: the transport failed: ${String(error)}`);
1395
+ }
1396
+ //#endregion
1397
+ //#region src/compat/server/handshake-error.ts
1398
+ const HANDSHAKE_ERROR = Symbol("ventiws.handshakeError");
1399
+ function attachHandshakeError(socket) {
1400
+ const handler = () => {
1401
+ socket.destroy();
1402
+ };
1403
+ socket[HANDSHAKE_ERROR] = handler;
1404
+ socket.on("error", handler);
1405
+ }
1406
+ function detachHandshakeError(socket) {
1407
+ const record = socket;
1408
+ const handler = record[HANDSHAKE_ERROR];
1409
+ if (handler === void 0) return;
1410
+ socket.removeListener("error", handler);
1411
+ delete record[HANDSHAKE_ERROR];
1412
+ }
1413
+ //#endregion
1414
+ //#region src/compat/extensions/format.ts
1415
+ function formatExtension(name, parameters) {
1416
+ const parts = [name];
1417
+ for (const [key, value] of Object.entries(parameters)) parts.push(value === "" ? key : `${key}=${value}`);
1418
+ return parts.join("; ");
1419
+ }
1420
+ //#endregion
1421
+ //#region src/compat/extensions/negotiated.ts
1422
+ const PERMESSAGE_DEFLATE = "permessage-deflate";
1423
+ const NO_CONTEXT_TAKEOVER = {
1424
+ server_no_context_takeover: "",
1425
+ client_no_context_takeover: ""
1426
+ };
1427
+ //#endregion
1428
+ //#region src/compat/extensions/params.ts
1429
+ const MIN_WINDOW_BITS = 8;
1430
+ const MAX_WINDOW_BITS = 15;
1431
+ function normalizeParameters(parameters, isServer) {
1432
+ const out = {};
1433
+ for (const [key, values] of Object.entries(parameters)) {
1434
+ if (values.length > 1) return null;
1435
+ const parsed = parseParameter(key, values[0] ?? "", isServer);
1436
+ if (parsed === null) return null;
1437
+ out[key] = parsed;
1438
+ }
1439
+ return {
1440
+ server_no_context_takeover: out.server_no_context_takeover === true,
1441
+ client_no_context_takeover: out.client_no_context_takeover === true,
1442
+ server_max_window_bits: numberOf(out.server_max_window_bits),
1443
+ client_max_window_bits: windowOf(out.client_max_window_bits)
1444
+ };
1445
+ }
1446
+ function parseParameter(key, value, isServer) {
1447
+ if (key === "server_no_context_takeover" || key === "client_no_context_takeover") return value === "" ? true : null;
1448
+ if (key === "client_max_window_bits") {
1449
+ if (value === "") return isServer ? true : null;
1450
+ return windowSize(value);
1451
+ }
1452
+ if (key === "server_max_window_bits") return value === "" ? null : windowSize(value);
1453
+ return null;
1454
+ }
1455
+ function windowSize(value) {
1456
+ const parsed = Number(value);
1457
+ if (!Number.isInteger(parsed) || parsed < MIN_WINDOW_BITS || parsed > MAX_WINDOW_BITS) return null;
1458
+ return parsed;
1459
+ }
1460
+ function windowOf(value) {
1461
+ return typeof value === "number" || value === true ? value : void 0;
1462
+ }
1463
+ function numberOf(value) {
1464
+ return typeof value === "number" ? value : void 0;
1465
+ }
1466
+ //#endregion
1467
+ //#region src/compat/extensions/window.ts
1468
+ function narrowed(parameters, name, option) {
1469
+ if (typeof option !== "number" || option >= 15) return;
1470
+ parameters[name] = String(option);
1471
+ }
1472
+ //#endregion
1473
+ //#region src/compat/extensions/offer-window.ts
1474
+ function serverWindowUsable(options, offer) {
1475
+ const asked = offer.server_max_window_bits;
1476
+ if (asked === void 0) return true;
1477
+ if (options.serverMaxWindowBits === false) return false;
1478
+ if (typeof options.serverMaxWindowBits === "number") {
1479
+ if (options.serverMaxWindowBits > asked) return false;
1480
+ return options.serverMaxWindowBits >= 15;
1481
+ }
1482
+ return asked >= 15;
1483
+ }
1484
+ //#endregion
1485
+ //#region src/compat/extensions/decline.ts
1486
+ function declines(options, offer) {
1487
+ if (options.serverNoContextTakeover === false && offer.server_no_context_takeover) return true;
1488
+ if (offer.server_max_window_bits !== void 0 && !serverWindowUsable(options, offer)) return true;
1489
+ if (offer.client_max_window_bits === true && options.clientMaxWindowBits === false) return true;
1490
+ if (typeof options.clientMaxWindowBits === "number") {
1491
+ if (offer.client_max_window_bits === void 0) return true;
1492
+ const asked = offer.client_max_window_bits;
1493
+ if (typeof asked === "number" && options.clientMaxWindowBits > asked) return true;
1494
+ }
1495
+ return false;
1496
+ }
1497
+ //#endregion
1498
+ //#region src/compat/extensions/deflate.ts
1499
+ function acceptAsServer(offers, options) {
1500
+ if (options === false) return null;
1501
+ if (offers.length === 0) return null;
1502
+ for (const offer of offers) {
1503
+ const normalized = normalizeParameters(offer.parameters, true);
1504
+ if (normalized === null) return { refusal: "Invalid or unacceptable Sec-WebSocket-Extensions header" };
1505
+ if (declines(options, normalized)) continue;
1506
+ return { accepted: answer(options, normalized) };
1507
+ }
1508
+ return { refusal: "Invalid or unacceptable Sec-WebSocket-Extensions header" };
1509
+ }
1510
+ function acceptAsClient(response, options) {
1511
+ if (options === false) return null;
1512
+ const normalized = normalizeParameters(response.parameters, false);
1513
+ if (normalized === null) return { refusal: "Invalid Sec-WebSocket-Extensions header" };
1514
+ if (normalized.client_no_context_takeover && options.clientNoContextTakeover === false) return { refusal: "Unexpected parameter \"client_no_context_takeover\"" };
1515
+ const asked = normalized.client_max_window_bits;
1516
+ if (asked === void 0) return { accepted: answer(options, normalized) };
1517
+ if (asked === true) return { refusal: "Value must be specified for \"client_max_window_bits\"" };
1518
+ const wanted = options.clientMaxWindowBits;
1519
+ if (wanted === false) return { refusal: "Unexpected or invalid parameter \"client_max_window_bits\"" };
1520
+ if (typeof wanted === "number" && asked > wanted) return { refusal: "Unexpected or invalid parameter \"client_max_window_bits\"" };
1521
+ if (typeof asked === "number" && asked < 15) return { refusal: "Server requested a window this implementation cannot use" };
1522
+ return { accepted: answer(options, normalized) };
1523
+ }
1524
+ function answer(options, offer) {
1525
+ const parameters = { ...NO_CONTEXT_TAKEOVER };
1526
+ if (offer.server_no_context_takeover) parameters.server_no_context_takeover = "";
1527
+ narrowed(parameters, "server_max_window_bits", options.serverMaxWindowBits);
1528
+ narrowed(parameters, "client_max_window_bits", options.clientMaxWindowBits);
1529
+ return {
1530
+ name: PERMESSAGE_DEFLATE,
1531
+ parameters,
1532
+ header: formatExtension(PERMESSAGE_DEFLATE, parameters)
1533
+ };
1534
+ }
1535
+ //#endregion
1536
+ //#region src/compat/extensions/scan.ts
1537
+ const TOKEN$1 = /^[!#$%&'*+\-.0-9A-Z^_`|a-z~]+$/;
1538
+ const COMMA$1 = 44;
1539
+ const SEMICOLON = 59;
1540
+ const EQUALS = 61;
1541
+ const QUOTE = 34;
1542
+ const isWhitespace = (code) => code === 32 || code === 9;
1543
+ function scanConfiguration(header, start) {
1544
+ const name = readToken(header, start, "an extension name");
1545
+ if (name === null) throw unexpectedEnd(header, start);
1546
+ const parameters = {};
1547
+ let index = start + name.length;
1548
+ for (;;) {
1549
+ index = skipWhitespace(header, index);
1550
+ if (index >= header.length || header.charCodeAt(index) === COMMA$1) break;
1551
+ if (header.charCodeAt(index) !== SEMICOLON) throw syntax(header, index, "expected ';' or ','");
1552
+ const afterSemicolon = skipWhitespace(header, index + 1);
1553
+ if (afterSemicolon >= header.length) {
1554
+ index = afterSemicolon;
1555
+ break;
1556
+ }
1557
+ index = readParameter(header, index, parameters);
1558
+ }
1559
+ return {
1560
+ name,
1561
+ parameters,
1562
+ rest: index
1563
+ };
1564
+ }
1565
+ function readParameter(header, start, into) {
1566
+ const nameStart = skipWhitespace(header, start + 1);
1567
+ const name = readToken(header, nameStart, "a parameter name");
1568
+ if (name === null) throw unexpectedEnd(header, nameStart);
1569
+ const afterEquals = skipWhitespace(header, nameStart + name.length);
1570
+ if (header.charCodeAt(afterEquals) !== EQUALS) {
1571
+ push(into, name, "");
1572
+ return afterEquals;
1573
+ }
1574
+ const value = readValue(header, skipWhitespace(header, afterEquals + 1));
1575
+ push(into, name, value.value);
1576
+ return value.end;
1577
+ }
1578
+ function push(into, name, value) {
1579
+ const existing = into[name];
1580
+ if (existing === void 0) into[name] = [value];
1581
+ else existing.push(value);
1582
+ }
1583
+ function readValue(header, start) {
1584
+ if (header.charCodeAt(start) !== QUOTE) {
1585
+ const token = readToken(header, start, "a parameter value");
1586
+ if (token === null) throw unexpectedEnd(header, start);
1587
+ if (!TOKEN$1.test(token)) throw syntax(header, start, "invalid parameter value");
1588
+ return {
1589
+ value: token,
1590
+ end: start + token.length
1591
+ };
1592
+ }
1593
+ let index = start + 1;
1594
+ let value = "";
1595
+ for (;;) {
1596
+ if (index >= header.length) throw unexpectedEnd(header, start);
1597
+ const char = header[index];
1598
+ if (char === "\\") {
1599
+ const escaped = header[index + 1];
1600
+ if (escaped === void 0) throw unexpectedEnd(header, index);
1601
+ value += escaped;
1602
+ index += 2;
1603
+ continue;
1604
+ }
1605
+ if (char === "\"") return {
1606
+ value,
1607
+ end: index + 1
1608
+ };
1609
+ value += char;
1610
+ index += 1;
1611
+ }
1612
+ }
1613
+ function readToken(header, start, what) {
1614
+ let index = start;
1615
+ while (index < header.length) {
1616
+ const code = header.charCodeAt(index);
1617
+ if (isWhitespace(code) || code === SEMICOLON || code === COMMA$1 || code === EQUALS) break;
1618
+ if (code === QUOTE) throw syntax(header, index, `expected ${what}`);
1619
+ index += 1;
1620
+ }
1621
+ if (index === start) return null;
1622
+ return header.slice(start, index);
1623
+ }
1624
+ function skipWhitespace(header, start) {
1625
+ let index = start;
1626
+ while (index < header.length && isWhitespace(header.charCodeAt(index))) index += 1;
1627
+ return index;
1628
+ }
1629
+ function syntax(header, at, detail) {
1630
+ return /* @__PURE__ */ new SyntaxError(`Invalid Sec-WebSocket-Extensions header at ${at}: ${detail}`);
1631
+ }
1632
+ function unexpectedEnd(header, at) {
1633
+ return /* @__PURE__ */ new SyntaxError(`Unexpected end of Sec-WebSocket-Extensions header at ${at}`);
1634
+ }
1635
+ //#endregion
1636
+ //#region src/compat/extensions/grammar.ts
1637
+ function parseExtensions(header) {
1638
+ const out = /* @__PURE__ */ new Map();
1639
+ let index = 0;
1640
+ while (index < header.length) {
1641
+ const scanned = scanConfiguration(header, index);
1642
+ const extension = {
1643
+ name: scanned.name,
1644
+ parameters: scanned.parameters
1645
+ };
1646
+ index = skipWhitespace(header, scanned.rest);
1647
+ if (index < header.length) {
1648
+ if (header.charCodeAt(index) !== COMMA) throw syntax(header, index, "expected ',' between extensions");
1649
+ index = skipWhitespace(header, index + 1);
1650
+ }
1651
+ const existing = out.get(extension.name);
1652
+ if (existing === void 0) out.set(extension.name, [extension]);
1653
+ else existing.push(extension);
1654
+ }
1655
+ if (out.size === 0) throw unexpectedEnd(header, 0);
1656
+ return out;
1657
+ }
1658
+ const COMMA = 44;
1659
+ //#endregion
1660
+ //#region src/compat/server/negotiate.ts
1661
+ function negotiateExtensions(state, request, socket) {
1662
+ const options = state.normalizedOptions.perMessageDeflate;
1663
+ if (options === false) return { outcome: "none" };
1664
+ const header = request.headers["sec-websocket-extensions"];
1665
+ if (header === void 0) return { outcome: "none" };
1666
+ let offers;
1667
+ try {
1668
+ offers = parseExtensions(Array.isArray(header) ? header.join(", ") : header).get("permessage-deflate") ?? [];
1669
+ } catch {
1670
+ abortOrEmit(state, request, socket, 400, "Invalid Sec-WebSocket-Extensions header");
1671
+ return { outcome: "refused" };
1672
+ }
1673
+ const outcome = acceptAsServer(offers, options);
1674
+ if (outcome === null) return { outcome: "none" };
1675
+ if ("refusal" in outcome) {
1676
+ abortOrEmit(state, request, socket, 400, outcome.refusal);
1677
+ return { outcome: "refused" };
1678
+ }
1679
+ return {
1680
+ outcome: "accepted",
1681
+ accepted: outcome.accepted
1682
+ };
1683
+ }
1684
+ //#endregion
1685
+ //#region src/compat/extensions/threshold.ts
1686
+ function thresholdOf(options) {
1687
+ if (options === false) return 0;
1688
+ return options.threshold;
1689
+ }
1690
+ //#endregion
1691
+ //#region src/compat/server/accept.ts
1692
+ const UPGRADED = Symbol("ventiws.upgraded");
1693
+ function completeUpgrade(state, request, socket, head, key, protocols, callback) {
1694
+ if (!socket.readable || !socket.writable) {
1695
+ socket.destroy();
1696
+ return;
1697
+ }
1698
+ if (socket[UPGRADED] === true) throw createError("ERR_INVALID_STATE", "server.handleUpgrade() was called more than once with the same socket, possibly due to a misconfiguration");
1699
+ if (state.lifecycle !== "running") {
1700
+ abortHandshake(socket, 503);
1701
+ return;
1702
+ }
1703
+ const headers = [
1704
+ "HTTP/1.1 101 Switching Protocols",
1705
+ "Upgrade: websocket",
1706
+ "Connection: Upgrade",
1707
+ `Sec-WebSocket-Accept: ${socketAccept(key)}`
1708
+ ];
1709
+ const negotiation = negotiateExtensions(state, request, socket);
1710
+ if (negotiation.outcome === "refused") return;
1711
+ const extensions = negotiation.outcome === "accepted" ? negotiation.accepted : null;
1712
+ const SocketClass = state.options.WebSocket ?? state.webSocket;
1713
+ const accepted = Reflect.construct(SocketClass, [
1714
+ null,
1715
+ void 0,
1716
+ state.options
1717
+ ]);
1718
+ const protocol = selectProtocol(state, protocols, request);
1719
+ if (protocol) headers.push(`Sec-WebSocket-Protocol: ${protocol}`);
1720
+ if (extensions) headers.push(`Sec-WebSocket-Extensions: ${extensions.header}`);
1721
+ emitEvent(state, "headers", headers, request);
1722
+ Object.defineProperty(socket, UPGRADED, { value: true });
1723
+ detachHandshakeError(socket);
1724
+ socket.write(headers.concat("\r\n").join("\r\n"));
1725
+ const acceptedState = socketStateOf(accepted);
1726
+ if (acceptedState !== void 0 && protocol) acceptedState.protocol = protocol;
1727
+ if (acceptedState !== void 0) {
1728
+ acceptedState.closeTimeout = state.normalizedOptions.closeTimeout;
1729
+ acceptedState.autoPong = state.normalizedOptions.autoPong;
1730
+ acceptedState.allowSynchronousEvents = state.normalizedOptions.allowSynchronousEvents;
1731
+ acceptedState.validateUtf8 = !state.normalizedOptions.skipUTF8Validation;
1732
+ acceptedState.maxPayload = state.normalizedOptions.maxPayload;
1733
+ acceptedState.maxFragments = state.normalizedOptions.maxFragments;
1734
+ acceptedState.maxBufferedChunks = state.normalizedOptions.maxBufferedChunks;
1735
+ acceptedState.compressible = extensions !== null;
1736
+ if (extensions) acceptedState.extensions = extensions.header;
1737
+ acceptedState.threshold = thresholdOf(state.normalizedOptions.perMessageDeflate);
1738
+ }
1739
+ attachSocket(accepted, socket);
1740
+ if (state.normalizedOptions.clientTracking) trackClient(state, accepted);
1741
+ callback(accepted, request);
1742
+ }
1743
+ //#endregion
1744
+ //#region src/compat/server/upgrade.ts
1745
+ function defaultShouldHandle(state, request) {
1746
+ const path = state.options.path;
1747
+ if (!path) return true;
1748
+ const url = request.url ?? "";
1749
+ const index = url.indexOf("?");
1750
+ return (index !== -1 ? url.slice(0, index) : url) === path;
1751
+ }
1752
+ function shouldHandle(state, request) {
1753
+ const assigned = state.record?.shouldHandle;
1754
+ if (typeof assigned === "function") return assigned(request);
1755
+ return defaultShouldHandle(state, request);
1756
+ }
1757
+ function handleUpgrade(state, request, socket, head, callback) {
1758
+ attachHandshakeError(socket);
1759
+ const key = request.headers["sec-websocket-key"];
1760
+ const upgrade = request.headers.upgrade;
1761
+ const version = Number(request.headers["sec-websocket-version"]);
1762
+ if (request.method !== "GET") {
1763
+ abortOrEmit(state, request, socket, 405, "Invalid HTTP method");
1764
+ return;
1765
+ }
1766
+ if (typeof upgrade !== "string" || upgrade.toLowerCase() !== "websocket") {
1767
+ abortOrEmit(state, request, socket, 400, "Invalid Upgrade header");
1768
+ return;
1769
+ }
1770
+ if (typeof key !== "string" || !KEY_PATTERN.test(key)) {
1771
+ abortOrEmit(state, request, socket, 400, "Missing or invalid Sec-WebSocket-Key header");
1772
+ return;
1773
+ }
1774
+ if (version !== 13 && version !== 8) {
1775
+ abortOrEmit(state, request, socket, 400, "Missing or invalid Sec-WebSocket-Version header", { "Sec-WebSocket-Version": "13, 8" });
1776
+ return;
1777
+ }
1778
+ if (!shouldHandle(state, request)) {
1779
+ abortHandshake(socket, 400);
1780
+ return;
1781
+ }
1782
+ const protocols = parseProtocols(state, request, socket);
1783
+ if (protocols === void 0) return;
1784
+ const verify = state.options.verifyClient ?? null;
1785
+ if (verify === null) {
1786
+ completeUpgrade(state, request, socket, head, key, protocols, callback);
1787
+ return;
1788
+ }
1789
+ const info = {
1790
+ origin: requestHeader(request, version === 8 ? "sec-websocket-origin" : "origin"),
1791
+ secure: isSecure(request),
1792
+ req: request
1793
+ };
1794
+ if (verify.length === 2) {
1795
+ verify(info, (verified, code, message, headers) => {
1796
+ if (!verified) {
1797
+ abortHandshake(socket, code || 401, message, headers);
1798
+ return;
1799
+ }
1800
+ completeUpgrade(state, request, socket, head, key, protocols, callback);
1801
+ });
1802
+ return;
1803
+ }
1804
+ if (!verify(info)) {
1805
+ abortHandshake(socket, 401);
1806
+ return;
1807
+ }
1808
+ completeUpgrade(state, request, socket, head, key, protocols, callback);
1809
+ }
1810
+ //#endregion
1811
+ //#region src/compat/server/listeners.ts
1812
+ function wireServer(state) {
1813
+ const httpServer = state.server;
1814
+ if (httpServer === null) return;
1815
+ const handlers = {
1816
+ onListening: () => {
1817
+ emitEvent(state, "listening");
1818
+ },
1819
+ onError: (error) => {
1820
+ emitEvent(state, "error", error);
1821
+ },
1822
+ onUpgrade: (request, socket, head) => {
1823
+ handleUpgrade(state, request, socket, head, (accepted, incoming) => {
1824
+ emitEvent(state, "connection", accepted, incoming);
1825
+ });
1826
+ }
1827
+ };
1828
+ httpServer.on("listening", handlers.onListening);
1829
+ httpServer.on("error", handlers.onError);
1830
+ httpServer.on("upgrade", handlers.onUpgrade);
1831
+ state.removeListeners = () => {
1832
+ httpServer.removeListener("listening", handlers.onListening);
1833
+ httpServer.removeListener("error", handlers.onError);
1834
+ httpServer.removeListener("upgrade", handlers.onUpgrade);
1835
+ };
1836
+ }
1837
+ //#endregion
1838
+ //#region src/compat/options/bounded.ts
1839
+ function maxPayloadOf(source, ceiling) {
1840
+ return boundedOption("maxPayload", source, DEFAULT_MAX_PAYLOAD, ceiling);
1841
+ }
1842
+ function maxFragmentsOf(source, ceiling) {
1843
+ return boundedOption("maxFragments", source, DEFAULT_MAX_FRAGMENTS, ceiling);
1844
+ }
1845
+ function maxBufferedChunksOf(source) {
1846
+ const raw = source.maxBufferedChunks;
1847
+ if (raw === void 0) return DEFAULT_MAX_BUFFERED_CHUNKS;
1848
+ if (typeof raw !== "number" || !Number.isSafeInteger(raw) || raw < 0) invalidOption(`The maxBufferedChunks option must be an integer in [0, ${Number.MAX_SAFE_INTEGER}] (received ${String(raw)})`, RangeError);
1849
+ return raw;
1850
+ }
1851
+ function boundedOption(name, source, fallback, readCeiling) {
1852
+ const raw = source[name];
1853
+ if (raw === void 0) return fallback;
1854
+ if (typeof raw === "number" && Number.isSafeInteger(raw) && raw >= 0 && raw === 0) return readCeiling();
1855
+ const ceiling = readCeiling();
1856
+ if (typeof raw !== "number" || !Number.isSafeInteger(raw) || raw < 0 || raw > ceiling) invalidOption(`The ${name} option must be an integer in [0, ${ceiling}] (received ${String(raw)})`, RangeError);
1857
+ return raw;
1858
+ }
1859
+ //#endregion
1860
+ //#region src/compat/options/server.ts
1861
+ function normalizeServerOptions(options) {
1862
+ const source = { ...options };
1863
+ const port = source.port ?? null;
1864
+ const server = source.server ?? null;
1865
+ const noServer = source.noServer ?? false;
1866
+ if (isAmbiguousListenTarget(port, server, noServer)) invalidOption("One and only one of the \"port\", \"server\", or \"noServer\" options must be specified");
1867
+ return {
1868
+ host: source.host ?? null,
1869
+ port,
1870
+ backlog: source.backlog ?? null,
1871
+ path: source.path ?? null,
1872
+ server,
1873
+ noServer,
1874
+ clientTracking: source.clientTracking === void 0 ? true : Boolean(source.clientTracking),
1875
+ allowSynchronousEvents: source.allowSynchronousEvents ?? true,
1876
+ autoPong: source.autoPong ?? true,
1877
+ maxPayload: maxPayloadOf(source, () => codecLimits().maxPayloadBytes),
1878
+ maxFragments: maxFragmentsOf(source, () => codecLimits().maxFragments),
1879
+ maxBufferedChunks: maxBufferedChunksOf(source),
1880
+ skipUTF8Validation: source.skipUTF8Validation ?? false,
1881
+ perMessageDeflate: normalizePerMessageDeflate(source.perMessageDeflate, false),
1882
+ closeTimeout: closeTimeoutOf(source),
1883
+ verifyClient: source.verifyClient ?? null,
1884
+ handleProtocols: source.handleProtocols ?? null,
1885
+ WebSocket: source.WebSocket
1886
+ };
1887
+ }
1888
+ function isAmbiguousListenTarget(port, server, noServer) {
1889
+ const hasPort = port !== null;
1890
+ const hasServer = Boolean(server);
1891
+ if (hasPort && (hasServer || noServer)) return true;
1892
+ if (hasServer && noServer) return true;
1893
+ return !hasPort && !hasServer && !noServer;
1894
+ }
1895
+ //#endregion
1896
+ //#region src/compat/server/server.ts
1897
+ const SERVER_BRAND = Symbol("ventiws.server");
1898
+ function requireAddon() {
1899
+ loadAddon();
1900
+ }
1901
+ function isServer(value) {
1902
+ if (typeof value !== "object" || value === null) return false;
1903
+ return value[SERVER_BRAND] === true;
1904
+ }
1905
+ function createWebSocketServer(socketClass, options, callback) {
1906
+ requireAddon();
1907
+ const resolved = {
1908
+ allowSynchronousEvents: true,
1909
+ autoPong: true,
1910
+ maxBufferedChunks: 262144,
1911
+ maxFragments: 16384,
1912
+ closeTimeout: 3e4,
1913
+ maxPayload: 104857600,
1914
+ skipUTF8Validation: false,
1915
+ perMessageDeflate: false,
1916
+ handleProtocols: null,
1917
+ clientTracking: true,
1918
+ verifyClient: null,
1919
+ noServer: false,
1920
+ backlog: null,
1921
+ server: null,
1922
+ host: null,
1923
+ path: null,
1924
+ port: null,
1925
+ WebSocket: socketClass,
1926
+ ...options
1927
+ };
1928
+ if (resolved.perMessageDeflate === true) resolved.perMessageDeflate = {};
1929
+ const normalized = normalizeServerOptions(resolved);
1930
+ const state = {
1931
+ options: resolved,
1932
+ normalizedOptions: normalized,
1933
+ path: resolved.path ?? "",
1934
+ clients: normalized.clientTracking ? /* @__PURE__ */ new Set() : void 0,
1935
+ webSocket: resolved.WebSocket ?? socketClass,
1936
+ server: null,
1937
+ lifecycle: "running",
1938
+ record: null,
1939
+ shouldEmitClose: false,
1940
+ removeListeners: null,
1941
+ listeners: createRegistry(),
1942
+ maxListeners: 10,
1943
+ warned: /* @__PURE__ */ new Set(),
1944
+ target: void 0
1945
+ };
1946
+ if (resolved.port !== null && resolved.port !== void 0) {
1947
+ const httpServer = createServer((_request, response) => {
1948
+ const body = STATUS_CODES[426] ?? "Upgrade Required";
1949
+ response.writeHead(426, {
1950
+ "Content-Length": Buffer.byteLength(body),
1951
+ "Content-Type": "text/plain"
1952
+ });
1953
+ response.end(body);
1954
+ });
1955
+ state.server = httpServer;
1956
+ httpServer.listen(resolved.port, resolved.host ?? void 0, resolved.backlog ?? void 0, callback);
1957
+ } else if (resolved.server) state.server = resolved.server;
1958
+ if (state.server !== null) wireServer(state);
1959
+ const server = {
1960
+ ...createEmitter(state),
1961
+ options: resolved,
1962
+ path: state.path,
1963
+ ...state.clients === void 0 ? {} : { clients: state.clients },
1964
+ address: () => addressOf(state),
1965
+ close: (closeCallback) => {
1966
+ closeWebSocketServer(state, closeCallback);
1967
+ },
1968
+ handleUpgrade: (request, socket, head, upgradeCallback) => {
1969
+ handleUpgrade(state, request, socket, head, upgradeCallback);
1970
+ },
1971
+ shouldHandle: (request) => defaultShouldHandle(state, request)
1972
+ };
1973
+ state.target = server;
1974
+ state.record = server;
1975
+ Object.defineProperty(server, SERVER_BRAND, { value: true });
1976
+ return server;
1977
+ }
1978
+ //#endregion
1979
+ //#region src/compat/client/protocols.ts
1980
+ const TOKEN = /^[!#$%&'*+\-.^_`|~\dA-Za-z]+$/;
1981
+ function normalizeProtocols(protocols) {
1982
+ if (protocols === void 0) return [];
1983
+ const list = typeof protocols === "string" ? [protocols] : protocols;
1984
+ if (!Array.isArray(list)) throw createError("ERR_INVALID_OPTION", "An invalid or duplicated subprotocol was specified", SyntaxError);
1985
+ const seen = /* @__PURE__ */ new Set();
1986
+ for (const protocol of list) {
1987
+ if (typeof protocol !== "string" || !TOKEN.test(protocol) || seen.has(protocol)) throw createError("ERR_INVALID_OPTION", "An invalid or duplicated subprotocol was specified", SyntaxError);
1988
+ seen.add(protocol);
1989
+ }
1990
+ return [...list];
1991
+ }
1992
+ function protocolSet(protocols) {
1993
+ return new Set(protocols);
1994
+ }
1995
+ function promoteOptions(protocols, options) {
1996
+ if (typeof protocols !== "object" || protocols === null || Array.isArray(protocols)) return {
1997
+ protocols,
1998
+ options
1999
+ };
2000
+ return {
2001
+ protocols: void 0,
2002
+ options: protocols
2003
+ };
2004
+ }
2005
+ function protocolRejection(chosen, offered) {
2006
+ if (chosen === void 0) return offered.size > 0 ? "Server sent no subprotocol" : null;
2007
+ if (offered.size === 0) return "Server sent a subprotocol but none was requested";
2008
+ if (!offered.has(chosen)) return "Server sent an invalid subprotocol";
2009
+ return null;
2010
+ }
2011
+ //#endregion
2012
+ //#region src/compat/events/dom-events.ts
2013
+ function createOpenEvent(target) {
2014
+ return {
2015
+ type: "open",
2016
+ target
2017
+ };
2018
+ }
2019
+ function createMessageEvent(target, data) {
2020
+ return {
2021
+ type: "message",
2022
+ target,
2023
+ data
2024
+ };
2025
+ }
2026
+ function createCloseEvent(target, code, reason, wasClean) {
2027
+ return {
2028
+ type: "close",
2029
+ target,
2030
+ code,
2031
+ reason: reason.toString(),
2032
+ wasClean
2033
+ };
2034
+ }
2035
+ function createErrorEvent(target, error) {
2036
+ return {
2037
+ type: "error",
2038
+ target,
2039
+ error,
2040
+ message: error.message
2041
+ };
2042
+ }
2043
+ function callListener(handler, target, event) {
2044
+ if (typeof handler === "function") {
2045
+ Reflect.apply(handler, target, [event]);
2046
+ return;
2047
+ }
2048
+ if (typeof handler === "object" && handler !== null) {
2049
+ const handleEvent = handler.handleEvent;
2050
+ if (typeof handleEvent === "function") {
2051
+ Reflect.apply(handleEvent, handler, [event]);
2052
+ return;
2053
+ }
2054
+ }
2055
+ throw new TypeError("EventListener must be a function or an object with a handleEvent method");
2056
+ }
2057
+ //#endregion
2058
+ //#region src/compat/events/dom-listeners.ts
2059
+ const DOM_TYPES = [
2060
+ "open",
2061
+ "error",
2062
+ "close",
2063
+ "message"
2064
+ ];
2065
+ const ATTRIBUTES = [
2066
+ ["onopen", "open"],
2067
+ ["onerror", "error"],
2068
+ ["onclose", "close"],
2069
+ ["onmessage", "message"]
2070
+ ];
2071
+ function isDomType(type) {
2072
+ return DOM_TYPES.includes(type);
2073
+ }
2074
+ function isAttribute(entry) {
2075
+ const tagged = asTagged(entry);
2076
+ return tagged[DOM_WRAPPER] === true && tagged.attribute === true;
2077
+ }
2078
+ function wrapperFor(state, type, handler) {
2079
+ const socket = state.target;
2080
+ switch (type) {
2081
+ case "open": return (() => {
2082
+ callListener(handler, socket, createOpenEvent(socket));
2083
+ });
2084
+ case "message": return ((data, isBinary) => {
2085
+ callListener(handler, socket, createMessageEvent(socket, isBinary ? data : data.toString()));
2086
+ });
2087
+ case "close": return ((code, reason) => {
2088
+ const clean = state.closeFrameReceived && state.closeFrameSent;
2089
+ callListener(handler, socket, createCloseEvent(socket, code, reason, clean));
2090
+ });
2091
+ case "error": return ((error) => {
2092
+ callListener(handler, socket, createErrorEvent(socket, error));
2093
+ });
2094
+ }
2095
+ }
2096
+ function addEventListener(state, type, handler, options = {}) {
2097
+ if (!isDomType(type)) return;
2098
+ const attributed = options.attribute === true;
2099
+ const duplicate = (state.listeners[type] ?? []).some((entry) => isDomEntry(entry) && originalOf(entry) === handler && !isAttribute(entry));
2100
+ if (!attributed && duplicate) return;
2101
+ const wrapper = wrapperFor(state, type, handler);
2102
+ wrapper.attribute = attributed;
2103
+ wrapper.listener = handler;
2104
+ wrapper[DOM_WRAPPER] = true;
2105
+ const entry = options.once ? onceWrapper(state, type, wrapper) : wrapper;
2106
+ state.listeners = subscribe(state.listeners, type, entry);
2107
+ }
2108
+ function removeEventListener(state, type, handler) {
2109
+ if (!isDomType(type)) return;
2110
+ state.listeners = unsubscribeMatching(state.listeners, type, (entry) => isDomEntry(entry) && originalOf(entry) === handler && !isAttribute(entry));
2111
+ }
2112
+ function findAttributeListener(state, type) {
2113
+ for (const entry of state.listeners[type] ?? []) if (isAttribute(entry)) return originalOf(entry);
2114
+ return null;
2115
+ }
2116
+ function defineDomAttributes(socket, state) {
2117
+ for (const [attribute, type] of ATTRIBUTES) Object.defineProperty(socket, attribute, {
2118
+ enumerable: true,
2119
+ configurable: true,
2120
+ get: () => findAttributeListener(state, type),
2121
+ set: (handler) => {
2122
+ state.listeners = unsubscribeMatching(state.listeners, type, (entry) => isAttribute(entry));
2123
+ if (typeof handler !== "function") return;
2124
+ addEventListener(state, type, handler, { attribute: true });
2125
+ }
2126
+ });
2127
+ }
2128
+ //#endregion
2129
+ //#region src/compat/socket/gating.ts
2130
+ function pauseConnection(state) {
2131
+ if (state.readyState === 0 || state.readyState === 3) return;
2132
+ state.isPaused = true;
2133
+ if (state.attachment === null) {
2134
+ state.transport?.pause();
2135
+ return;
2136
+ }
2137
+ pauseSocket(state.attachment.server, state.attachment.connection);
2138
+ }
2139
+ function resumeConnection(state) {
2140
+ if (state.readyState === 0 || state.readyState === 3) return;
2141
+ state.isPaused = false;
2142
+ if (state.attachment === null) {
2143
+ state.transport?.resume();
2144
+ return;
2145
+ }
2146
+ resumeSocket(state.attachment.server, state.attachment.connection);
2147
+ }
2148
+ //#endregion
2149
+ //#region src/compat/socket/control.ts
2150
+ function controlFrame(state, kind, data, mask, callback) {
2151
+ if (state.readyState === 0) throw notOpenError(0);
2152
+ let payload = data;
2153
+ let failure = callback;
2154
+ if (typeof payload === "function") {
2155
+ failure = payload;
2156
+ payload = void 0;
2157
+ } else if (typeof mask === "function") {
2158
+ failure = mask;
2159
+ mask = void 0;
2160
+ }
2161
+ if (typeof payload === "number") payload = String(payload);
2162
+ if (state.readyState !== 1) {
2163
+ defer(failure, notOpenError(state.readyState));
2164
+ return;
2165
+ }
2166
+ const bytes = toPayload(payload).bytes;
2167
+ if (!isValidControlPayload(bytes)) throw createError("ERR_INVALID_OPTION", "The data size must not be greater than 125 bytes", RangeError);
2168
+ const status = writeFrame(state, kind, bytes, true, false, mask !== false);
2169
+ if (status === "ok") {
2170
+ defer(failure);
2171
+ return;
2172
+ }
2173
+ defer(failure, frameError(status));
2174
+ }
2175
+ //#endregion
2176
+ //#region src/compat/socket/send-failure.ts
2177
+ const NOT_ATTACHED = "ventiws: the socket has no native transport attached; engine socket adoption is not implemented yet";
2178
+ function notAttachedError() {
2179
+ return createError("ERR_INVALID_STATE", NOT_ATTACHED);
2180
+ }
2181
+ function reportFailure(state, callback, error) {
2182
+ if (typeof callback === "function") {
2183
+ defer(callback, error);
2184
+ return;
2185
+ }
2186
+ reportWithoutClosing(state, error);
2187
+ }
2188
+ //#endregion
2189
+ //#region src/compat/socket/codec-send.ts
2190
+ function sendFramed(state, payload, options, callback) {
2191
+ const framing = frameOptions(options, payload.binary, state.isServer);
2192
+ const status = writeFrame(state, state.fragmentsOpen ? "continuation" : framing.binary ? "binary" : "text", payload.bytes, framing.fin, mayCompress(state, framing, payload.bytes.length), framing.mask);
2193
+ if (status === "ok") state.fragmentsOpen = !framing.fin;
2194
+ switch (status) {
2195
+ case "ok":
2196
+ defer(callback);
2197
+ return;
2198
+ case "backpressure":
2199
+ case "closing":
2200
+ case "closed":
2201
+ defer(callback, notOpenError(state.readyState));
2202
+ return;
2203
+ case "invalid-handle":
2204
+ case "payload-too-large":
2205
+ case "protocol-error":
2206
+ reportFailure(state, callback, frameError(status));
2207
+ return;
2208
+ }
2209
+ throw unhandledFrameStatus(status);
2210
+ }
2211
+ function mayCompress(state, framing, length) {
2212
+ if (!framing.compress || !state.compressible || !framing.fin || state.fragmentsOpen) return false;
2213
+ return length >= state.threshold;
2214
+ }
2215
+ function frameOptions(options, autodetected, isServer) {
2216
+ const source = typeof options === "object" && options !== null ? options : {};
2217
+ return {
2218
+ binary: booleanOr$1(source.binary, autodetected),
2219
+ fin: booleanOr$1(source.fin, true),
2220
+ compress: booleanOr$1(source.compress, true),
2221
+ mask: booleanOr$1(source.mask, !isServer)
2222
+ };
2223
+ }
2224
+ function booleanOr$1(value, fallback) {
2225
+ return typeof value === "boolean" ? value : fallback;
2226
+ }
2227
+ function unhandledFrameStatus(status) {
2228
+ return createError("ERR_INVALID_STATE", `ventiws: the codec reported an unknown frame status "${String(status)}"`);
2229
+ }
2230
+ //#endregion
2231
+ //#region src/compat/socket/send-blob.ts
2232
+ function isBlob(value) {
2233
+ return typeof value === "object" && value !== null && typeof value.arrayBuffer === "function" && typeof value.size === "number";
2234
+ }
2235
+ function sendBlob(state, blob, options, callback) {
2236
+ if (state.readyState !== 1) {
2237
+ defer(callback, notOpenError(state.readyState));
2238
+ return;
2239
+ }
2240
+ const reading = blob.arrayBuffer().then((buffer) => {
2241
+ if (state.readyState !== 1) {
2242
+ defer(callback, notOpenError(state.readyState));
2243
+ return;
2244
+ }
2245
+ framePayload(state, Buffer.from(buffer), {
2246
+ ...asOptions(options),
2247
+ binary: true
2248
+ }, callback);
2249
+ }).catch((error) => {
2250
+ defer(callback, error instanceof Error ? error : createError("ERR_INVALID_OPTION", String(error)));
2251
+ }).finally(() => {
2252
+ if (state.pendingSend === reading) state.pendingSend = null;
2253
+ });
2254
+ state.pendingSend = reading;
2255
+ }
2256
+ function afterPendingSend(state, work) {
2257
+ const pending = state.pendingSend;
2258
+ if (pending === null) {
2259
+ work();
2260
+ return;
2261
+ }
2262
+ state.pendingSend = pending.then(work, work);
2263
+ }
2264
+ function asOptions(options) {
2265
+ return typeof options === "object" && options !== null ? { ...options } : {};
2266
+ }
2267
+ //#endregion
2268
+ //#region src/compat/socket/send.ts
2269
+ function sendData(state, data, options, callback) {
2270
+ if (state.readyState === 0) throw notOpenError(0);
2271
+ afterPendingSend(state, () => framePayload(state, data, options, callback));
2272
+ }
2273
+ function framePayload(state, data, options, callback) {
2274
+ const failure = resolveCallback(options, callback);
2275
+ if (isBlob(data)) {
2276
+ sendBlob(state, data, options, failure);
2277
+ return;
2278
+ }
2279
+ const payload = toPayload(data);
2280
+ if (state.readyState !== 1) {
2281
+ defer(failure, notOpenError(state.readyState));
2282
+ return;
2283
+ }
2284
+ if (state.codec !== null) {
2285
+ sendFramed(state, payload, options, failure);
2286
+ return;
2287
+ }
2288
+ if (state.attachment === null) {
2289
+ if (typeof failure === "function") defer(failure, notAttachedError());
2290
+ else reportWithoutClosing(state, notAttachedError());
2291
+ return;
2292
+ }
2293
+ const binary = sendBinary(options, payload.binary);
2294
+ applySendStatus(state, sendSocket(state.attachment.server, state.attachment.connection, payload.bytes, binary), payload.bytes.length, failure);
2295
+ }
2296
+ function resolveCallback(options, callback) {
2297
+ if (typeof options === "function") return options;
2298
+ return callback;
2299
+ }
2300
+ function sendBinary(options, fallback) {
2301
+ if (typeof options !== "object" || options === null) return fallback;
2302
+ const binary = options.binary;
2303
+ return typeof binary === "boolean" ? binary : fallback;
2304
+ }
2305
+ function applySendStatus(state, status, length, callback) {
2306
+ switch (status) {
2307
+ case "ok":
2308
+ defer(callback);
2309
+ return;
2310
+ case "backpressure":
2311
+ reportFailure(state, callback, createError("ERR_BACKPRESSURE", "ventiws: the outbound staging ring is full"));
2312
+ return;
2313
+ case "closing":
2314
+ case "closed":
2315
+ defer(callback, notOpenError(state.readyState));
2316
+ return;
2317
+ case "invalid-handle":
2318
+ reportFailure(state, callback, createError("ERR_INVALID_HANDLE", "ventiws: the connection handle is stale"));
2319
+ return;
2320
+ case "payload-too-large":
2321
+ case "invalid-close-code":
2322
+ case "invalid-close-reason":
2323
+ case "protocol-error":
2324
+ case "policy-violation":
2325
+ reportFailure(state, callback, statusError(status));
2326
+ return;
2327
+ }
2328
+ throw unhandledStatus(status);
2329
+ }
2330
+ function unhandledStatus(status) {
2331
+ return createError("ERR_INVALID_STATE", `ventiws: the engine reported an unknown socket status "${String(status)}"`);
2332
+ }
2333
+ //#endregion
2334
+ //#region src/compat/socket/record.ts
2335
+ const BINARY_TYPE_VALUES = [
2336
+ "nodebuffer",
2337
+ "arraybuffer",
2338
+ "fragments",
2339
+ "blob"
2340
+ ];
2341
+ function buildSocketRecord(state) {
2342
+ const socket = {
2343
+ CONNECTING: 0,
2344
+ OPEN: 1,
2345
+ CLOSING: 2,
2346
+ CLOSED: 3,
2347
+ get binaryType() {
2348
+ return state.binaryType;
2349
+ },
2350
+ set binaryType(value) {
2351
+ if (!BINARY_TYPE_VALUES.includes(value)) return;
2352
+ state.binaryType = value;
2353
+ },
2354
+ get bufferedAmount() {
2355
+ return bufferedAmountOf(state);
2356
+ },
2357
+ get extensions() {
2358
+ return state.extensions;
2359
+ },
2360
+ get isPaused() {
2361
+ return state.isPaused;
2362
+ },
2363
+ get protocol() {
2364
+ return state.protocol;
2365
+ },
2366
+ get readyState() {
2367
+ return state.readyState;
2368
+ },
2369
+ get url() {
2370
+ return state.url;
2371
+ },
2372
+ ...createEmitter(state),
2373
+ send: (data, sendOptions, callback) => {
2374
+ sendData(state, data, sendOptions, callback);
2375
+ },
2376
+ ping: (data, mask, callback) => {
2377
+ controlFrame(state, "ping", data, mask, callback);
2378
+ },
2379
+ pong: (data, mask, callback) => {
2380
+ controlFrame(state, "pong", data, mask, callback);
2381
+ },
2382
+ close: (code, reason) => {
2383
+ closeConnection(state, code, reason);
2384
+ },
2385
+ terminate: () => {
2386
+ terminateConnection(state);
2387
+ },
2388
+ pause: () => {
2389
+ pauseConnection(state);
2390
+ },
2391
+ resume: () => {
2392
+ resumeConnection(state);
2393
+ },
2394
+ addEventListener: (type, handler, listenerOptions) => {
2395
+ addEventListener(state, type, handler, listenerOptions);
2396
+ },
2397
+ removeEventListener: (type, handler) => {
2398
+ removeEventListener(state, type, handler);
2399
+ }
2400
+ };
2401
+ state.target = socket;
2402
+ defineDomAttributes(socket, state);
2403
+ brandSocket(socket, state);
2404
+ return socket;
2405
+ }
2406
+ //#endregion
2407
+ //#region src/compat/options/client.ts
2408
+ function normalizeClientOptions(options) {
2409
+ const source = { ...options };
2410
+ const protocolVersion = source.protocolVersion ?? 13;
2411
+ if (protocolVersion !== 8 && protocolVersion !== 13) invalidOption(`Unsupported protocol version: ${protocolVersion} (supported versions: 8, 13)`, RangeError);
2412
+ return {
2413
+ protocolVersion,
2414
+ followRedirects: source.followRedirects ?? false,
2415
+ maxRedirects: source.maxRedirects ?? 10,
2416
+ handshakeTimeout: source.handshakeTimeout,
2417
+ maxPayload: maxPayloadOf(source, () => codecLimits().maxPayloadBytes),
2418
+ maxFragments: maxFragmentsOf(source, () => codecLimits().maxFragments),
2419
+ maxBufferedChunks: maxBufferedChunksOf(source),
2420
+ skipUTF8Validation: source.skipUTF8Validation ?? false,
2421
+ allowSynchronousEvents: source.allowSynchronousEvents ?? true,
2422
+ autoPong: source.autoPong ?? true,
2423
+ closeTimeout: closeTimeoutOf(source),
2424
+ perMessageDeflate: normalizePerMessageDeflate(source.perMessageDeflate, true),
2425
+ origin: source.origin,
2426
+ headers: source.headers === void 0 ? void 0 : { ...source.headers },
2427
+ requestOptions: { ...source },
2428
+ finishRequest: source.finishRequest,
2429
+ generateMask: source.generateMask
2430
+ };
2431
+ }
2432
+ //#endregion
2433
+ //#region src/compat/client/address.ts
2434
+ const DEFAULT_PORTS = {
2435
+ "ws:": 80,
2436
+ "wss:": 443
2437
+ };
2438
+ const WS_SCHEMES = {
2439
+ "http:": "ws:",
2440
+ "https:": "wss:",
2441
+ "ws:": "ws:",
2442
+ "wss:": "wss:",
2443
+ "ws+unix:": "ws:"
2444
+ };
2445
+ const IPC_SCHEME = "ws+unix:";
2446
+ function parseAddress(address) {
2447
+ const parsed = toUrl(address);
2448
+ const scheme = parsed.protocol;
2449
+ if (scheme === IPC_SCHEME) return buildIpc(parsed);
2450
+ const resolved = WS_SCHEMES[scheme];
2451
+ if (resolved === void 0) throw createError("ERR_INVALID_OPTION", "The URL's protocol must be one of \"ws:\", \"wss:\", \"http:\", \"https:\", or \"ws+unix:\"", SyntaxError);
2452
+ if (parsed.hash) throw createError("ERR_INVALID_OPTION", "The URL contains a fragment identifier", SyntaxError);
2453
+ return build(parsed, resolved);
2454
+ }
2455
+ function toUrl(address) {
2456
+ if (address instanceof URL) return address;
2457
+ try {
2458
+ return new URL(address);
2459
+ } catch {
2460
+ throw createError("ERR_INVALID_OPTION", `Invalid URL: ${address}`, SyntaxError);
2461
+ }
2462
+ }
2463
+ function build(parsed, scheme) {
2464
+ parsed.protocol = scheme;
2465
+ const hasCredentials = parsed.username !== "" || parsed.password !== "";
2466
+ const host = parsed.hostname.startsWith("[") ? parsed.hostname.slice(1, -1) : parsed.hostname;
2467
+ const port = parsed.port === "" ? DEFAULT_PORTS[scheme] : Number(parsed.port);
2468
+ return {
2469
+ url: parsed.href,
2470
+ secure: scheme === "wss:",
2471
+ host,
2472
+ authority: parsed.host,
2473
+ port,
2474
+ path: `${parsed.pathname}${parsed.search}`,
2475
+ auth: hasCredentials ? `${parsed.username}:${parsed.password}` : void 0,
2476
+ socketPath: void 0
2477
+ };
2478
+ }
2479
+ function buildIpc(parsed) {
2480
+ if (parsed.pathname === "") throw createError("ERR_INVALID_OPTION", "The URL's pathname is empty", SyntaxError);
2481
+ if (parsed.hash) throw createError("ERR_INVALID_OPTION", "The URL contains a fragment identifier", SyntaxError);
2482
+ const parts = parsed.pathname.split(":");
2483
+ const socketPath = parts[0] ?? "";
2484
+ const target = parts.length > 1 ? parts.slice(1).join(":") : "";
2485
+ return {
2486
+ url: parsed.href,
2487
+ secure: false,
2488
+ host: "",
2489
+ authority: socketPath,
2490
+ port: DEFAULT_PORTS["ws:"],
2491
+ path: target === "" ? "/" : `/${target.replace(/^\/+/, "")}${parsed.search}`,
2492
+ auth: void 0,
2493
+ socketPath
2494
+ };
2495
+ }
2496
+ //#endregion
2497
+ //#region src/compat/extensions/offer.ts
2498
+ function offer(options) {
2499
+ if (options === false) return void 0;
2500
+ return formatExtension(PERMESSAGE_DEFLATE, NO_CONTEXT_TAKEOVER);
2501
+ }
2502
+ //#endregion
2503
+ //#region src/compat/client/handshake-headers.ts
2504
+ function buildHeaders(options, protocols, key, credentials) {
2505
+ const headers = {
2506
+ "Sec-WebSocket-Version": String(options.protocolVersion),
2507
+ "Sec-WebSocket-Key": key,
2508
+ Connection: "Upgrade",
2509
+ Upgrade: "websocket"
2510
+ };
2511
+ if (protocols.length > 0) headers["Sec-WebSocket-Protocol"] = protocols.join(",");
2512
+ const extension = offer(options.perMessageDeflate);
2513
+ if (extension !== void 0) headers["Sec-WebSocket-Extensions"] = extension;
2514
+ if (options.origin) {
2515
+ if (options.protocolVersion < 13) headers["Sec-WebSocket-Origin"] = options.origin;
2516
+ else headers.Origin = options.origin;
2517
+ }
2518
+ const merged = {
2519
+ ...lowerCased(options.headers),
2520
+ ...headers
2521
+ };
2522
+ if (credentials !== void 0 && merged.authorization === void 0) merged.authorization = `Basic ${Buffer.from(credentials, "utf8").toString("base64")}`;
2523
+ return {
2524
+ key,
2525
+ headers: merged
2526
+ };
2527
+ }
2528
+ function lowerCased(headers) {
2529
+ const out = {};
2530
+ for (const [name, value] of Object.entries(headers ?? {})) out[name.toLowerCase()] = value;
2531
+ return out;
2532
+ }
2533
+ //#endregion
2534
+ //#region src/compat/client/transport-options.ts
2535
+ const FORWARDED = [
2536
+ "ca",
2537
+ "cert",
2538
+ "key",
2539
+ "pfx",
2540
+ "passphrase",
2541
+ "secureContext",
2542
+ "rejectUnauthorized",
2543
+ "checkServerIdentity",
2544
+ "servername",
2545
+ "agent",
2546
+ "createConnection",
2547
+ "localAddress",
2548
+ "family",
2549
+ "lookup",
2550
+ "insecureHTTPParser",
2551
+ "maxHeaderSize",
2552
+ "joinDuplicateHeaders",
2553
+ "signal"
2554
+ ];
2555
+ function transportOptions(address, options) {
2556
+ const carried = {};
2557
+ for (const key of FORWARDED) {
2558
+ const value = options.requestOptions[key];
2559
+ if (value !== void 0) carried[key] = value;
2560
+ }
2561
+ if (address.socketPath !== void 0) return {
2562
+ ...carried,
2563
+ socketPath: address.socketPath
2564
+ };
2565
+ if (!address.secure) return carried;
2566
+ return {
2567
+ ...carried,
2568
+ servername: stringOr(carried.servername, address.host),
2569
+ rejectUnauthorized: booleanOr(carried.rejectUnauthorized, true)
2570
+ };
2571
+ }
2572
+ function stringOr(value, fallback) {
2573
+ return typeof value === "string" ? value : fallback;
2574
+ }
2575
+ function booleanOr(value, fallback) {
2576
+ return typeof value === "boolean" ? value : fallback;
2577
+ }
2578
+ //#endregion
2579
+ //#region src/compat/client/request.ts
2580
+ const GUID = "258EAFA5-E914-47DA-95CA-C5AB0DC85B11";
2581
+ function expectedAccept(key) {
2582
+ return createHash("sha1").update(key + GUID).digest("base64");
2583
+ }
2584
+ function newKey() {
2585
+ return randomBytes(16).toString("base64");
2586
+ }
2587
+ function buildRequest(address, options, protocols, key, carriedAuth) {
2588
+ const built = buildHeaders(options, protocols, key, address.auth ?? carriedAuth);
2589
+ return {
2590
+ key: built.key,
2591
+ headers: built.headers,
2592
+ request: {
2593
+ hostname: address.host,
2594
+ port: address.port,
2595
+ path: address.path,
2596
+ method: "GET",
2597
+ headers: {
2598
+ host: hostHeader(address),
2599
+ ...built.headers
2600
+ },
2601
+ setHost: false,
2602
+ ...transportOptions(address, options)
2603
+ }
2604
+ };
2605
+ }
2606
+ function hostHeader(address) {
2607
+ if (address.socketPath !== void 0) return address.socketPath;
2608
+ return `${address.host.includes(":") ? `[${address.host}]` : address.host}:${address.port}`;
2609
+ }
2610
+ //#endregion
2611
+ //#region src/compat/client/extension.ts
2612
+ function acceptExtension(response, options) {
2613
+ const claimed = response.headers["sec-websocket-extensions"];
2614
+ if (claimed === void 0) return { accepted: null };
2615
+ const value = Array.isArray(claimed) ? claimed.join(", ") : claimed;
2616
+ if (options === false) return { refusal: "Server sent an extension but none was requested" };
2617
+ let offered;
2618
+ try {
2619
+ offered = parseExtensions(value).get("permessage-deflate") ?? [];
2620
+ } catch {
2621
+ return { refusal: "Invalid Sec-WebSocket-Extensions header" };
2622
+ }
2623
+ if (offered.length === 0) return { refusal: "Server sent an extension but none was requested" };
2624
+ return acceptAsClient(offered[0], options) ?? { accepted: null };
2625
+ }
2626
+ //#endregion
2627
+ //#region src/compat/client/open.ts
2628
+ function onUpgrade(attempt, response, socket, head) {
2629
+ const { state } = attempt;
2630
+ socket.on("error", () => void 0);
2631
+ emitEvent(state, "upgrade", response);
2632
+ if (state.readyState === 3) {
2633
+ discard(socket);
2634
+ return;
2635
+ }
2636
+ const refused = reject(response, attempt);
2637
+ if (refused !== null) {
2638
+ abort(attempt, createError("ERR_PROTOCOL", refused));
2639
+ return;
2640
+ }
2641
+ const extension = acceptExtension(response, attempt.options.perMessageDeflate);
2642
+ if ("refusal" in extension) {
2643
+ abort(attempt, createError("ERR_PROTOCOL", extension.refusal));
2644
+ return;
2645
+ }
2646
+ state.protocol = response.headers["sec-websocket-protocol"] ?? "";
2647
+ state.extensions = response.headers["sec-websocket-extensions"] ?? "";
2648
+ state.compressible = extension.accepted !== null;
2649
+ socket.setTimeout(0);
2650
+ const target = state.target ?? null;
2651
+ if (target === null) {
2652
+ abort(attempt, createError("ERR_INVALID_STATE", "ventiws: the socket record is missing"));
2653
+ return;
2654
+ }
2655
+ attempt.transport = socket;
2656
+ state.cancelHandshake = null;
2657
+ attachSocket(target, socket, CODEC_ROLE.client, head);
2658
+ }
2659
+ function reject(response, attempt) {
2660
+ const upgrade = response.headers.upgrade;
2661
+ if (upgrade === void 0 || upgrade.toLowerCase() !== "websocket") return "Invalid Upgrade header";
2662
+ if (response.headers["sec-websocket-accept"] !== expectedAccept(attempt.handshake.key)) return "Invalid Sec-WebSocket-Accept header";
2663
+ return protocolRejection(response.headers["sec-websocket-protocol"], attempt.offered);
2664
+ }
2665
+ //#endregion
2666
+ //#region src/compat/client/unexpected.ts
2667
+ function reportUnexpected(attempt, request, response) {
2668
+ if (emitEvent(attempt.state, "unexpected-response", request, response)) return;
2669
+ request.abort();
2670
+ abort(attempt, createError("ERR_PROTOCOL", `Unexpected server response: ${response.statusCode ?? 0}`));
2671
+ }
2672
+ //#endregion
2673
+ //#region src/compat/client/credentials.ts
2674
+ function stripCredentials(headers) {
2675
+ if (typeof headers !== "object" || headers === null) return;
2676
+ const map = headers;
2677
+ for (const name of Object.keys(map)) {
2678
+ const lower = name.toLowerCase();
2679
+ if (lower === "authorization" || lower === "cookie") delete map[name];
2680
+ }
2681
+ }
2682
+ //#endregion
2683
+ //#region src/compat/client/redirect.ts
2684
+ function isRedirect(status, location) {
2685
+ return location !== void 0 && status >= 300 && status < 400;
2686
+ }
2687
+ function onResponse(attempt, request, response) {
2688
+ const location = response.headers.location;
2689
+ if (isRedirect(response.statusCode ?? 0, location) && attempt.options.followRedirects) {
2690
+ follow(attempt, request, response, location ?? "");
2691
+ return;
2692
+ }
2693
+ reportUnexpected(attempt, request, response);
2694
+ }
2695
+ function follow(attempt, request, response, location) {
2696
+ if (attempt.redirects + 1 > attempt.options.maxRedirects) {
2697
+ request.abort();
2698
+ abort(attempt, createError("ERR_PROTOCOL", "Maximum redirects exceeded"));
2699
+ return;
2700
+ }
2701
+ const target = resolve(location, attempt.state.url);
2702
+ let next;
2703
+ try {
2704
+ next = parseAddress(target);
2705
+ } catch (error) {
2706
+ request.abort();
2707
+ abort(attempt, createError("ERR_PROTOCOL", error instanceof Error ? error.message : `Invalid URL: ${target}`));
2708
+ return;
2709
+ }
2710
+ if (attempt.address.secure && !next.secure) {
2711
+ request.abort();
2712
+ abort(attempt, createError("ERR_PROTOCOL", "Cannot follow a redirect from wss: to ws:"));
2713
+ return;
2714
+ }
2715
+ if (next.authority !== attempt.address.authority && listenerCount(attempt) === 0) {
2716
+ stripCredentials(attempt.handshake.request.headers);
2717
+ attempt.auth = void 0;
2718
+ }
2719
+ request.abort();
2720
+ attempt.state.url = next.url;
2721
+ attempt.address = next;
2722
+ attempt.redirects += 1;
2723
+ attempt.handshake = buildRequest(next, attempt.options, attempt.requested, newKey(), attempt.auth);
2724
+ const hop = create(attempt);
2725
+ if (hop === null) return;
2726
+ emitEvent(attempt.state, "redirect", next.url, hop);
2727
+ finish(attempt, hop);
2728
+ }
2729
+ function resolve(location, base) {
2730
+ try {
2731
+ return new URL(location, base).href;
2732
+ } catch {
2733
+ return location;
2734
+ }
2735
+ }
2736
+ function listenerCount(attempt) {
2737
+ return listenerCount$1(attempt.state.listeners, "redirect");
2738
+ }
2739
+ //#endregion
2740
+ //#region src/compat/client/dial.ts
2741
+ function dial(attempt) {
2742
+ const request = create(attempt);
2743
+ if (request === null) return null;
2744
+ finish(attempt, request);
2745
+ return request;
2746
+ }
2747
+ function finish(attempt, request) {
2748
+ const callback = attempt.options.finishRequest;
2749
+ if (callback === void 0) {
2750
+ request.end();
2751
+ return;
2752
+ }
2753
+ const target = attempt.state.target;
2754
+ if (target === void 0) {
2755
+ request.end();
2756
+ return;
2757
+ }
2758
+ callback(request, target);
2759
+ }
2760
+ function create(attempt) {
2761
+ const { options, handshake } = attempt;
2762
+ if (attempt.state.readyState === 3) return null;
2763
+ const request = issue(attempt, handshake.request);
2764
+ attempt.request = request;
2765
+ attempt.state.cancelHandshake = () => {
2766
+ request.abort();
2767
+ };
2768
+ request.on("error", (error) => {
2769
+ if (attempt.request !== request) return;
2770
+ attempt.request = null;
2771
+ failConnection(attempt.state, error);
2772
+ });
2773
+ if (options.handshakeTimeout) request.setTimeout(options.handshakeTimeout, () => {
2774
+ refuse(attempt, request, "Opening handshake has timed out");
2775
+ });
2776
+ request.on("upgrade", (response, socket, head) => {
2777
+ if (attempt.request !== request) return;
2778
+ attempt.request = null;
2779
+ onUpgrade(attempt, response, socket, head);
2780
+ });
2781
+ request.on("response", (response) => {
2782
+ if (attempt.request !== request) return;
2783
+ attempt.request = null;
2784
+ onResponse(attempt, request, response);
2785
+ });
2786
+ return request;
2787
+ }
2788
+ function issue(attempt, options) {
2789
+ return attempt.address.secure ? request$1(options) : request(options);
2790
+ }
2791
+ function refuse(attempt, request, message) {
2792
+ request?.abort();
2793
+ abort(attempt, createError("ERR_PROTOCOL", message));
2794
+ }
2795
+ function abort(attempt, error) {
2796
+ const { state } = attempt;
2797
+ if (state.readyState === 3) return;
2798
+ state.cancelHandshake = null;
2799
+ attempt.request?.destroy();
2800
+ attempt.request = null;
2801
+ if (attempt.transport !== null) discard(attempt.transport);
2802
+ failConnection(state, error);
2803
+ }
2804
+ function discard(socket) {
2805
+ socket.on("error", () => void 0);
2806
+ socket.destroy();
2807
+ }
2808
+ //#endregion
2809
+ //#region src/compat/client/connect.ts
2810
+ function connectSocket(address, protocols, options) {
2811
+ const normalized = normalizeClientOptions(options);
2812
+ const parsed = parseAddress(address);
2813
+ const requested = normalizeProtocols(protocols);
2814
+ const state = createSocketState();
2815
+ state.isServer = false;
2816
+ state.url = parsed.url;
2817
+ state.closeTimeout = normalized.closeTimeout;
2818
+ state.autoPong = normalized.autoPong;
2819
+ state.generateMask = normalized.generateMask ?? null;
2820
+ state.allowSynchronousEvents = normalized.allowSynchronousEvents;
2821
+ state.validateUtf8 = !normalized.skipUTF8Validation;
2822
+ state.maxPayload = normalized.maxPayload;
2823
+ state.maxFragments = normalized.maxFragments;
2824
+ state.maxBufferedChunks = normalized.maxBufferedChunks;
2825
+ state.threshold = thresholdOf(normalized.perMessageDeflate);
2826
+ const socket = buildSocketRecord(state);
2827
+ dial({
2828
+ state,
2829
+ options: normalized,
2830
+ requested,
2831
+ offered: protocolSet(requested),
2832
+ request: null,
2833
+ transport: null,
2834
+ handshake: buildRequest(parsed, normalized, requested, newKey()),
2835
+ address: parsed,
2836
+ auth: parsed.auth,
2837
+ redirects: 0
2838
+ });
2839
+ return socket;
2840
+ }
2841
+ //#endregion
2842
+ //#region src/compat/socket/socket.ts
2843
+ function createSocket(address, protocols, options) {
2844
+ if (address === null) return buildSocketRecord(createSocketState());
2845
+ const promoted = promoteOptions(protocols, options);
2846
+ return connectSocket(address, promoted.protocols, promoted.options);
2847
+ }
2848
+ //#endregion
2849
+ //#region src/compat/stream.ts
2850
+ function createWebSocketStream(ws, options) {
2851
+ let terminateOnDestroy = true;
2852
+ const duplex = new Duplex({
2853
+ ...options,
2854
+ autoDestroy: false,
2855
+ emitClose: false,
2856
+ objectMode: false,
2857
+ writableObjectMode: false
2858
+ });
2859
+ const onEnd = () => {
2860
+ if (!duplex.destroyed && duplex.writableFinished) duplex.destroy();
2861
+ };
2862
+ const onError = (error) => {
2863
+ duplex.removeListener("error", onError);
2864
+ duplex.destroy();
2865
+ if (duplex.listenerCount("error") === 0) duplex.emit("error", error);
2866
+ };
2867
+ ws.on("message", (message, isBinary) => {
2868
+ const data = !isBinary && duplex.readableObjectMode ? message.toString() : message;
2869
+ if (!duplex.push(data)) ws.pause();
2870
+ });
2871
+ ws.once("error", (error) => {
2872
+ if (duplex.destroyed) return;
2873
+ terminateOnDestroy = false;
2874
+ duplex.destroy(error);
2875
+ });
2876
+ ws.once("close", () => {
2877
+ if (!duplex.destroyed) duplex.push(null);
2878
+ });
2879
+ duplex._destroy = (error, callback) => {
2880
+ if (ws.readyState === ws.CLOSED) {
2881
+ callback(error);
2882
+ process.nextTick(() => {
2883
+ duplex.emit("close");
2884
+ });
2885
+ return;
2886
+ }
2887
+ let called = false;
2888
+ ws.once("error", (failure) => {
2889
+ called = true;
2890
+ callback(failure);
2891
+ });
2892
+ ws.once("close", () => {
2893
+ if (!called) callback(error);
2894
+ process.nextTick(() => {
2895
+ duplex.emit("close");
2896
+ });
2897
+ });
2898
+ if (terminateOnDestroy) ws.terminate();
2899
+ };
2900
+ duplex._final = (callback) => {
2901
+ if (ws.readyState === ws.CONNECTING) {
2902
+ ws.once("open", () => {
2903
+ duplex._final?.(callback);
2904
+ });
2905
+ return;
2906
+ }
2907
+ if (ws.readyState === ws.CLOSED) {
2908
+ callback();
2909
+ return;
2910
+ }
2911
+ ws.close();
2912
+ if (!closeFrameWritten(ws)) {
2913
+ ws.once("close", () => {
2914
+ callback();
2915
+ });
2916
+ return;
2917
+ }
2918
+ callback();
2919
+ if (duplex.readableEnded) duplex.destroy();
2920
+ };
2921
+ duplex._read = () => {
2922
+ if (ws.isPaused) ws.resume();
2923
+ };
2924
+ duplex._write = (chunk, encoding, callback) => {
2925
+ if (ws.readyState === ws.CONNECTING) {
2926
+ ws.once("open", () => {
2927
+ duplex._write?.(chunk, encoding, callback);
2928
+ });
2929
+ return;
2930
+ }
2931
+ ws.send(chunk, callback);
2932
+ };
2933
+ duplex.on("end", onEnd);
2934
+ duplex.on("error", onError);
2935
+ return duplex;
2936
+ }
2937
+ //#endregion
2938
+ //#region src/compat/constructors.ts
2939
+ function buildSocket(address, protocols, options) {
2940
+ if (new.target === void 0) throw new TypeError("Class constructor WebSocket cannot be invoked without 'new'");
2941
+ return createSocket(address, protocols, options);
2942
+ }
2943
+ function buildServer(options, callback) {
2944
+ if (new.target === void 0) throw new TypeError("Class constructor WebSocketServer cannot be invoked without 'new'");
2945
+ return createWebSocketServer(WebSocket, options, callback);
2946
+ }
2947
+ const WebSocket = buildSocket;
2948
+ const WebSocketServer = buildServer;
2949
+ Object.defineProperty(WebSocket, Symbol.hasInstance, { value: isSocket });
2950
+ Object.defineProperty(WebSocketServer, Symbol.hasInstance, { value: isServer });
2951
+ Object.defineProperties(WebSocket, {
2952
+ CONNECTING: {
2953
+ value: 0,
2954
+ enumerable: true
2955
+ },
2956
+ OPEN: {
2957
+ value: 1,
2958
+ enumerable: true
2959
+ },
2960
+ CLOSING: {
2961
+ value: 2,
2962
+ enumerable: true
2963
+ },
2964
+ CLOSED: {
2965
+ value: 3,
2966
+ enumerable: true
2967
+ }
2968
+ });
2969
+ Object.assign(WebSocket, {
2970
+ WebSocket,
2971
+ WebSocketServer,
2972
+ Server: WebSocketServer,
2973
+ createWebSocketStream
2974
+ });
2975
+ //#endregion
2976
+ export { WebSocket, WebSocket as default, WebSocketServer, createWebSocketStream, engineLimits };
2977
+