UNPKG

verdaccio

Version:

A lightweight private npm proxy registry

760 lines (730 loc) 98 kB
"use strict"; Object.defineProperty(exports, "__esModule", { value: true }); exports.default = void 0; var _assert = _interopRequireDefault(require("assert")); var _debug = _interopRequireDefault(require("debug")); var _lodash = _interopRequireDefault(require("lodash")); var _url = _interopRequireDefault(require("url")); var _streams = require("@verdaccio/streams"); var _utils = require("@verdaccio/utils"); var _constants = require("./constants"); var _storageUtils = require("./storage-utils"); var _utils2 = require("./utils"); function _interopRequireDefault(e) { return e && e.__esModule ? e : { default: e }; } const debug = (0, _debug.default)('verdaccio:local-storage'); /** * Implements Storage interface (same for storage.js, local-storage.js, up-storage.js). */ class LocalStorage { constructor(config, logger, localStorage) { this.logger = logger; this.config = config; this.storagePlugin = localStorage; } addPackage(name, pkg, callback) { const storage = this._getLocalStorage(name); if (_lodash.default.isNil(storage)) { return callback(_utils2.ErrorCode.getNotFound('this package cannot be added')); } storage.createPackage(name, (0, _storageUtils.generatePackageTemplate)(name), err => { // FIXME: it will be fixed here https://github.com/verdaccio/verdaccio/pull/1360 // @ts-ignore if (_lodash.default.isNull(err) === false && (err.code === _constants.STORAGE.FILE_EXIST_ERROR || err.code === _constants.HTTP_STATUS.CONFLICT)) { return callback(_utils2.ErrorCode.getConflict()); } const latest = (0, _utils.getLatestVersion)(pkg); if (_lodash.default.isNil(latest) === false && pkg.versions[latest]) { return callback(null, pkg.versions[latest]); } return callback(); }); } /** * Remove package. * @param {*} name * @param {*} callback * @return {Function} */ removePackage(name, callback) { const storage = this._getLocalStorage(name); debug('[storage] removing package %o', name); if (_lodash.default.isNil(storage)) { return callback(_utils2.ErrorCode.getNotFound()); } storage.readPackage(name, (err, data) => { if (_lodash.default.isNil(err) === false) { if (err.code === _constants.STORAGE.NO_SUCH_FILE_ERROR || err.code === _constants.HTTP_STATUS.NOT_FOUND) { return callback(_utils2.ErrorCode.getNotFound()); } return callback(err); } data = (0, _storageUtils.normalizePackage)(data); this.storagePlugin.remove(name, removeFailed => { if (removeFailed) { // This will happen when database is locked this.logger.error({ name }, `[storage/removePackage] the database is locked, removed has failed for @{name}`); return callback(_utils2.ErrorCode.getBadData(removeFailed.message)); } storage.deletePackage(_constants.STORAGE.PACKAGE_FILE_NAME, err => { if (err) { return callback(err); } const attachments = Object.keys(data._attachments); this._deleteAttachments(storage, attachments, callback); }); }); }); } /** * Synchronize remote package info with the local one * @param {*} name * @param {*} packageInfo * @param {*} callback */ updateVersions(name, packageInfo, callback) { this._readCreatePackage(name, (err, packageLocalJson) => { if (err) { return callback(err); } let change = false; // updating readme packageLocalJson.readme = (0, _storageUtils.getLatestReadme)(packageInfo); if (packageInfo.readme !== packageLocalJson.readme) { change = true; } for (const versionId in packageInfo.versions) { if (_lodash.default.isNil(packageLocalJson.versions[versionId])) { let version = packageInfo.versions[versionId]; // we don't keep readme for package versions, // only one readme per package version = (0, _storageUtils.cleanUpReadme)(version); version.contributors = (0, _utils.normalizeContributors)(version.contributors); change = true; packageLocalJson.versions[versionId] = version; if (version.dist && version.dist.tarball) { const urlObject = _url.default.parse(version.dist.tarball); const filename = urlObject.pathname.replace(/^.*\//, ''); // we do NOT overwrite any existing records if (_lodash.default.isNil(packageLocalJson._distfiles[filename])) { const hash = packageLocalJson._distfiles[filename] = { url: version.dist.tarball, sha: version.dist.shasum }; /* eslint spaced-comment: 0 */ const upLink = version[Symbol.for('__verdaccio_uplink')]; if (_lodash.default.isNil(upLink) === false) { this._updateUplinkToRemoteProtocol(hash, upLink); } } } } } for (const tag in packageInfo[_constants.DIST_TAGS]) { if (!packageLocalJson[_constants.DIST_TAGS][tag] || packageLocalJson[_constants.DIST_TAGS][tag] !== packageInfo[_constants.DIST_TAGS][tag]) { change = true; packageLocalJson[_constants.DIST_TAGS][tag] = packageInfo[_constants.DIST_TAGS][tag]; } } for (const up in packageInfo._uplinks) { if (Object.prototype.hasOwnProperty.call(packageInfo._uplinks, up)) { const need_change = !(0, _utils2.isObject)(packageLocalJson._uplinks[up]) || packageInfo._uplinks[up].etag !== packageLocalJson._uplinks[up].etag || packageInfo._uplinks[up].fetched !== packageLocalJson._uplinks[up].fetched; if (need_change) { change = true; packageLocalJson._uplinks[up] = packageInfo._uplinks[up]; } } } if ('time' in packageInfo && !_lodash.default.isEqual(packageLocalJson.time, packageInfo.time)) { packageLocalJson.time = packageInfo.time; change = true; } if (change) { debug('updating package %o info', name); this._writePackage(name, packageLocalJson, function (err) { callback(err, packageLocalJson); }); } else { callback(null, packageLocalJson); } }); } /** * Add a new version to a previous local package. * @param {*} name * @param {*} version * @param {*} metadata * @param {*} tag * @param {*} callback */ addVersion(name, version, metadata, tag, callback) { this._updatePackage(name, (data, cb) => { // keep only one readme per package data.readme = metadata.readme; // TODO: lodash remove metadata = (0, _storageUtils.cleanUpReadme)(metadata); metadata.contributors = (0, _utils.normalizeContributors)(metadata.contributors); const hasVersion = data.versions[version] != null; if (hasVersion) { return cb(_utils2.ErrorCode.getConflict()); } // if uploaded tarball has a different shasum, it's very likely that we have some kind of error if ((0, _utils2.isObject)(metadata.dist) && _lodash.default.isString(metadata.dist.tarball)) { const tarball = metadata.dist.tarball.replace(/.*\//, ''); if ((0, _utils2.isObject)(data._attachments[tarball])) { if (_lodash.default.isNil(data._attachments[tarball].shasum) === false && _lodash.default.isNil(metadata.dist.shasum) === false) { if (data._attachments[tarball].shasum != metadata.dist.shasum) { const errorMessage = `shasum error, ${data._attachments[tarball].shasum} != ${metadata.dist.shasum}`; return cb(_utils2.ErrorCode.getBadRequest(errorMessage)); } } const currentDate = new Date().toISOString(); // some old storage do not have this field #740 if (_lodash.default.isNil(data.time)) { data.time = {}; } data.time['modified'] = currentDate; if ('created' in data.time === false) { data.time.created = currentDate; } data.time[version] = currentDate; data._attachments[tarball].version = version; } } data.versions[version] = metadata; (0, _utils2.tagVersion)(data, version, tag); this.storagePlugin.add(name, addFailed => { if (addFailed) { return cb(_utils2.ErrorCode.getBadData(addFailed.message)); } cb(); }); }, callback); } /** * Merge a new list of tags for a local packages with the existing one. * @param {*} pkgName * @param {*} tags * @param {*} callback */ mergeTags(pkgName, tags, callback) { this._updatePackage(pkgName, (data, cb) => { /* eslint guard-for-in: 0 */ for (const tag in tags) { // this handle dist-tag rm command if (_lodash.default.isNull(tags[tag])) { delete data[_constants.DIST_TAGS][tag]; continue; } if (_lodash.default.isNil(data.versions[tags[tag]])) { return cb(this._getVersionNotFound()); } const version = tags[tag]; (0, _utils2.tagVersion)(data, version, tag); } cb(null); }, callback); } /** * Return version not found * @return {String} * @private */ _getVersionNotFound() { return _utils2.ErrorCode.getNotFound(_constants.API_ERROR.VERSION_NOT_EXIST); } /** * Return file no available * @return {String} * @private */ _getFileNotAvailable() { return _utils2.ErrorCode.getNotFound('no such file available'); } /** * Update the package metadata, tags and attachments (tarballs). * Note: Currently supports unpublishing and deprecation. * @param {*} name * @param {*} incomingPkg * @param {*} revision * @param {*} callback * @return {Function} */ changePackage(name, incomingPkg, revision, callback) { if (!(0, _utils2.isObject)(incomingPkg.versions) || !(0, _utils2.isObject)(incomingPkg[_constants.DIST_TAGS])) { this.logger.error({ name }, `changePackage bad data for @{name}`); return callback(_utils2.ErrorCode.getBadData()); } debug('changePackage udapting package for %o', name); this._updatePackage(name, (localData, cb) => { for (const version in localData.versions) { const incomingVersion = incomingPkg.versions[version]; if (_lodash.default.isNil(incomingVersion)) { this.logger.info({ name: name, version: version }, 'unpublishing @{name}@@{version}'); // FIXME: I prefer return a new object rather mutate the metadata delete localData.versions[version]; delete localData.time[version]; for (const file in localData._attachments) { if (localData._attachments[file].version === version) { delete localData._attachments[file].version; } } } else if (Object.prototype.hasOwnProperty.call(incomingVersion, 'deprecated')) { const incomingDeprecated = incomingVersion.deprecated; if (incomingDeprecated != localData.versions[version].deprecated) { if (!incomingDeprecated) { this.logger.info({ name: name, version: version }, 'undeprecating @{name}@@{version}'); delete localData.versions[version].deprecated; } else { this.logger.info({ name: name, version: version }, 'deprecating @{name}@@{version}'); localData.versions[version].deprecated = incomingDeprecated; } localData.time.modified = new Date().toISOString(); } } } localData[_constants.USERS] = incomingPkg[_constants.USERS]; localData[_constants.DIST_TAGS] = incomingPkg[_constants.DIST_TAGS]; cb(null); }, function (err) { if (err) { return callback(err); } callback(); }); } /** * Remove a tarball. * @param {*} name * @param {*} filename * @param {*} revision * @param {*} callback */ removeTarball(name, filename, revision, callback) { (0, _assert.default)((0, _utils.validateName)(filename)); this._updatePackage(name, (data, cb) => { if (data._attachments[filename]) { delete data._attachments[filename]; cb(null); } else { cb(this._getFileNotAvailable()); } }, err => { if (err) { return callback(err); } const storage = this._getLocalStorage(name); if (storage) { storage.deletePackage(filename, callback); } }); } /** * Add a tarball. * @param {String} name * @param {String} filename * @return {Stream} */ addTarball(name, filename) { (0, _assert.default)((0, _utils.validateName)(filename)); let length = 0; const shaOneHash = (0, _utils.createTarballHash)(); const uploadStream = new _streams.UploadTarball({}); const _transform = uploadStream._transform; const storage = this._getLocalStorage(name); uploadStream.abort = function () {}; uploadStream.done = function () {}; uploadStream._transform = function (data, ...args) { shaOneHash.update(data); // measure the length for validation reasons length += data.length; const appliedData = [data, ...args]; // FIXME: not sure about this approach, tsc complains // @ts-ignore _transform.apply(uploadStream, appliedData); }; if (name === '__proto__') { process.nextTick(() => { uploadStream.emit('error', _utils2.ErrorCode.getForbidden()); }); return uploadStream; } if (!storage) { process.nextTick(() => { uploadStream.emit('error', "can't upload this package"); }); return uploadStream; } const writeStream = storage.writeTarball(filename); writeStream.on('error', err => { if (err.code === _constants.STORAGE.FILE_EXIST_ERROR || err.code === _constants.HTTP_STATUS.CONFLICT) { uploadStream.emit('error', _utils2.ErrorCode.getConflict()); uploadStream.abort(); } else if (err.code === _constants.STORAGE.NO_SUCH_FILE_ERROR || err.code === _constants.HTTP_STATUS.NOT_FOUND) { // check if package exists to throw an appropriate message this.getPackageMetadata(name, function (_err) { if (_err) { uploadStream.emit('error', _err); } else { uploadStream.emit('error', err); } }); } else { uploadStream.emit('error', err); } }); writeStream.on('open', function () { // re-emitting open because it's handled in storage.js uploadStream.emit('open'); }); writeStream.on('success', () => { this._updatePackage(name, function updater(data, cb) { data._attachments[filename] = { shasum: shaOneHash.digest('hex') }; cb(null); }, function (err) { if (err) { uploadStream.emit('error', err); } else { uploadStream.emit('success'); } }); }); uploadStream.abort = function () { writeStream.abort(); }; uploadStream.done = function () { if (!length) { uploadStream.emit('error', _utils2.ErrorCode.getBadData('refusing to accept zero-length file')); writeStream.abort(); } else { writeStream.done(); } }; uploadStream.pipe(writeStream); return uploadStream; } /** * Get a tarball. * @param {*} name * @param {*} filename * @return {ReadTarball} */ getTarball(name, filename) { (0, _assert.default)((0, _utils.validateName)(filename)); const storage = this._getLocalStorage(name); if (_lodash.default.isNil(storage)) { return this._createFailureStreamResponse(); } return this._streamSuccessReadTarBall(storage, filename); } /** * Return a stream that emits a read failure. * @private * @return {ReadTarball} */ _createFailureStreamResponse() { const stream = new _streams.ReadTarball({}); process.nextTick(() => { stream.emit('error', this._getFileNotAvailable()); }); return stream; } /** * Return a stream that emits the tarball data * @param {Object} storage * @param {String} filename * @private * @return {ReadTarball} */ _streamSuccessReadTarBall(storage, filename) { const stream = new _streams.ReadTarball({}); const readTarballStream = storage.readTarball(filename); const e404 = _utils2.ErrorCode.getNotFound; stream.abort = function () { if (_lodash.default.isNil(readTarballStream) === false) { readTarballStream.abort(); } }; readTarballStream.on('error', function (err) { if (err.code === _constants.STORAGE.NO_SUCH_FILE_ERROR || err.code === _constants.HTTP_STATUS.NOT_FOUND) { stream.emit('error', e404('no such file available')); } else { stream.emit('error', err); } }); readTarballStream.on('content-length', function (content) { stream.emit('content-length', content); }); readTarballStream.on('open', function () { // re-emitting open because it's handled in storage.js stream.emit('open'); readTarballStream.pipe(stream); }); return stream; } /** * Retrieve a package by name. * @param {*} name * @param {*} callback * @return {Function} */ getPackageMetadata(name, callback = () => {}) { const storage = this._getLocalStorage(name); if (_lodash.default.isNil(storage)) { return callback(_utils2.ErrorCode.getNotFound()); } this._readPackage(name, storage, callback); } getPackageMetadataAsync(name) { return new Promise((resolve, reject) => { this.getPackageMetadata(name, (err, metadata) => { if (err) { return reject(err); } resolve(metadata); }); }); } updateVersionsAsync(name, packageInfo) { return new Promise((resolve, reject) => { this.updateVersions(name, packageInfo, (err, result) => { if (err) { return reject(err); } resolve(result); }); }); } /** * Search a local package. * @param {*} startKey * @param {*} options * @return {Function} */ search(startKey, options) { debug('options for search %o', options); const stream = new _streams.ReadTarball({ objectMode: true }); this._searchEachPackage((item, cb) => { // @ts-ignore if (item.time > parseInt(startKey, 10)) { this.getPackageMetadata(item.name, (err, data) => { if (err) { return cb(err); } // @ts-ignore const time = new Date(item.time).toISOString(); const result = (0, _storageUtils.prepareSearchPackage)(data, time); if (_lodash.default.isNil(result) === false) { stream.push(result); } cb(null); }); } else { cb(null); } }, function onEnd(err) { if (err) { stream.emit('error', err); return; } stream.end(); }); return stream; } /** * Retrieve a wrapper that provide access to the package location. * @param {Object} pkgName package name. * @return {Object} */ _getLocalStorage(pkgName) { return this.storagePlugin.getPackageStorage(pkgName); } /** * Read a json file from storage. * @param {Object} storage * @param {Function} callback */ _readPackage(name, storage, callback) { storage.readPackage(name, (err, result) => { if (err) { if (err.code === _constants.STORAGE.NO_SUCH_FILE_ERROR || err.code === _constants.HTTP_STATUS.NOT_FOUND) { return callback(_utils2.ErrorCode.getNotFound()); } return callback(this._internalError(err, _constants.STORAGE.PACKAGE_FILE_NAME, 'error reading')); } callback(err, (0, _storageUtils.normalizePackage)(result)); }); } /** * Walks through each package and calls `on_package` on them. * @param {*} onPackage * @param {*} onEnd */ _searchEachPackage(onPackage, onEnd) { // save wait whether plugin still do not support search functionality if (_lodash.default.isNil(this.storagePlugin.search)) { this.logger.warn('plugin search not implemented yet'); onEnd(); } else { this.storagePlugin.search(onPackage, onEnd, _utils.validateName); } } /** * Retrieve either a previous created local package or a boilerplate. * @param {*} pkgName * @param {*} callback * @return {Function} */ _readCreatePackage(pkgName, callback) { const storage = this._getLocalStorage(pkgName); if (_lodash.default.isNil(storage)) { this._createNewPackage(pkgName, callback); return; } storage.readPackage(pkgName, (err, data) => { // TODO: race condition if (_lodash.default.isNil(err) === false) { if (err.code === _constants.STORAGE.NO_SUCH_FILE_ERROR || err.code === _constants.HTTP_STATUS.NOT_FOUND) { data = (0, _storageUtils.generatePackageTemplate)(pkgName); } else { return callback(this._internalError(err, _constants.STORAGE.PACKAGE_FILE_NAME, 'error reading')); } } callback(null, (0, _storageUtils.normalizePackage)(data)); }); } _createNewPackage(name, callback) { return callback(null, (0, _storageUtils.normalizePackage)((0, _storageUtils.generatePackageTemplate)(name))); } /** * Handle internal error * @param {*} err * @param {*} file * @param {*} message * @return {Object} Error instance */ _internalError(err, file, message) { this.logger.error({ err: err, file: file }, `${message} @{file}: @{!err.message}`); return _utils2.ErrorCode.getInternalError(); } /** * @param {*} name package name * @param {*} updateHandler function(package, cb) - update function * @param {*} callback callback that gets invoked after it's all updated * @return {Function} */ _updatePackage(name, updateHandler, callback) { const storage = this._getLocalStorage(name); if (!storage) { return callback(_utils2.ErrorCode.getNotFound()); } storage.updatePackage(name, updateHandler, this._writePackage.bind(this), _storageUtils.normalizePackage, callback); } /** * Update the revision (_rev) string for a package. * @param {*} name * @param {*} json * @param {*} callback * @return {Function} */ _writePackage(name, json, callback) { const storage = this._getLocalStorage(name); if (_lodash.default.isNil(storage)) { return callback(); } storage.savePackage(name, this._setDefaultRevision(json), callback); } _setDefaultRevision(json) { // calculate revision from couch db if (_lodash.default.isString(json._rev) === false) { json._rev = _constants.STORAGE.DEFAULT_REVISION; } // this is intended in debug mode we do not want modify the store revision if (_lodash.default.isNil(this.config._debug)) { json._rev = (0, _storageUtils.generateRevision)(json._rev); } return json; } _deleteAttachments(storage, attachments, callback) { debug('[storage/_deleteAttachments] delete attachments total: %o', attachments?.length); const unlinkNext = function (cb) { if (_lodash.default.isEmpty(attachments)) { return cb(); } const attachment = attachments.shift(); storage.deletePackage(attachment, function () { unlinkNext(cb); }); }; unlinkNext(function () { // try to unlink the directory, but ignore errors because it can fail storage.removePackage(function (err) { callback(err); }); }); } /** * Ensure the dist file remains as the same protocol * @param {Object} hash metadata * @param {String} upLinkKey registry key * @private */ _updateUplinkToRemoteProtocol(hash, upLinkKey) { // if we got this information from a known registry, // use the same protocol for the tarball // // see https://github.com/rlidwka/sinopia/issues/166 const tarballUrl = _url.default.parse(hash.url); const uplinkUrl = _url.default.parse(this.config.uplinks[upLinkKey].url); if (uplinkUrl.host === tarballUrl.host) { tarballUrl.protocol = uplinkUrl.protocol; hash.registry = upLinkKey; hash.url = _url.default.format(tarballUrl); } } async getSecret(config) { const secretKey = await this.storagePlugin.getSecret(); return this.storagePlugin.setSecret(config.checkSecretKey(secretKey)); } saveToken(token) { if (_lodash.default.isFunction(this.storagePlugin.saveToken) === false) { return Promise.reject(_utils2.ErrorCode.getCode(_constants.HTTP_STATUS.SERVICE_UNAVAILABLE, _constants.SUPPORT_ERRORS.PLUGIN_MISSING_INTERFACE)); } return this.storagePlugin.saveToken(token); } deleteToken(user, tokenKey) { if (_lodash.default.isFunction(this.storagePlugin.deleteToken) === false) { return Promise.reject(_utils2.ErrorCode.getCode(_constants.HTTP_STATUS.SERVICE_UNAVAILABLE, _constants.SUPPORT_ERRORS.PLUGIN_MISSING_INTERFACE)); } return this.storagePlugin.deleteToken(user, tokenKey); } readTokens(filter) { if (_lodash.default.isFunction(this.storagePlugin.readTokens) === false) { return Promise.reject(_utils2.ErrorCode.getCode(_constants.HTTP_STATUS.SERVICE_UNAVAILABLE, _constants.SUPPORT_ERRORS.PLUGIN_MISSING_INTERFACE)); } return this.storagePlugin.readTokens(filter); } } var _default = exports.default = LocalStorage; //# sourceMappingURL=data:application/json;charset=utf-8;base64,eyJ2ZXJzaW9uIjozLCJuYW1lcyI6WyJfYXNzZXJ0IiwiX2ludGVyb3BSZXF1aXJlRGVmYXVsdCIsInJlcXVpcmUiLCJfZGVidWciLCJfbG9kYXNoIiwiX3VybCIsIl9zdHJlYW1zIiwiX3V0aWxzIiwiX2NvbnN0YW50cyIsIl9zdG9yYWdlVXRpbHMiLCJfdXRpbHMyIiwiZSIsIl9fZXNNb2R1bGUiLCJkZWZhdWx0IiwiZGVidWciLCJidWlsRGVidWciLCJMb2NhbFN0b3JhZ2UiLCJjb25zdHJ1Y3RvciIsImNvbmZpZyIsImxvZ2dlciIsImxvY2FsU3RvcmFnZSIsInN0b3JhZ2VQbHVnaW4iLCJhZGRQYWNrYWdlIiwibmFtZSIsInBrZyIsImNhbGxiYWNrIiwic3RvcmFnZSIsIl9nZXRMb2NhbFN0b3JhZ2UiLCJfIiwiaXNOaWwiLCJFcnJvckNvZGUiLCJnZXROb3RGb3VuZCIsImNyZWF0ZVBhY2thZ2UiLCJnZW5lcmF0ZVBhY2thZ2VUZW1wbGF0ZSIsImVyciIsImlzTnVsbCIsImNvZGUiLCJTVE9SQUdFIiwiRklMRV9FWElTVF9FUlJPUiIsIkhUVFBfU1RBVFVTIiwiQ09ORkxJQ1QiLCJnZXRDb25mbGljdCIsImxhdGVzdCIsImdldExhdGVzdFZlcnNpb24iLCJ2ZXJzaW9ucyIsInJlbW92ZVBhY2thZ2UiLCJyZWFkUGFja2FnZSIsImRhdGEiLCJOT19TVUNIX0ZJTEVfRVJST1IiLCJOT1RfRk9VTkQiLCJub3JtYWxpemVQYWNrYWdlIiwicmVtb3ZlIiwicmVtb3ZlRmFpbGVkIiwiZXJyb3IiLCJnZXRCYWREYXRhIiwibWVzc2FnZSIsImRlbGV0ZVBhY2thZ2UiLCJQQUNLQUdFX0ZJTEVfTkFNRSIsImF0dGFjaG1lbnRzIiwiT2JqZWN0Iiwia2V5cyIsIl9hdHRhY2htZW50cyIsIl9kZWxldGVBdHRhY2htZW50cyIsInVwZGF0ZVZlcnNpb25zIiwicGFja2FnZUluZm8iLCJfcmVhZENyZWF0ZVBhY2thZ2UiLCJwYWNrYWdlTG9jYWxKc29uIiwiY2hhbmdlIiwicmVhZG1lIiwiZ2V0TGF0ZXN0UmVhZG1lIiwidmVyc2lvbklkIiwidmVyc2lvbiIsImNsZWFuVXBSZWFkbWUiLCJjb250cmlidXRvcnMiLCJub3JtYWxpemVDb250cmlidXRvcnMiLCJkaXN0IiwidGFyYmFsbCIsInVybE9iamVjdCIsIlVybE5vZGUiLCJwYXJzZSIsImZpbGVuYW1lIiwicGF0aG5hbWUiLCJyZXBsYWNlIiwiX2Rpc3RmaWxlcyIsImhhc2giLCJ1cmwiLCJzaGEiLCJzaGFzdW0iLCJ1cExpbmsiLCJTeW1ib2wiLCJmb3IiLCJfdXBkYXRlVXBsaW5rVG9SZW1vdGVQcm90b2NvbCIsInRhZyIsIkRJU1RfVEFHUyIsInVwIiwiX3VwbGlua3MiLCJwcm90b3R5cGUiLCJoYXNPd25Qcm9wZXJ0eSIsImNhbGwiLCJuZWVkX2NoYW5nZSIsImlzT2JqZWN0IiwiZXRhZyIsImZldGNoZWQiLCJpc0VxdWFsIiwidGltZSIsIl93cml0ZVBhY2thZ2UiLCJhZGRWZXJzaW9uIiwibWV0YWRhdGEiLCJfdXBkYXRlUGFja2FnZSIsImNiIiwiaGFzVmVyc2lvbiIsImlzU3RyaW5nIiwiZXJyb3JNZXNzYWdlIiwiZ2V0QmFkUmVxdWVzdCIsImN1cnJlbnREYXRlIiwiRGF0ZSIsInRvSVNPU3RyaW5nIiwiY3JlYXRlZCIsInRhZ1ZlcnNpb24iLCJhZGQiLCJhZGRGYWlsZWQiLCJtZXJnZVRhZ3MiLCJwa2dOYW1lIiwidGFncyIsIl9nZXRWZXJzaW9uTm90Rm91bmQiLCJBUElfRVJST1IiLCJWRVJTSU9OX05PVF9FWElTVCIsIl9nZXRGaWxlTm90QXZhaWxhYmxlIiwiY2hhbmdlUGFja2FnZSIsImluY29taW5nUGtnIiwicmV2aXNpb24iLCJsb2NhbERhdGEiLCJpbmNvbWluZ1ZlcnNpb24iLCJpbmZvIiwiZmlsZSIsImluY29taW5nRGVwcmVjYXRlZCIsImRlcHJlY2F0ZWQiLCJtb2RpZmllZCIsIlVTRVJTIiwicmVtb3ZlVGFyYmFsbCIsImFzc2VydCIsInZhbGlkYXRlTmFtZSIsImFkZFRhcmJhbGwiLCJsZW5ndGgiLCJzaGFPbmVIYXNoIiwiY3JlYXRlVGFyYmFsbEhhc2giLCJ1cGxvYWRTdHJlYW0iLCJVcGxvYWRUYXJiYWxsIiwiX3RyYW5zZm9ybSIsImFib3J0IiwiZG9uZSIsImFyZ3MiLCJ1cGRhdGUiLCJhcHBsaWVkRGF0YSIsImFwcGx5IiwicHJvY2VzcyIsIm5leHRUaWNrIiwiZW1pdCIsImdldEZvcmJpZGRlbiIsIndyaXRlU3RyZWFtIiwid3JpdGVUYXJiYWxsIiwib24iLCJnZXRQYWNrYWdlTWV0YWRhdGEiLCJfZXJyIiwidXBkYXRlciIsImRpZ2VzdCIsInBpcGUiLCJnZXRUYXJiYWxsIiwiX2NyZWF0ZUZhaWx1cmVTdHJlYW1SZXNwb25zZSIsIl9zdHJlYW1TdWNjZXNzUmVhZFRhckJhbGwiLCJzdHJlYW0iLCJSZWFkVGFyYmFsbCIsInJlYWRUYXJiYWxsU3RyZWFtIiwicmVhZFRhcmJhbGwiLCJlNDA0IiwiY29udGVudCIsIl9yZWFkUGFja2FnZSIsImdldFBhY2thZ2VNZXRhZGF0YUFzeW5jIiwiUHJvbWlzZSIsInJlc29sdmUiLCJyZWplY3QiLCJ1cGRhdGVWZXJzaW9uc0FzeW5jIiwicmVzdWx0Iiwic2VhcmNoIiwic3RhcnRLZXkiLCJvcHRpb25zIiwib2JqZWN0TW9kZSIsIl9zZWFyY2hFYWNoUGFja2FnZSIsIml0ZW0iLCJwYXJzZUludCIsInByZXBhcmVTZWFyY2hQYWNrYWdlIiwicHVzaCIsIm9uRW5kIiwiZW5kIiwiZ2V0UGFja2FnZVN0b3JhZ2UiLCJfaW50ZXJuYWxFcnJvciIsIm9uUGFja2FnZSIsIndhcm4iLCJfY3JlYXRlTmV3UGFja2FnZSIsImdldEludGVybmFsRXJyb3IiLCJ1cGRhdGVIYW5kbGVyIiwidXBkYXRlUGFja2FnZSIsImJpbmQiLCJqc29uIiwic2F2ZVBhY2thZ2UiLCJfc2V0RGVmYXVsdFJldmlzaW9uIiwiX3JldiIsIkRFRkFVTFRfUkVWSVNJT04iLCJnZW5lcmF0ZVJldmlzaW9uIiwidW5saW5rTmV4dCIsImlzRW1wdHkiLCJhdHRhY2htZW50Iiwic2hpZnQiLCJ1cExpbmtLZXkiLCJ0YXJiYWxsVXJsIiwidXBsaW5rVXJsIiwidXBsaW5rcyIsImhvc3QiLCJwcm90b2NvbCIsInJlZ2lzdHJ5IiwiZm9ybWF0IiwiZ2V0U2VjcmV0Iiwic2VjcmV0S2V5Iiwic2V0U2VjcmV0IiwiY2hlY2tTZWNyZXRLZXkiLCJzYXZlVG9rZW4iLCJ0b2tlbiIsImlzRnVuY3Rpb24iLCJnZXRDb2RlIiwiU0VSVklDRV9VTkFWQUlMQUJMRSIsIlNVUFBPUlRfRVJST1JTIiwiUExVR0lOX01JU1NJTkdfSU5URVJGQUNFIiwiZGVsZXRlVG9rZW4iLCJ1c2VyIiwidG9rZW5LZXkiLCJyZWFkVG9rZW5zIiwiZmlsdGVyIiwiX2RlZmF1bHQiLCJleHBvcnRzIl0sInNvdXJjZXMiOlsiLi4vLi4vc3JjL2xpYi9sb2NhbC1zdG9yYWdlLnRzIl0sInNvdXJjZXNDb250ZW50IjpbImltcG9ydCBhc3NlcnQgZnJvbSAnYXNzZXJ0JztcbmltcG9ydCBidWlsRGVidWcgZnJvbSAnZGVidWcnO1xuaW1wb3J0IF8gZnJvbSAnbG9kYXNoJztcbmltcG9ydCBVcmxOb2RlIGZyb20gJ3VybCc7XG5cbmltcG9ydCB7IFJlYWRUYXJiYWxsLCBVcGxvYWRUYXJiYWxsIH0gZnJvbSAnQHZlcmRhY2Npby9zdHJlYW1zJztcbmltcG9ydCB7XG4gIEF1dGhvcixcbiAgQ2FsbGJhY2ssXG4gIENvbmZpZyxcbiAgRGlzdEZpbGUsXG4gIExvZ2dlcixcbiAgTWFuaWZlc3QsXG4gIE1lcmdlVGFncyxcbiAgU3RvcmFnZVVwZGF0ZUNhbGxiYWNrLFxuICBUb2tlbixcbiAgVG9rZW5GaWx0ZXIsXG4gIFZlcnNpb24sXG4gIG9uRW5kU2VhcmNoUGFja2FnZSxcbiAgb25TZWFyY2hQYWNrYWdlLFxufSBmcm9tICdAdmVyZGFjY2lvL3R5cGVzJztcbmltcG9ydCB7XG4gIGNyZWF0ZVRhcmJhbGxIYXNoLFxuICBnZXRMYXRlc3RWZXJzaW9uLFxuICBub3JtYWxpemVDb250cmlidXRvcnMsXG4gIHZhbGlkYXRlTmFtZSxcbn0gZnJvbSAnQHZlcmRhY2Npby91dGlscyc7XG5cbmltcG9ydCB7IFN0b3JhZ2VQbHVnaW5MZWdhY3kgfSBmcm9tICcuLi8uLi90eXBlcy9jdXN0b20nO1xuaW1wb3J0IHsgU3RyaW5nVmFsdWUgfSBmcm9tICcuLi90eXBlcyc7XG5pbXBvcnQgeyBBUElfRVJST1IsIERJU1RfVEFHUywgSFRUUF9TVEFUVVMsIFNUT1JBR0UsIFNVUFBPUlRfRVJST1JTLCBVU0VSUyB9IGZyb20gJy4vY29uc3RhbnRzJztcbmltcG9ydCB7XG4gIGNsZWFuVXBSZWFkbWUsXG4gIGdlbmVyYXRlUGFja2FnZVRlbXBsYXRlLFxuICBnZW5lcmF0ZVJldmlzaW9uLFxuICBnZXRMYXRlc3RSZWFkbWUsXG4gIG5vcm1hbGl6ZVBhY2thZ2UsXG59IGZyb20gJy4vc3RvcmFnZS11dGlscyc7XG5pbXBvcnQgeyBwcmVwYXJlU2VhcmNoUGFja2FnZSB9IGZyb20gJy4vc3RvcmFnZS11dGlscyc7XG5pbXBvcnQgeyBFcnJvckNvZGUsIGlzT2JqZWN0LCB0YWdWZXJzaW9uIH0gZnJvbSAnLi91dGlscyc7XG5cbmNvbnN0IGRlYnVnID0gYnVpbERlYnVnKCd2ZXJkYWNjaW86bG9jYWwtc3RvcmFnZScpO1xuZXhwb3J0IHR5cGUgU3RvcmFnZVBsdWdpbiA9IFN0b3JhZ2VQbHVnaW5MZWdhY3k8Q29uZmlnPiB8IGFueTtcbi8qKlxuICogSW1wbGVtZW50cyBTdG9yYWdlIGludGVyZmFjZSAoc2FtZSBmb3Igc3RvcmFnZS5qcywgbG9jYWwtc3RvcmFnZS5qcywgdXAtc3RvcmFnZS5qcykuXG4gKi9cbmNsYXNzIExvY2FsU3RvcmFnZSB7XG4gIHB1YmxpYyBjb25maWc6IENvbmZpZztcbiAgcHVibGljIHN0b3JhZ2VQbHVnaW46IFN0b3JhZ2VQbHVnaW47XG4gIHB1YmxpYyBsb2dnZXI6IExvZ2dlcjtcblxuICBwdWJsaWMgY29uc3RydWN0b3IoY29uZmlnOiBDb25maWcsIGxvZ2dlcjogTG9nZ2VyLCBsb2NhbFN0b3JhZ2U6IFN0b3JhZ2VQbHVnaW4pIHtcbiAgICB0aGlzLmxvZ2dlciA9IGxvZ2dlcjtcbiAgICB0aGlzLmNvbmZpZyA9IGNvbmZpZztcbiAgICB0aGlzLnN0b3JhZ2VQbHVnaW4gPSBsb2NhbFN0b3JhZ2U7XG4gIH1cblxuICBwdWJsaWMgYWRkUGFja2FnZShuYW1lOiBzdHJpbmcsIHBrZzogTWFuaWZlc3QsIGNhbGxiYWNrOiBDYWxsYmFjayk6IHZvaWQge1xuICAgIGNvbnN0IHN0b3JhZ2U6IGFueSA9IHRoaXMuX2dldExvY2FsU3RvcmFnZShuYW1lKTtcblxuICAgIGlmIChfLmlzTmlsKHN0b3JhZ2UpKSB7XG4gICAgICByZXR1cm4gY2FsbGJhY2soRXJyb3JDb2RlLmdldE5vdEZvdW5kKCd0aGlzIHBhY2thZ2UgY2Fubm90IGJlIGFkZGVkJykpO1xuICAgIH1cblxuICAgIHN0b3JhZ2UuY3JlYXRlUGFja2FnZShuYW1lLCBnZW5lcmF0ZVBhY2thZ2VUZW1wbGF0ZShuYW1lKSwgKGVycikgPT4ge1xuICAgICAgLy8gRklYTUU6IGl0IHdpbGwgYmUgZml4ZWQgaGVyZSBodHRwczovL2dpdGh1Yi5jb20vdmVyZGFjY2lvL3ZlcmRhY2Npby9wdWxsLzEzNjBcbiAgICAgIC8vIEB0cy1pZ25vcmVcbiAgICAgIGlmIChcbiAgICAgICAgXy5pc051bGwoZXJyKSA9PT0gZmFsc2UgJiZcbiAgICAgICAgKGVyci5jb2RlID09PSBTVE9SQUdFLkZJTEVfRVhJU1RfRVJST1IgfHwgZXJyLmNvZGUgPT09IEhUVFBfU1RBVFVTLkNPTkZMSUNUKVxuICAgICAgKSB7XG4gICAgICAgIHJldHVybiBjYWxsYmFjayhFcnJvckNvZGUuZ2V0Q29uZmxpY3QoKSk7XG4gICAgICB9XG5cbiAgICAgIGNvbnN0IGxhdGVzdCA9IGdldExhdGVzdFZlcnNpb24ocGtnKTtcbiAgICAgIGlmIChfLmlzTmlsKGxhdGVzdCkgPT09IGZhbHNlICYmIHBrZy52ZXJzaW9uc1tsYXRlc3RdKSB7XG4gICAgICAgIHJldHVybiBjYWxsYmFjayhudWxsLCBwa2cudmVyc2lvbnNbbGF0ZXN0XSk7XG4gICAgICB9XG5cbiAgICAgIHJldHVybiBjYWxsYmFjaygpO1xuICAgIH0pO1xuICB9XG5cbiAgLyoqXG4gICAqIFJlbW92ZSBwYWNrYWdlLlxuICAgKiBAcGFyYW0geyp9IG5hbWVcbiAgICogQHBhcmFtIHsqfSBjYWxsYmFja1xuICAgKiBAcmV0dXJuIHtGdW5jdGlvbn1cbiAgICovXG4gIHB1YmxpYyByZW1vdmVQYWNrYWdlKG5hbWU6IHN0cmluZywgY2FsbGJhY2s6IENhbGxiYWNrKTogdm9pZCB7XG4gICAgY29uc3Qgc3RvcmFnZTogYW55ID0gdGhpcy5fZ2V0TG9jYWxTdG9yYWdlKG5hbWUpO1xuICAgIGRlYnVnKCdbc3RvcmFnZV0gcmVtb3ZpbmcgcGFja2FnZSAlbycsIG5hbWUpO1xuICAgIGlmIChfLmlzTmlsKHN0b3JhZ2UpKSB7XG4gICAgICByZXR1cm4gY2FsbGJhY2soRXJyb3JDb2RlLmdldE5vdEZvdW5kKCkpO1xuICAgIH1cblxuICAgIHN0b3JhZ2UucmVhZFBhY2thZ2UobmFtZSwgKGVyciwgZGF0YTogTWFuaWZlc3QpOiB2b2lkID0+IHtcbiAgICAgIGlmIChfLmlzTmlsKGVycikgPT09IGZhbHNlKSB7XG4gICAgICAgIGlmIChlcnIuY29kZSA9PT0gU1RPUkFHRS5OT19TVUNIX0ZJTEVfRVJST1IgfHwgZXJyLmNvZGUgPT09IEhUVFBfU1RBVFVTLk5PVF9GT1VORCkge1xuICAgICAgICAgIHJldHVybiBjYWxsYmFjayhFcnJvckNvZGUuZ2V0Tm90Rm91bmQoKSk7XG4gICAgICAgIH1cbiAgICAgICAgcmV0dXJuIGNhbGxiYWNrKGVycik7XG4gICAgICB9XG5cbiAgICAgIGRhdGEgPSBub3JtYWxpemVQYWNrYWdlKGRhdGEpO1xuICAgICAgdGhpcy5zdG9yYWdlUGx1Z2luLnJlbW92ZShuYW1lLCAocmVtb3ZlRmFpbGVkOiBFcnJvcik6IHZvaWQgPT4ge1xuICAgICAgICBpZiAocmVtb3ZlRmFpbGVkKSB7XG4gICAgICAgICAgLy8gVGhpcyB3aWxsIGhhcHBlbiB3aGVuIGRhdGFiYXNlIGlzIGxvY2tlZFxuICAgICAgICAgIHRoaXMubG9nZ2VyLmVycm9yKFxuICAgICAgICAgICAgeyBuYW1lIH0sXG4gICAgICAgICAgICBgW3N0b3JhZ2UvcmVtb3ZlUGFja2FnZV0gdGhlIGRhdGFiYXNlIGlzIGxvY2tlZCwgcmVtb3ZlZCBoYXMgZmFpbGVkIGZvciBAe25hbWV9YFxuICAgICAgICAgICk7XG4gICAgICAgICAgcmV0dXJuIGNhbGxiYWNrKEVycm9yQ29kZS5nZXRCYWREYXRhKHJlbW92ZUZhaWxlZC5tZXNzYWdlKSk7XG4gICAgICAgIH1cblxuICAgICAgICBzdG9yYWdlLmRlbGV0ZVBhY2thZ2UoU1RPUkFHRS5QQUNLQUdFX0ZJTEVfTkFNRSwgKGVycik6IHZvaWQgPT4ge1xuICAgICAgICAgIGlmIChlcnIpIHtcbiAgICAgICAgICAgIHJldHVybiBjYWxsYmFjayhlcnIpO1xuICAgICAgICAgIH1cbiAgICAgICAgICBjb25zdCBhdHRhY2htZW50cyA9IE9iamVjdC5rZXlzKGRhdGEuX2F0dGFjaG1lbnRzKTtcblxuICAgICAgICAgIHRoaXMuX2RlbGV0ZUF0dGFjaG1lbnRzKHN0b3JhZ2UsIGF0dGFjaG1lbnRzLCBjYWxsYmFjayk7XG4gICAgICAgIH0pO1xuICAgICAgfSk7XG4gICAgfSk7XG4gIH1cblxuICAvKipcbiAgICogU3luY2hyb25pemUgcmVtb3RlIHBhY2thZ2UgaW5mbyB3aXRoIHRoZSBsb2NhbCBvbmVcbiAgICogQHBhcmFtIHsqfSBuYW1lXG4gICAqIEBwYXJhbSB7Kn0gcGFja2FnZUluZm9cbiAgICogQHBhcmFtIHsqfSBjYWxsYmFja1xuICAgKi9cbiAgcHVibGljIHVwZGF0ZVZlcnNpb25zKG5hbWU6IHN0cmluZywgcGFja2FnZUluZm86IE1hbmlmZXN0LCBjYWxsYmFjazogQ2FsbGJhY2spOiB2b2lkIHtcbiAgICB0aGlzLl9yZWFkQ3JlYXRlUGFja2FnZShuYW1lLCAoZXJyLCBwYWNrYWdlTG9jYWxKc29uKTogdm9pZCA9PiB7XG4gICAgICBpZiAoZXJyKSB7XG4gICAgICAgIHJldHVybiBjYWxsYmFjayhlcnIpO1xuICAgICAgfVxuXG4gICAgICBsZXQgY2hhbmdlID0gZmFsc2U7XG4gICAgICAvLyB1cGRhdGluZyByZWFkbWVcbiAgICAgIHBhY2thZ2VMb2NhbEpzb24ucmVhZG1lID0gZ2V0TGF0ZXN0UmVhZG1lKHBhY2thZ2VJbmZvKTtcbiAgICAgIGlmIChwYWNrYWdlSW5mby5yZWFkbWUgIT09IHBhY2thZ2VMb2NhbEpzb24ucmVhZG1lKSB7XG4gICAgICAgIGNoYW5nZSA9IHRydWU7XG4gICAgICB9XG4gICAgICBmb3IgKGNvbnN0IHZlcnNpb25JZCBpbiBwYWNrYWdlSW5mby52ZXJzaW9ucykge1xuICAgICAgICBpZiAoXy5pc05pbChwYWNrYWdlTG9jYWxKc29uLnZlcnNpb25zW3ZlcnNpb25JZF0pKSB7XG4gICAgICAgICAgbGV0IHZlcnNpb24gPSBwYWNrYWdlSW5mby52ZXJzaW9uc1t2ZXJzaW9uSWRdO1xuXG4gICAgICAgICAgLy8gd2UgZG9uJ3Qga2VlcCByZWFkbWUgZm9yIHBhY2thZ2UgdmVyc2lvbnMsXG4gICAgICAgICAgLy8gb25seSBvbmUgcmVhZG1lIHBlciBwYWNrYWdlXG4gICAgICAgICAgdmVyc2lvbiA9IGNsZWFuVXBSZWFkbWUodmVyc2lvbik7XG4gICAgICAgICAgdmVyc2lvbi5jb250cmlidXRvcnMgPSBub3JtYWxpemVDb250cmlidXRvcnModmVyc2lvbi5jb250cmlidXRvcnMgYXMgQXV0aG9yW10pO1xuXG4gICAgICAgICAgY2hhbmdlID0gdHJ1ZTtcbiAgICAgICAgICBwYWNrYWdlTG9jYWxKc29uLnZlcnNpb25zW3ZlcnNpb25JZF0gPSB2ZXJzaW9uO1xuXG4gICAgICAgICAgaWYgKHZlcnNpb24uZGlzdCAmJiB2ZXJzaW9uLmRpc3QudGFyYmFsbCkge1xuICAgICAgICAgICAgY29uc3QgdXJsT2JqZWN0OiBhbnkgPSBVcmxOb2RlLnBhcnNlKHZlcnNpb24uZGlzdC50YXJiYWxsKTtcbiAgICAgICAgICAgIGNvbnN0IGZpbGVuYW1lID0gdXJsT2JqZWN0LnBhdGhuYW1lLnJlcGxhY2UoL14uKlxcLy8sICcnKTtcblxuICAgICAgICAgICAgLy8gd2UgZG8gTk9UIG92ZXJ3cml0ZSBhbnkgZXhpc3RpbmcgcmVjb3Jkc1xuICAgICAgICAgICAgaWYgKF8uaXNOaWwocGFja2FnZUxvY2FsSnNvbi5fZGlzdGZpbGVzW2ZpbGVuYW1lXSkpIHtcbiAgICAgICAgICAgICAgY29uc3QgaGFzaDogRGlzdEZpbGUgPSAocGFja2FnZUxvY2FsSnNvbi5fZGlzdGZpbGVzW2ZpbGVuYW1lXSA9IHtcbiAgICAgICAgICAgICAgICB1cmw6IHZlcnNpb24uZGlzdC50YXJiYWxsLFxuICAgICAgICAgICAgICAgIHNoYTogdmVyc2lvbi5kaXN0LnNoYXN1bSxcbiAgICAgICAgICAgICAgfSk7XG4gICAgICAgICAgICAgIC8qIGVzbGludCBzcGFjZWQtY29tbWVudDogMCAqL1xuICAgICAgICAgICAgICBjb25zdCB1cExpbms6IHN0cmluZyA9IHZlcnNpb25bU3ltYm9sLmZvcignX192ZXJkYWNjaW9fdXBsaW5rJyldO1xuXG4gICAgICAgICAgICAgIGlmIChfLmlzTmlsKHVwTGluaykgPT09IGZhbHNlKSB7XG4gICAgICAgICAgICAgICAgdGhpcy5fdXBkYXRlVXBsaW5rVG9SZW1vdGVQcm90b2NvbChoYXNoLCB1cExpbmspO1xuICAgICAgICAgICAgICB9XG4gICAgICAgICAgICB9XG4gICAgICAgICAgfVxuICAgICAgICB9XG4gICAgICB9XG5cbiAgICAgIGZvciAoY29uc3QgdGFnIGluIHBhY2thZ2VJbmZvW0RJU1RfVEFHU10pIHtcbiAgICAgICAgaWYgKFxuICAgICAgICAgICFwYWNrYWdlTG9jYWxKc29uW0RJU1RfVEFHU11bdGFnXSB8fFxuICAgICAgICAgIHBhY2thZ2VMb2NhbEpzb25bRElTVF9UQUdTXVt0YWddICE9PSBwYWNrYWdlSW5mb1tESVNUX1RBR1NdW3RhZ11cbiAgICAgICAgKSB7XG4gICAgICAgICAgY2hhbmdlID0gdHJ1ZTtcbiAgICAgICAgICBwYWNrYWdlTG9jYWxKc29uW0RJU1RfVEFHU11bdGFnXSA9IHBhY2thZ2VJbmZvW0RJU1RfVEFHU11bdGFnXTtcbiAgICAgICAgfVxuICAgICAgfVxuXG4gICAgICBmb3IgKGNvbnN0IHVwIGluIHBhY2thZ2VJbmZvLl91cGxpbmtzKSB7XG4gICAgICAgIGlmIChPYmplY3QucHJvdG90eXBlLmhhc093blByb3BlcnR5LmNhbGwocGFja2FnZUluZm8uX3VwbGlua3MsIHVwKSkge1xuICAgICAgICAgIGNvbnN0IG5lZWRfY2hhbmdlID1cbiAgICAgICAgICAgICFpc09iamVjdChwYWNrYWdlTG9jYWxKc29uLl91cGxpbmtzW3VwXSkgfHxcbiAgICAgICAgICAgIHBhY2thZ2VJbmZvLl91cGxpbmtzW3VwXS5ldGFnICE9PSBwYWNrYWdlTG9jYWxKc29uLl91cGxpbmtzW3VwXS5ldGFnIHx8XG4gICAgICAgICAgICBwYWNrYWdlSW5mby5fdXBsaW5rc1t1cF0uZmV0Y2hlZCAhPT0gcGFja2FnZUxvY2FsSnNvbi5fdXBsaW5rc1t1cF0uZmV0Y2hlZDtcblxuICAgICAgICAgIGlmIChuZWVkX2NoYW5nZSkge1xuICAgICAgICAgICAgY2hhbmdlID0gdHJ1ZTtcbiAgICAgICAgICAgIHBhY2thZ2VMb2NhbEpzb24uX3VwbGlua3NbdXBdID0gcGFja2FnZUluZm8uX3VwbGlua3NbdXBdO1xuICAgICAgICAgIH1cbiAgICAgICAgfVxuICAgICAgfVxuXG4gICAgICBpZiAoJ3RpbWUnIGluIHBhY2thZ2VJbmZvICYmICFfLmlzRXF1YWwocGFja2FnZUxvY2FsSnNvbi50aW1lLCBwYWNrYWdlSW5mby50aW1lKSkge1xuICAgICAgICBwYWNrYWdlTG9jYWxKc29uLnRpbWUgPSBwYWNrYWdlSW5mby50aW1lO1xuICAgICAgICBjaGFuZ2UgPSB0cnVlO1xuICAgICAgfVxuXG4gICAgICBpZiAoY2hhbmdlKSB7XG4gICAgICAgIGRlYnVnKCd1cGRhdGluZyBwYWNrYWdlICVvIGluZm8nLCBuYW1lKTtcbiAgICAgICAgdGhpcy5fd3JpdGVQYWNrYWdlKG5hbWUsIHBhY2thZ2VMb2NhbEpzb24sIGZ1bmN0aW9uIChlcnIpOiB2b2lkIHtcbiAgICAgICAgICBjYWxsYmFjayhlcnIsIHBhY2thZ2VMb2NhbEpzb24pO1xuICAgICAgICB9KTtcbiAgICAgIH0gZWxzZSB7XG4gICAgICAgIGNhbGxiYWNrKG51bGwsIHBhY2thZ2VMb2NhbEpzb24pO1xuICAgICAgfVxuICAgIH0pO1xuICB9XG5cbiAgLyoqXG4gICAqIEFkZCBhIG5ldyB2ZXJzaW9uIHRvIGEgcHJldmlvdXMgbG9jYWwgcGFja2FnZS5cbiAgICogQHBhcmFtIHsqfSBuYW1lXG4gICAqIEBwYXJhbSB7Kn0gdmVyc2lvblxuICAgKiBAcGFyYW0geyp9IG1ldGFkYXRhXG4gICAqIEBwYXJhbSB7Kn0gdGFnXG4gICAqIEBwYXJhbSB7Kn0gY2FsbGJhY2tcbiAgICovXG4gIHB1YmxpYyBhZGRWZXJzaW9uKFxuICAgIG5hbWU6IHN0cmluZyxcbiAgICB2ZXJzaW9uOiBzdHJpbmcsXG4gICAgbWV0YWRhdGE6IFZlcnNpb24sXG4gICAgdGFnOiBTdHJpbmdWYWx1ZSxcbiAgICBjYWxsYmFjazogQ2FsbGJhY2tcbiAgKTogdm9pZCB7XG4gICAgdGhpcy5fdXBkYXRlUGFja2FnZShcbiAgICAgIG5hbWUsXG4gICAgICAoZGF0YSwgY2I6IENhbGxiYWNrKTogdm9pZCA9PiB7XG4gICAgICAgIC8vIGtlZXAgb25seSBvbmUgcmVhZG1lIHBlciBwYWNrYWdlXG4gICAgICAgIGRhdGEucmVhZG1lID0gbWV0YWRhdGEucmVhZG1lO1xuXG4gICAgICAgIC8vIFRPRE86IGxvZGFzaCByZW1vdmVcbiAgICAgICAgbWV0YWRhdGEgPSBjbGVhblVwUmVhZG1lKG1ldGFkYXRhKTtcbiAgICAgICAgbWV0YWRhdGEuY29udHJpYnV0b3JzID0gbm9ybWFsaXplQ29udHJpYnV0b3JzKG1ldGFkYXRhLmNvbnRyaWJ1dG9ycyBhcyBBdXRob3JbXSk7XG5cbiAgICAgICAgY29uc3QgaGFzVmVyc2lvbiA9IGRhdGEudmVyc2lvbnNbdmVyc2lvbl0gIT0gbnVsbDtcbiAgICAgICAgaWYgKGhhc1ZlcnNpb24pIHtcbiAgICAgICAgICByZXR1cm4gY2IoRXJyb3JDb2RlLmdldENvbmZsaWN0KCkpO1xuICAgICAgICB9XG5cbiAgICAgICAgLy8gaWYgdXBsb2FkZWQgdGFyYmFsbCBoYXMgYSBkaWZmZXJlbnQgc2hhc3VtLCBpdCdzIHZlcnkgbGlrZWx5IHRoYXQgd2UgaGF2ZSBzb21lIGtpbmQgb2YgZXJyb3JcbiAgICAgICAgaWYgKGlzT2JqZWN0KG1ldGFkYXRhLmRpc3QpICYmIF8uaXNTdHJpbmcobWV0YWRhdGEuZGlzdC50YXJiYWxsKSkge1xuICAgICAgICAgIGNvbnN0IHRhcmJhbGwgPSBtZXRhZGF0YS5kaXN0LnRhcmJhbGwucmVwbGFjZSgvLipcXC8vLCAnJyk7XG5cbiAgICAgICAgICBpZiAoaXNPYmplY3QoZGF0YS5fYXR0YWNobWVudHNbdGFyYmFsbF0pKSB7XG4gICAgICAgICAgICBpZiAoXG4gICAgICAgICAgICAgIF8uaXNOaWwoZGF0YS5fYXR0YWNobWVudHNbdGFyYmFsbF0uc2hhc3VtKSA9PT0gZmFsc2UgJiZcbiAgICAgICAgICAgICAgXy5pc05pbChtZXRhZGF0YS5kaXN0LnNoYXN1bSkgPT09IGZhbHNlXG4gICAgICAgICAgICApIHtcbiAgICAgICAgICAgICAgaWYgKGRhdGEuX2F0dGFjaG1lbnRzW3RhcmJhbGxdLnNoYXN1bSAhPSBtZXRhZGF0YS5kaXN0LnNoYXN1bSkge1xuICAgICAgICAgICAgICAgIGNvbnN0IGVycm9yTWVzc2FnZSA9IGBzaGFzdW0gZXJyb3IsICR7ZGF0YS5fYXR0YWNobWVudHNbdGFyYmFsbF0uc2hhc3VtfSAhPSAke21ldGFkYXRhLmRpc3Quc2hhc3VtfWA7XG4gICAgICAgICAgICAgICAgcmV0dXJuIGNiKEVycm9yQ29kZS5nZXRCYWRSZXF1ZXN0KGVycm9yTWVzc2FnZSkpO1xuICAgICAgICAgICAgICB9XG4gICAgICAgICAgICB9XG5cbiAgICAgICAgICAgIGNvbnN0IGN1cnJlbnREYXRlID0gbmV3IERhdGUoKS50b0lTT1N0cmluZygpO1xuXG4gICAgICAgICAgICAvLyBzb21lIG9sZCBzdG9yYWdlIGRvIG5vdCBoYXZlIHRoaXMgZmllbGQgIzc0MFxuICAgICAgICAgICAgaWYgKF8uaXNOaWwoZGF0YS50aW1lKSkge1xuICAgICAgICAgICAgICBkYXRhLnRpbWUgPSB7fTtcbiAgICAgICAgICAgIH1cblxuICAgICAgICAgICAgZGF0YS50aW1lWydtb2RpZmllZCddID0gY3VycmVudERhdGU7XG5cbiAgICAgICAgICAgIGlmICgnY3JlYXRlZCcgaW4gZGF0YS50aW1lID09PSBmYWxzZSkge1xuICAgICAgICAgICAgICBkYXRhLnRpbWUuY3JlYXRlZCA9IGN1cnJlbnREYXRlO1xuICAgICAgICAgICAgfVxuXG4gICAgICAgICAgICBkYXRhLnRpbWVbdmVyc2lvbl0gPSBjdXJyZW50RGF0ZTtcbiAgICAgICAgICAgIGRhdGEuX2F0dGFjaG1lbnRzW3RhcmJhbGxdLnZlcnNpb24gPSB2ZXJzaW9uO1xuICAgICAgICAgIH1cbiAgICAgICAgfVxuXG4gICAgICAgIGRhdGEudmVyc2lvbnNbdmVyc2lvbl0gPSBtZXRhZGF0YTtcbiAgICAgICAgdGFnVmVyc2lvbihkYXRhLCB2ZXJzaW9uLCB0YWcpO1xuXG4gICAgICAgIHRoaXMuc3RvcmFnZVBsdWdpbi5hZGQobmFtZSwgKGFkZEZhaWxlZCk6IHZvaWQgPT4ge1xuICAgICAgICAgIGlmIChhZGRGYWlsZWQpIHtcbiAgICAgICAgICAgIHJldHVybiBjYihFcnJvckNvZGUuZ2V0QmFkRGF0YShhZGRGYWlsZWQubWVzc2FnZSkpO1xuICAgICAgICAgIH1cblxuICAgICAgICAgIGNiKCk7XG4gICAgICAgIH0pO1xuICAgICAgfSxcbiAgICAgIGNhbGxiYWNrXG4gICAgKTtcbiAgfVxuXG4gIC8qKlxuICAgKiBNZXJnZSBhIG5ldyBsaXN0IG9mIHRhZ3MgZm9yIGEgbG9jYWwgcGFja2FnZXMgd2l0aCB0aGUgZXhpc3Rpbmcgb25lLlxuICAgKiBAcGFyYW0geyp9IHBrZ05hbWVcbiAgICogQHBhcmFtIHsqfSB0YWdzXG4gICAqIEBwYXJhbSB7Kn0gY2FsbGJhY2tcbiAgICovXG4gIHB1YmxpYyBtZXJnZVRhZ3MocGtnTmFtZTogc3RyaW5nLCB0YWdzOiBNZXJnZVRhZ3MsIGNhbGxiYWNrOiBDYWxsYmFjayk6IHZvaWQge1xuICAgIHRoaXMuX3VwZGF0ZVBhY2thZ2UoXG4gICAgICBwa2dOYW1lLFxuICAgICAgKGRhdGEsIGNiKTogdm9pZCA9PiB7XG4gICAgICAgIC8qIGVzbGludCBndWFyZC1mb3ItaW46IDAgKi9cbiAgICAgICAgZm9yIChjb25zdCB0YWcgaW4gdGFncykge1xuICAgICAgICAgIC8vIHRoaXMgaGFuZGxlIGRpc3QtdGFnIHJtIGNvbW1hbmRcbiAgICAgICAgICBpZiAoXy5pc051bGwodGFnc1t0YWddKSkge1xuICAgICAgICAgICAgZGVsZXRlIGRhdGFbRElTVF9UQUdTXVt0YWddO1xuICAgICAgICAgICAgY29udGludWU7XG4gICAgICAgICAgfVxuXG4gICAgICAgICAgaWYgKF8uaXNOaWwoZGF0YS52ZXJzaW9uc1t0YWdzW3RhZ11dKSkge1xuICAgICAgICAgICAgcmV0dXJuIGNiKHRoaXMuX2dldFZlcnNpb25Ob3RGb3VuZCgpKTtcbiAgICAgICAgICB9XG4gICAgICAgICAgY29uc3QgdmVyc2lvbjogc3RyaW5nID0gdGFnc1t0YWddO1xuICAgICAgICAgIHRhZ1ZlcnNpb24oZGF0YSwgdmVyc2lvbiwgdGFnKTtcbiAgICAgICAgfVxuICAgICAgICBjYihudWxsKTtcbiAgICAgIH0sXG4gICAgICBjYWxsYmFja1xuICAgICk7XG4gIH1cblxuICAvKipcbiAgICogUmV0dXJuIHZlcnNpb24gbm90IGZvdW5kXG4gICAqIEByZXR1cm4ge1N0cmluZ31cbiAgICogQHByaXZhdGVcbiAgICovXG4gIHByaXZhdGUgX2dldFZlcnNpb25Ob3RGb3VuZCgpOiBhbnkge1xuICAgIHJldHVybiBFcnJvckNvZGUuZ2V0Tm90Rm91bmQoQVBJX0VSUk9SLlZFUlNJT05fTk9UX0VYSVNUKTtcbiAgfVxuXG4gIC8qKlxuICAgKiBSZXR1cm4gZmlsZSBubyBhdmFpbGFibGVcbiAgICogQHJldHVybiB7U3RyaW5nfVxuICAgKiBAcHJpdmF0ZVxuICAgKi9cbiAgcHJpdmF0ZSBfZ2V0RmlsZU5vdEF2YWlsYWJsZSgpOiBhbnkge1xuICAgIHJldHVybiBFcnJvckNvZGUuZ2V0Tm90Rm91bmQoJ25vIHN1Y2ggZmlsZSBhdmFpbGFibGUnKTtcbiAgfVxuXG4gIC8qKlxuICAgKiBVcGRhdGUgdGhlIHBhY2thZ2UgbWV0YWRhdGEsIHRhZ3MgYW5kIGF0dGFjaG1lbnRzICh0YXJiYWxscykuXG4gICAqIE5vdGU6IEN1cnJlbnRseSBzdXBwb3J0cyB1bnB1Ymxpc2hpbmcgYW5kIGRlcHJlY2F0aW9uLlxuICAgKiBAcGFyYW0geyp9IG5hbWVcbiAgICogQHBhcmFtIHsqfSBpbmNvbWluZ1BrZ1xuICAgKiBAcGFyYW0geyp9IHJldmlzaW9uXG4gICAqIEBwYXJhbSB7Kn0gY2FsbGJhY2tcbiAgICogQHJldHVybiB7RnVuY3Rpb259XG4gICAqL1xuICBwdWJsaWMgY2hhbmdlUGFja2FnZShcbiAgICBuYW1lOiBzdHJpbmcsXG4gICAgaW5jb21pbmdQa2c6IE1hbmlmZXN0LFxuICAgIHJldmlzaW9uOiBzdHJpbmcgfCB2b2lkLFxuICAgIGNhbGxiYWNrOiBDYWxsYmFja1xuICApOiB2b2lkIHtcbiAgICBpZiAoIWlzT2JqZWN0KGluY29taW5nUGtnLnZlcnNpb25zKSB8fCAhaXNPYmplY3QoaW5jb21pbmdQa2dbRElTVF9UQUdTXSkpIHtcbiAgICAgIHRoaXMubG9nZ2VyLmVycm9yKHsgbmFtZSB9LCBgY2hhbmdlUGFja2FnZSBiYWQgZGF0YSBmb3IgQHtuYW1lfWApO1xuICAgICAgcmV0dXJuIGNhbGxiYWNrKEVycm9yQ29kZS5nZXRCYWREYXRhKCkpO1xuICAgIH1cbiAgICBkZWJ1ZygnY2hhbmdlUGFja2FnZSB1ZGFwdGluZyBwYWNrYWdlIGZvciAlbycsIG5hbWUpO1xuICAgIHRoaXMuX3VwZGF0ZVBhY2thZ2UoXG4gICAgICBuYW1lLFxuICAgICAgKGxvY2FsRGF0YTogTWFuaWZlc3QsIGNiOiBDYWxsYmFjayk6IHZvaWQgPT4ge1xuICAgICAgICBmb3IgKGNvbnN0IHZlcnNpb24gaW4gbG9jYWxEYXRhLnZlcnNpb25zKSB7XG4gICAgICAgICAgY29uc3QgaW5jb21pbmdWZXJzaW9uID0gaW5jb21pbmdQa2cudmVyc2lvbnNbdmVyc2lvbl07XG4gICAgICAgICAgaWYgKF8uaXNOaWwoaW5jb21pbmdWZXJzaW9uKSkge1xuICAgICAgICAgICAgdGhpcy5sb2dnZXIuaW5mbyh7IG5hbWU6IG5hbWUsIHZlcnNpb246IHZlcnNpb24gfSwgJ3VucHVibGlzaGluZyBAe25hbWV9QEB7dmVyc2lvbn0nKTtcblxuICAgICAgICAgICAgLy8gRklYTUU6IEkgcHJlZmVyIHJldHVybiBhIG5ldyBvYmplY3QgcmF0aGVyIG11dGF0ZSB0aGUgbWV0YWRhdGFcbiAgICAgICAgICAgIGRlbGV0ZSBsb2NhbERhdGEudmVyc2lvbnNbdmVyc2lvbl07XG4gICAgICAgICAgICBkZWxldGUgbG9jYWxEYXRhLnRpbWUhW3ZlcnNpb25dO1xuXG4gICAgICAgICAgICBmb3IgKGNvbnN0IGZpbGUgaW4gbG9jYWxEYXRhLl9hdHRhY2htZW50cykge1xuICAgICAgICAgICAgICBpZiAobG9jYWxEYXRhLl9hdHRhY2htZW50c1tmaWxlXS52ZXJzaW9uID09PSB2ZXJzaW9uKSB7XG4gICAgICAgICAgICAgICAgZGVsZXRlIGxvY2FsRGF0YS5fYXR0YWNobWVudHNbZmlsZV0udmVyc2lvbjtcbiAgICAgICAgICAgICAgfVxuICAgICAgICAgICAgfVxuICAgICAgICAgIH0gZWxzZSBpZiAoT2JqZWN0LnByb3RvdHlwZS5oYXNPd25Qcm9wZXJ0eS5jYWxsKGluY29taW5nVmVyc2lvbiwgJ2RlcHJlY2F0ZWQnKSkge1xuICAgICAgICAgICAgY29uc3QgaW5jb21pbmdEZXByZWNhdGVkID0gaW5jb21pbmdWZXJzaW9uLmRlcHJlY2F0ZWQ7XG4gICAgICAgICAgICBpZiAoaW5jb21pbmdEZXByZWNhdGVkICE9IGxvY2FsRGF0YS52ZXJzaW9uc1t2ZXJzaW9uXS5kZXByZWNhdGVkKSB7XG4gICAgICAgICAgICAgIGlmICghaW5jb21pbmdEZXByZWNhdGVkKSB7XG4gICAgICAgICAgICAgICAgdGhpcy5sb2dnZXIuaW5mbyhcbiAgICAgICAgICAgICAgICAgIHsgbmFtZTogbmFtZSwgdmVyc2lvbjogdmVyc2lvbiB9LFxuICAgICAgICAgICAgICAgICAgJ3VuZGVwcmVjYXRpbmcgQHtuYW1lfUBAe3ZlcnNpb259J1xuICAgICAgICAgICAgICAgICk7XG4gICAgICAgICAgICAgICAgZGVsZXRlIGxvY2FsRGF0YS52ZXJzaW9uc1t2ZXJzaW9uXS5kZXByZWNhdGVkO1xuICAgICAgICAgICAgICB9IGVsc2Uge1xuICAgICAgICAgICAgICAgIHRoaXMubG9nZ2VyLmluZm8oXG4gICAgICAgICAgICAgICAgICB7IG5hbWU6IG5hbWUsIHZlcnNpb246IHZlcnNpb24gfSxcbiAgICAgICAgICAgICAgICAgICdkZXByZWNhdGluZyBAe25hbWV9QEB7dmVyc2lvbn0nXG4gICAgICAgICAgICAgICAgKTtcbiAgICAgICAgICAgICAgICBsb2NhbERhdGEudmVyc2lvbnNbdmVyc2lvbl0uZGVwcmVjYXRlZCA9IGluY29taW5nRGVwcmVjYXRlZDtcbiAgICAgICAgICAgICAgfVxuICAgICAgICAgICAgICBsb2NhbERhdGEudGltZSEubW9kaWZpZWQgPSBuZXcgRGF0ZSgpLnRvSVNPU3RyaW5nKCk7XG4gICAgICAgICAgICB9XG4gICAgICAgICAgfVxuICAgICAgICB9XG5cbiAgICAgICAgbG9jYWxEYXRhW1VTRVJTXSA9IGluY29taW5nUGtnW1VTRVJTXTtcbiAgICAgICAgbG9jYWxEYXRhW0RJU1RfVEFHU10gPSBpbmNvbWluZ1BrZ1tESVNUX1RBR1NdO1xuICAgICAgICBjYihudWxsKTtcbiAgICAgIH0sXG4gICAgICBmdW5jdGlvbiAoZXJyKTogdm9pZCB7XG4gICAgICAgIGlmIChlcnIpIHtcbiAgICAgICAgICByZXR1cm4gY2FsbGJhY2soZXJyKTtcbiAgICAgICAgfVxuICAgICAgICBjYWxsYmFjaygpO1xuICAgICAgfVxuICAgICk7XG4gIH1cbiAgLyoqXG4gICAqIFJlbW92ZSBhIHRhcmJhbGwuXG4gICAqIEBwYXJhbSB7Kn0gbmFtZVxuICAgKiBAcGFyYW0geyp9IGZpbGVuYW1lXG4gICAqIEBwYXJhbSB7Kn0gcmV2aXNpb25cbiAgICogQHBhcmFtIHsqfSBjYWxsYmFja1xuICAgKi9cbiAgcHVibGljIHJlbW92ZVRhcmJhbGwobmFtZTogc3RyaW5nLCBmaWxlbmFtZTogc3RyaW5nLCByZXZpc2lvbjogc3RyaW5nLCBjYWxsYmFjazogQ2FsbGJhY2spOiB2b2lkIHtcbiAgICBhc3NlcnQodmFsaWRhdGVOYW1lKGZpbGVuYW1lKSk7XG5cbiAgICB0aGlzLl91cGRhdGVQYWNrYWdlKFxuICAgICAgbmFtZSxcbiAgICAgIChkYXRhLCBjYik6IHZvaWQgPT4ge1xuICAgICAgICBpZiAoZGF0YS5fYXR0YWNobWVudHNbZmlsZW5hbWVdKSB7XG4gICAgICAgICAgZGVsZXRlIGRhdGEuX2F0dGFjaG1lbnRzW2ZpbGVuYW1lXTtcbiAgICAgICAgICBjYihudWxsKTtcbiAgICAgICAgfSBlbHNlIHtcbiAgICAgICAgICBjYih0aGlzLl9nZXRGaWxlTm90QXZhaWxhYmxlKCkpO1xuICAgICAgICB9XG4gICAgICB9LFxuICAgICAgKGVycjogYW55KTogdm9pZCA9PiB7XG4gICAgICAgIGlmIChlcnIpIHtcbiAgICAgICAgICByZXR1cm4gY2FsbGJhY2soZXJyKTtcbiAgICAgICAgfVxuICAgICAgICBjb25zdCBzdG9yYWdlID0gdGhpcy5fZ2V0TG9jYWxTdG9yYWdlKG5hbWUpO1xuXG4gICAgICAgIGlmIChzdG9yYWdlKSB7XG4gICAgICAgICAgc3RvcmFnZS5kZWxldGVQYWNrYWdlKGZpbGVuYW1lLCBjYWxsYmFjayk7XG4gICAgICAgIH1cbiAgICAgIH1cbiAgICApO1xuICB9XG5cbiAgLyoqXG4gICAqIEFkZCBhIHRhcmJhbGwuXG4gICAqIEBwYXJhbSB7U3RyaW5nfSBuYW1lXG4gICAqIEBwYXJhbSB7U3RyaW5nfSBmaWxlbmFtZVxuICAgKiBAcmV0dXJuIHtTdHJlYW19XG4gICAqL1xuICBwdWJsaWMgYWRkVGFyYmFsbChuYW1lOiBzdHJpbmcsIGZpbGVuYW1lOiBzdHJpbmcpIHtcbiAgICBhc3NlcnQodmFsaWRhdGVOYW1lKGZpbGVuYW1lKSk7XG5cbiAgICBsZXQgbGVuZ3RoID0gMDtcbiAgICBjb25zdCBzaGFPbmVIYXNoID0gY3JlYXRlVGFyYmFsbEhhc2goKTtcbiAgICBjb25zdCB1cGxvYWRTdHJlYW0gPSBuZXcgVXBsb2FkVGFyYmFsbCh7fSk7XG4gICAgY29uc3QgX3RyYW5zZm9ybSA9IHVwbG9hZFN0cmVhbS5fdHJhbnNmb3JtO1xuICAgIGNvbnN0IHN0b3JhZ2UgPSB0aGlzLl9nZXRMb2NhbFN0b3JhZ2UobmFtZSk7XG5cbiAgICB1cGxvYWRTdHJlYW0uYWJvcnQgPSBmdW5jdGlvbiAoKTogdm9pZCB7fTtcbiAgICB1cGxvYWRTdHJlYW0uZG9uZSA9IGZ1bmN0aW9uICgpOiB2b2lkIHt9O1xuXG4gICAgdXBsb2FkU3RyZWFtLl90cmFuc2Zvcm0gPSBmdW5jdGlvbiAoZGF0YSwgLi4uYXJncyk6IHZvaWQge1xuICAgICAgc2hhT25lSGFzaC51cGRhdGUoZGF0YSk7XG4gICAgICAvLyBtZWFzdXJlIHRoZSBsZW5ndGggZm9yIHZhbGlkYXRpb24gcmVhc29uc1xuICAgICAgbGVuZ3RoICs9IGRhdGEubGVuZ3RoO1xuICAgICAgY29uc3QgYXBwbGllZERhdGEgPSBbZGF0YSwgLi4uYXJnc107XG4gICAgICAvLyBGSVhNRTogbm90IHN1cmUgYWJvdXQgdGhpcyBhcHByb2FjaCwgdHNjIGNvbXBsYWluc1xuICAgICAgLy8gQHRz