UNPKG

verdaccio

Version:

A lightweight private npm proxy registry

489 lines (464 loc) 60 kB
"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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