@theintern/digdug
Version:
Dig Dug. A simple abstraction library for downloading and launching WebDriver service tunnels.
349 lines • 12.3 kB
JavaScript
;
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