crossws 0.2.3 → 0.3.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (66) hide show
  1. package/README.md +8 -9
  2. package/adapters/cloudflare-durable.d.ts +2 -0
  3. package/adapters/sse.d.ts +2 -0
  4. package/dist/adapters/bun.d.mts +25 -6
  5. package/dist/adapters/bun.d.ts +25 -6
  6. package/dist/adapters/bun.mjs +54 -62
  7. package/dist/adapters/cloudflare-durable.d.mts +20 -0
  8. package/dist/adapters/cloudflare-durable.d.ts +20 -0
  9. package/dist/adapters/cloudflare-durable.mjs +132 -0
  10. package/dist/adapters/cloudflare.d.mts +5 -5
  11. package/dist/adapters/cloudflare.d.ts +5 -5
  12. package/dist/adapters/cloudflare.mjs +56 -49
  13. package/dist/adapters/deno.d.mts +10 -3
  14. package/dist/adapters/deno.d.ts +10 -3
  15. package/dist/adapters/deno.mjs +54 -45
  16. package/dist/adapters/node.d.mts +4 -2
  17. package/dist/adapters/node.d.ts +4 -2
  18. package/dist/adapters/node.mjs +108 -71
  19. package/dist/adapters/sse.d.mts +12 -0
  20. package/dist/adapters/sse.d.ts +12 -0
  21. package/dist/adapters/sse.mjs +120 -0
  22. package/dist/adapters/uws.d.mts +45 -9
  23. package/dist/adapters/uws.d.ts +45 -9
  24. package/dist/adapters/uws.mjs +148 -124
  25. package/dist/index.d.mts +136 -4
  26. package/dist/index.d.ts +136 -4
  27. package/dist/index.mjs +1 -10
  28. package/dist/shared/crossws.B4sHId41.mjs +42 -0
  29. package/dist/shared/crossws.By9qWDAI.mjs +8 -0
  30. package/dist/shared/crossws.ChIJSJVK.d.mts +297 -0
  31. package/dist/shared/crossws.ChIJSJVK.d.ts +297 -0
  32. package/dist/shared/crossws.DTY7a69w.mjs +315 -0
  33. package/dist/shared/{crossws.a5db571c.mjs → crossws.YgHWLi0G.mjs} +284 -115
  34. package/dist/websocket/{index.d.mts → native.d.mts} +1 -1
  35. package/dist/websocket/{node.d.cts → native.d.ts} +1 -1
  36. package/dist/websocket/native.mjs +3 -0
  37. package/dist/websocket/node.d.mts +1 -1
  38. package/dist/websocket/node.d.ts +1 -1
  39. package/dist/websocket/node.mjs +1 -1
  40. package/dist/websocket/sse.d.mts +41 -0
  41. package/dist/websocket/sse.d.ts +41 -0
  42. package/dist/websocket/sse.mjs +127 -0
  43. package/package.json +78 -75
  44. package/dist/adapters/bun.cjs +0 -95
  45. package/dist/adapters/bun.d.cts +0 -17
  46. package/dist/adapters/cloudflare.cjs +0 -65
  47. package/dist/adapters/cloudflare.d.cts +0 -12
  48. package/dist/adapters/deno.cjs +0 -63
  49. package/dist/adapters/deno.d.cts +0 -15
  50. package/dist/adapters/node.cjs +0 -726
  51. package/dist/adapters/node.d.cts +0 -298
  52. package/dist/adapters/uws.cjs +0 -153
  53. package/dist/adapters/uws.d.cts +0 -19
  54. package/dist/index.cjs +0 -17
  55. package/dist/index.d.cts +0 -6
  56. package/dist/shared/crossws.2ed26345.cjs +0 -3931
  57. package/dist/shared/crossws.34cfc8d0.mjs +0 -139
  58. package/dist/shared/crossws.a2e5c71e.d.cts +0 -112
  59. package/dist/shared/crossws.a2e5c71e.d.mts +0 -112
  60. package/dist/shared/crossws.a2e5c71e.d.ts +0 -112
  61. package/dist/shared/crossws.c13afbe7.cjs +0 -146
  62. package/dist/websocket/index.cjs +0 -5
  63. package/dist/websocket/index.d.cts +0 -10
  64. package/dist/websocket/index.d.ts +0 -10
  65. package/dist/websocket/index.mjs +0 -3
  66. package/dist/websocket/node.cjs +0 -17
