New Case Study:See how Anthropic automated 95% of dependency reviews with Socket.Learn More
Socket
Sign inDemoInstall
Socket

websocket-as-promised

Package Overview
Dependencies
Maintainers
1
Versions
30
Alerts
File Explorer

Advanced tools

Socket logo

Install Socket

Detect and block malicious and high-risk dependencies

Install

websocket-as-promised - npm Package Compare versions

Comparing version 0.1.3 to 0.1.4

95

lib/index.js

@@ -13,2 +13,3 @@ /**

var Channel = require('chnl');
var Pendings = require('pendings');

@@ -19,2 +20,8 @@

var DEFAULT_OPTIONS = {
idProp: 'id',
timeout: 0,
WebSocket: typeof WebSocket !== 'undefined' ? WebSocket : null
};
module.exports = function () {

@@ -26,2 +33,3 @@ /**

* @param {String} [options.idProp="id"] id property name attached to each message
* @param {Object} [options.timeout=0] default timeout for requests
* @param {Object} [options.WebSocket=WebSocket] custom WebSocket constructor

@@ -32,6 +40,5 @@ */

options = options || {};
this._idProp = options.idProp || 'id';
this._WebSocket = options.WebSocket || WebSocket;
this._pendings = new Pendings();
this._options = Object.assign({}, DEFAULT_OPTIONS, options);
this._pendings = new Pendings({ timeout: this._options.timeout });
this._onMessage = new Channel();
this._ws = null;

@@ -61,14 +68,14 @@ }

return this._pendings.set(OPENING_ID, function () {
_this._ws = new _this._WebSocket(url);
_this._ws = new _this._options.WebSocket(url);
_this._ws.addEventListener('open', function (event) {
return _this._onOpen(event);
return _this._handleOpen(event);
});
_this._ws.addEventListener('message', function (event) {
return _this._onMessage(event);
return _this._handleMessage(event);
});
_this._ws.addEventListener('error', function (event) {
return _this._onError(event);
return _this._handleError(event);
});
_this._ws.addEventListener('close', function (event) {
return _this._onClose(event);
return _this._handleClose(event);
});

@@ -79,5 +86,7 @@ });

