ventiws 1.0.0-rc.1 → 1.0.0-rc.3

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/README.md CHANGED
@@ -6,13 +6,13 @@
6
6
 
7
7
  A WebSocket implementation for Node.js with the API of
8
8
  [`ws`](https://github.com/websockets/ws), delivered as a native addon whose framing
9
- engine is written in Zig. It targets `ws` 8.21.3 and reproduces that release's
9
+ engine is written in Zig. It targets `ws` 8.22.0 and reproduces that release's
10
10
  observable behaviour rather than its source. ventiws is not affiliated with the
11
11
  `ws` project and vendors none of its code; the upstream API reference is vendored
12
12
  as the pinned contract, credited in
13
13
  [THIRD_PARTY_NOTICES.md](THIRD_PARTY_NOTICES.md).
14
14
 
15
- **ventiws is at `1.0.0-rc.1`.** The surface may change between beta
15
+ **ventiws is at `1.0.0-rc.3`.** The surface may change between beta
16
16
  releases. What "beta" means here is bounded: the `ws` compatibility
17
17
  contract is tracked row by row in
18
18
  [COMPATIBILITY.md](COMPATIBILITY.md), and the four places ventiws knowingly
package/dist/index.cjs CHANGED
@@ -459,6 +459,97 @@ function createEmitter(state) {
459
459
  }
460
460
  };
461
461
  }
462
+ function normalizeParameters(parameters, isServer) {
463
+ const out = {};
464
+ for (const [key, values] of Object.entries(parameters)) {
465
+ if (values.length > 1) return null;
466
+ const parsed = parseParameter(key, values[0] ?? "", isServer);
467
+ if (parsed === null) return null;
468
+ out[key] = parsed;
469
+ }
470
+ return {
471
+ server_no_context_takeover: out.server_no_context_takeover === true,
472
+ client_no_context_takeover: out.client_no_context_takeover === true,
473
+ server_max_window_bits: numberOf(out.server_max_window_bits),
474
+ client_max_window_bits: windowOf(out.client_max_window_bits)
475
+ };
476
+ }
477
+ function parseParameter(key, value, isServer) {
478
+ if (key === "server_no_context_takeover" || key === "client_no_context_takeover") return value === "" ? true : null;
479
+ if (key === "client_max_window_bits") {
480
+ if (value === "") return isServer ? true : null;
481
+ return windowSize(value);
482
+ }
483
+ if (key === "server_max_window_bits") return value === "" ? null : windowSize(value);
484
+ return null;
485
+ }
486
+ function windowSize(value) {
487
+ const parsed = Number(value);
488
+ if (!Number.isInteger(parsed) || parsed < 8 || parsed > 15) return null;
489
+ return parsed;
490
+ }
491
+ function windowOf(value) {
492
+ return typeof value === "number" || value === true ? value : void 0;
493
+ }
494
+ function numberOf(value) {
495
+ return typeof value === "number" ? value : void 0;
496
+ }
497
+ //#endregion
498
+ //#region src/compat/options/shared.ts
499
+ const DEFAULT_MAX_PAYLOAD = 104857600;
500
+ const DEFAULT_MAX_FRAGMENTS = 16384;
501
+ const DEFAULT_MAX_BUFFERED_CHUNKS = 262144;
502
+ const DEFAULT_CLOSE_TIMEOUT = 3e4;
503
+ const SUBPROTOCOL_PATTERN = /^[!#$%&'*+\-.0-9A-Z^_`|a-z~]+$/;
504
+ function isProtocolToken(value) {
505
+ return typeof value === "string" && SUBPROTOCOL_PATTERN.test(value);
506
+ }
507
+ function invalidOption(message, constructor = TypeError) {
508
+ throw createError("ERR_INVALID_OPTION", message, constructor);
509
+ }
510
+ function normalizeProtocols(protocols) {
511
+ if (protocols === void 0) return [];
512
+ const list = typeof protocols === "string" ? [protocols] : protocols;
513
+ const candidates = Array.isArray(list) ? list : [list];
514
+ const seen = /* @__PURE__ */ new Set();
515
+ const result = [];
516
+ for (const protocol of candidates) {
517
+ if (typeof protocol !== "string" || !SUBPROTOCOL_PATTERN.test(protocol) || seen.has(protocol)) invalidOption("An invalid or duplicated subprotocol was specified", SyntaxError);
518
+ seen.add(protocol);
519
+ result.push(protocol);
520
+ }
521
+ return result;
522
+ }
523
+ function parseProtocolHeader(header) {
524
+ return normalizeProtocols(header.split(",").map((protocol) => protocol.trim()));
525
+ }
526
+ function windowBitsOf(name, raw) {
527
+ if (raw === void 0 || raw === false) return raw;
528
+ if (typeof raw === "number" && Number.isInteger(raw) && raw >= 8 && raw <= 15) return raw;
529
+ return invalidOption(`The ${name} option must be an integer in [8, 15] (received ${String(raw)})`, RangeError);
530
+ }
531
+ function normalizePerMessageDeflate(value, fallback) {
532
+ if (value !== void 0 && !value) return false;
533
+ const resolved = value ?? fallback;
534
+ if (!resolved) return false;
535
+ const options = resolved === true ? {} : resolved;
536
+ return {
537
+ serverNoContextTakeover: options.serverNoContextTakeover,
538
+ clientNoContextTakeover: options.clientNoContextTakeover,
539
+ serverMaxWindowBits: windowBitsOf("serverMaxWindowBits", options.serverMaxWindowBits),
540
+ clientMaxWindowBits: windowBitsOf("clientMaxWindowBits", options.clientMaxWindowBits),
541
+ threshold: options.threshold ?? 1024,
542
+ concurrencyLimit: options.concurrencyLimit ?? 10,
543
+ zlibDeflateOptions: options.zlibDeflateOptions,
544
+ zlibInflateOptions: options.zlibInflateOptions
545
+ };
546
+ }
547
+ function closeTimeoutOf(source) {
548
+ const raw = source.closeTimeout;
549
+ if (raw === void 0) return DEFAULT_CLOSE_TIMEOUT;
550
+ if (typeof raw !== "number" || !Number.isFinite(raw) || raw < 0) invalidOption(`The closeTimeout option must be a non-negative number (received ${String(raw)})`, RangeError);
551
+ return raw;
552
+ }
462
553
  //#endregion
463
554
  //#region src/compat/server/clients.ts
464
555
  function clientCount(state) {
@@ -475,7 +566,7 @@ function trackClient(state, socket) {
475
566
  }
476
567
  //#endregion
477
568
  //#region package.json
478
- var version = "1.0.0-rc.1";
569
+ var version = "1.0.0-rc.3";
479
570
  //#endregion
480
571
  //#region src/logging/lifecycle.ts
481
572
  const CONTROL = /\p{Cc}/gu;
@@ -557,97 +648,6 @@ function closeWebSocketServer(state, callback) {
557
648
  emitClose(state);
558
649
  });
559
650
  }
