gst-atom-xcuitest-driver
Version:
ATOM driver for iOS using XCUITest for backend
308 lines (239 loc) • 33.1 kB
JavaScript
"use strict";
var _interopRequireDefault = require("@babel/runtime/helpers/interopRequireDefault");
Object.defineProperty(exports, "__esModule", {
value: true
});
exports.default = exports.DeviceConnectionsFactory = exports.DEVICE_CONNECTIONS_FACTORY = void 0;
require("source-map-support/register");
var _lodash = _interopRequireDefault(require("lodash"));
var _net = _interopRequireDefault(require("net"));
var _bluebird = _interopRequireDefault(require("bluebird"));
var _appiumSupport = require("appium-support");
var _gstAtomIosDevice = require("gst-atom-ios-device");
var _portscanner = require("portscanner");
var _asyncbox = require("asyncbox");
const LOCALHOST = '127.0.0.1';
class iProxy {
constructor(udid, localport, deviceport, usbmuxdRemoteHost, usbmuxdRemotePort) {
this.localport = parseInt(localport, 10);
this.deviceport = parseInt(deviceport, 10);
this.udid = udid;
this.localServer = null;
this.log = _appiumSupport.logger.getLogger(`iProxy@${udid.substring(0, 8)}:${this.localport}`);
this.usbmuxdRemoteHost = usbmuxdRemoteHost;
this.usbmuxdRemotePort = usbmuxdRemotePort;
}
async start() {
if (this.localServer) {
return;
}
this.localServer = _net.default.createServer(async localSocket => {
let remoteSocket;
try {
var options = {
udid: this.udid,
port: this.deviceport,
usbmuxdRemoteHost: this.usbmuxdRemoteHost,
usbmuxdRemotePort: this.usbmuxdRemotePort
};
remoteSocket = await _gstAtomIosDevice.utilities.connectPort(null, options);
} catch (e) {
this.log.debug(e.message);
localSocket.destroy();
return;
}
const destroyCommChannel = () => {
remoteSocket.unpipe(localSocket);
localSocket.unpipe(remoteSocket);
};
remoteSocket.once('close', () => {
destroyCommChannel();
localSocket.destroy();
});
remoteSocket.on('error', e => this.log.debug(e));
localSocket.once('end', destroyCommChannel);
localSocket.once('close', () => {
destroyCommChannel();
remoteSocket.destroy();
});
localSocket.on('error', e => this.log.warn(e.message));
localSocket.pipe(remoteSocket);
remoteSocket.pipe(localSocket);
});
const listeningPromise = new _bluebird.default((resolve, reject) => {
this.localServer.once('listening', resolve);
this.localServer.once('error', reject);
});
this.localServer.listen(this.localport);
try {
await listeningPromise;
} catch (e) {
this.localServer = null;
throw e;
}
this.localServer.on('error', e => this.log.warn(e.message));
this.localServer.once('close', e => {
if (e) {
this.log.info(`The connection has been closed with error ${e.message}`);
} else {
this.log.info(`The connection has been closed`);
}
this.localServer = null;
});
this.onBeforeProcessExit = this._closeLocalServer.bind(this);
process.on('beforeExit', this.onBeforeProcessExit);
}
_closeLocalServer() {
if (!this.localServer) {
return;
}
this.log.debug(`Closing the connection`);
this.localServer.close();
this.localServer = null;
}
stop() {
if (this.onBeforeProcessExit) {
process.off('beforeExit', this.onBeforeProcessExit);
this.onBeforeProcessExit = null;
}
this._closeLocalServer();
}
}
const log = _appiumSupport.logger.getLogger('DevCon Factory');
const PORT_CLOSE_TIMEOUT = 15 * 1000;
const SPLITTER = ':';
class DeviceConnectionsFactory {
constructor() {
this._connectionsMapping = {};
}
_udidAsToken(udid) {
return `${_appiumSupport.util.hasValue(udid) ? udid : ''}${SPLITTER}`;
}
_portAsToken(port) {
return `${SPLITTER}${_appiumSupport.util.hasValue(port) ? port : ''}`;
}
_toKey(udid = null, port = null) {
return `${_appiumSupport.util.hasValue(udid) ? udid : ''}${SPLITTER}${_appiumSupport.util.hasValue(port) ? port : ''}`;
}
_releaseProxiedConnections(connectionKeys) {
const keys = connectionKeys.filter(k => _lodash.default.has(this._connectionsMapping[k], 'iproxy'));
for (const key of keys) {
log.info(`Releasing the listener for '${key}'`);
try {
this._connectionsMapping[key].iproxy.stop();
} catch (e) {
log.debug(e);
}
}
return keys;
}
listConnections(udid = null, port = null, strict = false) {
if (!udid && !port) {
return [];
}
return _lodash.default.keys(this._connectionsMapping).filter(key => strict && udid && port ? key === this._toKey(udid, port) : udid && key.startsWith(this._udidAsToken(udid)) || port && key.endsWith(this._portAsToken(port)));
}
async requestConnection(options = {}) {
const {
udid,
port,
usePortForwarding,
devicePort,
usbmuxdRemoteHost,
usbmuxdRemotePort
} = options;
if (!udid || !port) {
log.warn('Did not know how to request the connection:');
if (!udid) {
log.warn('- Device UDID is unset');
}
if (!port) {
log.warn('- The local port number is unset');
}
return;
}
log.info(`Requesting connection for device ${udid} on local port ${port}` + (devicePort ? `, device port ${devicePort}` : ''));
log.debug(`Cached connections count: ${_lodash.default.size(this._connectionsMapping)}`);
const connectionsOnPort = this.listConnections(null, port);
if (!_lodash.default.isEmpty(connectionsOnPort)) {
log.info(`Found cached connections on port #${port}: ${JSON.stringify(connectionsOnPort)}`);
}
if (usePortForwarding) {
let isPortBusy = (await (0, _portscanner.checkPortStatus)(port, LOCALHOST)) === 'open';
if (isPortBusy) {
log.warn(`Port #${port} is busy. Did you quit the previous driver session(s) properly?`);
if (!_lodash.default.isEmpty(connectionsOnPort)) {
log.info('Trying to release the port');
for (const key of this._releaseProxiedConnections(connectionsOnPort)) {
delete this._connectionsMapping[key];
}
const timer = new _appiumSupport.timing.Timer().start();
try {
await (0, _asyncbox.waitForCondition)(async () => {
try {
if ((await (0, _portscanner.checkPortStatus)(port, LOCALHOST)) !== 'open') {
log.info(`Port #${port} has been successfully released after ` + `${timer.getDuration().asMilliSeconds.toFixed(0)}ms`);
isPortBusy = false;
return true;
}
} catch (ign) {}
return false;
}, {
waitMs: PORT_CLOSE_TIMEOUT,
intervalMs: 300
});
} catch (ign) {
log.warn(`Did not know how to release port #${port} in ` + `${timer.getDuration().asMilliSeconds.toFixed(0)}ms`);
}
}
}
if (isPortBusy) {
throw new Error(`The port #${port} is occupied by an other process. ` + `You can either quit that process or select another free port.`);
}
}
const currentKey = this._toKey(udid, port);
if (usePortForwarding) {
const iproxy = new iProxy(udid, port, devicePort, usbmuxdRemoteHost, usbmuxdRemotePort);
try {
await iproxy.start();
this._connectionsMapping[currentKey] = {
iproxy
};
} catch (e) {
try {
iproxy.stop();
} catch (e1) {
log.debug(e1);
}
throw e;
}
} else {
this._connectionsMapping[currentKey] = {};
}
log.info(`Successfully requested the connection for ${currentKey}`);
}
releaseConnection(udid = null, port = null) {
if (!udid && !port) {
log.warn('Neither device UDID nor local port is set. ' + 'Did not know how to release the connection');
return;
}
log.info(`Releasing connections for ${udid || 'any'} device on ${port || 'any'} port number`);
const keys = this.listConnections(udid, port, true);
if (_lodash.default.isEmpty(keys)) {
log.info('No cached connections have been found');
return;
}
log.info(`Found cached connections to release: ${JSON.stringify(keys)}`);
this._releaseProxiedConnections(keys);
for (const key of keys) {
delete this._connectionsMapping[key];
}
log.debug(`Cached connections count: ${_lodash.default.size(this._connectionsMapping)}`);
}
}
exports.DeviceConnectionsFactory = DeviceConnectionsFactory;
const DEVICE_CONNECTIONS_FACTORY = new DeviceConnectionsFactory();
exports.DEVICE_CONNECTIONS_FACTORY = DEVICE_CONNECTIONS_FACTORY;
var _default = DEVICE_CONNECTIONS_FACTORY;
exports.default = _default;require('source-map-support').install();
//# sourceMappingURL=data:application/json;charset=utf8;base64,eyJ2ZXJzaW9uIjozLCJzb3VyY2VzIjpbImxpYi9kZXZpY2UtY29ubmVjdGlvbnMtZmFjdG9yeS5qcyJdLCJuYW1lcyI6WyJMT0NBTEhPU1QiLCJpUHJveHkiLCJjb25zdHJ1Y3RvciIsInVkaWQiLCJsb2NhbHBvcnQiLCJkZXZpY2Vwb3J0IiwidXNibXV4ZFJlbW90ZUhvc3QiLCJ1c2JtdXhkUmVtb3RlUG9ydCIsInBhcnNlSW50IiwibG9jYWxTZXJ2ZXIiLCJsb2ciLCJsb2dnZXIiLCJnZXRMb2dnZXIiLCJzdWJzdHJpbmciLCJzdGFydCIsIm5ldCIsImNyZWF0ZVNlcnZlciIsImxvY2FsU29ja2V0IiwicmVtb3RlU29ja2V0Iiwib3B0aW9ucyIsInBvcnQiLCJ1dGlsaXRpZXMiLCJjb25uZWN0UG9ydCIsImUiLCJkZWJ1ZyIsIm1lc3NhZ2UiLCJkZXN0cm95IiwiZGVzdHJveUNvbW1DaGFubmVsIiwidW5waXBlIiwib25jZSIsIm9uIiwid2FybiIsInBpcGUiLCJsaXN0ZW5pbmdQcm9taXNlIiwiQiIsInJlc29sdmUiLCJyZWplY3QiLCJsaXN0ZW4iLCJpbmZvIiwib25CZWZvcmVQcm9jZXNzRXhpdCIsIl9jbG9zZUxvY2FsU2VydmVyIiwiYmluZCIsInByb2Nlc3MiLCJjbG9zZSIsInN0b3AiLCJvZmYiLCJQT1JUX0NMT1NFX1RJTUVPVVQiLCJTUExJVFRFUiIsIkRldmljZUNvbm5lY3Rpb25zRmFjdG9yeSIsIl9jb25uZWN0aW9uc01hcHBpbmciLCJfdWRpZEFzVG9rZW4iLCJ1dGlsIiwiaGFzVmFsdWUiLCJfcG9ydEFzVG9rZW4iLCJfdG9LZXkiLCJfcmVsZWFzZVByb3hpZWRDb25uZWN0aW9ucyIsImNvbm5lY3Rpb25LZXlzIiwia2V5cyIsImZpbHRlciIsImsiLCJfIiwiaGFzIiwia2V5IiwiaXByb3h5IiwibGlzdENvbm5lY3Rpb25zIiwic3RyaWN0Iiwic3RhcnRzV2l0aCIsImVuZHNXaXRoIiwicmVxdWVzdENvbm5lY3Rpb24iLCJ1c2VQb3J0Rm9yd2FyZGluZyIsImRldmljZVBvcnQiLCJzaXplIiwiY29ubmVjdGlvbnNPblBvcnQiLCJpc0VtcHR5IiwiSlNPTiIsInN0cmluZ2lmeSIsImlzUG9ydEJ1c3kiLCJ0aW1lciIsInRpbWluZyIsIlRpbWVyIiwiZ2V0RHVyYXRpb24iLCJhc01pbGxpU2Vjb25kcyIsInRvRml4ZWQiLCJpZ24iLCJ3YWl0TXMiLCJpbnRlcnZhbE1zIiwiRXJyb3IiLCJjdXJyZW50S2V5IiwiZTEiLCJyZWxlYXNlQ29ubmVjdGlvbiIsIkRFVklDRV9DT05ORUNUSU9OU19GQUNUT1JZIl0sIm1hcHBpbmdzIjoiOzs7Ozs7Ozs7OztBQUFBOztBQUNBOztBQUNBOztBQUNBOztBQUNBOztBQUNBOztBQUNBOztBQUdBLE1BQU1BLFNBQVMsR0FBRyxXQUFsQjs7QUFFQSxNQUFNQyxNQUFOLENBQWE7QUFDWEMsRUFBQUEsV0FBVyxDQUFFQyxJQUFGLEVBQVFDLFNBQVIsRUFBbUJDLFVBQW5CLEVBQStCQyxpQkFBL0IsRUFBa0RDLGlCQUFsRCxFQUFxRTtBQUM5RSxTQUFLSCxTQUFMLEdBQWlCSSxRQUFRLENBQUNKLFNBQUQsRUFBWSxFQUFaLENBQXpCO0FBQ0EsU0FBS0MsVUFBTCxHQUFrQkcsUUFBUSxDQUFDSCxVQUFELEVBQWEsRUFBYixDQUExQjtBQUNBLFNBQUtGLElBQUwsR0FBWUEsSUFBWjtBQUNBLFNBQUtNLFdBQUwsR0FBbUIsSUFBbkI7QUFDQSxTQUFLQyxHQUFMLEdBQVdDLHNCQUFPQyxTQUFQLENBQWtCLFVBQVNULElBQUksQ0FBQ1UsU0FBTCxDQUFlLENBQWYsRUFBa0IsQ0FBbEIsQ0FBcUIsSUFBRyxLQUFLVCxTQUFVLEVBQWxFLENBQVg7QUFDQSxTQUFLRSxpQkFBTCxHQUF5QkEsaUJBQXpCO0FBQ0EsU0FBS0MsaUJBQUwsR0FBeUJBLGlCQUF6QjtBQUNEOztBQUVELFFBQU1PLEtBQU4sR0FBZTtBQUNiLFFBQUksS0FBS0wsV0FBVCxFQUFzQjtBQUNwQjtBQUNEOztBQUVELFNBQUtBLFdBQUwsR0FBbUJNLGFBQUlDLFlBQUosQ0FBaUIsTUFBT0MsV0FBUCxJQUF1QjtBQUN6RCxVQUFJQyxZQUFKOztBQUNBLFVBQUk7QUFFRixZQUFJQyxPQUFPLEdBQUc7QUFDWmhCLFVBQUFBLElBQUksRUFBRSxLQUFLQSxJQURDO0FBRVppQixVQUFBQSxJQUFJLEVBQUUsS0FBS2YsVUFGQztBQUdaQyxVQUFBQSxpQkFBaUIsRUFBRSxLQUFLQSxpQkFIWjtBQUlaQyxVQUFBQSxpQkFBaUIsRUFBRSxLQUFLQTtBQUpaLFNBQWQ7QUFNQVcsUUFBQUEsWUFBWSxHQUFHLE1BQU1HLDRCQUFVQyxXQUFWLENBQXNCLElBQXRCLEVBQTRCSCxPQUE1QixDQUFyQjtBQUNELE9BVEQsQ0FTRSxPQUFPSSxDQUFQLEVBQVU7QUFDVixhQUFLYixHQUFMLENBQVNjLEtBQVQsQ0FBZUQsQ0FBQyxDQUFDRSxPQUFqQjtBQUNBUixRQUFBQSxXQUFXLENBQUNTLE9BQVo7QUFDQTtBQUNEOztBQUVELFlBQU1DLGtCQUFrQixHQUFHLE1BQU07QUFDL0JULFFBQUFBLFlBQVksQ0FBQ1UsTUFBYixDQUFvQlgsV0FBcEI7QUFDQUEsUUFBQUEsV0FBVyxDQUFDVyxNQUFaLENBQW1CVixZQUFuQjtBQUNELE9BSEQ7O0FBSUFBLE1BQUFBLFlBQVksQ0FBQ1csSUFBYixDQUFrQixPQUFsQixFQUEyQixNQUFNO0FBQy9CRixRQUFBQSxrQkFBa0I7QUFDbEJWLFFBQUFBLFdBQVcsQ0FBQ1MsT0FBWjtBQUNELE9BSEQ7QUFLQVIsTUFBQUEsWUFBWSxDQUFDWSxFQUFiLENBQWdCLE9BQWhCLEVBQTBCUCxDQUFELElBQU8sS0FBS2IsR0FBTCxDQUFTYyxLQUFULENBQWVELENBQWYsQ0FBaEM7QUFDQU4sTUFBQUEsV0FBVyxDQUFDWSxJQUFaLENBQWlCLEtBQWpCLEVBQXdCRixrQkFBeEI7QUFDQVYsTUFBQUEsV0FBVyxDQUFDWSxJQUFaLENBQWlCLE9BQWpCLEVBQTBCLE1BQU07QUFDOUJGLFFBQUFBLGtCQUFrQjtBQUNsQlQsUUFBQUEsWUFBWSxDQUFDUSxPQUFiO0FBQ0QsT0FIRDtBQUlBVCxNQUFBQSxXQUFXLENBQUNhLEVBQVosQ0FBZSxPQUFmLEVBQXlCUCxDQUFELElBQU8sS0FBS2IsR0FBTCxDQUFTcUIsSUFBVCxDQUFjUixDQUFDLENBQUNFLE9BQWhCLENBQS9CO0FBQ0FSLE1BQUFBLFdBQVcsQ0FBQ2UsSUFBWixDQUFpQmQsWUFBakI7QUFDQUEsTUFBQUEsWUFBWSxDQUFDYyxJQUFiLENBQWtCZixXQUFsQjtBQUNELEtBbkNrQixDQUFuQjtBQW9DQSxVQUFNZ0IsZ0JBQWdCLEdBQUcsSUFBSUMsaUJBQUosQ0FBTSxDQUFDQyxPQUFELEVBQVVDLE1BQVYsS0FBcUI7QUFDbEQsV0FBSzNCLFdBQUwsQ0FBaUJvQixJQUFqQixDQUFzQixXQUF0QixFQUFtQ00sT0FBbkM7QUFDQSxXQUFLMUIsV0FBTCxDQUFpQm9CLElBQWpCLENBQXNCLE9BQXRCLEVBQStCTyxNQUEvQjtBQUNELEtBSHdCLENBQXpCO0FBSUEsU0FBSzNCLFdBQUwsQ0FBaUI0QixNQUFqQixDQUF3QixLQUFLakMsU0FBN0I7O0FBQ0EsUUFBSTtBQUNGLFlBQU02QixnQkFBTjtBQUNELEtBRkQsQ0FFRSxPQUFPVixDQUFQLEVBQVU7QUFDVixXQUFLZCxXQUFMLEdBQW1CLElBQW5CO0FBQ0EsWUFBTWMsQ0FBTjtBQUNEOztBQUNELFNBQUtkLFdBQUwsQ0FBaUJxQixFQUFqQixDQUFvQixPQUFwQixFQUE4QlAsQ0FBRCxJQUFPLEtBQUtiLEdBQUwsQ0FBU3FCLElBQVQsQ0FBY1IsQ0FBQyxDQUFDRSxPQUFoQixDQUFwQztBQUNBLFNBQUtoQixXQUFMLENBQWlCb0IsSUFBakIsQ0FBc0IsT0FBdEIsRUFBZ0NOLENBQUQsSUFBTztBQUNwQyxVQUFJQSxDQUFKLEVBQU87QUFDTCxhQUFLYixHQUFMLENBQVM0QixJQUFULENBQWUsNkNBQTRDZixDQUFDLENBQUNFLE9BQVEsRUFBckU7QUFDRCxPQUZELE1BRU87QUFDTCxhQUFLZixHQUFMLENBQVM0QixJQUFULENBQWUsZ0NBQWY7QUFDRDs7QUFDRCxXQUFLN0IsV0FBTCxHQUFtQixJQUFuQjtBQUNELEtBUEQ7QUFTQSxTQUFLOEIsbUJBQUwsR0FBMkIsS0FBS0MsaUJBQUwsQ0FBdUJDLElBQXZCLENBQTRCLElBQTVCLENBQTNCO0FBRUFDLElBQUFBLE9BQU8sQ0FBQ1osRUFBUixDQUFXLFlBQVgsRUFBeUIsS0FBS1MsbUJBQTlCO0FBQ0Q7O0FBRURDLEVBQUFBLGlCQUFpQixHQUFJO0FBQ25CLFFBQUksQ0FBQyxLQUFLL0IsV0FBVixFQUF1QjtBQUNyQjtBQUNEOztBQUVELFNBQUtDLEdBQUwsQ0FBU2MsS0FBVCxDQUFnQix3QkFBaEI7QUFDQSxTQUFLZixXQUFMLENBQWlCa0MsS0FBakI7QUFDQSxTQUFLbEMsV0FBTCxHQUFtQixJQUFuQjtBQUNEOztBQUVEbUMsRUFBQUEsSUFBSSxHQUFJO0FBQ04sUUFBSSxLQUFLTCxtQkFBVCxFQUE4QjtBQUM1QkcsTUFBQUEsT0FBTyxDQUFDRyxHQUFSLENBQVksWUFBWixFQUEwQixLQUFLTixtQkFBL0I7QUFDQSxXQUFLQSxtQkFBTCxHQUEyQixJQUEzQjtBQUNEOztBQUVELFNBQUtDLGlCQUFMO0FBQ0Q7O0FBL0ZVOztBQW1HYixNQUFNOUIsR0FBRyxHQUFHQyxzQkFBT0MsU0FBUCxDQUFpQixnQkFBakIsQ0FBWjs7QUFDQSxNQUFNa0Msa0JBQWtCLEdBQUcsS0FBSyxJQUFoQztBQUNBLE1BQU1DLFFBQVEsR0FBRyxHQUFqQjs7QUFFQSxNQUFNQyx3QkFBTixDQUErQjtBQUM3QjlDLEVBQUFBLFdBQVcsR0FBSTtBQUNiLFNBQUsrQyxtQkFBTCxHQUEyQixFQUEzQjtBQUNEOztBQUVEQyxFQUFBQSxZQUFZLENBQUUvQyxJQUFGLEVBQVE7QUFDbEIsV0FBUSxHQUFFZ0Qsb0JBQUtDLFFBQUwsQ0FBY2pELElBQWQsSUFBc0JBLElBQXRCLEdBQTZCLEVBQUcsR0FBRTRDLFFBQVMsRUFBckQ7QUFDRDs7QUFFRE0sRUFBQUEsWUFBWSxDQUFFakMsSUFBRixFQUFRO0FBQ2xCLFdBQVEsR0FBRTJCLFFBQVMsR0FBRUksb0JBQUtDLFFBQUwsQ0FBY2hDLElBQWQsSUFBc0JBLElBQXRCLEdBQTZCLEVBQUcsRUFBckQ7QUFDRDs7QUFFRGtDLEVBQUFBLE1BQU0sQ0FBRW5ELElBQUksR0FBRyxJQUFULEVBQWVpQixJQUFJLEdBQUcsSUFBdEIsRUFBNEI7QUFDaEMsV0FBUSxHQUFFK0Isb0JBQUtDLFFBQUwsQ0FBY2pELElBQWQsSUFBc0JBLElBQXRCLEdBQTZCLEVBQUcsR0FBRTRDLFFBQVMsR0FBRUksb0JBQUtDLFFBQUwsQ0FBY2hDLElBQWQsSUFBc0JBLElBQXRCLEdBQTZCLEVBQUcsRUFBdkY7QUFDRDs7QUFFRG1DLEVBQUFBLDBCQUEwQixDQUFFQyxjQUFGLEVBQWtCO0FBQzFDLFVBQU1DLElBQUksR0FBR0QsY0FBYyxDQUN4QkUsTUFEVSxDQUNGQyxDQUFELElBQU9DLGdCQUFFQyxHQUFGLENBQU0sS0FBS1osbUJBQUwsQ0FBeUJVLENBQXpCLENBQU4sRUFBbUMsUUFBbkMsQ0FESixDQUFiOztBQUVBLFNBQUssTUFBTUcsR0FBWCxJQUFrQkwsSUFBbEIsRUFBd0I7QUFDdEIvQyxNQUFBQSxHQUFHLENBQUM0QixJQUFKLENBQVUsK0JBQThCd0IsR0FBSSxHQUE1Qzs7QUFDQSxVQUFJO0FBQ0YsYUFBS2IsbUJBQUwsQ0FBeUJhLEdBQXpCLEVBQThCQyxNQUE5QixDQUFxQ25CLElBQXJDO0FBQ0QsT0FGRCxDQUVFLE9BQU9yQixDQUFQLEVBQVU7QUFDVmIsUUFBQUEsR0FBRyxDQUFDYyxLQUFKLENBQVVELENBQVY7QUFDRDtBQUNGOztBQUNELFdBQU9rQyxJQUFQO0FBQ0Q7O0FBRURPLEVBQUFBLGVBQWUsQ0FBRTdELElBQUksR0FBRyxJQUFULEVBQWVpQixJQUFJLEdBQUcsSUFBdEIsRUFBNEI2QyxNQUFNLEdBQUcsS0FBckMsRUFBNEM7QUFDekQsUUFBSSxDQUFDOUQsSUFBRCxJQUFTLENBQUNpQixJQUFkLEVBQW9CO0FBQ2xCLGFBQU8sRUFBUDtBQUNEOztBQU1ELFdBQU93QyxnQkFBRUgsSUFBRixDQUFPLEtBQUtSLG1CQUFaLEVBQ0pTLE1BREksQ0FDSUksR0FBRCxJQUFVRyxNQUFNLElBQUk5RCxJQUFWLElBQWtCaUIsSUFBbkIsR0FDWjBDLEdBQUcsS0FBSyxLQUFLUixNQUFMLENBQVluRCxJQUFaLEVBQWtCaUIsSUFBbEIsQ0FESSxHQUVaakIsSUFBSSxJQUFJMkQsR0FBRyxDQUFDSSxVQUFKLENBQWUsS0FBS2hCLFlBQUwsQ0FBa0IvQyxJQUFsQixDQUFmLENBQVIsSUFBbURpQixJQUFJLElBQUkwQyxHQUFHLENBQUNLLFFBQUosQ0FBYSxLQUFLZCxZQUFMLENBQWtCakMsSUFBbEIsQ0FBYixDQUgzRCxDQUFQO0FBS0Q7O0FBRUQsUUFBTWdELGlCQUFOLENBQXlCakQsT0FBTyxHQUFHLEVBQW5DLEVBQXVDO0FBQ3JDLFVBQU07QUFDSmhCLE1BQUFBLElBREk7QUFFSmlCLE1BQUFBLElBRkk7QUFHSmlELE1BQUFBLGlCQUhJO0FBSUpDLE1BQUFBLFVBSkk7QUFLSmhFLE1BQUFBLGlCQUxJO0FBTUpDLE1BQUFBO0FBTkksUUFPRlksT0FQSjs7QUFTQSxRQUFJLENBQUNoQixJQUFELElBQVMsQ0FBQ2lCLElBQWQsRUFBb0I7QUFDbEJWLE1BQUFBLEdBQUcsQ0FBQ3FCLElBQUosQ0FBUyw2Q0FBVDs7QUFDQSxVQUFJLENBQUM1QixJQUFMLEVBQVc7QUFDVE8sUUFBQUEsR0FBRyxDQUFDcUIsSUFBSixDQUFTLHdCQUFUO0FBQ0Q7O0FBQ0QsVUFBSSxDQUFDWCxJQUFMLEVBQVc7QUFDVFYsUUFBQUEsR0FBRyxDQUFDcUIsSUFBSixDQUFTLGtDQUFUO0FBQ0Q7O0FBQ0Q7QUFDRDs7QUFFRHJCLElBQUFBLEdBQUcsQ0FBQzRCLElBQUosQ0FBVSxvQ0FBbUNuQyxJQUFLLGtCQUFpQmlCLElBQUssRUFBL0QsSUFDTmtELFVBQVUsR0FBSSxpQkFBZ0JBLFVBQVcsRUFBL0IsR0FBbUMsRUFEdkMsQ0FBVDtBQUVBNUQsSUFBQUEsR0FBRyxDQUFDYyxLQUFKLENBQVcsNkJBQTRCb0MsZ0JBQUVXLElBQUYsQ0FBTyxLQUFLdEIsbUJBQVosQ0FBaUMsRUFBeEU7QUFDQSxVQUFNdUIsaUJBQWlCLEdBQUcsS0FBS1IsZUFBTCxDQUFxQixJQUFyQixFQUEyQjVDLElBQTNCLENBQTFCOztBQUNBLFFBQUksQ0FBQ3dDLGdCQUFFYSxPQUFGLENBQVVELGlCQUFWLENBQUwsRUFBbUM7QUFDakM5RCxNQUFBQSxHQUFHLENBQUM0QixJQUFKLENBQVUscUNBQW9DbEIsSUFBSyxLQUFJc0QsSUFBSSxDQUFDQyxTQUFMLENBQWVILGlCQUFmLENBQWtDLEVBQXpGO0FBQ0Q7O0FBRUQsUUFBSUgsaUJBQUosRUFBdUI7QUFDckIsVUFBSU8sVUFBVSxHQUFHLENBQUMsTUFBTSxrQ0FBZ0J4RCxJQUFoQixFQUFzQnBCLFNBQXRCLENBQVAsTUFBNkMsTUFBOUQ7O0FBQ0EsVUFBSTRFLFVBQUosRUFBZ0I7QUFDZGxFLFFBQUFBLEdBQUcsQ0FBQ3FCLElBQUosQ0FBVSxTQUFRWCxJQUFLLGlFQUF2Qjs7QUFDQSxZQUFJLENBQUN3QyxnQkFBRWEsT0FBRixDQUFVRCxpQkFBVixDQUFMLEVBQW1DO0FBQ2pDOUQsVUFBQUEsR0FBRyxDQUFDNEIsSUFBSixDQUFTLDRCQUFUOztBQUNBLGVBQUssTUFBTXdCLEdBQVgsSUFBa0IsS0FBS1AsMEJBQUwsQ0FBZ0NpQixpQkFBaEMsQ0FBbEIsRUFBc0U7QUFDcEUsbUJBQU8sS0FBS3ZCLG1CQUFMLENBQXlCYSxHQUF6QixDQUFQO0FBQ0Q7O0FBQ0QsZ0JBQU1lLEtBQUssR0FBRyxJQUFJQyxzQkFBT0MsS0FBWCxHQUFtQmpFLEtBQW5CLEVBQWQ7O0FBQ0EsY0FBSTtBQUNGLGtCQUFNLGdDQUFpQixZQUFZO0FBQ2pDLGtCQUFJO0FBQ0Ysb0JBQUksQ0FBQyxNQUFNLGtDQUFnQk0sSUFBaEIsRUFBc0JwQixTQUF0QixDQUFQLE1BQTZDLE1BQWpELEVBQXlEO0FBQ3ZEVSxrQkFBQUEsR0FBRyxDQUFDNEIsSUFBSixDQUFVLFNBQVFsQixJQUFLLHdDQUFkLEdBQ04sR0FBRXlELEtBQUssQ0FBQ0csV0FBTixHQUFvQkMsY0FBcEIsQ0FBbUNDLE9BQW5DLENBQTJDLENBQTNDLENBQThDLElBRG5EO0FBRUFOLGtCQUFBQSxVQUFVLEdBQUcsS0FBYjtBQUNBLHlCQUFPLElBQVA7QUFDRDtBQUNGLGVBUEQsQ0FPRSxPQUFPTyxHQUFQLEVBQVksQ0FBRTs7QUFDaEIscUJBQU8sS0FBUDtBQUNELGFBVkssRUFVSDtBQUNEQyxjQUFBQSxNQUFNLEVBQUV0QyxrQkFEUDtBQUVEdUMsY0FBQUEsVUFBVSxFQUFFO0FBRlgsYUFWRyxDQUFOO0FBY0QsV0FmRCxDQWVFLE9BQU9GLEdBQVAsRUFBWTtBQUNaekUsWUFBQUEsR0FBRyxDQUFDcUIsSUFBSixDQUFVLHFDQUFvQ1gsSUFBSyxNQUExQyxHQUNOLEdBQUV5RCxLQUFLLENBQUNHLFdBQU4sR0FBb0JDLGNBQXBCLENBQW1DQyxPQUFuQyxDQUEyQyxDQUEzQyxDQUE4QyxJQURuRDtBQUVEO0FBQ0Y7QUFDRjs7QUFFRCxVQUFJTixVQUFKLEVBQWdCO0FBQ2QsY0FBTSxJQUFJVSxLQUFKLENBQVcsYUFBWWxFLElBQUssb0NBQWxCLEdBQ2IsK0RBREcsQ0FBTjtBQUVEO0FBQ0Y7O0FBQ0QsVUFBTW1FLFVBQVUsR0FBRyxLQUFLakMsTUFBTCxDQUFZbkQsSUFBWixFQUFrQmlCLElBQWxCLENBQW5COztBQUNBLFFBQUlpRCxpQkFBSixFQUF1QjtBQUNyQixZQUFNTixNQUFNLEdBQUcsSUFBSTlELE1BQUosQ0FBV0UsSUFBWCxFQUFpQmlCLElBQWpCLEVBQXVCa0QsVUFBdkIsRUFBbUNoRSxpQkFBbkMsRUFBc0RDLGlCQUF0RCxDQUFmOztBQUNBLFVBQUk7QUFDRixjQUFNd0QsTUFBTSxDQUFDakQsS0FBUCxFQUFOO0FBQ0EsYUFBS21DLG1CQUFMLENBQXlCc0MsVUFBekIsSUFBdUM7QUFBQ3hCLFVBQUFBO0FBQUQsU0FBdkM7QUFDRCxPQUhELENBR0UsT0FBT3hDLENBQVAsRUFBVTtBQUNWLFlBQUk7QUFDRndDLFVBQUFBLE1BQU0sQ0FBQ25CLElBQVA7QUFDRCxTQUZELENBRUUsT0FBTzRDLEVBQVAsRUFBVztBQUNYOUUsVUFBQUEsR0FBRyxDQUFDYyxLQUFKLENBQVVnRSxFQUFWO0FBQ0Q7O0FBQ0QsY0FBTWpFLENBQU47QUFDRDtBQUNGLEtBYkQsTUFhTztBQUNMLFdBQUswQixtQkFBTCxDQUF5QnNDLFVBQXpCLElBQXVDLEVBQXZDO0FBQ0Q7O0FBQ0Q3RSxJQUFBQSxHQUFHLENBQUM0QixJQUFKLENBQVUsNkNBQTRDaUQsVUFBVyxFQUFqRTtBQUNEOztBQUVERSxFQUFBQSxpQkFBaUIsQ0FBRXRGLElBQUksR0FBRyxJQUFULEVBQWVpQixJQUFJLEdBQUcsSUFBdEIsRUFBNEI7QUFDM0MsUUFBSSxDQUFDakIsSUFBRCxJQUFTLENBQUNpQixJQUFkLEVBQW9CO0FBQ2xCVixNQUFBQSxHQUFHLENBQUNxQixJQUFKLENBQVMsZ0RBQ1AsNENBREY7QUFFQTtBQUNEOztBQUNEckIsSUFBQUEsR0FBRyxDQUFDNEIsSUFBSixDQUFVLDZCQUE0Qm5DLElBQUksSUFBSSxLQUFNLGNBQWFpQixJQUFJLElBQUksS0FBTSxjQUEvRTtBQUVBLFVBQU1xQyxJQUFJLEdBQUcsS0FBS08sZUFBTCxDQUFxQjdELElBQXJCLEVBQTJCaUIsSUFBM0IsRUFBaUMsSUFBakMsQ0FBYjs7QUFDQSxRQUFJd0MsZ0JBQUVhLE9BQUYsQ0FBVWhCLElBQVYsQ0FBSixFQUFxQjtBQUNuQi9DLE1BQUFBLEdBQUcsQ0FBQzRCLElBQUosQ0FBUyx1Q0FBVDtBQUNBO0FBQ0Q7O0FBQ0Q1QixJQUFBQSxHQUFHLENBQUM0QixJQUFKLENBQVUsd0NBQXVDb0MsSUFBSSxDQUFDQyxTQUFMLENBQWVsQixJQUFmLENBQXFCLEVBQXRFOztBQUNBLFNBQUtGLDBCQUFMLENBQWdDRSxJQUFoQzs7QUFDQSxTQUFLLE1BQU1LLEdBQVgsSUFBa0JMLElBQWxCLEVBQXdCO0FBQ3RCLGFBQU8sS0FBS1IsbUJBQUwsQ0FBeUJhLEdBQXpCLENBQVA7QUFDRDs7QUFDRHBELElBQUFBLEdBQUcsQ0FBQ2MsS0FBSixDQUFXLDZCQUE0Qm9DLGdCQUFFVyxJQUFGLENBQU8sS0FBS3RCLG1CQUFaLENBQWlDLEVBQXhFO0FBQ0Q7O0FBeEo0Qjs7O0FBMkovQixNQUFNeUMsMEJBQTBCLEdBQUcsSUFBSTFDLHdCQUFKLEVBQW5DOztlQUdlMEMsMEIiLCJzb3VyY2VzQ29udGVudCI6WyJpbXBvcnQgXyBmcm9tICdsb2Rhc2gnO1xuaW1wb3J0IG5ldCBmcm9tICduZXQnO1xuaW1wb3J0IEIgZnJvbSAnYmx1ZWJpcmQnO1xuaW1wb3J0IHsgbG9nZ2VyLCB1dGlsLCB0aW1pbmcgfSBmcm9tICdhcHBpdW0tc3VwcG9ydCc7XG5pbXBvcnQgeyB1dGlsaXRpZXMgfSBmcm9tICdnc3QtYXRvbS1pb3MtZGV2aWNlJztcbmltcG9ydCB7IGNoZWNrUG9ydFN0YXR1cyB9IGZyb20gJ3BvcnRzY2FubmVyJztcbmltcG9ydCB7IHdhaXRGb3JDb25kaXRpb24gfSBmcm9tICdhc3luY2JveCc7XG5cblxuY29uc3QgTE9DQUxIT1NUID0gJzEyNy4wLjAuMSc7XG5cbmNsYXNzIGlQcm94eSB7XG4gIGNvbnN0cnVjdG9yICh1ZGlkLCBsb2NhbHBvcnQsIGRldmljZXBvcnQsIHVzYm11eGRSZW1vdGVIb3N0LCB1c2JtdXhkUmVtb3RlUG9ydCkge1xuICAgIHRoaXMubG9jYWxwb3J0ID0gcGFyc2VJbnQobG9jYWxwb3J0LCAxMCk7XG4gICAgdGhpcy5kZXZpY2Vwb3J0ID0gcGFyc2VJbnQoZGV2aWNlcG9ydCwgMTApO1xuICAgIHRoaXMudWRpZCA9IHVkaWQ7XG4gICAgdGhpcy5sb2NhbFNlcnZlciA9IG51bGw7XG4gICAgdGhpcy5sb2cgPSBsb2dnZXIuZ2V0TG9nZ2VyKGBpUHJveHlAJHt1ZGlkLnN1YnN0cmluZygwLCA4KX06JHt0aGlzLmxvY2FscG9ydH1gKTtcbiAgICB0aGlzLnVzYm11eGRSZW1vdGVIb3N0ID0gdXNibXV4ZFJlbW90ZUhvc3Q7XG4gICAgdGhpcy51c2JtdXhkUmVtb3RlUG9ydCA9IHVzYm11eGRSZW1vdGVQb3J0O1xuICB9XG5cbiAgYXN5bmMgc3RhcnQgKCkge1xuICAgIGlmICh0aGlzLmxvY2FsU2VydmVyKSB7XG4gICAgICByZXR1cm47XG4gICAgfVxuXG4gICAgdGhpcy5sb2NhbFNlcnZlciA9IG5ldC5jcmVhdGVTZXJ2ZXIoYXN5bmMgKGxvY2FsU29ja2V0KSA9PiB7XG4gICAgICBsZXQgcmVtb3RlU29ja2V0O1xuICAgICAgdHJ5IHtcbiAgICAgICAgLy8gV2UgY2FuIG9ubHkgY29ubmVjdCB0byB0aGUgcmVtb3RlIHNvY2tldCBhZnRlciB0aGUgbG9jYWwgc29ja2V0IGNvbm5lY3Rpb24gc3VjY2VlZHNcbiAgICAgICAgdmFyIG9wdGlvbnMgPSB7XG4gICAgICAgICAgdWRpZDogdGhpcy51ZGlkLFxuICAgICAgICAgIHBvcnQ6IHRoaXMuZGV2aWNlcG9ydCxcbiAgICAgICAgICB1c2JtdXhkUmVtb3RlSG9zdDogdGhpcy51c2JtdXhkUmVtb3RlSG9zdCxcbiAgICAgICAgICB1c2JtdXhkUmVtb3RlUG9ydDogdGhpcy51c2JtdXhkUmVtb3RlUG9ydFxuICAgICAgICB9ICAgICAgICBcbiAgICAgICAgcmVtb3RlU29ja2V0ID0gYXdhaXQgdXRpbGl0aWVzLmNvbm5lY3RQb3J0KG51bGwsIG9wdGlvbnMpO1xuICAgICAgfSBjYXRjaCAoZSkge1xuICAgICAgICB0aGlzLmxvZy5kZWJ1ZyhlLm1lc3NhZ2UpO1xuICAgICAgICBsb2NhbFNvY2tldC5kZXN0cm95KCk7XG4gICAgICAgIHJldHVybjtcbiAgICAgIH1cblxuICAgICAgY29uc3QgZGVzdHJveUNvbW1DaGFubmVsID0gKCkgPT4ge1xuICAgICAgICByZW1vdGVTb2NrZXQudW5waXBlKGxvY2FsU29ja2V0KTtcbiAgICAgICAgbG9jYWxTb2NrZXQudW5waXBlKHJlbW90ZVNvY2tldCk7XG4gICAgICB9O1xuICAgICAgcmVtb3RlU29ja2V0Lm9uY2UoJ2Nsb3NlJywgKCkgPT4ge1xuICAgICAgICBkZXN0cm95Q29tbUNoYW5uZWwoKTtcbiAgICAgICAgbG9jYWxTb2NrZXQuZGVzdHJveSgpO1xuICAgICAgfSk7XG4gICAgICAvLyBub3QgYWxsIHJlbW90ZSBzb2NrZXQgZXJyb3JzIGFyZSBjcml0aWNhbCBmb3IgdGhlIHVzZXJcbiAgICAgIHJlbW90ZVNvY2tldC5vbignZXJyb3InLCAoZSkgPT4gdGhpcy5sb2cuZGVidWcoZSkpO1xuICAgICAgbG9jYWxTb2NrZXQub25jZSgnZW5kJywgZGVzdHJveUNvbW1DaGFubmVsKTtcbiAgICAgIGxvY2FsU29ja2V0Lm9uY2UoJ2Nsb3NlJywgKCkgPT4ge1xuICAgICAgICBkZXN0cm95Q29tbUNoYW5uZWwoKTtcbiAgICAgICAgcmVtb3RlU29ja2V0LmRlc3Ryb3koKTtcbiAgICAgIH0pO1xuICAgICAgbG9jYWxTb2NrZXQub24oJ2Vycm9yJywgKGUpID0+IHRoaXMubG9nLndhcm4oZS5tZXNzYWdlKSk7XG4gICAgICBsb2NhbFNvY2tldC5waXBlKHJlbW90ZVNvY2tldCk7XG4gICAgICByZW1vdGVTb2NrZXQucGlwZShsb2NhbFNvY2tldCk7XG4gICAgfSk7XG4gICAgY29uc3QgbGlzdGVuaW5nUHJvbWlzZSA9IG5ldyBCKChyZXNvbHZlLCByZWplY3QpID0+IHtcbiAgICAgIHRoaXMubG9jYWxTZXJ2ZXIub25jZSgnbGlzdGVuaW5nJywgcmVzb2x2ZSk7XG4gICAgICB0aGlzLmxvY2FsU2VydmVyLm9uY2UoJ2Vycm9yJywgcmVqZWN0KTtcbiAgICB9KTtcbiAgICB0aGlzLmxvY2FsU2VydmVyLmxpc3Rlbih0aGlzLmxvY2FscG9ydCk7XG4gICAgdHJ5IHtcbiAgICAgIGF3YWl0IGxpc3RlbmluZ1Byb21pc2U7XG4gICAgfSBjYXRjaCAoZSkge1xuICAgICAgdGhpcy5sb2NhbFNlcnZlciA9IG51bGw7XG4gICAgICB0aHJvdyBlO1xuICAgIH1cbiAgICB0aGlzLmxvY2FsU2VydmVyLm9uKCdlcnJvcicsIChlKSA9PiB0aGlzLmxvZy53YXJuKGUubWVzc2FnZSkpO1xuICAgIHRoaXMubG9jYWxTZXJ2ZXIub25jZSgnY2xvc2UnLCAoZSkgPT4ge1xuICAgICAgaWYgKGUpIHtcbiAgICAgICAgdGhpcy5sb2cuaW5mbyhgVGhlIGNvbm5lY3Rpb24gaGFzIGJlZW4gY2xvc2VkIHdpdGggZXJyb3IgJHtlLm1lc3NhZ2V9YCk7XG4gICAgICB9IGVsc2Uge1xuICAgICAgICB0aGlzLmxvZy5pbmZvKGBUaGUgY29ubmVjdGlvbiBoYXMgYmVlbiBjbG9zZWRgKTtcbiAgICAgIH1cbiAgICAgIHRoaXMubG9jYWxTZXJ2ZXIgPSBudWxsO1xuICAgIH0pO1xuXG4gICAgdGhpcy5vbkJlZm9yZVByb2Nlc3NFeGl0ID0gdGhpcy5fY2xvc2VMb2NhbFNlcnZlci5iaW5kKHRoaXMpO1xuICAgIC8vIE1ha2Ugc3VyZSB3ZSBmcmVlIHVwIHRoZSBzb2NrZXQgb24gcHJvY2VzcyBleGl0XG4gICAgcHJvY2Vzcy5vbignYmVmb3JlRXhpdCcsIHRoaXMub25CZWZvcmVQcm9jZXNzRXhpdCk7XG4gIH1cblxuICBfY2xvc2VMb2NhbFNlcnZlciAoKSB7XG4gICAgaWYgKCF0aGlzLmxvY2FsU2VydmVyKSB7XG4gICAgICByZXR1cm47XG4gICAgfVxuXG4gICAgdGhpcy5sb2cuZGVidWcoYENsb3NpbmcgdGhlIGNvbm5lY3Rpb25gKTtcbiAgICB0aGlzLmxvY2FsU2VydmVyLmNsb3NlKCk7XG4gICAgdGhpcy5sb2NhbFNlcnZlciA9IG51bGw7XG4gIH1cblxuICBzdG9wICgpIHtcbiAgICBpZiAodGhpcy5vbkJlZm9yZVByb2Nlc3NFeGl0KSB7XG4gICAgICBwcm9jZXNzLm9mZignYmVmb3JlRXhpdCcsIHRoaXMub25CZWZvcmVQcm9jZXNzRXhpdCk7XG4gICAgICB0aGlzLm9uQmVmb3JlUHJvY2Vzc0V4aXQgPSBudWxsO1xuICAgIH1cblxuICAgIHRoaXMuX2Nsb3NlTG9jYWxTZXJ2ZXIoKTtcbiAgfVxufVxuXG5cbmNvbnN0IGxvZyA9IGxvZ2dlci5nZXRMb2dnZXIoJ0RldkNvbiBGYWN0b3J5Jyk7XG5jb25zdCBQT1JUX0NMT1NFX1RJTUVPVVQgPSAxNSAqIDEwMDA7IC8vIDE1IHNlY29uZHNcbmNvbnN0IFNQTElUVEVSID0gJzonO1xuXG5jbGFzcyBEZXZpY2VDb25uZWN0aW9uc0ZhY3Rvcnkge1xuICBjb25zdHJ1Y3RvciAoKSB7XG4gICAgdGhpcy5fY29ubmVjdGlvbnNNYXBwaW5nID0ge307XG4gIH1cblxuICBfdWRpZEFzVG9rZW4gKHVkaWQpIHtcbiAgICByZXR1cm4gYCR7dXRpbC5oYXNWYWx1ZSh1ZGlkKSA/IHVkaWQgOiAnJ30ke1NQTElUVEVSfWA7XG4gIH1cblxuICBfcG9ydEFzVG9rZW4gKHBvcnQpIHtcbiAgICByZXR1cm4gYCR7U1BMSVRURVJ9JHt1dGlsLmhhc1ZhbHVlKHBvcnQpID8gcG9ydCA6ICcnfWA7XG4gIH1cblxuICBfdG9LZXkgKHVkaWQgPSBudWxsLCBwb3J0ID0gbnVsbCkge1xuICAgIHJldHVybiBgJHt1dGlsLmhhc1ZhbHVlKHVkaWQpID8gdWRpZCA6ICcnfSR7U1BMSVRURVJ9JHt1dGlsLmhhc1ZhbHVlKHBvcnQpID8gcG9ydCA6ICcnfWA7XG4gIH1cblxuICBfcmVsZWFzZVByb3hpZWRDb25uZWN0aW9ucyAoY29ubmVjdGlvbktleXMpIHtcbiAgICBjb25zdCBrZXlzID0gY29ubmVjdGlvbktleXNcbiAgICAgIC5maWx0ZXIoKGspID0+IF8uaGFzKHRoaXMuX2Nvbm5lY3Rpb25zTWFwcGluZ1trXSwgJ2lwcm94eScpKTtcbiAgICBmb3IgKGNvbnN0IGtleSBvZiBrZXlzKSB7XG4gICAgICBsb2cuaW5mbyhgUmVsZWFzaW5nIHRoZSBsaXN0ZW5lciBmb3IgJyR7a2V5fSdgKTtcbiAgICAgIHRyeSB7XG4gICAgICAgIHRoaXMuX2Nvbm5lY3Rpb25zTWFwcGluZ1trZXldLmlwcm94eS5zdG9wKCk7XG4gICAgICB9IGNhdGNoIChlKSB7XG4gICAgICAgIGxvZy5kZWJ1ZyhlKTtcbiAgICAgIH1cbiAgICB9XG4gICAgcmV0dXJuIGtleXM7XG4gIH1cblxuICBsaXN0Q29ubmVjdGlvbnMgKHVkaWQgPSBudWxsLCBwb3J0ID0gbnVsbCwgc3RyaWN0ID0gZmFsc2UpIHtcbiAgICBpZiAoIXVkaWQgJiYgIXBvcnQpIHtcbiAgICAgIHJldHVybiBbXTtcbiAgICB9XG5cbiAgICAvLyBgdGhpcy5fY29ubmVjdGlvbk1hcHBpbmdgIGtleXMgaGF2ZSBmb3JtYXQgYHVkaWQ6cG9ydGBcbiAgICAvLyB0aGUgYHN0cmljdGAgYXJndW1lbnQgZW5mb3JjZXMgdG8gbWF0Y2gga2V5cyBoYXZpbmcgYm90aCBgdWRpZGAgYW5kIGBwb3J0YFxuICAgIC8vIGlmIHRoZXkgYXJlIGRlZmluZWRcbiAgICAvLyB3aGlsZSBpbiBub24tc3RyaWN0IG1vZGUga2V5cyBoYXZpbmcgYW55IG9mIHRoZXNlIGFyZSBnb2luZyB0byBiZSBtYXRjaGVkXG4gICAgcmV0dXJuIF8ua2V5cyh0aGlzLl9jb25uZWN0aW9uc01hcHBpbmcpXG4gICAgICAuZmlsdGVyKChrZXkpID0+IChzdHJpY3QgJiYgdWRpZCAmJiBwb3J0KVxuICAgICAgICA/IChrZXkgPT09IHRoaXMuX3RvS2V5KHVkaWQsIHBvcnQpKVxuICAgICAgICA6ICh1ZGlkICYmIGtleS5zdGFydHNXaXRoKHRoaXMuX3VkaWRBc1Rva2VuKHVkaWQpKSB8fCBwb3J0ICYmIGtleS5lbmRzV2l0aCh0aGlzLl9wb3J0QXNUb2tlbihwb3J0KSkpXG4gICAgICApO1xuICB9XG5cbiAgYXN5bmMgcmVxdWVzdENvbm5lY3Rpb24gKG9wdGlvbnMgPSB7fSkge1xuICAgIGNvbnN0IHtcbiAgICAgIHVkaWQsXG4gICAgICBwb3J0LFxuICAgICAgdXNlUG9ydEZvcndhcmRpbmcsXG4gICAgICBkZXZpY2VQb3J0LFxuICAgICAgdXNibXV4ZFJlbW90ZUhvc3QsXG4gICAgICB1c2JtdXhkUmVtb3RlUG9ydFxuICAgIH0gPSBvcHRpb25zO1xuXG4gICAgaWYgKCF1ZGlkIHx8ICFwb3J0KSB7XG4gICAgICBsb2cud2FybignRGlkIG5vdCBrbm93IGhvdyB0byByZXF1ZXN0IHRoZSBjb25uZWN0aW9uOicpO1xuICAgICAgaWYgKCF1ZGlkKSB7XG4gICAgICAgIGxvZy53YXJuKCctIERldmljZSBVRElEIGlzIHVuc2V0Jyk7XG4gICAgICB9XG4gICAgICBpZiAoIXBvcnQpIHtcbiAgICAgICAgbG9nLndhcm4oJy0gVGhlIGxvY2FsIHBvcnQgbnVtYmVyIGlzIHVuc2V0Jyk7XG4gICAgICB9XG4gICAgICByZXR1cm47XG4gICAgfVxuXG4gICAgbG9nLmluZm8oYFJlcXVlc3RpbmcgY29ubmVjdGlvbiBmb3IgZGV2aWNlICR7dWRpZH0gb24gbG9jYWwgcG9ydCAke3BvcnR9YCArXG4gICAgICAoZGV2aWNlUG9ydCA/IGAsIGRldmljZSBwb3J0ICR7ZGV2aWNlUG9ydH1gIDogJycpKTtcbiAgICBsb2cuZGVidWcoYENhY2hlZCBjb25uZWN0aW9ucyBjb3VudDogJHtfLnNpemUodGhpcy5fY29ubmVjdGlvbnNNYXBwaW5nKX1gKTtcbiAgICBjb25zdCBjb25uZWN0aW9uc09uUG9ydCA9IHRoaXMubGlzdENvbm5lY3Rpb25zKG51bGwsIHBvcnQpO1xuICAgIGlmICghXy5pc0VtcHR5KGNvbm5lY3Rpb25zT25Qb3J0KSkge1xuICAgICAgbG9nLmluZm8oYEZvdW5kIGNhY2hlZCBjb25uZWN0aW9ucyBvbiBwb3J0ICMke3BvcnR9OiAke0pTT04uc3RyaW5naWZ5KGNvbm5lY3Rpb25zT25Qb3J0KX1gKTtcbiAgICB9XG5cbiAgICBpZiAodXNlUG9ydEZvcndhcmRpbmcpIHtcbiAgICAgIGxldCBpc1BvcnRCdXN5ID0gKGF3YWl0IGNoZWNrUG9ydFN0YXR1cyhwb3J0LCBMT0NBTEhPU1QpKSA9PT0gJ29wZW4nO1xuICAgICAgaWYgKGlzUG9ydEJ1c3kpIHtcbiAgICAgICAgbG9nLndhcm4oYFBvcnQgIyR7cG9ydH0gaXMgYnVzeS4gRGlkIHlvdSBxdWl0IHRoZSBwcmV2aW91cyBkcml2ZXIgc2Vzc2lvbihzKSBwcm9wZXJseT9gKTtcbiAgICAgICAgaWYgKCFfLmlzRW1wdHkoY29ubmVjdGlvbnNPblBvcnQpKSB7XG4gICAgICAgICAgbG9nLmluZm8oJ1RyeWluZyB0byByZWxlYXNlIHRoZSBwb3J0Jyk7XG4gICAgICAgICAgZm9yIChjb25zdCBrZXkgb2YgdGhpcy5fcmVsZWFzZVByb3hpZWRDb25uZWN0aW9ucyhjb25uZWN0aW9uc09uUG9ydCkpIHtcbiAgICAgICAgICAgIGRlbGV0ZSB0aGlzLl9jb25uZWN0aW9uc01hcHBpbmdba2V5XTtcbiAgICAgICAgICB9XG4gICAgICAgICAgY29uc3QgdGltZXIgPSBuZXcgdGltaW5nLlRpbWVyKCkuc3RhcnQoKTtcbiAgICAgICAgICB0cnkge1xuICAgICAgICAgICAgYXdhaXQgd2FpdEZvckNvbmRpdGlvbihhc3luYyAoKSA9PiB7XG4gICAgICAgICAgICAgIHRyeSB7XG4gICAgICAgICAgICAgICAgaWYgKChhd2FpdCBjaGVja1BvcnRTdGF0dXMocG9ydCwgTE9DQUxIT1NUKSkgIT09ICdvcGVuJykge1xuICAgICAgICAgICAgICAgICAgbG9nLmluZm8oYFBvcnQgIyR7cG9ydH0gaGFzIGJlZW4gc3VjY2Vzc2Z1bGx5IHJlbGVhc2VkIGFmdGVyIGAgK1xuICAgICAgICAgICAgICAgICAgICBgJHt0aW1lci5nZXREdXJhdGlvbigpLmFzTWlsbGlTZWNvbmRzLnRvRml4ZWQoMCl9bXNgKTtcbiAgICAgICAgICAgICAgICAgIGlzUG9ydEJ1c3kgPSBmYWxzZTtcbiAgICAgICAgICAgICAgICAgIHJldHVybiB0cnVlO1xuICAgICAgICAgICAgICAgIH1cbiAgICAgICAgICAgICAgfSBjYXRjaCAoaWduKSB7fVxuICAgICAgICAgICAgICByZXR1cm4gZmFsc2U7XG4gICAgICAgICAgICB9LCB7XG4gICAgICAgICAgICAgIHdhaXRNczogUE9SVF9DTE9TRV9USU1FT1VULFxuICAgICAgICAgICAgICBpbnRlcnZhbE1zOiAzMDAsXG4gICAgICAgICAgICB9KTtcbiAgICAgICAgICB9IGNhdGNoIChpZ24pIHtcbiAgICAgICAgICAgIGxvZy53YXJuKGBEaWQgbm90IGtub3cgaG93IHRvIHJlbGVhc2UgcG9ydCAjJHtwb3J0fSBpbiBgICtcbiAgICAgICAgICAgICAgYCR7dGltZXIuZ2V0RHVyYXRpb24oKS5hc01pbGxpU2Vjb25kcy50b0ZpeGVkKDApfW1zYCk7XG4gICAgICAgICAgfVxuICAgICAgICB9XG4gICAgICB9XG5cbiAgICAgIGlmIChpc1BvcnRCdXN5KSB7XG4gICAgICAgIHRocm93IG5ldyBFcnJvcihgVGhlIHBvcnQgIyR7cG9ydH0gaXMgb2NjdXBpZWQgYnkgYW4gb3RoZXIgcHJvY2Vzcy4gYCArXG4gICAgICAgICAgYFlvdSBjYW4gZWl0aGVyIHF1aXQgdGhhdCBwcm9jZXNzIG9yIHNlbGVjdCBhbm90aGVyIGZyZWUgcG9ydC5gKTtcbiAgICAgIH1cbiAgICB9XG4gICAgY29uc3QgY3VycmVudEtleSA9IHRoaXMuX3RvS2V5KHVkaWQsIHBvcnQpO1xuICAgIGlmICh1c2VQb3J0Rm9yd2FyZGluZykge1xuICAgICAgY29uc3QgaXByb3h5ID0gbmV3IGlQcm94eSh1ZGlkLCBwb3J0LCBkZXZpY2VQb3J0LCB1c2JtdXhkUmVtb3RlSG9zdCwgdXNibXV4ZFJlbW90ZVBvcnQpO1xuICAgICAgdHJ5IHtcbiAgICAgICAgYXdhaXQgaXByb3h5LnN0YXJ0KCk7XG4gICAgICAgIHRoaXMuX2Nvbm5lY3Rpb25zTWFwcGluZ1tjdXJyZW50S2V5XSA9IHtpcHJveHl9O1xuICAgICAgfSBjYXRjaCAoZSkge1xuICAgICAgICB0cnkge1xuICAgICAgICAgIGlwcm94eS5zdG9wKCk7XG4gICAgICAgIH0gY2F0Y2ggKGUxKSB7XG4gICAgICAgICAgbG9nLmRlYnVnKGUxKTtcbiAgICAgICAgfVxuICAgICAgICB0aHJvdyBlO1xuICAgICAgfVxuICAgIH0gZWxzZSB7XG4gICAgICB0aGlzLl9jb25uZWN0aW9uc01hcHBpbmdbY3VycmVudEtleV0gPSB7fTtcbiAgICB9XG4gICAgbG9nLmluZm8oYFN1Y2Nlc3NmdWxseSByZXF1ZXN0ZWQgdGhlIGNvbm5lY3Rpb24gZm9yICR7Y3VycmVudEtleX1gKTtcbiAgfVxuXG4gIHJlbGVhc2VDb25uZWN0aW9uICh1ZGlkID0gbnVsbCwgcG9ydCA9IG51bGwpIHtcbiAgICBpZiAoIXVkaWQgJiYgIXBvcnQpIHtcbiAgICAgIGxvZy53YXJuKCdOZWl0aGVyIGRldmljZSBVRElEIG5vciBsb2NhbCBwb3J0IGlzIHNldC4gJyArXG4gICAgICAgICdEaWQgbm90IGtub3cgaG93IHRvIHJlbGVhc2UgdGhlIGNvbm5lY3Rpb24nKTtcbiAgICAgIHJldHVybjtcbiAgICB9XG4gICAgbG9nLmluZm8oYFJlbGVhc2luZyBjb25uZWN0aW9ucyBmb3IgJHt1ZGlkIHx8ICdhbnknfSBkZXZpY2Ugb24gJHtwb3J0IHx8ICdhbnknfSBwb3J0IG51bWJlcmApO1xuXG4gICAgY29uc3Qga2V5cyA9IHRoaXMubGlzdENvbm5lY3Rpb25zKHVkaWQsIHBvcnQsIHRydWUpO1xuICAgIGlmIChfLmlzRW1wdHkoa2V5cykpIHtcbiAgICAgIGxvZy5pbmZvKCdObyBjYWNoZWQgY29ubmVjdGlvbnMgaGF2ZSBiZWVuIGZvdW5kJyk7XG4gICAgICByZXR1cm47XG4gICAgfVxuICAgIGxvZy5pbmZvKGBGb3VuZCBjYWNoZWQgY29ubmVjdGlvbnMgdG8gcmVsZWFzZTogJHtKU09OLnN0cmluZ2lmeShrZXlzKX1gKTtcbiAgICB0aGlzLl9yZWxlYXNlUHJveGllZENvbm5lY3Rpb25zKGtleXMpO1xuICAgIGZvciAoY29uc3Qga2V5IG9mIGtleXMpIHtcbiAgICAgIGRlbGV0ZSB0aGlzLl9jb25uZWN0aW9uc01hcHBpbmdba2V5XTtcbiAgICB9XG4gICAgbG9nLmRlYnVnKGBDYWNoZWQgY29ubmVjdGlvbnMgY291bnQ6ICR7Xy5zaXplKHRoaXMuX2Nvbm5lY3Rpb25zTWFwcGluZyl9YCk7XG4gIH1cbn1cblxuY29uc3QgREVWSUNFX0NPTk5FQ1RJT05TX0ZBQ1RPUlkgPSBuZXcgRGV2aWNlQ29ubmVjdGlvbnNGYWN0b3J5KCk7XG5cbmV4cG9ydCB7IERFVklDRV9DT05ORUNUSU9OU19GQUNUT1JZLCBEZXZpY2VDb25uZWN0aW9uc0ZhY3RvcnkgfTtcbmV4cG9ydCBkZWZhdWx0IERFVklDRV9DT05ORUNUSU9OU19GQUNUT1JZO1xuIl0sImZpbGUiOiJsaWIvZGV2aWNlLWNvbm5lY3Rpb25zLWZhY3RvcnkuanMiLCJzb3VyY2VSb290IjoiLi4vLi4ifQ==