@browserstack/testcafe
Version:
Automated browser testing for the modern web development stack.
121 lines • 20.2 kB
JavaScript
;
var __importDefault = (this && this.__importDefault) || function (mod) {
return (mod && mod.__esModule) ? mod : { "default": mod };
};
Object.defineProperty(exports, "__esModule", { value: true });
const events_1 = require("events");
const time_limit_promise_1 = __importDefault(require("time-limit-promise"));
const promisify_event_1 = __importDefault(require("promisify-event"));
const lodash_1 = require("lodash");
// @ts-ignore
const map_reverse_1 = __importDefault(require("map-reverse"));
const runtime_1 = require("../errors/runtime");
const types_1 = require("../errors/types");
const status_1 = __importDefault(require("../browser/connection/status"));
const LOCAL_BROWSERS_READY_TIMEOUT = 2 * 60 * 1000;
const REMOTE_BROWSERS_READY_TIMEOUT = 6 * 60 * 1000;
const RELEASE_TIMEOUT = 10000;
class BrowserSet extends events_1.EventEmitter {
constructor(browserConnectionGroups) {
super();
this._pendingReleases = [];
this.browserConnectionGroups = browserConnectionGroups;
this._browserConnections = lodash_1.flatten(browserConnectionGroups);
this._connectionsReadyTimeout = null;
this._browserErrorHandler = (error) => this.emit('error', error);
this._browserConnections.forEach(bc => bc.on('error', this._browserErrorHandler));
// NOTE: We're setting an empty error handler, because Node kills the process on an 'error' event
// if there is no handler. See: https://nodejs.org/api/events.html#events_class_events_eventemitter
this.on('error', lodash_1.noop);
}
static async _waitIdle(bc) {
if (bc.idle || !bc.isReady())
return;
await promisify_event_1.default(bc, 'idle');
}
static async _closeConnection(bc) {
if (bc.status === status_1.default.closed || !bc.isReady())
return;
bc.close();
await promisify_event_1.default(bc, 'closed');
}
async _getReadyTimeout() {
const isLocalBrowser = (connection) => connection.provider.isLocalBrowser(connection.id, connection.browserInfo.browserName);
const remoteBrowsersExist = (await Promise.all(this._browserConnections.map(isLocalBrowser))).indexOf(false) > -1;
return remoteBrowsersExist ? REMOTE_BROWSERS_READY_TIMEOUT : LOCAL_BROWSERS_READY_TIMEOUT;
}
_createPendingConnectionPromise(readyPromise, timeout, timeoutError) {
const timeoutPromise = new Promise((_, reject) => {
this._connectionsReadyTimeout = setTimeout(() => reject(timeoutError), timeout);
});
return Promise
.race([readyPromise, timeoutPromise])
.then(value => {
this._connectionsReadyTimeout.unref();
return value;
}, error => {
this._connectionsReadyTimeout.unref();
throw error;
});
}
async _waitConnectionsOpened() {
const connectionsReadyPromise = Promise.all(this._browserConnections
.filter(bc => bc.status !== status_1.default.opened)
.map(bc => promisify_event_1.default(bc, 'opened')));
const timeoutError = new runtime_1.GeneralError(types_1.RUNTIME_ERRORS.cannotEstablishBrowserConnection);
const readyTimeout = await this._getReadyTimeout();
await this._createPendingConnectionPromise(connectionsReadyPromise, readyTimeout, timeoutError);
}
_checkForDisconnections() {
const disconnectedUserAgents = this._browserConnections
.filter(bc => bc.status === status_1.default.closed)
.map(bc => bc.userAgent);
if (disconnectedUserAgents.length)
throw new runtime_1.GeneralError(types_1.RUNTIME_ERRORS.cannotRunAgainstDisconnectedBrowsers, disconnectedUserAgents.join(', '));
}
//API
static from(browserConnections) {
const browserSet = new BrowserSet(browserConnections);
const prepareConnection = Promise.resolve()
.then(() => {
browserSet._checkForDisconnections();
return browserSet._waitConnectionsOpened();
})
.then(() => browserSet);
return Promise
.race([
prepareConnection,
promisify_event_1.default(browserSet, 'error')
])
.catch(async (error) => {
await browserSet.dispose();
throw error;
});
}
releaseConnection(bc) {
if (!this._browserConnections.includes(bc))
return Promise.resolve();
lodash_1.pull(this._browserConnections, bc);
bc.removeListener('error', this._browserErrorHandler);
const appropriateStateSwitch = bc.permanent ?
BrowserSet._waitIdle(bc) :
BrowserSet._closeConnection(bc);
const release = time_limit_promise_1.default(appropriateStateSwitch, RELEASE_TIMEOUT)
.then(() => lodash_1.pull(this._pendingReleases, release));
this._pendingReleases.push(release);
return release;
}
async dispose() {
// NOTE: When browserConnection is cancelled, it is removed from
// the this.connections array, which leads to shifting indexes
// towards the beginning. So, we must copy the array in order to iterate it,
// or we can perform iteration from the end to the beginning.
if (this._connectionsReadyTimeout)
this._connectionsReadyTimeout.unref();
map_reverse_1.default(this._browserConnections, (bc) => this.releaseConnection(bc));
await Promise.all(this._pendingReleases);
}
}
exports.default = BrowserSet;
module.exports = exports.default;
//# sourceMappingURL=data:application/json;base64,eyJ2ZXJzaW9uIjozLCJmaWxlIjoiYnJvd3Nlci1zZXQuanMiLCJzb3VyY2VSb290IjoiIiwic291cmNlcyI6WyIuLi8uLi9zcmMvcnVubmVyL2Jyb3dzZXItc2V0LnRzIl0sIm5hbWVzIjpbXSwibWFwcGluZ3MiOiI7Ozs7O0FBQUEsbUNBQXNDO0FBQ3RDLDRFQUF1RDtBQUN2RCxzRUFBNkM7QUFDN0MsbUNBQXVEO0FBQ3ZELGFBQWE7QUFDYiw4REFBcUM7QUFDckMsK0NBQWlEO0FBQ2pELDJDQUFpRDtBQUVqRCwwRUFBbUU7QUFFbkUsTUFBTSw0QkFBNEIsR0FBSSxDQUFDLEdBQUcsRUFBRSxHQUFHLElBQUksQ0FBQztBQUNwRCxNQUFNLDZCQUE2QixHQUFHLENBQUMsR0FBRyxFQUFFLEdBQUcsSUFBSSxDQUFDO0FBQ3BELE1BQU0sZUFBZSxHQUFpQixLQUFLLENBQUM7QUFFNUMsTUFBcUIsVUFBVyxTQUFRLHFCQUFZO0lBT2hELFlBQW9CLHVCQUE4QztRQUM5RCxLQUFLLEVBQUUsQ0FBQztRQUVSLElBQUksQ0FBQyxnQkFBZ0IsR0FBVyxFQUFFLENBQUM7UUFDbkMsSUFBSSxDQUFDLHVCQUF1QixHQUFJLHVCQUF1QixDQUFDO1FBQ3hELElBQUksQ0FBQyxtQkFBbUIsR0FBUSxnQkFBTyxDQUFDLHVCQUF1QixDQUFDLENBQUM7UUFDakUsSUFBSSxDQUFDLHdCQUF3QixHQUFHLElBQUksQ0FBQztRQUVyQyxJQUFJLENBQUMsb0JBQW9CLEdBQUcsQ0FBQyxLQUFZLEVBQUUsRUFBRSxDQUFDLElBQUksQ0FBQyxJQUFJLENBQUMsT0FBTyxFQUFFLEtBQUssQ0FBQyxDQUFDO1FBRXhFLElBQUksQ0FBQyxtQkFBbUIsQ0FBQyxPQUFPLENBQUMsRUFBRSxDQUFDLEVBQUUsQ0FBQyxFQUFFLENBQUMsRUFBRSxDQUFDLE9BQU8sRUFBRSxJQUFJLENBQUMsb0JBQW9CLENBQUMsQ0FBQyxDQUFDO1FBRWxGLGlHQUFpRztRQUNqRyxtR0FBbUc7UUFDbkcsSUFBSSxDQUFDLEVBQUUsQ0FBQyxPQUFPLEVBQUUsYUFBSSxDQUFDLENBQUM7SUFDM0IsQ0FBQztJQUVPLE1BQU0sQ0FBQyxLQUFLLENBQUMsU0FBUyxDQUFFLEVBQXFCO1FBQ2pELElBQUksRUFBRSxDQUFDLElBQUksSUFBSSxDQUFDLEVBQUUsQ0FBQyxPQUFPLEVBQUU7WUFDeEIsT0FBTztRQUVYLE1BQU0seUJBQWMsQ0FBQyxFQUFFLEVBQUUsTUFBTSxDQUFDLENBQUM7SUFDckMsQ0FBQztJQUVPLE1BQU0sQ0FBQyxLQUFLLENBQUMsZ0JBQWdCLENBQUUsRUFBcUI7UUFDeEQsSUFBSSxFQUFFLENBQUMsTUFBTSxLQUFLLGdCQUF1QixDQUFDLE1BQU0sSUFBSSxDQUFDLEVBQUUsQ0FBQyxPQUFPLEVBQUU7WUFDN0QsT0FBTztRQUVYLEVBQUUsQ0FBQyxLQUFLLEVBQUUsQ0FBQztRQUVYLE1BQU0seUJBQWMsQ0FBQyxFQUFFLEVBQUUsUUFBUSxDQUFDLENBQUM7SUFDdkMsQ0FBQztJQUVPLEtBQUssQ0FBQyxnQkFBZ0I7UUFDMUIsTUFBTSxjQUFjLEdBQVEsQ0FBQyxVQUE2QixFQUFXLEVBQUUsQ0FBQyxVQUFVLENBQUMsUUFBUSxDQUFDLGNBQWMsQ0FBQyxVQUFVLENBQUMsRUFBRSxFQUFFLFVBQVUsQ0FBQyxXQUFXLENBQUMsV0FBVyxDQUFDLENBQUM7UUFDOUosTUFBTSxtQkFBbUIsR0FBRyxDQUFDLE1BQU0sT0FBTyxDQUFDLEdBQUcsQ0FBQyxJQUFJLENBQUMsbUJBQW1CLENBQUMsR0FBRyxDQUFDLGNBQWMsQ0FBQyxDQUFDLENBQUMsQ0FBQyxPQUFPLENBQUMsS0FBSyxDQUFDLEdBQUcsQ0FBQyxDQUFDLENBQUM7UUFFbEgsT0FBTyxtQkFBbUIsQ0FBQyxDQUFDLENBQUMsNkJBQTZCLENBQUMsQ0FBQyxDQUFDLDRCQUE0QixDQUFDO0lBQzlGLENBQUM7SUFFTywrQkFBK0IsQ0FBRSxZQUEwQyxFQUFFLE9BQWUsRUFBRSxZQUEwQjtRQUM1SCxNQUFNLGNBQWMsR0FBRyxJQUFJLE9BQU8sQ0FBQyxDQUFDLENBQUMsRUFBRSxNQUFNLEVBQUUsRUFBRTtZQUM3QyxJQUFJLENBQUMsd0JBQXdCLEdBQUcsVUFBVSxDQUFDLEdBQUcsRUFBRSxDQUFDLE1BQU0sQ0FBQyxZQUFZLENBQUMsRUFBRSxPQUFPLENBQUMsQ0FBQztRQUNwRixDQUFDLENBQUMsQ0FBQztRQUVILE9BQU8sT0FBTzthQUNULElBQUksQ0FBQyxDQUFDLFlBQVksRUFBRSxjQUFjLENBQUMsQ0FBQzthQUNwQyxJQUFJLENBQ0QsS0FBSyxDQUFDLEVBQUU7WUFDSCxJQUFJLENBQUMsd0JBQTJDLENBQUMsS0FBSyxFQUFFLENBQUM7WUFFMUQsT0FBTyxLQUFLLENBQUM7UUFDakIsQ0FBQyxFQUNELEtBQUssQ0FBQyxFQUFFO1lBQ0gsSUFBSSxDQUFDLHdCQUEyQyxDQUFDLEtBQUssRUFBRSxDQUFDO1lBRTFELE1BQU0sS0FBSyxDQUFDO1FBQ2hCLENBQUMsQ0FDSixDQUFDO0lBQ1YsQ0FBQztJQUVPLEtBQUssQ0FBQyxzQkFBc0I7UUFDaEMsTUFBTSx1QkFBdUIsR0FBRyxPQUFPLENBQUMsR0FBRyxDQUN2QyxJQUFJLENBQUMsbUJBQW1CO2FBQ25CLE1BQU0sQ0FBQyxFQUFFLENBQUMsRUFBRSxDQUFDLEVBQUUsQ0FBQyxNQUFNLEtBQUssZ0JBQXVCLENBQUMsTUFBTSxDQUFDO2FBQzFELEdBQUcsQ0FBQyxFQUFFLENBQUMsRUFBRSxDQUFDLHlCQUFjLENBQUMsRUFBRSxFQUFFLFFBQVEsQ0FBQyxDQUFDLENBQy9DLENBQUM7UUFFRixNQUFNLFlBQVksR0FBRyxJQUFJLHNCQUFZLENBQUMsc0JBQWMsQ0FBQyxnQ0FBZ0MsQ0FBQyxDQUFDO1FBQ3ZGLE1BQU0sWUFBWSxHQUFHLE1BQU0sSUFBSSxDQUFDLGdCQUFnQixFQUFFLENBQUM7UUFFbkQsTUFBTSxJQUFJLENBQUMsK0JBQStCLENBQUMsdUJBQXVCLEVBQUUsWUFBWSxFQUFFLFlBQVksQ0FBQyxDQUFDO0lBQ3BHLENBQUM7SUFFTyx1QkFBdUI7UUFDM0IsTUFBTSxzQkFBc0IsR0FBRyxJQUFJLENBQUMsbUJBQW1CO2FBQ2xELE1BQU0sQ0FBQyxFQUFFLENBQUMsRUFBRSxDQUFDLEVBQUUsQ0FBQyxNQUFNLEtBQUssZ0JBQXVCLENBQUMsTUFBTSxDQUFDO2FBQzFELEdBQUcsQ0FBQyxFQUFFLENBQUMsRUFBRSxDQUFDLEVBQUUsQ0FBQyxTQUFTLENBQUMsQ0FBQztRQUU3QixJQUFJLHNCQUFzQixDQUFDLE1BQU07WUFDN0IsTUFBTSxJQUFJLHNCQUFZLENBQUMsc0JBQWMsQ0FBQyxvQ0FBb0MsRUFBRSxzQkFBc0IsQ0FBQyxJQUFJLENBQUMsSUFBSSxDQUFDLENBQUMsQ0FBQztJQUN2SCxDQUFDO0lBR0QsS0FBSztJQUNFLE1BQU0sQ0FBQyxJQUFJLENBQUUsa0JBQXlDO1FBQ3pELE1BQU0sVUFBVSxHQUFHLElBQUksVUFBVSxDQUFDLGtCQUFrQixDQUFDLENBQUM7UUFFdEQsTUFBTSxpQkFBaUIsR0FBRyxPQUFPLENBQUMsT0FBTyxFQUFFO2FBQ3RDLElBQUksQ0FBQyxHQUFHLEVBQUU7WUFDUCxVQUFVLENBQUMsdUJBQXVCLEVBQUUsQ0FBQztZQUNyQyxPQUFPLFVBQVUsQ0FBQyxzQkFBc0IsRUFBRSxDQUFDO1FBQy9DLENBQUMsQ0FBQzthQUNELElBQUksQ0FBQyxHQUFHLEVBQUUsQ0FBQyxVQUFVLENBQUMsQ0FBQztRQUU1QixPQUFPLE9BQU87YUFDVCxJQUFJLENBQUM7WUFDRixpQkFBaUI7WUFDakIseUJBQWMsQ0FBQyxVQUFVLEVBQUUsT0FBTyxDQUFDO1NBQ3RDLENBQUM7YUFDRCxLQUFLLENBQUMsS0FBSyxFQUFDLEtBQUssRUFBQyxFQUFFO1lBQ2pCLE1BQU0sVUFBVSxDQUFDLE9BQU8sRUFBRSxDQUFDO1lBRTNCLE1BQU0sS0FBSyxDQUFDO1FBQ2hCLENBQUMsQ0FBQyxDQUFDO0lBQ1gsQ0FBQztJQUVNLGlCQUFpQixDQUFFLEVBQXFCO1FBQzNDLElBQUksQ0FBQyxJQUFJLENBQUMsbUJBQW1CLENBQUMsUUFBUSxDQUFDLEVBQUUsQ0FBQztZQUN0QyxPQUFPLE9BQU8sQ0FBQyxPQUFPLEVBQUUsQ0FBQztRQUU3QixhQUFNLENBQUMsSUFBSSxDQUFDLG1CQUFtQixFQUFFLEVBQUUsQ0FBQyxDQUFDO1FBRXJDLEVBQUUsQ0FBQyxjQUFjLENBQUMsT0FBTyxFQUFFLElBQUksQ0FBQyxvQkFBb0IsQ0FBQyxDQUFDO1FBRXRELE1BQU0sc0JBQXNCLEdBQUcsRUFBRSxDQUFDLFNBQVMsQ0FBQyxDQUFDO1lBQ3pDLFVBQVUsQ0FBQyxTQUFTLENBQUMsRUFBRSxDQUFDLENBQUMsQ0FBQztZQUMxQixVQUFVLENBQUMsZ0JBQWdCLENBQUMsRUFBRSxDQUFDLENBQUM7UUFFcEMsTUFBTSxPQUFPLEdBQUcsNEJBQXFCLENBQUMsc0JBQXNCLEVBQUUsZUFBZSxDQUFDO2FBQ3pFLElBQUksQ0FBQyxHQUFHLEVBQUUsQ0FBQyxhQUFNLENBQUMsSUFBSSxDQUFDLGdCQUFnQixFQUFFLE9BQU8sQ0FBQyxDQUFrQixDQUFDO1FBRXpFLElBQUksQ0FBQyxnQkFBZ0IsQ0FBQyxJQUFJLENBQUMsT0FBTyxDQUFDLENBQUM7UUFFcEMsT0FBTyxPQUFPLENBQUM7SUFDbkIsQ0FBQztJQUVNLEtBQUssQ0FBQyxPQUFPO1FBQ2hCLGdFQUFnRTtRQUNoRSw4REFBOEQ7UUFDOUQsNEVBQTRFO1FBQzVFLDZEQUE2RDtRQUM3RCxJQUFJLElBQUksQ0FBQyx3QkFBd0I7WUFDN0IsSUFBSSxDQUFDLHdCQUF3QixDQUFDLEtBQUssRUFBRSxDQUFDO1FBRTFDLHFCQUFVLENBQUMsSUFBSSxDQUFDLG1CQUFtQixFQUFFLENBQUMsRUFBcUIsRUFBRSxFQUFFLENBQUMsSUFBSSxDQUFDLGlCQUFpQixDQUFDLEVBQUUsQ0FBQyxDQUFDLENBQUM7UUFFNUYsTUFBTSxPQUFPLENBQUMsR0FBRyxDQUFDLElBQUksQ0FBQyxnQkFBZ0IsQ0FBQyxDQUFDO0lBQzdDLENBQUM7Q0FDSjtBQWxKRCw2QkFrSkMiLCJzb3VyY2VzQ29udGVudCI6WyJpbXBvcnQgeyBFdmVudEVtaXR0ZXIgfSBmcm9tICdldmVudHMnO1xuaW1wb3J0IGdldFRpbWVMaW1pdGVkUHJvbWlzZSBmcm9tICd0aW1lLWxpbWl0LXByb21pc2UnO1xuaW1wb3J0IHByb21pc2lmeUV2ZW50IGZyb20gJ3Byb21pc2lmeS1ldmVudCc7XG5pbXBvcnQgeyBmbGF0dGVuLCBub29wLCBwdWxsIGFzIHJlbW92ZSB9IGZyb20gJ2xvZGFzaCc7XG4vLyBAdHMtaWdub3JlXG5pbXBvcnQgbWFwUmV2ZXJzZSBmcm9tICdtYXAtcmV2ZXJzZSc7XG5pbXBvcnQgeyBHZW5lcmFsRXJyb3IgfSBmcm9tICcuLi9lcnJvcnMvcnVudGltZSc7XG5pbXBvcnQgeyBSVU5USU1FX0VSUk9SUyB9IGZyb20gJy4uL2Vycm9ycy90eXBlcyc7XG5pbXBvcnQgQnJvd3NlckNvbm5lY3Rpb24gZnJvbSAnLi4vYnJvd3Nlci9jb25uZWN0aW9uJztcbmltcG9ydCBCcm93c2VyQ29ubmVjdGlvblN0YXR1cyBmcm9tICcuLi9icm93c2VyL2Nvbm5lY3Rpb24vc3RhdHVzJztcblxuY29uc3QgTE9DQUxfQlJPV1NFUlNfUkVBRFlfVElNRU9VVCAgPSAyICogNjAgKiAxMDAwO1xuY29uc3QgUkVNT1RFX0JST1dTRVJTX1JFQURZX1RJTUVPVVQgPSA2ICogNjAgKiAxMDAwO1xuY29uc3QgUkVMRUFTRV9USU1FT1VUICAgICAgICAgICAgICAgPSAxMDAwMDtcblxuZXhwb3J0IGRlZmF1bHQgY2xhc3MgQnJvd3NlclNldCBleHRlbmRzIEV2ZW50RW1pdHRlciB7XG4gICAgcHJpdmF0ZSByZWFkb25seSBfYnJvd3NlckNvbm5lY3Rpb25zOiBCcm93c2VyQ29ubmVjdGlvbltdO1xuICAgIHByaXZhdGUgcmVhZG9ubHkgX2Jyb3dzZXJFcnJvckhhbmRsZXI6IChlcnJvcjogRXJyb3IpID0+IHZvaWQ7XG4gICAgcHJpdmF0ZSByZWFkb25seSBfcGVuZGluZ1JlbGVhc2VzOiBQcm9taXNlPHZvaWQ+W107XG4gICAgcHJpdmF0ZSBfY29ubmVjdGlvbnNSZWFkeVRpbWVvdXQ6IG51bGwgfCBOb2RlSlMuVGltZW91dDtcbiAgICBwdWJsaWMgYnJvd3NlckNvbm5lY3Rpb25Hcm91cHM6IEJyb3dzZXJDb25uZWN0aW9uW11bXTtcblxuICAgIHB1YmxpYyBjb25zdHJ1Y3RvciAoYnJvd3NlckNvbm5lY3Rpb25Hcm91cHM6IEJyb3dzZXJDb25uZWN0aW9uW11bXSkge1xuICAgICAgICBzdXBlcigpO1xuXG4gICAgICAgIHRoaXMuX3BlbmRpbmdSZWxlYXNlcyAgICAgICAgID0gW107XG4gICAgICAgIHRoaXMuYnJvd3NlckNvbm5lY3Rpb25Hcm91cHMgID0gYnJvd3NlckNvbm5lY3Rpb25Hcm91cHM7XG4gICAgICAgIHRoaXMuX2Jyb3dzZXJDb25uZWN0aW9ucyAgICAgID0gZmxhdHRlbihicm93c2VyQ29ubmVjdGlvbkdyb3Vwcyk7XG4gICAgICAgIHRoaXMuX2Nvbm5lY3Rpb25zUmVhZHlUaW1lb3V0ID0gbnVsbDtcblxuICAgICAgICB0aGlzLl9icm93c2VyRXJyb3JIYW5kbGVyID0gKGVycm9yOiBFcnJvcikgPT4gdGhpcy5lbWl0KCdlcnJvcicsIGVycm9yKTtcblxuICAgICAgICB0aGlzLl9icm93c2VyQ29ubmVjdGlvbnMuZm9yRWFjaChiYyA9PiBiYy5vbignZXJyb3InLCB0aGlzLl9icm93c2VyRXJyb3JIYW5kbGVyKSk7XG5cbiAgICAgICAgLy8gTk9URTogV2UncmUgc2V0dGluZyBhbiBlbXB0eSBlcnJvciBoYW5kbGVyLCBiZWNhdXNlIE5vZGUga2lsbHMgdGhlIHByb2Nlc3Mgb24gYW4gJ2Vycm9yJyBldmVudFxuICAgICAgICAvLyBpZiB0aGVyZSBpcyBubyBoYW5kbGVyLiBTZWU6IGh0dHBzOi8vbm9kZWpzLm9yZy9hcGkvZXZlbnRzLmh0bWwjZXZlbnRzX2NsYXNzX2V2ZW50c19ldmVudGVtaXR0ZXJcbiAgICAgICAgdGhpcy5vbignZXJyb3InLCBub29wKTtcbiAgICB9XG5cbiAgICBwcml2YXRlIHN0YXRpYyBhc3luYyBfd2FpdElkbGUgKGJjOiBCcm93c2VyQ29ubmVjdGlvbik6IFByb21pc2U8dm9pZD4ge1xuICAgICAgICBpZiAoYmMuaWRsZSB8fCAhYmMuaXNSZWFkeSgpKVxuICAgICAgICAgICAgcmV0dXJuO1xuXG4gICAgICAgIGF3YWl0IHByb21pc2lmeUV2ZW50KGJjLCAnaWRsZScpO1xuICAgIH1cblxuICAgIHByaXZhdGUgc3RhdGljIGFzeW5jIF9jbG9zZUNvbm5lY3Rpb24gKGJjOiBCcm93c2VyQ29ubmVjdGlvbik6IFByb21pc2U8dm9pZD4ge1xuICAgICAgICBpZiAoYmMuc3RhdHVzID09PSBCcm93c2VyQ29ubmVjdGlvblN0YXR1cy5jbG9zZWQgfHwgIWJjLmlzUmVhZHkoKSlcbiAgICAgICAgICAgIHJldHVybjtcblxuICAgICAgICBiYy5jbG9zZSgpO1xuXG4gICAgICAgIGF3YWl0IHByb21pc2lmeUV2ZW50KGJjLCAnY2xvc2VkJyk7XG4gICAgfVxuXG4gICAgcHJpdmF0ZSBhc3luYyBfZ2V0UmVhZHlUaW1lb3V0ICgpOiBQcm9taXNlPG51bWJlcj4ge1xuICAgICAgICBjb25zdCBpc0xvY2FsQnJvd3NlciAgICAgID0gKGNvbm5lY3Rpb246IEJyb3dzZXJDb25uZWN0aW9uKTogYm9vbGVhbiA9PiBjb25uZWN0aW9uLnByb3ZpZGVyLmlzTG9jYWxCcm93c2VyKGNvbm5lY3Rpb24uaWQsIGNvbm5lY3Rpb24uYnJvd3NlckluZm8uYnJvd3Nlck5hbWUpO1xuICAgICAgICBjb25zdCByZW1vdGVCcm93c2Vyc0V4aXN0ID0gKGF3YWl0IFByb21pc2UuYWxsKHRoaXMuX2Jyb3dzZXJDb25uZWN0aW9ucy5tYXAoaXNMb2NhbEJyb3dzZXIpKSkuaW5kZXhPZihmYWxzZSkgPiAtMTtcblxuICAgICAgICByZXR1cm4gcmVtb3RlQnJvd3NlcnNFeGlzdCA/IFJFTU9URV9CUk9XU0VSU19SRUFEWV9USU1FT1VUIDogTE9DQUxfQlJPV1NFUlNfUkVBRFlfVElNRU9VVDtcbiAgICB9XG5cbiAgICBwcml2YXRlIF9jcmVhdGVQZW5kaW5nQ29ubmVjdGlvblByb21pc2UgKHJlYWR5UHJvbWlzZTogUHJvbWlzZTxCcm93c2VyQ29ubmVjdGlvbltdPiwgdGltZW91dDogbnVtYmVyLCB0aW1lb3V0RXJyb3I6IEdlbmVyYWxFcnJvcik6IFByb21pc2U8dW5rbm93bj4ge1xuICAgICAgICBjb25zdCB0aW1lb3V0UHJvbWlzZSA9IG5ldyBQcm9taXNlKChfLCByZWplY3QpID0+IHtcbiAgICAgICAgICAgIHRoaXMuX2Nvbm5lY3Rpb25zUmVhZHlUaW1lb3V0ID0gc2V0VGltZW91dCgoKSA9PiByZWplY3QodGltZW91dEVycm9yKSwgdGltZW91dCk7XG4gICAgICAgIH0pO1xuXG4gICAgICAgIHJldHVybiBQcm9taXNlXG4gICAgICAgICAgICAucmFjZShbcmVhZHlQcm9taXNlLCB0aW1lb3V0UHJvbWlzZV0pXG4gICAgICAgICAgICAudGhlbihcbiAgICAgICAgICAgICAgICB2YWx1ZSA9PiB7XG4gICAgICAgICAgICAgICAgICAgICh0aGlzLl9jb25uZWN0aW9uc1JlYWR5VGltZW91dCBhcyBOb2RlSlMuVGltZW91dCkudW5yZWYoKTtcblxuICAgICAgICAgICAgICAgICAgICByZXR1cm4gdmFsdWU7XG4gICAgICAgICAgICAgICAgfSxcbiAgICAgICAgICAgICAgICBlcnJvciA9PiB7XG4gICAgICAgICAgICAgICAgICAgICh0aGlzLl9jb25uZWN0aW9uc1JlYWR5VGltZW91dCBhcyBOb2RlSlMuVGltZW91dCkudW5yZWYoKTtcblxuICAgICAgICAgICAgICAgICAgICB0aHJvdyBlcnJvcjtcbiAgICAgICAgICAgICAgICB9XG4gICAgICAgICAgICApO1xuICAgIH1cblxuICAgIHByaXZhdGUgYXN5bmMgX3dhaXRDb25uZWN0aW9uc09wZW5lZCAoKTogUHJvbWlzZTx2b2lkPiB7XG4gICAgICAgIGNvbnN0IGNvbm5lY3Rpb25zUmVhZHlQcm9taXNlID0gUHJvbWlzZS5hbGwoXG4gICAgICAgICAgICB0aGlzLl9icm93c2VyQ29ubmVjdGlvbnNcbiAgICAgICAgICAgICAgICAuZmlsdGVyKGJjID0+IGJjLnN0YXR1cyAhPT0gQnJvd3NlckNvbm5lY3Rpb25TdGF0dXMub3BlbmVkKVxuICAgICAgICAgICAgICAgIC5tYXAoYmMgPT4gcHJvbWlzaWZ5RXZlbnQoYmMsICdvcGVuZWQnKSlcbiAgICAgICAgKTtcblxuICAgICAgICBjb25zdCB0aW1lb3V0RXJyb3IgPSBuZXcgR2VuZXJhbEVycm9yKFJVTlRJTUVfRVJST1JTLmNhbm5vdEVzdGFibGlzaEJyb3dzZXJDb25uZWN0aW9uKTtcbiAgICAgICAgY29uc3QgcmVhZHlUaW1lb3V0ID0gYXdhaXQgdGhpcy5fZ2V0UmVhZHlUaW1lb3V0KCk7XG5cbiAgICAgICAgYXdhaXQgdGhpcy5fY3JlYXRlUGVuZGluZ0Nvbm5lY3Rpb25Qcm9taXNlKGNvbm5lY3Rpb25zUmVhZHlQcm9taXNlLCByZWFkeVRpbWVvdXQsIHRpbWVvdXRFcnJvcik7XG4gICAgfVxuXG4gICAgcHJpdmF0ZSBfY2hlY2tGb3JEaXNjb25uZWN0aW9ucyAoKTogdm9pZCB7XG4gICAgICAgIGNvbnN0IGRpc2Nvbm5lY3RlZFVzZXJBZ2VudHMgPSB0aGlzLl9icm93c2VyQ29ubmVjdGlvbnNcbiAgICAgICAgICAgIC5maWx0ZXIoYmMgPT4gYmMuc3RhdHVzID09PSBCcm93c2VyQ29ubmVjdGlvblN0YXR1cy5jbG9zZWQpXG4gICAgICAgICAgICAubWFwKGJjID0+IGJjLnVzZXJBZ2VudCk7XG5cbiAgICAgICAgaWYgKGRpc2Nvbm5lY3RlZFVzZXJBZ2VudHMubGVuZ3RoKVxuICAgICAgICAgICAgdGhyb3cgbmV3IEdlbmVyYWxFcnJvcihSVU5USU1FX0VSUk9SUy5jYW5ub3RSdW5BZ2FpbnN0RGlzY29ubmVjdGVkQnJvd3NlcnMsIGRpc2Nvbm5lY3RlZFVzZXJBZ2VudHMuam9pbignLCAnKSk7XG4gICAgfVxuXG5cbiAgICAvL0FQSVxuICAgIHB1YmxpYyBzdGF0aWMgZnJvbSAoYnJvd3NlckNvbm5lY3Rpb25zOiBCcm93c2VyQ29ubmVjdGlvbltdW10pOiBQcm9taXNlPEJyb3dzZXJTZXQ+IHtcbiAgICAgICAgY29uc3QgYnJvd3NlclNldCA9IG5ldyBCcm93c2VyU2V0KGJyb3dzZXJDb25uZWN0aW9ucyk7XG5cbiAgICAgICAgY29uc3QgcHJlcGFyZUNvbm5lY3Rpb24gPSBQcm9taXNlLnJlc29sdmUoKVxuICAgICAgICAgICAgLnRoZW4oKCkgPT4ge1xuICAgICAgICAgICAgICAgIGJyb3dzZXJTZXQuX2NoZWNrRm9yRGlzY29ubmVjdGlvbnMoKTtcbiAgICAgICAgICAgICAgICByZXR1cm4gYnJvd3NlclNldC5fd2FpdENvbm5lY3Rpb25zT3BlbmVkKCk7XG4gICAgICAgICAgICB9KVxuICAgICAgICAgICAgLnRoZW4oKCkgPT4gYnJvd3NlclNldCk7XG5cbiAgICAgICAgcmV0dXJuIFByb21pc2VcbiAgICAgICAgICAgIC5yYWNlKFtcbiAgICAgICAgICAgICAgICBwcmVwYXJlQ29ubmVjdGlvbixcbiAgICAgICAgICAgICAgICBwcm9taXNpZnlFdmVudChicm93c2VyU2V0LCAnZXJyb3InKVxuICAgICAgICAgICAgXSlcbiAgICAgICAgICAgIC5jYXRjaChhc3luYyBlcnJvciA9PiB7XG4gICAgICAgICAgICAgICAgYXdhaXQgYnJvd3NlclNldC5kaXNwb3NlKCk7XG5cbiAgICAgICAgICAgICAgICB0aHJvdyBlcnJvcjtcbiAgICAgICAgICAgIH0pO1xuICAgIH1cblxuICAgIHB1YmxpYyByZWxlYXNlQ29ubmVjdGlvbiAoYmM6IEJyb3dzZXJDb25uZWN0aW9uKTogUHJvbWlzZTx2b2lkPiB7XG4gICAgICAgIGlmICghdGhpcy5fYnJvd3NlckNvbm5lY3Rpb25zLmluY2x1ZGVzKGJjKSlcbiAgICAgICAgICAgIHJldHVybiBQcm9taXNlLnJlc29sdmUoKTtcblxuICAgICAgICByZW1vdmUodGhpcy5fYnJvd3NlckNvbm5lY3Rpb25zLCBiYyk7XG5cbiAgICAgICAgYmMucmVtb3ZlTGlzdGVuZXIoJ2Vycm9yJywgdGhpcy5fYnJvd3NlckVycm9ySGFuZGxlcik7XG5cbiAgICAgICAgY29uc3QgYXBwcm9wcmlhdGVTdGF0ZVN3aXRjaCA9IGJjLnBlcm1hbmVudCA/XG4gICAgICAgICAgICBCcm93c2VyU2V0Ll93YWl0SWRsZShiYykgOlxuICAgICAgICAgICAgQnJvd3NlclNldC5fY2xvc2VDb25uZWN0aW9uKGJjKTtcblxuICAgICAgICBjb25zdCByZWxlYXNlID0gZ2V0VGltZUxpbWl0ZWRQcm9taXNlKGFwcHJvcHJpYXRlU3RhdGVTd2l0Y2gsIFJFTEVBU0VfVElNRU9VVClcbiAgICAgICAgICAgIC50aGVuKCgpID0+IHJlbW92ZSh0aGlzLl9wZW5kaW5nUmVsZWFzZXMsIHJlbGVhc2UpKSBhcyBQcm9taXNlPHZvaWQ+O1xuXG4gICAgICAgIHRoaXMuX3BlbmRpbmdSZWxlYXNlcy5wdXNoKHJlbGVhc2UpO1xuXG4gICAgICAgIHJldHVybiByZWxlYXNlO1xuICAgIH1cblxuICAgIHB1YmxpYyBhc3luYyBkaXNwb3NlICgpOiBQcm9taXNlPHZvaWQ+IHtcbiAgICAgICAgLy8gTk9URTogV2hlbiBicm93c2VyQ29ubmVjdGlvbiBpcyBjYW5jZWxsZWQsIGl0IGlzIHJlbW92ZWQgZnJvbVxuICAgICAgICAvLyB0aGUgdGhpcy5jb25uZWN0aW9ucyBhcnJheSwgd2hpY2ggbGVhZHMgdG8gc2hpZnRpbmcgaW5kZXhlc1xuICAgICAgICAvLyB0b3dhcmRzIHRoZSBiZWdpbm5pbmcuIFNvLCB3ZSBtdXN0IGNvcHkgdGhlIGFycmF5IGluIG9yZGVyIHRvIGl0ZXJhdGUgaXQsXG4gICAgICAgIC8vIG9yIHdlIGNhbiBwZXJmb3JtIGl0ZXJhdGlvbiBmcm9tIHRoZSBlbmQgdG8gdGhlIGJlZ2lubmluZy5cbiAgICAgICAgaWYgKHRoaXMuX2Nvbm5lY3Rpb25zUmVhZHlUaW1lb3V0KVxuICAgICAgICAgICAgdGhpcy5fY29ubmVjdGlvbnNSZWFkeVRpbWVvdXQudW5yZWYoKTtcblxuICAgICAgICBtYXBSZXZlcnNlKHRoaXMuX2Jyb3dzZXJDb25uZWN0aW9ucywgKGJjOiBCcm93c2VyQ29ubmVjdGlvbikgPT4gdGhpcy5yZWxlYXNlQ29ubmVjdGlvbihiYykpO1xuXG4gICAgICAgIGF3YWl0IFByb21pc2UuYWxsKHRoaXMuX3BlbmRpbmdSZWxlYXNlcyk7XG4gICAgfVxufVxuIl19