560
- function normalizeParameters(parameters, isServer) {
561
- const out = {};
562
- for (const [key, values] of Object.entries(parameters)) {
563
- if (values.length > 1) return null;
564
- const parsed = parseParameter(key, values[0] ?? "", isServer);
565
- if (parsed === null) return null;
566
- out[key] = parsed;
567
- }
568
- return {
569
- server_no_context_takeover: out.server_no_context_takeover === true,
570
- client_no_context_takeover: out.client_no_context_takeover === true,
571
- server_max_window_bits: numberOf(out.server_max_window_bits),
572
- client_max_window_bits: windowOf(out.client_max_window_bits)
573
- };
574
- }
575
- function parseParameter(key, value, isServer) {
576
- if (key === "server_no_context_takeover" || key === "client_no_context_takeover") return value === "" ? true : null;
577
- if (key === "client_max_window_bits") {
578
- if (value === "") return isServer ? true : null;
579
- return windowSize(value);
580
- }
581
- if (key === "server_max_window_bits") return value === "" ? null : windowSize(value);
582
- return null;
583
- }
584
- function windowSize(value) {
585
- const parsed = Number(value);
586
- if (!Number.isInteger(parsed) || parsed < 8 || parsed > 15) return null;
587
- return parsed;
588
- }
589
- function windowOf(value) {
590
- return typeof value === "number" || value === true ? value : void 0;
591
- }
592
- function numberOf(value) {
593
- return typeof value === "number" ? value : void 0;
594
- }
595
- //#endregion
596
- //#region src/compat/options/shared.ts
597
- const DEFAULT_MAX_PAYLOAD = 104857600;
598
- const DEFAULT_MAX_FRAGMENTS = 16384;
599
- const DEFAULT_MAX_BUFFERED_CHUNKS = 262144;
600
- const DEFAULT_CLOSE_TIMEOUT = 3e4;
601
- const SUBPROTOCOL_PATTERN = /^[!#$%&'*+\-.0-9A-Z^_`|a-z~]+$/;
602
- function isProtocolToken(value) {
603
- return typeof value === "string" && SUBPROTOCOL_PATTERN.test(value);
604
- }
605
- function invalidOption(message, constructor = TypeError) {
606
- throw createError("ERR_INVALID_OPTION", message, constructor);
607
- }
608
- function normalizeProtocols$1(protocols) {
609
- if (protocols === void 0) return [];
610
- const list = typeof protocols === "string" ? [protocols] : protocols;
611
- const candidates = Array.isArray(list) ? list : [list];
612
- const seen = /* @__PURE__ */ new Set();
613
- const result = [];
614
- for (const protocol of candidates) {
615
- if (typeof protocol !== "string" || !SUBPROTOCOL_PATTERN.test(protocol) || seen.has(protocol)) invalidOption("An invalid or duplicated subprotocol was specified", SyntaxError);
616
- seen.add(protocol);
617
- result.push(protocol);
618
- }
619
- return result;
620
- }
621
- function parseProtocolHeader(header) {
622
- return normalizeProtocols$1(header.split(",").map((protocol) => protocol.trim()));
623
- }
624
- function windowBitsOf(name, raw) {
625
- if (raw === void 0 || raw === false) return raw;
626
- if (typeof raw === "number" && Number.isInteger(raw) && raw >= 8 && raw <= 15) return raw;
627
- return invalidOption(`The ${name} option must be an integer in [8, 15] (received ${String(raw)})`, RangeError);
628
- }
629
- function normalizePerMessageDeflate(value, fallback) {
630
- if (value !== void 0 && !value) return false;
631
- const resolved = value ?? fallback;
632
- if (!resolved) return false;
633
- const options = resolved === true ? {} : resolved;
634
- return {
635
- serverNoContextTakeover: options.serverNoContextTakeover,
636
- clientNoContextTakeover: options.clientNoContextTakeover,
637
- serverMaxWindowBits: windowBitsOf("serverMaxWindowBits", options.serverMaxWindowBits),
638
- clientMaxWindowBits: windowBitsOf("clientMaxWindowBits", options.clientMaxWindowBits),
639
- threshold: options.threshold ?? 1024,
640
- concurrencyLimit: options.concurrencyLimit ?? 10,
641
- zlibDeflateOptions: options.zlibDeflateOptions,
642
- zlibInflateOptions: options.zlibInflateOptions
643
- };
644
- }
645
- function closeTimeoutOf(source) {
646
- const raw = source.closeTimeout;
647
- if (raw === void 0) return DEFAULT_CLOSE_TIMEOUT;
648
- if (typeof raw !== "number" || !Number.isFinite(raw) || raw < 0) invalidOption(`The closeTimeout option must be a non-negative number (received ${String(raw)})`, RangeError);
649
- return raw;
650
- }
651
651
  //#endregion
652
652
  //#region src/compat/server/handshake.ts
653
653
  const KEY_PATTERN = /^[+/0-9A-Za-z]{22}==$/;
@@ -1389,9 +1389,9 @@ function closeConnection(state, code, reason) {
1389
1389
  return;
1390
1390
  }
1391
1391
  if (state.readyState === 2) return;
1392
- state.readyState = 2;
1393
1392
  const closeCode = closeCodeOf(code);
1394
1393
  const closeReason = toCloseReason(reason);
1394
+ state.readyState = 2;
1395
1395
  if (state.codec !== null) {
1396
1396
  closeFramed(state, closeCode, closeReason);
1397
1397
  armCloseTimeout(state, state.closeTimeout);
@@ -1903,7 +1903,7 @@ function answer(options, offer) {
1903
1903
  }
1904
1904
  //#endregion
1905
1905
  //#region src/compat/extensions/scan.ts
1906
- const TOKEN$1 = /^[!#$%&'*+\-.0-9A-Z^_`|a-z~]+$/;
1906
+ const TOKEN = /^[!#$%&'*+\-.0-9A-Z^_`|a-z~]+$/;
1907
1907
  const COMMA$1 = 44;
1908
1908
  const SEMICOLON = 59;
1909
1909
  const EQUALS = 61;
@@ -1953,7 +1953,7 @@ function readValue(header, start) {
1953
1953
  if (header.charCodeAt(start) !== QUOTE) {
1954
1954
  const token = readToken(header, start, "a parameter value");
1955
1955
  if (token === null) throw unexpectedEnd(header, start);
1956
- if (!TOKEN$1.test(token)) throw syntax(header, start, "invalid parameter value");
1956
+ if (!TOKEN.test(token)) throw syntax(header, start, "invalid parameter value");
1957
1957
  return {
1958
1958
  value: token,
1959
1959
  end: start + token.length
@@ -2280,10 +2280,10 @@ function createWebSocketServer(socketClass, options, callback) {
2280
2280
  const resolved = {
2281
2281
  allowSynchronousEvents: true,
2282
2282
  autoPong: true,
2283
- maxBufferedChunks: 262144,
2284
- maxFragments: 16384,
2285
- closeTimeout: 3e4,
2286
- maxPayload: 104857600,
2283
+ maxBufferedChunks: DEFAULT_MAX_BUFFERED_CHUNKS,
2284
+ maxFragments: DEFAULT_MAX_FRAGMENTS,
2285
+ closeTimeout: DEFAULT_CLOSE_TIMEOUT,
2286
+ maxPayload: DEFAULT_MAX_PAYLOAD,
2287
2287
  skipUTF8Validation: false,
2288
2288
  perMessageDeflate: false,
2289
2289
  handleProtocols: null,
@@ -2350,18 +2350,6 @@ function createWebSocketServer(socketClass, options, callback) {
2350
2350
  }
2351
2351
  //#endregion
2352
2352
  //#region src/compat/client/protocols.ts
2353
- const TOKEN = /^[!#$%&'*+\-.^_`|~\dA-Za-z]+$/;
2354
- function normalizeProtocols(protocols) {
2355
- if (protocols === void 0) return [];
2356
- const list = typeof protocols === "string" ? [protocols] : protocols;
2357
- if (!Array.isArray(list)) throw createError("ERR_INVALID_OPTION", "An invalid or duplicated subprotocol was specified", SyntaxError);
2358
- const seen = /* @__PURE__ */ new Set();
2359
- for (const protocol of list) {
2360
- if (typeof protocol !== "string" || !TOKEN.test(protocol) || seen.has(protocol)) throw createError("ERR_INVALID_OPTION", "An invalid or duplicated subprotocol was specified", SyntaxError);
2361
- seen.add(protocol);
2362
- }
2363
- return [...list];
2364
- }
2365
2353
  function protocolSet(protocols) {
2366
2354
  return new Set(protocols);
2367
2355
  }
@@ -2814,6 +2802,7 @@ function normalizeClientOptions(options) {
2814
2802
  skipUTF8Validation: source.skipUTF8Validation ?? false,
2815
2803
  allowSynchronousEvents: source.allowSynchronousEvents ?? true,
2816
2804
  autoPong: source.autoPong ?? true,
2805
+ protocols: source.protocols,
2817
2806
  closeTimeout: closeTimeoutOf(source),
2818
2807
  perMessageDeflate: normalizePerMessageDeflate(source.perMessageDeflate, true),
2819
2808
  origin: source.origin,
@@ -3211,7 +3200,7 @@ function dial(attempt) {
3211
3200
  function connectSocket(address, protocols, options) {
3212
3201
  const normalized = normalizeClientOptions(options);
3213
3202
  const parsed = parseAddress(address);
3214
- const requested = normalizeProtocols(protocols);
3203
+ const requested = normalizeProtocols(protocols === void 0 ? normalized.protocols : protocols);
3215
3204
  const state = createSocketState();
3216
3205
  state.isServer = false;
3217
3206
  state.url = parsed.url;
package/dist/index.d.cts CHANGED
@@ -202,6 +202,8 @@ declare namespace WebSocket$1 {
202
202
  allowSynchronousEvents?: boolean | undefined;
203
203
  autoPong?: boolean | undefined;
204
204
  maxPayload?: number | undefined;
205
+ maxBufferedChunks?: number | undefined;
206
+ maxFragments?: number | undefined;
205
207
  skipUTF8Validation?: boolean | undefined;
206
208
  createConnection?: typeof createConnection | undefined;
207
209
  finishRequest?: FinishRequestCallback | undefined;
@@ -271,6 +273,8 @@ declare namespace WebSocket$1 {
271
273
  clientTracking?: boolean | undefined;
272
274
  perMessageDeflate?: boolean | PerMessageDeflateOptions | undefined;
273
275
  maxPayload?: number | undefined;
276
+ maxBufferedChunks?: number | undefined;
277
+ maxFragments?: number | undefined;
274
278
  skipUTF8Validation?: boolean | undefined;
275
279
  WebSocket?: U | undefined;
276
280
  }
package/dist/index.d.mts CHANGED
@@ -203,6 +203,8 @@ declare namespace WebSocket$1 {
203
203
  allowSynchronousEvents?: boolean | undefined;
204
204
  autoPong?: boolean | undefined;
205
205
  maxPayload?: number | undefined;
206
+ maxBufferedChunks?: number | undefined;
207
+ maxFragments?: number | undefined;
206
208
  skipUTF8Validation?: boolean | undefined;
207
209
  createConnection?: typeof createConnection | undefined;
208
210
  finishRequest?: FinishRequestCallback | undefined;
@@ -272,6 +274,8 @@ declare namespace WebSocket$1 {
272
274
  clientTracking?: boolean | undefined;
273
275
  perMessageDeflate?: boolean | PerMessageDeflateOptions | undefined;
274
276
  maxPayload?: number | undefined;
277
+ maxBufferedChunks?: number | undefined;
278
+ maxFragments?: number | undefined;
275
279
  skipUTF8Validation?: boolean | undefined;
276
280
  WebSocket?: U | undefined;
277
281
  }
package/dist/index.mjs CHANGED
@@ -432,6 +432,97 @@ function createEmitter(state) {
432
432
  }
433
433
  };
434
434
  }
435
+ function normalizeParameters(parameters, isServer) {
436
+ const out = {};
437
+ for (const [key, values] of Object.entries(parameters)) {
438
+ if (values.length > 1) return null;
439
+ const parsed = parseParameter(key, values[0] ?? "", isServer);
440
+ if (parsed === null) return null;
441
+ out[key] = parsed;
442
+ }
443
+ return {
444
+ server_no_context_takeover: out.server_no_context_takeover === true,
445
+ client_no_context_takeover: out.client_no_context_takeover === true,
446
+ server_max_window_bits: numberOf(out.server_max_window_bits),
447
+ client_max_window_bits: windowOf(out.client_max_window_bits)
448
+ };
449
+ }
450
+ function parseParameter(key, value, isServer) {
451
+ if (key === "server_no_context_takeover" || key === "client_no_context_takeover") return value === "" ? true : null;
452
+ if (key === "client_max_window_bits") {
453
+ if (value === "") return isServer ? true : null;
454
+ return windowSize(value);
455
+ }
456
+ if (key === "server_max_window_bits") return value === "" ? null : windowSize(value);
457
+ return null;
458
+ }
459
+ function windowSize(value) {
460
+ const parsed = Number(value);
461
+ if (!Number.isInteger(parsed) || parsed < 8 || parsed > 15) return null;
462
+ return parsed;
463
+ }
464
+ function windowOf(value) {
465
+ return typeof value === "number" || value === true ? value : void 0;
466
+ }
467
+ function numberOf(value) {
468
+ return typeof value === "number" ? value : void 0;
469
+ }
470
+ //#endregion
471
+ //#region src/compat/options/shared.ts
472
+ const DEFAULT_MAX_PAYLOAD = 104857600;
473
+ const DEFAULT_MAX_FRAGMENTS = 16384;
474
+ const DEFAULT_MAX_BUFFERED_CHUNKS = 262144;
475
+ const DEFAULT_CLOSE_TIMEOUT = 3e4;
476
+ const SUBPROTOCOL_PATTERN = /^[!#$%&'*+\-.0-9A-Z^_`|a-z~]+$/;
477
+ function isProtocolToken(value) {
478
+ return typeof value === "string" && SUBPROTOCOL_PATTERN.test(value);
479
+ }
480
+ function invalidOption(message, constructor = TypeError) {
481
+ throw createError("ERR_INVALID_OPTION", message, constructor);
482
+ }
483
+ function normalizeProtocols(protocols) {
484
+ if (protocols === void 0) return [];
485
+ const list = typeof protocols === "string" ? [protocols] : protocols;
486
+ const candidates = Array.isArray(list) ? list : [list];
487
+ const seen = /* @__PURE__ */ new Set();
488
+ const result = [];
489
+ for (const protocol of candidates) {
490
+ if (typeof protocol !== "string" || !SUBPROTOCOL_PATTERN.test(protocol) || seen.has(protocol)) invalidOption("An invalid or duplicated subprotocol was specified", SyntaxError);
491
+ seen.add(protocol);
492
+ result.push(protocol);
493
+ }
494
+ return result;
495
+ }
496
+ function parseProtocolHeader(header) {
497
+ return normalizeProtocols(header.split(",").map((protocol) => protocol.trim()));
498
+ }
499
+ function windowBitsOf(name, raw) {
500
+ if (raw === void 0 || raw === false) return raw;
501
+ if (typeof raw === "number" && Number.isInteger(raw) && raw >= 8 && raw <= 15) return raw;
502
+ return invalidOption(`The ${name} option must be an integer in [8, 15] (received ${String(raw)})`, RangeError);
503
+ }
504
+ function normalizePerMessageDeflate(value, fallback) {
505
+ if (value !== void 0 && !value) return false;
506
+ const resolved = value ?? fallback;
507
+ if (!resolved) return false;
508
+ const options = resolved === true ? {} : resolved;
509
+ return {
510
+ serverNoContextTakeover: options.serverNoContextTakeover,
511
+ clientNoContextTakeover: options.clientNoContextTakeover,
512
+ serverMaxWindowBits: windowBitsOf("serverMaxWindowBits", options.serverMaxWindowBits),
513
+ clientMaxWindowBits: windowBitsOf("clientMaxWindowBits", options.clientMaxWindowBits),
514
+ threshold: options.threshold ?? 1024,
515
+ concurrencyLimit: options.concurrencyLimit ?? 10,
516
+ zlibDeflateOptions: options.zlibDeflateOptions,
517
+ zlibInflateOptions: options.zlibInflateOptions
518
+ };
519
+ }
520
+ function closeTimeoutOf(source) {
521
+ const raw = source.closeTimeout;
522
+ if (raw === void 0) return DEFAULT_CLOSE_TIMEOUT;
523
+ if (typeof raw !== "number" || !Number.isFinite(raw) || raw < 0) invalidOption(`The closeTimeout option must be a non-negative number (received ${String(raw)})`, RangeError);
524
+ return raw;
525
+ }
435
526
  //#endregion
436
527
  //#region src/compat/server/clients.ts
437
528
  function clientCount(state) {
@@ -448,7 +539,7 @@ function trackClient(state, socket) {
448
539
  }
449
540
  //#endregion
450
541
  //#region package.json
451
- var version = "1.0.0-rc.1";
542
+ var version = "1.0.0-rc.3";
452
543
  //#endregion
453
544
  //#region src/logging/lifecycle.ts
454
545
  const CONTROL = /\p{Cc}/gu;
@@ -530,97 +621,6 @@ function closeWebSocketServer(state, callback) {
530
621
  emitClose(state);
531
622
  });
532
623
  }
533
- function normalizeParameters(parameters, isServer) {
534
- const out = {};
535
- for (const [key, values] of Object.entries(parameters)) {
536
- if (values.length > 1) return null;
537
- const parsed = parseParameter(key, values[0] ?? "", isServer);
538
- if (parsed === null) return null;
539
- out[key] = parsed;
540
- }
541
- return {
542
- server_no_context_takeover: out.server_no_context_takeover === true,
543
- client_no_context_takeover: out.client_no_context_takeover === true,
544
- server_max_window_bits: numberOf(out.server_max_window_bits),
545
- client_max_window_bits: windowOf(out.client_max_window_bits)
546
- };
547
- }
548
- function parseParameter(key, value, isServer) {
549
- if (key === "server_no_context_takeover" || key === "client_no_context_takeover") return value === "" ? true : null;
550
- if (key === "client_max_window_bits") {
551
- if (value === "") return isServer ? true : null;
552
- return windowSize(value);
553
- }
554
- if (key === "server_max_window_bits") return value === "" ? null : windowSize(value);
555
- return null;
556
- }
557
- function windowSize(value) {
558
- const parsed = Number(value);
559
- if (!Number.isInteger(parsed) || parsed < 8 || parsed > 15) return null;
560
- return parsed;
561
- }
562
- function windowOf(value) {
563
- return typeof value === "number" || value === true ? value : void 0;
564
- }
565
- function numberOf(value) {
566
- return typeof value === "number" ? value : void 0;
567
- }
568
- //#endregion
569
- //#region src/compat/options/shared.ts
570
- const DEFAULT_MAX_PAYLOAD = 104857600;
571
- const DEFAULT_MAX_FRAGMENTS = 16384;
572
- const DEFAULT_MAX_BUFFERED_CHUNKS = 262144;
573
- const DEFAULT_CLOSE_TIMEOUT = 3e4;
574
- const SUBPROTOCOL_PATTERN = /^[!#$%&'*+\-.0-9A-Z^_`|a-z~]+$/;
575
- function isProtocolToken(value) {
576
- return typeof value === "string" && SUBPROTOCOL_PATTERN.test(value);
577
- }
578
- function invalidOption(message, constructor = TypeError) {
579
- throw createError("ERR_INVALID_OPTION", message, constructor);
580
- }
581
- function normalizeProtocols$1(protocols) {
582
- if (protocols === void 0) return [];
583
- const list = typeof protocols === "string" ? [protocols] : protocols;
584
- const candidates = Array.isArray(list) ? list : [list];
585
- const seen = /* @__PURE__ */ new Set();
586
- const result = [];
587
- for (const protocol of candidates) {
588
- if (typeof protocol !== "string" || !SUBPROTOCOL_PATTERN.test(protocol) || seen.has(protocol)) invalidOption("An invalid or duplicated subprotocol was specified", SyntaxError);
589
- seen.add(protocol);
590
- result.push(protocol);
591
- }
592
- return result;
593
- }
594
- function parseProtocolHeader(header) {
595
- return normalizeProtocols$1(header.split(",").map((protocol) => protocol.trim()));
596
- }
597
- function windowBitsOf(name, raw) {
598
- if (raw === void 0 || raw === false) return raw;
599
- if (typeof raw === "number" && Number.isInteger(raw) && raw >= 8 && raw <= 15) return raw;
600
- return invalidOption(`The ${name} option must be an integer in [8, 15] (received ${String(raw)})`, RangeError);
601
- }
602
- function normalizePerMessageDeflate(value, fallback) {
603
- if (value !== void 0 && !value) return false;
604
- const resolved = value ?? fallback;
605
- if (!resolved) return false;
606
- const options = resolved === true ? {} : resolved;
607
- return {
608
- serverNoContextTakeover: options.serverNoContextTakeover,
609
- clientNoContextTakeover: options.clientNoContextTakeover,
610
- serverMaxWindowBits: windowBitsOf("serverMaxWindowBits", options.serverMaxWindowBits),
611
- clientMaxWindowBits: windowBitsOf("clientMaxWindowBits", options.clientMaxWindowBits),
612
- threshold: options.threshold ?? 1024,
613
- concurrencyLimit: options.concurrencyLimit ?? 10,
614
- zlibDeflateOptions: options.zlibDeflateOptions,
615
- zlibInflateOptions: options.zlibInflateOptions
616
- };
617
- }
618
- function closeTimeoutOf(source) {
619
- const raw = source.closeTimeout;
620
- if (raw === void 0) return DEFAULT_CLOSE_TIMEOUT;
621
- if (typeof raw !== "number" || !Number.isFinite(raw) || raw < 0) invalidOption(`The closeTimeout option must be a non-negative number (received ${String(raw)})`, RangeError);
622
- return raw;
623
- }
624
624
  //#endregion
625
625
  //#region src/compat/server/handshake.ts
626
626
  const KEY_PATTERN = /^[+/0-9A-Za-z]{22}==$/;
@@ -1362,9 +1362,9 @@ function closeConnection(state, code, reason) {
1362
1362
  return;
1363
1363
  }
1364
1364
  if (state.readyState === 2) return;
1365
- state.readyState = 2;
1366
1365
  const closeCode = closeCodeOf(code);
1367
1366
  const closeReason = toCloseReason(reason);
1367
+ state.readyState = 2;
1368
1368
  if (state.codec !== null) {
1369
1369
  closeFramed(state, closeCode, closeReason);
1370
1370
  armCloseTimeout(state, state.closeTimeout);
@@ -1876,7 +1876,7 @@ function answer(options, offer) {
1876
1876
  }
1877
1877
  //#endregion
1878
1878
  //#region src/compat/extensions/scan.ts
1879
- const TOKEN$1 = /^[!#$%&'*+\-.0-9A-Z^_`|a-z~]+$/;
1879
+ const TOKEN = /^[!#$%&'*+\-.0-9A-Z^_`|a-z~]+$/;
1880
1880
  const COMMA$1 = 44;
1881
1881
  const SEMICOLON = 59;
1882
1882
  const EQUALS = 61;
@@ -1926,7 +1926,7 @@ function readValue(header, start) {
1926
1926
  if (header.charCodeAt(start) !== QUOTE) {
1927
1927
  const token = readToken(header, start, "a parameter value");
1928
1928
  if (token === null) throw unexpectedEnd(header, start);
1929
- if (!TOKEN$1.test(token)) throw syntax(header, start, "invalid parameter value");
1929
+ if (!TOKEN.test(token)) throw syntax(header, start, "invalid parameter value");
1930
1930
  return {
1931
1931
  value: token,
1932
1932
  end: start + token.length
@@ -2253,10 +2253,10 @@ function createWebSocketServer(socketClass, options, callback) {
2253
2253
  const resolved = {
2254
2254
  allowSynchronousEvents: true,
2255
2255
  autoPong: true,
2256
- maxBufferedChunks: 262144,
2257
- maxFragments: 16384,
2258
- closeTimeout: 3e4,
2259
- maxPayload: 104857600,
2256
+ maxBufferedChunks: DEFAULT_MAX_BUFFERED_CHUNKS,
2257
+ maxFragments: DEFAULT_MAX_FRAGMENTS,
2258
+ closeTimeout: DEFAULT_CLOSE_TIMEOUT,
2259
+ maxPayload: DEFAULT_MAX_PAYLOAD,
2260
2260
  skipUTF8Validation: false,
2261
2261
  perMessageDeflate: false,
2262
2262
  handleProtocols: null,
@@ -2323,18 +2323,6 @@ function createWebSocketServer(socketClass, options, callback) {
2323
2323
  }
2324
2324
  //#endregion
2325
2325
  //#region src/compat/client/protocols.ts
2326
- const TOKEN = /^[!#$%&'*+\-.^_`|~\dA-Za-z]+$/;
2327
- function normalizeProtocols(protocols) {
2328
- if (protocols === void 0) return [];
2329
- const list = typeof protocols === "string" ? [protocols] : protocols;
2330
- if (!Array.isArray(list)) throw createError("ERR_INVALID_OPTION", "An invalid or duplicated subprotocol was specified", SyntaxError);
2331
- const seen = /* @__PURE__ */ new Set();
2332
- for (const protocol of list) {
2333
- if (typeof protocol !== "string" || !TOKEN.test(protocol) || seen.has(protocol)) throw createError("ERR_INVALID_OPTION", "An invalid or duplicated subprotocol was specified", SyntaxError);
2334
- seen.add(protocol);
2335
- }
2336
- return [...list];
2337
- }
2338
2326
  function protocolSet(protocols) {
2339
2327
  return new Set(protocols);
2340
2328
  }
@@ -2787,6 +2775,7 @@ function normalizeClientOptions(options) {
2787
2775
  skipUTF8Validation: source.skipUTF8Validation ?? false,
2788
2776
  allowSynchronousEvents: source.allowSynchronousEvents ?? true,
2789
2777
  autoPong: source.autoPong ?? true,
2778
+ protocols: source.protocols,
2790
2779
  closeTimeout: closeTimeoutOf(source),
2791
2780
  perMessageDeflate: normalizePerMessageDeflate(source.perMessageDeflate, true),
2792
2781
  origin: source.origin,
@@ -3184,7 +3173,7 @@ function dial(attempt) {
3184
3173
  function connectSocket(address, protocols, options) {
3185
3174
  const normalized = normalizeClientOptions(options);
3186
3175
  const parsed = parseAddress(address);
3187
- const requested = normalizeProtocols(protocols);
3176
+ const requested = normalizeProtocols(protocols === void 0 ? normalized.protocols : protocols);
3188
3177
  const state = createSocketState();
3189
3178
  state.isServer = false;
3190
3179
  state.url = parsed.url;
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "ventiws",
3
- "version": "1.0.0-rc.1",
3
+ "version": "1.0.0-rc.3",
4
4
  "description": "A drop-in replacement for ws for Node.js, built on the first-party µWebZockets Zig engine via napi-zig.",
5
5
  "homepage": "https://github.com/farbenbuilds/ventiws#readme",
6
6
  "bugs": {
@@ -34,10 +34,10 @@
34
34
  "node": ">=22.12.0"
35
35
  },
36
36
  "optionalDependencies": {
37
- "@ventiws/binding-darwin-arm64": "1.0.0-rc.1",
38
- "@ventiws/binding-darwin-x64": "1.0.0-rc.1",
39
- "@ventiws/binding-linux-arm64-gnu": "1.0.0-rc.1",
40
- "@ventiws/binding-linux-x64-gnu": "1.0.0-rc.1",
41
- "@ventiws/binding-linux-x64-musl": "1.0.0-rc.1"
37
+ "@ventiws/binding-darwin-arm64": "1.0.0-rc.3",
38
+ "@ventiws/binding-darwin-x64": "1.0.0-rc.3",
39
+ "@ventiws/binding-linux-arm64-gnu": "1.0.0-rc.3",
40
+ "@ventiws/binding-linux-x64-gnu": "1.0.0-rc.3",
41
+ "@ventiws/binding-linux-x64-musl": "1.0.0-rc.3"
42
42
  }
43
43
  }