UNPKG

nightmare

Version:

A high-level browser automation library.

169 lines (145 loc) 4.52 kB
'use strict' /** * Module dependencies */ var Emitter = require('events').EventEmitter var sliced = require('sliced') var debug = require('debug')('nightmare:ipc') // If this process has a parent, redirect debug logs to it if (process.send) { debug = function() { process.send(['nightmare:ipc:debug'].concat(sliced(arguments))) } } /** * Export `IPC` */ module.exports = IPC /** * Initialize `IPC` */ var instance = Symbol() function IPC(process) { if (process[instance]) { return process[instance] } var emitter = (process[instance] = new Emitter()) var emit = emitter.emit var callId = 0 var responders = {} // no parent if (!process.send) { return emitter } process.on('message', function(data) { // handle debug logging specially if (data[0] === 'nightmare:ipc:debug') { debug.apply(null, sliced(data, 1)) } emit.apply(emitter, sliced(data)) }) emitter.emit = function() { if (process.connected) { process.send(sliced(arguments)) } } /** * Call a responder function in the associated process. (In the process, * responders can be registered with `ipc.respondTo()`.) The last argument * should be a callback function, which will called with the results of the * responder. * This returns an event emitter. You can listen for the results of the * responder using the `end` event (this is the same as passing a callback). * Additionally, you can listen for `data` events, which the responder may * send to indicate some sort of progress. * @param {String} name Name of the responder function to call * @param {...Objects} [arguments] Any number of arguments to send * @param {Function} [callback] A callback function that handles the results * @return {Emitter} */ emitter.call = function(name) { var args = sliced(arguments, 1) var callback = args.pop() if (typeof callback !== 'function') { args.push(callback) callback = undefined } var id = callId++ var progress = new Emitter() emitter.on(`CALL_DATA_${id}`, function() { progress.emit.apply(progress, ['data'].concat(sliced(arguments))) }) emitter.once(`CALL_RESULT_${id}`, function(err) { // unserialize errors err = unserializeError(err) progress.emit.apply( progress, ['end'].concat(err).concat(sliced(arguments, 1)) ) emitter.removeAllListeners(`CALL_DATA_${id}`) progress.removeAllListeners() progress = undefined if (callback) { callback.apply(null, [err].concat(sliced(arguments, 1))) } }) emitter.emit.apply(emitter, ['CALL', id, name].concat(args)) return progress } /** * Register a responder to be called from other processes with `ipc.call()`. * The responder should be a function that accepts any number of arguments, * where the last argument is a callback function. When the responder has * finished its work, it MUST call the callback. The first argument should be * an error, if any, and the second should be the results. * Only one responder can be registered for a given name. * @param {String} name The name to register the responder under. * @param {Function} responder */ emitter.respondTo = function(name, responder) { if (responders[name]) { debug(`Replacing responder named "${name}"`) } responders[name] = responder } emitter.on('CALL', function(id, name) { var responder = responders[name] var done = function(err) { err = serializeError(err) emitter.emit.apply( emitter, [`CALL_RESULT_${id}`].concat(err).concat(sliced(arguments, 1)) ) } done.progress = function() { emitter.emit.apply(emitter, [`CALL_DATA_${id}`].concat(sliced(arguments))) } if (!responder) { return done(new Error(`Nothing responds to "${name}"`)) } try { responder.apply(null, sliced(arguments, 2).concat([done])) } catch (error) { done(error) } }) return emitter } function serializeError(err) { if (!(err instanceof Error)) return err return { code: err.code, message: err.message, details: err.detail, stack: err.stack || '' } } function unserializeError(err) { if (!err || !err.message) return err const e = new Error(err.message) e.code = err.code || -1 if (err.stack) e.stack = err.stack if (err.details) e.details = err.details if (err.url) e.url = err.url return e }