projen
Version:
CDK for software projects
298 lines • 36 kB
JavaScript
;
Object.defineProperty(exports, "__esModule", { value: true });
exports.codeArtifactRegex = void 0;
exports.renderBundleName = renderBundleName;
exports.extractCodeArtifactDetails = extractCodeArtifactDetails;
exports.minVersion = minVersion;
exports.tryResolveModule = tryResolveModule;
exports.tryResolveModuleManifestPath = tryResolveModuleManifestPath;
exports.tryResolveManifestPathFromDefaultExport = tryResolveManifestPathFromDefaultExport;
exports.tryResolveManifestPathFromPath = tryResolveManifestPathFromPath;
exports.tryResolveManifestPathFromSearch = tryResolveManifestPathFromSearch;
exports.tryResolveModuleManifest = tryResolveModuleManifest;
exports.tryResolveDependencyVersion = tryResolveDependencyVersion;
exports.hasDependencyVersion = hasDependencyVersion;
exports.installedVersionProbablyMatches = installedVersionProbablyMatches;
const fs_1 = require("fs");
const path_1 = require("path");
const semver = require("semver");
const node_package_1 = require("./node-package");
const util_1 = require("../util");
function renderBundleName(entrypoint) {
const parts = (0, path_1.join)(entrypoint).split(path_1.sep);
if (parts[0] === "src") {
parts.shift(); // just remove 'src' if its the first element for ergonomics
}
const p = parts.join(path_1.posix.sep);
const dir = (0, path_1.dirname)(p);
const base = (0, path_1.basename)(p, (0, path_1.extname)(p));
return path_1.posix.join(dir, base);
}
/**
* Regex for AWS CodeArtifact registry
*/
exports.codeArtifactRegex = /^https:\/\/(?<registry>(?<domain>[^\.]+)-(?<accountId>\d{12})\.d\.codeartifact\.(?<region>[^\.]+).*\.amazonaws\.com\/.*\/(?<repository>[^\/.]+)\/)/;
/**
* gets AWS details from the Code Artifact registry URL
* throws exception if not matching expected pattern
* @param registryUrl Code Artifact registry URL
* @returns object containing the (domain, accountId, region, repository)
*/
function extractCodeArtifactDetails(registryUrl) {
const match = registryUrl.match(exports.codeArtifactRegex);
if (match?.groups) {
const { domain, accountId, region, repository, registry } = match.groups;
return { domain, accountId, region, repository, registry };
}
throw new Error("Could not get CodeArtifact details from npm Registry");
}
function minVersion(version) {
if (semver.validRange(version)) {
return semver.minVersion(version)?.version;
}
else {
return version;
}
}
/**
* Attempt to resolve location of the given `moduleId`.
* @param moduleId Module ID to lookup.
* @param options Passed through to `require.resolve`.
*/
function tryResolveModule(moduleId, options) {
try {
return require.resolve(moduleId, options);
}
catch {
return undefined;
}
}
/**
* Attempt to resolve a module's manifest (package.json) path via `require.resolve` lookup.
*
* @remarks
* If the target package has `exports` that differ from the default
* (i.e, it defines the `exports` field in its manifest) and does not
* explicitly include an entry for `package.json`, this strategy will fail.
* See {@link tryResolveManifestPathFromDefaultExport} as an alternative.
*
* @param moduleId Module ID to lookup.
* @param options Passed through to `require.resolve`.
*/
function tryResolveModuleManifestPath(moduleId, options) {
// cannot just `require('dependency/package.json')` here because
// `options.paths` may not overlap with this node proc's resolution paths.
const manifestId = `${moduleId}/package.json`;
return tryResolveModule(manifestId, options);
}
/**
* Attempt to resolve a module's manifest (package.json) path by looking for the nearest
* `package.json` file that is an ancestor to the module's default export location.
*
* @param moduleId Module ID to lookup.
* @param options Passed through to `require.resolve`.
*/
function tryResolveManifestPathFromDefaultExport(moduleId, options) {
const defaultExportPath = tryResolveModule(moduleId, options);
if (!defaultExportPath) {
return undefined;
}
const moduleDir = (0, util_1.findUp)("package.json", defaultExportPath);
if (!moduleDir) {
return undefined;
}
return (0, path_1.join)(moduleDir, "package.json");
}
/**
* Attempt to resolve a module's manifest (package.json) path by checking for its existence under `node_modules` relative to `basePath`.
*
* @remarks
* This strategy can be helpful in the scenario that a module defines
* custom exports without `package.json` and no default export (i.e, some type definition packages).
*
* @param moduleId Module ID to lookup.
* @param basePath Root path to search from.
*/
function tryResolveManifestPathFromPath(moduleId, basePath) {
const base = basePath.includes("node_modules")
? basePath
: (0, path_1.join)(basePath, "node_modules");
const filePath = (0, path_1.resolve)(base, ...moduleId.split("/"), "package.json");
if ((0, fs_1.existsSync)(filePath)) {
return filePath;
}
return undefined;
}
/**
* Attempt to resolve a module's manifest (package.json) path by searching for it in the optionally provided paths array
* as well as the current node processes' default resolution paths.
* @param moduleId Module ID to search for.
* @param options Search options.
*/
function tryResolveManifestPathFromSearch(moduleId, options) {
const searchPaths = [
...(options?.paths ?? []),
...(require.resolve.paths(moduleId) ?? []),
];
for (const path of searchPaths) {
const result = tryResolveManifestPathFromPath(moduleId, path);
// early return on first result.
if (result) {
return result;
}
}
return undefined;
}
/**
* Attempt to resolve a module's manifest (package.json) using multiple strategies.
* @param moduleId Module to resolve manifest path for.
* @param options Resolution options.
*/
function tryResolveModuleManifest(moduleId, options) {
const strategies = [
tryResolveModuleManifestPath,
tryResolveManifestPathFromDefaultExport,
tryResolveManifestPathFromSearch,
];
for (const strategy of strategies) {
const result = strategy(moduleId, options);
// early return on first result.
if (result) {
try {
const manifest = JSON.parse((0, fs_1.readFileSync)(result, "utf8"));
// verify name matches target module.
if (manifest.name === moduleId) {
return manifest;
}
}
catch {
// continue to next strategy.
}
}
}
return undefined;
}
/**
* Attempt to resolve the installed version of a given dependency.
* @param dependencyName Name of dependency.
* @param options Optional options passed through to `require.resolve`.
*/
function tryResolveDependencyVersion(dependencyName, options) {
const manifest = tryResolveModuleManifest(dependencyName, options);
if (!manifest) {
return undefined;
}
return manifest?.version;
}
/**
* Whether the given dependency version is installed
*
* This can be used to test for the presence of certain versions of devDependencies,
* and do something dependency-specific in certain Components. For example, test for
* a version of Jest and generate different configs based on the Jest version.
*
* NOTE: The implementation of this function currently is currently
* approximate: to do it correctly, we would need a separate implementation
* for every package manager, to query its installed version (either that, or we
* would code to query `package-lock.json`, `yarn.lock`, etc...).
*
* Instead, we will look at `package.json`, and assume that the versions
* picked by the package manager match ~that. This will work well enough for
* major version checks, but may fail for point versions.
*
* What we SHOULD do is: `actualVersion ∈ checkRange`.
*
* What we do instead is a slightly more sophisticated version of
* `requestedRange ∩ checkRange != ∅`. This will always give a correct result if
* `requestedRange ⊆ checkRange`, but may give false positives when `checkRange
* ⊆ requestedRange`.
*
* May return `undefined` if the question cannot be answered. These include the
* following cases:
*
* - The dependency is requested via local file dependencies (`file://...`)
* - The dependency uses an other type of URL, such as a GitHub URL
* - The dependency is not found in the `package.json`, such as when
* bootstrapping from an external projen package, and the `package.json`
* file only has that one external package as a dependency
*
* Otherwise it will return `true` if the installed version is probably in the
* requested range, and `false` if it is probably not.
*
* This API may eventually be added to the public projen API, but only after
* we implement exact version checking.
*
* @param dependencyName The name of the dependency
* @param checkRange A particular version, or range of versions.
*/
function hasDependencyVersion(project, dependencyName, checkRange) {
const file = node_package_1.NodePackage.of(project)?.file;
if (!file) {
return undefined;
}
if (!(0, fs_1.existsSync)(file.absolutePath)) {
return undefined;
}
const pj = JSON.parse((0, fs_1.readFileSync)(file.absolutePath, "utf-8"));
// Technicaly, we should be intersecting all ranges to come up with the most narrow dependency
// range, but `semver` doesn't allow doing that and we don't want to add a dependency on `semver-intersect`.
//
// Let's take the first dependency declaration we find, and assume that people
// set up their `package.json` correctly.
let requestedRange;
for (const key of ["dependencies", "devDependencies", "peerDependencies"]) {
const deps = pj[key] ?? {};
let requestedVersion = deps[dependencyName];
if (requestedVersion) {
// If this is not a valid range, it could be 'file:dep.tgz', or a GitHub URL. No way to know what
// version we're getting, bail out.
if (!semver.validRange(requestedVersion)) {
return undefined;
}
requestedRange = requestedVersion;
break;
}
}
// If the dependency is not found in the `package.json`, we can't answer the question (yet).
// This means that the dependency hasn't been added yet, which means we know (upstream) what we're going to request,
// or we're going to ask for '*' and we'll get the latest version.
if (!requestedRange) {
return undefined;
}
return installedVersionProbablyMatches(requestedRange, checkRange);
}
/**
* Whether the given requestedRange *probably* leads to the installation of a version that matches checkRange
*
* We assume that NPM always installs the most recent version of a package that
* is allowed by the requestedRange.
*/
function installedVersionProbablyMatches(requestedRange, checkRange) {
const options = {
includePrerelease: true,
loose: true,
};
// No questions asked: always true
if (semver.subset(requestedRange, checkRange, options)) {
return true;
}
// Also no questions asked: always false
if (!semver.intersects(requestedRange, checkRange, options)) {
return false;
}
// Now we're in tricky territory. We intersect, but aren't a full subset.
// We are in one of the following 2 situations, which we will tie-break by
// assuming NPM will install the most recent matching version in 'requested'.
//
// requested | check | result
// -----------|----------|-----------
// >= 2 | >= 3 | probably true (chance of FP)
// <= 2 | <= 1 | probably false (change of FN)
//
// `semver` doesn't make it easy to distinguish these cases (we can't request
// the `maxVersion` that satisfies a range). Instead what we do is
// get the `minVersion` of each range, and if they compare equal we assume
// we're in the bottom case with `<=` checks, and return `false`.
return !semver.eq(semver.minVersion(requestedRange, options) ?? "1.2.3", semver.minVersion(checkRange, options) ?? "1.2.3");
}
//# sourceMappingURL=data:application/json;base64,eyJ2ZXJzaW9uIjozLCJmaWxlIjoidXRpbC5qcyIsInNvdXJjZVJvb3QiOiIiLCJzb3VyY2VzIjpbIi4uLy4uL3NyYy9qYXZhc2NyaXB0L3V0aWwudHMiXSwibmFtZXMiOltdLCJtYXBwaW5ncyI6Ijs7O0FBdUJBLDRDQVVDO0FBY0QsZ0VBT0M7QUFFRCxnQ0FNQztBQU9ELDRDQVNDO0FBY0Qsb0VBUUM7QUFTRCwwRkFhQztBQVlELHdFQVlDO0FBUUQsNEVBZ0JDO0FBT0QsNERBMkJDO0FBT0Qsa0VBU0M7QUEyQ0Qsb0RBNENDO0FBUUQsMEVBcUNDO0FBaFdELDJCQUE4QztBQUM5QywrQkFBNkU7QUFDN0UsaUNBQWlDO0FBQ2pDLGlEQUE2QztBQUU3QyxrQ0FBaUM7QUFrQmpDLFNBQWdCLGdCQUFnQixDQUFDLFVBQWtCO0lBQ2pELE1BQU0sS0FBSyxHQUFHLElBQUEsV0FBSSxFQUFDLFVBQVUsQ0FBQyxDQUFDLEtBQUssQ0FBQyxVQUFHLENBQUMsQ0FBQztJQUMxQyxJQUFJLEtBQUssQ0FBQyxDQUFDLENBQUMsS0FBSyxLQUFLLEVBQUUsQ0FBQztRQUN2QixLQUFLLENBQUMsS0FBSyxFQUFFLENBQUMsQ0FBQyw0REFBNEQ7SUFDN0UsQ0FBQztJQUVELE1BQU0sQ0FBQyxHQUFHLEtBQUssQ0FBQyxJQUFJLENBQUMsWUFBSyxDQUFDLEdBQUcsQ0FBQyxDQUFDO0lBQ2hDLE1BQU0sR0FBRyxHQUFHLElBQUEsY0FBTyxFQUFDLENBQUMsQ0FBQyxDQUFDO0lBQ3ZCLE1BQU0sSUFBSSxHQUFHLElBQUEsZUFBUSxFQUFDLENBQUMsRUFBRSxJQUFBLGNBQU8sRUFBQyxDQUFDLENBQUMsQ0FBQyxDQUFDO0lBQ3JDLE9BQU8sWUFBSyxDQUFDLElBQUksQ0FBQyxHQUFHLEVBQUUsSUFBSSxDQUFDLENBQUM7QUFDL0IsQ0FBQztBQUVEOztHQUVHO0FBQ1UsUUFBQSxpQkFBaUIsR0FDNUIsb0pBQW9KLENBQUM7QUFFdko7Ozs7O0dBS0c7QUFDSCxTQUFnQiwwQkFBMEIsQ0FBQyxXQUFtQjtJQUM1RCxNQUFNLEtBQUss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LENBQUMsVUFBVSxDQUFDLFVBQVUsRUFBRSxPQUFPLENBQUMsSUFBSSxPQUFPLENBQ2xELENBQUM7QUFDSixDQUFDIiwic291cmNlc0NvbnRlbnQiOlsiaW1wb3J0IHsgZXhpc3RzU3luYywgcmVhZEZpbGVTeW5jIH0gZnJvbSBcImZzXCI7XG5pbXBvcnQgeyBiYXNlbmFtZSwgZGlybmFtZSwgZXh0bmFtZSwgam9pbiwgc2VwLCByZXNvbHZlLCBwb3NpeCB9IGZyb20gXCJwYXRoXCI7XG5pbXBvcnQgKiBhcyBzZW12ZXIgZnJvbSBcInNlbXZlclwiO1xuaW1wb3J0IHsgTm9kZVBhY2thZ2UgfSBmcm9tIFwiLi9ub2RlLXBhY2thZ2VcIjtcbmltcG9ydCB7IFByb2plY3QgfSBmcm9tIFwiLi4vcHJvamVjdFwiO1xuaW1wb3J0IHsgZmluZFVwIH0gZnJvbSBcIi4uL3V0aWxcIjtcblxuLyoqXG4gKiBCYXNpYyBpbnRlcmZhY2UgZm9yIGBwYWNrYWdlLmpzb25gLlxuICovXG5pbnRlcmZhY2UgUGFja2FnZU1hbmlmZXN0IHtcbiAgW2tleTogc3RyaW5nXTogYW55O1xuXG4gIC8qKlxuICAgKiBQYWNrYWdlIG5hbWUuXG4gICAqL1xuICBuYW1lOiBzdHJpbmc7XG4gIC8qKlxuICAgKiBQYWNrYWdlIHZlcnNpb24uXG4gICAqL1xuICB2ZXJzaW9uOiBzdHJpbmc7XG59XG5cbmV4cG9ydCBmdW5jdGlvbiByZW5kZXJCdW5kbGVOYW1lKGVudHJ5cG9pbnQ6IHN0cmluZykge1xuICBjb25zdCBwYXJ0cyA9IGpvaW4oZW50cnlwb2ludCkuc3BsaXQoc2VwKTtcbiAgaWYgKHBhcnRzWzBdID09PSBcInNyY1wiKSB7XG4gICAgcGFydHMuc2hpZnQoKTsgLy8ganVzdCByZW1vdmUgJ3NyYycgaWYgaXRzIHRoZSBmaXJzdCBlbGVtZW50IGZvciBlcmdvbm9taWNzXG4gIH1cblxuICBjb25zdCBwID0gcGFydHMuam9pbihwb3NpeC5zZXApO1xuICBjb25zdCBkaXIgPSBkaXJuYW1lKHApO1xuICBjb25zdCBiYXNlID0gYmFzZW5hbWUocCwgZXh0bmFtZShwKSk7XG4gIHJldHVybiBwb3NpeC5qb2luKGRpciwgYmFzZSk7XG59XG5cbi8qKlxuICogUmVnZXggZm9yIEFXUyBDb2RlQXJ0aWZhY3QgcmVnaXN0cnlcbiAqL1xuZXhwb3J0IGNvbnN0IGNvZGVBcnRpZmFjdFJlZ2V4ID1cbiAgL15odHRwczpcXC9cXC8oPzxyZWdpc3RyeT4oPzxkb21haW4+W15cXC5dKyktKD88YWNjb3VudElkPlxcZHsxMn0pXFwuZFxcLmNvZGVhcnRpZmFjdFxcLig/PHJlZ2lvbj5bXlxcLl0rKS4qXFwuYW1hem9uYXdzXFwuY29tXFwvLipcXC8oPzxyZXBvc2l0b3J5PlteXFwvLl0rKVxcLykvO1xuXG4vKipcbiAqIGdldHMgQVdTIGRldGFpbHMgZnJvbSB0aGUgQ29kZSBBcnRpZmFjdCByZWdpc3RyeSBVUkxcbiAqIHRocm93cyBleGNlcHRpb24gaWYgbm90IG1hdGNoaW5nIGV4cGVjdGVkIHBhdHRlcm5cbiAqIEBwYXJhbSByZWdpc3RyeVVybCBDb2RlIEFydGlmYWN0IHJlZ2lzdHJ5IFVSTFxuICogQHJldHVybnMgb2JqZWN0IGNvbnRhaW5pbmcgdGhlIChkb21haW4sIGFjY291bnRJZCwgcmVnaW9uLCByZXBvc2l0b3J5KVxuICovXG5leHBvcnQgZnVuY3Rpb24gZXh0cmFjdENvZGVBcnRpZmFjdERldGFpbHMocmVnaXN0cnlVcmw6IHN0cmluZykge1xuICBjb25zdCBtYXRjaCA9IHJlZ2lzdHJ5VXJsLm1hdGNoKGNvZGVBcnRpZmFjdFJlZ2V4KTtcbiAgaWYgKG1hdGNoPy5ncm91cHMpIHtcbiAgICBjb25zdCB7IGRvbWFpbiwgYWNjb3VudElkLCByZWdpb24sIHJlcG9zaXRvcnksIHJlZ2lzdHJ5IH0gPSBtYXRjaC5ncm91cHM7XG4gICAgcmV0dXJuIHsgZG9tYWluLCBhY2NvdW50SWQsIHJlZ2lvbiwgcmVwb3NpdG9yeSwgcmVnaXN0cnkgfTtcbiAgfVxuICB0aHJvdyBuZXcgRXJyb3IoXCJDb3VsZCBub3QgZ2V0IENvZGVBcnRpZmFjdCBkZXRhaWxzIGZyb20gbnBtIFJlZ2lzdHJ5XCIpO1xufVxuXG5leHBvcnQgZnVuY3Rpb24gbWluVmVyc2lvbih2ZXJzaW9uOiBzdHJpbmcpOiBzdHJpbmcgfCB1bmRlZmluZWQge1xuICBpZiAoc2VtdmVyLnZhbGlkUmFuZ2UodmVyc2lvbikpIHtcbiAgICByZXR1cm4gc2VtdmVyLm1pblZlcnNpb24odmVyc2lvbik/LnZlcnNpb247XG4gIH0gZWxzZSB7XG4gICAgcmV0dXJuIHZlcnNpb247XG4gIH1cbn1cblxuLyoqXG4gKiBBdHRlbXB0IHRvIHJlc29sdmUgbG9jYXRpb24gb2YgdGhlIGdpdmVuIGBtb2R1bGVJZGAuXG4gKiBAcGFyYW0gbW9kdWxlSWQgTW9kdWxlIElEIHRvIGxvb2t1cC5cbiAqIEBwYXJhbSBvcHRpb25zIFBhc3NlZCB0aHJvdWdoIHRvIGByZXF1aXJlLnJlc29sdmVgLlxuICovXG5leHBvcnQgZnVuY3Rpb24gdHJ5UmVzb2x2ZU1vZHVsZShcbiAgbW9kdWxlSWQ6IHN0cmluZyxcbiAgb3B0aW9ucz86IHsgcGF0aHM6IHN0cmluZ1tdIH1cbik6IHN0cmluZyB8IHVuZGVmaW5lZCB7XG4gIHRyeSB7XG4gICAgcmV0dXJuIHJlcXVpcmUucmVzb2x2ZShtb2R1bGVJZCwgb3B0aW9ucyk7XG4gIH0gY2F0Y2gge1xuICAgIHJldHVybiB1bmRlZmluZWQ7XG4gIH1cbn1cblxuLyoqXG4gKiBBdHRlbXB0IHRvIHJlc29sdmUgYSBtb2R1bGUncyBtYW5pZmVzdCAocGFja2FnZS5qc29uKSBwYXRoIHZpYSBgcmVxdWlyZS5yZXNvbHZlYCBsb29rdXAuXG4gKlxuICogQHJlbWFya3NcbiAqIElmIHRoZSB0YXJnZXQgcGFja2FnZSBoYXMgYGV4cG9ydHNgIHRoYXQgZGlmZmVyIGZyb20gdGhlIGRlZmF1bHRcbiAqIChpLmUsIGl0IGRlZmluZXMgdGhlIGBleHBvcnRzYCBmaWVsZCBpbiBpdHMgbWFuaWZlc3QpIGFuZCBkb2VzIG5vdFxuICogZXhwbGljaXRseSBpbmNsdWRlIGFuIGVudHJ5IGZvciBgcGFja2FnZS5qc29uYCwgdGhpcyBzdHJhdGVneSB3aWxsIGZhaWwuXG4gKiBTZWUge0BsaW5rIHRyeVJlc29sdmVNYW5pZmVzdFBhdGhGcm9tRGVmYXVsdEV4cG9ydH0gYXMgYW4gYWx0ZXJuYXRpdmUuXG4gKlxuICogQHBhcmFtIG1vZHVsZUlkIE1vZHVsZSBJRCB0byBsb29rdXAuXG4gKiBAcGFyYW0gb3B0aW9ucyBQYXNzZWQgdGhyb3VnaCB0byBgcmVxdWlyZS5yZXNvbHZlYC5cbiAqL1xuZXhwb3J0IGZ1bmN0aW9uIHRyeVJlc29sdmVNb2R1bGVNYW5pZmVzdFBhdGgoXG4gIG1vZHVsZUlkOiBzdHJpbmcsXG4gIG9wdGlvbnM/OiB7IHBhdGhzOiBzdHJpbmdbXSB9XG4pOiBzdHJpbmcgfCB1bmRlZmluZWQge1xuICAvLyBjYW5ub3QganVzdCBgcmVxdWlyZSgnZGVwZW5kZW5jeS9wYWNrYWdlLmpzb24nKWAgaGVyZSBiZWNhdXNlXG4gIC8vIGBvcHRpb25zLnBhdGhzYCBtYXkgbm90IG92ZXJsYXAgd2l0aCB0aGlzIG5vZGUgcHJvYydzIHJlc29sdXRpb24gcGF0aHMuXG4gIGNvbnN0IG1hbmlmZXN0SWQgPSBgJHttb2R1bGVJZH0vcGFja2FnZS5qc29uYDtcbiAgcmV0dXJuIHRyeVJlc29sdmVNb2R1bGUobWFuaWZlc3RJZCwgb3B0aW9ucyk7XG59XG5cbi8qKlxuICogQXR0ZW1wdCB0byByZXNvbHZlIGEgbW9kdWxlJ3MgbWFuaWZlc3QgKHBhY2thZ2UuanNvbikgcGF0aCBieSBsb29raW5nIGZvciB0aGUgbmVhcmVzdFxuICogYHBhY2thZ2UuanNvbmAgZmlsZSB0aGF0IGlzIGFuIGFuY2VzdG9yIHRvIHRoZSBtb2R1bGUncyBkZWZhdWx0IGV4cG9ydCBsb2NhdGlvbi5cbiAqXG4gKiBAcGFyYW0gbW9kdWxlSWQgTW9kdWxlIElEIHRvIGxvb2t1cC5cbiAqIEBwYXJhbSBvcHRpb25zIFBhc3NlZCB0aHJvdWdoIHRvIGByZXF1aXJlLnJlc29sdmVgLlxuICovXG5leHBvcnQgZnVuY3Rpb24gdHJ5UmVzb2x2ZU1hbmlmZXN0UGF0aEZyb21EZWZhdWx0RXhwb3J0KFxuICBtb2R1bGVJZDogc3RyaW5nLFxuICBvcHRpb25zPzogeyBwYXRoczogc3RyaW5nW10gfVxuKTogc3RyaW5nIHwgdW5kZWZpbmVkIHtcbiAgY29uc3QgZGVmYXVsdEV4cG9ydFBhdGggPSB0cnlSZXNvbHZlTW9kdWxlKG1vZHVsZUlkLCBvcHRpb25zKTtcbiAgaWYgKCFkZWZhdWx0RXhwb3J0UGF0aCkge1xuICAgIHJldHVybiB1bmRlZmluZWQ7XG4gIH1cbiAgY29uc3QgbW9kdWxlRGlyID0gZmluZFVwKFwicGFja2FnZS5qc29uXCIsIGRlZmF1bHRFeHBvcnRQYXRoKTtcbiAgaWYgKCFtb2R1bGVEaXIpIHtcbiAgICByZXR1cm4gdW5kZWZpbmVkO1xuICB9XG4gIHJldHVybiBqb2luKG1vZHVsZURpciwgXCJwYWNrYWdlLmpzb25cIik7XG59XG5cbi8qKlxuICogQXR0ZW1wdCB0byByZXNvbHZlIGEgbW9kdWxlJ3MgbWFuaWZlc3QgKHBhY2thZ2UuanNvbikgcGF0aCBieSBjaGVja2luZyBmb3IgaXRzIGV4aXN0ZW5jZSB1bmRlciBgbm9kZV9tb2R1bGVzYCByZWxhdGl2ZSB0byBgYmFzZVBhdGhgLlxuICpcbiAqIEByZW1hcmtzXG4gKiBUaGlzIHN0cmF0ZWd5IGNhbiBiZSBoZWxwZnVsIGluIHRoZSBzY2VuYXJpbyB0aGF0IGEgbW9kdWxlIGRlZmluZXNcbiAqIGN1c3RvbSBleHBvcnRzIHdpdGhvdXQgYHBhY2thZ2UuanNvbmAgYW5kIG5vIGRlZmF1bHQgZXhwb3J0IChpLmUsIHNvbWUgdHlwZSBkZWZpbml0aW9uIHBhY2thZ2VzKS5cbiAqXG4gKiBAcGFyYW0gbW9kdWxlSWQgTW9kdWxlIElEIHRvIGxvb2t1cC5cbiAqIEBwYXJhbSBiYXNlUGF0aCBSb290IHBhdGggdG8gc2VhcmNoIGZyb20uXG4gKi9cbmV4cG9ydCBmdW5jdGlvbiB0cnlSZXNvbHZlTWFuaWZlc3RQYXRoRnJvbVBhdGgoXG4gIG1vZHVsZUlkOiBzdHJpbmcsXG4gIGJhc2VQYXRoOiBzdHJpbmdcbikge1xuICBjb25zdCBiYXNlID0gYmFzZVBhdGguaW5jbHVkZXMoXCJub2RlX21vZHVsZXNcIilcbiAgICA/IGJhc2VQYXRoXG4gICAgOiBqb2luKGJhc2VQYXRoLCBcIm5vZGVfbW9kdWxlc1wiKTtcbiAgY29uc3QgZmlsZVBhdGggPSByZXNvbHZlKGJhc2UsIC4uLm1vZHVsZUlkLnNwbGl0KFwiL1wiKSwgXCJwYWNrYWdlLmpzb25cIik7XG4gIGlmIChleGlzdHNTeW5jKGZpbGVQYXRoKSkge1xuICAgIHJldHVybiBmaWxlUGF0aDtcbiAgfVxuICByZXR1cm4gdW5kZWZpbmVkO1xufVxuXG4vKipcbiAqIEF0dGVtcHQgdG8gcmVzb2x2ZSBhIG1vZHVsZSdzIG1hbmlmZXN0IChwYWNrYWdlLmpzb24pIHBhdGggYnkgc2VhcmNoaW5nIGZvciBpdCBpbiB0aGUgb3B0aW9uYWxseSBwcm92aWRlZCBwYXRocyBhcnJheVxuICogYXMgd2VsbCBhcyB0aGUgY3VycmVudCBub2RlIHByb2Nlc3NlcycgZGVmYXVsdCByZXNvbHV0aW9uIHBhdGhzLlxuICogQHBhcmFtIG1vZHVsZUlkIE1vZHVsZSBJRCB0byBzZWFyY2ggZm9yLlxuICogQHBhcmFtIG9wdGlvbnMgU2VhcmNoIG9wdGlvbnMuXG4gKi9cbmV4cG9ydCBmdW5jdGlvbiB0cnlSZXNvbHZlTWFuaWZlc3RQYXRoRnJvbVNlYXJjaChcbiAgbW9kdWxlSWQ6IHN0cmluZyxcbiAgb3B0aW9ucz86IHsgcGF0aHM6IHN0cmluZ1tdIH1cbik6IHN0cmluZyB8IHVuZGVmaW5lZCB7XG4gIGNvbnN0IHNlYXJjaFBhdGhzID0gW1xuICAgIC4uLihvcHRpb25zPy5wYXRocyA/PyBbXSksXG4gICAgLi4uKHJlcXVpcmUucmVzb2x2ZS5wYXRocyhtb2R1bGVJZCkgPz8gW10pLFxuICBdO1xuICBmb3IgKGNvbnN0IHBhdGggb2Ygc2VhcmNoUGF0aHMpIHtcbiAgICBjb25zdCByZXN1bHQgPSB0cnlSZXNvbHZlTWFuaWZlc3RQYXRoRnJvbVBhdGgobW9kdWxlSWQsIHBhdGgpO1xuICAgIC8vIGVhcmx5IHJldHVybiBvbiBmaXJzdCByZXN1bHQuXG4gICAgaWYgKHJlc3VsdCkge1xuICAgICAgcmV0dXJuIHJlc3VsdDtcbiAgICB9XG4gIH1cbiAgcmV0dXJuIHVuZGVmaW5lZDtcbn1cblxuLyoqXG4gKiBBdHRlbXB0IHRvIHJlc29sdmUgYSBtb2R1bGUncyBtYW5pZmVzdCAocGFja2FnZS5qc29uKSB1c2luZyBtdWx0aXBsZSBzdHJhdGVnaWVzLlxuICogQHBhcmFtIG1vZHVsZUlkIE1vZHVsZSB0byByZXNvbHZlIG1hbmlmZXN0IHBhdGggZm9yLlxuICogQHBhcmFtIG9wdGlvbnMgUmVzb2x1dGlvbiBvcHRpb25zLlxuICovXG5leHBvcnQgZnVuY3Rpb24gdHJ5UmVzb2x2ZU1vZHVsZU1hbmlmZXN0KFxuICBtb2R1bGVJZDogc3RyaW5nLFxuICBvcHRpb25zPzogeyBwYXRoczogc3RyaW5nW10gfVxuKTogUGFja2FnZU1hbmlmZXN0IHwgdW5kZWZpbmVkIHtcbiAgY29uc3Qgc3RyYXRlZ2llcyA9IFtcbiAgICB0cnlSZXNvbHZlTW9kdWxlTWFuaWZlc3RQYXRoLFxuICAgIHRyeVJlc29sdmVNYW5pZmVzdFBhdGhGcm9tRGVmYXVsdEV4cG9ydCxcbiAgICB0cnlSZXNvbHZlTWFuaWZlc3RQYXRoRnJvbVNlYXJjaCxcbiAgXTtcbiAgZm9yIChjb25zdCBzdHJhdGVneSBvZiBzdHJhdGVnaWVzKSB7XG4gICAgY29uc3QgcmVzdWx0ID0gc3RyYXRlZ3kobW9kdWxlSWQsIG9wdGlvbnMpO1xuICAgIC8vIGVhcmx5IHJldHVybiBvbiBmaXJzdCByZXN1bHQuXG4gICAgaWYgKHJlc3VsdCkge1xuICAgICAgdHJ5IHtcbiAgICAgICAgY29uc3QgbWFuaWZlc3QgPSBKU09OLnBhcnNlKFxuICAgICAgICAgIHJlYWRGaWxlU3luYyhyZXN1bHQsIFwidXRmOFwiKVxuICAgICAgICApIGFzIFBhY2thZ2VNYW5pZmVzdDtcbiAgICAgICAgLy8gdmVyaWZ5IG5hbWUgbWF0Y2hlcyB0YXJnZXQgbW9kdWxlLlxuICAgICAgICBpZiAobWFuaWZlc3QubmFtZSA9PT0gbW9kdWxlSWQpIHtcbiAgICAgICAgICByZXR1cm4gbWFuaWZlc3Q7XG4gICAgICAgIH1cbiAgICAgIH0gY2F0Y2gge1xuICAgICAgICAvLyBjb250aW51ZSB0byBuZXh0IHN0cmF0ZWd5LlxuICAgICAgfVxuICAgIH1cbiAgfVxuICByZXR1cm4gdW5kZWZpbmVkO1xufVxuXG4vKipcbiAqIEF0dGVtcHQgdG8gcmVzb2x2ZSB0aGUgaW5zdGFsbGVkIHZlcnNpb24gb2YgYSBnaXZlbiBkZXBlbmRlbmN5LlxuICogQHBhcmFtIGRlcGVuZGVuY3lOYW1lIE5hbWUgb2YgZGVwZW5kZW5jeS5cbiAqIEBwYXJhbSBvcHRpb25zIE9wdGlvbmFsIG9wdGlvbnMgcGFzc2VkIHRocm91Z2ggdG8gYHJlcXVpcmUucmVzb2x2ZWAuXG4gKi9cbmV4cG9ydCBmdW5jdGlvbiB0cnlSZXNvbHZlRGVwZW5kZW5jeVZlcnNpb24oXG4gIGRlcGVuZGVuY3lOYW1lOiBzdHJpbmcsXG4gIG9wdGlvbnM/OiB7IHBhdGhzOiBzdHJpbmdbXSB9XG4pOiBzdHJpbmcgfCB1bmRlZmluZWQge1xuICBjb25zdCBtYW5pZmVzdCA9IHRyeVJlc29sdmVNb2R1bGVNYW5pZmVzdChkZXBlbmRlbmN5TmFtZSwgb3B0aW9ucyk7XG4gIGlmICghbWFuaWZlc3QpIHtcbiAgICByZXR1cm4gdW5kZWZpbmVkO1xuICB9XG4gIHJldHVybiBtYW5pZmVzdD8udmVyc2lvbjtcbn1cblxuLyoqXG4gKiBXaGV0aGVyIHRoZSBnaXZlbiBkZXBlbmRlbmN5IHZlcnNpb24gaXMgaW5zdGFsbGVkXG4gKlxuICogVGhpcyBjYW4gYmUgdXNlZCB0byB0ZXN0IGZvciB0aGUgcHJlc2VuY2Ugb2YgY2VydGFpbiB2ZXJzaW9ucyBvZiBkZXZEZXBlbmRlbmNpZXMsXG4gKiBhbmQgZG8gc29tZXRoaW5nIGRlcGVuZGVuY3ktc3BlY2lmaWMgaW4gY2VydGFpbiBDb21wb25lbnRzLiBGb3IgZXhhbXBsZSwgdGVzdCBmb3JcbiAqIGEgdmVyc2lvbiBvZiBKZXN0IGFuZCBnZW5lcmF0ZSBkaWZmZXJlbnQgY29uZmlncyBiYXNlZCBvbiB0aGUgSmVzdCB2ZXJzaW9uLlxuICpcbiAqIE5PVEU6IFRoZSBpbXBsZW1lbnRhdGlvbiBvZiB0aGlzIGZ1bmN0aW9uIGN1cnJlbnRseSBpcyBjdXJyZW50bHlcbiAqIGFwcHJveGltYXRlOiB0byBkbyBpdCBjb3JyZWN0bHksIHdlIHdvdWxkIG5lZWQgYSBzZXBhcmF0ZSBpbXBsZW1lbnRhdGlvblxuICogZm9yIGV2ZXJ5IHBhY2thZ2UgbWFuYWdlciwgdG8gcXVlcnkgaXRzIGluc3RhbGxlZCB2ZXJzaW9uIChlaXRoZXIgdGhhdCwgb3Igd2VcbiAqIHdvdWxkIGNvZGUgdG8gcXVlcnkgYHBhY2thZ2UtbG9jay5qc29uYCwgYHlhcm4ubG9ja2AsIGV0Yy4uLikuXG4gKlxuICogSW5zdGVhZCwgd2Ugd2lsbCBsb29rIGF0IGBwYWNrYWdlLmpzb25gLCBhbmQgYXNzdW1lIHRoYXQgdGhlIHZlcnNpb25zXG4gKiBwaWNrZWQgYnkgdGhlIHBhY2thZ2UgbWFuYWdlciBtYXRjaCB+dGhhdC4gVGhpcyB3aWxsIHdvcmsgd2VsbCBlbm91Z2ggZm9yXG4gKiBtYWpvciB2ZXJzaW9uIGNoZWNrcywgYnV0IG1heSBmYWlsIGZvciBwb2ludCB2ZXJzaW9ucy5cbiAqXG4gKiBXaGF0IHdlIFNIT1VMRCBkbyBpczogYGFjdHVhbFZlcnNpb24g4oiIIGNoZWNrUmFuZ2VgLlxuICpcbiAqIFdoYXQgd2UgZG8gaW5zdGVhZCBpcyBhIHNsaWdodGx5IG1vcmUgc29waGlzdGljYXRlZCB2ZXJzaW9uIG9mXG4gKiBgcmVxdWVzdGVkUmFuZ2Ug4oipIGNoZWNrUmFuZ2UgIT0g4oiFYC4gVGhpcyB3aWxsIGFsd2F5cyBnaXZlIGEgY29ycmVjdCByZXN1bHQgaWZcbiAqIGByZXF1ZXN0ZWRSYW5nZSDiioYgY2hlY2tSYW5nZWAsIGJ1dCBtYXkgZ2l2ZSBmYWxzZSBwb3NpdGl2ZXMgd2hlbiBgY2hlY2tSYW5nZVxuICog4oqGIHJlcXVlc3RlZFJhbmdlYC5cbiAqXG4gKiBNYXkgcmV0dXJuIGB1bmRlZmluZWRgIGlmIHRoZSBxdWVzdGlvbiBjYW5ub3QgYmUgYW5zd2VyZWQuIFRoZXNlIGluY2x1ZGUgdGhlXG4gKiBmb2xsb3dpbmcgY2FzZXM6XG4gKlxuICogICAtIFRoZSBkZXBlbmRlbmN5IGlzIHJlcXVlc3RlZCB2aWEgbG9jYWwgZmlsZSBkZXBlbmRlbmNpZXMgKGBmaWxlOi8vLi4uYClcbiAqICAgLSBUaGUgZGVwZW5kZW5jeSB1c2VzIGFuIG90aGVyIHR5cGUgb2YgVVJMLCBzdWNoIGFzIGEgR2l0SHViIFVSTFxuICogICAtIFRoZSBkZXBlbmRlbmN5IGlzIG5vdCBmb3VuZCBpbiB0aGUgYHBhY2thZ2UuanNvbmAsIHN1Y2ggYXMgd2hlblxuICogICAgIGJvb3RzdHJhcHBpbmcgZnJvbSBhbiBleHRlcm5hbCBwcm9qZW4gcGFja2FnZSwgYW5kIHRoZSBgcGFja2FnZS5qc29uYFxuICogICAgIGZpbGUgb25seSBoYXMgdGhhdCBvbmUgZXh0ZXJuYWwgcGFja2FnZSBhcyBhIGRlcGVuZGVuY3lcbiAqXG4gKiBPdGhlcndpc2UgaXQgd2lsbCByZXR1cm4gYHRydWVgIGlmIHRoZSBpbnN0YWxsZWQgdmVyc2lvbiBpcyBwcm9iYWJseSBpbiB0aGVcbiAqIHJlcXVlc3RlZCByYW5nZSwgYW5kIGBmYWxzZWAgaWYgaXQgaXMgcHJvYmFibHkgbm90LlxuICpcbiAqIFRoaXMgQVBJIG1heSBldmVudHVhbGx5IGJlIGFkZGVkIHRvIHRoZSBwdWJsaWMgcHJvamVuIEFQSSwgYnV0IG9ubHkgYWZ0ZXJcbiAqIHdlIGltcGxlbWVudCBleGFjdCB2ZXJzaW9uIGNoZWNraW5nLlxuICpcbiAqIEBwYXJhbSBkZXBlbmRlbmN5TmFtZSBUaGUgbmFtZSBvZiB0aGUgZGVwZW5kZW5jeVxuICogQHBhcmFtIGNoZWNrUmFuZ2UgQSBwYXJ0aWN1bGFyIHZlcnNpb24sIG9yIHJhbmdlIG9mIHZlcnNpb25zLlxuICovXG5leHBvcnQgZnVuY3Rpb24gaGFzRGVwZW5kZW5jeVZlcnNpb24oXG4gIHByb2plY3Q6IFByb2plY3QsXG4gIGRlcGVuZGVuY3lOYW1lOiBzdHJpbmcsXG4gIGNoZWNrUmFuZ2U6IHN0cmluZ1xuKTogYm9vbGVhbiB8IHVuZGVmaW5lZCB7XG4gIGNvbnN0IGZpbGUgPSBOb2RlUGFja2FnZS5vZihwcm9qZWN0KT8uZmlsZTtcbiAgaWYgKCFmaWxlKSB7XG4gICAgcmV0dXJuIHVuZGVmaW5lZDtcbiAgfVxuXG4gIGlmICghZXhpc3RzU3luYyhmaWxlLmFic29sdXRlUGF0aCkpIHtcbiAgICByZXR1cm4gdW5kZWZpbmVkO1xuICB9XG5cbiAgY29uc3QgcGogPSBKU09OLnBhcnNlKHJlYWRGaWxlU3luYyhmaWxlLmFic29sdXRlUGF0aCwgXCJ1dGYtOFwiKSk7XG5cbiAgLy8gVGVjaG5pY2FseSwgd2Ugc2hvdWxkIGJlIGludGVyc2VjdGluZyBhbGwgcmFuZ2VzIHRvIGNvbWUgdXAgd2l0aCB0aGUgbW9zdCBuYXJyb3cgZGVwZW5kZW5jeVxuICAvLyByYW5nZSwgYnV0IGBzZW12ZXJgIGRvZXNuJ3QgYWxsb3cgZG9pbmcgdGhhdCBhbmQgd2UgZG9uJ3Qgd2FudCB0byBhZGQgYSBkZXBlbmRlbmN5IG9uIGBzZW12ZXItaW50ZXJzZWN0YC5cbiAgLy9cbiAgLy8gTGV0J3MgdGFrZSB0aGUgZmlyc3QgZGVwZW5kZW5jeSBkZWNsYXJhdGlvbiB3ZSBmaW5kLCBhbmQgYXNzdW1lIHRoYXQgcGVvcGxlXG4gIC8vIHNldCB1cCB0aGVpciBgcGFja2FnZS5qc29uYCBjb3JyZWN0bHkuXG4gIGxldCByZXF1ZXN0ZWRSYW5nZTogc3RyaW5nIHwgdW5kZWZpbmVkO1xuICBmb3IgKGNvbnN0IGtleSBvZiBbXCJkZXBlbmRlbmNpZXNcIiwgXCJkZXZEZXBlbmRlbmNpZXNcIiwgXCJwZWVyRGVwZW5kZW5jaWVzXCJdKSB7XG4gICAgY29uc3QgZGVwcyA9IHBqW2tleV0gPz8ge307XG4gICAgbGV0IHJlcXVlc3RlZFZlcnNpb24gPSBkZXBzW2RlcGVuZGVuY3lOYW1lXTtcbiAgICBpZiAocmVxdWVzdGVkVmVyc2lvbikge1xuICAgICAgLy8gSWYgdGhpcyBpcyBub3QgYSB2YWxpZCByYW5nZSwgaXQgY291bGQgYmUgJ2ZpbGU6ZGVwLnRneicsIG9yIGEgR2l0SHViIFVSTC4gTm8gd2F5IHRvIGtub3cgd2hhdFxuICAgICAgLy8gdmVyc2lvbiB3ZSdyZSBnZXR0aW5nLCBiYWlsIG91dC5cbiAgICAgIGlmICghc2VtdmVyLnZhbGlkUmFuZ2UocmVxdWVzdGVkVmVyc2lvbikpIHtcbiAgICAgICAgcmV0dXJuIHVuZGVmaW5lZDtcbiAgICAgIH1cbiAgICAgIHJlcXVlc3RlZFJhbmdlID0gcmVxdWVzdGVkVmVyc2lvbjtcbiAgICAgIGJyZWFrO1xuICAgIH1cbiAgfVxuXG4gIC8vIElmIHRoZSBkZXBlbmRlbmN5IGlzIG5vdCBmb3VuZCBpbiB0aGUgYHBhY2thZ2UuanNvbmAsIHdlIGNhbid0IGFuc3dlciB0aGUgcXVlc3Rpb24gKHlldCkuXG4gIC8vIFRoaXMgbWVhbnMgdGhhdCB0aGUgZGVwZW5kZW5jeSBoYXNuJ3QgYmVlbiBhZGRlZCB5ZXQsIHdoaWNoIG1lYW5zIHdlIGtub3cgKHVwc3RyZWFtKSB3aGF0IHdlJ3JlIGdvaW5nIHRvIHJlcXVlc3QsXG4gIC8vIG9yIHdlJ3JlIGdvaW5nIHRvIGFzayBmb3IgJyonIGFuZCB3ZSdsbCBnZXQgdGhlIGxhdGVzdCB2ZXJzaW9uLlxuICBpZiAoIXJlcXVlc3RlZFJhbmdlKSB7XG4gICAgcmV0dXJuIHVuZGVmaW5lZDtcbiAgfVxuXG4gIHJldHVybiBpbnN0YWxsZWRWZXJzaW9uUHJvYmFibHlNYXRjaGVzKHJlcXVlc3RlZFJhbmdlLCBjaGVja1JhbmdlKTtcbn1cblxuLyoqXG4gKiBXaGV0aGVyIHRoZSBnaXZlbiByZXF1ZXN0ZWRSYW5nZSAqcHJvYmFibHkqIGxlYWRzIHRvIHRoZSBpbnN0YWxsYXRpb24gb2YgYSB2ZXJzaW9uIHRoYXQgbWF0Y2hlcyBjaGVja1JhbmdlXG4gKlxuICogV2UgYXNzdW1lIHRoYXQgTlBNIGFsd2F5cyBpbnN0YWxscyB0aGUgbW9zdCByZWNlbnQgdmVyc2lvbiBvZiBhIHBhY2thZ2UgdGhhdFxuICogaXMgYWxsb3dlZCBieSB0aGUgcmVxdWVzdGVkUmFuZ2UuXG4gKi9cbmV4cG9ydCBmdW5jdGlvbiBpbnN0YWxsZWRWZXJzaW9uUHJvYmFibHlNYXRjaGVzKFxuICByZXF1ZXN0ZWRSYW5nZTogc3RyaW5nLFxuICBjaGVja1JhbmdlOiBzdHJpbmdcbik6IGJvb2xlYW4ge1xuICBjb25zdCBvcHRpb25zID0ge1xuICAgIGluY2x1ZGVQcmVyZWxlYXNlOiB0cnVlLFxuICAgIGxvb3NlOiB0cnVlLFxuICB9O1xuXG4gIC8vIE5vIHF1ZXN0aW9ucyBhc2tlZDogYWx3YXlzIHRydWVcbiAgaWYgKHNlbXZlci5zdWJzZXQocmVxdWVzdGVkUmFuZ2UsIGNoZWNrUmFuZ2UsIG9wdGlvbnMpKSB7XG4gICAgcmV0dXJuIHRydWU7XG4gIH1cblxuICAvLyBBbHNvIG5vIHF1ZXN0aW9ucyBhc2tlZDogYWx3YXlzIGZhbHNlXG4gIGlmICghc2VtdmVyLmludGVyc2VjdHMocmVxdWVzdGVkUmFuZ2UsIGNoZWNrUmFuZ2UsIG9wdGlvbnMpKSB7XG4gICAgcmV0dXJuIGZhbHNlO1xuICB9XG5cbiAgLy8gTm93IHdlJ3JlIGluIHRyaWNreSB0ZXJyaXRvcnkuIFdlIGludGVyc2VjdCwgYnV0IGFyZW4ndCBhIGZ1bGwgc3Vic2V0LlxuICAvLyBXZSBhcmUgaW4gb25lIG9mIHRoZSBmb2xsb3dpbmcgMiBzaXR1YXRpb25zLCB3aGljaCB3ZSB3aWxsIHRpZS1icmVhayBieVxuICAvLyBhc3N1bWluZyBOUE0gd2lsbCBpbnN0YWxsIHRoZSBtb3N0IHJlY2VudCBtYXRjaGluZyB2ZXJzaW9uIGluICdyZXF1ZXN0ZWQnLlxuICAvL1xuICAvLyByZXF1ZXN0ZWQgIHwgY2hlY2sgICAgfCByZXN1bHRcbiAgLy8gLS0tLS0tLS0tLS18LS0tLS0tLS0tLXwtLS0tLS0tLS0tLVxuICAvLyAgID49IDIgICAgIHwgID49IDMgICAgfCBwcm9iYWJseSB0cnVlIChjaGFuY2Ugb2YgRlApXG4gIC8vICAgPD0gMiAgICAgfCAgPD0gMSAgICB8IHByb2JhYmx5IGZhbHNlIChjaGFuZ2Ugb2YgRk4pXG4gIC8vXG4gIC8vIGBzZW12ZXJgIGRvZXNuJ3QgbWFrZSBpdCBlYXN5IHRvIGRpc3Rpbmd1aXNoIHRoZXNlIGNhc2VzICh3ZSBjYW4ndCByZXF1ZXN0XG4gIC8vIHRoZSBgbWF4VmVyc2lvbmAgdGhhdCBzYXRpc2ZpZXMgYSByYW5nZSkuIEluc3RlYWQgd2hhdCB3ZSBkbyBpc1xuICAvLyBnZXQgdGhlIGBtaW5WZXJzaW9uYCBvZiBlYWNoIHJhbmdlLCBhbmQgaWYgdGhleSBjb21wYXJlIGVxdWFsIHdlIGFzc3VtZVxuICAvLyB3ZSdyZSBpbiB0aGUgYm90dG9tIGNhc2Ugd2l0aCBgPD1gIGNoZWNrcywgYW5kIHJldHVybiBgZmFsc2VgLlxuXG4gIHJldHVybiAhc2VtdmVyLmVxKFxuICAgIHNlbXZlci5taW5WZXJzaW9uKHJlcXVlc3RlZFJhbmdlLCBvcHRpb25zKSA/PyBcIjEuMi4zXCIsXG4gICAgc2VtdmVyLm1pblZlcnNpb24oY2hlY2tSYW5nZSwgb3B0aW9ucykgPz8gXCIxLjIuM1wiXG4gICk7XG59XG4iXX0=