UNPKG

verdaccio

Version:

A lightweight private npm proxy registry

683 lines (656 loc) 89 kB
"use strict"; Object.defineProperty(exports, "__esModule", { value: true }); exports.default = void 0; var _assert = _interopRequireDefault(require("assert")); var _async = _interopRequireDefault(require("async")); var _debug = _interopRequireDefault(require("debug")); var _lodash = _interopRequireDefault(require("lodash")); var _stream = _interopRequireDefault(require("stream")); var _config = require("@verdaccio/config"); var _core = require("@verdaccio/core"); var _loaders = require("@verdaccio/loaders"); var _localStorageLegacy = _interopRequireDefault(require("@verdaccio/local-storage-legacy")); var _searchIndexer = require("@verdaccio/search-indexer"); var _streams = require("@verdaccio/streams"); var _logger = require("../lib/logger"); var _constants = require("./constants"); var _localStorage = _interopRequireDefault(require("./local-storage")); var _metadataUtils = require("./metadata-utils"); var _storageUtils = require("./storage-utils"); var _upStorage = _interopRequireDefault(require("./up-storage")); var _uplinkUtil = require("./uplink-util"); var _utils = require("./utils"); function _interopRequireDefault(e) { return e && e.__esModule ? e : { default: e }; } const debug = (0, _debug.default)('verdaccio:storage'); class Storage { constructor(config) { this.config = config; this.uplinks = (0, _uplinkUtil.setupUpLinks)(config); this.logger = _logger.logger; // eslint-disable-next-line @typescript-eslint/ban-ts-comment // @ts-ignore this.localStorage = null; } async init(config, filters) { if (this.localStorage === null) { this.filters = filters; const storageInstance = await this.loadStorage(config, this.logger); this.localStorage = new _localStorage.default(this.config, _logger.logger, storageInstance); await this.localStorage.getSecret(config); debug('initialization completed'); } else { debug('storage has been already initialized'); } if (!this.filters) { this.filters = await (0, _loaders.asyncLoadPlugin)(this.config.filters, { config: this.config, logger: this.logger }, plugin => { return typeof plugin.filter_metadata !== 'undefined'; }, true, this.config?.serverSettings?.pluginPrefix, _core.PLUGIN_CATEGORY.FILTER); debug('filters available %o', this.filters.length); } } async loadStorage(config, logger) { const Storage = await this.loadStorePlugin(); if (_lodash.default.isNil(Storage)) { (0, _assert.default)(this.config.storage, 'CONFIG: storage path not defined'); debug('no custom storage found, loading default storage @verdaccio/local-storage'); const localStorage = new _localStorageLegacy.default(config, logger); logger.info({ name: '@verdaccio/local-storage', pluginCategory: _core.PLUGIN_CATEGORY.STORAGE }, 'plugin @{name} successfully loaded (@{pluginCategory})'); return localStorage; } return Storage; } async loadStorePlugin() { const plugins = await (0, _loaders.asyncLoadPlugin)(this.config.store, { config: this.config, logger: this.logger }, plugin => { return typeof plugin.getPackageStorage !== 'undefined'; }, true, this.config?.serverSettings?.pluginPrefix, _core.PLUGIN_CATEGORY.STORAGE); if (plugins.length > 1) { this.logger.warn('more than one storage plugins has been detected, multiple storage are not supported, one will be selected automatically'); } return _lodash.default.head(plugins); } /** * Add a {name} package to a system Function checks if package with the same name is available from uplinks. If it isn't, we create package locally Used storages: local (write) && uplinks */ async addPackage(name, metadata, callback) { try { await (0, _storageUtils.checkPackageLocal)(name, this.localStorage); await (0, _storageUtils.checkPackageRemote)(name, this._isAllowPublishOffline(), this._syncUplinksMetadata.bind(this)); await (0, _storageUtils.publishPackage)(name, metadata, this.localStorage); callback(); } catch (err) { callback(err); } } _isAllowPublishOffline() { return typeof this.config.publish !== 'undefined' && _lodash.default.isBoolean(this.config.publish.allow_offline) && this.config.publish.allow_offline; } readTokens(filter) { return this.localStorage.readTokens(filter); } saveToken(token) { return this.localStorage.saveToken(token); } deleteToken(user, tokenKey) { return this.localStorage.deleteToken(user, tokenKey); } /** * Add a new version of package {name} to a system Used storages: local (write) */ addVersion(name, version, metadata, tag, callback) { this.localStorage.addVersion(name, version, metadata, tag, callback); } /** * Tags a package version with a provided tag Used storages: local (write) */ mergeTags(name, tagHash, callback) { this.localStorage.mergeTags(name, tagHash, callback); } /** * Change an existing package (i.e. unpublish one version) Function changes a package info from local storage and all uplinks with write access./ Used storages: local (write) */ changePackage(name, metadata, revision, callback) { this.localStorage.changePackage(name, metadata, revision, callback); } /** * Remove a package from a system Function removes a package from local storage Used storages: local (write) */ removePackage(name, callback) { this.localStorage.removePackage(name, callback); // update the indexer _searchIndexer.SearchMemoryIndexer.remove(name).catch(reason => { debug('indexer has failed on remove item %o', reason); _logger.logger.error('indexer has failed on remove item'); }); } /** Remove a tarball from a system Function removes a tarball from local storage. Tarball in question should not be linked to in any existing versions, i.e. package version should be unpublished first. Used storage: local (write) */ removeTarball(name, filename, revision, callback) { this.localStorage.removeTarball(name, filename, revision, callback); } /** * Upload a tarball for {name} package Function is synchronous and returns a WritableStream Used storages: local (write) */ addTarball(name, filename) { return this.localStorage.addTarball(name, filename); } hasLocalTarball(name, filename) { const self = this; return new Promise((resolve, reject) => { let localStream = self.localStorage.getTarball(name, filename); let isOpen = false; localStream.on('error', err => { if (isOpen || err.status !== _constants.HTTP_STATUS.NOT_FOUND) { reject(err); } // local reported 404 or request was aborted already if (localStream) { localStream.abort(); localStream = null; } resolve(false); }); localStream.on('open', function () { isOpen = true; localStream.abort(); localStream = null; resolve(true); }); }); } /** Get a tarball from a storage for {name} package Function is synchronous and returns a ReadableStream Function tries to read tarball locally, if it fails then it reads package information in order to figure out where we can get this tarball from Used storages: local || uplink (just one) */ getTarball(name, filename) { const readStream = new _streams.ReadTarball({}); readStream.abort = function () {}; const self = this; // Check if the tarball is allowed by filter plugins before serving. // Filters may block specific versions, so we verify the tarball's version // still exists in the filtered metadata. this._isTarballAllowedByFilters(name, filename).then(async allowed => { if (!allowed) { readStream.emit('error', _utils.ErrorCode.getNotFound(_constants.API_ERROR.NO_PACKAGE)); return; } // trying local first let localStream = self.localStorage.getTarball(name, filename); let isOpen = false; localStream.on('error', err => { if (isOpen || err.status !== _constants.HTTP_STATUS.NOT_FOUND) { return readStream.emit('error', err); } // local reported 404 const err404 = err; localStream.abort(); localStream = null; // we force for garbage collector const lookupFromUplinks = info => { self._syncUplinksMetadata(name, info, {}, (syncErr, syncInfo) => { if (_lodash.default.isNil(syncErr) === false) { return readStream.emit('error', syncErr); } // _syncUplinksMetadata returns filter-applied metadata; if the // version was removed by a filter, surface a 404 like a missing tarball. if (self.filters?.length && !(0, _utils.hasTarball)(syncInfo, filename)) { return readStream.emit('error', err404); } if (_lodash.default.isNil(syncInfo._distfiles) || _lodash.default.isNil(syncInfo._distfiles[filename])) { return readStream.emit('error', err404); } serveFile(syncInfo._distfiles[filename]); }); }; self.localStorage.getPackageMetadataAsync(name).then(info => { if (info._distfiles && _lodash.default.isNil(info._distfiles[filename]) === false) { // information about this file exists locally serveFile(info._distfiles[filename]); } else { // we know nothing about this file, trying to get information elsewhere lookupFromUplinks(info); } }, () => { // we know nothing about this file, trying to get information elsewhere lookupFromUplinks(null); }); }); localStream.on('content-length', function (v) { readStream.emit('content-length', v); }); localStream.on('open', function () { isOpen = true; localStream.pipe(readStream); }); }); return readStream; /** * Fetch and cache local/remote packages. * @param {Object} file define the package shape */ function serveFile(file) { let uplink = null; for (const uplinkId in self.uplinks) { if ((0, _config.hasProxyTo)(name, uplinkId, self.config.packages)) { uplink = self.uplinks[uplinkId]; } } if (uplink == null) { uplink = new _upStorage.default({ url: file.url, cache: true, _autogenerated: true }, self.config); } let savestream = null; if (uplink.config.cache) { savestream = self.localStorage.addTarball(name, filename); } let on_open = function () { // prevent it from being called twice on_open = function () {}; const rstream2 = uplink.fetchTarball(file.url); rstream2.on('error', function (err) { if (savestream) { savestream.abort(); } savestream = null; readStream.emit('error', err); }); rstream2.on('end', function () { if (savestream) { savestream.done(); } }); rstream2.on('content-length', function (v) { readStream.emit('content-length', v); if (savestream) { savestream.emit('content-length', v); } }); rstream2.pipe(readStream); if (savestream) { rstream2.pipe(savestream); } }; if (savestream) { savestream.on('open', function () { on_open(); }); savestream.on('error', function (err) { self.logger.warn({ err: err, fileName: file }, 'error saving file @{fileName}: @{err.message}\n@{err.stack}'); if (savestream) { savestream.abort(); } savestream = null; on_open(); }); } else { on_open(); } } } /** Retrieve a package metadata for {name} package Function invokes localStorage.getPackage and uplink.get_package for every uplink with proxy_access rights against {name} and combines results into one json object Used storages: local && uplink (proxy_access) * @param {object} options * @property {string} options.name Package Name * @property {object} options.req Express `req` object * @property {boolean} options.keepUpLinkData keep up link info in package meta, last update, etc. * @property {function} options.callback Callback for receive data */ getPackage(options) { this.localStorage.getPackageMetadata(options.name, (err, data) => { if (err && (!err.status || err.status >= _constants.HTTP_STATUS.INTERNAL_ERROR)) { // report internal errors right away return options.callback(err); } this._syncUplinksMetadata(options.name, data, { req: options.req, uplinksLook: options.uplinksLook }, function getPackageSynUpLinksCallback(err, result, uplinkErrors) { if (err) { return options.callback(err); } (0, _utils.normalizeDistTags)((0, _storageUtils.cleanUpLinksRef)(options.keepUpLinkData, result)); // npm can throw if this field doesn't exist result._attachments = {}; if (options.abbreviated === true) { options.callback(null, (0, _storageUtils.convertAbbreviatedManifest)(result), uplinkErrors); } else { options.callback(null, result, uplinkErrors); } }); }); } /** Retrieve remote and local packages more recent than {startkey} Function streams all packages from all uplinks first, and then local packages. Note that local packages could override registry ones just because they appear in JSON last. That's a trade-off we make to avoid memory issues. Used storages: local && uplink (proxy_access) * @param {*} startkey * @param {*} options * @return {Stream} */ search(startkey, options) { const self = this; const searchStream = new _stream.default.PassThrough({ objectMode: true }); _async.default.eachSeries(Object.keys(this.uplinks), function (up_name, cb) { // shortcut: if `local=1` is supplied, don't call uplinks if (options.req?.query?.local !== undefined) { return cb(); } _logger.logger.info(`search for uplink ${up_name}`); // search by keyword for each uplink const uplinkStream = self.uplinks[up_name].search(options); // join uplink stream with streams PassThrough uplinkStream.pipe(searchStream, { end: false }); uplinkStream.on('error', function (err) { self.logger.error({ err: err }, 'uplink error: @{err.message}'); cb(); // to avoid call callback more than once cb = function () {}; }); uplinkStream.on('end', function () { cb(); // to avoid call callback more than once cb = function () {}; }); searchStream.abort = function () { if (uplinkStream.abort) { uplinkStream.abort(); } cb(); // to avoid call callback more than once cb = function () {}; }; }, // executed after all series function () { // attach a local search results const localSearchStream = self.localStorage.search(startkey, options); searchStream.abort = function () { localSearchStream.abort(); }; localSearchStream.pipe(searchStream, { end: true }); localSearchStream.on('error', function (err) { self.logger.error({ err: err }, 'search error: @{err.message}'); searchStream.end(); }); }); return searchStream; } /** * Retrieve only private local packages * @param {*} callback */ getLocalDatabase(callback) { this.localStorage.storagePlugin.get((err, locals) => { if (err) { return callback(err); } this._collectLocalPackages(locals).then(packages => callback(null, packages), err => callback(err)); }); } /** * Read each local package name, apply filters, and collect the latest version. */ async _collectLocalPackages(locals) { const packages = []; for (const name of locals) { try { const pkgMetadata = await this.localStorage.getPackageMetadataAsync(name); const { filteredPackage } = await this._applyFilters(pkgMetadata); const latest = filteredPackage[_constants.DIST_TAGS]?.latest; if (latest && filteredPackage.versions[latest]) { const version = filteredPackage.versions[latest]; const timeList = filteredPackage.time; const time = timeList[latest]; // @ts-ignore version.time = time; // Add for stars api // @ts-ignore version.users = filteredPackage.users; packages.push(version); } else { this.logger.warn({ package: name }, 'package @{package} does not have a "latest" tag?'); } } catch (err) { this.logger.error({ err, package: name }, 'error reading package @{package}'); } } return packages; } /** * Function fetches package metadata from uplinks and synchronizes it with local data if package is available locally, it MUST be provided in pkginfo returns callback(err, result, uplink_errors) */ _syncUplinksMetadata(name, packageInfo, options, callback) { let found = true; const self = this; const upLinks = []; const hasToLookIntoUplinks = _lodash.default.isNil(options.uplinksLook) || options.uplinksLook; if (!packageInfo) { found = false; packageInfo = (0, _storageUtils.generatePackageTemplate)(name); } for (const uplink in this.uplinks) { if ((0, _config.hasProxyTo)(name, uplink, this.config.packages) && hasToLookIntoUplinks) { upLinks.push(this.uplinks[uplink]); } } _async.default.map(upLinks, (upLink, cb) => { const _options = Object.assign({}, options); const upLinkMeta = packageInfo._uplinks[upLink.upname]; if ((0, _utils.isObject)(upLinkMeta)) { const fetched = upLinkMeta.fetched; if (fetched && Date.now() - fetched < upLink.maxage) { return cb(); } _options.etag = upLinkMeta.etag; } upLink.getRemoteMetadata(name, _options, (err, upLinkResponse, eTag) => { if (err && err.remoteStatus === 304) { upLinkMeta.fetched = Date.now(); } if (err || !upLinkResponse) { return cb(null, [err || _utils.ErrorCode.getInternalError('no data')]); } try { upLinkResponse = _core.validationUtils.normalizeMetadata(upLinkResponse, name); } catch (err) { self.logger.error({ sub: 'out', err: err }, 'package.json validating error @{!err.message}\n@{err.stack}'); return cb(null, [err]); } packageInfo._uplinks[upLink.upname] = { etag: eTag, fetched: Date.now() }; packageInfo = (0, _storageUtils.mergeUplinkTimeIntoLocal)(packageInfo, upLinkResponse); (0, _uplinkUtil.updateVersionsHiddenUpLink)(upLinkResponse.versions, upLink); try { (0, _metadataUtils.mergeVersions)(packageInfo, upLinkResponse); } catch (err) { self.logger.error({ sub: 'out', err: err }, 'package.json parsing error @{!err.message}\n@{err.stack}'); return cb(null, [err]); } // if we got to this point, assume that the correct package exists // on the uplink found = true; cb(); }); }, // @ts-ignore async (err, upLinksErrors) => { (0, _assert.default)(!err && Array.isArray(upLinksErrors)); // Check for connection timeout or reset errors with uplink(s) // (these should be handled differently from the package not being found) if (!found) { let uplinkTimeoutError; for (let i = 0; i < upLinksErrors.length; i++) { if (upLinksErrors[i]) { for (let j = 0; j < upLinksErrors[i].length; j++) { if (upLinksErrors[i][j]) { const code = upLinksErrors[i][j].code; if (code === 'ETIMEDOUT' || code === 'ESOCKETTIMEDOUT' || code === 'ECONNRESET') { uplinkTimeoutError = true; break; } } } } } if (uplinkTimeoutError) { return callback(_utils.ErrorCode.getServiceUnavailable(), null, upLinksErrors); } return callback(_utils.ErrorCode.getNotFound(_constants.API_ERROR.NO_PACKAGE), null, upLinksErrors); } if (upLinks.length === 0) { const { filteredPackage, filterErrors } = await self._applyFilters(packageInfo); return callback(null, filteredPackage, filterErrors); } try { const packageJsonLocal = await self.localStorage.updateVersionsAsync(name, packageInfo); const { filteredPackage, filterErrors } = await self._applyFilters(packageJsonLocal); callback(null, filteredPackage, _lodash.default.concat(upLinksErrors, filterErrors)); } catch (err) { callback(err); } }); } /** * Apply all configured filter plugins to a package manifest sequentially. * Each filter's output is passed as input to the next filter. * Returns the filtered manifest and any errors that occurred. */ async _applyFilters(packageInfo) { const filterErrors = []; let filteredPackage = packageInfo; for (const filter of this.filters ?? []) { try { filteredPackage = await filter.filter_metadata(filteredPackage); } catch (err) { filterErrors.push(err); } } return { filteredPackage, filterErrors }; } /** * Check if a tarball should be served based on filter plugins, using only * local metadata. Returns true (defer) when the version isn't cached locally * — the uplink-sync path in getTarball re-checks filters after sync, which * avoids a redundant uplink round-trip and double filter application. */ async _isTarballAllowedByFilters(name, filename) { if (!this.filters?.length) { return true; } try { const pkgMetadata = await this.localStorage.getPackageMetadataAsync(name); if (!(0, _utils.hasTarball)(pkgMetadata, filename)) { return true; } const { filteredPackage } = await this._applyFilters(pkgMetadata); return (0, _utils.hasTarball)(filteredPackage, filename); } catch (err) { if (err?.status === _constants.HTTP_STATUS.NOT_FOUND) { return true; } this.logger.error({ package: name, fileName: filename, err }, 'error checking filters for tarball @{fileName} of package @{package}: @{err.message}'); return true; } } /** * Set a hidden value for each version. * @param {Array} versions list of version * @param {String} upLink uplink name * @private */ _updateVersionsHiddenUpLink(versions, upLink) { for (const i in versions) { if (Object.prototype.hasOwnProperty.call(versions, i)) { const version = versions[i]; // holds a "hidden" value to be used by the package storage. version[Symbol.for('__verdaccio_uplink')] = upLink.upname; } } } } var _default = exports.default = Storage; //# sourceMappingURL=data:application/json;charset=utf-8;base64,eyJ2ZXJzaW9uIjozLCJuYW1lcyI6WyJfYXNzZXJ0IiwiX2ludGVyb3BSZXF1aXJlRGVmYXVsdCIsInJlcXVpcmUiLCJfYXN5bmMiLCJfZGVidWciLCJfbG9kYXNoIiwiX3N0cmVhbSIsIl9jb25maWciLCJfY29yZSIsIl9sb2FkZXJzIiwiX2xvY2FsU3RvcmFnZUxlZ2FjeSIsIl9zZWFyY2hJbmRleGVyIiwiX3N0cmVhbXMiLCJfbG9nZ2VyIiwiX2NvbnN0YW50cyIsIl9sb2NhbFN0b3JhZ2UiLCJfbWV0YWRhdGFVdGlscyIsIl9zdG9yYWdlVXRpbHMiLCJfdXBTdG9yYWdlIiwiX3VwbGlua1V0aWwiLCJfdXRpbHMiLCJlIiwiX19lc01vZHVsZSIsImRlZmF1bHQiLCJkZWJ1ZyIsImJ1aWxkRGVidWciLCJTdG9yYWdlIiwiY29uc3RydWN0b3IiLCJjb25maWciLCJ1cGxpbmtzIiwic2V0dXBVcExpbmtzIiwibG9nZ2VyIiwibG9jYWxTdG9yYWdlIiwiaW5pdCIsImZpbHRlcnMiLCJzdG9yYWdlSW5zdGFuY2UiLCJsb2FkU3RvcmFnZSIsIkxvY2FsU3RvcmFnZSIsImdldFNlY3JldCIsImFzeW5jTG9hZFBsdWdpbiIsInBsdWdpbiIsImZpbHRlcl9tZXRhZGF0YSIsInNlcnZlclNldHRpbmdzIiwicGx1Z2luUHJlZml4IiwiUExVR0lOX0NBVEVHT1JZIiwiRklMVEVSIiwibGVuZ3RoIiwibG9hZFN0b3JlUGx1Z2luIiwiXyIsImlzTmlsIiwiYXNzZXJ0Iiwic3RvcmFnZSIsIkxvY2FsRGF0YWJhc2VQbHVnaW4iLCJpbmZvIiwibmFtZSIsInBsdWdpbkNhdGVnb3J5IiwiU1RPUkFHRSIsInBsdWdpbnMiLCJzdG9yZSIsImdldFBhY2thZ2VTdG9yYWdlIiwid2FybiIsImhlYWQiLCJhZGRQYWNrYWdlIiwibWV0YWRhdGEiLCJjYWxsYmFjayIsImNoZWNrUGFja2FnZUxvY2FsIiwiY2hlY2tQYWNrYWdlUmVtb3RlIiwiX2lzQWxsb3dQdWJsaXNoT2ZmbGluZSIsIl9zeW5jVXBsaW5rc01ldGFkYXRhIiwiYmluZCIsInB1Ymxpc2hQYWNrYWdlIiwiZXJyIiwicHVibGlzaCIsImlzQm9vbGVhbiIsImFsbG93X29mZmxpbmUiLCJyZWFkVG9rZW5zIiwiZmlsdGVyIiwic2F2ZVRva2VuIiwidG9rZW4iLCJkZWxldGVUb2tlbiIsInVzZXIiLCJ0b2tlbktleSIsImFkZFZlcnNpb24iLCJ2ZXJzaW9uIiwidGFnIiwibWVyZ2VUYWdzIiwidGFnSGFzaCIsImNoYW5nZVBhY2thZ2UiLCJyZXZpc2lvbiIsInJlbW92ZVBhY2thZ2UiLCJTZWFyY2hNZW1vcnlJbmRleGVyIiwicmVtb3ZlIiwiY2F0Y2giLCJyZWFzb24iLCJlcnJvciIsInJlbW92ZVRhcmJhbGwiLCJmaWxlbmFtZSIsImFkZFRhcmJhbGwiLCJoYXNMb2NhbFRhcmJhbGwiLCJzZWxmIiwiUHJvbWlzZSIsInJlc29sdmUiLCJyZWplY3QiLCJsb2NhbFN0cmVhbSIsImdldFRhcmJhbGwiLCJpc09wZW4iLCJvbiIsInN0YXR1cyIsIkhUVFBfU1RBVFVTIiwiTk9UX0ZPVU5EIiwiYWJvcnQiLCJyZWFkU3RyZWFtIiwiUmVhZFRhcmJhbGwiLCJfaXNUYXJiYWxsQWxsb3dlZEJ5RmlsdGVycyIsInRoZW4iLCJhbGxvd2VkIiwiZW1pdCIsIkVycm9yQ29kZSIsImdldE5vdEZvdW5kIiwiQVBJX0VSUk9SIiwiTk9fUEFDS0FHRSIsImVycjQwNCIsImxvb2t1cEZyb21VcGxpbmtzIiwic3luY0VyciIsInN5bmNJbmZvIiwiaGFzVGFyYmFsbCIsIl9kaXN0ZmlsZXMiLCJzZXJ2ZUZpbGUiLCJnZXRQYWNrYWdlTWV0YWRhdGFBc3luYyIsInYiLCJwaXBlIiwiZmlsZSIsInVwbGluayIsInVwbGlua0lkIiwiaGFzUHJveHlUbyIsInBhY2thZ2VzIiwiUHJveHlTdG9yYWdlIiwidXJsIiwiY2FjaGUiLCJfYXV0b2dlbmVyYXRlZCIsInNhdmVzdHJlYW0iLCJvbl9vcGVuIiwicnN0cmVhbTIiLCJmZXRjaFRhcmJhbGwiLCJkb25lIiwiZmlsZU5hbWUiLCJnZXRQYWNrYWdlIiwib3B0aW9ucyIsImdldFBhY2thZ2VNZXRhZGF0YSIsImRhdGEiLCJJTlRFUk5BTF9FUlJPUiIsInJlcSIsInVwbGlua3NMb29rIiwiZ2V0UGFja2FnZVN5blVwTGlua3NDYWxsYmFjayIsInJlc3VsdCIsInVwbGlua0Vycm9ycyIsIm5vcm1hbGl6ZURpc3RUYWdzIiwiY2xlYW5VcExpbmtzUmVmIiwia2VlcFVwTGlua0RhdGEiLCJfYXR0YWNobWVudHMiLCJhYmJyZXZpYXRlZCIsImNvbnZlcnRBYmJyZXZpYXRlZE1hbmlmZXN0Iiwic2VhcmNoIiwic3RhcnRrZXkiLCJzZWFyY2hTdHJlYW0iLCJTdHJlYW0iLCJQYXNzVGhyb3VnaCIsIm9iamVjdE1vZGUiLCJhc3luYyIsImVhY2hTZXJpZXMiLCJPYmplY3QiLCJrZXlzIiwidXBfbmFtZSIsImNiIiwicXVlcnkiLCJsb2NhbCIsInVuZGVmaW5lZCIsInVwbGlua1N0cmVhbSIsImVuZCIsImxvY2FsU2VhcmNoU3RyZWFtIiwiZ2V0TG9jYWxEYXRhYmFzZSIsInN0b3JhZ2VQbHVnaW4iLCJnZXQiLCJsb2NhbHMiLCJfY29sbGVjdExvY2FsUGFja2FnZXMiLCJwa2dNZXRhZGF0YSIsImZpbHRlcmVkUGFja2FnZSIsIl9hcHBseUZpbHRlcnMiLCJsYXRlc3QiLCJESVNUX1RBR1MiLCJ2ZXJzaW9ucyIsInRpbWVMaXN0IiwidGltZSIsInVzZXJzIiwicHVzaCIsInBhY2thZ2UiLCJwYWNrYWdlSW5mbyIsImZvdW5kIiwidXBMaW5rcyIsImhhc1RvTG9va0ludG9VcGxpbmtzIiwiZ2VuZXJhdGVQYWNrYWdlVGVtcGxhdGUiLCJtYXAiLCJ1cExpbmsiLCJfb3B0aW9ucyIsImFzc2lnbiIsInVwTGlua01ldGEiLCJfdXBsaW5rcyIsInVwbmFtZSIsImlzT2JqZWN0IiwiZmV0Y2hlZCIsIkRhdGUiLCJub3ciLCJtYXhhZ2UiLCJldGFnIiwiZ2V0UmVtb3RlTWV0YWRhdGEiLCJ1cExpbmtSZXNwb25zZSIsImVUYWciLCJyZW1vdGVTdGF0dXMiLCJnZXRJbnRlcm5hbEVycm9yIiwidmFsaWRhdGlvblV0aWxzIiwibm9ybWFsaXplTWV0YWRhdGEiLCJzdWIiLCJtZXJnZVVwbGlua1RpbWVJbnRvTG9jYWwiLCJ1cGRhdGVWZXJzaW9uc0hpZGRlblVwTGluayIsIm1lcmdlVmVyc2lvbnMiLCJ1cExpbmtzRXJyb3JzIiwiQXJyYXkiLCJpc0FycmF5IiwidXBsaW5rVGltZW91dEVycm9yIiwiaSIsImoiLCJjb2RlIiwiZ2V0U2VydmljZVVuYXZhaWxhYmxlIiwiZmlsdGVyRXJyb3JzIiwicGFja2FnZUpzb25Mb2NhbCIsInVwZGF0ZVZlcnNpb25zQXN5bmMiLCJjb25jYXQiLCJfdXBkYXRlVmVyc2lvbnNIaWRkZW5VcExpbmsiLCJwcm90b3R5cGUiLCJoYXNPd25Qcm9wZXJ0eSIsImNhbGwiLCJTeW1ib2wiLCJmb3IiLCJfZGVmYXVsdCIsImV4cG9ydHMiXSwic291cmNlcyI6WyIuLi8uLi9zcmMvbGliL3N0b3JhZ2UudHMiXSwic291cmNlc0NvbnRlbnQiOlsiaW1wb3J0IGFzc2VydCBmcm9tICdhc3NlcnQnO1xuaW1wb3J0IGFzeW5jIGZyb20gJ2FzeW5jJztcbmltcG9ydCBidWlsZERlYnVnIGZyb20gJ2RlYnVnJztcbmltcG9ydCBfIGZyb20gJ2xvZGFzaCc7XG5pbXBvcnQgU3RyZWFtIGZyb20gJ3N0cmVhbSc7XG5cbmltcG9ydCB7IGhhc1Byb3h5VG8gfSBmcm9tICdAdmVyZGFjY2lvL2NvbmZpZyc7XG5pbXBvcnQgeyBQTFVHSU5fQ0FURUdPUlksIHBsdWdpblV0aWxzLCB2YWxpZGF0aW9uVXRpbHMgfSBmcm9tICdAdmVyZGFjY2lvL2NvcmUnO1xuaW1wb3J0IHsgYXN5bmNMb2FkUGx1Z2luIH0gZnJvbSAnQHZlcmRhY2Npby9sb2FkZXJzJztcbmltcG9ydCBMb2NhbERhdGFiYXNlUGx1Z2luIGZyb20gJ0B2ZXJkYWNjaW8vbG9jYWwtc3RvcmFnZS1sZWdhY3knO1xuaW1wb3J0IHsgU2VhcmNoTWVtb3J5SW5kZXhlciB9IGZyb20gJ0B2ZXJkYWNjaW8vc2VhcmNoLWluZGV4ZXInO1xuaW1wb3J0IHsgUmVhZFRhcmJhbGwgfSBmcm9tICdAdmVyZGFjY2lvL3N0cmVhbXMnO1xuaW1wb3J0IHtcbiAgQ2FsbGJhY2ssXG4gIENvbmZpZyxcbiAgRGlzdEZpbGUsXG4gIExvZ2dlcixcbiAgTWFuaWZlc3QsXG4gIE1lcmdlVGFncyxcbiAgVmVyc2lvbixcbiAgVmVyc2lvbnMsXG59IGZyb20gJ0B2ZXJkYWNjaW8vdHlwZXMnO1xuaW1wb3J0IHsgR2VuZXJpY0JvZHksIFRva2VuLCBUb2tlbkZpbHRlciB9IGZyb20gJ0B2ZXJkYWNjaW8vdHlwZXMnO1xuXG5pbXBvcnQgeyBTdG9yYWdlUGx1Z2luTGVnYWN5IH0gZnJvbSAnLi4vLi4vdHlwZXMvY3VzdG9tJztcbmltcG9ydCB7IGxvZ2dlciB9IGZyb20gJy4uL2xpYi9sb2dnZXInO1xuaW1wb3J0IHsgSVBsdWdpbkZpbHRlcnMsIElTeW5jVXBsaW5rcywgU3RyaW5nVmFsdWUgfSBmcm9tICcuLi90eXBlcyc7XG5pbXBvcnQgeyBBUElfRVJST1IsIERJU1RfVEFHUywgSFRUUF9TVEFUVVMgfSBmcm9tICcuL2NvbnN0YW50cyc7XG5pbXBvcnQgTG9jYWxTdG9yYWdlLCB7IFN0b3JhZ2VQbHVnaW4gfSBmcm9tICcuL2xvY2FsLXN0b3JhZ2UnO1xuaW1wb3J0IHsgbWVyZ2VWZXJzaW9ucyB9IGZyb20gJy4vbWV0YWRhdGEtdXRpbHMnO1xuaW1wb3J0IHtcbiAgY2hlY2tQYWNrYWdlTG9jYWwsXG4gIGNoZWNrUGFja2FnZVJlbW90ZSxcbiAgY2xlYW5VcExpbmtzUmVmLFxuICBjb252ZXJ0QWJicmV2aWF0ZWRNYW5pZmVzdCxcbiAgZ2VuZXJhdGVQYWNrYWdlVGVtcGxhdGUsXG4gIG1lcmdlVXBsaW5rVGltZUludG9Mb2NhbCxcbiAgcHVibGlzaFBhY2thZ2UsXG59IGZyb20gJy4vc3RvcmFnZS11dGlscyc7XG5pbXBvcnQgUHJveHlTdG9yYWdlIGZyb20gJy4vdXAtc3RvcmFnZSc7XG5pbXBvcnQgeyBzZXR1cFVwTGlua3MsIHVwZGF0ZVZlcnNpb25zSGlkZGVuVXBMaW5rIH0gZnJvbSAnLi91cGxpbmstdXRpbCc7XG5pbXBvcnQgeyBFcnJvckNvZGUsIGhhc1RhcmJhbGwsIGlzT2JqZWN0LCBub3JtYWxpemVEaXN0VGFncyB9IGZyb20gJy4vdXRpbHMnO1xuXG5jb25zdCBkZWJ1ZyA9IGJ1aWxkRGVidWcoJ3ZlcmRhY2NpbzpzdG9yYWdlJyk7XG5cbmNsYXNzIFN0b3JhZ2Uge1xuICBwdWJsaWMgbG9jYWxTdG9yYWdlOiBMb2NhbFN0b3JhZ2U7XG4gIHB1YmxpYyBjb25maWc6IENvbmZpZztcbiAgcHVibGljIGxvZ2dlcjogTG9nZ2VyO1xuICBwdWJsaWMgdXBsaW5rczogUmVjb3JkPHN0cmluZywgUHJveHlTdG9yYWdlPjtcbiAgcHVibGljIGZpbHRlcnM6IElQbHVnaW5GaWx0ZXJzIHwgdW5kZWZpbmVkO1xuXG4gIHB1YmxpYyBjb25zdHJ1Y3Rvcihjb25maWc6IENvbmZpZykge1xuICAgIHRoaXMuY29uZmlnID0gY29uZmlnO1xuICAgIHRoaXMudXBsaW5rcyA9IHNldHVwVXBMaW5rcyhjb25maWcpO1xuICAgIHRoaXMubG9nZ2VyID0gbG9nZ2VyO1xuICAgIC8vIGVzbGludC1kaXNhYmxlLW5leHQtbGluZSBAdHlwZXNjcmlwdC1lc2xpbnQvYmFuLXRzLWNvbW1lbnRcbiAgICAvLyBAdHMtaWdub3JlXG4gICAgdGhpcy5sb2NhbFN0b3JhZ2UgPSBudWxsO1xuICB9XG5cbiAgcHVibGljIGFzeW5jIGluaXQoY29uZmlnOiBDb25maWcsIGZpbHRlcnM/OiBJUGx1Z2luRmlsdGVycyk6IFByb21pc2U8dm9pZD4ge1xuICAgIGlmICh0aGlzLmxvY2FsU3RvcmFnZSA9PT0gbnVsbCkge1xuICAgICAgdGhpcy5maWx0ZXJzID0gZmlsdGVycztcbiAgICAgIGNvbnN0IHN0b3JhZ2VJbnN0YW5jZSA9IGF3YWl0IHRoaXMubG9hZFN0b3JhZ2UoY29uZmlnLCB0aGlzLmxvZ2dlcik7XG4gICAgICB0aGlzLmxvY2FsU3RvcmFnZSA9IG5ldyBMb2NhbFN0b3JhZ2UodGhpcy5jb25maWcsIGxvZ2dlciwgc3RvcmFnZUluc3RhbmNlKTtcbiAgICAgIGF3YWl0IHRoaXMubG9jYWxTdG9yYWdlLmdldFNlY3JldChjb25maWcpO1xuICAgICAgZGVidWcoJ2luaXRpYWxpemF0aW9uIGNvbXBsZXRlZCcpO1xuICAgIH0gZWxzZSB7XG4gICAgICBkZWJ1Zygnc3RvcmFnZSBoYXMgYmVlbiBhbHJlYWR5IGluaXRpYWxpemVkJyk7XG4gICAgfVxuXG4gICAgaWYgKCF0aGlzLmZpbHRlcnMpIHtcbiAgICAgIHRoaXMuZmlsdGVycyA9IGF3YWl0IGFzeW5jTG9hZFBsdWdpbjxwbHVnaW5VdGlscy5NYW5pZmVzdEZpbHRlcjx1bmtub3duPj4oXG4gICAgICAgIHRoaXMuY29uZmlnLmZpbHRlcnMsXG4gICAgICAgIHtcbiAgICAgICAgICBjb25maWc6IHRoaXMuY29uZmlnLFxuICAgICAgICAgIGxvZ2dlcjogdGhpcy5sb2dnZXIsXG4gICAgICAgIH0sXG4gICAgICAgIChwbHVnaW46IHBsdWdpblV0aWxzLk1hbmlmZXN0RmlsdGVyPENvbmZpZz4pID0+IHtcbiAgICAgICAgICByZXR1cm4gdHlwZW9mIHBsdWdpbi5maWx0ZXJfbWV0YWRhdGEgIT09ICd1bmRlZmluZWQnO1xuICAgICAgICB9LFxuICAgICAgICB0cnVlLFxuICAgICAgICB0aGlzLmNvbmZpZz8uc2VydmVyU2V0dGluZ3M/LnBsdWdpblByZWZpeCxcbiAgICAgICAgUExVR0lOX0NBVEVHT1JZLkZJTFRFUlxuICAgICAgKTtcbiAgICAgIGRlYnVnKCdmaWx0ZXJzIGF2YWlsYWJsZSAlbycsIHRoaXMuZmlsdGVycy5sZW5ndGgpO1xuICAgIH1cbiAgfVxuXG4gIHByaXZhdGUgYXN5bmMgbG9hZFN0b3JhZ2UoY29uZmlnOiBDb25maWcsIGxvZ2dlcjogTG9nZ2VyKTogUHJvbWlzZTxTdG9yYWdlUGx1Z2luPiB7XG4gICAgY29uc3QgU3RvcmFnZSA9IGF3YWl0IHRoaXMubG9hZFN0b3JlUGx1Z2luKCk7XG4gICAgaWYgKF8uaXNOaWwoU3RvcmFnZSkpIHtcbiAgICAgIGFzc2VydCh0aGlzLmNvbmZpZy5zdG9yYWdlLCAnQ09ORklHOiBzdG9yYWdlIHBhdGggbm90IGRlZmluZWQnKTtcbiAgICAgIGRlYnVnKCdubyBjdXN0b20gc3RvcmFnZSBmb3VuZCwgbG9hZGluZyBkZWZhdWx0IHN0b3JhZ2UgQHZlcmRhY2Npby9sb2NhbC1zdG9yYWdlJyk7XG4gICAgICBjb25zdCBsb2NhbFN0b3JhZ2UgPSBuZXcgTG9jYWxEYXRhYmFzZVBsdWdpbihjb25maWcsIGxvZ2dlcik7XG4gICAgICBsb2dnZXIuaW5mbyhcbiAgICAgICAgeyBuYW1lOiAnQHZlcmRhY2Npby9sb2NhbC1zdG9yYWdlJywgcGx1Z2luQ2F0ZWdvcnk6IFBMVUdJTl9DQVRFR09SWS5TVE9SQUdFIH0sXG4gICAgICAgICdwbHVnaW4gQHtuYW1lfSBzdWNjZXNzZnVsbHkgbG9hZGVkIChAe3BsdWdpbkNhdGVnb3J5fSknXG4gICAgICApO1xuICAgICAgcmV0dXJuIGxvY2FsU3RvcmFnZTtcbiAgICB9XG4gICAgcmV0dXJuIFN0b3JhZ2UgYXMgU3RvcmFnZVBsdWdpbjtcbiAgfVxuXG4gIHByaXZhdGUgYXN5bmMgbG9hZFN0b3JlUGx1Z2luKCk6IFByb21pc2U8U3RvcmFnZVBsdWdpbkxlZ2FjeTxDb25maWc+IHwgdW5kZWZpbmVkPiB7XG4gICAgY29uc3QgcGx1Z2luczogU3RvcmFnZVBsdWdpbkxlZ2FjeTxDb25maWc+W10gPSBhd2FpdCBhc3luY0xvYWRQbHVnaW48XG4gICAgICBwbHVnaW5VdGlscy5TdG9yYWdlPHVua25vd24+XG4gICAgPihcbiAgICAgIHRoaXMuY29uZmlnLnN0b3JlLFxuICAgICAge1xuICAgICAgICBjb25maWc6IHRoaXMuY29uZmlnLFxuICAgICAgICBsb2dnZXI6IHRoaXMubG9nZ2VyLFxuICAgICAgfSxcbiAgICAgIChwbHVnaW4pID0+IHtcbiAgICAgICAgcmV0dXJuIHR5cGVvZiBwbHVnaW4uZ2V0UGFja2FnZVN0b3JhZ2UgIT09ICd1bmRlZmluZWQnO1xuICAgICAgfSxcbiAgICAgIHRydWUsXG4gICAgICB0aGlzLmNvbmZpZz8uc2VydmVyU2V0dGluZ3M/LnBsdWdpblByZWZpeCxcbiAgICAgIFBMVUdJTl9DQVRFR09SWS5TVE9SQUdFXG4gICAgKTtcblxuICAgIGlmIChwbHVnaW5zLmxlbmd0aCA+IDEpIHtcbiAgICAgIHRoaXMubG9nZ2VyLndhcm4oXG4gICAgICAgICdtb3JlIHRoYW4gb25lIHN0b3JhZ2UgcGx1Z2lucyBoYXMgYmVlbiBkZXRlY3RlZCwgbXVsdGlwbGUgc3RvcmFnZSBhcmUgbm90IHN1cHBvcnRlZCwgb25lIHdpbGwgYmUgc2VsZWN0ZWQgYXV0b21hdGljYWxseSdcbiAgICAgICk7XG4gICAgfVxuXG4gICAgcmV0dXJuIF8uaGVhZChwbHVnaW5zKTtcbiAgfVxuXG4gIC8qKlxuICAgKiAgQWRkIGEge25hbWV9IHBhY2thZ2UgdG8gYSBzeXN0ZW1cbiAgIEZ1bmN0aW9uIGNoZWNrcyBpZiBwYWNrYWdlIHdpdGggdGhlIHNhbWUgbmFtZSBpcyBhdmFpbGFibGUgZnJvbSB1cGxpbmtzLlxuICAgSWYgaXQgaXNuJ3QsIHdlIGNyZWF0ZSBwYWNrYWdlIGxvY2FsbHlcbiAgIFVzZWQgc3RvcmFnZXM6IGxvY2FsICh3cml0ZSkgJiYgdXBsaW5rc1xuICAgKi9cbiAgcHVibGljIGFzeW5jIGFkZFBhY2thZ2UobmFtZTogc3RyaW5nLCBtZXRhZGF0YTogYW55LCBjYWxsYmFjazogYW55KTogUHJvbWlzZTx2b2lkPiB7XG4gICAgdHJ5IHtcbiAgICAgIGF3YWl0IGNoZWNrUGFja2FnZUxvY2FsKG5hbWUsIHRoaXMubG9jYWxTdG9yYWdlKTtcbiAgICAgIGF3YWl0IGNoZWNrUGFja2FnZVJlbW90ZShcbiAgICAgICAgbmFtZSxcbiAgICAgICAgdGhpcy5faXNBbGxvd1B1Ymxpc2hPZmZsaW5lKCksXG4gICAgICAgIHRoaXMuX3N5bmNVcGxpbmtzTWV0YWRhdGEuYmluZCh0aGlzKVxuICAgICAgKTtcbiAgICAgIGF3YWl0IHB1Ymxpc2hQYWNrYWdlKG5hbWUsIG1ldGFkYXRhLCB0aGlzLmxvY2FsU3RvcmFnZSk7XG4gICAgICBjYWxsYmFjaygpO1xuICAgIH0gY2F0Y2ggKGVycjogYW55KSB7XG4gICAgICBjYWxsYmFjayhlcnIpO1xuICAgIH1cbiAgfVxuXG4gIHByaXZhdGUgX2lzQWxsb3dQdWJsaXNoT2ZmbGluZSgpOiBib29sZWFuIHtcbiAgICByZXR1cm4gKFxuICAgICAgdHlwZW9mIHRoaXMuY29uZmlnLnB1Ymxpc2ggIT09ICd1bmRlZmluZWQnICYmXG4gICAgICBfLmlzQm9vbGVhbih0aGlzLmNvbmZpZy5wdWJsaXNoLmFsbG93X29mZmxpbmUpICYmXG4gICAgICB0aGlzLmNvbmZpZy5wdWJsaXNoLmFsbG93X29mZmxpbmVcbiAgICApO1xuICB9XG5cbiAgcHVibGljIHJlYWRUb2tlbnMoZmlsdGVyOiBUb2tlbkZpbHRlcik6IFByb21pc2U8VG9rZW5bXT4ge1xuICAgIHJldHVybiB0aGlzLmxvY2FsU3RvcmFnZS5yZWFkVG9rZW5zKGZpbHRlcik7XG4gIH1cblxuICBwdWJsaWMgc2F2ZVRva2VuKHRva2VuOiBUb2tlbik6IFByb21pc2U8dm9pZD4ge1xuICAgIHJldHVybiB0aGlzLmxvY2FsU3RvcmFnZS5zYXZlVG9rZW4odG9rZW4pO1xuICB9XG5cbiAgcHVibGljIGRlbGV0ZVRva2VuKHVzZXI6IHN0cmluZywgdG9rZW5LZXk6IHN0cmluZyk6IFByb21pc2U8YW55PiB7XG4gICAgcmV0dXJuIHRoaXMubG9jYWxTdG9yYWdlLmRlbGV0ZVRva2VuKHVzZXIsIHRva2VuS2V5KTtcbiAgfVxuXG4gIC8qKlxuICAgKiBBZGQgYSBuZXcgdmVyc2lvbiBvZiBwYWNrYWdlIHtuYW1lfSB0byBhIHN5c3RlbVxuICAgVXNlZCBzdG9yYWdlczogbG9jYWwgKHdyaXRlKVxuICAgKi9cbiAgcHVibGljIGFkZFZlcnNpb24oXG4gICAgbmFtZTogc3RyaW5nLFxuICAgIHZlcnNpb246IHN0cmluZyxcbiAgICBtZXRhZGF0YTogVmVyc2lvbixcbiAgICB0YWc6IFN0cmluZ1ZhbHVlLFxuICAgIGNhbGxiYWNrOiBDYWxsYmFja1xuICApOiB2b2lkIHtcbiAgICB0aGlzLmxvY2FsU3RvcmFnZS5hZGRWZXJzaW9uKG5hbWUsIHZlcnNpb24sIG1ldGFkYXRhLCB0YWcsIGNhbGxiYWNrKTtcbiAgfVxuXG4gIC8qKlxuICAgKiBUYWdzIGEgcGFja2FnZSB2ZXJzaW9uIHdpdGggYSBwcm92aWRlZCB0YWdcbiAgIFVzZWQgc3RvcmFnZXM6IGxvY2FsICh3cml0ZSlcbiAgICovXG4gIHB1YmxpYyBtZXJnZVRhZ3MobmFtZTogc3RyaW5nLCB0YWdIYXNoOiBNZXJnZVRhZ3MsIGNhbGxiYWNrOiBDYWxsYmFjayk6IHZvaWQge1xuICAgIHRoaXMubG9jYWxTdG9yYWdlLm1lcmdlVGFncyhuYW1lLCB0YWdIYXNoLCBjYWxsYmFjayk7XG4gIH1cblxuICAvKipcbiAgICogQ2hhbmdlIGFuIGV4aXN0aW5nIHBhY2thZ2UgKGkuZS4gdW5wdWJsaXNoIG9uZSB2ZXJzaW9uKVxuICAgRnVuY3Rpb24gY2hhbmdlcyBhIHBhY2thZ2UgaW5mbyBmcm9tIGxvY2FsIHN0b3JhZ2UgYW5kIGFsbCB1cGxpbmtzIHdpdGggd3JpdGUgYWNjZXNzLi9cbiAgIFVzZWQgc3RvcmFnZXM6IGxvY2FsICh3cml0ZSlcbiAgICovXG4gIHB1YmxpYyBjaGFuZ2VQYWNrYWdlKFxuICAgIG5hbWU6IHN0cmluZyxcbiAgICBtZXRhZGF0YTogTWFuaWZlc3QsXG4gICAgcmV2aXNpb246IHN0cmluZyxcbiAgICBjYWxsYmFjazogQ2FsbGJhY2tcbiAgKTogdm9pZCB7XG4gICAgdGhpcy5sb2NhbFN0b3JhZ2UuY2hhbmdlUGFja2FnZShuYW1lLCBtZXRhZGF0YSwgcmV2aXNpb24sIGNhbGxiYWNrKTtcbiAgfVxuXG4gIC8qKlxuICAgKiBSZW1vdmUgYSBwYWNrYWdlIGZyb20gYSBzeXN0ZW1cbiAgIEZ1bmN0aW9uIHJlbW92ZXMgYSBwYWNrYWdlIGZyb20gbG9jYWwgc3RvcmFnZVxuICAgVXNlZCBzdG9yYWdlczogbG9jYWwgKHdyaXRlKVxuICAgKi9cbiAgcHVibGljIHJlbW92ZVBhY2thZ2UobmFtZTogc3RyaW5nLCBjYWxsYmFjazogQ2FsbGJhY2spOiB2b2lkIHtcbiAgICB0aGlzLmxvY2FsU3RvcmFnZS5yZW1vdmVQYWNrYWdlKG5hbWUsIGNhbGxiYWNrKTtcbiAgICAvLyB1cGRhdGUgdGhlIGluZGV4ZXJcbiAgICBTZWFyY2hNZW1vcnlJbmRleGVyLnJlbW92ZShuYW1lKS5jYXRjaCgocmVhc29uKSA9PiB7XG4gICAgICBkZWJ1ZygnaW5kZXhlciBoYXMgZmFpbGVkIG9uIHJlbW92ZSBpdGVtICVvJywgcmVhc29uKTtcbiAgICAgIGxvZ2dlci5lcnJvcignaW5kZXhlciBoYXMgZmFpbGVkIG9uIHJlbW92ZSBpdGVtJyk7XG4gICAgfSk7XG4gIH1cblxuICAvKipcbiAgIFJlbW92ZSBhIHRhcmJhbGwgZnJvbSBhIHN5c3RlbVxuICAgRnVuY3Rpb24gcmVtb3ZlcyBhIHRhcmJhbGwgZnJvbSBsb2NhbCBzdG9yYWdlLlxuICAgVGFyYmFsbCBpbiBxdWVzdGlvbiBzaG91bGQgbm90IGJlIGxpbmtlZCB0byBpbiBhbnkgZXhpc3RpbmdcbiAgIHZlcnNpb25zLCBpLmUuIHBhY2thZ2UgdmVyc2lvbiBzaG91bGQgYmUgdW5wdWJsaXNoZWQgZmlyc3QuXG4gICBVc2VkIHN0b3JhZ2U6IGxvY2FsICh3cml0ZSlcbiAgICovXG4gIHB1YmxpYyByZW1vdmVUYXJiYWxsKG5hbWU6IHN0cmluZywgZmlsZW5hbWU6IHN0cmluZywgcmV2aXNpb246IHN0cmluZywgY2FsbGJhY2s6IENhbGxiYWNrKTogdm9pZCB7XG4gICAgdGhpcy5sb2NhbFN0b3JhZ2UucmVtb3ZlVGFyYmFsbChuYW1lLCBmaWxlbmFtZSwgcmV2aXNpb24sIGNhbGxiYWNrKTtcbiAgfVxuXG4gIC8qKlxuICAgKiBVcGxvYWQgYSB0YXJiYWxsIGZvciB7bmFtZX0gcGFja2FnZVxuICAgRnVuY3Rpb24gaXMgc3luY2hyb25vdXMgYW5kIHJldHVybnMgYSBXcml0YWJsZVN0cmVhbVxuICAgVXNlZCBzdG9yYWdlczogbG9jYWwgKHdyaXRlKVxuICAgKi9cbiAgcHVibGljIGFkZFRhcmJhbGwobmFtZTogc3RyaW5nLCBmaWxlbmFtZTogc3RyaW5nKSB7XG4gICAgcmV0dXJuIHRoaXMubG9jYWxTdG9yYWdlLmFkZFRhcmJhbGwobmFtZSwgZmlsZW5hbWUpO1xuICB9XG5cbiAgcHVibGljIGhhc0xvY2FsVGFyYmFsbChuYW1lOiBzdHJpbmcsIGZpbGVuYW1lOiBzdHJpbmcpOiBQcm9taXNlPGJvb2xlYW4+IHtcbiAgICBjb25zdCBzZWxmID0gdGhpcztcbiAgICByZXR1cm4gbmV3IFByb21pc2U8Ym9vbGVhbj4oKHJlc29sdmUsIHJlamVjdCk6IHZvaWQgPT4ge1xuICAgICAgbGV0IGxvY2FsU3RyZWFtOiBhbnkgPSBzZWxmLmxvY2FsU3RvcmFnZS5nZXRUYXJiYWxsKG5hbWUsIGZpbGVuYW1lKTtcbiAgICAgIGxldCBpc09wZW4gPSBmYWxzZTtcbiAgICAgIGxvY2FsU3RyZWFtLm9uKCdlcnJvcicsIChlcnIpOiBhbnkgPT4ge1xuICAgICAgICBpZiAoaXNPcGVuIHx8IGVyci5zdGF0dXMgIT09IEhUVFBfU1RBVFVTLk5PVF9GT1VORCkge1xuICAgICAgICAgIHJlamVjdChlcnIpO1xuICAgICAgICB9XG4gICAgICAgIC8vIGxvY2FsIHJlcG9ydGVkIDQwNCBvciByZXF1ZXN0IHdhcyBhYm9ydGVkIGFscmVhZHlcbiAgICAgICAgaWYgKGxvY2FsU3RyZWFtKSB7XG4gICAgICAgICAgbG9jYWxTdHJlYW0uYWJvcnQoKTtcbiAgICAgICAgICBsb2NhbFN0cmVhbSA9IG51bGw7XG4gICAgICAgIH1cbiAgICAgICAgcmVzb2x2ZShmYWxzZSk7XG4gICAgICB9KTtcbiAgICAgIGxvY2FsU3RyZWFtLm9uKCdvcGVuJywgZnVuY3Rpb24gKCk6IHZvaWQge1xuICAgICAgICBpc09wZW4gPSB0cnVlO1xuICAgICAgICBsb2NhbFN0cmVhbS5hYm9ydCgpO1xuICAgICAgICBsb2NhbFN0cmVhbSA9IG51bGw7XG4gICAgICAgIHJlc29sdmUodHJ1ZSk7XG4gICAgICB9KTtcbiAgICB9KTtcbiAgfVxuXG4gIC8qKlxuICAgR2V0IGEgdGFyYmFsbCBmcm9tIGEgc3RvcmFnZSBmb3Ige25hbWV9IHBhY2thZ2VcbiAgIEZ1bmN0aW9uIGlzIHN5bmNocm9ub3VzIGFuZCByZXR1cm5zIGEgUmVhZGFibGVTdHJlYW1cbiAgIEZ1bmN0aW9uIHRyaWVzIHRvIHJlYWQgdGFyYmFsbCBsb2NhbGx5LCBpZiBpdCBmYWlscyB0aGVuIGl0IHJlYWRzIHBhY2thZ2VcbiAgIGluZm9ybWF0aW9uIGluIG9yZGVyIHRvIGZpZ3VyZSBvdXQgd2hlcmUgd2UgY2FuIGdldCB0aGlzIHRhcmJhbGwgZnJvbVxuICAgVXNlZCBzdG9yYWdlczogbG9jYWwgfHwgdXBsaW5rIChqdXN0IG9uZSlcbiAgICovXG4gIHB1YmxpYyBnZXRUYXJiYWxsKG5hbWU6IHN0cmluZywgZmlsZW5hbWU6IHN0cmluZykge1xuICAgIGNvbnN0IHJlYWRTdHJlYW0gPSBuZXcgUmVhZFRhcmJhbGwoe30pO1xuICAgIHJlYWRTdHJlYW0uYWJvcnQgPSBmdW5jdGlvbiAoKSB7fTtcblxuICAgIGNvbnN0IHNlbGYgPSB0aGlzO1xuXG4gICAgLy8gQ2hlY2sgaWYgdGhlIHRhcmJhbGwgaXMgYWxsb3dlZCBieSBmaWx0ZXIgcGx1Z2lucyBiZWZvcmUgc2VydmluZy5cbiAgICAvLyBGaWx0ZXJzIG1heSBibG9jayBzcGVjaWZpYyB2ZXJzaW9ucywgc28gd2UgdmVyaWZ5IHRoZSB0YXJiYWxsJ3MgdmVyc2lvblxuICAgIC8vIHN0aWxsIGV4aXN0cyBpbiB0aGUgZmlsdGVyZWQgbWV0YWRhdGEuXG4gICAgdGhpcy5faXNUYXJiYWxsQWxsb3dlZEJ5RmlsdGVycyhuYW1lLCBmaWxlbmFtZSkudGhlbihhc3luYyAoYWxsb3dlZCkgPT4ge1xuICAgICAgaWYgKCFhbGxvd2VkKSB7XG4gICAgICAgIHJlYWRTdHJlYW0uZW1pdCgnZXJyb3InLCBFcnJvckNvZGUuZ2V0Tm90Rm91bmQoQVBJX0VSUk9SLk5PX1BBQ0tBR0UpKTtcbiAgICAgICAgcmV0dXJuO1xuICAgICAgfVxuXG4gICAgICAvLyB0cnlpbmcgbG9jYWwgZmlyc3RcbiAgICAgIGxldCBsb2NhbFN0cmVhbTogYW55ID0gc2VsZi5sb2NhbFN0b3JhZ2UuZ2V0VGFyYmFsbChuYW1lLCBmaWxlbmFtZSk7XG4gICAgICBsZXQgaXNPcGVuID0gZmFsc2U7XG4gICAgICBsb2NhbFN0cmVhbS5vbignZXJyb3InLCAoZXJyKTogYW55ID0+IHtcbiAgICAgICAgaWYgKGlzT3BlbiB8fCBlcnIuc3RhdHVzICE9PSBIVFRQX1NUQVRVUy5OT1RfRk9VTkQpIHtcbiAgICAgICAgICByZXR1cm4gcmVhZFN0cmVhbS5lbWl0KCdlcnJvcicsIGVycik7XG4gICAgICAgIH1cblxuICAgICAgICAvLyBsb2NhbCByZXBvcnRlZCA0MDRcbiAgICAgICAgY29uc3QgZXJyNDA0ID0gZXJyO1xuICAgICAgICBsb2NhbFN0cmVhbS5hYm9ydCgpO1xuICAgICAgICBsb2NhbFN0cmVhbSA9IG51bGw7IC8vIHdlIGZvcmNlIGZvciBnYXJiYWdlIGNvbGxlY3RvclxuXG4gICAgICAgIGNvbnN0IGxvb2t1cEZyb21VcGxpbmtzID0gKGluZm86IE1hbmlmZXN0IHwgbnVsbCk6IHZvaWQgPT4ge1xuICAgICAgICAgIHNlbGYuX3N5bmNVcGxpbmtzTWV0YWRhdGEoXG4gICAgICAgICAgICBuYW1lLFxuICAgICAgICAgICAgaW5mbyBhcyBNYW5pZmVzdCxcbiAgICAgICAgICAgIHt9LFxuICAgICAgICAgICAgKHN5bmNFcnIsIHN5bmNJbmZvOiBNYW5pZmVzdCk6IGFueSA9PiB7XG4gICAgICAgICAgICAgIGlmIChfLmlzTmlsKHN5bmNFcnIpID09PSBmYWxzZSkge1xuICAgICAgICAgICAgICAgIHJldHVybiByZWFkU3RyZWFtLmVtaXQoJ2Vycm9yJywgc3luY0Vycik7XG4gICAgICAgICAgICAgIH1cbiAgICAgICAgICAgICAgLy8gX3N5bmNVcGxpbmtzTWV0YWRhdGEgcmV0dXJucyBmaWx0ZXItYXBwbGllZCBtZXRhZGF0YTsgaWYgdGhlXG4gICAgICAgICAgICAgIC8vIHZlcnNpb24gd2FzIHJlbW92ZWQgYnkgYSBmaWx0ZXIsIHN1cmZhY2UgYSA0MDQgbGlrZSBhIG1pc3NpbmcgdGFyYmFsbC5cbiAgICAgICAgICAgICAgaWYgKHNlbGYuZmlsdGVycz8ubGVuZ3RoICYmICFoYXNUYXJiYWxsKHN5bmNJbmZvLCBmaWxlbmFtZSkpIHtcbiAgICAgICAgICAgICAgICByZXR1cm4gcmVhZFN0cmVhbS5lbWl0KCdlcnJvcicsIGVycjQwNCk7XG4gICAgICAgICAgICAgIH1cbiAgICAgICAgICAgICAgaWYgKF8uaXNOaWwoc3luY0luZm8uX2Rpc3RmaWxlcykgfHwgXy5pc05pbChzeW5jSW5mby5fZGlzdGZpbGVzW2ZpbGVuYW1lXSkpIHtcbiAgICAgICAgICAgICAgICByZXR1cm4gcmVhZFN0cmVhbS5lbWl0KCdlcnJvcicsIGVycjQwNCk7XG4gICAgICAgICAgICAgIH1cbiAgICAgICAgICAgICAgc2VydmVGaWxlKHN5bmNJbmZvLl9kaXN0ZmlsZXNbZmlsZW5hbWVdKTtcbiAgICAgICAgICAgIH1cbiAgICAgICAgICApO1xuICAgICAgICB9O1xuXG4gICAgICAgIHNlbGYubG9jYWxTdG9yYWdlLmdldFBhY2thZ2VNZXRhZGF0YUFzeW5jKG5hbWUpLnRoZW4oXG4gICAgICAgICAgKGluZm8pID0+IHtcbiAgICAgICAgICAgIGlmIChpbmZvLl9kaXN0ZmlsZXMgJiYgXy5pc05pbChpbmZvLl9kaXN0ZmlsZXNbZmlsZW5hbWVdKSA9PT0gZmFsc2UpIHtcbiAgICAgICAgICAgICAgLy8gaW5mb3JtYXRpb24gYWJvdXQgdGhpcyBmaWxlIGV4aXN0cyBsb2NhbGx5XG4gICAgICAgICAgICAgIHNlcnZlRmlsZShpbmZvLl9kaXN0ZmlsZXNbZmlsZW5hbWVdKTtcbiAgICAgICAgICAgIH0gZWxzZSB7XG4gICAgICAgICAgICAgIC8vIHdlIGtub3cgbm90aGluZyBhYm91dCB0aGlzIGZpbGUsIHRyeWluZyB0byBnZXQgaW5mb3JtYXRpb24gZWxzZXdoZXJlXG4gICAgICAgICAgICAgIGxvb2t1cEZyb21VcGxpbmtzKGluZm8pO1xuICAgICAgICAgICAgfVxuICAgICAgICAgIH0sXG4gICAgICAgICAgKCkgPT4ge1xuICAgICAgICAgICAgLy8gd2Uga25vdyBub3RoaW5nIGFib3V0IHRoaXMgZmlsZSwgdHJ5aW5nIHRvIGdldCBpbmZvcm1hdGlvbiBlbHNld2hlcmVcbiAgICAgICAgICAgIGxvb2t1cEZyb21VcGxpbmtzKG51bGwpO1xuICAgICAgICAgIH1cbiAgICAgICAgKTtcbiAgICAgIH0pO1xuICAgICAgbG9jYWxTdHJlYW0ub24oJ2NvbnRlbnQtbGVuZ3RoJywgZnVuY3Rpb24gKHYpOiB2b2lkIHtcbiAgICAgICAgcmVhZFN0cmVhbS5lbWl0KCdjb250ZW50LWxlbmd0aCcsIHYpO1xuICAgICAgfSk7XG4gICAgICBsb2NhbFN0cmVhbS5vbignb3BlbicsIGZ1bmN0aW9uICgpOiB2b2lkIHtcbiAgICAgICAgaXNPcGVuID0gdHJ1ZTtcbiAgICAgICAgbG9jYWxTdHJlYW0ucGlwZShyZWFkU3RyZWFtKTtcbiAgICAgIH0pO1xuICAgIH0pO1xuXG4gICAgcmV0dXJuIHJlYWRTdHJlYW07XG5cbiAgICAvKipcbiAgICAgKiBGZXRjaCBhbmQgY2FjaGUgbG9jYWwvcmVtb3RlIHBhY2thZ2VzLlxuICAgICAqIEBwYXJhbSB7T2JqZWN0fSBmaWxlIGRlZmluZSB0aGUgcGFja2FnZSBzaGFwZVxuICAgICAqL1xuICAgIGZ1bmN0aW9uIHNlcnZlRmlsZShmaWxlOiBEaXN0RmlsZSk6IHZvaWQge1xuICAgICAgbGV0IHVwbGluazogYW55ID0gbnVsbDtcblxuICAgICAgZm9yIChjb25zdCB1cGxpbmtJZCBpbiBzZWxmLnVwbGlua3MpIHtcbiAgICAgICAgaWYgKGhhc1Byb3h5VG8obmFtZSwgdXBsaW5rSWQsIHNlbGYuY29uZmlnLnBhY2thZ2VzKSkge1xuICAgICAgICAgIHVwbGluayA9IHNlbGYudXBsaW5rc1t1cGxpbmtJZF07XG4gICAgICAgIH1cbiAgICAgIH1cblxuICAgICAgaWYgKHVwbGluayA9PSBudWxsKSB7XG4gICAgICAgIHVwbGluayA9IG5ldyBQcm94eVN0b3JhZ2UoXG4gICAgICAgICAge1xuICAgICAgICAgICAgdXJsOiBmaWxlLnVybCxcbiAgICAgICAgICAgIGNhY2hlOiB0cnVlLFxuICAgICAgICAgICAgX2F1dG9nZW5lcmF0ZWQ6IHRydWUsXG4gICAgICAgICAgfSxcbiAgICAgICAgICBzZWxmLmNvbmZpZ1xuICAgICAgICApO1xuICAgICAgfVxuXG4gICAgICBsZXQgc2F2ZXN0cmVhbTogYW55ID0gbnVsbDtcbiAgICAgIGlmICh1cGxpbmsuY29uZmlnLmNhY2hlKSB7XG4gICAgICAgIHNhdmVzdHJlYW0gPSBzZWxmLmxvY2FsU3RvcmFnZS5hZGRUYXJiYWxsKG5hbWUsIGZpbGVuYW1lKTtcbiAgICAgIH1cblxuICAgICAgbGV0IG9uX29wZW4gPSBmdW5jdGlvbiAoKTogdm9pZCB7XG4gICAgICAgIC8vIHByZXZlbnQgaXQgZnJvbSBiZWluZyBjYWxsZWQgdHdpY2VcbiAgICAgICAgb25fb3BlbiA9IGZ1bmN0aW9uICgpIHt9O1xuICAgICAgICBjb25zdCByc3RyZWFtMiA9IHVwbGluay5mZXRjaFRhcmJhbGwoZmlsZS51cmwpO1xuICAgICAgICByc3RyZWFtMi5vbignZXJyb3InLCBmdW5jdGlvbiAoZXJyKTogdm9pZCB7XG4gICAgICAgICAgaWYgKHNhdmVzdHJlYW0pIHtcbiAgICAgICAgICAgIHNhdmVzdHJlYW0uYWJvcnQoKTtcbiAgICAgICAgICB9XG4gICAgICAgICAgc2F2ZXN0cmVhbSA9IG51bGw7XG4gICAgICAgICAgcmVhZFN0cmVhbS5lbWl0KCdlcnJvcicsIGVycik7XG4gICAgICAgIH0pO1xuICAgICAgICByc3RyZWFtMi5vbignZW5kJywgZnVuY3Rpb24gKCk6IHZvaWQge1xuICAgICAgICAgIGlmIChzYXZlc3RyZWFtKSB7XG4gICAgICAgICAgICBzYXZlc3RyZWFtLmRvbmUoKTtcbiAgICAgICAgICB9XG4gICAgICAgIH0pO1xuXG4gICAgICAgIHJzdHJlYW0yLm9uKCdjb250ZW50LWxlbmd0aCcsIGZ1bmN0aW9uICh2KTogdm9pZCB7XG4gICAgICAgICAgcmVhZFN0cmVhbS5lbWl0KCdjb250ZW50LWxlbmd0aCcsIHYpO1xuICAgICAgICAgIGlmIChzYXZlc3RyZWFtKSB7XG4gICAgICAgICAgICBzYXZlc3RyZWFtLmVtaXQoJ2NvbnRlbnQtbGVuZ3RoJywgdik7XG4gICAgICAgICAgfVxuICAgICAgICB9KTtcbiAgICAgICAgcnN0cmVhbTIucGlwZShyZWFkU3RyZWFtKTtcbiAgICAgICAgaWYgKHNhdmVzdHJlYW0pIHtcbiAgICAgICAgICByc3RyZWFtMi5waXBlKHNhdmVzdHJlYW0pO1xuICAgICAgICB9XG4gICAgICB9O1xuXG4gICAgICBpZiAoc2F2ZXN0cmVhbSkge1xuICAgICAgICBzYXZlc3RyZWFtLm9uKCdvcGVuJywgZnVuY3Rpb24gKCk6IHZvaWQge1xuICAgICAgICAgIG9uX29wZW4oKTtcbiAgICAgICAgfSk7XG5cbiAgICAgICAgc2F2ZXN0cmVhbS5vbignZXJyb3InLCBmdW5jdGlvbiAoZXJyKTogdm9pZCB7XG4gICAgICAgICAgc2VsZi5sb2dnZXIud2FybihcbiAgICAgICAgICAgIHsgZXJyOiBlcnIsIGZpbGVOYW1lOiBmaWxlIH0sXG4gICAgICAgICAgICAnZXJyb3Igc2F2aW5nIGZpbGUgQHtmaWxlTmFtZX06IEB7ZXJyLm1lc3NhZ2V9XFxuQHtlcnIuc3RhY2t9J1xuICAgICAgICAgICk7XG4gICAgICAgICAgaWYgKHNhdmVzdHJlYW0pIHtcbiAgICAgICAgICAgIHNhdmVzdHJlYW0uYWJvcnQoKTtcbiAgICAgICAgICB9XG4gICAgICAgICAgc2F2ZXN0cmVhbSA9IG51bGw7XG4gICAgICAgICAgb25fb3BlbigpO1xuICAgICAgICB9KTtcbiAgICAgIH0gZWxzZSB7XG4gICAgICAgIG9uX29wZW4oKTtcbiAgICAgIH1cbiAgICB9XG4gIH1cblxuICAvKipcbiAgIFJldHJpZXZlIGEgcGFja2FnZSBtZXRhZGF0YSBmb3Ige25hbWV9IHBhY2thZ2VcbiAgIEZ1bmN0aW9uIGludm9rZXMgbG9jYWxTdG9yYWdlLmdldFBhY2thZ2UgYW5kIHVwbGluay5nZXRfcGFja2FnZSBmb3IgZXZlcnlcbiAgIHVwbGluayB3aXRoIHByb3h5X2FjY2VzcyByaWdodHMgYWdhaW5zdCB7bmFtZX0gYW5kIGNvbWJpbmVzIHJlc3VsdHNcbiAgIGludG8gb25lIGpzb24gb2JqZWN0XG4gICBVc2VkIHN0b3JhZ2VzOiBsb2NhbCAmJiB1cGxpbmsgKHByb3h5X2FjY2VzcylcblxuICAgKiBAcGFyYW0ge29iamVjdH0gb3B0aW9uc1xuICAgKiBAcHJvcGVydHkge3N0cmluZ30gb3B0aW9ucy5uYW1lIFBhY2thZ2UgTmFtZVxuICAgKiBAcHJvcGVydHkge29iamVjdH0gIG9wdGlvbnMucmVxIEV4cHJlc3MgYHJlcWAgb2JqZWN0XG4gICAqIEBwcm9wZXJ0eSB7Ym9vbGVhbn0gb3B0aW9ucy5rZWVwVXBMaW5rRGF0YSBrZWVwIHVwIGxpbmsgaW5mbyBpbiBwYWNrYWdlIG1ldGEsIGxhc3QgdXBkYXRlLCBldGMuXG4gICAqIEBwcm9wZXJ0eSB7ZnVuY3Rpb259IG9wdGlvbnMuY2FsbGJhY2sgQ2FsbGJhY2sgZm9yIHJlY2VpdmUgZGF0YVxuICAgKi9cbiAgcHVibGljIGdldFBhY2thZ2Uob3B0aW9ucyk6IHZvaWQge1xuICAgIHRoaXMubG9jYWxTdG9yYWdlLmdldFBhY2thZ2VNZXRhZGF0YShvcHRpb25zLm5hbWUsIChlcnIsIGRhdGEpOiB2b2lkID0+IHtcbiAgICAgIGlmIChlcnIgJiYgKCFlcnIuc3RhdHVzIHx8IGVyci5zdGF0dXMgPj0gSFRUUF9TVEFUVVMuSU5URVJOQUxfRVJST1IpKSB7XG4gICAgICAgIC8vIHJlcG9ydCBpbnRlcm5hbCBlcnJvcnMgcmlnaHQgYXdheVxuICAgICAgICByZXR1cm4gb3B0aW9ucy5jYWxsYmFjayhlcnIpO1xuICAgICAgfVxuXG4gICAgICB0aGlzLl9zeW5jVXBsaW5rc01ldGFkYXRhKFxuICAgICAgICBvcHRpb25zLm5hbWUsXG4gICAgICAgIGRhdGEsXG4gICAgICAgIHsgcmVxOiBvcHRpb25zLnJlcSwgdXBsaW5rc0xvb2s6IG9wdGlvbnMudXBsaW5rc0xvb2sgfSxcbiAgICAgICAgZnVuY3Rpb24gZ2V0UGFja2FnZVN5blVwTGlua3NDYWxsYmFjayhlcnIsIHJlc3VsdDogTWFuaWZlc3QsIHVwbGlua0Vycm9ycyk6IHZvaWQge1xuICAgICAgICAgIGlmIChlcnIpIHtcbiAgICAgICAgICAgIHJldHVybiBvcHRpb25zLmNhbGxiYWNrKGVycik7XG4gICAgICAgICAgfVxuXG4gICAgICAgICAgbm9ybWFsaXplRGlzdFRhZ3MoY2xlYW5VcExpbmtzUmVmKG9wdGlvbnMua2VlcFVwTGlua0RhdGEsIHJlc3VsdCkpO1xuXG4gICAgICAgICAgLy8gbnBtIGNhbiB0aHJvdyBpZiB0aGlzIGZpZWxkIGRvZXNuJ3QgZXhpc3RcbiAgICAgICAgICByZXN1bHQuX2F0dGFjaG1lbnRzID0ge307XG4gICAgICAgICAgaWYgKG9wdGlvbnMuYWJicmV2aWF0ZWQgPT09IHRydWUpIHtcbiAgICAgICAgICAgIG9wdGlvbnMuY2FsbGJhY2sobnVsbCwgY29udmVydEFiYnJldmlhdGVkTWFuaWZlc3QocmVzdWx0KSwgdXBsaW5rRXJyb3JzKTtcbiAgICAgICAgICB9IGVsc2Uge1xuICAgICAgICAgICAgb3B0aW9ucy5jYWxsYmFjayhudWxsLCByZXN1bHQsIHVwbGlua0Vycm9ycyk7XG4gICAgICAgICAgfVxuICAgICAgICB9XG4gICAgICApO1xuICAgIH0pO1xuICB9XG5cbiAgLyoqXG4gICBSZXRyaWV2ZSByZW1vdGUgYW5kIGxvY2FsIHBhY2thZ2VzIG1vcmUgcmVjZW50IHRoYW4ge3N0YXJ0a2V5fVxuICAgRnVuY3Rpb24gc3RyZWFtcyBhbGwgcGFja2FnZXMgZnJvbSBhbGwgdXBsaW5rcyBmaXJzdCwgYW5kIHRoZW5cbiAgIGxvY2FsIHBhY2thZ2VzLlxuICAgTm90ZSB0aGF0IGxvY2FsIHBhY2thZ2VzIGNvdWxkIG92ZXJyaWRlIHJlZ2lzdHJ5IG9uZXMganVzdCBiZWNhdXNlXG4gICB0aGV5IGFwcGVhciBpbiBKU09OIGxhc3QuIFRoYXQncyBhIHRyYWRlLW9mZiB3ZSBtYWtlIHRvIGF2b2lkXG4gICBtZW1vcnkgaXNzdWVzLlxuICAgVXNlZCBzdG9yYWdlczogbG9jYWwgJiYgdXBsaW5rIChwcm94eV9hY2Nlc3MpXG4gICAqIEBwYXJhbSB7Kn0gc3RhcnRrZXlcbiAgICogQHBhcmFtIHsqfSBvcHRpb25zXG4gICAqIEByZXR1cm4ge1N0cmVhbX1cbiAgICovXG4gIHB1YmxpYyBzZWFyY2goc3RhcnRrZXk6IHN0cmluZywgb3B0aW9uczogYW55KSB7XG4gICAgY29uc3Qgc2VsZiA9IHRoaXM7XG4gICAgY29uc3Qgc2VhcmNoU3RyZWFtOiBhbnkgPSBuZXcgU3RyZWFtLlBhc3NUaHJvdWdoKHsgb2JqZWN0TW9kZTogdHJ1ZSB9KTtcbiAgICBhc3luYy5lYWNoU2VyaWVzKFxuICAgICAgT2JqZWN0LmtleXModGhpcy51cGxpbmtzKSxcbiAgICAgIGZ1bmN0aW9uICh1cF9uYW1lLCBjYik6IHZvaWQge1xuICAgICAgICAvLyBzaG9ydGN1dDo