ventiws 1.0.0-rc.0 → 1.0.0-rc.1

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
@@ -12,7 +12,7 @@ observable behaviour rather than its source. ventiws is not affiliated with the
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.0`.** The surface may change between beta
15
+ **ventiws is at `1.0.0-rc.1`.** 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
@@ -475,7 +475,7 @@ function trackClient(state, socket) {
475
475
  }
476
476
  //#endregion
477
477
  //#region package.json
478
- var version = "1.0.0-rc.0";
478
+ var version = "1.0.0-rc.1";
479
479
  //#endregion
480
480
  //#region src/logging/lifecycle.ts
481
481
  const CONTROL = /\p{Cc}/gu;
@@ -1091,8 +1091,8 @@ function queuedBytes(state) {
1091
1091
  return state.transport.writableLength;
1092
1092
  }
1093
1093
  function bufferedAmountOf(state) {
1094
- if (state.attachment === null) return queuedBytes(state);
1095
- return socketBufferedAmount(state.attachment.server, state.attachment.connection);
1094
+ if (state.attachment === null) return queuedBytes(state) + state.bufferedExtra;
1095
+ return socketBufferedAmount(state.attachment.server, state.attachment.connection) + state.bufferedExtra;
1096
1096
  }
1097
1097
  function defer(callback, error) {
1098
1098
  if (typeof callback !== "function") return;
@@ -1327,6 +1327,7 @@ function createSocketState() {
1327
1327
  maxFragments: DEFAULT_MAX_FRAGMENTS,
1328
1328
  cancelHandshake: null,
1329
1329
  pendingSend: null,
1330
+ bufferedExtra: 0,
1330
1331
  compressible: false,
1331
1332
  threshold: 0,
1332
1333
  autoPong: true,
@@ -1354,6 +1355,7 @@ function finishConnection(state, code, reason) {
1354
1355
  state.closeCode = code;
1355
1356
  state.closeReason = reason;
1356
1357
  state.pendingInput = [];
1358
+ state.cancelHandshake = null;
1357
1359
  logSocketClose(state, code, reason);
1358
1360
  emitEvent(state, "close", code, reason);
1359
1361
  }
@@ -1382,6 +1384,7 @@ function reportWithoutClosing(state, error) {
1382
1384
  function closeConnection(state, code, reason) {
1383
1385
  if (state.readyState === 3) return;
1384
1386
  if (state.readyState === 0) {
1387
+ state.cancelHandshake?.();
1385
1388
  failConnection(state, createError("ERR_INVALID_STATE", "WebSocket was closed before the connection was established"));
1386
1389
  return;
1387
1390
  }
@@ -1529,6 +1532,7 @@ function shapeBinary(binaryType, payload, ends) {
1529
1532
  }
1530
1533
  }
1531
1534
  function slicedArrayBuffer(payload) {
1535
+ if (payload.length === payload.buffer.byteLength) return payload.buffer;
1532
1536
  return payload.buffer.slice(payload.byteOffset, payload.byteOffset + payload.byteLength);
1533
1537
  }
1534
1538
  function sliceFragments(payload, ends) {
@@ -1536,7 +1540,7 @@ function sliceFragments(payload, ends) {
1536
1540
  const pieces = [];
1537
1541
  let start = 0;
1538
1542
  for (const end of ends) {
1539
- const at = Math.min(end, payload.length);
1543
+ const at = Math.max(start, Math.min(end, payload.length));
1540
1544
  if (at > start) pieces.push(payload.subarray(start, at));
1541
1545
  start = at;
1542
1546
  }
@@ -1554,7 +1558,7 @@ function dispatch(state, handle, kind, code, payload, ends) {
1554
1558
  emitBinary(state, payload, ends);
1555
1559
  return;
1556
1560
  case "ping":
1557
- if (state.autoPong) writePong(state, payload);
1561
+ if (state.autoPong && state.readyState === 1) writePong(state, payload);
1558
1562
  emitEvent(state, "ping", payload);
1559
1563
  return;
1560
1564
  case "pong":
@@ -1615,7 +1619,7 @@ function drainDeferred(state, handle, released, resume) {
1615
1619
  return;
1616
1620
  }
1617
1621
  honoured = false;
1618
- const ends = codecFragmentEnds(handle);
1622
+ const ends = state.binaryType === "fragments" ? codecFragmentEnds(handle) : null;
1619
1623
  takeCodecEvent(handle);
1620
1624
  if (event === null) continue;
1621
1625
  dispatch(state, handle, event.kind, event.code, event.payload, ends);
@@ -1744,6 +1748,7 @@ function failTransport(state, error) {
1744
1748
  function terminateConnection(state) {
1745
1749
  if (state.readyState === 3) return;
1746
1750
  if (state.readyState === 0) {
1751
+ state.cancelHandshake?.();
1747
1752
  failConnection(state, createError("ERR_INVALID_STATE", "WebSocket was closed before the connection was established"));
1748
1753
  return;
1749
1754
  }
@@ -1847,7 +1852,6 @@ function serverWindowUsable(options, offer) {
1847
1852
  function declines(options, offer) {
1848
1853
  if (options.serverNoContextTakeover === false && offer.server_no_context_takeover) return true;
1849
1854
  if (!serverWindowUsable(options, offer)) return true;
1850
- if (offer.client_max_window_bits === true && options.clientMaxWindowBits === false) return true;
1851
1855
  if (typeof options.clientMaxWindowBits === "number") {
1852
1856
  if (offer.client_max_window_bits === void 0) return true;
1853
1857
  const asked = offer.client_max_window_bits;
@@ -1872,6 +1876,7 @@ function acceptAsClient(response, options) {
1872
1876
  if (options === false) return null;
1873
1877
  const normalized = normalizeParameters(response.parameters, false);
1874
1878
  if (normalized === null) return { refusal: "Invalid Sec-WebSocket-Extensions header" };
1879
+ if (normalized.server_no_context_takeover !== true) return { refusal: "Missing parameter \"server_no_context_takeover\"" };
1875
1880
  if (normalized.client_no_context_takeover && options.clientNoContextTakeover === false) return { refusal: "Unexpected parameter \"client_no_context_takeover\"" };
1876
1881
  const asked = normalized.client_max_window_bits;
1877
1882
  if (asked === void 0) return { accepted: answer(options, normalized) };
@@ -2598,11 +2603,13 @@ function isBlob(value) {
2598
2603
  }
2599
2604
  function sendBlob(state, blob, options, callback) {
2600
2605
  if (state.readyState !== 1) {
2606
+ state.bufferedExtra += blob.size;
2601
2607
  defer(callback, notOpenError(state.readyState));
2602
2608
  return;
2603
2609
  }
2604
2610
  const reading = blob.arrayBuffer().then((buffer) => {
2605
2611
  if (state.readyState !== 1) {
2612
+ state.bufferedExtra += buffer.byteLength;
2606
2613
  defer(callback, notOpenError(state.readyState));
2607
2614
  return;
2608
2615
  }
@@ -2621,25 +2628,18 @@ function asOptions(options) {
2621
2628
  return typeof options === "object" && options !== null ? { ...options } : {};
2622
2629
  }
2623
2630
  //#endregion
2624
- //#region src/compat/socket/send.ts
2625
- function sendData(state, data, options, callback) {
2626
- if (state.readyState === 0) throw notOpenError(0);
2627
- const pending = state.pendingSend;
2628
- if (pending === null) {
2629
- framePayload(state, data, options, callback);
2630
- return;
2631
- }
2632
- const work = () => framePayload(state, data, options, callback);
2633
- pending.then(work, work);
2634
- }
2631
+ //#region src/compat/socket/send-frame.ts
2635
2632
  function framePayload(state, data, options, callback) {
2636
2633
  const failure = resolveCallback(options, callback);
2637
2634
  if (isBlob(data)) {
2638
2635
  sendBlob(state, data, options, failure);
2639
2636
  return;
2640
2637
  }
2641
- const payload = toPayload(data);
2638
+ frameResolved(state, toPayload(data), options, failure);
2639
+ }
2640
+ function frameResolved(state, payload, options, failure) {
2642
2641
  if (state.readyState !== 1) {
2642
+ state.bufferedExtra += payload.bytes.length;
2643
2643
  defer(failure, notOpenError(state.readyState));
2644
2644
  return;
2645
2645
  }
@@ -2693,6 +2693,38 @@ function unhandledStatus(status) {
2693
2693
  return createError("ERR_INVALID_STATE", `ventiws: the engine reported an unknown socket status "${String(status)}"`);
2694
2694
  }
2695
2695
  //#endregion
2696
+ //#region src/compat/socket/send.ts
2697
+ function sendData(state, data, options, callback) {
2698
+ if (state.readyState === 0) throw notOpenError(0);
2699
+ const pending = state.pendingSend;
2700
+ if (pending === null) {
2701
+ framePayload(state, data, options, callback);
2702
+ return;
2703
+ }
2704
+ queueSend(state, pending, data, options, callback);
2705
+ }
2706
+ function queueSend(state, pending, data, options, callback) {
2707
+ const failure = resolveCallback(options, callback);
2708
+ if (isBlob(data)) {
2709
+ settle(state, pending, failure, () => framePayload(state, data, options, callback));
2710
+ return;
2711
+ }
2712
+ const payload = toPayload(data);
2713
+ settle(state, pending, failure, () => frameResolved(state, payload, options, failure));
2714
+ }
2715
+ function settle(state, pending, failure, work) {
2716
+ const run = () => {
2717
+ try {
2718
+ work();
2719
+ } catch (error) {
2720
+ const reported = error instanceof Error ? error : createError("ERR_INVALID_STATE", String(error));
2721
+ if (typeof failure === "function") defer(failure, reported);
2722
+ else reportWithoutClosing(state, reported);
2723
+ }
2724
+ };
2725
+ pending.then(run, run);
2726
+ }
2727
+ //#endregion
2696
2728
  //#region src/compat/socket/record.ts
2697
2729
  const BINARY_TYPE_VALUES = [
2698
2730
  "nodebuffer",
@@ -3137,6 +3169,7 @@ function follow(attempt, request, response, location) {
3137
3169
  }
3138
3170
  if (next.authority !== attempt.address.authority && listenerCount(attempt) === 0) {
3139
3171
  stripCredentials(attempt.handshake.request.headers);
3172
+ stripCredentials(attempt.options.headers);
3140
3173
  attempt.auth = void 0;
3141
3174
  }
3142
3175
  request.abort();
package/dist/index.mjs CHANGED
@@ -448,7 +448,7 @@ function trackClient(state, socket) {
448
448
  }
449
449
  //#endregion
450
450
  //#region package.json
451
- var version = "1.0.0-rc.0";
451
+ var version = "1.0.0-rc.1";
452
452
  //#endregion
453
453
  //#region src/logging/lifecycle.ts
454
454
  const CONTROL = /\p{Cc}/gu;
@@ -1064,8 +1064,8 @@ function queuedBytes(state) {
1064
1064
  return state.transport.writableLength;
1065
1065
  }
1066
1066
  function bufferedAmountOf(state) {
1067
- if (state.attachment === null) return queuedBytes(state);
1068
- return socketBufferedAmount(state.attachment.server, state.attachment.connection);
1067
+ if (state.attachment === null) return queuedBytes(state) + state.bufferedExtra;
1068
+ return socketBufferedAmount(state.attachment.server, state.attachment.connection) + state.bufferedExtra;
1069
1069
  }
1070
1070
  function defer(callback, error) {
1071
1071
  if (typeof callback !== "function") return;
@@ -1300,6 +1300,7 @@ function createSocketState() {
1300
1300
  maxFragments: DEFAULT_MAX_FRAGMENTS,
1301
1301
  cancelHandshake: null,
1302
1302
  pendingSend: null,
1303
+ bufferedExtra: 0,
1303
1304
  compressible: false,
1304
1305
  threshold: 0,
1305
1306
  autoPong: true,
@@ -1327,6 +1328,7 @@ function finishConnection(state, code, reason) {
1327
1328
  state.closeCode = code;
1328
1329
  state.closeReason = reason;
1329
1330
  state.pendingInput = [];
1331
+ state.cancelHandshake = null;
1330
1332
  logSocketClose(state, code, reason);
1331
1333
  emitEvent(state, "close", code, reason);
1332
1334
  }
@@ -1355,6 +1357,7 @@ function reportWithoutClosing(state, error) {
1355
1357
  function closeConnection(state, code, reason) {
1356
1358
  if (state.readyState === 3) return;
1357
1359
  if (state.readyState === 0) {
1360
+ state.cancelHandshake?.();
1358
1361
  failConnection(state, createError("ERR_INVALID_STATE", "WebSocket was closed before the connection was established"));
1359
1362
  return;
1360
1363
  }
@@ -1502,6 +1505,7 @@ function shapeBinary(binaryType, payload, ends) {
1502
1505
  }
1503
1506
  }
1504
1507
  function slicedArrayBuffer(payload) {
1508
+ if (payload.length === payload.buffer.byteLength) return payload.buffer;
1505
1509
  return payload.buffer.slice(payload.byteOffset, payload.byteOffset + payload.byteLength);
1506
1510
  }
1507
1511
  function sliceFragments(payload, ends) {
@@ -1509,7 +1513,7 @@ function sliceFragments(payload, ends) {
1509
1513
  const pieces = [];
1510
1514
  let start = 0;
1511
1515
  for (const end of ends) {
1512
- const at = Math.min(end, payload.length);
1516
+ const at = Math.max(start, Math.min(end, payload.length));
1513
1517
  if (at > start) pieces.push(payload.subarray(start, at));
1514
1518
  start = at;
1515
1519
  }
@@ -1527,7 +1531,7 @@ function dispatch(state, handle, kind, code, payload, ends) {
1527
1531
  emitBinary(state, payload, ends);
1528
1532
  return;
1529
1533
  case "ping":
1530
- if (state.autoPong) writePong(state, payload);
1534
+ if (state.autoPong && state.readyState === 1) writePong(state, payload);
1531
1535
  emitEvent(state, "ping", payload);
1532
1536
  return;
1533
1537
  case "pong":
@@ -1588,7 +1592,7 @@ function drainDeferred(state, handle, released, resume) {
1588
1592
  return;
1589
1593
  }
1590
1594
  honoured = false;
1591
- const ends = codecFragmentEnds(handle);
1595
+ const ends = state.binaryType === "fragments" ? codecFragmentEnds(handle) : null;
1592
1596
  takeCodecEvent(handle);
1593
1597
  if (event === null) continue;
1594
1598
  dispatch(state, handle, event.kind, event.code, event.payload, ends);
@@ -1717,6 +1721,7 @@ function failTransport(state, error) {
1717
1721
  function terminateConnection(state) {
1718
1722
  if (state.readyState === 3) return;
1719
1723
  if (state.readyState === 0) {
1724
+ state.cancelHandshake?.();
1720
1725
  failConnection(state, createError("ERR_INVALID_STATE", "WebSocket was closed before the connection was established"));
1721
1726
  return;
1722
1727
  }
@@ -1820,7 +1825,6 @@ function serverWindowUsable(options, offer) {
1820
1825
  function declines(options, offer) {
1821
1826
  if (options.serverNoContextTakeover === false && offer.server_no_context_takeover) return true;
1822
1827
  if (!serverWindowUsable(options, offer)) return true;
1823
- if (offer.client_max_window_bits === true && options.clientMaxWindowBits === false) return true;
1824
1828
  if (typeof options.clientMaxWindowBits === "number") {
1825
1829
  if (offer.client_max_window_bits === void 0) return true;
1826
1830
  const asked = offer.client_max_window_bits;
@@ -1845,6 +1849,7 @@ function acceptAsClient(response, options) {
1845
1849
  if (options === false) return null;
1846
1850
  const normalized = normalizeParameters(response.parameters, false);
1847
1851
  if (normalized === null) return { refusal: "Invalid Sec-WebSocket-Extensions header" };
1852
+ if (normalized.server_no_context_takeover !== true) return { refusal: "Missing parameter \"server_no_context_takeover\"" };
1848
1853
  if (normalized.client_no_context_takeover && options.clientNoContextTakeover === false) return { refusal: "Unexpected parameter \"client_no_context_takeover\"" };
1849
1854
  const asked = normalized.client_max_window_bits;
1850
1855
  if (asked === void 0) return { accepted: answer(options, normalized) };
@@ -2571,11 +2576,13 @@ function isBlob(value) {
2571
2576
  }
2572
2577
  function sendBlob(state, blob, options, callback) {
2573
2578
  if (state.readyState !== 1) {
2579
+ state.bufferedExtra += blob.size;
2574
2580
  defer(callback, notOpenError(state.readyState));
2575
2581
  return;
2576
2582
  }
2577
2583
  const reading = blob.arrayBuffer().then((buffer) => {
2578
2584
  if (state.readyState !== 1) {
2585
+ state.bufferedExtra += buffer.byteLength;
2579
2586
  defer(callback, notOpenError(state.readyState));
2580
2587
  return;
2581
2588
  }
@@ -2594,25 +2601,18 @@ function asOptions(options) {
2594
2601
  return typeof options === "object" && options !== null ? { ...options } : {};
2595
2602
  }
2596
2603
  //#endregion
2597
- //#region src/compat/socket/send.ts
2598
- function sendData(state, data, options, callback) {
2599
- if (state.readyState === 0) throw notOpenError(0);
2600
- const pending = state.pendingSend;
2601
- if (pending === null) {
2602
- framePayload(state, data, options, callback);
2603
- return;
2604
- }
2605
- const work = () => framePayload(state, data, options, callback);
2606
- pending.then(work, work);
2607
- }
2604
+ //#region src/compat/socket/send-frame.ts
2608
2605
  function framePayload(state, data, options, callback) {
2609
2606
  const failure = resolveCallback(options, callback);
2610
2607
  if (isBlob(data)) {
2611
2608
  sendBlob(state, data, options, failure);
2612
2609
  return;
2613
2610
  }
2614
- const payload = toPayload(data);
2611
+ frameResolved(state, toPayload(data), options, failure);
2612
+ }
2613
+ function frameResolved(state, payload, options, failure) {
2615
2614
  if (state.readyState !== 1) {
2615
+ state.bufferedExtra += payload.bytes.length;
2616
2616
  defer(failure, notOpenError(state.readyState));
2617
2617
  return;
2618
2618
  }
@@ -2666,6 +2666,38 @@ function unhandledStatus(status) {
2666
2666
  return createError("ERR_INVALID_STATE", `ventiws: the engine reported an unknown socket status "${String(status)}"`);
2667
2667
  }
2668
2668
  //#endregion
2669
+ //#region src/compat/socket/send.ts
2670
+ function sendData(state, data, options, callback) {
2671
+ if (state.readyState === 0) throw notOpenError(0);
2672
+ const pending = state.pendingSend;
2673
+ if (pending === null) {
2674
+ framePayload(state, data, options, callback);
2675
+ return;
2676
+ }
2677
+ queueSend(state, pending, data, options, callback);
2678
+ }
2679
+ function queueSend(state, pending, data, options, callback) {
2680
+ const failure = resolveCallback(options, callback);
2681
+ if (isBlob(data)) {
2682
+ settle(state, pending, failure, () => framePayload(state, data, options, callback));
2683
+ return;
2684
+ }
2685
+ const payload = toPayload(data);
2686
+ settle(state, pending, failure, () => frameResolved(state, payload, options, failure));
2687
+ }
2688
+ function settle(state, pending, failure, work) {
2689
+ const run = () => {
2690
+ try {
2691
+ work();
2692
+ } catch (error) {
2693
+ const reported = error instanceof Error ? error : createError("ERR_INVALID_STATE", String(error));
2694
+ if (typeof failure === "function") defer(failure, reported);
2695
+ else reportWithoutClosing(state, reported);
2696
+ }
2697
+ };
2698
+ pending.then(run, run);
2699
+ }
2700
+ //#endregion
2669
2701
  //#region src/compat/socket/record.ts
2670
2702
  const BINARY_TYPE_VALUES = [
2671
2703
  "nodebuffer",
@@ -3110,6 +3142,7 @@ function follow(attempt, request, response, location) {
3110
3142
  }
3111
3143
  if (next.authority !== attempt.address.authority && listenerCount(attempt) === 0) {
3112
3144
  stripCredentials(attempt.handshake.request.headers);
3145
+ stripCredentials(attempt.options.headers);
3113
3146
  attempt.auth = void 0;
3114
3147
  }
3115
3148
  request.abort();
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "ventiws",
3
- "version": "1.0.0-rc.0",
3
+ "version": "1.0.0-rc.1",
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.0",
38
- "@ventiws/binding-darwin-x64": "1.0.0-rc.0",
39
- "@ventiws/binding-linux-arm64-gnu": "1.0.0-rc.0",
40
- "@ventiws/binding-linux-x64-gnu": "1.0.0-rc.0",
41
- "@ventiws/binding-linux-x64-musl": "1.0.0-rc.0"
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"
42
42
  }
43
43
  }