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

@ch1/rpc-web-socket

Package Overview
Dependencies
Maintainers
1
Versions
3
Alerts
File Explorer

Advanced tools

Socket logo

Install Socket

Detect and block malicious and high-risk dependencies

Install

@ch1/rpc-web-socket - npm Package Compare versions

Comparing version 1.1.0 to 1.2.0

11

CHANGELOG.md
# CHANGE LOG
## 1.2.0
- Rejects function back log on error (server)
- Rejects function back log on error (client)
- Rejects function back log on disconnect (server)
- Rejects function back log on disconnect (client)
- Destroys on error (client)
- Destroys on disconnect (client)
- ping/pong keep alive (server)
- adds code coverage
## 1.1.0
- commonJS exports now supported

145

dist/index.cjs.js

@@ -17,2 +17,20 @@ 'use strict';

*/
function configureNativeSocketOnMethod(config) {
config.on = (callback) => {
const handler = (data) => {
callback(JSON.parse(data.data));
};
config.socket.addEventListener('message', handler);
return () => config.socket.removeEventListener('message', handler);
};
}
function configureWsSocketOnMethod(config) {
config.on = (callback) => {
const handler = (data) => {
callback(JSON.parse(data));
};
config.socket.on('message', handler);
return () => config.socket.removeEventListener('message', handler);
};
}
function configureOnEmit(config) {

@@ -26,18 +44,6 @@ if (!config.socket) {

}
config.on = (callback) => {
const handler = (data) => {
callback(JSON.parse(data.data));
};
config.socket.addEventListener('message', handler);
return () => config.socket.removeEventListener('message', handler);
};
configureNativeSocketOnMethod(config);
}
else {
config.on = (callback) => {
const handler = (data) => {
callback(JSON.parse(data));
};
config.socket.on('message', handler);
return () => config.socket.removeEventListener('message', handler);
};
configureWsSocketOnMethod(config);
}