/**
* Send data and wait for response containing `id` property
* Performs request and resolves after response with corresponding `id`.
*
* @param {Object} data
* @param {Object} [options]
* @param {Number} [options.timeout]
* @returns {Promise}

@@ -87,17 +96,16 @@ */

}, {
key: 'send',
value: function send(data) {
key: 'request',
value: function request(data, options) {
var _this2 = this;
return this._pendings.add(function (id) {
if (!data || (typeof data === 'undefined' ? 'undefined' : _typeof(data)) !== 'object') {
throw new Error('WebSocket data should be a plain object, got ' + data);
}
if (data[_this2._idProp] !== undefined) {
throw new Error('WebSocket data should not contain system property: ' + _this2._idProp);
}
data[_this2._idProp] = id;
if (!data || (typeof data === 'undefined' ? 'undefined' : _typeof(data)) !== 'object') {
return Promise.reject(new Error('WebSocket data should be a plain object, got ' + data));
}
var idProp = this._options.idProp;
var fn = function fn(id) {
data[idProp] = id;
var dataStr = JSON.stringify(data);
_this2._ws.send(dataStr);
});
};
return data[idProp] === undefined ? this._pendings.add(fn, options) : this._pendings.set(data[idProp], fn, options);
}

@@ -121,34 +129,30 @@

}, {
key: '_onOpen',
value: function _onOpen(event) {
key: '_handleOpen',
value: function _handleOpen(event) {
this._pendings.resolve(OPENING_ID, event);
}
}, {
key: '_onMessage',
value: function _onMessage(event) {
key: '_handleMessage',
value: function _handleMessage(event) {
if (event.data) {
var data = JSON.parse(event.data);
var id = data && data[this._idProp];
var id = data && data[this._options.idProp];
if (id) {
this._pendings.resolve(id, data);
}
this._onMessage.dispatch(data);
}
}
}, {
key: '_onError',
value: function _onError(event) {
if (this._pendings.has(OPENING_ID)) {
this._pendings.reject(OPENING_ID, event);
}
if (this._pendings.has(CLOSING_ID)) {
this._pendings.reject(CLOSING_ID, event);
}
key: '_handleError',
value: function _handleError(event) {
this._pendings.reject(OPENING_ID, event);
this._pendings.reject(CLOSING_ID, event);
}
}, {
key: '_onClose',
value: function _onClose(event) {
key: '_handleClose',
value: function _handleClose(event) {
this._ws = null;
if (this._pendings.has(CLOSING_ID)) {
this._pendings.resolve(CLOSING_ID, event);
}
this._pendings.resolve(CLOSING_ID, event);
this._pendings.rejectAll(new Error('Connection closed.'));
}

@@ -160,2 +164,15 @@ }, {

}
/**
* OnMessage channel with `.addListener` / `.removeListener` methods.
* @see https://github.com/vitalets/chnl
*
* @returns {Channel}
*/
}, {
key: 'onMessage',
get: function get() {
return this._onMessage;
}
}]);

@@ -162,0 +179,0 @@

{
"name": "websocket-as-promised",
"version": "0.1.3",
"version": "0.1.4",
"description": "Promise-based WebSocket wrapper",

@@ -39,3 +39,4 @@ "author": {

"dependencies": {
"pendings": "^0.1.4"
"chnl": "^0.2.5",
"pendings": "^0.1.5"
},

@@ -42,0 +43,0 @@ "devDependencies": {

@@ -49,2 +49,3 @@ # websocket-as-promised

* @param {String} [options.idProp="id"] id property name attached to each message
* @param {Object} [options.timeout=0] default timeout for requests
* @param {Object} [options.WebSocket=WebSocket] custom WebSocket constructor

@@ -62,8 +63,10 @@ */

```
#### .send(data)
#### .request(data, options)
```
/**
* Send data and wait for response containing `id` property
* Performs request and resolves after response with corresponding `id`.
*
* @param {Object} data
* @param {Object} [options]
* @param {Number} [options.timeout]
* @returns {Promise}

@@ -81,2 +84,12 @@ */

#### .onMessage
```
/**
* OnMessage channel with `.addListener` / `.removeListener` methods.
* @see https://github.com/vitalets/chnl
*
* @returns {Channel}
*/
```
#### .ws

@@ -83,0 +96,0 @@ ```

@@ -7,2 +7,3 @@ /**

const Channel = require('chnl');
const Pendings = require('pendings');

@@ -13,2 +14,8 @@

const DEFAULT_OPTIONS = {
idProp: 'id',
timeout: 0,
WebSocket: typeof WebSocket !== 'undefined' ? WebSocket : null,
};
module.exports = class WebSocketAsPromised {

@@ -20,9 +27,9 @@ /**

* @param {String} [options.idProp="id"] id property name attached to each message
* @param {Object} [options.timeout=0] default timeout for requests
* @param {Object} [options.WebSocket=WebSocket] custom WebSocket constructor
*/
constructor(options) {
options = options || {};
this._idProp = options.idProp || 'id';
this._WebSocket = options.WebSocket || WebSocket;
this._pendings = new Pendings();
this._options = Object.assign({}, DEFAULT_OPTIONS, options);
this._pendings = new Pendings({timeout: this._options.timeout});
this._onMessage = new Channel();
this._ws = null;

@@ -41,2 +48,12 @@ }

/**
* OnMessage channel with `.addListener` / `.removeListener` methods.
* @see https://github.com/vitalets/chnl
*
* @returns {Channel}
*/
get onMessage() {
return this._onMessage;
}
/**
* Open WebSocket connection

@@ -49,7 +66,7 @@ *

return this._pendings.set(OPENING_ID, () => {
this._ws = new this._WebSocket(url);
this._ws.addEventListener('open', event => this._onOpen(event));
this._ws.addEventListener('message', event => this._onMessage(event));
this._ws.addEventListener('error', event => this._onError(event));
this._ws.addEventListener('close', event => this._onClose(event));
this._ws = new this._options.WebSocket(url);
this._ws.addEventListener('open', event => this._handleOpen(event));
this._ws.addEventListener('message', event => this._handleMessage(event));
this._ws.addEventListener('error', event => this._handleError(event));
this._ws.addEventListener('close', event => this._handleClose(event));
});

@@ -59,19 +76,22 @@ }

/**
* Send data and wait for response containing `id` property
* Performs request and resolves after response with corresponding `id`.
*
* @param {Object} data
* @param {Object} [options]
* @param {Number} [options.timeout]
* @returns {Promise}
*/
send(data) {
return this._pendings.add(id => {
if (!data || typeof data !== 'object') {
throw new Error(`WebSocket data should be a plain object, got ${data}`);
}
if (data[this._idProp] !== undefined) {
throw new Error(`WebSocket data should not contain system property: ${this._idProp}`);
}
data[this._idProp] = id;
request(data, options) {
if (!data || typeof data !== 'object') {
return Promise.reject(new Error(`WebSocket data should be a plain object, got ${data}`));
}
const idProp = this._options.idProp;
const fn = id => {
data[idProp] = id;
const dataStr = JSON.stringify(data);
this._ws.send(dataStr);
});
};
return data[idProp] === undefined
? this._pendings.add(fn, options)
: this._pendings.set(data[idProp], fn, options);
}

@@ -88,31 +108,27 @@

_onOpen(event) {
_handleOpen(event) {
this._pendings.resolve(OPENING_ID, event);
}
_onMessage(event) {
_handleMessage(event) {
if (event.data) {
const data = JSON.parse(event.data);
const id = data && data[this._idProp];
const id = data && data[this._options.idProp];
if (id) {
this._pendings.resolve(id, data);
}
this._onMessage.dispatch(data);
}
}
_onError(event) {
if (this._pendings.has(OPENING_ID)) {
this._pendings.reject(OPENING_ID, event);
}
if (this._pendings.has(CLOSING_ID)) {
this._pendings.reject(CLOSING_ID, event);
}
_handleError(event) {
this._pendings.reject(OPENING_ID, event);
this._pendings.reject(CLOSING_ID, event);
}
_onClose(event) {
_handleClose(event) {
this._ws = null;
if (this._pendings.has(CLOSING_ID)) {
this._pendings.resolve(CLOSING_ID, event);
}
this._pendings.resolve(CLOSING_ID, event);
this._pendings.rejectAll(new Error('Connection closed.'));
}
};

@@ -12,2 +12,6 @@ 'use strict';

function sleep(ms) {
return new Promise(resolve => setTimeout(resolve, ms));
}
describe('WebSocketAsPromised', function () {

@@ -37,13 +41,27 @@

it('should open connection', function () {
return this.wsp.open(this.url)
.then(event => assert.equal(event.type, 'open'));
const res = this.wsp.open(this.url);
return assert.eventually.propertyVal(res, 'type', 'open');
});
it('should send and receive data with id', function () {
return this.wsp.open(this.url)
.then(() => this.wsp.send({foo: 'bar'}))
.then(data => {
assert.equal(data.foo, 'bar');
assert.property(data, 'id');
it('should request and resolve with generated id', function () {
const res = this.wsp.open(this.url).then(() => this.wsp.request({foo: 'bar'}));
return Promise.all([
assert.eventually.propertyVal(res, 'foo', 'bar'),
assert.eventually.property(res, 'id')
]);
});
it('should request and resolve with specified id', function () {
const res = this.wsp.open(this.url).then(() => this.wsp.request({foo: 'bar', id: 1}));
return assert.eventually.propertyVal(res, 'id', 1);
});
it('should not resolve/reject for response without ID', function () {
let a = 0;
const res = this.wsp.open(this.url)
.then(() => {
this.wsp.request({noId: true}).then(() => a = a + 1, () => {});
return sleep(100).then(() => a);
});
return assert.eventually.equal(res, 0);
});

@@ -53,8 +71,17 @@

const CLOSE_NORMAL = 1000;
return this.wsp.open(this.url)
.then(() => this.wsp.close())
.then(event => assert.equal(event.code, CLOSE_NORMAL));
const res = this.wsp.open(this.url).then(() => this.wsp.close());
return assert.eventually.propertyVal(res, 'code', CLOSE_NORMAL);
});
it('should reject for invalid url', function () {
it('should reject all pending requests on close', function () {
let a = '';
const res = this.wsp.open(this.url)
.then(() => {
this.wsp.request({noId: true}).catch(e => a = e.message);
return sleep(10).then(() => this.wsp.close()).then(() => a);
});
return assert.eventually.equal(res, 'Connection closed.');
});
it('should reject connection for invalid url', function () {
const res = this.wsp.open('abc');

@@ -65,10 +92,40 @@ return assert.isRejected(res, 'You must specify a full WebSocket URL, including protocol.');

it('should customize idProp', function () {
this.wsp = new WebSocketAsPromised({WebSocket: W3CWebSocket, idProp: 'myId'});
return this.wsp.open(this.url)
.then(() => this.wsp.send({foo: 'bar'}))
.then(data => {
assert.equal(data.foo, 'bar');
assert.property(data, 'myId');
});
const wsp = new WebSocketAsPromised({WebSocket: W3CWebSocket, idProp: 'myId'});
const res = wsp.open(this.url).then(() => wsp.request({foo: 'bar'}));
return Promise.all([
assert.eventually.propertyVal(res, 'foo', 'bar'),
assert.eventually.property(res, 'myId'),
]);
});
it('should dispatch data via onMessage channel', function () {
const wsp = new WebSocketAsPromised({WebSocket: W3CWebSocket});
const res = new Promise(resolve => {
wsp.onMessage.addListener(resolve);
wsp.open(this.url).then(() => wsp.request({foo: 'bar'}));
});
return assert.eventually.propertyVal(res, 'foo', 'bar');
});
describe('timeout', function () {
it('should reject request after timeout', function () {
const wsp = new WebSocketAsPromised({WebSocket: W3CWebSocket, timeout: 50});
const res = wsp.open(this.url).then(() => wsp.request({foo: 'bar', delay: 100}));
return assert.isRejected(res, 'Promise rejected by timeout (50 ms)');
});
it('should resolve request before timeout', function () {
const wsp = new WebSocketAsPromised({WebSocket: W3CWebSocket, timeout: 100});
const res = wsp.open(this.url).then(() => wsp.request({foo: 'bar', delay: 50}));
return assert.eventually.propertyVal(res, 'foo', 'bar');
});
it('should reject request after custom timeout', function () {
const wsp = new WebSocketAsPromised({WebSocket: W3CWebSocket, timeout: 100});
const options = {timeout: 50};
const res = wsp.open(this.url).then(() => wsp.request({foo: 'bar', delay: 70}, options));
return assert.isRejected(res, 'Promise rejected by timeout (50 ms)');
});
});
});

@@ -24,8 +24,3 @@ 'use strict';

if (message.type === 'utf8') {
const data = JSON.parse(message.utf8Data);
if (data.error) {
// todo: trigger error
} else {
connection.sendUTF(message.utf8Data);
}
handleUTF8Message(connection, message);
} else if (message.type === 'binary') {

@@ -43,2 +38,16 @@ connection.sendBytes(message.binaryData);

});
function handleUTF8Message(connection, message) {
const data = JSON.parse(message.utf8Data);
if (data.nonJSONResponse) {
connection.sendUTF('non JSON response');
} else if (data.noId) {
delete data.id;
connection.sendUTF(JSON.stringify(data));
} else if (data.delay) {
setTimeout(() => connection.sendUTF(message.utf8Data), data.delay);
} else {
connection.sendUTF(message.utf8Data);
}
}
};

@@ -45,0 +54,0 @@

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