verdaccio
Version:
A lightweight private npm proxy registry
489 lines (464 loc) • 60 kB
JavaScript
"use strict";
Object.defineProperty(exports, "__esModule", {
value: true
});
exports.WHITELIST = void 0;
exports.checkPackageLocal = checkPackageLocal;
exports.checkPackageRemote = checkPackageRemote;
exports.cleanUpLinksRef = cleanUpLinksRef;
exports.cleanUpReadme = cleanUpReadme;
exports.convertAbbreviatedManifest = convertAbbreviatedManifest;
exports.distFileFromVersion = distFileFromVersion;
exports.generatePackageTemplate = generatePackageTemplate;
exports.generateRevision = generateRevision;
exports.getLatestReadme = getLatestReadme;
exports.hasInstallScript = hasInstallScript;
exports.isPublishablePackage = isPublishablePackage;
exports.lookupDistFile = lookupDistFile;
exports.mergeUplinkTimeIntoLocal = mergeUplinkTimeIntoLocal;
exports.normalizePackage = normalizePackage;
exports.prepareSearchPackage = prepareSearchPackage;
exports.publishPackage = publishPackage;
exports.tarballMatchesFilename = tarballMatchesFilename;
var _debug = _interopRequireDefault(require("debug"));
var _lodash = _interopRequireDefault(require("lodash"));
var _core = require("@verdaccio/core");
var _searchIndexer = require("@verdaccio/search-indexer");
var _utils = require("@verdaccio/utils");
var _constants = require("./constants");
var _logger = require("./logger");
var _utils2 = require("./utils");
function _interopRequireDefault(e) { return e && e.__esModule ? e : { default: e }; }
const debug = (0, _debug.default)('verdaccio:storage-utils');
/**
* Create an empty package manifest with all the internal bookkeeping
* sections (`_uplinks`, `_distfiles`, `_attachments`, `_rev`) initialized.
* Used as the starting point when a package is seen for the first time.
* @param name package name
* @return an empty manifest for the given package
*/
function generatePackageTemplate(name) {
return {
// standard things
name,
versions: {},
time: {},
[_constants.USERS]: {},
[_constants.DIST_TAGS]: {},
_uplinks: {},
_distfiles: {},
_attachments: {},
_rev: ''
};
}
/**
* Normalize a manifest read from storage: ensure the object sections
* (`versions`, `dist-tags`, `_distfiles`, `_attachments`, `_uplinks`, `time`)
* exist, default the revision and `_id`, and normalize the dist-tags shape.
* The manifest is mutated in place.
* @param pkg package manifest reference
* @return the same manifest, normalized
*/
function normalizePackage(pkg) {
const pkgProperties = ['versions', 'dist-tags', '_distfiles', '_attachments', '_uplinks', 'time'];
pkgProperties.forEach(key => {
const pkgProp = pkg[key];
if (_lodash.default.isNil(pkgProp) || (0, _utils2.isObject)(pkgProp) === false) {
pkg[key] = {};
}
});
if (_lodash.default.isString(pkg._rev) === false) {
debug('package %o has no revision, setting default', pkg.name);
pkg._rev = _constants.STORAGE.DEFAULT_REVISION;
}
if (_lodash.default.isString(pkg._id) === false) {
pkg._id = pkg.name;
}
// normalize dist-tags
(0, _utils2.normalizeDistTags)(pkg);
return pkg;
}
/**
* Produce the next revision id in the `<counter>-<hash>` format used by the
* storage layer, incrementing the counter of the previous revision.
* @param rev previous revision id, eg. `2-a1b2c3`
* @return the next revision id
*/
function generateRevision(rev) {
const _rev = rev.split('-');
return (+_rev[0] || 0) + 1 + '-' + (0, _utils.generateRandomHexString)();
}
/**
* Pick the most relevant readme for a package: the manifest readme or the
* `latest` version readme first, otherwise the first readme found across the
* `next`, `beta`, `alpha`, `test`, `dev` and `canary` dist-tags, in that order.
* @param pkg package manifest
* @return the readme content, or an empty string when none is available
*/
function getLatestReadme(pkg) {
const versions = pkg['versions'] || {};
const distTags = pkg[_constants.DIST_TAGS] || {};
// FIXME: here is a bit tricky add the types
const latestVersion = distTags['latest'] ? versions[distTags['latest']] || {} : {};
let readme = _lodash.default.trim(pkg.readme || latestVersion.readme || '');
if (readme) {
return readme;
}
// In case of empty readme - trying to get ANY readme in the following order: 'next','beta','alpha','test','dev','canary'
const readmeDistTagsPriority = ['next', 'beta', 'alpha', 'test', 'dev', 'canary'];
readmeDistTagsPriority.map(function (tag) {
if (readme) {
return readme;
}
const version = distTags[tag] ? versions[distTags[tag]] || {} : {};
readme = _lodash.default.trim(version.readme || readme);
});
return readme;
}
/**
* Remove the readme from a version object: only one readme is kept per
* package (see getLatestReadme), never one per version. The version is
* mutated in place.
* @param version version metadata
* @return the same version without its readme
*/
function cleanUpReadme(version) {
if (_lodash.default.isNil(version) === false) {
// @ts-ignore
delete version.readme;
}
return version;
}
/**
* Manifest properties allowed in responses served to clients; everything
* else is verdaccio-internal bookkeeping and is stripped by cleanUpLinksRef.
*/
const WHITELIST = exports.WHITELIST = ['_rev', 'name', 'versions', 'dist-tags', 'readme', 'time', '_id', 'users'];
/**
* Strip verdaccio-internal sections (`_distfiles`, `_attachments`, ...) from
* a manifest before serving it to a client. The manifest is mutated in place.
* @param keepUpLinkData whether the `_uplinks` section should be preserved
* @param result manifest to clean up
* @return the same manifest with only whitelisted properties
*/
function cleanUpLinksRef(keepUpLinkData, result) {
const propertyToKeep = [...WHITELIST];
if (keepUpLinkData === true) {
propertyToKeep.push('_uplinks');
}
for (const i in result) {
if (propertyToKeep.indexOf(i) === -1) {
// Remove sections like '_uplinks' from response
delete result[i];
}
}
return result;
}
/**
* Verify a package does not exist locally yet; part of the publish flow.
* @param name package name
* @param localStorage local storage instance
* @throws conflict error when the package already exists locally
*/
async function checkPackageLocal(name, localStorage) {
debug('checking if %o already exists locally', name);
try {
const results = await localStorage.getPackageMetadataAsync(name);
if (results) {
debug('package %o already exists locally', name);
throw _utils2.ErrorCode.getConflict(_constants.API_ERROR.PACKAGE_EXIST);
}
} catch (err) {
if (err.status === _constants.HTTP_STATUS.NOT_FOUND) {
debug('package %o does not exist locally', name);
return;
}
throw err;
}
}
/**
* Create a new package in the local storage and register its latest version
* in the in-memory search indexer.
* @param name package name
* @param metadata package manifest to store
* @param localStorage local storage instance
*/
function publishPackage(name, metadata, localStorage) {
debug('publishing a new package for %o', name);
return new Promise((resolve, reject) => {
localStorage.addPackage(name, metadata, (err, latest) => {
if (!_lodash.default.isNull(err)) {
return reject(err);
} else if (!_lodash.default.isUndefined(latest)) {
_searchIndexer.SearchMemoryIndexer.add(latest).catch(reason => {
debug('indexer has failed on add item %o', reason);
_logger.logger.error('indexer has failed on add item');
});
}
return resolve();
});
});
}
/**
* Verify a package does not exist on any uplink yet; part of the publish
* flow. Uplink failures other than 404 reject the publish, unless
* `publish.allow_offline` is enabled in the configuration.
* @param name package name
* @param isAllowPublishOffline whether publishing with unreachable uplinks is allowed
* @param syncMetadata bound Storage._syncUplinksMetadata function
* @throws conflict error when the package exists upstream, service unavailable when uplinks are offline
*/
function checkPackageRemote(name, isAllowPublishOffline, syncMetadata) {
debug('checking if %o already exists on uplinks', name);
return new Promise((resolve, reject) => {
syncMetadata(name, null, {}, (err, packageJsonLocal, upLinksErrors) => {
// something weird
if (err && err.status !== _constants.HTTP_STATUS.NOT_FOUND) {
return reject(err);
}
// checking package exist already
if (_lodash.default.isNil(packageJsonLocal) === false) {
debug('package %o already exists on an uplink', name);
return reject(_utils2.ErrorCode.getConflict(_constants.API_ERROR.PACKAGE_EXIST));
}
for (let errorItem = 0; errorItem < upLinksErrors.length; errorItem++) {
// checking error
// if uplink fails with a status other than 404, we report failure
if (_lodash.default.isNil(upLinksErrors[errorItem][0]) === false) {
if (upLinksErrors[errorItem][0].status !== _constants.HTTP_STATUS.NOT_FOUND) {
if (isAllowPublishOffline) {
debug('uplink offline for %o, publish offline is allowed', name);
return resolve();
}
debug('uplink offline for %o, rejecting publish', name);
return reject(_utils2.ErrorCode.getServiceUnavailable(_constants.API_ERROR.UPLINK_OFFLINE_PUBLISH));
}
}
}
return resolve();
});
});
}
/**
* Merge the `time` field of a remote manifest into the cached one; remote
* entries win on conflicts. Returns a new manifest, the inputs are untouched.
* @param cacheManifest local cached manifest
* @param remoteManifest manifest fetched from an uplink
* @return the cached manifest with the merged time field
*/
function mergeUplinkTimeIntoLocal(cacheManifest, remoteManifest) {
if ('time' in remoteManifest) {
debug('merging remote time field into local manifest for %o', cacheManifest.name);
// remote override cache time conflicts
return {
...cacheManifest,
time: {
...cacheManifest.time,
...remoteManifest.time
}
};
}
return cacheManifest;
}
/**
* Build the search item emitted for a local package during a search stream,
* based on its latest version: npm-search compatible maintainers
* (`{ username, email }`), publisher (from `_npmUser`, falling back to the
* first maintainer), license and links.
* @param data package manifest
* @param time modified time attached to the search item
* @return the search item, or undefined when the package has no usable latest version
*/
function prepareSearchPackage(data, time) {
const latest = _core.pkgUtils.getLatest(data);
if (!latest || !data.versions[latest]) {
debug('no latest version found for %o, skipping search item', data.name);
return;
}
debug('preparing search item for %o@%o', data.name, latest);
if (latest && data.versions[latest]) {
const version = data.versions[latest];
const versions = {
[latest]: 'latest'
};
// packument maintainers carry `name`, but the npm search API contract uses
// `username` and the npm CLI crashes on entries without it — emit both
const rawMaintainers = version.maintainers || [version.author].filter(Boolean);
const maintainers = Array.isArray(rawMaintainers) ? rawMaintainers.map(maintainer => typeof maintainer === 'string' ? {
username: maintainer,
email: ''
} : {
...maintainer,
username: maintainer?.username ?? maintainer?.name ?? '',
email: maintainer?.email ?? ''
}) : [];
// npmjs fills `publisher` with the user who published the version; use
// `_npmUser` when the publishing client provided it and fall back to the
// first maintainer, otherwise the npm CLI renders "published by ???"
const npmUser = version._npmUser;
const publisher = npmUser?.name ? {
username: npmUser.name,
email: npmUser.email ?? ''
} : maintainers[0] ?? {};
const pkg = {
name: version.name,
description: version.description,
[_constants.DIST_TAGS]: {
latest
},
maintainers,
publisher,
author: version.author,
repository: version.repository,
readmeFilename: version.readmeFilename || '',
homepage: version.homepage,
keywords: version.keywords,
time: {
modified: time
},
bugs: version.bugs,
license: version.license,
versions
};
return pkg;
}
}
/**
* Whether the tarball url path ends with the given file name
* (any query string or fragment is ignored, like the path parsing
* in updateVersions).
*/
function tarballMatchesFilename(tarball, filename) {
return tarball.split(/[?#]/)[0].endsWith(`/${filename}`);
}
/**
* Build a distfile record from a version's dist metadata when its tarball
* url matches the given file name.
*/
function distFileFromVersion(version, filename) {
const dist = version?.dist;
if (dist?.tarball && tarballMatchesFilename(dist.tarball, filename)) {
return {
url: dist.tarball,
sha: dist.shasum
};
}
return null;
}
/**
* Resolve the distfile record for a tarball filename. Prefers the
* `_distfiles` bookkeeping, but falls back to the version metadata when the
* record is missing (storages written by other verdaccio versions cache
* versions without their distfile records, which made those tarballs
* permanently unavailable).
* @param manifest cached manifest
* @param filename tarball file name, eg. pkg-1.0.0.tgz
*/
function lookupDistFile(manifest, filename) {
const distFile = manifest?._distfiles?.[filename];
if (_lodash.default.isNil(distFile) === false) {
return distFile;
}
const versions = manifest?.versions ?? {};
// tarballs are conventionally named <basename>-<version>.tgz, so the
// version can usually be derived from the filename without scanning
// manifests that carry thousands of versions
const basename = manifest?.name?.replace(/^.*\//, '');
if (basename && filename.startsWith(`${basename}-`) && filename.endsWith('.tgz')) {
const versionId = filename.slice(basename.length + 1, -'.tgz'.length);
const distFromVersion = distFileFromVersion(versions[versionId], filename);
if (distFromVersion) {
debug('distfile record missing for %o, using version %o dist', filename, versionId);
return distFromVersion;
}
}
// unconventional tarball names: fall back to a scan
for (const versionId in versions) {
const distFromVersion = distFileFromVersion(versions[versionId], filename);
if (distFromVersion) {
debug('distfile record missing for %o, using version %o dist', filename, versionId);
return distFromVersion;
}
}
debug('no distfile record or version dist found for %o', filename);
return null;
}
/**
* Check whether the package metadata has enough data to be published.
* @param pkg package manifest
* @return true when the manifest carries a versions section
*/
function isPublishablePackage(pkg) {
const keys = Object.keys(pkg);
return keys.includes('versions');
}
/**
* Whether a version declares an install lifecycle script (`install`,
* `preinstall` or `postinstall`); surfaced in abbreviated manifests as
* `hasInstallScript` so clients can warn about script execution.
* @param version version metadata
* @return true when an install script is present
*/
function hasInstallScript(version) {
if (version?.scripts) {
const scripts = Object.keys(version.scripts);
return scripts.find(item => {
return ['install', 'preinstall', 'postinstall'].includes(item);
}) !== undefined;
}
return false;
}
/**
* Convert a full manifest into the abbreviated format served when the client
* requests `application/vnd.npm.install-v1+json` (npm install), keeping only
* the fields the npm registry contract defines for installation.
* https://github.com/npm/registry/blob/master/docs/responses/package-metadata.md
* @param manifest full package manifest
* @return the abbreviated manifest
*/
function convertAbbreviatedManifest(manifest) {
if (debug.enabled) {
debug('converting %o to abbreviated manifest with %o versions', manifest.name, Object.keys(manifest.versions).length);
}
const abbreviatedVersions = Object.keys(manifest.versions).reduce((acc, version) => {
const _version = manifest.versions[version];
// This should be align with this document
// https://github.com/npm/registry/blob/master/docs/responses/package-metadata.md#abbreviated-version-object
const _version_abbreviated = {
name: _version.name,
version: _version.version,
description: _version.description,
deprecated: _version.deprecated,
bin: _version.bin,
dist: _version.dist,
engines: _version.engines,
cpu: _version.cpu,
os: _version.os,
funding: _version.funding,
directories: _version.directories,
dependencies: _version.dependencies,
devDependencies: _version.devDependencies,
peerDependencies: _version.peerDependencies,
peerDependenciesMeta: _version.peerDependenciesMeta,
optionalDependencies: _version.optionalDependencies,
bundleDependencies: _version.bundleDependencies,
// npm cli specifics
_hasShrinkwrap: _version._hasShrinkwrap,
hasInstallScript: hasInstallScript(_version)
};
acc[version] = _version_abbreviated;
return acc;
}, {});
const convertedManifest = {
name: manifest['name'],
[_constants.DIST_TAGS]: manifest[_constants.DIST_TAGS],
versions: abbreviatedVersions,
// @ts-ignore
modified: manifest?.time?.modified,
// NOTE: special case for pnpm https://github.com/pnpm/rfcs/pull/2
time: manifest?.time
};
// @ts-ignore
return convertedManifest;
}
//# sourceMappingURL=data:application/json;charset=utf-8;base64,eyJ2ZXJzaW9uIjozLCJuYW1lcyI6WyJfZGVidWciLCJfaW50ZXJvcFJlcXVpcmVEZWZhdWx0IiwicmVxdWlyZSIsIl9sb2Rhc2giLCJfY29yZSIsIl9zZWFyY2hJbmRleGVyIiwiX3V0aWxzIiwiX2NvbnN0YW50cyIsIl9sb2dnZXIiLCJfdXRpbHMyIiwiZSIsIl9fZXNNb2R1bGUiLCJkZWZhdWx0IiwiZGVidWciLCJjcmVhdGVEZWJ1ZyIsImdlbmVyYXRlUGFja2FnZVRlbXBsYXRlIiwibmFtZSIsInZlcnNpb25zIiwidGltZSIsIlVTRVJTIiwiRElTVF9UQUdTIiwiX3VwbGlua3MiLCJfZGlzdGZpbGVzIiwiX2F0dGFjaG1lbnRzIiwiX3JldiIsIm5vcm1hbGl6ZVBhY2thZ2UiLCJwa2ciLCJwa2dQcm9wZXJ0aWVzIiwiZm9yRWFjaCIsImtleSIsInBrZ1Byb3AiLCJfIiwiaXNOaWwiLCJpc09iamVjdCIsImlzU3RyaW5nIiwiU1RPUkFHRSIsIkRFRkFVTFRfUkVWSVNJT04iLCJfaWQiLCJub3JtYWxpemVEaXN0VGFncyIsImdlbmVyYXRlUmV2aXNpb24iLCJyZXYiLCJzcGxpdCIsImdlbmVyYXRlUmFuZG9tSGV4U3RyaW5nIiwiZ2V0TGF0ZXN0UmVhZG1lIiwiZGlzdFRhZ3MiLCJsYXRlc3RWZXJzaW9uIiwicmVhZG1lIiwidHJpbSIsInJlYWRtZURpc3RUYWdzUHJpb3JpdHkiLCJtYXAiLCJ0YWciLCJ2ZXJzaW9uIiwiY2xlYW5VcFJlYWRtZSIsIldISVRFTElTVCIsImV4cG9ydHMiLCJjbGVhblVwTGlua3NSZWYiLCJrZWVwVXBMaW5rRGF0YSIsInJlc3VsdCIsInByb3BlcnR5VG9LZWVwIiwicHVzaCIsImkiLCJpbmRleE9mIiwiY2hlY2tQYWNrYWdlTG9jYWwiLCJsb2NhbFN0b3JhZ2UiLCJyZXN1bHRzIiwiZ2V0UGFja2FnZU1ldGFkYXRhQXN5bmMiLCJFcnJvckNvZGUiLCJnZXRDb25mbGljdCIsIkFQSV9FUlJPUiIsIlBBQ0tBR0VfRVhJU1QiLCJlcnIiLCJzdGF0dXMiLCJIVFRQX1NUQVRVUyIsIk5PVF9GT1VORCIsInB1Ymxpc2hQYWNrYWdlIiwibWV0YWRhdGEiLCJQcm9taXNlIiwicmVzb2x2ZSIsInJlamVjdCIsImFkZFBhY2thZ2UiLCJsYXRlc3QiLCJpc051bGwiLCJpc1VuZGVmaW5lZCIsIlNlYXJjaE1lbW9yeUluZGV4ZXIiLCJhZGQiLCJjYXRjaCIsInJlYXNvbiIsImxvZ2dlciIsImVycm9yIiwiY2hlY2tQYWNrYWdlUmVtb3RlIiwiaXNBbGxvd1B1Ymxpc2hPZmZsaW5lIiwic3luY01ldGFkYXRhIiwicGFja2FnZUpzb25Mb2NhbCIsInVwTGlua3NFcnJvcnMiLCJlcnJvckl0ZW0iLCJsZW5ndGgiLCJnZXRTZXJ2aWNlVW5hdmFpbGFibGUiLCJVUExJTktfT0ZGTElORV9QVUJMSVNIIiwibWVyZ2VVcGxpbmtUaW1lSW50b0xvY2FsIiwiY2FjaGVNYW5pZmVzdCIsInJlbW90ZU1hbmlmZXN0IiwicHJlcGFyZVNlYXJjaFBhY2thZ2UiLCJkYXRhIiwicGtnVXRpbHMiLCJnZXRMYXRlc3QiLCJyYXdNYWludGFpbmVycyIsIm1haW50YWluZXJzIiwiYXV0aG9yIiwiZmlsdGVyIiwiQm9vbGVhbiIsIkFycmF5IiwiaXNBcnJheSIsIm1haW50YWluZXIiLCJ1c2VybmFtZSIsImVtYWlsIiwibnBtVXNlciIsIl9ucG1Vc2VyIiwicHVibGlzaGVyIiwiZGVzY3JpcHRpb24iLCJyZXBvc2l0b3J5IiwicmVhZG1lRmlsZW5hbWUiLCJob21lcGFnZSIsImtleXdvcmRzIiwibW9kaWZpZWQiLCJidWdzIiwibGljZW5zZSIsInRhcmJhbGxNYXRjaGVzRmlsZW5hbWUiLCJ0YXJiYWxsIiwiZmlsZW5hbWUiLCJlbmRzV2l0aCIsImRpc3RGaWxlRnJvbVZlcnNpb24iLCJkaXN0IiwidXJsIiwic2hhIiwic2hhc3VtIiwibG9va3VwRGlzdEZpbGUiLCJtYW5pZmVzdCIsImRpc3RGaWxlIiwiYmFzZW5hbWUiLCJyZXBsYWNlIiwic3RhcnRzV2l0aCIsInZlcnNpb25JZCIsInNsaWNlIiwiZGlzdEZyb21WZXJzaW9uIiwiaXNQdWJsaXNoYWJsZVBhY2thZ2UiLCJrZXlzIiwiT2JqZWN0IiwiaW5jbHVkZXMiLCJoYXNJbnN0YWxsU2NyaXB0Iiwic2NyaXB0cyIsImZpbmQiLCJpdGVtIiwidW5kZWZpbmVkIiwiY29udmVydEFiYnJldmlhdGVkTWFuaWZlc3QiLCJlbmFibGVkIiwiYWJicmV2aWF0ZWRWZXJzaW9ucyIsInJlZHVjZSIsImFjYyIsIl92ZXJzaW9uIiwiX3ZlcnNpb25fYWJicmV2aWF0ZWQiLCJkZXByZWNhdGVkIiwiYmluIiwiZW5naW5lcyIsImNwdSIsIm9zIiwiZnVuZGluZyIsImRpcmVjdG9yaWVzIiwiZGVwZW5kZW5jaWVzIiwiZGV2RGVwZW5kZW5jaWVzIiwicGVlckRlcGVuZGVuY2llcyIsInBlZXJEZXBlbmRlbmNpZXNNZXRhIiwib3B0aW9uYWxEZXBlbmRlbmNpZXMiLCJidW5kbGVEZXBlbmRlbmNpZXMiLCJfaGFzU2hyaW5rd3JhcCIsImNvbnZlcnRlZE1hbmlmZXN0Il0sInNvdXJjZXMiOlsiLi4vLi4vc3JjL2xpYi9zdG9yYWdlLXV0aWxzLnRzIl0sInNvdXJjZXNDb250ZW50IjpbImltcG9ydCBjcmVhdGVEZWJ1ZyBmcm9tICdkZWJ1Zyc7XG5pbXBvcnQgXyBmcm9tICdsb2Rhc2gnO1xuXG5pbXBvcnQgeyBwa2dVdGlscyB9IGZyb20gJ0B2ZXJkYWNjaW8vY29yZSc7XG5pbXBvcnQgeyBTZWFyY2hNZW1vcnlJbmRleGVyIH0gZnJvbSAnQHZlcmRhY2Npby9zZWFyY2gtaW5kZXhlcic7XG5pbXBvcnQge1xuICBBYmJyZXZpYXRlZE1hbmlmZXN0LFxuICBBYmJyZXZpYXRlZFZlcnNpb25zLFxuICBEaXN0RmlsZSxcbiAgTWFuaWZlc3QsXG4gIFZlcnNpb24sXG59IGZyb20gJ0B2ZXJkYWNjaW8vdHlwZXMnO1xuaW1wb3J0IHsgZ2VuZXJhdGVSYW5kb21IZXhTdHJpbmcgfSBmcm9tICdAdmVyZGFjY2lvL3V0aWxzJztcblxuaW1wb3J0IHsgQVBJX0VSUk9SLCBESVNUX1RBR1MsIEhUVFBfU1RBVFVTLCBTVE9SQUdFLCBVU0VSUyB9IGZyb20gJy4vY29uc3RhbnRzJztcbmltcG9ydCBMb2NhbFN0b3JhZ2UgZnJvbSAnLi9sb2NhbC1zdG9yYWdlJztcbmltcG9ydCB7IGxvZ2dlciB9IGZyb20gJy4vbG9nZ2VyJztcbmltcG9ydCB7IEVycm9yQ29kZSwgaXNPYmplY3QsIG5vcm1hbGl6ZURpc3RUYWdzIH0gZnJvbSAnLi91dGlscyc7XG5cbmNvbnN0IGRlYnVnID0gY3JlYXRlRGVidWcoJ3ZlcmRhY2NpbzpzdG9yYWdlLXV0aWxzJyk7XG5cbi8qKlxuICogQ3JlYXRlIGFuIGVtcHR5IHBhY2thZ2UgbWFuaWZlc3Qgd2l0aCBhbGwgdGhlIGludGVybmFsIGJvb2trZWVwaW5nXG4gKiBzZWN0aW9ucyAoYF91cGxpbmtzYCwgYF9kaXN0ZmlsZXNgLCBgX2F0dGFjaG1lbnRzYCwgYF9yZXZgKSBpbml0aWFsaXplZC5cbiAqIFVzZWQgYXMgdGhlIHN0YXJ0aW5nIHBvaW50IHdoZW4gYSBwYWNrYWdlIGlzIHNlZW4gZm9yIHRoZSBmaXJzdCB0aW1lLlxuICogQHBhcmFtIG5hbWUgcGFja2FnZSBuYW1lXG4gKiBAcmV0dXJuIGFuIGVtcHR5IG1hbmlmZXN0IGZvciB0aGUgZ2l2ZW4gcGFja2FnZVxuICovXG5leHBvcnQgZnVuY3Rpb24gZ2VuZXJhdGVQYWNrYWdlVGVtcGxhdGUobmFtZTogc3RyaW5nKTogTWFuaWZlc3Qge1xuICByZXR1cm4ge1xuICAgIC8vIHN0YW5kYXJkIHRoaW5nc1xuICAgIG5hbWUsXG4gICAgdmVyc2lvbnM6IHt9LFxuICAgIHRpbWU6IHt9LFxuICAgIFtVU0VSU106IHt9LFxuICAgIFtESVNUX1RBR1NdOiB7fSxcbiAgICBfdXBsaW5rczoge30sXG4gICAgX2Rpc3RmaWxlczoge30sXG4gICAgX2F0dGFjaG1lbnRzOiB7fSxcbiAgICBfcmV2OiAnJyxcbiAgfTtcbn1cblxuLyoqXG4gKiBOb3JtYWxpemUgYSBtYW5pZmVzdCByZWFkIGZyb20gc3RvcmFnZTogZW5zdXJlIHRoZSBvYmplY3Qgc2VjdGlvbnNcbiAqIChgdmVyc2lvbnNgLCBgZGlzdC10YWdzYCwgYF9kaXN0ZmlsZXNgLCBgX2F0dGFjaG1lbnRzYCwgYF91cGxpbmtzYCwgYHRpbWVgKVxuICogZXhpc3QsIGRlZmF1bHQgdGhlIHJldmlzaW9uIGFuZCBgX2lkYCwgYW5kIG5vcm1hbGl6ZSB0aGUgZGlzdC10YWdzIHNoYXBlLlxuICogVGhlIG1hbmlmZXN0IGlzIG11dGF0ZWQgaW4gcGxhY2UuXG4gKiBAcGFyYW0gcGtnIHBhY2thZ2UgbWFuaWZlc3QgcmVmZXJlbmNlXG4gKiBAcmV0dXJuIHRoZSBzYW1lIG1hbmlmZXN0LCBub3JtYWxpemVkXG4gKi9cbmV4cG9ydCBmdW5jdGlvbiBub3JtYWxpemVQYWNrYWdlKHBrZzogTWFuaWZlc3QpOiBNYW5pZmVzdCB7XG4gIGNvbnN0IHBrZ1Byb3BlcnRpZXMgPSBbJ3ZlcnNpb25zJywgJ2Rpc3QtdGFncycsICdfZGlzdGZpbGVzJywgJ19hdHRhY2htZW50cycsICdfdXBsaW5rcycsICd0aW1lJ107XG5cbiAgcGtnUHJvcGVydGllcy5mb3JFYWNoKChrZXkpOiB2b2lkID0+IHtcbiAgICBjb25zdCBwa2dQcm9wID0gcGtnW2tleV07XG5cbiAgICBpZiAoXy5pc05pbChwa2dQcm9wKSB8fCBpc09iamVjdChwa2dQcm9wKSA9PT0gZmFsc2UpIHtcbiAgICAgIHBrZ1trZXldID0ge307XG4gICAgfVxuICB9KTtcblxuICBpZiAoXy5pc1N0cmluZyhwa2cuX3JldikgPT09IGZhbHNlKSB7XG4gICAgZGVidWcoJ3BhY2thZ2UgJW8gaGFzIG5vIHJldmlzaW9uLCBzZXR0aW5nIGRlZmF1bHQnLCBwa2cubmFtZSk7XG4gICAgcGtnLl9yZXYgPSBTVE9SQUdFLkRFRkFVTFRfUkVWSVNJT047XG4gIH1cblxuICBpZiAoXy5pc1N0cmluZyhwa2cuX2lkKSA9PT0gZmFsc2UpIHtcbiAgICBwa2cuX2lkID0gcGtnLm5hbWU7XG4gIH1cblxuICAvLyBub3JtYWxpemUgZGlzdC10YWdzXG4gIG5vcm1hbGl6ZURpc3RUYWdzKHBrZyk7XG5cbiAgcmV0dXJuIHBrZztcbn1cblxuLyoqXG4gKiBQcm9kdWNlIHRoZSBuZXh0IHJldmlzaW9uIGlkIGluIHRoZSBgPGNvdW50ZXI+LTxoYXNoPmAgZm9ybWF0IHVzZWQgYnkgdGhlXG4gKiBzdG9yYWdlIGxheWVyLCBpbmNyZW1lbnRpbmcgdGhlIGNvdW50ZXIgb2YgdGhlIHByZXZpb3VzIHJldmlzaW9uLlxuICogQHBhcmFtIHJldiBwcmV2aW91cyByZXZpc2lvbiBpZCwgZWcuIGAyLWExYjJjM2BcbiAqIEByZXR1cm4gdGhlIG5leHQgcmV2aXNpb24gaWRcbiAqL1xuZXhwb3J0IGZ1bmN0aW9uIGdlbmVyYXRlUmV2aXNpb24ocmV2OiBzdHJpbmcpOiBzdHJpbmcge1xuICBjb25zdCBfcmV2ID0gcmV2LnNwbGl0KCctJyk7XG5cbiAgcmV0dXJuICgrX3JldlswXSB8fCAwKSArIDEgKyAnLScgKyBnZW5lcmF0ZVJhbmRvbUhleFN0cmluZygpO1xufVxuXG4vKipcbiAqIFBpY2sgdGhlIG1vc3QgcmVsZXZhbnQgcmVhZG1lIGZvciBhIHBhY2thZ2U6IHRoZSBtYW5pZmVzdCByZWFkbWUgb3IgdGhlXG4gKiBgbGF0ZXN0YCB2ZXJzaW9uIHJlYWRtZSBmaXJzdCwgb3RoZXJ3aXNlIHRoZSBmaXJzdCByZWFkbWUgZm91bmQgYWNyb3NzIHRoZVxuICogYG5leHRgLCBgYmV0YWAsIGBhbHBoYWAsIGB0ZXN0YCwgYGRldmAgYW5kIGBjYW5hcnlgIGRpc3QtdGFncywgaW4gdGhhdCBvcmRlci5cbiAqIEBwYXJhbSBwa2cgcGFja2FnZSBtYW5pZmVzdFxuICogQHJldHVybiB0aGUgcmVhZG1lIGNvbnRlbnQsIG9yIGFuIGVtcHR5IHN0cmluZyB3aGVuIG5vbmUgaXMgYXZhaWxhYmxlXG4gKi9cbmV4cG9ydCBmdW5jdGlvbiBnZXRMYXRlc3RSZWFkbWUocGtnOiBNYW5pZmVzdCk6IHN0cmluZyB7XG4gIGNvbnN0IHZlcnNpb25zID0gcGtnWyd2ZXJzaW9ucyddIHx8IHt9O1xuICBjb25zdCBkaXN0VGFncyA9IHBrZ1tESVNUX1RBR1NdIHx8IHt9O1xuICAvLyBGSVhNRTogaGVyZSBpcyBhIGJpdCB0cmlja3kgYWRkIHRoZSB0eXBlc1xuICBjb25zdCBsYXRlc3RWZXJzaW9uOiBWZXJzaW9uIHwgYW55ID0gZGlzdFRhZ3NbJ2xhdGVzdCddID8gdmVyc2lvbnNbZGlzdFRhZ3NbJ2xhdGVzdCddXSB8fCB7fSA6IHt9O1xuICBsZXQgcmVhZG1lID0gXy50cmltKHBrZy5yZWFkbWUgfHwgbGF0ZXN0VmVyc2lvbi5yZWFkbWUgfHwgJycpO1xuICBpZiAocmVhZG1lKSB7XG4gICAgcmV0dXJuIHJlYWRtZTtcbiAgfVxuXG4gIC8vIEluIGNhc2Ugb2YgZW1wdHkgcmVhZG1lIC0gdHJ5aW5nIHRvIGdldCBBTlkgcmVhZG1lIGluIHRoZSBmb2xsb3dpbmcgb3JkZXI6ICduZXh0JywnYmV0YScsJ2FscGhhJywndGVzdCcsJ2RldicsJ2NhbmFyeSdcbiAgY29uc3QgcmVhZG1lRGlzdFRhZ3NQcmlvcml0eSA9IFsnbmV4dCcsICdiZXRhJywgJ2FscGhhJywgJ3Rlc3QnLCAnZGV2JywgJ2NhbmFyeSddO1xuICByZWFkbWVEaXN0VGFnc1ByaW9yaXR5Lm1hcChmdW5jdGlvbiAodGFnKTogc3RyaW5nIHwgdm9pZCB7XG4gICAgaWYgKHJlYWRtZSkge1xuICAgICAgcmV0dXJuIHJlYWRtZTtcbiAgICB9XG4gICAgY29uc3QgdmVyc2lvbjogVmVyc2lvbiB8IGFueSA9IGRpc3RUYWdzW3RhZ10gPyB2ZXJzaW9uc1tkaXN0VGFnc1t0YWddXSB8fCB7fSA6IHt9O1xuICAgIHJlYWRtZSA9IF8udHJpbSh2ZXJzaW9uLnJlYWRtZSB8fCByZWFkbWUpO1xuICB9KTtcbiAgcmV0dXJuIHJlYWRtZTtcbn1cblxuLyoqXG4gKiBSZW1vdmUgdGhlIHJlYWRtZSBmcm9tIGEgdmVyc2lvbiBvYmplY3Q6IG9ubHkgb25lIHJlYWRtZSBpcyBrZXB0IHBlclxuICogcGFja2FnZSAoc2VlIGdldExhdGVzdFJlYWRtZSksIG5ldmVyIG9uZSBwZXIgdmVyc2lvbi4gVGhlIHZlcnNpb24gaXNcbiAqIG11dGF0ZWQgaW4gcGxhY2UuXG4gKiBAcGFyYW0gdmVyc2lvbiB2ZXJzaW9uIG1ldGFkYXRhXG4gKiBAcmV0dXJuIHRoZSBzYW1lIHZlcnNpb24gd2l0aG91dCBpdHMgcmVhZG1lXG4gKi9cbmV4cG9ydCBmdW5jdGlvbiBjbGVhblVwUmVhZG1lKHZlcnNpb246IFZlcnNpb24pOiBWZXJzaW9uIHtcbiAgaWYgKF8uaXNOaWwodmVyc2lvbikgPT09IGZhbHNlKSB7XG4gICAgLy8gQHRzLWlnbm9yZVxuICAgIGRlbGV0ZSB2ZXJzaW9uLnJlYWRtZTtcbiAgfVxuXG4gIHJldHVybiB2ZXJzaW9uO1xufVxuXG4vKipcbiAqIE1hbmlmZXN0IHByb3BlcnRpZXMgYWxsb3dlZCBpbiByZXNwb25zZXMgc2VydmVkIHRvIGNsaWVudHM7IGV2ZXJ5dGhpbmdcbiAqIGVsc2UgaXMgdmVyZGFjY2lvLWludGVybmFsIGJvb2trZWVwaW5nIGFuZCBpcyBzdHJpcHBlZCBieSBjbGVhblVwTGlua3NSZWYuXG4gKi9cbmV4cG9ydCBjb25zdCBXSElURUxJU1QgPSBbXG4gICdfcmV2JyxcbiAgJ25hbWUnLFxuICAndmVyc2lvbnMnLFxuICAnZGlzdC10YWdzJyxcbiAgJ3JlYWRtZScsXG4gICd0aW1lJyxcbiAgJ19pZCcsXG4gICd1c2VycycsXG5dO1xuXG4vKipcbiAqIFN0cmlwIHZlcmRhY2Npby1pbnRlcm5hbCBzZWN0aW9ucyAoYF9kaXN0ZmlsZXNgLCBgX2F0dGFjaG1lbnRzYCwgLi4uKSBmcm9tXG4gKiBhIG1hbmlmZXN0IGJlZm9yZSBzZXJ2aW5nIGl0IHRvIGEgY2xpZW50LiBUaGUgbWFuaWZlc3QgaXMgbXV0YXRlZCBpbiBwbGFjZS5cbiAqIEBwYXJhbSBrZWVwVXBMaW5rRGF0YSB3aGV0aGVyIHRoZSBgX3VwbGlua3NgIHNlY3Rpb24gc2hvdWxkIGJlIHByZXNlcnZlZFxuICogQHBhcmFtIHJlc3VsdCBtYW5pZmVzdCB0byBjbGVhbiB1cFxuICogQHJldHVybiB0aGUgc2FtZSBtYW5pZmVzdCB3aXRoIG9ubHkgd2hpdGVsaXN0ZWQgcHJvcGVydGllc1xuICovXG5leHBvcnQgZnVuY3Rpb24gY2xlYW5VcExpbmtzUmVmKGtlZXBVcExpbmtEYXRhOiBib29sZWFuLCByZXN1bHQ6IE1hbmlmZXN0KTogTWFuaWZlc3Qge1xuICBjb25zdCBwcm9wZXJ0eVRvS2VlcCA9IFsuLi5XSElURUxJU1RdO1xuICBpZiAoa2VlcFVwTGlua0RhdGEgPT09IHRydWUpIHtcbiAgICBwcm9wZXJ0eVRvS2VlcC5wdXNoKCdfdXBsaW5rcycpO1xuICB9XG5cbiAgZm9yIChjb25zdCBpIGluIHJlc3VsdCkge1xuICAgIGlmIChwcm9wZXJ0eVRvS2VlcC5pbmRleE9mKGkpID09PSAtMSkge1xuICAgICAgLy8gUmVtb3ZlIHNlY3Rpb25zIGxpa2UgJ191cGxpbmtzJyBmcm9tIHJlc3BvbnNlXG4gICAgICBkZWxldGUgcmVzdWx0W2ldO1xuICAgIH1cbiAgfVxuXG4gIHJldHVybiByZXN1bHQ7XG59XG5cbi8qKlxuICogVmVyaWZ5IGEgcGFja2FnZSBkb2VzIG5vdCBleGlzdCBsb2NhbGx5IHlldDsgcGFydCBvZiB0aGUgcHVibGlzaCBmbG93LlxuICogQHBhcmFtIG5hbWUgcGFja2FnZSBuYW1lXG4gKiBAcGFyYW0gbG9jYWxTdG9yYWdlIGxvY2FsIHN0b3JhZ2UgaW5zdGFuY2VcbiAqIEB0aHJvd3MgY29uZmxpY3QgZXJyb3Igd2hlbiB0aGUgcGFja2FnZSBhbHJlYWR5IGV4aXN0cyBsb2NhbGx5XG4gKi9cbmV4cG9ydCBhc3luYyBmdW5jdGlvbiBjaGVja1BhY2thZ2VMb2NhbChuYW1lOiBzdHJpbmcsIGxvY2FsU3RvcmFnZTogTG9jYWxTdG9yYWdlKTogUHJvbWlzZTx2b2lkPiB7XG4gIGRlYnVnKCdjaGVja2luZyBpZiAlbyBhbHJlYWR5IGV4aXN0cyBsb2NhbGx5JywgbmFtZSk7XG4gIHRyeSB7XG4gICAgY29uc3QgcmVzdWx0cyA9IGF3YWl0IGxvY2FsU3RvcmFnZS5nZXRQYWNrYWdlTWV0YWRhdGFBc3luYyhuYW1lKTtcbiAgICBpZiAocmVzdWx0cykge1xuICAgICAgZGVidWcoJ3BhY2thZ2UgJW8gYWxyZWFkeSBleGlzdHMgbG9jYWxseScsIG5hbWUpO1xuICAgICAgdGhyb3cgRXJyb3JDb2RlLmdldENvbmZsaWN0KEFQSV9FUlJPUi5QQUNLQUdFX0VYSVNUKTtcbiAgICB9XG4gIH0gY2F0Y2ggKGVycjogYW55KSB7XG4gICAgaWYgKGVyci5zdGF0dXMgPT09IEhUVFBfU1RBVFVTLk5PVF9GT1VORCkge1xuICAgICAgZGVidWcoJ3BhY2thZ2UgJW8gZG9lcyBub3QgZXhpc3QgbG9jYWxseScsIG5hbWUpO1xuICAgICAgcmV0dXJuO1xuICAgIH1cbiAgICB0aHJvdyBlcnI7XG4gIH1cbn1cblxuLyoqXG4gKiBDcmVhdGUgYSBuZXcgcGFja2FnZSBpbiB0aGUgbG9jYWwgc3RvcmFnZSBhbmQgcmVnaXN0ZXIgaXRzIGxhdGVzdCB2ZXJzaW9uXG4gKiBpbiB0aGUgaW4tbWVtb3J5IHNlYXJjaCBpbmRleGVyLlxuICogQHBhcmFtIG5hbWUgcGFja2FnZSBuYW1lXG4gKiBAcGFyYW0gbWV0YWRhdGEgcGFja2FnZSBtYW5pZmVzdCB0byBzdG9yZVxuICogQHBhcmFtIGxvY2FsU3RvcmFnZSBsb2NhbCBzdG9yYWdlIGluc3RhbmNlXG4gKi9cbmV4cG9ydCBmdW5jdGlvbiBwdWJsaXNoUGFja2FnZShcbiAgbmFtZTogc3RyaW5nLFxuICBtZXRhZGF0YTogYW55LFxuICBsb2NhbFN0b3JhZ2U6IExvY2FsU3RvcmFnZVxuKTogUHJvbWlzZTx2b2lkPiB7XG4gIGRlYnVnKCdwdWJsaXNoaW5nIGEgbmV3IHBhY2thZ2UgZm9yICVvJywgbmFtZSk7XG4gIHJldHVybiBuZXcgUHJvbWlzZSgocmVzb2x2ZSwgcmVqZWN0KTogdm9pZCA9PiB7XG4gICAgbG9jYWxTdG9yYWdlLmFkZFBhY2thZ2UobmFtZSwgbWV0YWRhdGEsIChlcnIsIGxhdGVzdCk6IHZvaWQgPT4ge1xuICAgICAgaWYgKCFfLmlzTnVsbChlcnIpKSB7XG4gICAgICAgIHJldHVybiByZWplY3QoZXJyKTtcbiAgICAgIH0gZWxzZSBpZiAoIV8uaXNVbmRlZmluZWQobGF0ZXN0KSkge1xuICAgICAgICBTZWFyY2hNZW1vcnlJbmRleGVyLmFkZChsYXRlc3QpLmNhdGNoKChyZWFzb24pID0+IHtcbiAgICAgICAgICBkZWJ1ZygnaW5kZXhlciBoYXMgZmFpbGVkIG9uIGFkZCBpdGVtICVvJywgcmVhc29uKTtcbiAgICAgICAgICBsb2dnZXIuZXJyb3IoJ2luZGV4ZXIgaGFzIGZhaWxlZCBvbiBhZGQgaXRlbScpO1xuICAgICAgICB9KTtcbiAgICAgIH1cbiAgICAgIHJldHVybiByZXNvbHZlKCk7XG4gICAgfSk7XG4gIH0pO1xufVxuXG4vKipcbiAqIFZlcmlmeSBhIHBhY2thZ2UgZG9lcyBub3QgZXhpc3Qgb24gYW55IHVwbGluayB5ZXQ7IHBhcnQgb2YgdGhlIHB1Ymxpc2hcbiAqIGZsb3cuIFVwbGluayBmYWlsdXJlcyBvdGhlciB0aGFuIDQwNCByZWplY3QgdGhlIHB1Ymxpc2gsIHVubGVzc1xuICogYHB1Ymxpc2guYWxsb3dfb2ZmbGluZWAgaXMgZW5hYmxlZCBpbiB0aGUgY29uZmlndXJhdGlvbi5cbiAqIEBwYXJhbSBuYW1lIHBhY2thZ2UgbmFtZVxuICogQHBhcmFtIGlzQWxsb3dQdWJsaXNoT2ZmbGluZSB3aGV0aGVyIHB1Ymxpc2hpbmcgd2l0aCB1bnJlYWNoYWJsZSB1cGxpbmtzIGlzIGFsbG93ZWRcbiAqIEBwYXJhbSBzeW5jTWV0YWRhdGEgYm91bmQgU3RvcmFnZS5fc3luY1VwbGlua3NNZXRhZGF0YSBmdW5jdGlvblxuICogQHRocm93cyBjb25mbGljdCBlcnJvciB3aGVuIHRoZSBwYWNrYWdlIGV4aXN0cyB1cHN0cmVhbSwgc2VydmljZSB1bmF2YWlsYWJsZSB3aGVuIHVwbGlua3MgYXJlIG9mZmxpbmVcbiAqL1xuZXhwb3J0IGZ1bmN0aW9uIGNoZWNrUGFja2FnZVJlbW90ZShcbiAgbmFtZTogc3RyaW5nLFxuICBpc0FsbG93UHVibGlzaE9mZmxpbmU6IGJvb2xlYW4sXG4gIHN5bmNNZXRhZGF0YTogYW55XG4pOiBQcm9taXNlPHZvaWQ+IHtcbiAgZGVidWcoJ2NoZWNraW5nIGlmICVvIGFscmVhZHkgZXhpc3RzIG9uIHVwbGlua3MnLCBuYW1lKTtcbiAgcmV0dXJuIG5ldyBQcm9taXNlKChyZXNvbHZlLCByZWplY3QpOiB2b2lkID0+IHtcbiAgICBzeW5jTWV0YWRhdGEobmFtZSwgbnVsbCwge30sIChlcnIsIHBhY2thZ2VKc29uTG9jYWwsIHVwTGlua3NFcnJvcnMpOiB2b2lkID0+IHtcbiAgICAgIC8vIHNvbWV0aGluZyB3ZWlyZFxuICAgICAgaWYgKGVyciAmJiBlcnIuc3RhdHVzICE9PSBIVFRQX1NUQVRVUy5OT1RfRk9VTkQpIHtcbiAgICAgICAgcmV0dXJuIHJlamVjdChlcnIpO1xuICAgICAgfVxuXG4gICAgICAvLyBjaGVja2luZyBwYWNrYWdlIGV4aXN0IGFscmVhZHlcbiAgICAgIGlmIChfLmlzTmlsKHBhY2thZ2VKc29uTG9jYWwpID09PSBmYWxzZSkge1xuICAgICAgICBkZWJ1ZygncGFja2FnZSAlbyBhbHJlYWR5IGV4aXN0cyBvbiBhbiB1cGxpbmsnLCBuYW1lKTtcbiAgICAgICAgcmV0dXJuIHJlamVjdChFcnJvckNvZGUuZ2V0Q29uZmxpY3QoQVBJX0VSUk9SLlBBQ0tBR0VfRVhJU1QpKTtcbiAgICAgIH1cblxuICAgICAgZm9yIChsZXQgZXJyb3JJdGVtID0gMDsgZXJyb3JJdGVtIDwgdXBMaW5rc0Vycm9ycy5sZW5ndGg7IGVycm9ySXRlbSsrKSB7XG4gICAgICAgIC8vIGNoZWNraW5nIGVycm9yXG4gICAgICAgIC8vIGlmIHVwbGluayBmYWlscyB3aXRoIGEgc3RhdHVzIG90aGVyIHRoYW4gNDA0LCB3ZSByZXBvcnQgZmFpbHVyZVxuICAgICAgICBpZiAoXy5pc05pbCh1cExpbmtzRXJyb3JzW2Vycm9ySXRlbV1bMF0pID09PSBmYWxzZSkge1xuICAgICAgICAgIGlmICh1cExpbmtzRXJyb3JzW2Vycm9ySXRlbV1bMF0uc3RhdHVzICE9PSBIVFRQX1NUQVRVUy5OT1RfRk9VTkQpIHtcbiAgICAgICAgICAgIGlmIChpc0FsbG93UHVibGlzaE9mZmxpbmUpIHtcbiAgICAgICAgICAgICAgZGVidWcoJ3VwbGluayBvZmZsaW5lIGZvciAlbywgcHVibGlzaCBvZmZsaW5lIGlzIGFsbG93ZWQnLCBuYW1lKTtcbiAgICAgICAgICAgICAgcmV0dXJuIHJlc29sdmUoKTtcbiAgICAgICAgICAgIH1cblxuICAgICAgICAgICAgZGVidWcoJ3VwbGluayBvZmZsaW5lIGZvciAlbywgcmVqZWN0aW5nIHB1Ymxpc2gnLCBuYW1lKTtcbiAgICAgICAgICAgIHJldHVybiByZWplY3QoRXJyb3JDb2RlLmdldFNlcnZpY2VVbmF2YWlsYWJsZShBUElfRVJST1IuVVBMSU5LX09GRkxJTkVfUFVCTElTSCkpO1xuICAgICAgICAgIH1cbiAgICAgICAgfVxuICAgICAgfVxuXG4gICAgICByZXR1cm4gcmVzb2x2ZSgpO1xuICAgIH0pO1xuICB9KTtcbn1cblxuLyoqXG4gKiBNZXJnZSB0aGUgYHRpbWVgIGZpZWxkIG9mIGEgcmVtb3RlIG1hbmlmZXN0IGludG8gdGhlIGNhY2hlZCBvbmU7IHJlbW90ZVxuICogZW50cmllcyB3aW4gb24gY29uZmxpY3RzLiBSZXR1cm5zIGEgbmV3IG1hbmlmZXN0LCB0aGUgaW5wdXRzIGFyZSB1bnRvdWNoZWQuXG4gKiBAcGFyYW0gY2FjaGVNYW5pZmVzdCBsb2NhbCBjYWNoZWQgbWFuaWZlc3RcbiAqIEBwYXJhbSByZW1vdGVNYW5pZmVzdCBtYW5pZmVzdCBmZXRjaGVkIGZyb20gYW4gdXBsaW5rXG4gKiBAcmV0dXJuIHRoZSBjYWNoZWQgbWFuaWZlc3Qgd2l0aCB0aGUgbWVyZ2VkIHRpbWUgZmllbGRcbiAqL1xuZXhwb3J0IGZ1bmN0aW9uIG1lcmdlVXBsaW5rVGltZUludG9Mb2NhbChjYWNoZU1hbmlmZXN0OiBNYW5pZmVzdCwgcmVtb3RlTWFuaWZlc3Q6IE1hbmlmZXN0KTogYW55IHtcbiAgaWYgKCd0aW1lJyBpbiByZW1vdGVNYW5pZmVzdCkge1xuICAgIGRlYnVnKCdtZXJnaW5nIHJlbW90ZSB0aW1lIGZpZWxkIGludG8gbG9jYWwgbWFuaWZlc3QgZm9yICVvJywgY2FjaGVNYW5pZmVzdC5uYW1lKTtcbiAgICAvLyByZW1vdGUgb3ZlcnJpZGUgY2FjaGUgdGltZSBjb25mbGljdHNcbiAgICByZXR1cm4geyAuLi5jYWNoZU1hbmlmZXN0LCB0aW1lOiB7IC4uLmNhY2hlTWFuaWZlc3QudGltZSwgLi4ucmVtb3RlTWFuaWZlc3QudGltZSB9IH07XG4gIH1cblxuICByZXR1cm4gY2FjaGVNYW5pZmVzdDtcbn1cblxuLyoqXG4gKiBCdWlsZCB0aGUgc2VhcmNoIGl0ZW0gZW1pdHRlZCBmb3IgYSBsb2NhbCBwYWNrYWdlIGR1cmluZyBhIHNlYXJjaCBzdHJlYW0sXG4gKiBiYXNlZCBvbiBpdHMgbGF0ZXN0IHZlcnNpb246IG5wbS1zZWFyY2ggY29tcGF0aWJsZSBtYWludGFpbmVyc1xuICogKGB7IHVzZXJuYW1lLCBlbWFpbCB9YCksIHB1Ymxpc2hlciAoZnJvbSBgX25wbVVzZXJgLCBmYWxsaW5nIGJhY2sgdG8gdGhlXG4gKiBmaXJzdCBtYWludGFpbmVyKSwgbGljZW5zZSBhbmQgbGlua3MuXG4gKiBAcGFyYW0gZGF0YSBwYWNrYWdlIG1hbmlmZXN0XG4gKiBAcGFyYW0gdGltZSBtb2RpZmllZCB0aW1lIGF0dGFjaGVkIHRvIHRoZSBzZWFyY2ggaXRlbVxuICogQHJldHVybiB0aGUgc2VhcmNoIGl0ZW0sIG9yIHVuZGVmaW5lZCB3aGVuIHRoZSBwYWNrYWdlIGhhcyBubyB1c2FibGUgbGF0ZXN0IHZlcnNpb25cbiAqL1xuZXhwb3J0IGZ1bmN0aW9uIHByZXBhcmVTZWFyY2hQYWNrYWdlKGRhdGE6IE1hbmlmZXN0LCB0aW1lOiB1bmtub3duKTogYW55IHtcbiAgY29uc3QgbGF0ZXN0ID0gcGtnVXRpbHMuZ2V0TGF0ZXN0KGRhdGEpO1xuXG4gIGlmICghbGF0ZXN0IHx8ICFkYXRhLnZlcnNpb25zW2xhdGVzdF0pIHtcbiAgICBkZWJ1Zygnbm8gbGF0ZXN0IHZlcnNpb24gZm91bmQgZm9yICVvLCBza2lwcGluZyBzZWFyY2ggaXRlbScsIGRhdGEubmFtZSk7XG4gICAgcmV0dXJuO1xuICB9XG5cbiAgZGVidWcoJ3ByZXBhcmluZyBzZWFyY2ggaXRlbSBmb3IgJW9AJW8nLCBkYXRhLm5hbWUsIGxhdGVzdCk7XG4gIGlmIChsYXRlc3QgJiYgZGF0YS52ZXJzaW9uc1tsYXRlc3RdKSB7XG4gICAgY29uc3QgdmVyc2lvbjogVmVyc2lvbiA9IGRhdGEudmVyc2lvbnNbbGF0ZXN0XTtcbiAgICBjb25zdCB2ZXJzaW9uczogYW55ID0geyBbbGF0ZXN0XTogJ2xhdGVzdCcgfTtcbiAgICAvLyBwYWNrdW1lbnQgbWFpbnRhaW5lcnMgY2FycnkgYG5hbWVgLCBidXQgdGhlIG5wbSBzZWFyY2ggQVBJIGNvbnRyYWN0IHVzZXNcbiAgICAvLyBgdXNlcm5hbWVgIGFuZCB0aGUgbnBtIENMSSBjcmFzaGVzIG9uIGVudHJpZXMgd2l0aG91dCBpdCDigJQgZW1pdCBib3RoXG4gICAgY29uc3QgcmF3TWFpbnRhaW5lcnMgPSB2ZXJzaW9uLm1haW50YWluZXJzIHx8IFt2ZXJzaW9uLmF1dGhvcl0uZmlsdGVyKEJvb2xlYW4pO1xuICAgIGNvbnN0IG1haW50YWluZXJzID0gQXJyYXkuaXNBcnJheShyYXdNYWludGFpbmVycylcbiAgICAgID8gcmF3TWFpbnRhaW5lcnMubWFwKChtYWludGFpbmVyOiBhbnkpID0+XG4gICAgICAgICAgdHlwZW9mIG1haW50YWluZXIgPT09ICdzdHJpbmcnXG4gICAgICAgICAgICA/IHsgdXNlcm5hbWU6IG1haW50YWluZXIsIGVtYWlsOiAnJyB9XG4gICAgICAgICAgICA6IHtcbiAgICAgICAgICAgICAgICAuLi5tYWludGFpbmVyLFxuICAgICAgICAgICAgICAgIHVzZXJuYW1lOiBtYWludGFpbmVyPy51c2VybmFtZSA/PyBtYWludGFpbmVyPy5uYW1lID8/ICcnLFxuICAgICAgICAgICAgICAgIGVtYWlsOiBtYWludGFpbmVyPy5lbWFpbCA/PyAnJyxcbiAgICAgICAgICAgICAgfVxuICAgICAgICApXG4gICAgICA6IFtdO1xuICAgIC8vIG5wbWpzIGZpbGxzIGBwdWJsaXNoZXJgIHdpdGggdGhlIHVzZXIgd2hvIHB1Ymxpc2hlZCB0aGUgdmVyc2lvbjsgdXNlXG4gICAgLy8gYF9ucG1Vc2VyYCB3aGVuIHRoZSBwdWJsaXNoaW5nIGNsaWVudCBwcm92aWRlZCBpdCBhbmQgZmFsbCBiYWNrIHRvIHRoZVxuICAgIC8vIGZpcnN0IG1haW50YWluZXIsIG90aGVyd2lzZSB0aGUgbnBtIENMSSByZW5kZXJzIFwicHVibGlzaGVkIGJ5ID8/P1wiXG4gICAgY29uc3QgbnBtVXNlciA9ICh2ZXJzaW9uIGFzIGFueSkuX25wbVVzZXI7XG4gICAgY29uc3QgcHVibGlzaGVyID0gbnBtVXNlcj8ubmFtZVxuICAgICAgPyB7IHVzZXJuYW1lOiBucG1Vc2VyLm5hbWUsIGVtYWlsOiBucG1Vc2VyLmVtYWlsID8/ICcnIH1cbiAgICAgIDogKG1haW50YWluZXJzWzBdID8/IHt9KTtcbiAgICBjb25zdCBwa2c6IGFueSA9IHtcbiAgICAgIG5hbWU6IHZlcnNpb24ubmFtZSxcbiAgICAgIGRlc2NyaXB0aW9uOiB2ZXJzaW9uLmRlc2NyaXB0aW9uLFxuICAgICAgW0RJU1RfVEFHU106IHsgbGF0ZXN0IH0sXG4gICAgICBtYWludGFpbmVycyxcbiAgICAgIHB1Ymxpc2hlcixcbiAgICAgIGF1dGhvcjogdmVyc2lvbi5hdXRob3IsXG4gICAgICByZXBvc2l0b3J5OiB2ZXJzaW9uLnJlcG9zaXRvcnksXG4gICAgICByZWFkbWVGaWxlbmFtZTogdmVyc2lvbi5yZWFkbWVGaWxlbmFtZSB8fCAnJyxcbiAgICAgIGhvbWVwYWdlOiB2ZXJzaW9uLmhvbWVwYWdlLFxuICAgICAga2V5d29yZHM6IHZlcnNpb24ua2V5d29yZHMsXG4gICAgICB0aW1lOiB7XG4gICAgICAgIG1vZGlmaWVkOiB0aW1lLFxuICAgICAgfSxcbiAgICAgIGJ1Z3M6IHZlcnNpb24uYnVncyxcbiAgICAgIGxpY2Vuc2U6IHZlcnNpb24ubGljZW5zZSxcbiAgICAgIHZlcnNpb25zLFxuICAgIH07XG5cbiAgICByZXR1cm4gcGtnO1xuICB9XG59XG5cbi8qKlxuICogV2hldGhlciB0aGUgdGFyYmFsbCB1cmwgcGF0aCBlbmRzIHdpdGggdGhlIGdpdmVuIGZpbGUgbmFtZVxuICogKGFueSBxdWVyeSBzdHJpbmcgb3IgZnJhZ21lbnQgaXMgaWdub3JlZCwgbGlrZSB0aGUgcGF0aCBwYXJzaW5nXG4gKiBpbiB1cGRhdGVWZXJzaW9ucykuXG4gKi9cbmV4cG9ydCBmdW5jdGlvbiB0YXJiYWxsTWF0Y2hlc0ZpbGVuYW1lKHRhcmJhbGw6IHN0cmluZywgZmlsZW5hbWU6IHN0cmluZyk6IGJvb2xlYW4ge1xuICByZXR1cm4gdGFyYmFsbC5zcGxpdCgvWz8jXS8pWzBdLmVuZHNXaXRoKGAvJHtmaWxlbmFtZX1gKTtcbn1cblxuLyoqXG4gKiBCdWlsZCBhIGRpc3RmaWxlIHJlY29yZCBmcm9tIGEgdmVyc2lvbidzIGRpc3QgbWV0YWRhdGEgd2hlbiBpdHMgdGFyYmFsbFxuICogdXJsIG1hdGNoZXMgdGhlIGdpdmVuIGZpbGUgbmFtZS5cbiAqL1xuZXhwb3J0IGZ1bmN0aW9uIGRpc3RGaWxlRnJvbVZlcnNpb24oXG4gIHZlcnNpb246IFZlcnNpb24gfCB1bmRlZmluZWQsXG4gIGZpbGVuYW1lOiBzdHJpbmdcbik6IERpc3RGaWxlIHwgbnVsbCB7XG4gIGNvbnN0IGRpc3QgPSB2ZXJzaW9uPy5kaXN0O1xuXG4gIGlmIChkaXN0Py50YXJiYWxsICYmIHRhcmJhbGxNYXRjaGVzRmlsZW5hbWUoZGlzdC50YXJiYWxsLCBmaWxlbmFtZSkpIHtcbiAgICByZXR1cm4geyB1cmw6IGRpc3QudGFyYmFsbCwgc2hhOiBkaXN0LnNoYXN1bSB9O1xuICB9XG5cbiAgcmV0dXJuIG51bGw7XG59XG5cbi8qKlxuICogUmVzb2x2ZSB0aGUgZGlzdGZpbGUgcmVjb3JkIGZvciBhIHRhcmJhbGwgZmlsZW5hbWUuIFByZWZlcnMgdGhlXG4gKiBgX2Rpc3RmaWxlc2AgYm9va2tlZXBpbmcsIGJ1dCBmYWxscyBiYWNrIHRvIHRoZSB2ZXJzaW9uIG1ldGFkYXRhIHdoZW4gdGhlXG4gKiByZWNvcmQgaXMgbWlzc2luZyAoc3RvcmFnZXMgd3JpdHRlbiBieSBvdGhlciB2ZXJkYWNjaW8gdmVyc2lvbnMgY2FjaGVcbiAqIHZlcnNpb25zIHdpdGhvdXQgdGhlaXIgZGlzdGZpbGUgcmVjb3Jkcywgd2hpY2ggbWFkZSB0aG9zZSB0YXJiYWxsc1xuICogcGVybWFuZW50bHkgdW5hdmFpbGFibGUpLlxuICogQHBhcmFtIG1hbmlmZXN0IGNhY2hlZCBtYW5pZmVzdFxuICogQHBhcmFtIGZpbGVuYW1lIHRhcmJhbGwgZmlsZSBuYW1lLCBlZy4gcGtnLTEuMC4wLnRnelxuICovXG5leHBvcnQgZnVuY3Rpb24gbG9va3VwRGlzdEZpbGUobWFuaWZlc3Q6IE1hbmlmZXN0LCBmaWxlbmFtZTogc3RyaW5nKTogRGlzdEZpbGUgfCBudWxsIHtcbiAgY29uc3QgZGlzdEZpbGUgPSBtYW5pZmVzdD8uX2Rpc3RmaWxlcz8uW2ZpbGVuYW1lXTtcbiAgaWYgKF8uaXNOaWwoZGlzdEZpbGUpID09PSBmYWxzZSkge1xuICAgIHJldHVybiBkaXN0RmlsZTtcbiAgfVxuXG4gIGNvbnN0IHZlcnNpb25zID0gbWFuaWZlc3Q/LnZlcnNpb25zID8/IHt9O1xuXG4gIC8vIHRhcmJhbGxzIGFyZSBjb252ZW50aW9uYWxseSBuYW1lZCA8YmFzZW5hbWU+LTx2ZXJzaW9uPi50Z3osIHNvIHRoZVxuICAvLyB2ZXJzaW9uIGNhbiB1c3VhbGx5IGJlIGRlcml2ZWQgZnJvbSB0aGUgZmlsZW5hbWUgd2l0aG91dCBzY2FubmluZ1xuICAvLyBtYW5pZmVzdHMgdGhhdCBjYXJyeSB0aG91c2FuZHMgb2YgdmVyc2lvbnNcbiAgY29uc3QgYmFzZW5hbWUgPSBtYW5pZmVzdD8ubmFtZT8ucmVwbGFjZSgvXi4qXFwvLywgJycpO1xuICBpZiAoYmFzZW5hbWUgJiYgZmlsZW5hbWUuc3RhcnRzV2l0aChgJHtiYXNlbmFtZX0tYCkgJiYgZmlsZW5hbWUuZW5kc1dpdGgoJy50Z3onKSkge1xuICAgIGNvbnN0IHZlcnNpb25JZCA9IGZpbGVuYW1lLnNsaWNlKGJhc2VuYW1lLmxlbmd0aCArIDEsIC0nLnRneicubGVuZ3RoKTtcbiAgICBjb25zdCBkaXN0RnJvbVZlcnNpb24gPSBkaXN0RmlsZUZyb21WZXJzaW9uKHZlcnNpb25zW3ZlcnNpb25JZF0sIGZpbGVuYW1lKTtcblxuICAgIGlmIChkaXN0RnJvbVZlcnNpb24pIHtcbiAgICAgIGRlYnVnKCdkaXN0ZmlsZSByZWNvcmQgbWlzc2luZyBmb3IgJW8sIHVzaW5nIHZlcnNpb24gJW8gZGlzdCcsIGZpbGVuYW1lLCB2ZXJzaW9uSWQpO1xuICAgICAgcmV0dXJuIGRpc3RGcm9tVmVyc2lvbjtcbiAgICB9XG4gIH1cblxuICAvLyB1bmNvbnZlbnRpb25hbCB0YXJiYWxsIG5hbWVzOiBmYWxsIGJhY2sgdG8gYSBzY2FuXG4gIGZvciAoY29uc3QgdmVyc2lvbklkIGluIHZlcnNpb25zKSB7XG4gICAgY29uc3QgZGlzdEZyb21WZXJzaW9uID0gZGlzdEZpbGVGcm9tVmVyc2lvbih2ZXJzaW9uc1t2ZXJzaW9uSWRdLCBmaWxlbmFtZSk7XG5cbiAgICBpZiAoZGlzdEZyb21WZXJzaW9uKSB7XG4gICAgICBkZWJ1ZygnZGlzdGZpbGUgcmVjb3JkIG1pc3NpbmcgZm9yICVvLCB1c2luZyB2ZXJzaW9uICVvIGRpc3QnLCBmaWxlbmFtZSwgdmVyc2lvbklkKTtcbiAgICAgIHJldHVybiBkaXN0RnJvbVZlcnNpb247XG4gICAgfVxuICB9XG5cbiAgZGVidWcoJ25vIGRpc3RmaWxlIHJlY29yZCBvciB2ZXJzaW9uIGRpc3QgZm91bmQgZm9yICVvJywgZmlsZW5hbWUpO1xuICByZXR1cm4gbnVsbDtcbn1cblxuLyoqXG4gKiBDaGVjayB3aGV0aGVyIHRoZSBwYWNrYWdlIG1ldGFkYXRhIGhhcyBlbm91Z2ggZGF0YSB0byBiZSBwdWJsaXNoZWQuXG4gKiBAcGFyYW0gcGtnIHBhY2thZ2UgbWFuaWZlc3RcbiAqIEByZXR1cm4gdHJ1ZSB3aGVuIHRoZSBtYW5pZmVzdCBjYXJyaWVzIGEgdmVyc2lvbnMgc2VjdGlvblxuICovXG5leHBvcnQgZnVuY3Rpb24gaXNQdWJsaXNoYWJsZVBhY2thZ2UocGtnOiBNYW5pZmVzdCk6IGJvb2xlYW4ge1xuICBjb25zdCBrZXlzOiBzdHJpbmdbXSA9IE9iamVjdC5rZXlzKHBrZyk7XG5cbiAgcmV0dXJuIGtleXMuaW5jbHVkZXMoJ3ZlcnNpb25zJyk7XG59XG5cbi8qKlxuICogV2hldGhlciBhIHZlcnNpb24gZGVjbGFyZXMgYW4gaW5zdGFsbCBsaWZlY3ljbGUgc2NyaXB0IChgaW5zdGFsbGAsXG4gKiBgcHJlaW5zdGFsbGAgb3IgYHBvc3RpbnN0YWxsYCk7IHN1cmZhY2VkIGluIGFiYnJldmlhdGVkIG1hbmlmZXN0cyBhc1xuICogYGhhc0luc3RhbGxTY3JpcHRgIHNvIGNsaWVudHMgY2FuIHdhcm4gYWJvdXQgc2NyaXB0IGV4ZWN1dGlvbi5cbiAqIEBwYXJhbSB2ZXJzaW9uIHZlcnNpb24gbWV0YWRhdGFcbiAqIEByZXR1cm4gdHJ1ZSB3aGVuIGFuIGluc3RhbGwgc2NyaXB0IGlzIHByZXNlbnRcbiAqL1xuZXhwb3J0IGZ1bmN0aW9uIGhhc0luc3RhbGxTY3JpcHQodmVyc2lvbjogVmVyc2lvbikge1xuICBpZiAodmVyc2lvbj8uc2NyaXB0cykge1xuICAgIGNvbnN0IHNjcmlwdHMgPSBPYmplY3Qua2V5cyh2ZXJzaW9uLnNjcmlwdHMpO1xuICAgIHJldHVybiAoXG4gICAgICBzY3JpcHRzLmZpbmQoKGl0ZW0pID0+IHtcbiAgICAgICAgcmV0dXJuIFsnaW5zdGFsbCcsICdwcmVpbnN0YWxsJywgJ3Bvc3RpbnN0YWxsJ10uaW5jbHVkZXMoaXRlbSk7XG4gICAgICB9KSAhPT0gdW5kZWZpbmVkXG4gICAgKTtcbiAgfVxuICByZXR1cm4gZmFsc2U7XG59XG5cbi8qKlxuICogQ29udmVydCBhIGZ1bGwgbWFuaWZlc3QgaW50byB0aGUgYWJicmV2aWF0ZWQgZm9ybWF0IHNlcnZlZCB3aGVuIHRoZSBjbGllbnRcbiAqIHJlcXVlc3RzIGBhcHBsaWNhdGlvbi92bmQubnBtLmluc3RhbGwtdjEranNvbmAgKG5wbSBpbnN0YWxsKSwga2VlcGluZyBvbmx5XG4gKiB0aGUgZmllbGRzIHRoZSBucG0gcmVnaXN0cnkgY29udHJhY3QgZGVmaW5lcyBmb3IgaW5zdGFsbGF0aW9uLlxuICogaHR0cHM6Ly9naXRodWIuY29tL25wbS9yZWdpc3RyeS9ibG9iL21hc3Rlci9kb2NzL3Jlc3BvbnNlcy9wYWNrYWdlLW1ldGFkYXRhLm1kXG4gKiBAcGFyYW0gbWFuaWZlc3QgZnVsbCBwYWNrYWdlIG1hbmlmZXN0XG4gKiBAcmV0dXJuIHRoZSBhYmJyZXZpYXRlZCBtYW5pZmVzdFxuICovXG5leHBvcnQgZnVuY3Rpb24gY29udmVydEFiYnJldmlhdGVkTWFuaWZlc3QobWFuaWZlc3Q6IE1hbmlmZXN0KTogQWJicmV2aWF0ZWRNYW5pZmVzdCB7XG4gIGlmIChkZWJ1Zy5lbmFibGVkKSB7XG4gICAgZGVidWcoXG4gICAgICAnY29udmVydGluZyAlbyB0byBhYmJyZXZpYXRlZCBtYW5pZmVzdCB3aXRoICVvIHZlcnNpb25zJyxcbiAgICAgIG1hbmlmZXN0Lm5hbWUsXG4gICAgICBPYmplY3Qua2V5cyhtYW5pZmVzdC52ZXJzaW9ucykubGVuZ3RoXG4gICAgKTtcbiAgfVxuICBjb25zdCBhYmJyZXZpYXRlZFZlcnNpb25zID0gT2JqZWN0LmtleXMobWFuaWZlc3QudmVyc2lvbnMpLnJlZHVjZShcbiAgICAoYWNjOiBBYmJyZXZpYXRlZFZlcnNpb25zLCB2ZXJzaW9uOiBzdHJpbmcpID0+IHtcbiAgICAgIGNvbnN0IF92ZXJzaW9uID0gbWFuaWZlc3QudmVyc2lvbnNbdmVyc2lvbl07XG4gICAgICAvLyBUaGlzIHNob3VsZCBiZSBhbGlnbiB3aXRoIHRoaXMgZG9jdW1lbnRcbiAgICAgIC8vIGh0dHBzOi8vZ2l0aHViLmNvbS9ucG0vcmVnaXN0cnkvYmxvYi9tYXN0ZXIvZG9jcy9yZXNwb25zZXMvcGFja2FnZS1tZXRhZGF0YS5tZCNhYmJyZXZpYXRlZC12ZXJzaW9uLW9iamVjdFxuICAgICAgY29uc3QgX3ZlcnNpb25fYWJicmV2aWF0ZWQgPSB7XG4gICAgICAgIG5hbWU6IF92ZXJzaW9uLm5hbWUsXG4gICAgICAgIHZlcnNpb246IF92ZXJzaW9uLnZlcnNpb24sXG4gICAgICAgIGRlc2NyaXB0aW9uOiBfdmVyc2lvbi5kZXNjcmlwdGlvbixcbiAgICAgICAgZGVwcmVjYXRlZDogX3ZlcnNpb24uZGVwcmVjYXRlZCxcbiAgICAgICAgYmluOiBfdmVyc2lvbi5iaW4sXG4gICAgICAgIGRpc3Q6IF92ZXJzaW9uLmRpc3QsXG4gICAgICAgIGVuZ2luZXM6IF92ZXJzaW9uLmVuZ2luZXMsXG4gICAgICAgIGNwdTogX3ZlcnNpb24uY3B1LFxuICAgICAgICBvczogX3ZlcnNpb24ub3MsXG4gICAgICAgIGZ1bmRpbmc6IF92ZXJzaW9uLmZ1bmRpbmcsXG4gICAgICAgIGRpcmVjdG9yaWVzOiBfdmVyc2lvbi5kaXJlY3RvcmllcyxcbiAgICAgICAgZGVwZW5kZW5jaWVzOiBfdmVyc2lvbi5kZXBlbmRlbmNpZXMsXG4gICAgICAgIGRldkRlcGVuZGVuY2llczogX3ZlcnNpb24uZGV2RGVwZW5kZW5jaWVzLFxuICAgICAgICBwZWVyRGVwZW5kZW5jaWVzOiBfdmVyc2lvbi5wZWVyRGVwZW5kZW5jaWVzLFxuICAgICAgICBwZWVyRGVwZW5kZW5jaWVzTWV0YTogX3ZlcnNpb24ucGVlckRlcGVuZGVuY2llc01ldGEsXG4gICAgICAgIG9wdGlvbmFsRGVwZW5kZW5jaWVzOiBfdmVyc2lvbi5vcHRpb25hbERlcGVuZGVuY2llcyxcbiAgICAgICAgYnVuZGxlRGVwZW5kZW5jaWVzOiBfdmVyc2lvbi5idW5kbGVEZXBlbmRlbmNpZXMsXG4gICAgICAgIC8vIG5wbSBjbGkgc3BlY2lmaWNzXG4gICAgICAgIF9oYXNTaHJpbmt3cmFwOiBfdmVyc2lvbi5faGFzU2hyaW5rd3JhcCxcbiAgICAgICAgaGFzSW5zdGFsbFNjcmlwdDogaGFzSW5zdGFsbFNjcmlwdChfdmVyc2lvbiksXG4gICAgICB9O1xuICAgICAgYWNjW3ZlcnNpb25dID0gX3ZlcnNpb25fYWJicmV2aWF0ZWQ7XG4gICAgICByZXR1cm4gYWNjO1xuICAgIH0sXG4gICAge31cbiAgKTtcblxuICBjb25zdCBjb252ZXJ0ZWRNYW5pZmVzdCA9IHtcbiAgICBuYW1lOiBtYW5pZmVzdFsnbmFtZSddLFxuICAgIFtESVNUX1RBR1NdOiBtYW5pZmVzdFtESVNUX1RBR1NdLFxuICAgIHZlcnNpb25zOiBhYmJyZXZpYXRlZFZlcnNpb25zLFxuICAgIC8vIEB0cy1pZ25vcmVcbiAgICBtb2RpZmllZDogbWFuaWZlc3Q/LnRpbWU/Lm1vZGlmaWVkLFxuICAgIC8vIE5PVEU6IHNwZWNpYWwgY2FzZSBmb3IgcG5wbSBodHRwczovL2dpdGh1Yi5jb20vcG5wbS9yZmNzL3B1bGwvMlxuICAgIHRpbWU6IG1hbmlmZXN0Py50aW1lLFxuICB9O1xuXG4gIC8vIEB0cy1pZ25vcmVcbiAgcmV0dXJuIGNvbnZlcnRlZE1hbmlmZXN0O1xufVxuIl0sIm1hcHBpbmdzIjoiOzs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7OztBQUFBLElBQUFBLE1BQUEsR0FBQUMsc0JBQUEsQ0FBQUMsT0FBQTtBQUNBLElBQUFDLE9BQUEsR0FBQUYsc0JBQUEsQ0FBQUMsT0FBQTtBQUVBLElBQUFFLEtBQUEsR0FBQUYsT0FBQTtBQUNBLElBQUFHLGNBQUEsR0FBQUgsT0FBQTtBQVFBLElBQUFJLE1BQUEsR0FBQUosT0FBQTtBQUVBLElBQUFLLFVBQUEsR0FBQUwsT0FBQTtBQUVBLElBQUFNLE9BQUEsR0FBQU4sT0FBQTtBQUNBLElBQUFPLE9BQUEsR0FBQVAsT0FBQTtBQUFpRSxTQUFBRCx1QkFBQVMsQ0FBQSxXQUFBQSxDQUFBLElBQUFBLENBQUEsQ0FBQUMsVUFBQSxHQUFBRCxDQUFB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