@jsenv/core 39.0.5 → 39.1.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.
package/dist/js/ws.js CHANGED
@@ -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$2;
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$2
@@ -870,7 +898,7 @@ const {
870
898
  BINARY_TYPES: BINARY_TYPES$1,
871
899
  EMPTY_BUFFER: EMPTY_BUFFER$2,
872
900
  kStatusCode: kStatusCode$1,
873
- kWebSocket: kWebSocket$2
901
+ kWebSocket: kWebSocket$3
874
902
  } = constants;
875
903
  const { concat, toArrayBuffer, unmask } = bufferUtilExports;
876
904
  const { isValidStatusCode: isValidStatusCode$1, isValidUTF8 } = validationExports;
@@ -919,7 +947,7 @@ let Receiver$1 = class Receiver extends Writable {
919
947
  this._isServer = !!options.isServer;
920
948
  this._maxPayload = options.maxPayload | 0;
921
949
  this._skipUTF8Validation = !!options.skipUTF8Validation;
922
- this[kWebSocket$2] = undefined;
950
+ this[kWebSocket$3] = undefined;
923
951
 
924
952
  this._bufferedBytes = 0;
925
953
  this._buffers = [];
@@ -1422,6 +1450,8 @@ let Receiver$1 = class Receiver extends Writable {
1422
1450
  data = concat(fragments, messageLength);
1423
1451
  } else if (this._binaryType === 'arraybuffer') {
1424
1452
  data = toArrayBuffer(concat(fragments, messageLength));
1453
+ } else if (this._binaryType === 'blob') {
1454
+ data = new Blob(fragments);
1425
1455
  } else {
1426
1456
  data = fragments;
1427
1457
  }
@@ -1570,8 +1600,8 @@ var receiver = Receiver$1;
1570
1600
  const { randomFillSync } = require$$1;
1571
1601
 
1572
1602
  const PerMessageDeflate$2 = permessageDeflate;
1573
- const { EMPTY_BUFFER: EMPTY_BUFFER$1 } = constants;
1574
- const { isValidStatusCode } = validationExports;
1603
+ const { EMPTY_BUFFER: EMPTY_BUFFER$1, kWebSocket: kWebSocket$2, NOOP: NOOP$1 } = constants;
1604
+ const { isBlob: isBlob$1, isValidStatusCode } = validationExports;
1575
1605
  const { mask: applyMask, toBuffer: toBuffer$1 } = bufferUtilExports;
1576
1606
 
1577
1607
  const kByteLength = Symbol('kByteLength');
@@ -1580,6 +1610,10 @@ const RANDOM_POOL_SIZE = 8 * 1024;
1580
1610
  let randomPool;
1581
1611
  let randomPoolPointer = RANDOM_POOL_SIZE;
1582
1612
 
1613
+ const DEFAULT = 0;
1614
+ const DEFLATING = 1;
1615
+ const GET_BLOB_DATA = 2;
1616
+
1583
1617
  /**
1584
1618
  * HyBi Sender implementation.
1585
1619
  */
@@ -1606,8 +1640,10 @@ let Sender$1 = class Sender {
1606
1640
  this._compress = false;
1607
1641
 
1608
1642
  this._bufferedBytes = 0;
1609
- this._deflating = false;
1610
1643
  this._queue = [];
1644
+ this._state = DEFAULT;
1645
+ this.onerror = NOOP$1;
1646
+ this[kWebSocket$2] = undefined;
1611
1647
  }
1612
1648
 
1613
1649
  /**
@@ -1774,7 +1810,7 @@ let Sender$1 = class Sender {
1774
1810
  rsv1: false
1775
1811
  };
1776
1812
 
1777
- if (this._deflating) {
1813
+ if (this._state !== DEFAULT) {
1778
1814
  this.enqueue([this.dispatch, buf, false, options, cb]);
1779
1815
  } else {
1780
1816
  this.sendFrame(Sender.frame(buf, options), cb);
@@ -1796,6 +1832,9 @@ let Sender$1 = class Sender {
1796
1832
  if (typeof data === 'string') {
1797
1833
  byteLength = Buffer.byteLength(data);
1798
1834
  readOnly = false;
1835
+ } else if (isBlob$1(data)) {
1836
+ byteLength = data.size;
1837
+ readOnly = false;
1799
1838
  } else {
1800
1839
  data = toBuffer$1(data);
1801
1840
  byteLength = data.length;
@@ -1817,7 +1856,13 @@ let Sender$1 = class Sender {
1817
1856
  rsv1: false
1818
1857
  };
1819
1858
 
1820
- 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) {
1821
1866
  this.enqueue([this.dispatch, data, false, options, cb]);
1822
1867
  } else {
1823
1868
  this.sendFrame(Sender.frame(data, options), cb);
@@ -1839,6 +1884,9 @@ let Sender$1 = class Sender {
1839
1884
  if (typeof data === 'string') {
1840
1885
  byteLength = Buffer.byteLength(data);
1841
1886
  readOnly = false;
1887
+ } else if (isBlob$1(data)) {
1888
+ byteLength = data.size;
1889
+ readOnly = false;
1842
1890
  } else {
1843
1891
  data = toBuffer$1(data);
1844
1892
  byteLength = data.length;
@@ -1860,7 +1908,13 @@ let Sender$1 = class Sender {
1860
1908
  rsv1: false
1861
1909
  };
1862
1910
 
1863
- 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) {
1864
1918
  this.enqueue([this.dispatch, data, false, options, cb]);
1865
1919
  } else {
1866
1920
  this.sendFrame(Sender.frame(data, options), cb);
@@ -1894,6 +1948,9 @@ let Sender$1 = class Sender {
1894
1948
  if (typeof data === 'string') {
1895
1949
  byteLength = Buffer.byteLength(data);
1896
1950
  readOnly = false;
1951
+ } else if (isBlob$1(data)) {
1952
+ byteLength = data.size;
1953
+ readOnly = false;
1897
1954
  } else {
1898
1955
  data = toBuffer$1(data);
1899
1956
  byteLength = data.length;
@@ -1921,40 +1978,94 @@ let Sender$1 = class Sender {
1921
1978
 
1922
1979
  if (options.fin) this._firstFragment = true;
1923
1980
 
1924
- if (perMessageDeflate) {
1925
- const opts = {
1926
- [kByteLength]: byteLength,
1927
- fin: options.fin,
1928
- generateMask: this._generateMask,
1929
- mask: options.mask,
1930
- maskBuffer: this._maskBuffer,
1931
- opcode,
1932
- readOnly,
1933
- rsv1
1934
- };
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
+ };
1935
1991
 
1936
- if (this._deflating) {
1937
- 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]);
1938
1995
  } else {
1939
- this.dispatch(data, this._compress, opts, cb);
1996
+ this.getBlobData(data, this._compress, opts, cb);
1940
1997
  }
1998
+ } else if (this._state !== DEFAULT) {
1999
+ this.enqueue([this.dispatch, data, this._compress, opts, cb]);
1941
2000
  } else {
1942
- this.sendFrame(
1943
- Sender.frame(data, {
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: false
1952
- }),
1953
- cb
1954
- );
2001
+ this.dispatch(data, this._compress, opts, cb);
1955
2002
  }
1956
2003
  }
1957
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
+
1958
2069
  /**
1959
2070
  * Dispatches a message.
1960
2071
  *
@@ -1987,27 +2098,19 @@ let Sender$1 = class Sender {
1987
2098
  const perMessageDeflate = this._extensions[PerMessageDeflate$2.extensionName];
1988
2099
 
1989
2100
  this._bufferedBytes += options[kByteLength];
1990
- this._deflating = true;
2101
+ this._state = DEFLATING;
1991
2102
  perMessageDeflate.compress(data, options.fin, (_, buf) => {
1992
2103
  if (this._socket.destroyed) {
1993
2104
  const err = new Error(
1994
2105
  'The socket was closed while data was being compressed'
1995
2106
  );
1996
2107
 
1997
- if (typeof cb === 'function') cb(err);
1998
-
1999
- for (let i = 0; i < this._queue.length; i++) {
2000
- const params = this._queue[i];
2001
- const callback = params[params.length - 1];
2002
-
2003
- if (typeof callback === 'function') callback(err);
2004
- }
2005
-
2108
+ callCallbacks(this, err, cb);
2006
2109
  return;
2007
2110
  }
2008
2111
 
2009
2112
  this._bufferedBytes -= options[kByteLength];
2010
- this._deflating = false;
2113
+ this._state = DEFAULT;
2011
2114
  options.readOnly = false;
2012
2115
  this.sendFrame(Sender.frame(buf, options), cb);
2013
2116
  this.dequeue();
@@ -2020,7 +2123,7 @@ let Sender$1 = class Sender {
2020
2123
  * @private
2021
2124
  */
2022
2125
  dequeue() {
2023
- while (!this._deflating && this._queue.length) {
2126
+ while (this._state === DEFAULT && this._queue.length) {
2024
2127
  const params = this._queue.shift();
2025
2128
 
2026
2129
  this._bufferedBytes -= params[3][kByteLength];
@@ -2060,6 +2163,38 @@ let Sender$1 = class Sender {
2060
2163
 
2061
2164
  var sender = Sender$1;
2062
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
+
2063
2198
  const { kForOnEventAttribute: kForOnEventAttribute$1, kListener: kListener$1 } = constants;
2064
2199
 
2065
2200
  const kCode = Symbol('kCode');
@@ -2566,6 +2701,8 @@ const { URL } = require$$7;
2566
2701
  const PerMessageDeflate$1 = permessageDeflate;
2567
2702
  const Receiver = receiver;
2568
2703
  const Sender = sender;
2704
+ const { isBlob } = validationExports;
2705
+
2569
2706
  const {
2570
2707
  BINARY_TYPES,
2571
2708
  EMPTY_BUFFER,
@@ -2610,6 +2747,7 @@ let WebSocket$1 = class WebSocket extends EventEmitter$1 {
2610
2747
  this._closeFrameSent = false;
2611
2748
  this._closeMessage = EMPTY_BUFFER;
2612
2749
  this._closeTimer = null;
2750
+ this._errorEmitted = false;
2613
2751
  this._extensions = {};
2614
2752
  this._paused = false;
2615
2753
  this._protocol = '';
@@ -2642,9 +2780,8 @@ let WebSocket$1 = class WebSocket extends EventEmitter$1 {
2642
2780
  }
2643
2781
 
2644
2782
  /**
2645
- * This deviates from the WHATWG interface since ws doesn't support the
2646
- * required default "blob" type (instead we define a custom "nodebuffer"
2647
- * type).
2783
+ * For historical reasons, the custom "nodebuffer" type is used by the default
2784
+ * instead of "blob".
2648
2785
  *
2649
2786
  * @type {String}
2650
2787
  */
@@ -2765,11 +2902,14 @@ let WebSocket$1 = class WebSocket extends EventEmitter$1 {
2765
2902
  skipUTF8Validation: options.skipUTF8Validation
2766
2903
  });
2767
2904
 
2768
- this._sender = new Sender(socket, this._extensions, options.generateMask);
2905
+ const sender = new Sender(socket, this._extensions, options.generateMask);
2906
+
2769
2907
  this._receiver = receiver;
2908
+ this._sender = sender;
2770
2909
  this._socket = socket;
2771
2910
 
2772
2911
  receiver[kWebSocket$1] = this;
2912
+ sender[kWebSocket$1] = this;
2773
2913
  socket[kWebSocket$1] = this;
2774
2914
 
2775
2915
  receiver.on('conclude', receiverOnConclude);
@@ -2779,6 +2919,8 @@ let WebSocket$1 = class WebSocket extends EventEmitter$1 {
2779
2919
  receiver.on('ping', receiverOnPing);
2780
2920
  receiver.on('pong', receiverOnPong);
2781
2921
 
2922
+ sender.onerror = senderOnError;
2923
+
2782
2924
  //
2783
2925
  // These methods may not be available if `socket` is just a `Duplex`.
2784
2926
  //
@@ -2874,13 +3016,7 @@ let WebSocket$1 = class WebSocket extends EventEmitter$1 {
2874
3016
  }
2875
3017
  });
2876
3018
 
2877
- //
2878
- // Specify a timeout for the closing handshake to complete.
2879
- //
2880
- this._closeTimer = setTimeout(
2881
- this._socket.destroy.bind(this._socket),
2882
- closeTimeout
2883
- );
3019
+ setCloseTimer(this);
2884
3020
  }
2885
3021
 
2886
3022
  /**
@@ -3584,6 +3720,11 @@ function initAsClient(websocket, address, protocols, options) {
3584
3720
  */
3585
3721
  function emitErrorAndClose(websocket, err) {
3586
3722
  websocket._readyState = WebSocket$1.CLOSING;
3723
+ //
3724
+ // The following assignment is practically useless and is done only for
3725
+ // consistency.
3726
+ //
3727
+ websocket._errorEmitted = true;
3587
3728
  websocket.emit('error', err);
3588
3729
  websocket.emitClose();
3589
3730
  }
@@ -3664,7 +3805,7 @@ function abortHandshake$1(websocket, stream, message) {
3664
3805
  */
3665
3806
  function sendAfterClose(websocket, data, cb) {
3666
3807
  if (data) {
3667
- const length = toBuffer(data).length;
3808
+ const length = isBlob(data) ? data.size : toBuffer(data).length;
3668
3809
 
3669
3810
  //
3670
3811
  // The `_bufferedAmount` property is used only when the peer is a client and
@@ -3740,7 +3881,10 @@ function receiverOnError(err) {
3740
3881
  websocket.close(err[kStatusCode]);
3741
3882
  }
3742
3883
 
3743
- websocket.emit('error', err);
3884
+ if (!websocket._errorEmitted) {
3885
+ websocket._errorEmitted = true;
3886
+ websocket.emit('error', err);
3887
+ }
3744
3888
  }
3745
3889
 
3746
3890
  /**
@@ -3796,6 +3940,47 @@ function resume(stream) {
3796
3940
  stream.resume();
3797
3941
  }
3798
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$1];
3951
+
3952
+ if (websocket.readyState === WebSocket$1.CLOSED) return;
3953
+ if (websocket.readyState === WebSocket$1.OPEN) {
3954
+ websocket._readyState = WebSocket$1.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
+
3799
3984
  /**
3800
3985
  * The listener of the socket `'close'` event.
3801
3986
  *