@@ -51,28 +57,95 @@ if (typeof config.socket.send !== 'function') {

}
function create(config = {}, remote, remoteDesc) {
function configureNativeSocket(config, remote, remoteDesc) {
let resolve;
let reject;
let rpc$$1;
const earlyDestroys = [];
const expose = {
onDestroy: (...args) => {
let destroyed = false;
const destroy = (reason) => {
destroyed = true;
desc.onDestroy(reason);
};
const desc = {
isValid: () => {
if (destroyed) {
return false;
}
else {
return true;
}
},
args,
onDestroy: reason => { },
};
earlyDestroys.push(desc);
return destroy;
},
ready: new Promise((res, rej) => {
resolve = res;
reject = rej;
}),
};
// @todo awkard... this will need a refactor
config.socket.addEventListener('close', () => {
if (rpc$$1) {
rpc$$1.destroy('rpc: web-socket closed');
}
else {
console.warn('rpc: web-socket error: rpc still not defined (close)');
}
});
// @todo awkard... this will need a refactor
config.socket.addEventListener('error', error => {
if (rpc$$1) {
rpc$$1.destroy('rpc: web-socket error: native: ' + error.message);
}
else {
console.warn('rpc: web-socket error: rpc still not defined (error)');
}
});
config.socket.addEventListener('open', () => {
rpc$$1 = rpc.create(config, remote, remoteDesc);
earlyDestroys.forEach(desc => {
if (desc.isValid()) {
desc.onDestroy = rpc$$1.onDestroy(...desc.args);
}
});
rpc$$1.ready
.then(() => {
Object.keys(rpc$$1).forEach(key => {
expose[key] = rpc$$1[key];
});
resolve();
})
.catch(reject);
});
return expose;
}
function configureWsSocket(config, remote, remoteDesc) {
const rpc$$1 = rpc.create(config, remote, remoteDesc);
const interval = setInterval(() => {
if (config.socket.isAlive === false) {
config.socket.terminate();
}
config.socket.isAlive = false;
config.socket.ping();
}, config.pingDelay || 10000);
config.socket.isAlive = true;
config.socket.on('pong', () => (config.socket.isAlive = true));
const destroy = rpc$$1.destroy;
rpc$$1.destroy = () => {
clearInterval(interval);
return destroy();
};
return rpc$$1;
}
function create(config, remote, remoteDesc) {
configureOnEmit(config);
if (typeof WebSocket !== 'undefined') {
let resolve;
let reject;
const expose = {
ready: new Promise((res, rej) => {
resolve = res;
reject = rej;
}),
};
config.socket.addEventListener('open', () => {
const rpc$$1 = rpc.create(config, remote, remoteDesc);
rpc$$1.ready
.then(() => {
Object.keys(rpc$$1).forEach(key => {
expose[key] = rpc$$1[key];
});
resolve();
})
.catch(reject);
});
return expose;
return configureNativeSocket(config, remote, remoteDesc);
}
else {
return rpc.create(config, remote, remoteDesc);
return configureWsSocket(config, remote, remoteDesc);
}

@@ -79,0 +152,0 @@ }

@@ -12,6 +12,12 @@ /**

import { Remote, RemoteDesc, RPCAbstractConfig } from '@ch1/rpc';
export declare type WebSocketType = {
on: (message: string, callback: (data: string) => void) => void;
send: (data: string) => void;
export declare type NativeWebSocket = {
addEventListener: (message: string, handler: (result: {
data: any;
}) => any) => any;
send: (data: string) => any;
};
export declare type WsWebSocket = {
on: (message: string, callback: (data: any) => any) => any;
send: (data: string) => any;
};
/**

@@ -21,9 +27,8 @@ * Socket RPC Config

export interface RPCSocketConfig extends RPCAbstractConfig {
socket?: WebSocketType | {
addEventListener: (message: string, callback: (data: any) => void) => void;
send: (data: any) => void;
};
pingDelay?: number;
socket: NativeWebSocket | WsWebSocket;
}
export declare function create<T>(config?: RPCSocketConfig, remote?: Remote<any>, remoteDesc?: RemoteDesc): {
export declare function create<RemoteType>(config: RPCSocketConfig, remote?: Remote<any>, remoteDesc?: RemoteDesc): {
onDestroy: (...args: any[]) => (reason?: string) => void;
ready: Promise<{}>;
} | import("@ch1/rpc/dist/interfaces").RPC<T>;
} | import("@ch1/rpc/dist/interfaces").RPC<RemoteType>;

@@ -12,2 +12,20 @@ /**

import { create as createRemote, } from '@ch1/rpc';
function configureNativeSocketOnMethod(config) {
config.on = (callback) => {
const handler = (data) => {
callback(JSON.parse(data.data));
};
config.socket.addEventListener('message', handler);
return () => config.socket.removeEventListener('message', handler);
};
}
function configureWsSocketOnMethod(config) {
config.on = (callback) => {
const handler = (data) => {
callback(JSON.parse(data));
};
config.socket.on('message', handler);
return () => config.socket.removeEventListener('message', handler);
};
}
function configureOnEmit(config) {

@@ -21,18 +39,6 @@ if (!config.socket) {

}
config.on = (callback) => {
const handler = (data) => {
callback(JSON.parse(data.data));
};
config.socket.addEventListener('message', handler);
return () => config.socket.removeEventListener('message', handler);
};
configureNativeSocketOnMethod(config);
}
else {
config.on = (callback) => {
const handler = (data) => {
callback(JSON.parse(data));
};
config.socket.on('message', handler);
return () => config.socket.removeEventListener('message', handler);
};
configureWsSocketOnMethod(config);
}

@@ -46,30 +52,97 @@ if (typeof config.socket.send !== 'function') {

}
export function create(config = {}, remote, remoteDesc) {
function configureNativeSocket(config, remote, remoteDesc) {
let resolve;
let reject;
let rpc;
const earlyDestroys = [];
const expose = {
onDestroy: (...args) => {
let destroyed = false;
const destroy = (reason) => {
destroyed = true;
desc.onDestroy(reason);
};
const desc = {
isValid: () => {
if (destroyed) {
return false;
}
else {
return true;
}
},
args,
onDestroy: reason => { },
};
earlyDestroys.push(desc);
return destroy;
},
ready: new Promise((res, rej) => {
resolve = res;
reject = rej;
}),
};
// @todo awkard... this will need a refactor
config.socket.addEventListener('close', () => {
if (rpc) {
rpc.destroy('rpc: web-socket closed');
}
else {
console.warn('rpc: web-socket error: rpc still not defined (close)');
}
});
// @todo awkard... this will need a refactor
config.socket.addEventListener('error', error => {
if (rpc) {
rpc.destroy('rpc: web-socket error: native: ' + error.message);
}
else {
console.warn('rpc: web-socket error: rpc still not defined (error)');
}
});
config.socket.addEventListener('open', () => {
rpc = createRemote(config, remote, remoteDesc);
earlyDestroys.forEach(desc => {
if (desc.isValid()) {
desc.onDestroy = rpc.onDestroy(...desc.args);
}
});
rpc.ready
.then(() => {
Object.keys(rpc).forEach(key => {
expose[key] = rpc[key];
});
resolve();
})
.catch(reject);
});
return expose;
}
function configureWsSocket(config, remote, remoteDesc) {
const rpc = createRemote(config, remote, remoteDesc);
const interval = setInterval(() => {
if (config.socket.isAlive === false) {
config.socket.terminate();
}
config.socket.isAlive = false;
config.socket.ping();
}, config.pingDelay || 10000);
config.socket.isAlive = true;
config.socket.on('pong', () => (config.socket.isAlive = true));
const destroy = rpc.destroy;
rpc.destroy = () => {
clearInterval(interval);
return destroy();
};
return rpc;
}
export function create(config, remote, remoteDesc) {
configureOnEmit(config);
if (typeof WebSocket !== 'undefined') {
let resolve;
let reject;
const expose = {
ready: new Promise((res, rej) => {
resolve = res;
reject = rej;
}),
};
config.socket.addEventListener('open', () => {
const rpc = createRemote(config, remote, remoteDesc);
rpc.ready
.then(() => {
Object.keys(rpc).forEach(key => {
expose[key] = rpc[key];
});
resolve();
})
.catch(reject);
});
return expose;
return configureNativeSocket(config, remote, remoteDesc);
}
else {
return createRemote(config, remote, remoteDesc);
return configureWsSocket(config, remote, remoteDesc);
}
}
//# sourceMappingURL=web-socket.js.map

@@ -21,3 +21,5 @@ // Karma configuration

// available preprocessors: https://npmjs.org/browse/keyword/karma-preprocessor
preprocessors: {},
preprocessors: {
'intermediate/**/*.js': 'coverage',
},

