pot-js
Version:
Process management module
335 lines (263 loc) • 9.73 kB
JavaScript
'use strict';
Object.defineProperty(exports, "__esModule", {
value: true
});
exports.EventTypes = undefined;
var _extends = Object.assign || function (target) { for (var i = 1; i < arguments.length; i++) { var source = arguments[i]; for (var key in source) { if (Object.prototype.hasOwnProperty.call(source, key)) { target[key] = source[key]; } } } return target; };
exports.default = respawn;
var _crossSpawn = require('cross-spawn');
var _crossSpawn2 = _interopRequireDefault(_crossSpawn);
var _events = require('events');
var _fkill = require('fkill');
var _fkill2 = _interopRequireDefault(_fkill);
var _cluster = require('cluster');
var _cluster2 = _interopRequireDefault(_cluster);
var _lodash = require('lodash');
var _isWin = require('./isWin');
var _isWin2 = _interopRequireDefault(_isWin);
function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { default: obj }; }
function _objectWithoutProperties(obj, keys) { var target = {}; for (var i in obj) { if (keys.indexOf(i) >= 0) continue; if (!Object.prototype.hasOwnProperty.call(obj, i)) continue; target[i] = obj[i]; } return target; }
function _asyncToGenerator(fn) { return function () { var gen = fn.apply(this, arguments); return new Promise(function (resolve, reject) { function step(key, arg) { try { var info = gen[key](arg); var value = info.value; } catch (error) { reject(error); return; } if (info.done) { resolve(value); } else { return Promise.resolve(value).then(function (value) { step("next", value); }, function (err) { step("throw", err); }); } } return step("next"); }); }; } /**
* this module is inspired by [respawn](https://github.com/mafintosh/respawn)
*/
const defaultSleep = function defaultSleep(sleep) {
sleep = Array.isArray(sleep) ? sleep : [sleep || 1000];
return function (restarts) {
return sleep[restarts - 1] || sleep[sleep.length - 1];
};
};
const kill = (() => {
var _ref = _asyncToGenerator(function* (pid) {
return (0, _fkill2.default)(pid, { force: _isWin2.default }).catch(_lodash.noop);
});
function kill(_x) {
return _ref.apply(this, arguments);
}
return kill;
})();
const EventTypes = {
SPAWN: 'spawn',
START: 'start',
RESTART: 'restart',
SLEEP: 'sleep',
CRASH: 'crash',
EXIT: 'exit',
STOP: 'stop',
STDOUT: 'stdout',
STDERR: 'stderr',
WARN: 'warn'
};
class Monitor extends _events.EventEmitter {
constructor(opts) {
super();
this.id = 0;
this.status = 'stopped';
this.execPath = opts.execPath;
this.execArgv = opts.execArgv;
this.name = opts.name;
this.cwd = opts.cwd || '.';
this.env = opts.env || {};
this.data = _extends({}, opts.data);
this.globalState = opts.globalState;
this.uid = opts.uid;
this.gid = opts.gid;
this.pid = 0;
this.ppid = opts.ppid;
this.restarts = -1;
this.crashes = 0;
this.stdio = opts.stdio;
this.stdout = opts.stdout;
this.stderr = opts.stderr;
this.silent = opts.silent;
this.windowsVerbatimArguments = opts.windowsVerbatimArguments;
this.windowsHide = opts.windowsHide !== false;
this.crashed = false;
this.sleep = typeof opts.sleep === 'function' ? opts.sleep : defaultSleep(opts.sleep);
this.maxRestarts = opts.maxRestarts === 0 ? 0 : opts.maxRestarts || -1;
this.kill = opts.kill === false ? false : opts.kill || 30000;
this.child = null;
this.worker = null;
this.started = null;
this.timeout = null;
}
stop() {
var _this = this;
return _asyncToGenerator(function* () {
if (_this.status === 'stopped' || _this.status === 'stopping') {
return;
}
_this.status = 'stopping';
clearTimeout(_this.timeout);
if (!_this.child) return _this._stopped();
let wait;
const child = _this.child;
const sigkill = (() => {
var _ref2 = _asyncToGenerator(function* () {
yield kill(child.pid);
_this.emit('force-kill');
});
return function sigkill() {
return _ref2.apply(this, arguments);
};
})();
const onexit = function () {
clearTimeout(wait);
};
if (_this.kill !== false) {
wait = setTimeout(sigkill, _this.kill);
_this.child.on('exit', onexit);
}
yield Promise.all([new Promise(function (resolve) {
if (_this.child) _this.child.once('exit', resolve);else resolve();
}), kill(_this.child.pid)]);
})();
}
start() {
var _arguments = arguments,
_this2 = this;
return _asyncToGenerator(function* () {
let options = _arguments.length > 0 && _arguments[0] !== undefined ? _arguments[0] : {};
let restart = options.restart;
if (_this2.status === 'running') {
if (!restart) return false;
yield _this2.stop();
}
let clock = 60000;
const env = Object.assign({}, process.env, _this2.env);
const loop = function () {
_this2.restarts++;
// const cmd =
// typeof this.command === 'function' ? this.command() : this.command;
_cluster2.default.setupMaster({
execPath: _this2.execPath,
execArgv: _this2.execArgv,
cwd: _this2.cwd,
uid: _this2.uid,
gid: _this2.gid,
silent: _this2.silent,
windowsVerbatimArguments: _this2.windowsVerbatimArguments,
windowsHide: _this2.windowsHide
});
_this2.worker = _cluster2.default.fork(env);
const child = _this2.worker.process;
// const child = spawn(cmd[0], cmd.slice(1), {
// cwd: this.cwd,
// env,
// uid: this.uid,
// gid: this.gid,
// stdio: this.stdio,
// silent: this.silent,
// windowsVerbatimArguments: this.windowsVerbatimArguments,
// windowsHide: this.windowsHide,
// });
_this2.started = new Date();
_this2.status = 'running';
_this2.child = child;
_this2.pid = child.pid;
_this2.data.pid = _this2.pid;
_this2.data.ppid = _this2.ppid;
_this2.emit(EventTypes.SPAWN, child);
child.setMaxListeners(0);
if (child.stdout) {
child.stdout.on('data', function (data) {
_this2.emit(EventTypes.STDOUT, data);
});
if (_this2.stdout) {
child.stdout.pipe(_this2.stdout);
}
}
if (child.stderr) {
child.stderr.on('data', function (data) {
_this2.emit(EventTypes.STDERR, data);
});
if (_this2.stderr) {
child.stderr.pipe(_this2.stderr);
}
}
child.on('message', function (message) {
_this2.emit('message', message);
});
const clear = function () {
if (_this2.child !== child) return false;
_this2.child = null;
_this2.pid = 0;
return true;
};
child.on('error', function (err) {
_this2.emit(EventTypes.WARN, err); // too opionated? maybe just forward err
if (!clear()) return;
if (_this2.status === 'stopping') return _this2._stopped();
_this2.crashes++;
_this2._crash();
});
child.on('exit', function (code, signal) {
_this2.emit(EventTypes.EXIT, code, signal);
if (!clear()) return;
if (_this2.status === 'stopping') return _this2._stopped();
clock -= Date.now() - (_this2.started ? _this2.started.getTime() : 0);
if (clock <= 0) {
clock = 60000;
}
_this2.crashes++;
if (_this2.restarts > _this2.maxRestarts && _this2.maxRestarts !== -1) {
return _this2._crash();
}
_this2.status = 'sleeping';
_this2.emit(EventTypes.SLEEP);
const restartTimeout = _this2.sleep(_this2.restarts);
_this2.timeout = setTimeout(loop, restartTimeout);
});
_this2.worker.on('online', function () {
_this2.emit(_this2.restarts ? EventTypes.RESTART : EventTypes.START);
});
};
clearTimeout(_this2.timeout);
loop();
return _this2.status === 'running';
})();
}
restart() {
var _this3 = this;
return _asyncToGenerator(function* () {
return _this3.start({ restart: true });
})();
}
toJSON() {
return _extends({}, this.data, {
pid: this.pid,
ppid: this.ppid,
globalState: this.globalState,
monitor: {
instanceId: this.id,
name: this.name,
status: this.status,
started: this.started,
pid: this.ppid,
crashes: this.crashes,
command: this.command,
cwd: this.cwd,
env: this.env
}
});
}
_crash() {
if (this.status !== 'running') return;
this.status = 'crashed';
this.emit(EventTypes.CRASH);
if (this.status === 'crashed') this._stopped();
}
_stopped() {
if (this.status === 'stopped') return;
if (this.status !== 'crashed') this.status = 'stopped';
this.started = null;
this.emit(EventTypes.STOP);
}
}
function respawn() {
let opts = arguments.length > 0 && arguments[0] !== undefined ? arguments[0] : {};
var _opts$instances = opts.instances;
const instances = _opts$instances === undefined ? 1 : _opts$instances,
options = _objectWithoutProperties(opts, ['instances']);
return new Array(instances).fill().map(() => new Monitor(options));
}
exports.EventTypes = EventTypes;