Huge News!Announcing our $40M Series B led by Abstract Ventures.Learn More
Socket
Sign inDemoInstall
Socket

ws

Package Overview
Dependencies
Maintainers
4
Versions
169
Alerts
File Explorer

Advanced tools

Socket logo

Install Socket

Detect and block malicious and high-risk dependencies

Install

ws - npm Package Compare versions

Comparing version 6.1.4 to 6.2.0

78

lib/buffer-util.js
'use strict';
const { EMPTY_BUFFER } = require('./constants');
/**

@@ -12,2 +14,5 @@ * Merges an array of buffers into a new buffer.

function concat(list, totalLength) {
if (list.length === 0) return EMPTY_BUFFER;
if (list.length === 1) return list[0];
const target = Buffer.allocUnsafe(totalLength);

@@ -56,2 +61,61 @@ var offset = 0;

/**
* Converts a buffer to an `ArrayBuffer`.
*
* @param {Buffer} buf The buffer to convert
* @return {ArrayBuffer} Converted buffer
* @public
*/
function toArrayBuffer(buf) {
if (buf.byteLength === buf.buffer.byteLength) {
return buf.buffer;
}
return buf.buffer.slice(buf.byteOffset, buf.byteOffset + buf.byteLength);
}
/**
* Converts `data` to a `Buffer`.
*
* @param {*} data The data to convert
* @return {Buffer} The buffer
* @throws {TypeError}
* @public
*/
function toBuffer(data) {
toBuffer.readOnly = true;
if (Buffer.isBuffer(data)) return data;
var buf;
if (data instanceof ArrayBuffer) {
buf = Buffer.from(data);
} else if (ArrayBuffer.isView(data)) {
buf = viewToBuffer(data);
} else {
buf = Buffer.from(data);
toBuffer.readOnly = false;
}
return buf;
}
/**
* Converts an `ArrayBuffer` view into a buffer.
*
* @param {(DataView|TypedArray)} view The view to convert
* @return {Buffer} Converted view
* @private
*/
function viewToBuffer(view) {
const buf = Buffer.from(view.buffer);
if (view.byteLength !== view.buffer.byteLength) {
return buf.slice(view.byteOffset, view.byteOffset + view.byteLength);
}
return buf;
}
try {

@@ -62,2 +126,3 @@ const bufferUtil = require('bufferutil');

module.exports = {
concat,
mask(source, mask, output, offset, length) {

@@ -67,10 +132,17 @@ if (length < 48) _mask(source, mask, output, offset, length);

},
toArrayBuffer,
toBuffer,
unmask(buffer, mask) {
if (buffer.length < 32) _unmask(buffer, mask);
else bu.unmask(buffer, mask);
},
concat
}
};
} catch (e) /* istanbul ignore next */ {
module.exports = { concat, mask: _mask, unmask: _unmask };
module.exports = {
concat,
mask: _mask,
toArrayBuffer,
toBuffer,
unmask: _unmask
};
}

65

lib/receiver.js
'use strict';
const stream = require('stream');
const { Writable } = require('stream');
const PerMessageDeflate = require('./permessage-deflate');
const bufferUtil = require('./buffer-util');
const validation = require('./validation');
const constants = require('./constants');
const {
BINARY_TYPES,
EMPTY_BUFFER,
kStatusCode,
kWebSocket
} = require('./constants');
const { concat, toArrayBuffer, unmask } = require('./buffer-util');
const { isValidStatusCode, isValidUTF8 } = require('./validation');

@@ -22,3 +27,3 @@ const GET_INFO = 0;

