UNPKG

pot-js

Version:

Process management module

329 lines (260 loc) 9.38 kB
'use strict'; Object.defineProperty(exports, "__esModule", { value: true }); 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; }; 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('../utils/isWin'); var _isWin2 = _interopRequireDefault(_isWin); var _path = require('path'); function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { default: obj }; } 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]; }; }; // if `cluster` is not set, and the base name of `execPath` includes `node` // `cluster` will be `true` const ensureClusterMode = function ensureClusterMode(_ref) { let cluster = _ref.cluster, execPath = _ref.execPath; return (0, _lodash.isBoolean)(cluster) ? cluster : /\bnode\b/.test((0, _path.basename)(execPath)); }; const kill = (() => { var _ref2 = _asyncToGenerator(function* (pid) { return (0, _fkill2.default)(pid, { force: _isWin2.default }).catch(_lodash.noop); }); function kill(_x) { return _ref2.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 WorkerMonitor extends _events.EventEmitter { constructor(opts) { super(); this.id = 0; this.status = 'stopped'; this.execPath = opts.execPath; this.execArgv = opts.execArgv; this.cluster = ensureClusterMode(opts); this.name = opts.name; this.cwd = opts.cwd || '.'; this.env = opts.env || {}; this.data = _extends({}, opts.data); 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.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 _ref3 = _asyncToGenerator(function* () { yield kill(child.pid); _this.emit('force-kill'); }); return function sigkill() { return _ref3.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++; let worker; let child; const commomOptions = { cwd: _this2.cwd, uid: _this2.uid, gid: _this2.gid, stdio: _this2.stdio, silent: _this2.silent, windowsVerbatimArguments: _this2.windowsVerbatimArguments, windowsHide: _this2.windowsHide }; if (_this2.cluster) { _cluster2.default.setupMaster(_extends({}, commomOptions, { execPath: _this2.execPath, execArgv: _this2.execArgv })); worker = _cluster2.default.fork(env); child = worker.process; } else { child = (0, _crossSpawn2.default)(_this2.execPath, _this2.execArgv, _extends({}, commomOptions, { env })); } _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); }); const emitReady = function () { _this2.emit(_this2.restarts ? EventTypes.RESTART : EventTypes.START); }; worker ? worker.on('online', emitReady) : emitReady(); }; 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, 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); } } exports.default = WorkerMonitor; WorkerMonitor.EventTypes = EventTypes;