UNPKG

node-simple-ipc

Version:

A Node.Js module for local Inter Process Communication

210 lines 7.32 kB
"use strict"; Object.defineProperty(exports, "__esModule", { value: true }); exports.NodeSimpleIpc = void 0; const events_1 = require("events"); const errors_1 = require("./errors"); const utils_1 = require("./utils"); class NodeSimpleIpc { /** * Constructor. * * @param ipcProcess IPC process (NodeJS.process or ChildProcess). By default "process" will be used. * @param options NodeSimpleIpc options. */ constructor(ipcProcess = process, options) { // Store registered RPC endpoint names. this.registeredRpcNames = {}; this.ipcProcess = ipcProcess; this.options = Object.assign({ actTimeout: 30e3, startService: true }, options); this.messageHandler = this.onIpcMessage.bind(this); this.rpcEm = new events_1.EventEmitter(); this.eventsEm = new events_1.EventEmitter(); this.ipcProcess.on('message', this.messageHandler); } onIpcMessage(data) { if ((0, utils_1.isIpcInput)(data)) { // If RPC handler not found then send not found error. if (!(data.name in this.registeredRpcNames)) { this.sendOutput(data, { data: undefined, error: { message: `RPC "${data.name}" not found.`, }, }); return; } // data.name return; } if ((0, utils_1.isIpcOutput)(data)) { this.rpcEm.emit(data.correlationId, data); return; } if ((0, utils_1.isIpcEvent)(data)) { this.eventsEm.emit(data.name, data.data); return; } } /** * Start a RPC request. * * @param name RPC name. * @param data Request data (optional). * @param options Request options. * @returns The response. */ act(name, data, options) { (0, utils_1.assertValidIpcName)(name); const finOpts = Object.assign({ timeout: this.options.actTimeout }, options); const correlationId = (0, utils_1.uniqueId)().toString(); let timeoutId; return new Promise((resolve, reject) => { // Event handler const listenReply = (ipcOutput) => { clearTimeout(timeoutId); if (ipcOutput.error) { reject(new errors_1.RemoteError(ipcOutput.error.message, ipcOutput.error)); } else { resolve(ipcOutput.data); } }; // Register event handler this.rpcEm.once(correlationId, listenReply); // Process timeout case timeoutId = setTimeout(() => { // Remove listener this.rpcEm.off(correlationId, listenReply); // Throw timeout error reject(new errors_1.TimeoutError(`Reply timeout. IPC name: ${name}.`)); }, finOpts.timeout); this.sendInput({ correlationId, name, data, }); }); } /** * Add a RPC endpoint. * * @param name RPC name. * @param handlerFn Handler function. * @returns Function you can call to remove the RPC endpoint. */ add(name, handlerFn) { (0, utils_1.assertValidIpcName)(name); (0, utils_1.assertValidIpcHandler)(handlerFn); if (name in this.registeredRpcNames) { throw new Error(`The RPC named "${name}" already exists.`); } this.registeredRpcNames[name] = 1; const messageListener = (message) => { // Ignore all messages not related to this lib if (!(0, utils_1.isIpcInput)(message)) { return; } const ipcInput = message; // Pass only messages related to this endpoint if (ipcInput.name !== name) { return; } // Convert sync function to async for catching all errors new Promise((resolve) => resolve(handlerFn(ipcInput.data))) .then((data) => { this.sendOutput(ipcInput, { data }); }) .catch((err) => { this.sendOutput(ipcInput, { data: undefined, error: (0, utils_1.serializeError)(err), }); }); }; this.ipcProcess.on('message', messageListener); return () => { this.ipcProcess.off('message', messageListener); delete this.registeredRpcNames[name]; }; } /** * Adds the listener function to the end of the listeners. * * @param event Event name. * @param handler Listener function. * @returns Function you can call to remove the event handler. */ on(event, handler) { this.eventsEm.on(event, handler); return () => { this.eventsEm.off(event, handler); }; } /** * Adds a one-time listener function for the event. The next time event is triggered, this listener is removed and then invoked. * * @param event Event name. * @param handler Listener function. * @returns Function you can call to remove the event handler. */ once(event, handler) { this.eventsEm.once(event, handler); return () => { this.eventsEm.off(event, handler); }; } /** * Removes the specified listener from the listener array. * * @param event Event name. * @param handler Listener function. * @returns Returns a reference to the NodeSimpleIpc. */ off(event, handler) { this.eventsEm.off(event, handler); } /** * Send an event over IPC. * * @param event Event name. * @param data Event data (optional). * @returns The sending result. */ emit(event, data) { if (!this.ipcProcess.send) throw new Error('The send() method is not defined.'); const ipcEvent = { type: "E" /* Event */, name: event, data, }; return this.ipcProcess.send(ipcEvent); } /** * Send RPC output properties over IPC. * * @param input Input properties. * @param partialOutput Output properties (only data and error). * @returns The sending result. */ sendOutput(input, partialOutput) { if (!this.ipcProcess.send) throw new Error('The send() method is not defined.'); const output = Object.assign(Object.assign({}, partialOutput), { correlationId: input.correlationId, name: input.name, type: "O" /* Output */ }); return this.ipcProcess.send(output); } /** * Send RPC input properties over IPC. * * @param partialInput Input properties. * @returns The sending result. */ sendInput(partialInput) { if (!this.ipcProcess.send) throw new Error('The send() method is not defined.'); const input = Object.assign(Object.assign({}, partialInput), { type: "I" /* Input */ }); return this.ipcProcess.send(input); } } exports.NodeSimpleIpc = NodeSimpleIpc; //# sourceMappingURL=node-simple-ipc.js.map