*/
class Receiver extends stream.Writable {
class Receiver extends Writable {
/**

@@ -34,4 +39,4 @@ * Creates a Receiver instance.

this._binaryType = binaryType || constants.BINARY_TYPES[0];
this[constants.kWebSocket] = undefined;
this._binaryType = binaryType || BINARY_TYPES[0];
this[kWebSocket] = undefined;
this._extensions = extensions || {};

@@ -320,3 +325,3 @@ this._maxPayload = maxPayload | 0;

getData(cb) {
var data = constants.EMPTY_BUFFER;
var data = EMPTY_BUFFER;

@@ -330,3 +335,3 @@ if (this._payloadLength) {

data = this.consume(this._payloadLength);
if (this._masked) bufferUtil.unmask(data, this._mask);
if (this._masked) unmask(data, this._mask);
}

@@ -405,5 +410,5 @@

if (this._binaryType === 'nodebuffer') {
data = toBuffer(fragments, messageLength);
data = concat(fragments, messageLength);
} else if (this._binaryType === 'arraybuffer') {
data = toArrayBuffer(toBuffer(fragments, messageLength));
data = toArrayBuffer(concat(fragments, messageLength));
} else {

@@ -415,5 +420,5 @@ data = fragments;

} else {
const buf = toBuffer(fragments, messageLength);
const buf = concat(fragments, messageLength);
if (!validation.isValidUTF8(buf)) {
if (!isValidUTF8(buf)) {
this._loop = false;

@@ -449,3 +454,3 @@ return error(Error, 'invalid UTF-8 sequence', true, 1007);

if (!validation.isValidStatusCode(code)) {
if (!isValidStatusCode(code)) {
return error(RangeError, `invalid status code ${code}`, true, 1002);

@@ -456,3 +461,3 @@ }

if (!validation.isValidUTF8(buf)) {
if (!isValidUTF8(buf)) {
return error(Error, 'invalid UTF-8 sequence', true, 1007);

@@ -493,32 +498,4 @@ }

Error.captureStackTrace(err, error);
err[constants.kStatusCode] = statusCode;
err[kStatusCode] = statusCode;
return err;
}
/**
* Makes a buffer from a list of fragments.
*
* @param {Buffer[]} fragments The list of fragments composing the message
* @param {Number} messageLength The length of the message
* @return {Buffer}
* @private
*/
function toBuffer(fragments, messageLength) {
if (fragments.length === 1) return fragments[0];
if (fragments.length > 1) return bufferUtil.concat(fragments, messageLength);
return constants.EMPTY_BUFFER;
}
/**
* Converts a buffer to an `ArrayBuffer`.
*
* @param {Buffer} buf The buffer to convert
* @return {ArrayBuffer} Converted buffer
*/
function toArrayBuffer(buf) {
if (buf.byteLength === buf.buffer.byteLength) {
return buf.buffer;
}
return buf.buffer.slice(buf.byteOffset, buf.byteOffset + buf.byteLength);
}
'use strict';
const crypto = require('crypto');
const { randomBytes } = require('crypto');
const PerMessageDeflate = require('./permessage-deflate');
const bufferUtil = require('./buffer-util');
const validation = require('./validation');
const constants = require('./constants');
const { EMPTY_BUFFER } = require('./constants');
const { isValidStatusCode } = require('./validation');
const { mask: applyMask, toBuffer } = require('./buffer-util');

@@ -80,3 +80,3 @@ /**

const mask = crypto.randomBytes(4);
const mask = randomBytes(4);

@@ -90,7 +90,7 @@ target[1] = payloadLength | 0x80;

if (merge) {
bufferUtil.mask(data, mask, target, offset, data.length);
applyMask(data, mask, target, offset, data.length);
return [target];
}
bufferUtil.mask(data, mask, data, 0, data.length);
applyMask(data, mask, data, 0, data.length);
return [target, data];

@@ -112,7 +112,4 @@ }

if (code === undefined) {
buf = constants.EMPTY_BUFFER;
} else if (
typeof code !== 'number' ||
!validation.isValidStatusCode(code)
) {
buf = EMPTY_BUFFER;
} else if (typeof code !== 'number' || !isValidStatusCode(code)) {
throw new TypeError('First argument must be a valid error code number');

@@ -165,19 +162,8 @@ } else if (data === undefined || data === '') {

ping(data, mask, cb) {
var readOnly = true;
const buf = toBuffer(data);
if (!Buffer.isBuffer(data)) {
if (data instanceof ArrayBuffer) {
data = Buffer.from(data);
} else if (ArrayBuffer.isView(data)) {
data = viewToBuffer(data);
} else {
data = Buffer.from(data);
readOnly = false;
}
}
if (this._deflating) {
this.enqueue([this.doPing, data, mask, readOnly, cb]);
this.enqueue([this.doPing, buf, mask, toBuffer.readOnly, cb]);
} else {
this.doPing(data, mask, readOnly, cb);
this.doPing(buf, mask, toBuffer.readOnly, cb);
}

@@ -217,19 +203,8 @@ }

pong(data, mask, cb) {
var readOnly = true;
const buf = toBuffer(data);
if (!Buffer.isBuffer(data)) {
if (data instanceof ArrayBuffer) {
data = Buffer.from(data);
} else if (ArrayBuffer.isView(data)) {
data = viewToBuffer(data);
} else {
data = Buffer.from(data);
readOnly = false;
}
}
if (this._deflating) {
this.enqueue([this.doPong, data, mask, readOnly, cb]);
this.enqueue([this.doPong, buf, mask, toBuffer.readOnly, cb]);
} else {
this.doPong(data, mask, readOnly, cb);
this.doPong(buf, mask, toBuffer.readOnly, cb);
}

@@ -273,23 +248,11 @@ }

send(data, options, cb) {
const buf = toBuffer(data);
const perMessageDeflate = this._extensions[PerMessageDeflate.extensionName];
var opcode = options.binary ? 2 : 1;
var rsv1 = options.compress;
var readOnly = true;
if (!Buffer.isBuffer(data)) {
if (data instanceof ArrayBuffer) {
data = Buffer.from(data);
} else if (ArrayBuffer.isView(data)) {
data = viewToBuffer(data);
} else {
data = Buffer.from(data);
readOnly = false;
}
}
const perMessageDeflate = this._extensions[PerMessageDeflate.extensionName];
if (this._firstFragment) {
this._firstFragment = false;
if (rsv1 && perMessageDeflate) {
rsv1 = data.length >= perMessageDeflate._threshold;
rsv1 = buf.length >= perMessageDeflate._threshold;
}

@@ -310,13 +273,13 @@ this._compress = rsv1;

mask: options.mask,
readOnly
readOnly: toBuffer.readOnly
};
if (this._deflating) {
this.enqueue([this.dispatch, data, this._compress, opts, cb]);
this.enqueue([this.dispatch, buf, this._compress, opts, cb]);
} else {
this.dispatch(data, this._compress, opts, cb);
this.dispatch(buf, this._compress, opts, cb);
}
} else {
this.sendFrame(
Sender.frame(data, {
Sender.frame(buf, {
fin: options.fin,

@@ -326,3 +289,3 @@ rsv1: false,

mask: options.mask,
readOnly
readOnly: toBuffer.readOnly
}),

@@ -408,18 +371,1 @@ cb

module.exports = Sender;
/**
* Converts an `ArrayBuffer` view into a buffer.
*
* @param {(DataView|TypedArray)} view The view to convert
* @return {Buffer} Converted view
* @private
*/
function viewToBuffer(view) {
const buf = Buffer.from(view.buffer);
if (view.byteLength !== view.buffer.byteLength) {
return buf.slice(view.byteOffset, view.byteOffset + view.byteLength);
}
return buf;
}

@@ -9,5 +9,7 @@ 'use strict';

const extension = require('./extension');
const constants = require('./constants');
const WebSocket = require('./websocket');
const { GUID } = require('./constants');
const keyRegex = /^[+/0-9A-Za-z]{22}==$/;
/**

@@ -180,2 +182,6 @@ * Class representing a WebSocket server.

const key =
req.headers['sec-websocket-key'] !== undefined
? req.headers['sec-websocket-key'].trim()
: false;
const version = +req.headers['sec-websocket-version'];

@@ -187,3 +193,4 @@ const extensions = {};

req.headers.upgrade.toLowerCase() !== 'websocket' ||
!req.headers['sec-websocket-key'] ||
!key ||
!keyRegex.test(key) ||
(version !== 8 && version !== 13) ||

@@ -231,3 +238,3 @@ !this.shouldHandle(req)

this.completeUpgrade(extensions, req, socket, head, cb);
this.completeUpgrade(key, extensions, req, socket, head, cb);
});

@@ -240,3 +247,3 @@ return;

this.completeUpgrade(extensions, req, socket, head, cb);
this.completeUpgrade(key, extensions, req, socket, head, cb);
}

@@ -247,2 +254,3 @@

*
* @param {String} key The value of the `Sec-WebSocket-Key` header
* @param {Object} extensions The accepted extensions

@@ -255,3 +263,3 @@ * @param {http.IncomingMessage} req The request object

*/
completeUpgrade(extensions, req, socket, head, cb) {
completeUpgrade(key, extensions, req, socket, head, cb) {
//

@@ -262,5 +270,5 @@ // Destroy the socket if the client has already sent a FIN packet.

const key = crypto
const digest = crypto
.createHash('sha1')
.update(req.headers['sec-websocket-key'] + constants.GUID, 'binary')
.update(key + GUID)
.digest('base64');

@@ -272,3 +280,3 @@

'Connection: Upgrade',
`Sec-WebSocket-Accept: ${key}`
`Sec-WebSocket-Accept: ${digest}`
];

@@ -275,0 +283,0 @@

@@ -14,8 +14,14 @@ 'use strict';

const extension = require('./extension');
const constants = require('./constants');
const Receiver = require('./receiver');
const Sender = require('./sender');
const {
BINARY_TYPES,
EMPTY_BUFFER,
GUID,
kStatusCode,
kWebSocket,
NOOP
} = require('./constants');
const readyStates = ['CONNECTING', 'OPEN', 'CLOSING', 'CLOSED'];
const kWebSocket = constants.kWebSocket;
const protocolVersions = [8, 13];

@@ -43,3 +49,3 @@ const closeTimeout = 30 * 1000; // Allow 30 seconds to terminate the connection cleanly.

this._binaryType = constants.BINARY_TYPES[0];
this._binaryType = BINARY_TYPES[0];
this._closeFrameReceived = false;

@@ -51,3 +57,2 @@ this._closeFrameSent = false;

this._extensions = {};
this._isServer = true;
this._receiver = null;

@@ -58,2 +63,5 @@ this._sender = null;

if (address !== null) {
this._isServer = false;
this._redirects = 0;
if (Array.isArray(protocols)) {

@@ -66,3 +74,5 @@ protocols = protocols.join(', ');

initAsClient.call(this, address, protocols, options);
initAsClient(this, address, protocols, options);
} else {
this._isServer = true;
}

@@ -95,3 +105,3 @@ }

set binaryType(type) {
if (!constants.BINARY_TYPES.includes(type)) return;
if (!BINARY_TYPES.includes(type)) return;

@@ -274,3 +284,3 @@ this._binaryType = type;

if (mask === undefined) mask = !this._isServer;
this._sender.ping(data || constants.EMPTY_BUFFER, mask, cb);
this._sender.ping(data || EMPTY_BUFFER, mask, cb);
}

@@ -307,3 +317,3 @@

if (mask === undefined) mask = !this._isServer;
this._sender.pong(data || constants.EMPTY_BUFFER, mask, cb);
this._sender.pong(data || EMPTY_BUFFER, mask, cb);
}

@@ -355,3 +365,3 @@

this._sender.send(data || constants.EMPTY_BUFFER, opts, cb);
this._sender.send(data || EMPTY_BUFFER, opts, cb);
}

@@ -429,18 +439,27 @@

*
* @param {WebSocket} websocket The client to initialize
* @param {(String|url.Url|url.URL)} address The URL to which to connect
* @param {String} protocols The subprotocols
* @param {Object} options Connection options
* @param {(Boolean|Object)} options.perMessageDeflate Enable/disable permessage-deflate
* @param {Number} options.handshakeTimeout Timeout in milliseconds for the handshake request
* @param {Number} options.protocolVersion Value of the `Sec-WebSocket-Version` header
* @param {String} options.origin Value of the `Origin` or `Sec-WebSocket-Origin` header
* @param {(Boolean|Object)} options.perMessageDeflate Enable/disable
* permessage-deflate
* @param {Number} options.handshakeTimeout Timeout in milliseconds for the
* handshake request
* @param {Number} options.protocolVersion Value of the `Sec-WebSocket-Version`
* header
* @param {String} options.origin Value of the `Origin` or
* `Sec-WebSocket-Origin` header
* @param {Number} options.maxPayload The maximum allowed message size
* @param {Boolean} options.followRedirects Whether or not to follow redirects
* @param {Number} options.maxRedirects The maximum number of redirects allowed
* @private
*/
function initAsClient(address, protocols, options) {
options = Object.assign(
function initAsClient(websocket, address, protocols, options) {
const opts = Object.assign(
{
protocolVersion: protocolVersions[1],
maxPayload: 100 * 1024 * 1024,
perMessageDeflate: true,
maxPayload: 100 * 1024 * 1024
followRedirects: false,
maxRedirects: 10
},

@@ -462,5 +481,5 @@ options,

if (!protocolVersions.includes(options.protocolVersion)) {
if (!protocolVersions.includes(opts.protocolVersion)) {
throw new RangeError(
`Unsupported protocol version: ${options.protocolVersion} ` +
`Unsupported protocol version: ${opts.protocolVersion} ` +
`(supported versions: ${protocolVersions.join(', ')})`

@@ -470,4 +489,2 @@ );

this._isServer = false;
var parsedUrl;

@@ -477,3 +494,3 @@

parsedUrl = address;
this.url = address.href;
websocket.url = address.href;
} else {

@@ -484,3 +501,3 @@ //

parsedUrl = url.URL ? new url.URL(address) : url.parse(address);
this.url = address;
websocket.url = address;
}

@@ -491,3 +508,3 @@

if (!parsedUrl.host && (!isUnixSocket || !parsedUrl.pathname)) {
throw new Error(`Invalid URL: ${this.url}`);
throw new Error(`Invalid URL: ${websocket.url}`);
}

@@ -499,3 +516,3 @@

const key = crypto.randomBytes(16).toString('base64');
const httpObj = isSecure ? https : http;
const get = isSecure ? https.get : http.get;
const path = parsedUrl.search

@@ -506,11 +523,11 @@ ? `${parsedUrl.pathname || '/'}${parsedUrl.search}`

options.createConnection = isSecure ? tlsConnect : netConnect;
options.defaultPort = options.defaultPort || defaultPort;
options.port = parsedUrl.port || defaultPort;
options.host = parsedUrl.hostname.startsWith('[')
opts.createConnection = isSecure ? tlsConnect : netConnect;
opts.defaultPort = opts.defaultPort || defaultPort;
opts.port = parsedUrl.port || defaultPort;
opts.host = parsedUrl.hostname.startsWith('[')
? parsedUrl.hostname.slice(1, -1)
: parsedUrl.hostname;
options.headers = Object.assign(
opts.headers = Object.assign(
{
'Sec-WebSocket-Version': options.protocolVersion,
'Sec-WebSocket-Version': opts.protocolVersion,
'Sec-WebSocket-Key': key,

@@ -520,14 +537,14 @@ Connection: 'Upgrade',

},
options.headers
opts.headers
);
options.path = path;
options.timeout = options.handshakeTimeout;
opts.path = path;
opts.timeout = opts.handshakeTimeout;
if (options.perMessageDeflate) {
if (opts.perMessageDeflate) {
perMessageDeflate = new PerMessageDeflate(
options.perMessageDeflate !== true ? options.perMessageDeflate : {},
opts.perMessageDeflate !== true ? opts.perMessageDeflate : {},
false,
options.maxPayload
opts.maxPayload
);
options.headers['Sec-WebSocket-Extensions'] = extension.format({
opts.headers['Sec-WebSocket-Extensions'] = extension.format({
[PerMessageDeflate.extensionName]: perMessageDeflate.offer()

@@ -537,15 +554,15 @@ });

if (protocols) {
options.headers['Sec-WebSocket-Protocol'] = protocols;
opts.headers['Sec-WebSocket-Protocol'] = protocols;
}
if (options.origin) {
if (options.protocolVersion < 13) {
options.headers['Sec-WebSocket-Origin'] = options.origin;
if (opts.origin) {
if (opts.protocolVersion < 13) {
opts.headers['Sec-WebSocket-Origin'] = opts.origin;
} else {
options.headers.Origin = options.origin;
opts.headers.Origin = opts.origin;
}
}
if (parsedUrl.auth) {
options.auth = parsedUrl.auth;
opts.auth = parsedUrl.auth;
} else if (parsedUrl.username || parsedUrl.password) {
options.auth = `${parsedUrl.username}:${parsedUrl.password}`;
opts.auth = `${parsedUrl.username}:${parsedUrl.password}`;
}

@@ -556,11 +573,11 @@

options.socketPath = parts[0];
options.path = parts[1];
opts.socketPath = parts[0];
opts.path = parts[1];
}
var req = (this._req = httpObj.get(options));
var req = (websocket._req = get(opts));
if (options.handshakeTimeout) {
if (opts.timeout) {
req.on('timeout', () => {
abortHandshake(this, req, 'Opening handshake has timed out');
abortHandshake(websocket, req, 'Opening handshake has timed out');
});

@@ -570,18 +587,43 @@ }

req.on('error', (err) => {
if (this._req.aborted) return;
if (websocket._req.aborted) return;
req = this._req = null;
this.readyState = WebSocket.CLOSING;
this.emit('error', err);
this.emitClose();
req = websocket._req = null;
websocket.readyState = WebSocket.CLOSING;
websocket.emit('error', err);
websocket.emitClose();
});
req.on('response', (res) => {
if (this.emit('unexpected-response', req, res)) return;
const location = res.headers.location;
const statusCode = res.statusCode;
abortHandshake(this, req, `Unexpected server response: ${res.statusCode}`);
if (
location &&
opts.followRedirects &&
statusCode >= 300 &&
statusCode < 400
) {
if (++websocket._redirects > opts.maxRedirects) {
abortHandshake(websocket, req, 'Maximum redirects exceeded');
return;
}
req.abort();
const addr = url.URL
? new url.URL(location, address)
: url.resolve(address, location);
initAsClient(websocket, addr, protocols, options);
} else if (!websocket.emit('unexpected-response', req, res)) {
abortHandshake(
websocket,
req,
`Unexpected server response: ${res.statusCode}`
);
}
});
req.on('upgrade', (res, socket, head) => {
this.emit('upgrade', res);
websocket.emit('upgrade', res);

@@ -592,13 +634,13 @@ //

//
if (this.readyState !== WebSocket.CONNECTING) return;
if (websocket.readyState !== WebSocket.CONNECTING) return;
req = this._req = null;
req = websocket._req = null;
const digest = crypto
.createHash('sha1')
.update(key + constants.GUID, 'binary')
.update(key + GUID)
.digest('base64');
if (res.headers['sec-websocket-accept'] !== digest) {
abortHandshake(this, socket, 'Invalid Sec-WebSocket-Accept header');
abortHandshake(websocket, socket, 'Invalid Sec-WebSocket-Accept header');
return;

@@ -620,7 +662,7 @@ }

if (protError) {
abortHandshake(this, socket, protError);
abortHandshake(websocket, socket, protError);
return;
}
if (serverProt) this.protocol = serverProt;
if (serverProt) websocket.protocol = serverProt;

@@ -635,6 +677,12 @@ if (perMessageDeflate) {

perMessageDeflate.accept(extensions[PerMessageDeflate.extensionName]);
this._extensions[PerMessageDeflate.extensionName] = perMessageDeflate;
websocket._extensions[
PerMessageDeflate.extensionName
] = perMessageDeflate;
}
} catch (err) {
abortHandshake(this, socket, 'Invalid Sec-WebSocket-Extensions header');
abortHandshake(
websocket,
socket,
'Invalid Sec-WebSocket-Extensions header'
);
return;

@@ -644,3 +692,3 @@ }

this.setSocket(socket, head, options.maxPayload);
websocket.setSocket(socket, head, opts.maxPayload);
});

@@ -748,3 +796,3 @@ }

websocket.readyState = WebSocket.CLOSING;
websocket._closeCode = err[constants.kStatusCode];
websocket._closeCode = err[kStatusCode];
websocket.emit('error', err);

@@ -782,3 +830,3 @@ websocket._socket.destroy();

websocket.pong(data, !websocket._isServer, constants.NOOP);
websocket.pong(data, !websocket._isServer, NOOP);
websocket.emit('ping', data);

@@ -873,3 +921,3 @@ }

this.removeListener('error', socketOnError);
this.on('error', constants.NOOP);
this.on('error', NOOP);

@@ -876,0 +924,0 @@ if (websocket) {

{
"name": "ws",
"version": "6.1.4",
"version": "6.2.0",
"description": "Simple to use, blazing fast and thoroughly tested websocket client and server for Node.js",

@@ -28,3 +28,3 @@ "keywords": [

"integration": "npm run lint && mocha test/*.integration.js",
"lint": "eslint . --ignore-path .gitignore && prettylint '**/*.{json,md}' --ignore-path .gitignore"
"lint": "eslint --ignore-path .gitignore . && prettier --check --ignore-path .gitignore \"**/*.{json,md,yml}\""
},

@@ -37,11 +37,11 @@ "dependencies": {

"bufferutil": "~4.0.0",
"eslint": "~5.14.0",
"eslint-config-prettier": "~4.0.0",
"coveralls": "~3.0.3",
"eslint": "~5.15.0",
"eslint-config-prettier": "~4.1.0",
"eslint-plugin-prettier": "~3.0.0",
"mocha": "~5.2.0",
"mocha": "~6.0.0",
"nyc": "~13.3.0",
"prettier": "~1.16.1",
"prettylint": "~1.0.0",
"utf-8-validate": "~5.0.0"
}
}
# ws: a Node.js WebSocket library
[![Version npm](https://img.shields.io/npm/v/ws.svg)](https://www.npmjs.com/package/ws)
[![Linux Build](https://img.shields.io/travis/websockets/ws/master.svg)](https://travis-ci.org/websockets/ws)
[![Windows Build](https://ci.appveyor.com/api/projects/status/github/websockets/ws?branch=master&svg=true)](https://ci.appveyor.com/project/lpinca/ws)
[![Coverage Status](https://img.shields.io/coveralls/websockets/ws/master.svg)](https://coveralls.io/r/websockets/ws?branch=master)
[![Version npm](https://img.shields.io/npm/v/ws.svg?logo=npm)](https://www.npmjs.com/package/ws)
[![Linux Build](https://img.shields.io/travis/websockets/ws/master.svg?logo=travis)](https://travis-ci.org/websockets/ws)
[![Windows Build](https://img.shields.io/appveyor/ci/lpinca/ws/master.svg?logo=appveyor)](https://ci.appveyor.com/project/lpinca/ws)
[![Coverage Status](https://img.shields.io/coveralls/websockets/ws/master.svg)](https://coveralls.io/github/websockets/ws)

@@ -8,0 +8,0 @@ ws is a simple to use, blazing fast, and thoroughly tested WebSocket client and

SocketSocket SOC 2 Logo

Product

  • Package Alerts
  • Integrations
  • Docs
  • Pricing
  • FAQ
  • Roadmap
  • Changelog

Packages

npm

Stay in touch

Get open source security insights delivered straight into your inbox.


  • Terms
  • Privacy
  • Security

Made with ⚡️ by Socket Inc