UNPKG

@theintern/digdug

Version:

Dig Dug. A simple abstraction library for downloading and launching WebDriver service tunnels.

349 lines 12.3 kB
"use strict"; Object.defineProperty(exports, "__esModule", { value: true }); var tslib_1 = require("tslib"); var common_1 = require("@theintern/common"); var child_process_1 = require("child_process"); var path_1 = require("path"); var url_1 = require("url"); var util_1 = require("./lib/util"); var decompress_1 = tslib_1.__importDefault(require("decompress")); var Tunnel = (function (_super) { tslib_1.__extends(Tunnel, _super); function Tunnel(options) { var _this = _super.call(this) || this; Object.assign(_this, { architecture: process.arch, hostname: 'localhost', pathname: '/wd/hub/', platform: process.platform, port: 4444, protocol: 'http', verbose: false, state: 'stopped', }, options || {}); return _this; } Object.defineProperty(Tunnel.prototype, "clientUrl", { get: function () { return url_1.format(this); }, enumerable: false, configurable: true }); Object.defineProperty(Tunnel.prototype, "extraCapabilities", { get: function () { return {}; }, enumerable: false, configurable: true }); Object.defineProperty(Tunnel.prototype, "isDownloaded", { get: function () { return util_1.fileExists(this.executable); }, enumerable: false, configurable: true }); Object.defineProperty(Tunnel.prototype, "isRunning", { get: function () { return this._state === 'running'; }, enumerable: false, configurable: true }); Object.defineProperty(Tunnel.prototype, "isStarting", { get: function () { return this._state === 'starting'; }, enumerable: false, configurable: true }); Object.defineProperty(Tunnel.prototype, "isStopping", { get: function () { return this._state === 'stopping'; }, enumerable: false, configurable: true }); Tunnel.prototype.download = function (forceDownload) { if (forceDownload === void 0) { forceDownload = false; } if (!forceDownload && this.isDownloaded) { return common_1.Task.resolve(); } return this._downloadFile(this.url, this.proxy); }; Tunnel.prototype.getEnvironments = function () { var _this = this; if (!this.environmentUrl) { return common_1.Task.resolve([]); } return common_1.request(this.environmentUrl, { password: this.accessKey, username: this.username, proxy: this.proxy, }).then(function (response) { if (response.status >= 200 && response.status < 400) { return response.json().then(function (data) { return data.reduce(function (environments, environment) { return environments.concat(_this._normalizeEnvironment(environment)); }, []); }); } else { if (response.status === 401) { throw new Error('Missing or invalid username and access key'); } throw new Error("Server replied with a status of " + response.status); } }); }; Tunnel.prototype.sendJobState = function (_jobId, _data) { return common_1.Task.reject(new Error('Job state is not supported by this tunnel.')); }; Tunnel.prototype.start = function () { var _this = this; switch (this._state) { case 'stopping': throw new Error('Previous tunnel is still terminating'); case 'running': case 'starting': return this._startTask; } this._state = 'starting'; this._startTask = this.download().then(function () { return _this._start(function (child) { _this._process = child; _this._handle = common_1.createCompositeHandle(_this._handle || { destroy: function () { } }, util_1.on(child.stdout, 'data', proxyIOEvent(_this, 'stdout')), util_1.on(child.stderr, 'data', proxyIOEvent(_this, 'stderr')), util_1.on(child, 'exit', function () { _this._state = 'stopped'; })); }); }); this._startTask .then(function () { _this._startTask = undefined; _this._state = 'running'; _this.emit({ type: 'status', target: _this, status: 'Ready', }); }) .catch(function (error) { _this._startTask = undefined; _this._state = 'stopped'; _this.emit({ type: 'status', target: _this, status: error.name === 'CancelError' ? 'Start cancelled' : 'Failed to start tunnel', }); }); return this._startTask; }; Tunnel.prototype.stop = function () { var _this = this; switch (this._state) { case 'starting': this._startTask.cancel(); return this._startTask.finally(function () { return null; }); case 'stopping': return this._stopTask; } this._state = 'stopping'; this.emit({ type: 'status', target: this, status: 'Stopping', }); this._stopTask = this._stop() .then(function (returnValue) { if (_this._handle) { _this._handle.destroy(); } _this._process = _this._handle = undefined; _this._state = 'stopped'; _this.emit({ type: 'status', target: _this, status: 'Stopped', }); return returnValue; }) .catch(function (error) { _this._state = 'running'; throw error; }); return this._stopTask; }; Tunnel.prototype._downloadFile = function (url, proxy, options) { var _this = this; var req; if (!url) { return common_1.Task.reject(new Error('URL is empty')); } return new common_1.Task(function (resolve, reject) { req = common_1.request(url, { proxy: proxy, onDownloadProgress: function (event) { _this.emit({ type: 'downloadprogress', target: _this, url: url, total: event.total, received: event.received, }); }, }); req .then(function (response) { if (response.status >= 400) { throw new Error("Download server returned status code " + response.status + " for " + url); } else { response.arrayBuffer().then(function (data) { resolve(_this._postDownloadFile(Buffer.from(data), options)); }); } }) .catch(function (error) { reject(error); }); }, function () { req && req.cancel(); }); }; Tunnel.prototype._makeArgs = function () { var _values = []; for (var _i = 0; _i < arguments.length; _i++) { _values[_i] = arguments[_i]; } return []; }; Tunnel.prototype._makeChild = function (executor) { var values = []; for (var _i = 1; _i < arguments.length; _i++) { values[_i - 1] = arguments[_i]; } var command = this.executable; var args = this._makeArgs.apply(this, values); var options = this._makeOptions.apply(this, values); var child = child_process_1.spawn(command, args, options); child.stdout.setEncoding('utf8'); child.stderr.setEncoding('utf8'); var handle; var canceled = false; process.on('exit', function () { return util_1.kill(child.pid); }); process.on('SIGINT', function () { return util_1.kill(child.pid); }); var task = new common_1.Task(function (resolve, reject) { var errorMessage = ''; var exitCode; var stderrClosed = false; var exitted = false; function handleChildExit() { reject(new Error("Tunnel failed to start: " + (errorMessage || "Exit code: " + exitCode))); } handle = common_1.createCompositeHandle(util_1.on(child, 'error', reject), util_1.on(child.stderr, 'data', function (data) { errorMessage += data; }), util_1.on(child, 'exit', function () { exitted = true; if (stderrClosed) { handleChildExit(); } }), util_1.on(child.stderr, 'close', function () { stderrClosed = true; if (exitted) { handleChildExit(); } })); var result = executor(child, resolve, reject); if (result) { handle = common_1.createCompositeHandle(handle, result); } }, function () { canceled = true; try { util_1.kill(child.pid); } catch (error) { } }); return task.finally(function () { handle.destroy(); if (canceled) { return new Promise(function (resolve) { child.once('exit', function () { resolve(undefined); }); }); } }); }; Tunnel.prototype._makeOptions = function () { var _values = []; for (var _i = 0; _i < arguments.length; _i++) { _values[_i] = arguments[_i]; } return { env: process.env }; }; Tunnel.prototype._normalizeEnvironment = function (environment) { return environment; }; Tunnel.prototype._postDownloadFile = function (data, options) { var _this = this; return new Promise(function (resolve, reject) { var directory = _this.directory; if (options && options.directory) { directory = path_1.join(directory, options.directory); } decompress_1.default(data, directory) .then(function () { return resolve(); }) .catch(reject); }); }; Tunnel.prototype._start = function (executor) { return this._makeChild(function (child, resolve, reject) { var handle = common_1.createCompositeHandle(util_1.on(child.stdout, 'data', resolve), util_1.on(child.stderr, 'data', resolve), util_1.on(child, 'error', function (error) { reject(error); })); try { executor(child, resolve, reject); } catch (error) { reject(error); } return handle; }); }; Tunnel.prototype._stop = function () { var _this = this; return new Promise(function (resolve, reject) { var childProcess = _this._process; if (!childProcess) { resolve(); return; } childProcess.once('exit', function (code) { resolve(code == null ? undefined : code); }); try { util_1.kill(childProcess.pid); } catch (error) { reject(error); } }); }; return Tunnel; }(common_1.Evented)); exports.default = Tunnel; function proxyIOEvent(target, type) { return function (data) { target.emit({ type: type, target: target, data: String(data), }); }; } delete Tunnel.prototype.on; //# sourceMappingURL=Tunnel.js.map