UNPKG

ipc-node-go

Version:

An IPC implementation between Node and its child process (Golang binary) using the stdin / stdout as the transport.

158 lines (157 loc) 4.67 kB
"use strict"; const events_1 = require("events"); const child_process_1 = require("child_process"); class IPC extends events_1.EventEmitter { constructor(binPath) { super(); this.binPath = binPath; this.go = null; this.closed = false; /** * The `Golang` process will be pinging at every 20 seconds * and will wait another 20 seconds for reply via `pong` event name * else it will kill it's process. */ this.on('ping', () => this.send('pong')); } /** * Start the child process * @param arg */ init(arg = []) { this.closed = false; const self = this; const go = child_process_1.spawn(this.binPath, arg, {}); this.go = go; go.stderr.setEncoding('utf8'); go.stdout.setEncoding('utf8'); // emit the errors go.stderr.on('error', e => self.emit('error', e)); go.stderr.on('data', e => self.emit('log', e)); let outBuffer = ''; go.stdout.on('data', s => { outBuffer += s; if (s.endsWith('}\\n')) { self._processData(outBuffer); outBuffer = ''; } }); go.once('close', _ => { self.closed = true; self.emit('close'); }); process.on('beforeExit', () => this.kill()); return this; } _processData(payload) { let _data = this.parseJSON(payload); if (Array.isArray(_data)) { for (const item of _data) { this.emit('data', item); let { error, data, event } = item; this.emit(event, data, error); } } } /** * Kill the child process */ kill() { try { this.send('___EXIT___', null); this.closed = true; this.go.kill(); } catch (error) { } } /** * Send message to `Golang` process * @param event * @param data */ send(event, data = undefined) { this._send(event, data, false); } /** * sendRaw gives your access to a third `boolean` argument which * is used to determine if this is a sendAndReceive action */ sendRaw(event, data, isSendAndReceive = false) { this._send(event, data, isSendAndReceive); } /** * * @param event * @param data * @param SR this tells `Go` process if this message needs an acknowledgement */ _send(event, data, SR) { try { if (!this.go || this.closed) return; if (this.go && this.go.stdin.writable) { let payload; if (typeof data === 'object' || Array.isArray(data)) payload = JSON.stringify(data); else payload = data; // We are converting this to `JSON` this to preserve the // data types let d = JSON.stringify({ event, data: payload, SR: !!SR }); if (this.go.stdin.writable) { this.go.stdin.write(d + '\n'); } } } catch (error) { this.emit('error', error); } } /** * Send and receive an acknowledgement through * a callback from `Go` process * @param event * @param data * @param cb */ sendAndReceive(event, data, cb) { this._send(event, data, true); let rc = event + '___RC___'; this.once(rc, (data, error) => { if (typeof cb === 'function') cb(error, data); }); } /** * Receive and send back acknowledgement/data to `GO` * a callback from `Go` process * @param event * @param data * @param cb */ onReceiveAnSend(event, cb) { let channel = event + '___RS___'; this.on(event, data => { if (typeof cb === 'function') cb(channel, data); }); } parseJSON(s) { try { let data = s.replace(/}\\n/g, '},'); if (data.endsWith(',')) { data = data.slice(0, -1).trim(); } return JSON.parse(`[${data}]`); } catch (error) { this.emit('parse-error', error); return null; } } } module.exports = IPC;