@@ -27,3 +29,3 @@ // test results reporter to use

// available reporters: https://npmjs.org/browse/keyword/karma-reporter
reporters: ['progress'],
reporters: ['progress', 'coverage'],

@@ -54,3 +56,12 @@ // web server port

concurrency: Infinity,
coverageReporter: {
dir: 'coverage/',
reporters: [
{ type: 'html', subdir: '.' },
{ type: 'lcov', subdir: '.' },
{ type: 'json', subdir: '.', file: 'lcov.json' },
],
},
});
};

@@ -16,3 +16,3 @@ {

],
"version": "1.1.0",
"version": "1.2.0",
"author": {

@@ -44,5 +44,5 @@ "name": "Michael J. Bennett",

"postbuild": "rollup --input dist/index.js --format cjs --file dist/index.cjs.js",
"clean": "rimraf ./intermediate && rimraf ./dist",
"clean": "rimraf ./intermediate && rimraf ./dist && rimraf ./coverage",
"precommit": "lint-staged",
"pretest": "rimraf coverage && yarn build && rollup -c",
"pretest": "yarn build && rollup -c",
"test": "./test.js"

@@ -57,2 +57,3 @@ },

"karma-chrome-launcher": "^2.2.0",
"karma-coverage": "^1.1.2",
"karma-jasmine": "^1.1.2",

@@ -74,56 +75,6 @@ "lint-staged": "^7.2.0",

},
"jest": {
"collectCoverage": true,
"collectCoverageFrom": [
"src/**/*.{ts,tsx}",
"!**/node_modules/**",
"!**/coverage/**",
"!**/dist/**",
"!**/*.d.ts",
"!**/interfaces.ts"
],
"coverageDirectory": "coverage/",
"coveragePathIgnorePatterns": [
"/node_modules/",
"/coverage/",
"/dist/"
],
"coverageReporters": [
"json",
"lcov",
"text",
"html"
],
"coverageThreshold": {
"global": {
"branches": 80,
"functions": 80,
"lines": 80,
"statements": 90
}
},
"modulePathIgnorePatterns": [
"/coverage/",
"/dist/"
],
"transform": {
"^.+\\.tsx?$": "ts-jest"
},
"transformIgnorePatterns": [
"^node_modules/(?!@ch1).*$"
],
"testRegex": "(/__tests__/.*|(\\.|/)(test|spec))\\.(jsx?|tsx?)$",
"moduleFileExtensions": [
"ts",
"tsx",
"js",
"jsx",
"json",
"node"
]
},
"dependencies": {
"@ch1/rpc": "^1.0.4",
"@ch1/rpc": "^1.3",
"@ch1/utility": "^0.5.1"
}
}

@@ -11,13 +11,25 @@ # CH1 RPC

