verdaccio
Version:
A lightweight private npm proxy registry
623 lines (600 loc) • 84.5 kB
JavaScript
"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 }; }
function _defineProperty(e, r, t) { return (r = _toPropertyKey(r)) in e ? Object.defineProperty(e, r, { value: t, enumerable: !0, configurable: !0, writable: !0 }) : e[r] = t, e; }
function _toPropertyKey(t) { var i = _toPrimitive(t, "string"); return "symbol" == typeof i ? i : i + ""; }
function _toPrimitive(t, r) { if ("object" != typeof t || !t) return t; var e = t[Symbol.toPrimitive]; if (void 0 !== e) { var i = e.call(t, r || "default"); if ("object" != typeof i) return i; throw new TypeError("@@toPrimitive must return a primitive value."); } return ("string" === r ? String : Number)(t); }
const debug = (0, _debug.default)('verdaccio:storage');
class Storage {
constructor(config) {
_defineProperty(this, "localStorage", void 0);
_defineProperty(this, "config", void 0);
_defineProperty(this, "logger", void 0);
_defineProperty(this, "uplinks", void 0);
_defineProperty(this, "filters", void 0);
this.config = config;
this.uplinks = (0, _uplinkUtil.setupUpLinks)(config);
this.logger = _logger.logger;
this.filters = [];
// @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) {
var _this$config, _this$config$serverSe;
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 = this.config) === null || _this$config === void 0 ? void 0 : (_this$config$serverSe = _this$config.serverSettings) === null || _this$config$serverSe === void 0 ? void 0 : _this$config$serverSe.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() {
var _this$config2, _this$config2$serverS;
const plugins = await (0, _loaders.asyncLoadPlugin)(this.config.store, {
config: this.config,
logger: this.logger
}, plugin => {
return typeof plugin.getPackageStorage !== 'undefined';
}, true, (_this$config2 = this.config) === null || _this$config2 === void 0 ? void 0 : (_this$config2$serverS = _this$config2.serverSettings) === null || _this$config2$serverS === void 0 ? void 0 : _this$config2$serverS.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 => {
_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;
// if someone requesting tarball, it means that we should already have some
// information about it, so fetching package info is unnecessary
// trying local first
// flow: should be IReadTarball
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
self.localStorage.getPackageMetadata(name, (err, info) => {
if (_lodash.default.isNil(err) && 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
self._syncUplinksMetadata(name, info, {}, (err, info) => {
if (_lodash.default.isNil(err) === false) {
return readStream.emit('error', err);
}
if (_lodash.default.isNil(info._distfiles) || _lodash.default.isNil(info._distfiles[filename])) {
return readStream.emit('error', err404);
}
serveFile(info._distfiles[filename]);
});
}
});
});
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) {
var _options$req, _options$req$query;
// shortcut: if `local=1` is supplied, don't call uplinks
if (((_options$req = options.req) === null || _options$req === void 0 ? void 0 : (_options$req$query = _options$req.query) === null || _options$req$query === void 0 ? void 0 : _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) {
const self = this;
this.localStorage.storagePlugin.get((err, locals) => {
if (err) {
callback(err);
}
const packages = [];
const getPackage = function (itemPkg) {
self.localStorage.getPackageMetadata(locals[itemPkg], function (err, pkgMetadata) {
if (_lodash.default.isNil(err)) {
const latest = pkgMetadata[_constants.DIST_TAGS].latest;
if (latest && pkgMetadata.versions[latest]) {
const version = pkgMetadata.versions[latest];
const timeList = pkgMetadata.time;
const time = timeList[latest];
// @ts-ignore
version.time = time;
// Add for stars api
// @ts-ignore
version.users = pkgMetadata.users;
packages.push(version);
} else {
self.logger.warn({
package: locals[itemPkg]
}, 'package @{package} does not have a "latest" tag?');
}
}
if (itemPkg >= locals.length - 1) {
callback(null, packages);
} else {
getPackage(itemPkg + 1);
}
});
};
if (locals.length) {
getPackage(0);
} else {
callback(null, []);
}
});
}
/**
* 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
(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) {
return callback(null, packageInfo);
}
self.localStorage.updateVersions(name, packageInfo, async (err, packageJsonLocal) => {
if (err) {
return callback(err);
}
// Any error here will cause a 404, like an uplink error. This is likely the right thing to do
// as a broken filter is a security risk.
const filterErrors = [];
// This MUST be done serially and not in parallel as they modify packageJsonLocal
for (const filter of self.filters) {
try {
// These filters can assume it's save to modify packageJsonLocal and return it directly for
// performance (i.e. need not be pure)
packageJsonLocal = await filter.filter_metadata(packageJsonLocal);
} catch (err) {
filterErrors.push(err);
}
}
callback(null, packageJsonLocal, _lodash.default.concat(upLinksErrors, filterErrors));
});
});
}
/**
* 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,eyJ2ZXJzaW9uIjozLCJuYW1lcyI6WyJfYXNzZXJ0IiwiX2ludGVyb3BSZXF1aXJlRGVmYXVsdCIsInJlcXVpcmUiLCJfYXN5bmMiLCJfZGVidWciLCJfbG9kYXNoIiwiX3N0cmVhbSIsIl9jb25maWciLCJfY29yZSIsIl9sb2FkZXJzIiwiX2xvY2FsU3RvcmFnZUxlZ2FjeSIsIl9zZWFyY2hJbmRleGVyIiwiX3N0cmVhbXMiLCJfbG9nZ2VyIiwiX2NvbnN0YW50cyIsIl9sb2NhbFN0b3JhZ2UiLCJfbWV0YWRhdGFVdGlscyIsIl9zdG9yYWdlVXRpbHMiLCJfdXBTdG9yYWdlIiwiX3VwbGlua1V0aWwiLCJfdXRpbHMiLCJlIiwiX19lc01vZHVsZSIsImRlZmF1bHQiLCJfZGVmaW5lUHJvcGVydHkiLCJyIiwidCIsIl90b1Byb3BlcnR5S2V5IiwiT2JqZWN0IiwiZGVmaW5lUHJvcGVydHkiLCJ2YWx1ZSIsImVudW1lcmFibGUiLCJjb25maWd1cmFibGUiLCJ3cml0YWJsZSIsImkiLCJfdG9QcmltaXRpdmUiLCJTeW1ib2wiLCJ0b1ByaW1pdGl2ZSIsImNhbGwiLCJUeXBlRXJyb3IiLCJTdHJpbmciLCJOdW1iZXIiLCJkZWJ1ZyIsImJ1aWxkRGVidWciLCJTdG9yYWdlIiwiY29uc3RydWN0b3IiLCJjb25maWciLCJ1cGxpbmtzIiwic2V0dXBVcExpbmtzIiwibG9nZ2VyIiwiZmlsdGVycyIsImxvY2FsU3RvcmFnZSIsImluaXQiLCJzdG9yYWdlSW5zdGFuY2UiLCJsb2FkU3RvcmFnZSIsIkxvY2FsU3RvcmFnZSIsImdldFNlY3JldCIsIl90aGlzJGNvbmZpZyIsIl90aGlzJGNvbmZpZyRzZXJ2ZXJTZSIsImFzeW5jTG9hZFBsdWdpbiIsInBsdWdpbiIsImZpbHRlcl9tZXRhZGF0YSIsInNlcnZlclNldHRpbmdzIiwicGx1Z2luUHJlZml4IiwiUExVR0lOX0NBVEVHT1JZIiwiRklMVEVSIiwibGVuZ3RoIiwibG9hZFN0b3JlUGx1Z2luIiwiXyIsImlzTmlsIiwiYXNzZXJ0Iiwic3RvcmFnZSIsIkxvY2FsRGF0YWJhc2VQbHVnaW4iLCJpbmZvIiwibmFtZSIsInBsdWdpbkNhdGVnb3J5IiwiU1RPUkFHRSIsIl90aGlzJGNvbmZpZzIiLCJfdGhpcyRjb25maWcyJHNlcnZlclMiLCJwbHVnaW5zIiwic3RvcmUiLCJnZXRQYWNrYWdlU3RvcmFnZSIsIndhcm4iLCJoZWFkIiwiYWRkUGFja2FnZSIsIm1ldGFkYXRhIiwiY2FsbGJhY2siLCJjaGVja1BhY2thZ2VMb2NhbCIsImNoZWNrUGFja2FnZVJlbW90ZSIsIl9pc0FsbG93UHVibGlzaE9mZmxpbmUiLCJfc3luY1VwbGlua3NNZXRhZGF0YSIsImJpbmQiLCJwdWJsaXNoUGFja2FnZSIsImVyciIsInB1Ymxpc2giLCJpc0Jvb2xlYW4iLCJhbGxvd19vZmZsaW5lIiwicmVhZFRva2VucyIsImZpbHRlciIsInNhdmVUb2tlbiIsInRva2VuIiwiZGVsZXRlVG9rZW4iLCJ1c2VyIiwidG9rZW5LZXkiLCJhZGRWZXJzaW9uIiwidmVyc2lvbiIsInRhZyIsIm1lcmdlVGFncyIsInRhZ0hhc2giLCJjaGFuZ2VQYWNrYWdlIiwicmV2aXNpb24iLCJyZW1vdmVQYWNrYWdlIiwiU2VhcmNoTWVtb3J5SW5kZXhlciIsInJlbW92ZSIsImNhdGNoIiwicmVhc29uIiwiZXJyb3IiLCJyZW1vdmVUYXJiYWxsIiwiZmlsZW5hbWUiLCJhZGRUYXJiYWxsIiwiaGFzTG9jYWxUYXJiYWxsIiwic2VsZiIsIlByb21pc2UiLCJyZXNvbHZlIiwicmVqZWN0IiwibG9jYWxTdHJlYW0iLCJnZXRUYXJiYWxsIiwiaXNPcGVuIiwib24iLCJzdGF0dXMiLCJIVFRQX1NUQVRVUyIsIk5PVF9GT1VORCIsImFib3J0IiwicmVhZFN0cmVhbSIsIlJlYWRUYXJiYWxsIiwiZW1pdCIsImVycjQwNCIsImdldFBhY2thZ2VNZXRhZGF0YSIsIl9kaXN0ZmlsZXMiLCJzZXJ2ZUZpbGUiLCJ2IiwicGlwZSIsImZpbGUiLCJ1cGxpbmsiLCJ1cGxpbmtJZCIsImhhc1Byb3h5VG8iLCJwYWNrYWdlcyIsIlByb3h5U3RvcmFnZSIsInVybCIsImNhY2hlIiwiX2F1dG9nZW5lcmF0ZWQiLCJzYXZlc3RyZWFtIiwib25fb3BlbiIsInJzdHJlYW0yIiwiZmV0Y2hUYXJiYWxsIiwiZG9uZSIsImZpbGVOYW1lIiwiZ2V0UGFja2FnZSIsIm9wdGlvbnMiLCJkYXRhIiwiSU5URVJOQUxfRVJST1IiLCJyZXEiLCJ1cGxpbmtzTG9vayIsImdldFBhY2thZ2VTeW5VcExpbmtzQ2FsbGJhY2siLCJyZXN1bHQiLCJ1cGxpbmtFcnJvcnMiLCJub3JtYWxpemVEaXN0VGFncyIsImNsZWFuVXBMaW5rc1JlZiIsImtlZXBVcExpbmtEYXRhIiwiX2F0dGFjaG1lbnRzIiwiYWJicmV2aWF0ZWQiLCJjb252ZXJ0QWJicmV2aWF0ZWRNYW5pZmVzdCIsInNlYXJjaCIsInN0YXJ0a2V5Iiwic2VhcmNoU3RyZWFtIiwiU3RyZWFtIiwiUGFzc1Rocm91Z2giLCJvYmplY3RNb2RlIiwiYXN5bmMiLCJlYWNoU2VyaWVzIiwia2V5cyIsInVwX25hbWUiLCJjYiIsIl9vcHRpb25zJHJlcSIsIl9vcHRpb25zJHJlcSRxdWVyeSIsInF1ZXJ5IiwibG9jYWwiLCJ1bmRlZmluZWQiLCJ1cGxpbmtTdHJlYW0iLCJlbmQiLCJsb2NhbFNlYXJjaFN0cmVhbSIsImdldExvY2FsRGF0YWJhc2UiLCJzdG9yYWdlUGx1Z2luIiwiZ2V0IiwibG9jYWxzIiwiaXRlbVBrZyIsInBrZ01ldGFkYXRhIiwibGF0ZXN0IiwiRElTVF9UQUdTIiwidmVyc2lvbnMiLCJ0aW1lTGlzdCIsInRpbWUiLCJ1c2VycyIsInB1c2giLCJwYWNrYWdlIiwicGFja2FnZUluZm8iLCJmb3VuZCIsInVwTGlua3MiLCJoYXNUb0xvb2tJbnRvVXBsaW5rcyIsImdlbmVyYXRlUGFja2FnZVRlbXBsYXRlIiwibWFwIiwidXBMaW5rIiwiX29wdGlvbnMiLCJhc3NpZ24iLCJ1cExpbmtNZXRhIiwiX3VwbGlua3MiLCJ1cG5hbWUiLCJpc09iamVjdCIsImZldGNoZWQiLCJEYXRlIiwibm93IiwibWF4YWdlIiwiZXRhZyIsImdldFJlbW90ZU1ldGFkYXRhIiwidXBMaW5rUmVzcG9uc2UiLCJlVGFnIiwicmVtb3RlU3RhdHVzIiwiRXJyb3JDb2RlIiwiZ2V0SW50ZXJuYWxFcnJvciIsInZhbGlkYXRpb25VdGlscyIsIm5vcm1hbGl6ZU1ldGFkYXRhIiwic3ViIiwibWVyZ2VVcGxpbmtUaW1lSW50b0xvY2FsIiwidXBkYXRlVmVyc2lvbnNIaWRkZW5VcExpbmsiLCJtZXJnZVZlcnNpb25zIiwidXBMaW5rc0Vycm9ycyIsIkFycmF5IiwiaXNBcnJheSIsInVwbGlua1RpbWVvdXRFcnJvciIsImoiLCJjb2RlIiwiZ2V0U2VydmljZVVuYXZhaWxhYmxlIiwiZ2V0Tm90Rm91bmQiLCJBUElfRVJST1IiLCJOT19QQUNLQUdFIiwidXBkYXRlVmVyc2lvbnMiLCJwYWNrYWdlSnNvbkxvY2FsIiwiZmlsdGVyRXJyb3JzIiwiY29uY2F0IiwiX3VwZGF0ZVZlcnNpb25zSGlkZGVuVXBMaW5rIiwicHJvdG90eXBlIiwiaGFzT3duUHJvcGVydHkiLCJmb3IiLCJfZGVmYXVsdCIsImV4cG9ydHMiXSwic291cmNlcyI6WyIuLi8uLi9zcmMvbGliL3N0b3JhZ2UudHMiXSwic291cmNlc0NvbnRlbnQiOlsiaW1wb3J0IGFzc2VydCBmcm9tICdhc3NlcnQnO1xuaW1wb3J0IGFzeW5jLCB7IEFzeW5jUmVzdWx0QXJyYXlDYWxsYmFjayB9IGZyb20gJ2FzeW5jJztcbmltcG9ydCBidWlsZERlYnVnIGZyb20gJ2RlYnVnJztcbmltcG9ydCBfIGZyb20gJ2xvZGFzaCc7XG5pbXBvcnQgU3RyZWFtIGZyb20gJ3N0cmVhbSc7XG5cbmltcG9ydCB7IGhhc1Byb3h5VG8gfSBmcm9tICdAdmVyZGFjY2lvL2NvbmZpZyc7XG5pbXBvcnQgeyBQTFVHSU5fQ0FURUdPUlksIHBsdWdpblV0aWxzLCB2YWxpZGF0aW9uVXRpbHMgfSBmcm9tICdAdmVyZGFjY2lvL2NvcmUnO1xuaW1wb3J0IHsgYXN5bmNMb2FkUGx1Z2luIH0gZnJvbSAnQHZlcmRhY2Npby9sb2FkZXJzJztcbmltcG9ydCBMb2NhbERhdGFiYXNlUGx1Z2luIGZyb20gJ0B2ZXJkYWNjaW8vbG9jYWwtc3RvcmFnZS1sZWdhY3knO1xuaW1wb3J0IHsgU2VhcmNoTWVtb3J5SW5kZXhlciB9IGZyb20gJ0B2ZXJkYWNjaW8vc2VhcmNoLWluZGV4ZXInO1xuaW1wb3J0IHsgUmVhZFRhcmJhbGwgfSBmcm9tICdAdmVyZGFjY2lvL3N0cmVhbXMnO1xuaW1wb3J0IHtcbiAgQ2FsbGJhY2ssXG4gIENvbmZpZyxcbiAgRGlzdEZpbGUsXG4gIExvZ2dlcixcbiAgTWFuaWZlc3QsXG4gIE1lcmdlVGFncyxcbiAgUGFja2FnZSxcbiAgVmVyc2lvbixcbiAgVmVyc2lvbnMsXG59IGZyb20gJ0B2ZXJkYWNjaW8vdHlwZXMnO1xuaW1wb3J0IHsgR2VuZXJpY0JvZHksIFRva2VuLCBUb2tlbkZpbHRlciB9IGZyb20gJ0B2ZXJkYWNjaW8vdHlwZXMnO1xuXG5pbXBvcnQgeyBTdG9yYWdlUGx1Z2luTGVnYWN5IH0gZnJvbSAnLi4vLi4vdHlwZXMvY3VzdG9tJztcbmltcG9ydCB7IGxvZ2dlciB9IGZyb20gJy4uL2xpYi9sb2dnZXInO1xuaW1wb3J0IHsgSVBsdWdpbkZpbHRlcnMsIElTeW5jVXBsaW5rcywgU3RyaW5nVmFsdWUgfSBmcm9tICcuLi90eXBlcyc7XG5pbXBvcnQgeyBBUElfRVJST1IsIERJU1RfVEFHUywgSFRUUF9TVEFUVVMgfSBmcm9tICcuL2NvbnN0YW50cyc7XG5pbXBvcnQgTG9jYWxTdG9yYWdlLCB7IFN0b3JhZ2VQbHVnaW4gfSBmcm9tICcuL2xvY2FsLXN0b3JhZ2UnO1xuaW1wb3J0IHsgbWVyZ2VWZXJzaW9ucyB9IGZyb20gJy4vbWV0YWRhdGEtdXRpbHMnO1xuaW1wb3J0IHtcbiAgY2hlY2tQYWNrYWdlTG9jYWwsXG4gIGNoZWNrUGFja2FnZVJlbW90ZSxcbiAgY2xlYW5VcExpbmtzUmVmLFxuICBjb252ZXJ0QWJicmV2aWF0ZWRNYW5pZmVzdCxcbiAgZ2VuZXJhdGVQYWNrYWdlVGVtcGxhdGUsXG4gIG1lcmdlVXBsaW5rVGltZUludG9Mb2NhbCxcbiAgcHVibGlzaFBhY2thZ2UsXG59IGZyb20gJy4vc3RvcmFnZS11dGlscyc7XG5pbXBvcnQgUHJveHlTdG9yYWdlIGZyb20gJy4vdXAtc3RvcmFnZSc7XG5pbXBvcnQgeyBzZXR1cFVwTGlua3MsIHVwZGF0ZVZlcnNpb25zSGlkZGVuVXBMaW5rIH0gZnJvbSAnLi91cGxpbmstdXRpbCc7XG5pbXBvcnQgeyBFcnJvckNvZGUsIGlzT2JqZWN0LCBub3JtYWxpemVEaXN0VGFncyB9IGZyb20gJy4vdXRpbHMnO1xuXG5jb25zdCBkZWJ1ZyA9IGJ1aWxkRGVidWcoJ3ZlcmRhY2NpbzpzdG9yYWdlJyk7XG5cbmNsYXNzIFN0b3JhZ2Uge1xuICBwdWJsaWMgbG9jYWxTdG9yYWdlOiBMb2NhbFN0b3JhZ2U7XG4gIHB1YmxpYyBjb25maWc6IENvbmZpZztcbiAgcHVibGljIGxvZ2dlcjogTG9nZ2VyO1xuICBwdWJsaWMgdXBsaW5rczogUmVjb3JkPHN0cmluZywgUHJveHlTdG9yYWdlPjtcbiAgcHVibGljIGZpbHRlcnM6IElQbHVnaW5GaWx0ZXJzO1xuXG4gIHB1YmxpYyBjb25zdHJ1Y3Rvcihjb25maWc6IENvbmZpZykge1xuICAgIHRoaXMuY29uZmlnID0gY29uZmlnO1xuICAgIHRoaXMudXBsaW5rcyA9IHNldHVwVXBMaW5rcyhjb25maWcpO1xuICAgIHRoaXMubG9nZ2VyID0gbG9nZ2VyO1xuICAgIHRoaXMuZmlsdGVycyA9IFtdO1xuICAgIC8vIEB0cy1pZ25vcmVcbiAgICB0aGlzLmxvY2FsU3RvcmFnZSA9IG51bGw7XG4gIH1cblxuICBwdWJsaWMgYXN5bmMgaW5pdChjb25maWc6IENvbmZpZywgZmlsdGVyczogSVBsdWdpbkZpbHRlcnMgPSBbXSk6IFByb21pc2U8dm9pZD4ge1xuICAgIGlmICh0aGlzLmxvY2FsU3RvcmFnZSA9PT0gbnVsbCkge1xuICAgICAgdGhpcy5maWx0ZXJzID0gZmlsdGVycztcbiAgICAgIGNvbnN0IHN0b3JhZ2VJbnN0YW5jZSA9IGF3YWl0IHRoaXMubG9hZFN0b3JhZ2UoY29uZmlnLCB0aGlzLmxvZ2dlcik7XG4gICAgICB0aGlzLmxvY2FsU3RvcmFnZSA9IG5ldyBMb2NhbFN0b3JhZ2UodGhpcy5jb25maWcsIGxvZ2dlciwgc3RvcmFnZUluc3RhbmNlKTtcbiAgICAgIGF3YWl0IHRoaXMubG9jYWxTdG9yYWdlLmdldFNlY3JldChjb25maWcpO1xuICAgICAgZGVidWcoJ2luaXRpYWxpemF0aW9uIGNvbXBsZXRlZCcpO1xuICAgIH0gZWxzZSB7XG4gICAgICBkZWJ1Zygnc3RvcmFnZSBoYXMgYmVlbiBhbHJlYWR5IGluaXRpYWxpemVkJyk7XG4gICAgfVxuXG4gICAgaWYgKCF0aGlzLmZpbHRlcnMpIHtcbiAgICAgIHRoaXMuZmlsdGVycyA9IGF3YWl0IGFzeW5jTG9hZFBsdWdpbjxwbHVnaW5VdGlscy5NYW5pZmVzdEZpbHRlcjx1bmtub3duPj4oXG4gICAgICAgIHRoaXMuY29uZmlnLmZpbHRlcnMsXG4gICAgICAgIHtcbiAgICAgICAgICBjb25maWc6IHRoaXMuY29uZmlnLFxuICAgICAgICAgIGxvZ2dlcjogdGhpcy5sb2dnZXIsXG4gICAgICAgIH0sXG4gICAgICAgIChwbHVnaW46IHBsdWdpblV0aWxzLk1hbmlmZXN0RmlsdGVyPENvbmZpZz4pID0+IHtcbiAgICAgICAgICByZXR1cm4gdHlwZW9mIHBsdWdpbi5maWx0ZXJfbWV0YWRhdGEgIT09ICd1bmRlZmluZWQnO1xuICAgICAgICB9LFxuICAgICAgICB0cnVlLFxuICAgICAgICB0aGlzLmNvbmZpZz8uc2VydmVyU2V0dGluZ3M/LnBsdWdpblByZWZpeCxcbiAgICAgICAgUExVR0lOX0NBVEVHT1JZLkZJTFRFUlxuICAgICAgKTtcbiAgICAgIGRlYnVnKCdmaWx0ZXJzIGF2YWlsYWJsZSAlbycsIHRoaXMuZmlsdGVycy5sZW5ndGgpO1xuICAgIH1cbiAgfVxuXG4gIHByaXZhdGUgYXN5bmMgbG9hZFN0b3JhZ2UoY29uZmlnOiBDb25maWcsIGxvZ2dlcjogTG9nZ2VyKTogUHJvbWlzZTxTdG9yYWdlUGx1Z2luPiB7XG4gICAgY29uc3QgU3RvcmFnZSA9IGF3YWl0IHRoaXMubG9hZFN0b3JlUGx1Z2luKCk7XG4gICAgaWYgKF8uaXNOaWwoU3RvcmFnZSkpIHtcbiAgICAgIGFzc2VydCh0aGlzLmNvbmZpZy5zdG9yYWdlLCAnQ09ORklHOiBzdG9yYWdlIHBhdGggbm90IGRlZmluZWQnKTtcbiAgICAgIGRlYnVnKCdubyBjdXN0b20gc3RvcmFnZSBmb3VuZCwgbG9hZGluZyBkZWZhdWx0IHN0b3JhZ2UgQHZlcmRhY2Npby9sb2NhbC1zdG9yYWdlJyk7XG4gICAgICBjb25zdCBsb2NhbFN0b3JhZ2UgPSBuZXcgTG9jYWxEYXRhYmFzZVBsdWdpbihjb25maWcsIGxvZ2dlcik7XG4gICAgICBsb2dnZXIuaW5mbyhcbiAgICAgICAgeyBuYW1lOiAnQHZlcmRhY2Npby9sb2NhbC1zdG9yYWdlJywgcGx1Z2luQ2F0ZWdvcnk6IFBMVUdJTl9DQVRFR09SWS5TVE9SQUdFIH0sXG4gICAgICAgICdwbHVnaW4gQHtuYW1lfSBzdWNjZXNzZnVsbHkgbG9hZGVkIChAe3BsdWdpbkNhdGVnb3J5fSknXG4gICAgICApO1xuICAgICAgcmV0dXJuIGxvY2FsU3RvcmFnZTtcbiAgICB9XG4gICAgcmV0dXJuIFN0b3JhZ2UgYXMgU3RvcmFnZVBsdWdpbjtcbiAgfVxuXG4gIHByaXZhdGUgYXN5bmMgbG9hZFN0b3JlUGx1Z2luKCk6IFByb21pc2U8U3RvcmFnZVBsdWdpbkxlZ2FjeTxDb25maWc+IHwgdW5kZWZpbmVkPiB7XG4gICAgY29uc3QgcGx1Z2luczogU3RvcmFnZVBsdWdpbkxlZ2FjeTxDb25maWc+W10gPSBhd2FpdCBhc3luY0xvYWRQbHVnaW48XG4gICAgICBwbHVnaW5VdGlscy5TdG9yYWdlPHVua25vd24+XG4gICAgPihcbiAgICAgIHRoaXMuY29uZmlnLnN0b3JlLFxuICAgICAge1xuICAgICAgICBjb25maWc6IHRoaXMuY29uZmlnLFxuICAgICAgICBsb2dnZXI6IHRoaXMubG9nZ2VyLFxuICAgICAgfSxcbiAgICAgIChwbHVnaW4pID0+IHtcbiAgICAgICAgcmV0dXJuIHR5cGVvZiBwbHVnaW4uZ2V0UGFja2FnZVN0b3JhZ2UgIT09ICd1bmRlZmluZWQnO1xuICAgICAgfSxcbiAgICAgIHRydWUsXG4gICAgICB0aGlzLmNvbmZpZz8uc2VydmVyU2V0dGluZ3M/LnBsdWdpblByZWZpeCxcbiAgICAgIFBMVUdJTl9DQVRFR09SWS5TVE9SQUdFXG4gICAgKTtcblxuICAgIGlmIChwbHVnaW5zLmxlbmd0aCA+IDEpIHtcbiAgICAgIHRoaXMubG9nZ2VyLndhcm4oXG4gICAgICAgICdtb3JlIHRoYW4gb25lIHN0b3JhZ2UgcGx1Z2lucyBoYXMgYmVlbiBkZXRlY3RlZCwgbXVsdGlwbGUgc3RvcmFnZSBhcmUgbm90IHN1cHBvcnRlZCwgb25lIHdpbGwgYmUgc2VsZWN0ZWQgYXV0b21hdGljYWxseSdcbiAgICAgICk7XG4gICAgfVxuXG4gICAgcmV0dXJuIF8uaGVhZChwbHVnaW5zKTtcbiAgfVxuXG4gIC8qKlxuICAgKiAgQWRkIGEge25hbWV9IHBhY2thZ2UgdG8gYSBzeXN0ZW1cbiAgIEZ1bmN0aW9uIGNoZWNrcyBpZiBwYWNrYWdlIHdpdGggdGhlIHNhbWUgbmFtZSBpcyBhdmFpbGFibGUgZnJvbSB1cGxpbmtzLlxuICAgSWYgaXQgaXNuJ3QsIHdlIGNyZWF0ZSBwYWNrYWdlIGxvY2FsbHlcbiAgIFVzZWQgc3RvcmFnZXM6IGxvY2FsICh3cml0ZSkgJiYgdXBsaW5rc1xuICAgKi9cbiAgcHVibGljIGFzeW5jIGFkZFBhY2thZ2UobmFtZTogc3RyaW5nLCBtZXRhZGF0YTogYW55LCBjYWxsYmFjazogRnVuY3Rpb24pOiBQcm9taXNlPHZvaWQ+IHtcbiAgICB0cnkge1xuICAgICAgYXdhaXQgY2hlY2tQYWNrYWdlTG9jYWwobmFtZSwgdGhpcy5sb2NhbFN0b3JhZ2UpO1xuICAgICAgYXdhaXQgY2hlY2tQYWNrYWdlUmVtb3RlKFxuICAgICAgICBuYW1lLFxuICAgICAgICB0aGlzLl9pc0FsbG93UHVibGlzaE9mZmxpbmUoKSxcbiAgICAgICAgdGhpcy5fc3luY1VwbGlua3NNZXRhZGF0YS5iaW5kKHRoaXMpXG4gICAgICApO1xuICAgICAgYXdhaXQgcHVibGlzaFBhY2thZ2UobmFtZSwgbWV0YWRhdGEsIHRoaXMubG9jYWxTdG9yYWdlKTtcbiAgICAgIGNhbGxiYWNrKCk7XG4gICAgfSBjYXRjaCAoZXJyOiBhbnkpIHtcbiAgICAgIGNhbGxiYWNrKGVycik7XG4gICAgfVxuICB9XG5cbiAgcHJpdmF0ZSBfaXNBbGxvd1B1Ymxpc2hPZmZsaW5lKCk6IGJvb2xlYW4ge1xuICAgIHJldHVybiAoXG4gICAgICB0eXBlb2YgdGhpcy5jb25maWcucHVibGlzaCAhPT0gJ3VuZGVmaW5lZCcgJiZcbiAgICAgIF8uaXNCb29sZWFuKHRoaXMuY29uZmlnLnB1Ymxpc2guYWxsb3dfb2ZmbGluZSkgJiZcbiAgICAgIHRoaXMuY29uZmlnLnB1Ymxpc2guYWxsb3dfb2ZmbGluZVxuICAgICk7XG4gIH1cblxuICBwdWJsaWMgcmVhZFRva2VucyhmaWx0ZXI6IFRva2VuRmlsdGVyKTogUHJvbWlzZTxUb2tlbltdPiB7XG4gICAgcmV0dXJuIHRoaXMubG9jYWxTdG9yYWdlLnJlYWRUb2tlbnMoZmlsdGVyKTtcbiAgfVxuXG4gIHB1YmxpYyBzYXZlVG9rZW4odG9rZW46IFRva2VuKTogUHJvbWlzZTx2b2lkPiB7XG4gICAgcmV0dXJuIHRoaXMubG9jYWxTdG9yYWdlLnNhdmVUb2tlbih0b2tlbik7XG4gIH1cblxuICBwdWJsaWMgZGVsZXRlVG9rZW4odXNlcjogc3RyaW5nLCB0b2tlbktleTogc3RyaW5nKTogUHJvbWlzZTxhbnk+IHtcbiAgICByZXR1cm4gdGhpcy5sb2NhbFN0b3JhZ2UuZGVsZXRlVG9rZW4odXNlciwgdG9rZW5LZXkpO1xuICB9XG5cbiAgLyoqXG4gICAqIEFkZCBhIG5ldyB2ZXJzaW9uIG9mIHBhY2thZ2Uge25hbWV9IHRvIGEgc3lzdGVtXG4gICBVc2VkIHN0b3JhZ2VzOiBsb2NhbCAod3JpdGUpXG4gICAqL1xuICBwdWJsaWMgYWRkVmVyc2lvbihcbiAgICBuYW1lOiBzdHJpbmcsXG4gICAgdmVyc2lvbjogc3RyaW5nLFxuICAgIG1ldGFkYXRhOiBWZXJzaW9uLFxuICAgIHRhZzogU3RyaW5nVmFsdWUsXG4gICAgY2FsbGJhY2s6IENhbGxiYWNrXG4gICk6IHZvaWQge1xuICAgIHRoaXMubG9jYWxTdG9yYWdlLmFkZFZlcnNpb24obmFtZSwgdmVyc2lvbiwgbWV0YWRhdGEsIHRhZywgY2FsbGJhY2spO1xuICB9XG5cbiAgLyoqXG4gICAqIFRhZ3MgYSBwYWNrYWdlIHZlcnNpb24gd2l0aCBhIHByb3ZpZGVkIHRhZ1xuICAgVXNlZCBzdG9yYWdlczogbG9jYWwgKHdyaXRlKVxuICAgKi9cbiAgcHVibGljIG1lcmdlVGFncyhuYW1lOiBzdHJpbmcsIHRhZ0hhc2g6IE1lcmdlVGFncywgY2FsbGJhY2s6IENhbGxiYWNrKTogdm9pZCB7XG4gICAgdGhpcy5sb2NhbFN0b3JhZ2UubWVyZ2VUYWdzKG5hbWUsIHRhZ0hhc2gsIGNhbGxiYWNrKTtcbiAgfVxuXG4gIC8qKlxuICAgKiBDaGFuZ2UgYW4gZXhpc3RpbmcgcGFja2FnZSAoaS5lLiB1bnB1Ymxpc2ggb25lIHZlcnNpb24pXG4gICBGdW5jdGlvbiBjaGFuZ2VzIGEgcGFja2FnZSBpbmZvIGZyb20gbG9jYWwgc3RvcmFnZSBhbmQgYWxsIHVwbGlua3Mgd2l0aCB3cml0ZSBhY2Nlc3MuL1xuICAgVXNlZCBzdG9yYWdlczogbG9jYWwgKHdyaXRlKVxuICAgKi9cbiAgcHVibGljIGNoYW5nZVBhY2thZ2UoXG4gICAgbmFtZTogc3RyaW5nLFxuICAgIG1ldGFkYXRhOiBQYWNrYWdlLFxuICAgIHJldmlzaW9uOiBzdHJpbmcsXG4gICAgY2FsbGJhY2s6IENhbGxiYWNrXG4gICk6IHZvaWQge1xuICAgIHRoaXMubG9jYWxTdG9yYWdlLmNoYW5nZVBhY2thZ2UobmFtZSwgbWV0YWRhdGEsIHJldmlzaW9uLCBjYWxsYmFjayk7XG4gIH1cblxuICAvKipcbiAgICogUmVtb3ZlIGEgcGFja2FnZSBmcm9tIGEgc3lzdGVtXG4gICBGdW5jdGlvbiByZW1vdmVzIGEgcGFja2FnZSBmcm9tIGxvY2FsIHN0b3JhZ2VcbiAgIFVzZWQgc3RvcmFnZXM6IGxvY2FsICh3cml0ZSlcbiAgICovXG4gIHB1YmxpYyByZW1vdmVQYWNrYWdlKG5hbWU6IHN0cmluZywgY2FsbGJhY2s6IENhbGxiYWNrKTogdm9pZCB7XG4gICAgdGhpcy5sb2NhbFN0b3JhZ2UucmVtb3ZlUGFja2FnZShuYW1lLCBjYWxsYmFjayk7XG4gICAgLy8gdXBkYXRlIHRoZSBpbmRleGVyXG4gICAgU2VhcmNoTWVtb3J5SW5kZXhlci5yZW1vdmUobmFtZSkuY2F0Y2goKHJlYXNvbikgPT4ge1xuICAgICAgbG9nZ2VyLmVycm9yKCdpbmRleGVyIGhhcyBmYWlsZWQgb24gcmVtb3ZlIGl0ZW0nKTtcbiAgICB9KTtcbiAgfVxuXG4gIC8qKlxuICAgUmVtb3ZlIGEgdGFyYmFsbCBmcm9tIGEgc3lzdGVtXG4gICBGdW5jdGlvbiByZW1vdmVzIGEgdGFyYmFsbCBmcm9tIGxvY2FsIHN0b3JhZ2UuXG4gICBUYXJiYWxsIGluIHF1ZXN0aW9uIHNob3VsZCBub3QgYmUgbGlua2VkIHRvIGluIGFueSBleGlzdGluZ1xuICAgdmVyc2lvbnMsIGkuZS4gcGFja2FnZSB2ZXJzaW9uIHNob3VsZCBiZSB1bnB1Ymxpc2hlZCBmaXJzdC5cbiAgIFVzZWQgc3RvcmFnZTogbG9jYWwgKHdyaXRlKVxuICAgKi9cbiAgcHVibGljIHJlbW92ZVRhcmJhbGwobmFtZTogc3RyaW5nLCBmaWxlbmFtZTogc3RyaW5nLCByZXZpc2lvbjogc3RyaW5nLCBjYWxsYmFjazogQ2FsbGJhY2spOiB2b2lkIHtcbiAgICB0aGlzLmxvY2FsU3RvcmFnZS5yZW1vdmVUYXJiYWxsKG5hbWUsIGZpbGVuYW1lLCByZXZpc2lvbiwgY2FsbGJhY2spO1xuICB9XG5cbiAgLyoqXG4gICAqIFVwbG9hZCBhIHRhcmJhbGwgZm9yIHtuYW1lfSBwYWNrYWdlXG4gICBGdW5jdGlvbiBpcyBzeW5jaHJvbm91cyBhbmQgcmV0dXJucyBhIFdyaXRhYmxlU3RyZWFtXG4gICBVc2VkIHN0b3JhZ2VzOiBsb2NhbCAod3JpdGUpXG4gICAqL1xuICBwdWJsaWMgYWRkVGFyYmFsbChuYW1lOiBzdHJpbmcsIGZpbGVuYW1lOiBzdHJpbmcpIHtcbiAgICByZXR1cm4gdGhpcy5sb2NhbFN0b3JhZ2UuYWRkVGFyYmFsbChuYW1lLCBmaWxlbmFtZSk7XG4gIH1cblxuICBwdWJsaWMgaGFzTG9jYWxUYXJiYWxsKG5hbWU6IHN0cmluZywgZmlsZW5hbWU6IHN0cmluZyk6IFByb21pc2U8Ym9vbGVhbj4ge1xuICAgIGNvbnN0IHNlbGYgPSB0aGlzO1xuICAgIHJldHVybiBuZXcgUHJvbWlzZTxib29sZWFuPigocmVzb2x2ZSwgcmVqZWN0KTogdm9pZCA9PiB7XG4gICAgICBsZXQgbG9jYWxTdHJlYW06IGFueSA9IHNlbGYubG9jYWxTdG9yYWdlLmdldFRhcmJhbGwobmFtZSwgZmlsZW5hbWUpO1xuICAgICAgbGV0IGlzT3BlbiA9IGZhbHNlO1xuICAgICAgbG9jYWxTdHJlYW0ub24oJ2Vycm9yJywgKGVycik6IGFueSA9PiB7XG4gICAgICAgIGlmIChpc09wZW4gfHwgZXJyLnN0YXR1cyAhPT0gSFRUUF9TVEFUVVMuTk9UX0ZPVU5EKSB7XG4gICAgICAgICAgcmVqZWN0KGVycik7XG4gICAgICAgIH1cbiAgICAgICAgLy8gbG9jYWwgcmVwb3J0ZWQgNDA0IG9yIHJlcXVlc3Qgd2FzIGFib3J0ZWQgYWxyZWFkeVxuICAgICAgICBpZiAobG9jYWxTdHJlYW0pIHtcbiAgICAgICAgICBsb2NhbFN0cmVhbS5hYm9ydCgpO1xuICAgICAgICAgIGxvY2FsU3RyZWFtID0gbnVsbDtcbiAgICAgICAgfVxuICAgICAgICByZXNvbHZlKGZhbHNlKTtcbiAgICAgIH0pO1xuICAgICAgbG9jYWxTdHJlYW0ub24oJ29wZW4nLCBmdW5jdGlvbiAoKTogdm9pZCB7XG4gICAgICAgIGlzT3BlbiA9IHRydWU7XG4gICAgICAgIGxvY2FsU3RyZWFtLmFib3J0KCk7XG4gICAgICAgIGxvY2FsU3RyZWFtID0gbnVsbDtcbiAgICAgICAgcmVzb2x2ZSh0cnVlKTtcbiAgICAgIH0pO1xuICAgIH0pO1xuICB9XG5cbiAgLyoqXG4gICBHZXQgYSB0YXJiYWxsIGZyb20gYSBzdG9yYWdlIGZvciB7bmFtZX0gcGFja2FnZVxuICAgRnVuY3Rpb24gaXMgc3luY2hyb25vdXMgYW5kIHJldHVybnMgYSBSZWFkYWJsZVN0cmVhbVxuICAgRnVuY3Rpb24gdHJpZXMgdG8gcmVhZCB0YXJiYWxsIGxvY2FsbHksIGlmIGl0IGZhaWxzIHRoZW4gaXQgcmVhZHMgcGFja2FnZVxuICAgaW5mb3JtYXRpb24gaW4gb3JkZXIgdG8gZmlndXJlIG91dCB3aGVyZSB3ZSBjYW4gZ2V0IHRoaXMgdGFyYmFsbCBmcm9tXG4gICBVc2VkIHN0b3JhZ2VzOiBsb2NhbCB8fCB1cGxpbmsgKGp1c3Qgb25lKVxuICAgKi9cbiAgcHVibGljIGdldFRhcmJhbGwobmFtZTogc3RyaW5nLCBmaWxlbmFtZTogc3RyaW5nKSB7XG4gICAgY29uc3QgcmVhZFN0cmVhbSA9IG5ldyBSZWFkVGFyYmFsbCh7fSk7XG4gICAgcmVhZFN0cmVhbS5hYm9ydCA9IGZ1bmN0aW9uICgpIHt9O1xuXG4gICAgY29uc3Qgc2VsZiA9IHRoaXM7XG5cbiAgICAvLyBpZiBzb21lb25lIHJlcXVlc3RpbmcgdGFyYmFsbCwgaXQgbWVhbnMgdGhhdCB3ZSBzaG91bGQgYWxyZWFkeSBoYXZlIHNvbWVcbiAgICAvLyBpbmZvcm1hdGlvbiBhYm91dCBpdCwgc28gZmV0Y2hpbmcgcGFja2FnZSBpbmZvIGlzIHVubmVjZXNzYXJ5XG5cbiAgICAvLyB0cnlpbmcgbG9jYWwgZmlyc3RcbiAgICAvLyBmbG93OiBzaG91bGQgYmUgSVJlYWRUYXJiYWxsXG4gICAgbGV0IGxvY2FsU3RyZWFtOiBhbnkgPSBzZWxmLmxvY2FsU3RvcmFnZS5nZXRUYXJiYWxsKG5hbWUsIGZpbGVuYW1lKTtcbiAgICBsZXQgaXNPcGVuID0gZmFsc2U7XG4gICAgbG9jYWxTdHJlYW0ub24oJ2Vycm9yJywgKGVycik6IGFueSA9PiB7XG4gICAgICBpZiAoaXNPcGVuIHx8IGVyci5zdGF0dXMgIT09IEhUVFBfU1RBVFVTLk5PVF9GT1VORCkge1xuICAgICAgICByZXR1cm4gcmVhZFN0cmVhbS5lbWl0KCdlcnJvcicsIGVycik7XG4gICAgICB9XG5cbiAgICAgIC8vIGxvY2FsIHJlcG9ydGVkIDQwNFxuICAgICAgY29uc3QgZXJyNDA0ID0gZXJyO1xuICAgICAgbG9jYWxTdHJlYW0uYWJvcnQoKTtcbiAgICAgIGxvY2FsU3RyZWFtID0gbnVsbDsgLy8gd2UgZm9yY2UgZm9yIGdhcmJhZ2UgY29sbGVjdG9yXG4gICAgICBzZWxmLmxvY2FsU3RvcmFnZS5nZXRQYWNrYWdlTWV0YWRhdGEobmFtZSwgKGVyciwgaW5mbzogUGFja2FnZSk6IHZvaWQgPT4ge1xuICAgICAgICBpZiAoXy5pc05pbChlcnIpICYmIGluZm8uX2Rpc3RmaWxlcyAmJiBfLmlzTmlsKGluZm8uX2Rpc3RmaWxlc1tmaWxlbmFtZV0pID09PSBmYWxzZSkge1xuICAgICAgICAgIC8vIGluZm9ybWF0aW9uIGFib3V0IHRoaXMgZmlsZSBleGlzdHMgbG9jYWxseVxuICAgICAgICAgIHNlcnZlRmlsZShpbmZvLl9kaXN0ZmlsZXNbZmlsZW5hbWVdKTtcbiAgICAgICAgfSBlbHNlIHtcbiAgICAgICAgICAvLyB3ZSBrbm93IG5vdGhpbmcgYWJvdXQgdGhpcyBmaWxlLCB0cnlpbmcgdG8gZ2V0IGluZm9ybWF0aW9uIGVsc2V3aGVyZVxuICAgICAgICAgIHNlbGYuX3N5bmNVcGxpbmtzTWV0YWRhdGEobmFtZSwgaW5mbywge30sIChlcnIsIGluZm86IFBhY2thZ2UpOiBhbnkgPT4ge1xuICAgICAgICAgICAgaWYgKF8uaXNOaWwoZXJyKSA9PT0gZmFsc2UpIHtcbiAgICAgICAgICAgICAgcmV0dXJuIHJlYWRTdHJlYW0uZW1pdCgnZXJyb3InLCBlcnIpO1xuICAgICAgICAgICAgfVxuICAgICAgICAgICAgaWYgKF8uaXNOaWwoaW5mby5fZGlzdGZpbGVzKSB8fCBfLmlzTmlsKGluZm8uX2Rpc3RmaWxlc1tmaWxlbmFtZV0pKSB7XG4gICAgICAgICAgICAgIHJldHVybiByZWFkU3RyZWFtLmVtaXQoJ2Vycm9yJywgZXJyNDA0KTtcbiAgICAgICAgICAgIH1cbiAgICAgICAgICAgIHNlcnZlRmlsZShpbmZvLl9kaXN0ZmlsZXNbZmlsZW5hbWVdKTtcbiAgICAgICAgICB9KTtcbiAgICAgICAgfVxuICAgICAgfSk7XG4gICAgfSk7XG4gICAgbG9jYWxTdHJlYW0ub24oJ2NvbnRlbnQtbGVuZ3RoJywgZnVuY3Rpb24gKHYpOiB2b2lkIHtcbiAgICAgIHJlYWRTdHJlYW0uZW1pdCgnY29udGVudC1sZW5ndGgnLCB2KTtcbiAgICB9KTtcbiAgICBsb2NhbFN0cmVhbS5vbignb3BlbicsIGZ1bmN0aW9uICgpOiB2b2lkIHtcbiAgICAgIGlzT3BlbiA9IHRydWU7XG4gICAgICBsb2NhbFN0cmVhbS5waXBlKHJlYWRTdHJlYW0pO1xuICAgIH0pO1xuICAgIHJldHVybiByZWFkU3RyZWFtO1xuXG4gICAgLyoqXG4gICAgICogRmV0Y2ggYW5kIGNhY2hlIGxvY2FsL3JlbW90ZSBwYWNrYWdlcy5cbiAgICAgKiBAcGFyYW0ge09iamVjdH0gZmlsZSBkZWZpbmUgdGhlIHBhY2thZ2Ugc2hhcGVcbiAgICAgKi9cbiAgICBmdW5jdGlvbiBzZXJ2ZUZpbGUoZmlsZTogRGlzdEZpbGUpOiB2b2lkIHtcbiAgICAgIGxldCB1cGxpbms6IGFueSA9IG51bGw7XG5cbiAgICAgIGZvciAoY29uc3QgdXBsaW5rSWQgaW4gc2VsZi51cGxpbmtzKSB7XG4gICAgICAgIGlmIChoYXNQcm94eVRvKG5hbWUsIHVwbGlua0lkLCBzZWxmLmNvbmZpZy5wYWNrYWdlcykpIHtcbiAgICAgICAgICB1cGxpbmsgPSBzZWxmLnVwbGlua3NbdXBsaW5rSWRdO1xuICAgICAgICB9XG4gICAgICB9XG5cbiAgICAgIGlmICh1cGxpbmsgPT0gbnVsbCkge1xuICAgICAgICB1cGxpbmsgPSBuZXcgUHJveHlTdG9yYWdlKFxuICAgICAgICAgIHtcbiAgICAgICAgICAgIHVybDogZmlsZS51cmwsXG4gICAgICAgICAgICBjYWNoZTogdHJ1ZSxcbiAgICAgICAgICAgIF9hdXRvZ2VuZXJhdGVkOiB0cnVlLFxuICAgICAgICAgIH0sXG4gICAgICAgICAgc2VsZi5jb25maWdcbiAgICAgICAgKTtcbiAgICAgIH1cblxuICAgICAgbGV0IHNhdmVzdHJlYW06IGFueSA9IG51bGw7XG4gICAgICBpZiAodXBsaW5rLmNvbmZpZy5jYWNoZSkge1xuICAgICAgICBzYXZlc3RyZWFtID0gc2VsZi5sb2NhbFN0b3JhZ2UuYWRkVGFyYmFsbChuYW1lLCBmaWxlbmFtZSk7XG4gICAgICB9XG5cbiAgICAgIGxldCBvbl9vcGVuID0gZnVuY3Rpb24gKCk6IHZvaWQge1xuICAgICAgICAvLyBwcmV2ZW50IGl0IGZyb20gYmVpbmcgY2FsbGVkIHR3aWNlXG4gICAgICAgIG9uX29wZW4gPSBmdW5jdGlvbiAoKSB7fTtcbiAgICAgICAgY29uc3QgcnN0cmVhbTIgPSB1cGxpbmsuZmV0Y2hUYXJiYWxsKGZpbGUudXJsKTtcbiAgICAgICAgcnN0cmVhbTIub24oJ2Vycm9yJywgZnVuY3Rpb24gKGVycik6IHZvaWQge1xuICAgICAgICAgIGlmIChzYXZlc3RyZWFtKSB7XG4gICAgICAgICAgICBzYXZlc3RyZWFtLmFib3J0KCk7XG4gICAgICAgICAgfVxuICAgICAgICAgIHNhdmVzdHJlYW0gPSBudWxsO1xuICAgICAgICAgIHJlYWRTdHJlYW0uZW1pdCgnZXJyb3InLCBlcnIpO1xuICAgICAgICB9KTtcbiAgICAgICAgcnN0cmVhbTIub24oJ2VuZCcsIGZ1bmN0aW9uICgpOiB2b2lkIHtcbiAgICAgICAgICBpZiAoc2F2ZXN0cmVhbSkge1xuICAgICAgICAgICAgc2F2ZXN0cmVhbS5kb25lKCk7XG4gICAgICAgICAgfVxuICAgICAgICB9KTtcblxuICAgICAgICByc3RyZWFtMi5vbignY29udGVudC1sZW5ndGgnLCBmdW5jdGlvbiAodik6IHZvaWQge1xuICAgICAgICAgIHJlYWRTdHJlYW0uZW1pdCgnY29udGVudC1sZW5ndGgnLCB2KTtcbiAgICAgICAgICBpZiAoc2F2ZXN0cmVhbSkge1xuICAgICAgICAgICAgc2F2ZXN0cmVhbS5lbWl0KCdjb250ZW50LWxlbmd0aCcsIHYpO1xuICAgICAgICAgIH1cbiAgICAgICAgfSk7XG4gICAgICAgIHJzdHJlYW0yLnBpcGUocmVhZFN0cmVhbSk7XG4gICAgICAgIGlmIChzYXZlc3RyZWFtKSB7XG4gICAgICAgICAgcnN0cmVhbTIucGlwZShzYXZlc3RyZWFtKTtcbiAgICAgICAgfVxuICAgICAgfTtcblxuICAgICAgaWYgKHNhdmVzdHJlYW0pIHtcbiAgICAgICAgc2F2ZXN0cmVhbS5vbignb3BlbicsIGZ1bmN0aW9uICgpOiB2b2lkIHtcbiAgICAgICAgICBvbl9vcGVuKCk7XG4gICAgICAgIH0pO1xuXG4gICAgICAgIHNhdmVzdHJlYW0ub24oJ2Vycm9yJywgZnVuY3Rpb24gKGVycik6IHZvaWQge1xuICAgICAgICAgIHNlbGYubG9nZ2VyLndhcm4oXG4gICAgICAgICAgICB7IGVycjogZXJyLCBmaWxlTmFtZTogZmlsZSB9LFxuICAgICAgICAgICAgJ2Vycm9yIHNhdmluZyBmaWxlIEB7ZmlsZU5hbWV9OiBAe2Vyci5tZXNzYWdlfVxcbkB7ZXJyLnN0YWNrfSdcbiAgICAgICAgICApO1xuICAgICAgICAgIGlmIChzYXZlc3RyZWFtKSB7XG4gICAgICAgICAgICBzYXZlc3RyZWFtLmFib3J0KCk7XG4gICAgICAgICAgfVxuICAgICAgICAgIHNhdmVzdHJlYW0gPSBudWxsO1xuICAgICAgICAgIG9uX29wZW4oKTtcbiAgICAgICAgfSk7XG4gICAgICB9IGVsc2Uge1xuICAgICAgICBvbl9vcGVuKCk7XG4gICAgICB9XG4gICAgfVxuICB9XG5cbiAgLyoqXG4gICBSZXRyaWV2ZSBhIHBhY2thZ2UgbWV0YWRhdGEgZm9yIHtuYW1lfSBwYWNrYWdlXG4gICBGdW5jdGlvbiBpbnZva2VzIGxvY2FsU3RvcmFnZS5nZXRQYWNrYWdlIGFuZCB1cGxpbmsuZ2V0X3BhY2thZ2UgZm9yIGV2ZXJ5XG4gICB1cGxpbmsgd2l0aCBwcm94eV9hY2Nlc3MgcmlnaHRzIGFnYWluc3Qge25hbWV9IGFuZCBjb21iaW5lcyByZXN1bHRzXG4gICBpbnRvIG9uZSBqc29uIG9iamVjdFxuICAgVXNlZCBzdG9yYWdlczogbG9jYWwgJiYgdXBsaW5rIChwcm94eV9hY2Nlc3MpXG5cbiAgICogQHBhcmFtIHtvYmplY3R9IG9wdGlvbnNcbiAgICogQHByb3BlcnR5IHtzdHJpbmd9IG9wdGlvbnMubmFtZSBQYWNrYWdlIE5hbWVcbiAgICogQHByb3BlcnR5IHtvYmplY3R9ICBvcHRpb25zLnJlcSBFeHByZXNzIGByZXFgIG9iamVjdFxuICAgKiBAcHJvcGVydHkge2Jvb2xlYW59IG9wdGlvbnMua2VlcFVwTGlua0RhdGEga2VlcCB1cCBsaW5rIGluZm8gaW4gcGFja2FnZSBtZXRhLCBsYXN0IHVwZGF0ZSwgZXRjLlxuICAgKiBAcHJvcGVydHkge2Z1bmN0aW9ufSBvcHRpb25zLmNhbGxiYWNrIENhbGxiYWNrIGZvciByZWNlaXZlIGRhdGFcbiAgICovXG4gIHB1YmxpYyBnZXRQYWNrYWdlKG9wdGlvbnMpOiB2b2lkIHtcbiAgICB0aGlzLmxvY2FsU3RvcmFnZS5nZXRQYWNrYWdlTWV0YWRhdGEob3B0aW9ucy5uYW1lLCAoZXJyLCBkYXRhKTogdm9pZCA9PiB7XG4gICAgICBpZiAoZXJyICYmICghZXJyLnN0YXR1cyB8fCBlcnIuc3RhdHVzID49IEhUVFBfU1RBVFVTLklOVEVSTkFMX0VSUk9SKSkge1xuICAgICAgICAvLyByZXBvcnQgaW50ZXJuYWwgZXJyb3JzIHJpZ2h0IGF3YXlcbiAgICAgICAgcmV0dXJuIG9wdGlvbnMuY2FsbGJhY2soZXJyKTtcbiAgICAgIH1cblxuICAgICAgdGhpcy5fc3luY1VwbGlua3NNZXRhZGF0YShcbiAgICAgICAgb3B0aW9ucy5uYW1lLFxuICAgICAgICBkYXRhLFxuICAgICAgICB7IHJlcTogb3B0aW9ucy5yZXEsIHVwbGlua3NMb29rOiBvcHRpb25zLnVwbGlua3NMb29rIH0sXG4gICAgICAgIGZ1bmN0aW9uIGdldFBhY2thZ2VTeW5VcExpbmtzQ2FsbGJhY2soZXJyLCByZXN1bHQ6IFBhY2thZ2UsIHVwbGlua0Vycm9ycyk6IHZvaWQge1xuICAgICAgICAgIGlmIChlcnIpIHtcbiAgICAgICAgICAgIHJldHVybiBvcHRpb25zLmNhbGxiYWNrKGVycik7XG4gICAgICAgICAgfVxuXG4gICAgICAgICAgbm9ybWFsaXplRGlzdFRhZ3MoY2xlYW5VcExpbmtzUmVmKG9wdGlvbnMua2VlcFVwTGlua0RhdGEsIHJlc3VsdCkpO1xuXG4gICAgICAgICAgLy8gbnBtIGNhbiB0aHJvdyBpZiB0aGlzIGZpZWxkIGRvZXNuJ3QgZXhpc3RcbiAgICAgICAgICByZXN1bHQuX2F0dGFjaG1lbnRzID0ge307XG4gICAgICAgICAgaWYgKG9wdGlvbnMuYWJicmV2aWF0ZWQgPT09IHRydWUpIHtcbiAgICAgICAgICAgIG9wdGlvbnMuY2FsbGJhY2sobnVsbCwgY29udmVydEFiYnJldmlhdGVkTWFuaWZlc3QocmVzdWx0KSwgdXBsaW5rRXJyb3JzKTtcbiAgICAgICAgICB9IGVsc2Uge1xuICAgICAgICAgICAgb3B0aW9ucy5jYWxsYmFjayhudWxsLCByZXN1bHQsIHVwbGlua0Vycm9ycyk7XG4gICAgICAgICAgfVxuICAgICAgICB9XG4gICAgICApO1xuICAgIH0pO1xuICB9XG5cbiAgLyoqXG4gICBSZXRyaWV2ZSByZW1vdGUgYW5kIGxvY2FsIHBhY2thZ2VzIG1vcmUgcmVjZW50IHRoYW4ge3N0YXJ0a2V5fVxuICAgRnVuY3Rpb24gc3RyZWFtcyBhbGwgcGFja2FnZXMgZnJvbSBhbGwgdXBsaW5rcyBmaXJzdCwgYW5kIHRoZW5cbiAgIGxvY2FsIHBhY2thZ2VzLlxuICAgTm90ZSB0aGF0IGxvY2FsIHBhY2thZ2VzIGNvdWxkIG92ZXJyaWRlIHJlZ2lzdHJ5IG9uZXMganVzdCBiZWNhdXNlXG4gICB0aGV5IGFwcGVhciBpbiBKU09OIGxhc3QuIFRoYXQncyBhIHRyYWRlLW9mZiB3ZSBtYWtlIHRvIGF2b2lkXG4gICBtZW1vcnkgaXNzdWVzLlxuICAgVXNlZCBzdG9yYWdlczogbG9jYWwgJiYgdXBsaW5rIChwcm94eV9hY2Nlc3MpXG4gICAqIEBwYXJhbSB7Kn0gc3RhcnRrZXlcbiAgICogQHBhcmFtIHsqfSBvcHRpb25zXG4gICAqIEByZXR1cm4ge1N0cmVhbX1cbiAgICovXG4gIHB1YmxpYyBzZWFyY2goc3RhcnRrZXk6IHN0cmluZywgb3B0aW9uczogYW55KSB7XG4gICAgY29uc3Qgc2VsZiA9IHRoaXM7XG4gICAgY29uc3Qgc2VhcmNoU3RyZWFtOiBhbnkgPSBuZXcgU3RyZWFtLlBhc3NUaHJvdWdoKHsgb2JqZWN0TW9kZTogdHJ1ZSB9KTtcbiAgICBhc3luYy5lYWNoU2VyaWVzKFxuICAgICAgT2JqZWN0LmtleXModGhpcy51cGxpbmtzKSxcbiAgICAgIGZ1bmN0aW9uICh1cF9uYW1lLCBjYik6IHZvaWQge1xuICAgICAgICAvLyBzaG9ydGN1dDogaWYgYGxvY2FsPTFgIGlzIHN1cHBsaWVkLCBkb24ndCBjYWxsIHVwbGlua3NcbiAgICAgICAgaWYgKG9wdGlvbnMucmVxPy5xdWVyeT8ubG9jYWwgIT09IHVuZGVmaW5lZCkge1xuICAgICAgICAgIHJldHVybiBjYigpO1xuICAgICAgICB9XG4gICAgICAgIGxvZ2dlci5pbmZvKGBzZWFyY2ggZm9yIHVwbGluayAke3VwX25hbWV9YCk7XG4gICAgICAgIC8vIHNlYXJjaCBieSBrZXl3b3JkIGZvciBlYWNoIHVwbGlua1xuICAgICAgICBjb25zdCB1cGxpbmtTdHJlYW0gPSBzZWxmLnVwbGlua3NbdXBfbmFtZV0uc2VhcmNoKG9wdGlvbnMpO1xuICAgICAgICAvLyBqb2luIHVwbGluayBzdHJlYW0gd2l0aCBzdHJlYW1zIFBhc3NUaHJvdWdoXG4gICAgICAgIHVwbGlua1N0cmVhbS5waXBlKHNlYXJjaFN0cmVhbSwgeyBlbmQ6IGZhbHNlIH0pO1xuICAgICAgICB1cGxpbmtTdHJlYW0ub24oJ2Vycm9yJywgZnVuY3Rpb24gKGVycik6IHZvaWQge1xuICAgICAgICAgIHNlbGYubG9nZ2VyLmVycm9yKHsgZXJyOiBlcnIgfSwgJ3VwbGluayBlcnJvcjogQHtlcnIubWVzc2FnZX0nKTtcbiAgICAgICAgICBjYigpO1xuICAgICAgICAgIC8vIHRvIGF2b2lkIGNhbGwgY2FsbGJhY2sgbW9yZSB0aGFuIG9uY2VcbiAgICAgICAgICBjYiA9IGZ1bmN0aW9uICgpOiB2b2lkIHt9O1xuICAgICAgICB9KTtcbiAgICAgICAgdXBsaW5rU3RyZWFtLm9uKCdlbmQnLCBmdW5jdGlvbiAoKTogdm9pZCB7XG4gICAgICAgICAgY2IoKTtcbiAgICAgICAgICAvLyB0byBhdm9pZCBjYWxsIGNhbGxiYWNrIG1vcmUgdGhhbiBvbmNlXG4gICAgICAgICAgY2IgPSBmdW5jdGlvbiAoKTogdm9pZCB7fTtcbiAgICAgICAgfSk7XG5cbiAgICAgICAgc2VhcmNoU3RyZWFtLmFib3J0ID0gZnVuY3Rpb24gKCk6IHZvaWQge1xuICAgICAgICAgIGlmICh1cGxpbmtTdHJlYW0uYWJvcnQpIHtcbiAgICAgICAgICAgIHVwbGlua1N0cmVhbS5hYm9ydCgpO1xuICAgICAgICAgIH1cbiAgICAgICAgICBjYigpO1xuICAgICAgICAgIC8vIHRvIGF2b2lkIGNhbGwgY2FsbGJhY2sgbW9yZSB0aGFuIG9uY2VcbiAgICAgICAgICBjYiA9IGZ1bmN0aW9uICgpOiB2b2lkIHt9O1xuICAgICAgICB9O1xuICAgICAgfSxcbiAgICAgIC8vIGV4ZWN1dGVkIGFmdGVyIGFsbCBzZXJpZXNcbiAgICAgIGZ1bmN0aW9uICgpOiB2b2lkIHtcbiAgICAgICAgLy8gYXR0YWNoIGEgbG9jYWwgc2VhcmNoIHJlc3VsdHNcbiAgICAgICAgY29uc3QgbG9jYWxTZWFyY2hTdHJlYW0gPSBzZWxmLmxvY2FsU3RvcmFnZS5zZWFyY2goc3RhcnRrZXksIG9wdGlvbnMpO1xuICAgICAgICBzZWFyY2hTdHJlYW0uYWJvcnQgPSBmdW5jdGlvbiAoKTogdm9pZCB7XG4gICAgICAgICAgbG9jYWxTZWFyY2hTdHJlYW0uYWJvcnQoKTtcbiAgICAgICAgfTtcbiAgICAgICAgbG9jYWxTZWFyY2hTdHJlYW0ucGlwZShzZWFyY2hTdHJlYW0sIHsgZW5kOiB0cnVlIH0pO1xuICAgICAgICBsb2NhbFNlYXJjaFN0cmVhbS5vbignZXJyb3InLCBmdW5jdGlvbiAoZXJyOiBhbnkpOiB2b2lkIHtcbiAgICAgICAgICBzZWxmLmxvZ2dlci5lcnJvcih7IGVycjogZXJyIH0sICdzZWFyY2ggZXJyb3I6IEB7ZXJyLm1lc3NhZ2V9Jyk7XG4gICAgICAgICAgc2Vh