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 2.3.1 to 3.0.0

8

lib/EventTarget.js

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

* @param {(String|Buffer|ArrayBuffer|Buffer[])} data The received data
* @param {Boolean} isBinary Specifies if `data` is binary
* @param {WebSocket} target A reference to the target to which the event was dispatched
*/
constructor (data, isBinary, target) {
constructor (data, target) {
super('message', target);
this.binary = isBinary; // non-standard.
this.data = data;

@@ -103,4 +101,4 @@ }

function onMessage (data, flags) {
listener.call(this, new MessageEvent(data, !!flags.binary, this));
function onMessage (data) {
listener.call(this, new MessageEvent(data, this));
}

@@ -107,0 +105,0 @@

@@ -21,9 +21,12 @@ 'use strict';

constructor (options, isServer, maxPayload) {
this._maxPayload = maxPayload | 0;
this._options = options || {};
this._threshold = this._options.threshold !== undefined
? this._options.threshold
: 1024;
this._isServer = !!isServer;
this._deflate = null;
this._inflate = null;
this._deflate = null;
this.params = null;
this._maxPayload = maxPayload || 0;
this.threshold = this._options.threshold === undefined ? 1024 : this._options.threshold;
}

@@ -30,0 +33,0 @@

@@ -38,25 +38,25 @@ /*!

constructor (extensions, maxPayload, binaryType) {
this.binaryType = binaryType || constants.BINARY_TYPES[0];
this.extensions = extensions || {};
this.maxPayload = maxPayload | 0;
this._binaryType = binaryType || constants.BINARY_TYPES[0];
this._extensions = extensions || {};
this._maxPayload = maxPayload | 0;
this.bufferedBytes = 0;
this.buffers = [];
this._bufferedBytes = 0;
this._buffers = [];
this.compressed = false;
this.payloadLength = 0;
this.fragmented = 0;
this.masked = false;
this.fin = false;
this.mask = null;
this.opcode = 0;
this._compressed = false;
this._payloadLength = 0;
this._fragmented = 0;
this._masked = false;
this._fin = false;
this._mask = null;
this._opcode = 0;
this.totalPayloadLength = 0;
this.messageLength = 0;
this.fragments = [];
this._totalPayloadLength = 0;
this._messageLength = 0;
this._fragments = [];
this.cleanupCallback = null;
this.hadError = false;
this.dead = false;
this.loop = false;
this._cleanupCallback = null;
this._hadError = false;
this._dead = false;
this._loop = false;

@@ -69,3 +69,3 @@ this.onmessage = null;

this.state = GET_INFO;
this._state = GET_INFO;
}

@@ -85,9 +85,9 @@

this.bufferedBytes -= bytes;
this._bufferedBytes -= bytes;
if (bytes === this.buffers[0].length) return this.buffers.shift();
if (bytes === this._buffers[0].length) return this._buffers.shift();
if (bytes < this.buffers[0].length) {
dst = this.buffers[0].slice(0, bytes);
this.buffers[0] = this.buffers[0].slice(bytes);
if (bytes < this._buffers[0].length) {
dst = this._buffers[0].slice(0, bytes);
this._buffers[0] = this._buffers[0].slice(bytes);
return dst;

@@ -99,11 +99,11 @@ }

while (bytes > 0) {
l = this.buffers[0].length;
l = this._buffers[0].length;
if (bytes >= l) {
this.buffers[0].copy(dst, offset);
this._buffers[0].copy(dst, offset);
offset += l;
this.buffers.shift();
this._buffers.shift();
} else {
this.buffers[0].copy(dst, offset, 0, bytes);
this.buffers[0] = this.buffers[0].slice(bytes);
this._buffers[0].copy(dst, offset, 0, bytes);
this._buffers[0] = this._buffers[0].slice(bytes);
}

@@ -126,6 +126,6 @@

hasBufferedBytes (n) {
if (this.bufferedBytes >= n) return true;
if (this._bufferedBytes >= n) return true;
this.loop = false;
if (this.dead) this.cleanup(this.cleanupCallback);
this._loop = false;
if (this._dead) this.cleanup(this._cleanupCallback);
return false;

@@ -140,6 +140,6 @@ }

add (data) {
if (this.dead) return;
if (this._dead) return;
this.bufferedBytes += data.length;
this.buffers.push(data);
this._bufferedBytes += data.length;
this._buffers.push(data);
this.startLoop();

@@ -154,6 +154,6 @@ }

startLoop () {
this.loop = true;
this._loop = true;
while (this.loop) {
switch (this.state) {
while (this._loop) {
switch (this._state) {
case GET_INFO:

@@ -175,3 +175,3 @@ this.getInfo();

default: // `INFLATING`
this.loop = false;
this._loop = false;
}

@@ -198,3 +198,3 @@ }

if (compressed && !this.extensions[PerMessageDeflate.extensionName]) {
if (compressed && !this._extensions[PerMessageDeflate.extensionName]) {
this.error(new Error('RSV1 must be clear'), 1002);

@@ -204,7 +204,7 @@ return;

this.fin = (buf[0] & 0x80) === 0x80;
this.opcode = buf[0] & 0x0f;
this.payloadLength = buf[1] & 0x7f;
this._fin = (buf[0] & 0x80) === 0x80;
this._opcode = buf[0] & 0x0f;
this._payloadLength = buf[1] & 0x7f;
if (this.opcode === 0x00) {
if (this._opcode === 0x00) {
if (compressed) {

@@ -215,17 +215,17 @@ this.error(new Error('RSV1 must be clear'), 1002);

if (!this.fragmented) {
this.error(new Error(`invalid opcode: ${this.opcode}`), 1002);
if (!this._fragmented) {
this.error(new Error(`invalid opcode: ${this._opcode}`), 1002);
return;
} else {
this.opcode = this.fragmented;
this._opcode = this._fragmented;
}
} else if (this.opcode === 0x01 || this.opcode === 0x02) {
if (this.fragmented) {
this.error(new Error(`invalid opcode: ${this.opcode}`), 1002);
} else if (this._opcode === 0x01 || this._opcode === 0x02) {
if (this._fragmented) {
this.error(new Error(`invalid opcode: ${this._opcode}`), 1002);
return;
}
this.compressed = compressed;
} else if (this.opcode > 0x07 && this.opcode < 0x0b) {
if (!this.fin) {
this._compressed = compressed;
} else if (this._opcode > 0x07 && this._opcode < 0x0b) {
if (!this._fin) {
this.error(new Error('FIN must be set'), 1002);

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

if (this.payloadLength > 0x7d) {
if (this._payloadLength > 0x7d) {
this.error(new Error('invalid payload length'), 1002);

@@ -246,12 +246,12 @@ return;

} else {
this.error(new Error(`invalid opcode: ${this.opcode}`), 1002);
this.error(new Error(`invalid opcode: ${this._opcode}`), 1002);
return;
}
if (!this.fin && !this.fragmented) this.fragmented = this.opcode;
if (!this._fin && !this._fragmented) this._fragmented = this._opcode;
this.masked = (buf[1] & 0x80) === 0x80;
this._masked = (buf[1] & 0x80) === 0x80;
if (this.payloadLength === 126) this.state = GET_PAYLOAD_LENGTH_16;
else if (this.payloadLength === 127) this.state = GET_PAYLOAD_LENGTH_64;
if (this._payloadLength === 126) this._state = GET_PAYLOAD_LENGTH_16;
else if (this._payloadLength === 127) this._state = GET_PAYLOAD_LENGTH_64;
else this.haveLength();

@@ -268,3 +268,3 @@ }

this.payloadLength = this.readBuffer(2).readUInt16BE(0, true);
this._payloadLength = this.readBuffer(2).readUInt16BE(0, true);
this.haveLength();

@@ -293,3 +293,3 @@ }

this.payloadLength = (num * Math.pow(2, 32)) + buf.readUInt32BE(4, true);
this._payloadLength = (num * Math.pow(2, 32)) + buf.readUInt32BE(4, true);
this.haveLength();

@@ -304,8 +304,8 @@ }

haveLength () {
if (this.opcode < 0x08 && this.maxPayloadExceeded(this.payloadLength)) {
if (this._opcode < 0x08 && this.maxPayloadExceeded(this._payloadLength)) {
return;
}
if (this.masked) this.state = GET_MASK;
else this.state = GET_DATA;
if (this._masked) this._state = GET_MASK;
else this._state = GET_DATA;
}

@@ -321,4 +321,4 @@

this.mask = this.readBuffer(4);
this.state = GET_DATA;
this._mask = this.readBuffer(4);
this._state = GET_DATA;
}

@@ -334,13 +334,13 @@

if (this.payloadLength) {
if (!this.hasBufferedBytes(this.payloadLength)) return;
if (this._payloadLength) {
if (!this.hasBufferedBytes(this._payloadLength)) return;
data = this.readBuffer(this.payloadLength);
if (this.masked) bufferUtil.unmask(data, this.mask);
data = this.readBuffer(this._payloadLength);
if (this._masked) bufferUtil.unmask(data, this._mask);
}
if (this.opcode > 0x07) {
if (this._opcode > 0x07) {
this.controlMessage(data);
} else if (this.compressed) {
this.state = INFLATING;
} else if (this._compressed) {
this._state = INFLATING;
this.decompress(data);

@@ -359,5 +359,5 @@ } else if (this.pushFragment(data)) {

decompress (data) {
const extension = this.extensions[PerMessageDeflate.extensionName];
const perMessageDeflate = this._extensions[PerMessageDeflate.extensionName];
extension.decompress(data, this.fin, (err, buf) => {
perMessageDeflate.decompress(data, this._fin, (err, buf) => {
if (err) {

@@ -379,17 +379,17 @@ this.error(err, err.closeCode === 1009 ? 1009 : 1007);

dataMessage () {
if (this.fin) {
const messageLength = this.messageLength;
const fragments = this.fragments;
if (this._fin) {
const messageLength = this._messageLength;
const fragments = this._fragments;
this.totalPayloadLength = 0;
this.messageLength = 0;
this.fragmented = 0;
this.fragments = [];
this._totalPayloadLength = 0;
this._messageLength = 0;
this._fragmented = 0;
this._fragments = [];
if (this.opcode === 2) {
if (this._opcode === 2) {
var data;
if (this.binaryType === 'nodebuffer') {
if (this._binaryType === 'nodebuffer') {
data = toBuffer(fragments, messageLength);
} else if (this.binaryType === 'arraybuffer') {
} else if (this._binaryType === 'arraybuffer') {
data = toArrayBuffer(toBuffer(fragments, messageLength));

@@ -400,3 +400,3 @@ } else {

this.onmessage(data, { masked: this.masked, binary: true });
this.onmessage(data);
} else {

@@ -410,7 +410,7 @@ const buf = toBuffer(fragments, messageLength);

this.onmessage(buf.toString(), { masked: this.masked });
this.onmessage(buf.toString());
}
}
this.state = GET_INFO;
this._state = GET_INFO;
}

@@ -425,7 +425,7 @@

controlMessage (data) {
if (this.opcode === 0x08) {
if (this._opcode === 0x08) {
if (data.length === 0) {
this.onclose(1000, '', { masked: this.masked });
this.loop = false;
this.cleanup(this.cleanupCallback);
this.onclose(1000, '');
this._loop = false;
this.cleanup(this._cleanupCallback);
} else if (data.length === 1) {

@@ -448,5 +448,5 @@ this.error(new Error('invalid payload length'), 1002);

this.onclose(code, buf.toString(), { masked: this.masked });
this.loop = false;
this.cleanup(this.cleanupCallback);
this.onclose(code, buf.toString());
this._loop = false;
this.cleanup(this._cleanupCallback);
}

@@ -457,8 +457,6 @@

const flags = { masked: this.masked, binary: true };
if (this._opcode === 0x09) this.onping(data);
else this.onpong(data);
if (this.opcode === 0x09) this.onping(data, flags);
else this.onpong(data, flags);
this.state = GET_INFO;
this._state = GET_INFO;
}

@@ -475,5 +473,5 @@

this.onerror(err, code);
this.hadError = true;
this.loop = false;
this.cleanup(this.cleanupCallback);
this._hadError = true;
this._loop = false;
this.cleanup(this._cleanupCallback);
}

@@ -488,8 +486,8 @@

maxPayloadExceeded (length) {
if (length === 0 || this.maxPayload < 1) return false;
if (length === 0 || this._maxPayload < 1) return false;
const fullLength = this.totalPayloadLength + length;
const fullLength = this._totalPayloadLength + length;
if (fullLength <= this.maxPayload) {
this.totalPayloadLength = fullLength;
if (fullLength <= this._maxPayload) {
this._totalPayloadLength = fullLength;
return false;

@@ -513,7 +511,7 @@ }

const totalLength = this.messageLength + fragment.length;
const totalLength = this._messageLength + fragment.length;
if (this.maxPayload < 1 || totalLength <= this.maxPayload) {
this.messageLength = totalLength;
this.fragments.push(fragment);
if (this._maxPayload < 1 || totalLength <= this._maxPayload) {
this._messageLength = totalLength;
this._fragments.push(fragment);
return true;

@@ -533,13 +531,13 @@ }

cleanup (cb) {
this.dead = true;
this._dead = true;
if (!this.hadError && (this.loop || this.state === INFLATING)) {
this.cleanupCallback = cb;
if (!this._hadError && (this._loop || this._state === INFLATING)) {
this._cleanupCallback = cb;
} else {
this.extensions = null;
this.fragments = null;
this.buffers = null;
this.mask = null;
this._extensions = null;
this._fragments = null;
this._buffers = null;
this._mask = null;
this.cleanupCallback = null;
this._cleanupCallback = null;
this.onmessage = null;

@@ -546,0 +544,0 @@ this.onclose = null;

@@ -29,11 +29,11 @@ /*!

constructor (socket, extensions) {
this.perMessageDeflate = (extensions || {})[PerMessageDeflate.extensionName];
this._extensions = extensions || {};
this._socket = socket;
this.firstFragment = true;
this.compress = false;
this._firstFragment = true;
this._compress = false;
this.bufferedBytes = 0;
this.deflating = false;
this.queue = [];
this._bufferedBytes = 0;
this._deflating = false;
this._queue = [];

@@ -127,3 +127,3 @@ this.onerror = null;

if (this.deflating) {
if (this._deflating) {
this.enqueue([this.doClose, buf, mask, cb]);

@@ -174,3 +174,3 @@ } else {

if (this.deflating) {
if (this._deflating) {
this.enqueue([this.doPing, data, mask, readOnly]);

@@ -221,3 +221,3 @@ } else {

if (this.deflating) {
if (this._deflating) {
this.enqueue([this.doPong, data, mask, readOnly]);

@@ -275,8 +275,10 @@ } else {

if (this.firstFragment) {
this.firstFragment = false;
if (rsv1 && this.perMessageDeflate) {
rsv1 = data.length >= this.perMessageDeflate.threshold;
const perMessageDeflate = this._extensions[PerMessageDeflate.extensionName];
if (this._firstFragment) {
this._firstFragment = false;
if (rsv1 && perMessageDeflate) {
rsv1 = data.length >= perMessageDeflate._threshold;
}
this.compress = rsv1;
this._compress = rsv1;
} else {

@@ -287,5 +289,5 @@ rsv1 = false;

if (options.fin) this.firstFragment = true;
if (options.fin) this._firstFragment = true;
if (this.perMessageDeflate) {
if (perMessageDeflate) {
const opts = {

@@ -299,6 +301,6 @@ fin: options.fin,

if (this.deflating) {
this.enqueue([this.dispatch, data, this.compress, opts, cb]);
if (this._deflating) {
this.enqueue([this.dispatch, data, this._compress, opts, cb]);
} else {
this.dispatch(data, this.compress, opts, cb);
this.dispatch(data, this._compress, opts, cb);
}

@@ -336,4 +338,6 @@ } else {

this.deflating = true;
this.perMessageDeflate.compress(data, options.fin, (err, buf) => {
const perMessageDeflate = this._extensions[PerMessageDeflate.extensionName];
this._deflating = true;
perMessageDeflate.compress(data, options.fin, (err, buf) => {
if (err) {

@@ -347,3 +351,3 @@ if (cb) cb(err);

this.sendFrame(Sender.frame(buf, options), cb);
this.deflating = false;
this._deflating = false;
this.dequeue();

@@ -359,6 +363,6 @@ });

dequeue () {
while (!this.deflating && this.queue.length) {
const params = this.queue.shift();
while (!this._deflating && this._queue.length) {
const params = this._queue.shift();
this.bufferedBytes -= params[1].length;
this._bufferedBytes -= params[1].length;
params[0].apply(this, params.slice(1));

@@ -375,4 +379,4 @@ }

enqueue (params) {
this.bufferedBytes += params[1].length;
this.queue.push(params);
this._bufferedBytes += params[1].length;
this._queue.push(params);
}

@@ -379,0 +383,0 @@

@@ -68,3 +68,3 @@ /*!

if (Array.isArray(address)) {
initAsServerClient.call(this, address[0], address[1], address[2], options);
initAsServerClient.call(this, address[0], address[1], options);
} else {

@@ -87,3 +87,3 @@ initAsClient.call(this, address, protocols, options);

if (this._socket) {
amount = this._socket.bufferSize + this._sender.bufferedBytes;
amount = this._socket.bufferSize + this._sender._bufferedBytes;
}

@@ -111,3 +111,3 @@ return amount;

//
if (this._receiver) this._receiver.binaryType = type;
if (this._receiver) this._receiver._binaryType = type;
}

@@ -126,3 +126,3 @@

this._receiver = new Receiver(this.extensions, this.maxPayload, this.binaryType);
this._receiver = new Receiver(this.extensions, this._maxPayload, this.binaryType);
this._sender = new Sender(socket, this.extensions);

@@ -150,8 +150,8 @@ this._ultron = new Ultron(socket);

// receiver event handlers
this._receiver.onmessage = (data, flags) => this.emit('message', data, flags);
this._receiver.onping = (data, flags) => {
this._receiver.onmessage = (data) => this.emit('message', data);
this._receiver.onping = (data) => {
this.pong(data, !this._isServer, true);
this.emit('ping', data, flags);
this.emit('ping', data);
};
this._receiver.onpong = (data, flags) => this.emit('pong', data, flags);
this._receiver.onpong = (data) => this.emit('pong', data);
this._receiver.onclose = (code, reason) => {

@@ -463,9 +463,8 @@ this._closeMessage = reason;

*/
function initAsServerClient (req, socket, head, options) {
function initAsServerClient (socket, head, options) {
this.protocolVersion = options.protocolVersion;
this._maxPayload = options.maxPayload;
this.extensions = options.extensions;
this.maxPayload = options.maxPayload;
this.protocol = options.protocol;
this.upgradeReq = req;
this._isServer = true;

@@ -472,0 +471,0 @@

@@ -50,3 +50,3 @@ /*!

maxPayload: 100 * 1024 * 1024,
perMessageDeflate: true,
perMessageDeflate: false,
handleProtocols: null,

@@ -89,4 +89,3 @@ clientTracking: true,

this.handleUpgrade(req, socket, head, (client) => {
this.emit(`connection${req.url}`, client);
this.emit('connection', client);
this.emit('connection', client, req);
});

@@ -98,3 +97,2 @@ });

this.options = options;
this.path = options.path;
}

@@ -262,3 +260,3 @@

const client = new WebSocket([req, socket, head], null, {
const client = new WebSocket([socket, head], null, {
maxPayload: this.options.maxPayload,

@@ -265,0 +263,0 @@ protocolVersion: version,

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

@@ -41,6 +41,6 @@ "keywords": [

"eslint-plugin-standard": "~3.0.0",
"mocha": "~3.2.0",
"nyc": "~10.2.0",
"mocha": "~3.4.1",
"nyc": "~10.3.0",
"utf-8-validate": "~3.0.0"
}
}

@@ -19,2 +19,24 @@ # ws: a Node.js WebSocket library

## Table of Contents
* [Protocol support](#protocol-support)
* [Installing](#installing)
+ [Opt-in for performance and spec compliance](#opt-in-for-performance-and-spec-compliance)
* [API docs](#api-docs)
* [WebSocket compression](#websocket-compression)
* [Usage examples](#usage-examples)
+ [Sending and receiving text data](#sending-and-receiving-text-data)
+ [Sending binary data](#sending-binary-data)
+ [Server example](#server-example)
+ [Broadcast example](#broadcast-example)
+ [ExpressJS example](#expressjs-example)
+ [echo.websocket.org demo](#echowebsocketorg-demo)
+ [Other examples](#other-examples)
* [Error handling best practices](#error-handling-best-practices)
* [FAQ](#faq)
+ [How to get the IP address of the client?](#how-to-get-the-ip-address-of-the-client)
+ [How to detect and close broken connections?](#how-to-detect-and-close-broken-connections)
* [Changelog](#changelog)
* [License](#license)
## Protocol support

@@ -44,6 +66,5 @@

## API Docs
## API docs
See [`/doc/ws.md`](https://github.com/websockets/ws/blob/master/doc/ws.md)
for Node.js-like docs for the ws classes.
See [`/doc/ws.md`](./doc/ws.md) for Node.js-like docs for the ws classes.

@@ -57,8 +78,9 @@ ## WebSocket compression

The extension is enabled by default but adds a significant overhead in terms of
performance and memory comsumption. We suggest to use WebSocket compression
only if it is really needed.
The extension is disabled by default on the server and enabled by default on
the client. It adds a significant overhead in terms of performance and memory
comsumption so we suggest to enable it only if it is really needed.
To disable the extension you can set the `perMessageDeflate` option to `false`.
On the server:
The client will only use the extension if it is supported and enabled on the
server. To always disable the extension on the client set the
`perMessageDeflate` option to `false`.

@@ -68,13 +90,2 @@ ```js

const wss = new WebSocket.Server({
perMessageDeflate: false,
port: 8080
});
```
On the client:
```js
const WebSocket = require('ws');
const ws = new WebSocket('ws://www.host.com/path', {

@@ -98,5 +109,4 @@ perMessageDeflate: false

ws.on('message', function incoming(data, flags) {
// flags.binary will be set if a binary data is received.
// flags.masked will be set if the data was masked.
ws.on('message', function incoming(data) {
console.log(data);
});

@@ -184,6 +194,6 @@ ```

wss.on('connection', function connection(ws) {
const location = url.parse(ws.upgradeReq.url, true);
wss.on('connection', function connection(ws, req) {
const location = url.parse(req.url, true);
// You might use location.query.access_token to authenticate or share sessions
// or ws.upgradeReq.headers.cookie (see http://stackoverflow.com/a/16395220/151312)
// or req.headers.cookie (see http://stackoverflow.com/a/16395220/151312)

@@ -220,4 +230,4 @@ ws.on('message', function incoming(message) {

ws.on('message', function incoming(data, flags) {
console.log(`Roundtrip time: ${Date.now() - data} ms`, flags);
ws.on('message', function incoming(data) {
console.log(`Roundtrip time: ${Date.now() - data} ms`);

@@ -258,6 +268,66 @@ setTimeout(function timeout() {

## FAQ
### How to get the IP address of the client?
The remote IP address can be obtained from the raw socket.
```js
const WebSocket = require('ws');
const wss = new WebSocket.Server({ port: 8080 });
wss.on('connection', function connection(ws, req) {
const ip = req.connection.remoteAddress;
});
```
When the server runs behing a proxy like NGINX, the de-facto standard is to use
the `X-Forwarded-For` header.
```js
wss.on('connection', function connection(ws, req) {
const ip = req.headers['x-forwarded-for'];
});
```
### How to detect and close broken connections?
Sometimes the link between the server and the client can be interrupted in a
way that keeps both the server and the client unware of the broken state of the
connection (e.g. when pulling the cord).
In these cases ping messages can be used as a means to verify that the remote
endpoint is still responsive.
```js
const WebSocket = require('ws');
const wss = new WebSocket.Server({ port: 8080 });
function heartbeat() {
this.isAlive = true;
}
wss.on('connection', function connection(ws) {
ws.isAlive = true;
ws.on('pong', heartbeat);
});
const interval = setInterval(function ping() {
wss.clients.forEach(function each(ws) {
if (ws.isAlive === false) return ws.terminate();
ws.isAlive = false;
ws.ping('', false, true);
});
}, 30000);
```
Pong messages are automatically sent in reponse to ping messages as required
by the spec.
## Changelog
We're using the GitHub [`releases`](https://github.com/websockets/ws/releases)
for changelog entries.
We're using the GitHub [releases][changelog] for changelog entries.

@@ -269,1 +339,2 @@ ## License

[permessage-deflate]: https://tools.ietf.org/html/rfc7692
[changelog]: https://github.com/websockets/ws/releases
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