`yarn add @ch1/rpc-worker`
`yarn add @ch1/rpc-web-socket`
### Dependencies
This library has an external run time dependency for the server side portion,
which leverages the [excellent ws](https://github.com/websockets/ws 'Node WebSocket Library') library.
This library has an external _optional_ run time dependency for the server
side portion, which leverages the [excellent ws](https://github.com/websockets/ws 'Node WebSocket Library')
library.
The dependency is optional in that this library will work with anything that
satisfies the `ws` interface:
```ts
export type WsWebSocket = {
on: (message: string, callback: (data: any) => any) => any;
send: (data: string) => any;
};
```
We _do_ include `ws` as a `devDependency` since we use it for testing the
library end to end.
## Usage
_Warning, no error handling is implemented on the sockets yet, that will come in the next release_
Slightly easier API than in the raw [`@ch1/rpc`](https://github.com/bennett000/ch1-rpc 'CH1 RPC')

@@ -53,4 +65,79 @@

### Error Handling
Due to the nature of state that could exist on client/server, this library
takes the approach of terminating itself in the event of catastrophic failure.
- Individual functions that fail are left up to the user to handle
- Connection failures will result in the destruction of the object
- Pending async requests will have their error handlers triggered
best practice is to add an error listener:
```ts
const wrpc = require('@ch1/rpc-web-socket');
const wss = new WebSocket.Server({ port: 8080 });
wss.on('connection', ws => {
const rpc = wrpc.create(
{ socket: ws },
{
foo: () => new Promise(resolve => resolve(7)),
},
);
// This is teh relevant bit
rpc.onDestroy((reason?: string) => {
// put your error handling logic here.
});
});
```
#### Web Socket Connection Handling
This library attempts to automatically handle all connection errors and fail
fast where possible. This includes a server side "ping pong" to detect
"unplug" events.
_Reconnection Is Left Up To The User of The Library, `onDestroy` is
your friend_
Presumably the client will be responsible for the reconnection.
## API
The `create` function will provide an `RPC<RemoteType>` object:
```ts
export function create<RemoteType>(
// required
config: RPCSocketConfig,
// functions (optionally nested) to provide to other side of the connection
remote?: Remote<any>,
// not used for now
remoteDesc?: RemoteDesc,
) {
```
The `RPCSocketConfig` object looks like:
```ts
export interface RPCSocketConfig {
// optionally configure the ping/pong delay the server uses
// defaults to 10,000ms
pingDelay?: number;
// *mandatory* the socket to use, either WebSocket in the browser
// or something _like_ `ws` on the server
// (we're ws 6.x compatible)
socket: NativeWebSocket | WsWebSocket;
}
```
The `RPC<RemoteType>` object is described [in the documentation for @ch1/rpc](https://www.npmjs.com/package/@ch1/rpc '@ch1/rpc documentation')
## License
[LGPL](./LICENSE 'Lesser GNU Public License')

@@ -9,2 +9,3 @@ import nodeResolve from 'rollup-plugin-node-resolve';

format: 'iife',
sourcemap: true,
},

@@ -18,2 +19,3 @@ plugins: [nodeResolve({ jsnext: true })],

format: 'cjs',
sourcemap: true,
},

@@ -20,0 +22,0 @@ plugins: [nodeResolve({ jsnext: true })],

@@ -22,3 +22,20 @@ const TEST_FILE = '/base/intermediate/test-worker.js';

});
it('should destroy if the socket closes', done => {
const ws = new WebSocket('ws://localhost:5151');
const rpc = wrpc.create({ socket: ws });
ws.addEventListener('error', done);
rpc.onDestroy(reason => {
expect(reason).toMatch(/^.*close.*$/);
done();
});
rpc.ready
.then(() => {
ws.close();
})
.catch(done);
});
});
});