@@ -15,10 +15,16 @@ function getDefaultExportFromCjs (x) {
15
15
 
16
16
  var bufferUtil$1 = {exports: {}};
17
17
 
18
+ const BINARY_TYPES$2 = ['nodebuffer', 'arraybuffer', 'fragments'];
19
+ const hasBlob$1 = typeof Blob !== 'undefined';
20
+
21
+ if (hasBlob$1) BINARY_TYPES$2.push('blob');
22
+
18
23
  var constants = {
19
- BINARY_TYPES: ['nodebuffer', 'arraybuffer', 'fragments'],
24
+ BINARY_TYPES: BINARY_TYPES$2,
20
25
  EMPTY_BUFFER: Buffer.alloc(0),
21
26
  GUID: '258EAFA5-E914-47DA-95CA-C5AB0DC85B11',
27
+ hasBlob: hasBlob$1,
22
28
  kForOnEventAttribute: Symbol('kIsForOnEventAttribute'),
23
29
  kListener: Symbol('kListener'),
24
30
  kStatusCode: Symbol('status-code'),
@@ -734,6 +740,8 @@ var isValidUTF8_1;
734
740
 
735
741
  const { isUtf8 } = require$$0$1;
736
742
 
743
+ const { hasBlob } = constants;
744
+
737
745
  //
738
746
  // Allowed token characters:
739
747
  //
@@ -839,7 +847,27 @@ function _isValidUTF8(buf) {
839
847
  return true;
840
848
  }
841
849
 
850
+ /**
851
+ * Determines whether a value is a `Blob`.
852
+ *
853
+ * @param {*} value The value to be tested
854
+ * @return {Boolean} `true` if `value` is a `Blob`, else `false`
855
+ * @private
856
+ */
857
+ function isBlob$2(value) {
858
+ return (
859
+ hasBlob &&
860
+ typeof value === 'object' &&
861
+ typeof value.arrayBuffer === 'function' &&
862
+ typeof value.type === 'string' &&
863
+ typeof value.stream === 'function' &&
864
+ (value[Symbol.toStringTag] === 'Blob' ||
865
+ value[Symbol.toStringTag] === 'File')
866
+ );
867
+ }
868
+
842
869
  validation.exports = {
870
+ isBlob: isBlob$2,
843
871
  isValidStatusCode: isValidStatusCode$2,
844
872
  isValidUTF8: _isValidUTF8,
845
873
  tokenChars: tokenChars$1
@@ -870,19 +898,12 @@ const {
870
898
  BINARY_TYPES: BINARY_TYPES$1,
871
899
  EMPTY_BUFFER: EMPTY_BUFFER$2,
872
900
  kStatusCode: kStatusCode$1,
873
- kWebSocket: kWebSocket$1
901
+ kWebSocket: kWebSocket$2
874
902
  } = constants;
875
903
  const { concat, toArrayBuffer, unmask } = bufferUtilExports;
876
904
  const { isValidStatusCode: isValidStatusCode$1, isValidUTF8 } = validationExports;
877
905
 
878
906
  const FastBuffer = Buffer[Symbol.species];
879
- const promise = Promise.resolve();
880
-
881
- //
882
- // `queueMicrotask()` is not available in Node.js < 11.
883
- //
884
- const queueTask =
885
- typeof queueMicrotask === 'function' ? queueMicrotask : queueMicrotaskShim;
886
907
 
887
908
  const GET_INFO = 0;
888
909
  const GET_PAYLOAD_LENGTH_16 = 1;
@@ -902,7 +923,7 @@ let Receiver$1 = class Receiver extends Writable {
902
923
  * Creates a Receiver instance.
903
924
  *
904
925
  * @param {Object} [options] Options object
905
- * @param {Boolean} [options.allowSynchronousEvents=false] Specifies whether
926
+ * @param {Boolean} [options.allowSynchronousEvents=true] Specifies whether
906
927
  * any of the `'message'`, `'ping'`, and `'pong'` events can be emitted
907
928
  * multiple times in the same tick
908
929
  * @param {String} [options.binaryType=nodebuffer] The type for binary data
@@ -917,13 +938,16 @@ let Receiver$1 = class Receiver extends Writable {
917
938
  constructor(options = {}) {
918
939
  super();
919
940
 
920
- this._allowSynchronousEvents = !!options.allowSynchronousEvents;
941
+ this._allowSynchronousEvents =
942
+ options.allowSynchronousEvents !== undefined
943
+ ? options.allowSynchronousEvents
944
+ : true;
921
945
  this._binaryType = options.binaryType || BINARY_TYPES$1[0];
922
946
  this._extensions = options.extensions || {};
923
947
  this._isServer = !!options.isServer;
924
948
  this._maxPayload = options.maxPayload | 0;
925
949
  this._skipUTF8Validation = !!options.skipUTF8Validation;
926
- this[kWebSocket$1] = undefined;
950
+ this[kWebSocket$2] = undefined;
927
951
 
928
952
  this._bufferedBytes = 0;
929
953
  this._buffers = [];
@@ -1426,21 +1450,18 @@ let Receiver$1 = class Receiver extends Writable {
1426
1450
  data = concat(fragments, messageLength);
1427
1451
  } else if (this._binaryType === 'arraybuffer') {
1428
1452
  data = toArrayBuffer(concat(fragments, messageLength));
1453
+ } else if (this._binaryType === 'blob') {
1454
+ data = new Blob(fragments);
1429
1455
  } else {
1430
1456
  data = fragments;
1431
1457
  }
1432
1458
 
1433
- //
1434
- // If the state is `INFLATING`, it means that the frame data was
1435
- // decompressed asynchronously, so there is no need to defer the event
1436
- // as it will be emitted asynchronously anyway.
1437
- //
1438
- if (this._state === INFLATING || this._allowSynchronousEvents) {
1459
+ if (this._allowSynchronousEvents) {
1439
1460
  this.emit('message', data, true);
1440
1461
  this._state = GET_INFO;
1441
1462
  } else {
1442
1463
  this._state = DEFER_EVENT;
1443
- queueTask(() => {
1464
+ setImmediate(() => {
1444
1465
  this.emit('message', data, true);
1445
1466
  this._state = GET_INFO;
1446
1467
  this.startLoop(cb);
@@ -1467,7 +1488,7 @@ let Receiver$1 = class Receiver extends Writable {
1467
1488
  this._state = GET_INFO;
1468
1489
  } else {
1469
1490
  this._state = DEFER_EVENT;
1470
- queueTask(() => {
1491
+ setImmediate(() => {
1471
1492
  this.emit('message', buf, false);
1472
1493
  this._state = GET_INFO;
1473
1494
  this.startLoop(cb);
@@ -1538,7 +1559,7 @@ let Receiver$1 = class Receiver extends Writable {
1538
1559
  this._state = GET_INFO;
1539
1560
  } else {
1540
1561
  this._state = DEFER_EVENT;
1541
- queueTask(() => {
1562
+ setImmediate(() => {
1542
1563
  this.emit(this._opcode === 0x09 ? 'ping' : 'pong', data);
1543
1564
  this._state = GET_INFO;
1544
1565
  this.startLoop(cb);
@@ -1575,45 +1596,23 @@ let Receiver$1 = class Receiver extends Writable {
1575
1596
 
1576
1597
  var receiver = Receiver$1;
1577
1598
 
1578
- /**
1579
- * A shim for `queueMicrotask()`.
1580
- *
1581
- * @param {Function} cb Callback
1582
- */
1583
- function queueMicrotaskShim(cb) {
1584
- promise.then(cb).catch(throwErrorNextTick);
1585
- }
1586
-
1587
- /**
1588
- * Throws an error.
1589
- *
1590
- * @param {Error} err The error to throw
1591
- * @private
1592
- */
1593
- function throwError(err) {
1594
- throw err;
1595
- }
1596
-
1597
- /**
1598
- * Throws an error in the next tick.
1599
- *
1600
- * @param {Error} err The error to throw
1601
- * @private
1602
- */
1603
- function throwErrorNextTick(err) {
1604
- process.nextTick(throwError, err);
1605
- }
1606
-
1607
1599
  /* eslint no-unused-vars: ["error", { "varsIgnorePattern": "^Duplex" }] */
1608
1600
  const { randomFillSync } = require$$1;
1609
1601
 
1610
1602
  const PerMessageDeflate$1 = permessageDeflate;
1611
- const { EMPTY_BUFFER: EMPTY_BUFFER$1 } = constants;
1612
- const { isValidStatusCode } = validationExports;
1603
+ const { EMPTY_BUFFER: EMPTY_BUFFER$1, kWebSocket: kWebSocket$1, NOOP: NOOP$1 } = constants;
1604
+ const { isBlob: isBlob$1, isValidStatusCode } = validationExports;
1613
1605
  const { mask: applyMask, toBuffer: toBuffer$1 } = bufferUtilExports;
1614
1606
 
1615
1607
  const kByteLength = Symbol('kByteLength');
1616
1608
  const maskBuffer = Buffer.alloc(4);
1609
+ const RANDOM_POOL_SIZE = 8 * 1024;
1610
+ let randomPool;
1611
+ let randomPoolPointer = RANDOM_POOL_SIZE;
1612
+
1613
+ const DEFAULT = 0;
1614
+ const DEFLATING = 1;
1615
+ const GET_BLOB_DATA = 2;
1617
1616
 
1618
1617
  /**
1619
1618
  * HyBi Sender implementation.
@@ -1641,8 +1640,10 @@ let Sender$1 = class Sender {
1641
1640
  this._compress = false;
1642
1641
 
1643
1642
  this._bufferedBytes = 0;
1644
- this._deflating = false;
1645
1643
  this._queue = [];
1644
+ this._state = DEFAULT;
1645
+ this.onerror = NOOP$1;
1646
+ this[kWebSocket$1] = undefined;
1646
1647
  }
1647
1648
 
1648
1649
  /**
@@ -1678,7 +1679,24 @@ let Sender$1 = class Sender {
1678
1679
  if (options.generateMask) {
1679
1680
  options.generateMask(mask);
1680
1681
  } else {
1681
- randomFillSync(mask, 0, 4);
1682
+ if (randomPoolPointer === RANDOM_POOL_SIZE) {
1683
+ /* istanbul ignore else */
1684
+ if (randomPool === undefined) {
1685
+ //
1686
+ // This is lazily initialized because server-sent frames must not
1687
+ // be masked so it may never be used.
1688
+ //
1689
+ randomPool = Buffer.alloc(RANDOM_POOL_SIZE);
1690
+ }
1691
+
1692
+ randomFillSync(randomPool, 0, RANDOM_POOL_SIZE);
1693
+ randomPoolPointer = 0;
1694
+ }
1695
+
1696
+ mask[0] = randomPool[randomPoolPointer++];
1697
+ mask[1] = randomPool[randomPoolPointer++];
1698
+ mask[2] = randomPool[randomPoolPointer++];
1699
+ mask[3] = randomPool[randomPoolPointer++];
1682
1700
  }
1683
1701
 
1684
1702
  skipMasking = (mask[0] | mask[1] | mask[2] | mask[3]) === 0;
@@ -1792,7 +1810,7 @@ let Sender$1 = class Sender {
1792
1810
  rsv1: false
1793
1811
  };
1794
1812
 
1795
- if (this._deflating) {
1813
+ if (this._state !== DEFAULT) {
1796
1814
  this.enqueue([this.dispatch, buf, false, options, cb]);
1797
1815
  } else {
1798
1816
  this.sendFrame(Sender.frame(buf, options), cb);
@@ -1814,6 +1832,9 @@ let Sender$1 = class Sender {
1814
1832
  if (typeof data === 'string') {
1815
1833
  byteLength = Buffer.byteLength(data);
1816
1834
  readOnly = false;
1835
+ } else if (isBlob$1(data)) {
1836
+ byteLength = data.size;
1837
+ readOnly = false;
1817
1838
  } else {
1818
1839
  data = toBuffer$1(data);
1819
1840
  byteLength = data.length;
@@ -1835,7 +1856,13 @@ let Sender$1 = class Sender {
1835
1856
  rsv1: false
1836
1857
  };
1837
1858
 
1838
- if (this._deflating) {
1859
+ if (isBlob$1(data)) {
1860
+ if (this._state !== DEFAULT) {
1861
+ this.enqueue([this.getBlobData, data, false, options, cb]);
1862
+ } else {
1863
+ this.getBlobData(data, false, options, cb);
1864
+ }
1865
+ } else if (this._state !== DEFAULT) {
1839
1866
  this.enqueue([this.dispatch, data, false, options, cb]);
1840
1867
  } else {
1841
1868
  this.sendFrame(Sender.frame(data, options), cb);
@@ -1857,6 +1884,9 @@ let Sender$1 = class Sender {
1857
1884
  if (typeof data === 'string') {
1858
1885
  byteLength = Buffer.byteLength(data);
1859
1886
  readOnly = false;
1887
+ } else if (isBlob$1(data)) {
1888
+ byteLength = data.size;
1889
+ readOnly = false;
1860
1890
  } else {
1861
1891
  data = toBuffer$1(data);
1862
1892
  byteLength = data.length;
@@ -1878,7 +1908,13 @@ let Sender$1 = class Sender {
1878
1908
  rsv1: false
1879
1909
  };
1880
1910
 
1881
- if (this._deflating) {
1911
+ if (isBlob$1(data)) {
1912
+ if (this._state !== DEFAULT) {
1913
+ this.enqueue([this.getBlobData, data, false, options, cb]);
1914
+ } else {
1915
+ this.getBlobData(data, false, options, cb);
1916
+ }
1917
+ } else if (this._state !== DEFAULT) {
1882
1918
  this.enqueue([this.dispatch, data, false, options, cb]);
1883
1919
  } else {
1884
1920
  this.sendFrame(Sender.frame(data, options), cb);
@@ -1912,6 +1948,9 @@ let Sender$1 = class Sender {
1912
1948
  if (typeof data === 'string') {
1913
1949
  byteLength = Buffer.byteLength(data);
1914
1950
  readOnly = false;
1951
+ } else if (isBlob$1(data)) {
1952
+ byteLength = data.size;
1953
+ readOnly = false;
1915
1954
  } else {
1916
1955
  data = toBuffer$1(data);
1917
1956
  byteLength = data.length;
@@ -1939,40 +1978,94 @@ let Sender$1 = class Sender {
1939
1978
 
1940
1979
  if (options.fin) this._firstFragment = true;
1941
1980
 
1942
- if (perMessageDeflate) {
1943
- const opts = {
1944
- [kByteLength]: byteLength,
1945
- fin: options.fin,
1946
- generateMask: this._generateMask,
1947
- mask: options.mask,
1948
- maskBuffer: this._maskBuffer,
1949
- opcode,
1950
- readOnly,
1951
- rsv1
1952
- };
1981
+ const opts = {
1982
+ [kByteLength]: byteLength,
1983
+ fin: options.fin,
1984
+ generateMask: this._generateMask,
1985
+ mask: options.mask,
1986
+ maskBuffer: this._maskBuffer,
1987
+ opcode,
1988
+ readOnly,
1989
+ rsv1
1990
+ };
1953
1991
 
1954
- if (this._deflating) {
1955
- this.enqueue([this.dispatch, data, this._compress, opts, cb]);
1992
+ if (isBlob$1(data)) {
1993
+ if (this._state !== DEFAULT) {
1994
+ this.enqueue([this.getBlobData, data, this._compress, opts, cb]);
1956
1995
  } else {
1957
- this.dispatch(data, this._compress, opts, cb);
1996
+ this.getBlobData(data, this._compress, opts, cb);
1958
1997
  }
1998
+ } else if (this._state !== DEFAULT) {
1999
+ this.enqueue([this.dispatch, data, this._compress, opts, cb]);
1959
2000
  } else {
1960
- this.sendFrame(
1961
- Sender.frame(data, {
1962
- [kByteLength]: byteLength,
1963
- fin: options.fin,
1964
- generateMask: this._generateMask,
1965
- mask: options.mask,
1966
- maskBuffer: this._maskBuffer,
1967
- opcode,
1968
- readOnly,
1969
- rsv1: false
1970
- }),
1971
- cb
1972
- );
2001
+ this.dispatch(data, this._compress, opts, cb);
1973
2002
  }
1974
2003
  }
1975
2004
 
2005
+ /**
2006
+ * Gets the contents of a blob as binary data.
2007
+ *
2008
+ * @param {Blob} blob The blob
2009
+ * @param {Boolean} [compress=false] Specifies whether or not to compress
2010
+ * the data
2011
+ * @param {Object} options Options object
2012
+ * @param {Boolean} [options.fin=false] Specifies whether or not to set the
2013
+ * FIN bit
2014
+ * @param {Function} [options.generateMask] The function used to generate the
2015
+ * masking key
2016
+ * @param {Boolean} [options.mask=false] Specifies whether or not to mask
2017
+ * `data`
2018
+ * @param {Buffer} [options.maskBuffer] The buffer used to store the masking
2019
+ * key
2020
+ * @param {Number} options.opcode The opcode
2021
+ * @param {Boolean} [options.readOnly=false] Specifies whether `data` can be
2022
+ * modified
2023
+ * @param {Boolean} [options.rsv1=false] Specifies whether or not to set the
2024
+ * RSV1 bit
2025
+ * @param {Function} [cb] Callback
2026
+ * @private
2027
+ */
2028
+ getBlobData(blob, compress, options, cb) {
2029
+ this._bufferedBytes += options[kByteLength];
2030
+ this._state = GET_BLOB_DATA;
2031
+
2032
+ blob
2033
+ .arrayBuffer()
2034
+ .then((arrayBuffer) => {
2035
+ if (this._socket.destroyed) {
2036
+ const err = new Error(
2037
+ 'The socket was closed while the blob was being read'
2038
+ );
2039
+
2040
+ //
2041
+ // `callCallbacks` is called in the next tick to ensure that errors
2042
+ // that might be thrown in the callbacks behave like errors thrown
2043
+ // outside the promise chain.
2044
+ //
2045
+ process.nextTick(callCallbacks, this, err, cb);
2046
+ return;
2047
+ }
2048
+
2049
+ this._bufferedBytes -= options[kByteLength];
2050
+ const data = toBuffer$1(arrayBuffer);
2051
+
2052
+ if (!compress) {
2053
+ this._state = DEFAULT;
2054
+ this.sendFrame(Sender.frame(data, options), cb);
2055
+ this.dequeue();
2056
+ } else {
2057
+ this.dispatch(data, compress, options, cb);
2058
+ }
2059
+ })
2060
+ .catch((err) => {
2061
+ //
2062
+ // `onError` is called in the next tick for the same reason that
2063
+ // `callCallbacks` above is.
2064
+ //
2065
+ process.nextTick(onError, this, err, cb);
2066
+ });
2067
+ }
2068
+
1976
2069
  /**
1977
2070
  * Dispatches a message.
1978
2071
  *
@@ -2005,27 +2098,19 @@ let Sender$1 = class Sender {
2005
2098
  const perMessageDeflate = this._extensions[PerMessageDeflate$1.extensionName];
2006
2099
 
2007
2100
  this._bufferedBytes += options[kByteLength];
2008
- this._deflating = true;
2101
+ this._state = DEFLATING;
2009
2102
  perMessageDeflate.compress(data, options.fin, (_, buf) => {
2010
2103
  if (this._socket.destroyed) {
2011
2104
  const err = new Error(
2012
2105
  'The socket was closed while data was being compressed'
2013
2106
  );
2014
2107
 
2015
- if (typeof cb === 'function') cb(err);
2016
-
2017
- for (let i = 0; i < this._queue.length; i++) {
2018
- const params = this._queue[i];
2019
- const callback = params[params.length - 1];
2020
-
2021
- if (typeof callback === 'function') callback(err);
2022
- }
2023
-
2108
+ callCallbacks(this, err, cb);
2024
2109
  return;
2025
2110
  }
2026
2111
 
2027
2112
  this._bufferedBytes -= options[kByteLength];
2028
- this._deflating = false;
2113
+ this._state = DEFAULT;
2029
2114
  options.readOnly = false;
2030
2115
  this.sendFrame(Sender.frame(buf, options), cb);
2031
2116
  this.dequeue();
@@ -2038,7 +2123,7 @@ let Sender$1 = class Sender {
2038
2123
  * @private
2039
2124
  */
2040
2125
  dequeue() {
2041
- while (!this._deflating && this._queue.length) {
2126
+ while (this._state === DEFAULT && this._queue.length) {
2042
2127
  const params = this._queue.shift();
2043
2128
 
2044
2129
  this._bufferedBytes -= params[3][kByteLength];
@@ -2078,6 +2163,38 @@ let Sender$1 = class Sender {
2078
2163
 
2079
2164
  var sender = Sender$1;
2080
2165
 
2166
+ /**
2167
+ * Calls queued callbacks with an error.
2168
+ *
2169
+ * @param {Sender} sender The `Sender` instance
2170
+ * @param {Error} err The error to call the callbacks with
2171
+ * @param {Function} [cb] The first callback
2172
+ * @private
2173
+ */
2174
+ function callCallbacks(sender, err, cb) {
2175
+ if (typeof cb === 'function') cb(err);
2176
+
2177
+ for (let i = 0; i < sender._queue.length; i++) {
2178
+ const params = sender._queue[i];
2179
+ const callback = params[params.length - 1];
2180
+
2181
+ if (typeof callback === 'function') callback(err);
2182
+ }
2183
+ }
2184
+
2185
+ /**
2186
+ * Handles a `Sender` error.
2187
+ *
2188
+ * @param {Sender} sender The `Sender` instance
2189
+ * @param {Error} err The error
2190
+ * @param {Function} [cb] The first pending callback
2191
+ * @private
2192
+ */
2193
+ function onError(sender, err, cb) {
2194
+ callCallbacks(sender, err, cb);
2195
+ sender.onerror(err);
2196
+ }
2197
+
2081
2198
  const { kForOnEventAttribute: kForOnEventAttribute$1, kListener: kListener$1 } = constants;
2082
2199
 
2083
2200
  const kCode = Symbol('kCode');
@@ -2571,7 +2688,7 @@ function format$1(extensions) {
2571
2688
 
2572
2689
  var extension = { format: format$1, parse: parse$1 };
2573
2690
 
2574
- /* eslint no-unused-vars: ["error", { "varsIgnorePattern": "^Duplex|Readable$" }] */
2691
+ /* eslint no-unused-vars: ["error", { "varsIgnorePattern": "^Duplex|Readable$", "caughtErrors": "none" }] */
2575
2692
 
2576
2693
  const EventEmitter = require$$0$3;
2577
2694
  const https = require$$1$1;
@@ -2584,6 +2701,8 @@ const { URL } = require$$7;
2584
2701
  const PerMessageDeflate = permessageDeflate;
2585
2702
  const Receiver = receiver;
2586
2703
  const Sender = sender;
2704
+ const { isBlob } = validationExports;
2705
+
2587
2706
  const {
2588
2707
  BINARY_TYPES,
2589
2708
  EMPTY_BUFFER,
@@ -2628,6 +2747,7 @@ class WebSocket extends EventEmitter {
2628
2747
  this._closeFrameSent = false;
2629
2748
  this._closeMessage = EMPTY_BUFFER;
2630
2749
  this._closeTimer = null;
2750
+ this._errorEmitted = false;
2631
2751
  this._extensions = {};
2632
2752
  this._paused = false;
2633
2753
  this._protocol = '';
@@ -2660,9 +2780,8 @@ class WebSocket extends EventEmitter {
2660
2780
  }
2661
2781
 
2662
2782
  /**
2663
- * This deviates from the WHATWG interface since ws doesn't support the
2664
- * required default "blob" type (instead we define a custom "nodebuffer"
2665
- * type).
2783
+ * For historical reasons, the custom "nodebuffer" type is used by the default
2784
+ * instead of "blob".
2666
2785
  *
2667
2786
  * @type {String}
2668
2787
  */
@@ -2783,11 +2902,14 @@ class WebSocket extends EventEmitter {
2783
2902
  skipUTF8Validation: options.skipUTF8Validation
2784
2903
  });
2785
2904
 
2786
- this._sender = new Sender(socket, this._extensions, options.generateMask);
2905
+ const sender = new Sender(socket, this._extensions, options.generateMask);
2906
+
2787
2907
  this._receiver = receiver;
2908
+ this._sender = sender;
2788
2909
  this._socket = socket;
2789
2910
 
2790
2911
  receiver[kWebSocket] = this;
2912
+ sender[kWebSocket] = this;
2791
2913
  socket[kWebSocket] = this;
2792
2914
 
2793
2915
  receiver.on('conclude', receiverOnConclude);
@@ -2797,6 +2919,8 @@ class WebSocket extends EventEmitter {
2797
2919
  receiver.on('ping', receiverOnPing);
2798
2920
  receiver.on('pong', receiverOnPong);
2799
2921
 
2922
+ sender.onerror = senderOnError;
2923
+
2800
2924
  //
2801
2925
  // These methods may not be available if `socket` is just a `Duplex`.
2802
2926
  //
@@ -2892,13 +3016,7 @@ class WebSocket extends EventEmitter {
2892
3016
  }
2893
3017
  });
2894
3018
 
2895
- //
2896
- // Specify a timeout for the closing handshake to complete.
2897
- //
2898
- this._closeTimer = setTimeout(
2899
- this._socket.destroy.bind(this._socket),
2900
- closeTimeout
2901
- );
3019
+ setCloseTimer(this);
2902
3020
  }
2903
3021
 
2904
3022
  /**
@@ -3193,7 +3311,7 @@ var websocket = WebSocket;
3193
3311
  * @param {(String|URL)} address The URL to which to connect
3194
3312
  * @param {Array} protocols The subprotocols
3195
3313
  * @param {Object} [options] Connection options
3196
- * @param {Boolean} [options.allowSynchronousEvents=false] Specifies whether any
3314
+ * @param {Boolean} [options.allowSynchronousEvents=true] Specifies whether any
3197
3315
  * of the `'message'`, `'ping'`, and `'pong'` events can be emitted multiple
3198
3316
  * times in the same tick
3199
3317
  * @param {Boolean} [options.autoPong=true] Specifies whether or not to
@@ -3222,7 +3340,7 @@ var websocket = WebSocket;
3222
3340
  */
3223
3341
  function initAsClient(websocket, address, protocols, options) {
3224
3342
  const opts = {
3225
- allowSynchronousEvents: false,
3343
+ allowSynchronousEvents: true,
3226
3344
  autoPong: true,
3227
3345
  protocolVersion: protocolVersions[1],
3228
3346
  maxPayload: 100 * 1024 * 1024,
@@ -3231,7 +3349,6 @@ function initAsClient(websocket, address, protocols, options) {
3231
3349
  followRedirects: false,
3232
3350
  maxRedirects: 10,
3233
3351
  ...options,
3234
- createConnection: undefined,
3235
3352
  socketPath: undefined,
3236
3353
  hostname: undefined,
3237
3354
  protocol: undefined,
@@ -3302,7 +3419,8 @@ function initAsClient(websocket, address, protocols, options) {
3302
3419
  const protocolSet = new Set();
3303
3420
  let perMessageDeflate;
3304
3421
 
3305
- opts.createConnection = isSecure ? tlsConnect : netConnect;
3422
+ opts.createConnection =
3423
+ opts.createConnection || (isSecure ? tlsConnect : netConnect);
3306
3424
  opts.defaultPort = opts.defaultPort || defaultPort;
3307
3425
  opts.port = parsedUrl.port || defaultPort;
3308
3426
  opts.host = parsedUrl.hostname.startsWith('[')
@@ -3498,7 +3616,9 @@ function initAsClient(websocket, address, protocols, options) {
3498
3616
 
3499
3617
  req = websocket._req = null;
3500
3618
 
3501
- if (res.headers.upgrade.toLowerCase() !== 'websocket') {
3619
+ const upgrade = res.headers.upgrade;
3620
+
3621
+ if (upgrade === undefined || upgrade.toLowerCase() !== 'websocket') {
3502
3622
  abortHandshake(websocket, socket, 'Invalid Upgrade header');
3503
3623
  return;
3504
3624
  }
@@ -3600,6 +3720,11 @@ function initAsClient(websocket, address, protocols, options) {
3600
3720
  */
3601
3721
  function emitErrorAndClose(websocket, err) {
3602
3722
  websocket._readyState = WebSocket.CLOSING;
3723
+ //
3724
+ // The following assignment is practically useless and is done only for
3725
+ // consistency.
3726
+ //
3727
+ websocket._errorEmitted = true;
3603
3728
  websocket.emit('error', err);
3604
3729
  websocket.emitClose();
3605
3730
  }
@@ -3680,7 +3805,7 @@ function abortHandshake(websocket, stream, message) {
3680
3805
  */
3681
3806
  function sendAfterClose(websocket, data, cb) {
3682
3807
  if (data) {
3683
- const length = toBuffer(data).length;
3808
+ const length = isBlob(data) ? data.size : toBuffer(data).length;
3684
3809
 
3685
3810
  //
3686
3811
  // The `_bufferedAmount` property is used only when the peer is a client and
@@ -3756,7 +3881,10 @@ function receiverOnError(err) {
3756
3881
  websocket.close(err[kStatusCode]);
3757
3882
  }
3758
3883
 
3759
- websocket.emit('error', err);
3884
+ if (!websocket._errorEmitted) {
3885
+ websocket._errorEmitted = true;
3886
+ websocket.emit('error', err);
3887
+ }
3760
3888
  }
3761
3889
 
3762
3890
  /**
@@ -3812,6 +3940,47 @@ function resume(stream) {
3812
3940
  stream.resume();
3813
3941
  }
3814
3942
 
3943
+ /**
3944
+ * The `Sender` error event handler.
3945
+ *
3946
+ * @param {Error} The error
3947
+ * @private
3948
+ */
3949
+ function senderOnError(err) {
3950
+ const websocket = this[kWebSocket];
3951
+
3952
+ if (websocket.readyState === WebSocket.CLOSED) return;
3953
+ if (websocket.readyState === WebSocket.OPEN) {
3954
+ websocket._readyState = WebSocket.CLOSING;
3955
+ setCloseTimer(websocket);
3956
+ }
3957
+
3958
+ //
3959
+ // `socket.end()` is used instead of `socket.destroy()` to allow the other
3960
+ // peer to finish sending queued data. There is no need to set a timer here
3961
+ // because `CLOSING` means that it is already set or not needed.
3962
+ //
3963
+ this._socket.end();
3964
+
3965
+ if (!websocket._errorEmitted) {
3966
+ websocket._errorEmitted = true;
3967
+ websocket.emit('error', err);
3968
+ }
3969
+ }
3970
+
3971
+ /**
3972
+ * Set a timer to destroy the underlying raw socket of a WebSocket.
3973
+ *
3974
+ * @param {WebSocket} websocket The WebSocket instance
3975
+ * @private
3976
+ */
3977
+ function setCloseTimer(websocket) {
3978
+ websocket._closeTimer = setTimeout(
3979
+ websocket._socket.destroy.bind(websocket._socket),
3980
+ closeTimeout
3981
+ );
3982
+ }
3983
+
3815
3984
  /**
3816
3985
  * The listener of the socket `'close'` event.
3817
3986
  *