ipc-link-core
Advanced tools
Comparing version 0.0.1 to 0.1.0
{ | ||
"name": "ipc-link-core", | ||
"version": "0.0.1", | ||
"version": "0.1.0", | ||
"description": "IPC Utilities", | ||
@@ -5,0 +5,0 @@ "main": "src/index", |
@@ -12,5 +12,7 @@ # IPC-Link Core | ||
```javascript | ||
// This example must be run before interactive/world, since this serves the | ||
// IPC server the other sockets connect to. | ||
const { Node } = require('ipc-link-core'); | ||
const node = new Node() | ||
const node = new Node('hello') | ||
.on('connection', (name, socket) => { | ||
@@ -23,4 +25,5 @@ console.log(`Connected to ${name}`); | ||
.on('message', console.log.bind(null, 'Message')) | ||
.on('error', console.error.bind(null, 'Error')); | ||
node.serve('hello', 8001); | ||
.on('error', console.error.bind(null, 'Error')) | ||
.on('socketClose', console.log.bind(null, 'Closed Socket:')) | ||
.serve('hello', 8001); | ||
``` | ||
@@ -31,5 +34,7 @@ | ||
```javascript | ||
// This example depends on hello.js to be running in another process. | ||
// This Node is a socket that replies to hello.js with "world!" when it receives "Hello". | ||
const { Node } = require('ipc-link-core'); | ||
new Node() | ||
const node = new Node('world') | ||
.on('message', (message) => { | ||
@@ -41,4 +46,5 @@ console.log(`Received data from ${message.from}:`, message); | ||
.on('error', console.error) | ||
.on('connect', () => console.log('Connected!')) | ||
.connectTo('hello', 8001) | ||
.on('connect', () => console.log('Connected!')); | ||
node.connectTo('hello', 8001) | ||
.catch(() => console.log('Disconnected!')); | ||
@@ -45,0 +51,0 @@ ``` |
@@ -25,2 +25,3 @@ const { EventEmitter } = require('events'); | ||
* @typedef {Object} QueueEntry | ||
* @property {string} name The name of the socket this was sent to | ||
* @property {Function} resolve The resolve function | ||
@@ -32,8 +33,19 @@ * @property {Function} reject The reject function | ||
/** | ||
* @param {string} name The name for this Node | ||
* @param {NodeOptions} [options={}] The options for this Node instance | ||
*/ | ||
constructor({ maxRetries = Infinity, retryTime = 200 } = {}) { | ||
constructor(name, { maxRetries = Infinity, retryTime = 200 } = {}) { | ||
super(); | ||
if (typeof name !== 'string') throw new Error('A Node name must be specified and must be a string.'); | ||
/** | ||
* The name for this Node | ||
* @name Node#name | ||
* @type {string} | ||
* @readonly | ||
*/ | ||
Object.defineProperty(this, 'name', { value: name, enumerable: true }); | ||
/** | ||
* The amount of retries this Node will do when reconnecting | ||
@@ -60,2 +72,3 @@ * @type {number} | ||
* @type {Map<string, NodeSocket>} | ||
* @readonly | ||
*/ | ||
@@ -67,2 +80,3 @@ sockets: { value: new Map() }, | ||
* @type {Map<string, QueueEntry>} | ||
* @readonly | ||
* @private | ||
@@ -75,2 +89,3 @@ */ | ||
* @type {Map<string, Socket>} | ||
* @readonly | ||
* @private | ||
@@ -86,2 +101,3 @@ */ | ||
* @param {...*} options The options to pass to net.Server#listen | ||
* @returns {this} | ||
*/ | ||
@@ -92,6 +108,13 @@ serve(name, ...options) { | ||
.on('connection', (socket) => { | ||
socket.on('data', (data) => this._onDataMessage(name, socket, data)); | ||
let socketName = null; | ||
socket | ||
.on('data', (data) => this._onDataMessage(socketName, socket, data)) | ||
.on('close', () => { | ||
// Cleanup | ||
this._destroySocket(socketName, socket, true); | ||
}); | ||
this.sendTo(socket, kIdentify).then(sName => { | ||
this._serverNodes.set(sName, socket); | ||
this.emit('connection', sName, socket); | ||
socketName = sName; | ||
this._serverNodes.set(socketName, socket); | ||
this.emit('connection', socketName, socket); | ||
}); | ||
@@ -102,6 +125,10 @@ }) | ||
this.server = null; | ||
for (const socket of this._serverNodes.values()) socket.destroy(); | ||
this.emit('close'); | ||
const rejectError = new Error('Server has been disconnected.'); | ||
for (const element of this._queue.values()) element.reject(rejectError); | ||
if (this._queue.size) { | ||
const rejectError = new Error('Server has been disconnected.'); | ||
for (const element of this._queue.values()) element.reject(rejectError); | ||
} | ||
}) | ||
@@ -111,2 +138,3 @@ .on('error', this.emit.bind(this, 'error')) | ||
this.server.listen(...options); | ||
return this; | ||
} | ||
@@ -132,3 +160,3 @@ | ||
sendTo(name, data, receptive = true) { | ||
const socket = name instanceof net.Socket ? name : this.sockets.get(name); | ||
const socket = name instanceof net.Socket ? name : (sk => sk ? sk.socket : null)(this.sockets.get(name)); | ||
if (!socket) return Promise.reject(new Error('Failed to send to the socket.')); | ||
@@ -149,3 +177,3 @@ if (!socket.writable) return Promise.reject(new Error('The Socket is not writable.')); | ||
}; | ||
return this._queue.set(id, { resolve: send.bind(null, resolve), reject: send.bind(null, reject) }); | ||
return this._queue.set(id, { to: name, resolve: send.bind(null, resolve), reject: send.bind(null, reject) }); | ||
} catch (error) { | ||
@@ -194,6 +222,3 @@ return reject(error); | ||
if (--node.retriesRemaining <= 0) { | ||
node.socket.destroy(); | ||
node.socket.removeAllListeners(); | ||
this.sockets.delete(name); | ||
this.emit('destroy', name, node.socket); | ||
this._destroySocket(name, node.socket, false); | ||
reject(node.socket); | ||
@@ -215,3 +240,34 @@ } else { | ||
disconnectFrom(name) { | ||
const nodeSocket = this.sockets.get(name); | ||
if (!nodeSocket) throw new Error(`The socket ${name} is not connected to this one.`); | ||
this._destroySocket(name, nodeSocket.socket, false); | ||
} | ||
/** | ||
* Destroy a socket and perform all cleanup | ||
* @param {string} socketName The label name of the socket to destroy | ||
* @param {net.Socket} socket The Socket to destroy | ||
* @param {boolean} server Whether the destroy belongs to the Node's server or not | ||
* @private | ||
*/ | ||
_destroySocket(socketName, socket, server) { | ||
socket.destroy(); | ||
socket.removeAllListeners(); | ||
if (this._queue.size) { | ||
const rejectError = new Error('Socket has been disconnected.'); | ||
for (const element of this._queue.values()) if (element.to === socketName) element.reject(rejectError); | ||
} | ||
if (server) { | ||
this._serverNodes.delete(socketName); | ||
this.emit('socketClose', socketName); | ||
} else { | ||
this.sockets.delete(socketName); | ||
this.emit('destroy', socketName, socket); | ||
} | ||
} | ||
/** | ||
* Parse the message | ||
@@ -235,3 +291,3 @@ * @param {string} name The label name of the socket | ||
if (data === kIdentify) { | ||
socket.write(Node.packMessage(id, name, false)); | ||
socket.write(Node.packMessage(id, this.name, false)); | ||
return; | ||
@@ -243,3 +299,3 @@ } | ||
from: { value: name, enumerable: true }, | ||
receptive: { value: receptive !== '0', enumerable: true }, | ||
receptive: { value: receptive, enumerable: true }, | ||
reply: { value: (content) => receptive ? socket.write(Node.packMessage(id, content, false)) : false } | ||
@@ -259,3 +315,3 @@ }); | ||
const [id, type, _receptive] = buffer.toString('utf8', 0, kIndex - 1).split(' '); | ||
const receptive = _receptive !== '0'; | ||
const receptive = _receptive === '1'; | ||
if (type === '5') return [id, receptive, kPing]; | ||
@@ -364,2 +420,7 @@ if (type === '6') return [id, receptive, kIdentify]; | ||
/** | ||
* Emitted when a socket connected to the server closes. | ||
* @event Node#socketClose | ||
* @param {string} name The label name of the socket that closed | ||
*/ | ||
/** | ||
* Emitted when the Node's server is ready. | ||
@@ -366,0 +427,0 @@ * @event Node#listening |
@@ -0,4 +1,6 @@ | ||
// This example must be run before interactive/world, since this serves the | ||
// IPC server the other sockets connect to. | ||
const { Node } = require('../src/index'); | ||
const node = new Node() | ||
const node = new Node('hello') | ||
.on('connection', (name, socket) => { | ||
@@ -11,3 +13,4 @@ console.log(`Connected to ${name}`); | ||
.on('message', console.log.bind(null, 'Message')) | ||
.on('error', console.error.bind(null, 'Error')); | ||
node.serve('hello', 8001); | ||
.on('error', console.error.bind(null, 'Error')) | ||
.on('socketClose', console.log.bind(null, 'Closed Socket:')) | ||
.serve('hello', 8001); |
@@ -0,4 +1,6 @@ | ||
// This example depends on hello.js to be running in another process. | ||
// This Node is a socket that replies to hello.js with "world!" when it receives "Hello". | ||
const { Node } = require('../src/index'); | ||
new Node() | ||
const node = new Node('world') | ||
.on('message', (message) => { | ||
@@ -10,4 +12,5 @@ console.log(`Received data from ${message.from}:`, message); | ||
.on('error', console.error) | ||
.on('connect', () => console.log('Connected!')) | ||
.connectTo('hello', 8001) | ||
.on('connect', () => console.log('Connected!')); | ||
node.connectTo('hello', 8001) | ||
.catch(() => console.log('Disconnected!')); |
@@ -8,6 +8,7 @@ import { EventEmitter } from 'events'; | ||
export class Node extends EventEmitter { | ||
public constructor(options?: NodeOptions); | ||
public constructor(name: string, options?: NodeOptions); | ||
public maxRetries: number; | ||
public retryTime: number; | ||
public server: Server | null; | ||
public readonly name: string; | ||
public readonly sockets: Map<string, NodeSocket>; | ||
@@ -26,2 +27,3 @@ private readonly _queue: Map<string, QueueEntry>; | ||
public on(event: 'close', listener: () => void): this; | ||
public on(event: 'socketClose', listener: (name: string) => void): this; | ||
public on(event: 'listening', listener: () => void): this; | ||
@@ -43,2 +45,3 @@ | ||
public once(event: 'close', listener: () => void): this; | ||
public once(event: 'socketClose', listener: (name: string) => void): this; | ||
public once(event: 'listening', listener: () => void): this; | ||
@@ -51,11 +54,11 @@ | ||
public serve(name: string, port?: number, hostname?: string, backlog?: number, listeningListener?: Function): void; | ||
public serve(name: string, port?: number, hostname?: string, listeningListener?: Function): void; | ||
public serve(name: string, port?: number, backlog?: number, listeningListener?: Function): void; | ||
public serve(name: string, port?: number, listeningListener?: Function): void; | ||
public serve(name: string, path: string, backlog?: number, listeningListener?: Function): void; | ||
public serve(name: string, path: string, listeningListener?: Function): void; | ||
public serve(name: string, options: ListenOptions, listeningListener?: Function): void; | ||
public serve(name: string, handle: any, backlog?: number, listeningListener?: Function): void; | ||
public serve(name: string, handle: any, listeningListener?: Function): void; | ||
public serve(name: string, port?: number, hostname?: string, backlog?: number, listeningListener?: Function): this; | ||
public serve(name: string, port?: number, hostname?: string, listeningListener?: Function): this; | ||
public serve(name: string, port?: number, backlog?: number, listeningListener?: Function): this; | ||
public serve(name: string, port?: number, listeningListener?: Function): this; | ||
public serve(name: string, path: string, backlog?: number, listeningListener?: Function): this; | ||
public serve(name: string, path: string, listeningListener?: Function): this; | ||
public serve(name: string, options: ListenOptions, listeningListener?: Function): this; | ||
public serve(name: string, handle: any, backlog?: number, listeningListener?: Function): this; | ||
public serve(name: string, handle: any, listeningListener?: Function): this; | ||
public broadcast<T = any>(data: any): Promise<Array<T>>; | ||
@@ -68,3 +71,5 @@ public sendTo<T = any>(name: string, data: any, receptive?: boolean): Promise<T>; | ||
public connectTo(name: string, path: string, connectionListener?: Function): Promise<Socket>; | ||
public disconnectFrom(name: string): void; | ||
private _destroySocket(socketName: string, socket: Socket, server: boolean): void; | ||
private _onDataMessage(name: string, socket: Socket, buffer: Buffer): void; | ||
@@ -71,0 +76,0 @@ private static unPackMessage(buffer: Buffer): [string, boolean, any]; |
23856
13
525
57