@@ -20,7 +20,15 @@ /**

export type WebSocketType = {
on: (message: string, callback: (data: string) => void) => void;
send: (data: string) => void;
export type NativeWebSocket = {
addEventListener: (
message: string,
handler: (result: { data: any }) => any,
) => any;
send: (data: string) => any;
};
export type WsWebSocket = {
on: (message: string, callback: (data: any) => any) => any;
send: (data: string) => any;
};
/**

@@ -30,13 +38,28 @@ * Socket RPC Config

export interface RPCSocketConfig extends RPCAbstractConfig {
socket?:
| WebSocketType
| {
addEventListener: (
message: string,
callback: (data: any) => void,
) => void;
send: (data: any) => void;
};
pingDelay?: number;
socket: NativeWebSocket | WsWebSocket;
}
function configureNativeSocketOnMethod(config: any) {
config.on = (callback: (data: any) => any) => {
const handler = (data: any) => {
callback(JSON.parse(data.data));
};
config.socket.addEventListener('message', handler);
return () => config.socket.removeEventListener('message', handler);
};
}
function configureWsSocketOnMethod(config: any) {
config.on = (callback: (data: any) => any) => {
const handler = (data: any) => {
callback(JSON.parse(data));
};
config.socket.on('message', handler);
return () => config.socket.removeEventListener('message', handler);
};
}
function configureOnEmit(config: any) {

@@ -51,19 +74,5 @@ if (!config.socket) {

}
config.on = (callback: (data: any) => any) => {
const handler = (data: any) => {
callback(JSON.parse(data.data));
};
config.socket.addEventListener('message', handler);
return () => config.socket.removeEventListener('message', handler);
};
configureNativeSocketOnMethod(config);
} else {
config.on = (callback: (data: any) => any) => {
const handler = (data: any) => {
callback(JSON.parse(data));
};
config.socket.on('message', handler);
return () => config.socket.removeEventListener('message', handler);
};
configureWsSocketOnMethod(config);
}

@@ -80,4 +89,108 @@

export function create<T>(
config: RPCSocketConfig = {},
function configureNativeSocket<RemoteConfig>(
config: any,
remote: Remote<any>,
remoteDesc: RemoteDesc,
) {
let resolve;
let reject;
let rpc;
const earlyDestroys = [];
const expose = {
onDestroy: (...args: any[]) => {
let destroyed = false;
const destroy = (reason?: string) => {
destroyed = true;
desc.onDestroy(reason);
};
const desc = {
isValid: () => {
if (destroyed) {
return false;
} else {
return true;
}
},
args,
onDestroy: reason => {},
};
earlyDestroys.push(desc);
return destroy;
},
ready: new Promise((res, rej) => {
resolve = res;
reject = rej;
}),
};
// @todo awkard... this will need a refactor
(config.socket as any).addEventListener('close', () => {
if (rpc) {
rpc.destroy('rpc: web-socket closed');
} else {
console.warn('rpc: web-socket error: rpc still not defined (close)');
}
});
// @todo awkard... this will need a refactor
(config.socket as any).addEventListener('error', error => {
if (rpc) {
rpc.destroy('rpc: web-socket error: native: ' + error.message);
} else {
console.warn('rpc: web-socket error: rpc still not defined (error)');
}
});
(config.socket as any).addEventListener('open', () => {
rpc = createRemote<RemoteConfig>(<RPCConfig>config, remote, remoteDesc);
earlyDestroys.forEach(desc => {
if (desc.isValid()) {
desc.onDestroy = rpc.onDestroy(...desc.args);
}
});
rpc.ready
.then(() => {
Object.keys(rpc).forEach(key => {
expose[key] = rpc[key];
});
resolve();
})
.catch(reject);
});
return expose;
}
function configureWsSocket<RemoteConfig>(
config: any,
remote: Remote<any>,
remoteDesc: RemoteDesc,
) {
const rpc = createRemote<RemoteConfig>(
<RPCConfig>config,
remote,
remoteDesc,
);
const interval = setInterval(() => {
if (config.socket.isAlive === false) {
config.socket.terminate();
}
config.socket.isAlive = false;
config.socket.ping();
}, config.pingDelay || 10000);
config.socket.isAlive = true;
config.socket.on('pong', () => (config.socket.isAlive = true));
const destroy = rpc.destroy;
rpc.destroy = () => {
clearInterval(interval);
return destroy();
};
return rpc;
}
export function create<RemoteType>(
config: RPCSocketConfig,
remote?: Remote<any>,

@@ -89,25 +202,6 @@ remoteDesc?: RemoteDesc,

if (typeof WebSocket !== 'undefined') {
let resolve;
let reject;
const expose = {
ready: new Promise((res, rej) => {
resolve = res;
reject = rej;
}),
};
(config.socket as any).addEventListener('open', () => {
const rpc = createRemote<T>(<RPCConfig>config, remote, remoteDesc);
rpc.ready
.then(() => {
Object.keys(rpc).forEach(key => {
expose[key] = rpc[key];
});
resolve();
})
.catch(reject);
});
return expose;
return configureNativeSocket<RemoteType>(config, remote, remoteDesc);
} else {
return createRemote<T>(<RPCConfig>config, remote, remoteDesc);
return configureWsSocket<RemoteType>(config, remote, remoteDesc);
}
}

Sorry, the diff of this file is not supported yet

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