UNPKG

obsidian-dev-utils

Version:

This is the collection of useful functions that you can use for your Obsidian plugin development

482 lines (464 loc) 52.6 kB
/* THIS IS A GENERATED/BUNDLED FILE BY ESBUILD if you want to view the source, please visit the github repository of this plugin */ (function initCjs() { const globalThisRecord = globalThis; globalThisRecord['__name'] ??= name; const originalRequire = require; if (originalRequire && !originalRequire.__isPatched) { // eslint-disable-next-line no-global-assign, no-implicit-globals -- We need to patch the `require()` function. require = Object.assign( (id) => requirePatched(id), originalRequire, { __isPatched: true } ); } const newFuncs = { __extractDefault() { return extractDefault; }, process() { const browserProcess = { browser: true, cwd() { return '/'; }, env: {}, platform: 'android' }; return browserProcess; } }; for (const key of Object.keys(newFuncs)) { globalThisRecord[key] ??= newFuncs[key]?.(); } function name(obj) { return obj; } function extractDefault(module) { return module && module.__esModule && 'default' in module ? module.default : module; } const OBSIDIAN_BUILT_IN_MODULE_NAMES = [ 'obsidian', '@codemirror/autocomplete', '@codemirror/collab', '@codemirror/commands', '@codemirror/language', '@codemirror/lint', '@codemirror/search', '@codemirror/state', '@codemirror/text', '@codemirror/view', '@lezer/common', '@lezer/lr', '@lezer/highlight']; const DEPRECATED_OBSIDIAN_BUILT_IN_MODULE_NAMES = [ '@codemirror/closebrackets', '@codemirror/comment', '@codemirror/fold', '@codemirror/gutter', '@codemirror/highlight', '@codemirror/history', '@codemirror/matchbrackets', '@codemirror/panel', '@codemirror/rangeset', '@codemirror/rectangular-selection', '@codemirror/stream-parser', '@codemirror/tooltip']; function requirePatched(id) { if (OBSIDIAN_BUILT_IN_MODULE_NAMES.includes(id) || DEPRECATED_OBSIDIAN_BUILT_IN_MODULE_NAMES.includes(id)) { return originalRequire?.(id); } // eslint-disable-next-line @typescript-eslint/no-deprecated, @typescript-eslint/no-unnecessary-condition -- We need access to app here which might not be available yet. if (globalThis?.app?.isMobile) { if (id === 'process' || id === 'node:process') { console.debug(`The most likely you can safely ignore this error. Module not found: ${id}. Fake process object is returned instead.`); return globalThis.process; } } else { const module = originalRequire?.(id); if (module) { return extractDefault(module); } } console.debug(`The most likely you can safely ignore this error. Module not found: ${id}. Empty object is returned instead.`); return {}; } })(); "use strict"; var __defProp = Object.defineProperty; var __getOwnPropDesc = Object.getOwnPropertyDescriptor; var __getOwnPropNames = Object.getOwnPropertyNames; var __hasOwnProp = Object.prototype.hasOwnProperty; var __export = (target, all) => { for (var name in all) __defProp(target, name, { get: all[name], enumerable: true }); }; var __copyProps = (to, from, except, desc) => { if (from && typeof from === "object" || typeof from === "function") { for (let key of __getOwnPropNames(from)) if (!__hasOwnProp.call(to, key) && key !== except) __defProp(to, key, { get: () => from[key], enumerable: !(desc = __getOwnPropDesc(from, key)) || desc.enumerable }); } return to; }; var __toCommonJS = (mod) => __copyProps(__defProp({}, "__esModule", { value: true }), mod); var version_exports = {}; __export(version_exports, { VersionUpdateType: () => VersionUpdateType, addGitTag: () => addGitTag, addUpdatedFilesToGit: () => addUpdatedFilesToGit, checkGitHubCliInstalled: () => checkGitHubCliInstalled, checkGitInstalled: () => checkGitInstalled, checkGitRepoClean: () => checkGitRepoClean, copyUpdatedManifest: () => copyUpdatedManifest, getNewVersion: () => getNewVersion, getReleaseNotes: () => getReleaseNotes, getVersionUpdateType: () => getVersionUpdateType, gitPush: () => gitPush, publishGitHubRelease: () => publishGitHubRelease, updateChangelog: () => updateChangelog, updateVersion: () => updateVersion, updateVersionInFiles: () => updateVersionInFiles, validate: () => validate }); module.exports = __toCommonJS(version_exports); var import_Debug = require('../Debug.cjs'); var import_Error = require('../Error.cjs'); var import_ObsidianPluginRepoPaths = require('../obsidian/Plugin/ObsidianPluginRepoPaths.cjs'); var import_Path = require('../Path.cjs'); var import_String = require('../String.cjs'); var import_Fs = require('./Fs.cjs'); var import_JSON = require('./JSON.cjs'); var import_NodeModules = require('./NodeModules.cjs'); var import_Npm = require('./Npm.cjs'); var import_NpmRun = require('./NpmRun.cjs'); var import_ObsidianDevUtilsRepoPaths = require('./ObsidianDevUtilsRepoPaths.cjs'); var import_Root = require('./Root.cjs'); var VersionUpdateType = /* @__PURE__ */ ((VersionUpdateType2) => { VersionUpdateType2["Beta"] = "beta"; VersionUpdateType2["Invalid"] = "invalid"; VersionUpdateType2["Major"] = "major"; VersionUpdateType2["Manual"] = "manual"; VersionUpdateType2["Minor"] = "minor"; VersionUpdateType2["Patch"] = "patch"; return VersionUpdateType2; })(VersionUpdateType || {}); async function addGitTag(newVersion) { await (0, import_Root.execFromRoot)(`git tag -a ${newVersion} -m ${newVersion} --force`, { isQuiet: true }); } async function addUpdatedFilesToGit(newVersion) { await (0, import_Root.execFromRoot)(["git", "add", "--all"], { isQuiet: true }); await (0, import_Root.execFromRoot)(["git", "commit", "-m", `chore: release ${newVersion}`, "--allow-empty"], { isQuiet: true }); } async function checkGitHubCliInstalled() { try { await (0, import_Root.execFromRoot)("gh --version", { isQuiet: true }); } catch { throw new Error("GitHub CLI is not installed. Please install it from https://cli.github.com/"); } } async function checkGitInstalled() { try { await (0, import_Root.execFromRoot)("git --version", { isQuiet: true }); } catch { throw new Error("Git is not installed. Please install it from https://git-scm.com/"); } } async function checkGitRepoClean() { try { const stdout = await (0, import_Root.execFromRoot)("git status --porcelain --untracked-files=all", { isQuiet: true }); if (stdout) { throw new Error(); } } catch { throw new Error("Git repository is not clean. Please commit or stash your changes before releasing a new version."); } } async function copyUpdatedManifest() { await (0, import_NodeModules.cp)( (0, import_Root.resolvePathFromRootSafe)(import_ObsidianPluginRepoPaths.ObsidianPluginRepoPaths.ManifestJson), (0, import_Root.resolvePathFromRootSafe)((0, import_Path.join)(import_ObsidianPluginRepoPaths.ObsidianPluginRepoPaths.DistBuild, import_ObsidianPluginRepoPaths.ObsidianPluginRepoPaths.ManifestJson)), { force: true } ); } async function getNewVersion(versionUpdateType) { const versionType = getVersionUpdateType(versionUpdateType); if (versionType === "manual" /* Manual */) { return versionUpdateType; } const packageJson = await (0, import_Npm.readPackageJson)(); const currentVersion = packageJson.version ?? ""; const match = /^(?<Major>\d+)\.(?<Minor>\d+)\.(?<Patch>\d+)(?:-beta\.(?<Beta>\d+))?$/.exec(currentVersion); if (!match) { throw new Error(`Invalid current version format: ${currentVersion}`); } let major = Number(match.groups?.["Major"] ?? ""); let minor = Number(match.groups?.["Minor"] ?? ""); let patch = Number(match.groups?.["Patch"] ?? ""); let beta = Number(match.groups?.["Beta"] ?? ""); switch (versionType) { case "beta" /* Beta */: if (beta === 0) { patch++; } beta++; break; case "major" /* Major */: major++; minor = 0; patch = 0; beta = 0; break; case "minor" /* Minor */: minor++; patch = 0; beta = 0; break; case "patch" /* Patch */: if (beta === 0) { patch++; } else { beta = 0; } break; default: throw new Error(`Invalid version update type: ${versionType}`); } return `${String(major)}.${String(minor)}.${String(patch)}${beta > 0 ? `-beta.${String(beta)}` : ""}`; } async function getReleaseNotes(newVersion) { const changelogPath = (0, import_Root.resolvePathFromRootSafe)(import_ObsidianPluginRepoPaths.ObsidianPluginRepoPaths.ChangelogMd); const content = await (0, import_NodeModules.readFile)(changelogPath, "utf-8"); const newVersionEscaped = (0, import_String.replaceAll)(newVersion, ".", "\\."); const match = new RegExp(` ## ${newVersionEscaped} ((.| )+?) ##`).exec(content); let releaseNotes = match?.[1] ? `${match[1]} ` : ""; const tags = (await (0, import_Root.execFromRoot)("git tag --sort=-creatordate", { isQuiet: true })).split(/\r?\n/); const previousVersion = tags[1]; let changesUrl; const repoUrl = await (0, import_Root.execFromRoot)("gh repo view --json url -q .url", { isQuiet: true }); if (previousVersion) { changesUrl = `${repoUrl}/compare/${previousVersion}...${newVersion}`; } else { changesUrl = `${repoUrl}/commits/${newVersion}`; } releaseNotes += `**Full Changelog**: ${changesUrl}`; return releaseNotes; } function getVersionUpdateType(versionUpdateType) { const versionUpdateTypeEnum = versionUpdateType; switch (versionUpdateTypeEnum) { case "beta" /* Beta */: case "major" /* Major */: case "minor" /* Minor */: case "patch" /* Patch */: return versionUpdateTypeEnum; default: if (/^\d+\.\d+\.\d+(?:-[\w\d.-]+)?$/.test(versionUpdateType)) { return "manual" /* Manual */; } return "invalid" /* Invalid */; } } async function gitPush() { await (0, import_Root.execFromRoot)("git push --follow-tags --force", { isQuiet: true }); } async function publishGitHubRelease(newVersion, isObsidianPlugin) { let filePaths; if (isObsidianPlugin) { const buildFolder = (0, import_Root.resolvePathFromRootSafe)(import_ObsidianPluginRepoPaths.ObsidianPluginRepoPaths.DistBuild); const fileNames = await (0, import_Fs.readdirPosix)(buildFolder); filePaths = fileNames.map((fileName) => (0, import_Path.join)(buildFolder, fileName)); } else { const resultJson = await (0, import_Root.execFromRoot)(["npm", "pack", "--pack-destination", import_ObsidianDevUtilsRepoPaths.ObsidianDevUtilsRepoPaths.Dist, "--json"], { isQuiet: true }); const result = JSON.parse(resultJson); filePaths = [ (0, import_Path.join)(import_ObsidianDevUtilsRepoPaths.ObsidianDevUtilsRepoPaths.Dist, result[0].filename), (0, import_Path.join)(import_ObsidianDevUtilsRepoPaths.ObsidianDevUtilsRepoPaths.Dist, import_ObsidianDevUtilsRepoPaths.ObsidianDevUtilsRepoPaths.StylesCss) ]; } filePaths = filePaths.filter((filePath) => (0, import_NodeModules.existsSync)((0, import_Root.resolvePathFromRootSafe)(filePath))); await (0, import_Root.execFromRoot)([ "gh", "release", "create", newVersion, ...filePaths, "--title", `v${newVersion}`, ...isBeta(newVersion) ? ["--prerelease"] : [], "--notes-file", "-" ], { isQuiet: true, stdin: await getReleaseNotes(newVersion) }); } async function updateChangelog(newVersion) { const HEADER_LINES_COUNT = 2; const changelogPath = (0, import_Root.resolvePathFromRootSafe)(import_ObsidianPluginRepoPaths.ObsidianPluginRepoPaths.ChangelogMd); let previousChangelogLines; if ((0, import_NodeModules.existsSync)(changelogPath)) { const content = await (0, import_NodeModules.readFile)(changelogPath, "utf-8"); previousChangelogLines = content.split("\n").slice(HEADER_LINES_COUNT); if (previousChangelogLines.at(-1) === "") { previousChangelogLines.pop(); } } else { previousChangelogLines = []; } const lastTag = (0, import_String.replaceAll)(previousChangelogLines[0] ?? "", "## ", ""); const commitRange = lastTag ? `${lastTag}..HEAD` : "HEAD"; const commitMessagesStr = await (0, import_Root.execFromRoot)(`git log ${commitRange} --format=%B --first-parent -z`, { isQuiet: true }); const commitMessages = commitMessagesStr.split("\0").filter(Boolean).map(toSingleLine); let newChangeLog = `# CHANGELOG ## ${newVersion} `; for (const message of commitMessages) { newChangeLog += `- ${message} `; } if (previousChangelogLines.length > 0) { newChangeLog += "\n"; for (const line of previousChangelogLines) { newChangeLog += `${line} `; } } await (0, import_NodeModules.writeFile)(changelogPath, newChangeLog, "utf-8"); const codeVersion = await (0, import_Root.execFromRoot)("code --version", { isQuiet: true, shouldIgnoreExitCode: true }); const versionDebugger = (0, import_Debug.getLibDebugger)("Version"); if (codeVersion) { versionDebugger(`Please update the ${import_ObsidianPluginRepoPaths.ObsidianPluginRepoPaths.ChangelogMd} file. Close Visual Studio Code when you are done...`); await (0, import_Root.execFromRoot)(["code", "-w", changelogPath], { isQuiet: true, shouldIgnoreExitCode: true }); } else { versionDebugger("Could not find Visual Studio Code in your PATH. Using console mode instead."); await (0, import_NodeModules.createInterface)(process.stdin, process.stdout).question( `Please update the ${import_ObsidianPluginRepoPaths.ObsidianPluginRepoPaths.ChangelogMd} file. Press Enter when you are done...` ); } } async function updateVersion(versionUpdateType, prepareGitHubRelease) { if (!versionUpdateType) { const npmOldVersion = process.env["npm_old_version"]; const npmNewVersion = process.env["npm_new_version"]; if (npmOldVersion && npmNewVersion) { await updateVersionInFiles(npmOldVersion); await updateVersion(npmNewVersion, prepareGitHubRelease); return; } throw new Error("No version update type provided"); } const isObsidianPlugin = (0, import_NodeModules.existsSync)((0, import_Root.resolvePathFromRootSafe)(import_ObsidianPluginRepoPaths.ObsidianPluginRepoPaths.ManifestJson)) && (await (0, import_Npm.readPackageJson)()).name !== "obsidian-dev-utils"; validate(versionUpdateType); await checkGitInstalled(); await checkGitRepoClean(); await checkGitHubCliInstalled(); await (0, import_NpmRun.npmRun)("format:check"); await (0, import_NpmRun.npmRun)("spellcheck"); await (0, import_NpmRun.npmRun)("lint:md"); await (0, import_NpmRun.npmRun)("build"); await (0, import_NpmRun.npmRun)("lint"); const newVersion = await getNewVersion(versionUpdateType); await updateVersionInFiles(newVersion); if (isObsidianPlugin) { await updateVersionInFilesForPlugin(newVersion); } await updateChangelog(newVersion); await addUpdatedFilesToGit(newVersion); await addGitTag(newVersion); await gitPush(); await prepareGitHubRelease?.(newVersion); await publishGitHubRelease(newVersion, isObsidianPlugin); } async function updateVersionInFiles(newVersion) { await (0, import_Npm.editPackageJson)((packageJson) => { packageJson.version = newVersion; }); await (0, import_Npm.editPackageLockJson)(update, { shouldSkipIfMissing: true }); await (0, import_Npm.editNpmShrinkWrapJson)(update, { shouldSkipIfMissing: true }); function update(packageLockJson) { packageLockJson.version = newVersion; const defaultPackage = packageLockJson.packages?.[""]; if (defaultPackage) { defaultPackage.version = newVersion; } } } function validate(versionUpdateType) { if (getVersionUpdateType(versionUpdateType) === "invalid" /* Invalid */) { throw new Error("Invalid version update type. Please use 'major', 'minor', 'patch', or 'x.y.z[-suffix]' format."); } } async function getLatestObsidianVersion() { const response = await fetch("https://api.github.com/repos/obsidianmd/obsidian-releases/releases/latest"); const obsidianReleasesJson = await response.json(); return obsidianReleasesJson.name ?? (0, import_Error.throwExpression)(new Error("Could not find the name of the latest Obsidian release")); } function isBeta(version) { return version.includes("beta" /* Beta */); } function toSingleLine(str) { const lines = str.split(/\r?\n/).filter(Boolean); return lines.join(" "); } async function updateVersionInFilesForPlugin(newVersion) { const manifestBetaJsonPath = (0, import_Root.resolvePathFromRootSafe)(import_ObsidianPluginRepoPaths.ObsidianPluginRepoPaths.ManifestBetaJson); if (isBeta(newVersion)) { await (0, import_NodeModules.cp)( (0, import_Root.resolvePathFromRootSafe)(import_ObsidianPluginRepoPaths.ObsidianPluginRepoPaths.ManifestJson), manifestBetaJsonPath, { force: true } ); await (0, import_JSON.editJson)(import_ObsidianPluginRepoPaths.ObsidianPluginRepoPaths.ManifestBetaJson, (manifest) => { manifest.version = newVersion; }); } else { const latestObsidianVersion = await getLatestObsidianVersion(); await (0, import_JSON.editJson)(import_ObsidianPluginRepoPaths.ObsidianPluginRepoPaths.ManifestJson, (manifest) => { manifest.minAppVersion = latestObsidianVersion; manifest.version = newVersion; }); await (0, import_JSON.editJson)(import_ObsidianPluginRepoPaths.ObsidianPluginRepoPaths.VersionsJson, (versions) => { versions[newVersion] = latestObsidianVersion; }); if ((0, import_NodeModules.existsSync)(manifestBetaJsonPath)) { await (0, import_NodeModules.rm)(manifestBetaJsonPath); } } await copyUpdatedManifest(); } // Annotate the CommonJS export names for ESM import in node: 0 && (module.exports = { VersionUpdateType, addGitTag, addUpdatedFilesToGit, checkGitHubCliInstalled, checkGitInstalled, checkGitRepoClean, copyUpdatedManifest, getNewVersion, getReleaseNotes, getVersionUpdateType, gitPush, publishGitHubRelease, updateChangelog, updateVersion, updateVersionInFiles, validate }); //# sourceMappingURL=data:application/json;base64,ewogICJ2ZXJzaW9uIjogMywKICAic291cmNlcyI6IFsiLi4vLi4vLi4vLi4vc3JjL1NjcmlwdFV0aWxzL3ZlcnNpb24udHMiXSwKICAic291cmNlc0NvbnRlbnQiOiBbIi8qKlxuICogQHBhY2thZ2VEb2N1bWVudGF0aW9uXG4gKlxuICogVGhpcyBtb2R1bGUgcHJvdmlkZXMgZnVuY3Rpb25zIGZvciBtYW5hZ2luZyB2ZXJzaW9uIHVwZGF0ZXMgaW4gYSBwcm9qZWN0LlxuICogSXQgaW5jbHVkZXMgdGFza3Mgc3VjaCBhcyB2YWxpZGF0aW5nIHZlcnNpb24gdXBkYXRlIHR5cGVzLCBjaGVja2luZyB0aGUgc3RhdGVcbiAqIG9mIEdpdCBhbmQgR2l0SHViIENMSSwgdXBkYXRpbmcgdmVyc2lvbiBudW1iZXJzIGluIGZpbGVzLCBhbmQgcGVyZm9ybWluZ1xuICogR2l0IG9wZXJhdGlvbnMgc3VjaCBhcyB0YWdnaW5nIGFuZCBwdXNoaW5nLlxuICovXG5cbmltcG9ydCB0eXBlIHsgUGFja2FnZUxvY2tKc29uIH0gZnJvbSAnLi9OcG0udHMnO1xuXG5pbXBvcnQgeyBnZXRMaWJEZWJ1Z2dlciB9IGZyb20gJy4uL0RlYnVnLnRzJztcbmltcG9ydCB7IHRocm93RXhwcmVzc2lvbiB9IGZyb20gJy4uL0Vycm9yLnRzJztcbmltcG9ydCB7IE9ic2lkaWFuUGx1Z2luUmVwb1BhdGhzIH0gZnJvbSAnLi4vb2JzaWRpYW4vUGx1Z2luL09ic2lkaWFuUGx1Z2luUmVwb1BhdGhzLnRzJztcbmltcG9ydCB7IGpvaW4gfSBmcm9tICcuLi9QYXRoLnRzJztcbmltcG9ydCB7IHJlcGxhY2VBbGwgfSBmcm9tICcuLi9TdHJpbmcudHMnO1xuaW1wb3J0IHsgcmVhZGRpclBvc2l4IH0gZnJvbSAnLi9Gcy50cyc7XG5pbXBvcnQgeyBlZGl0SnNvbiB9IGZyb20gJy4vSlNPTi50cyc7XG5pbXBvcnQge1xuICBjcCxcbiAgY3JlYXRlSW50ZXJmYWNlLFxuICBleGlzdHNTeW5jLFxuICByZWFkRmlsZSxcbiAgcm0sXG4gIHdyaXRlRmlsZVxufSBmcm9tICcuL05vZGVNb2R1bGVzLnRzJztcbmltcG9ydCB7XG4gIGVkaXROcG1TaHJpbmtXcmFwSnNvbixcbiAgZWRpdFBhY2thZ2VKc29uLFxuICBlZGl0UGFja2FnZUxvY2tKc29uLFxuICByZWFkUGFja2FnZUpzb25cbn0gZnJvbSAnLi9OcG0udHMnO1xuaW1wb3J0IHsgbnBtUnVuIH0gZnJvbSAnLi9OcG1SdW4udHMnO1xuaW1wb3J0IHsgT2JzaWRpYW5EZXZVdGlsc1JlcG9QYXRocyB9IGZyb20gJy4vT2JzaWRpYW5EZXZVdGlsc1JlcG9QYXRocy50cyc7XG5pbXBvcnQge1xuICBleGVjRnJvbVJvb3QsXG4gIHJlc29sdmVQYXRoRnJvbVJvb3RTYWZlXG59IGZyb20gJy4vUm9vdC50cyc7XG5cbi8qKlxuICogRW51bSByZXByZXNlbnRpbmcgZGlmZmVyZW50IHR5cGVzIG9mIHZlcnNpb24gdXBkYXRlcy5cbiAqL1xuZXhwb3J0IGVudW0gVmVyc2lvblVwZGF0ZVR5cGUge1xuICBCZXRhID0gJ2JldGEnLFxuICBJbnZhbGlkID0gJ2ludmFsaWQnLFxuICBNYWpvciA9ICdtYWpvcicsXG4gIE1hbnVhbCA9ICdtYW51YWwnLFxuICBNaW5vciA9ICdtaW5vcicsXG4gIFBhdGNoID0gJ3BhdGNoJ1xufVxuXG4vKipcbiAqIFR5cGUgcmVwcmVzZW50aW5nIHRoZSBtYW5pZmVzdCBmaWxlIGZvcm1hdCBmb3IgT2JzaWRpYW4gcGx1Z2lucy5cbiAqL1xuZXhwb3J0IGludGVyZmFjZSBNYW5pZmVzdCB7XG4gIC8qKlxuICAgKiBBIG1pbmltdW0gT2JzaWRpYW4gdmVyc2lvbiByZXF1aXJlZCBmb3IgdGhlIHBsdWdpbi5cbiAgICovXG4gIG1pbkFwcFZlcnNpb246IHN0cmluZztcblxuICAvKipcbiAgICogQSB2ZXJzaW9uIG9mIHRoZSBwbHVnaW4uXG4gICAqL1xuICB2ZXJzaW9uOiBzdHJpbmc7XG59XG5cbi8qKlxuICogVHlwZSByZXByZXNlbnRpbmcgdGhlIHN0cnVjdHVyZSBvZiBPYnNpZGlhbiByZWxlYXNlcyBKU09OLlxuICovXG5leHBvcnQgaW50ZXJmYWNlIE9ic2lkaWFuUmVsZWFzZXNKc29uIHtcbiAgLyoqXG4gICAqIEEgbmFtZSBvZiB0aGUgT2JzaWRpYW4gcmVsZWFzZS5cbiAgICovXG4gIG5hbWU6IHN0cmluZztcbn1cblxuLyoqXG4gKiBDcmVhdGVzIGEgR2l0IHRhZyBmb3IgdGhlIG5ldyB2ZXJzaW9uLlxuICpcbiAqIEBwYXJhbSBuZXdWZXJzaW9uIC0gVGhlIG5ldyB2ZXJzaW9uIG51bWJlciB0byB1c2UgZm9yIHRoZSB0YWcuXG4gKiBAcmV0dXJucyBBIHtAbGluayBQcm9taXNlfSB0aGF0IHJlc29sdmVzIHdoZW4gdGhlIHRhZyBoYXMgYmVlbiBjcmVhdGVkLlxuICovXG5leHBvcnQgYXN5bmMgZnVuY3Rpb24gYWRkR2l0VGFnKG5ld1ZlcnNpb246IHN0cmluZyk6IFByb21pc2U8dm9pZD4ge1xuICBhd2FpdCBleGVjRnJvbVJvb3QoYGdpdCB0YWcgLWEgJHtuZXdWZXJzaW9ufSAtbSAke25ld1ZlcnNpb259IC0tZm9yY2VgLCB7IGlzUXVpZXQ6IHRydWUgfSk7XG59XG5cbi8qKlxuICogQWRkcyB1cGRhdGVkIGZpbGVzIHRvIHRoZSBHaXQgc3RhZ2luZyBhcmVhIGFuZCBjb21taXRzIHRoZW0gd2l0aCB0aGUgbmV3IHZlcnNpb24gbWVzc2FnZS5cbiAqXG4gKiBAcGFyYW0gbmV3VmVyc2lvbiAtIFRoZSBuZXcgdmVyc2lvbiBudW1iZXIgdXNlZCBhcyB0aGUgY29tbWl0IG1lc3NhZ2UuXG4gKiBAcmV0dXJucyBBIHtAbGluayBQcm9taXNlfSB0aGF0IHJlc29sdmVzIHdoZW4gdGhlIGZpbGVzIGhhdmUgYmVlbiBhZGRlZCBhbmQgY29tbWl0dGVkLlxuICovXG5leHBvcnQgYXN5bmMgZnVuY3Rpb24gYWRkVXBkYXRlZEZpbGVzVG9HaXQobmV3VmVyc2lvbjogc3RyaW5nKTogUHJvbWlzZTx2b2lkPiB7XG4gIGF3YWl0IGV4ZWNGcm9tUm9vdChbJ2dpdCcsICdhZGQnLCAnLS1hbGwnXSwgeyBpc1F1aWV0OiB0cnVlIH0pO1xuICBhd2FpdCBleGVjRnJvbVJvb3QoWydnaXQnLCAnY29tbWl0JywgJy1tJywgYGNob3JlOiByZWxlYXNlICR7bmV3VmVyc2lvbn1gLCAnLS1hbGxvdy1lbXB0eSddLCB7IGlzUXVpZXQ6IHRydWUgfSk7XG59XG5cbi8qKlxuICogQ2hlY2tzIGlmIHRoZSBHaXRIdWIgQ0xJIGlzIGluc3RhbGxlZCBvbiB0aGUgc3lzdGVtLlxuICpcbiAqIFRocm93cyBhbiBlcnJvciBpZiB0aGUgR2l0SHViIENMSSBpcyBub3QgaW5zdGFsbGVkLlxuICpcbiAqIEB0aHJvd3MgRXJyb3IgaWYgdGhlIEdpdEh1YiBDTEkgaXMgbm90IGluc3RhbGxlZC5cbiAqL1xuZXhwb3J0IGFzeW5jIGZ1bmN0aW9uIGNoZWNrR2l0SHViQ2xpSW5zdGFsbGVkKCk6IFByb21pc2U8dm9pZD4ge1xuICB0cnkge1xuICAgIGF3YWl0IGV4ZWNGcm9tUm9vdCgnZ2ggLS12ZXJzaW9uJywgeyBpc1F1aWV0OiB0cnVlIH0pO1xuICB9IGNhdGNoIHtcbiAgICB0aHJvdyBuZXcgRXJyb3IoJ0dpdEh1YiBDTEkgaXMgbm90IGluc3RhbGxlZC4gUGxlYXNlIGluc3RhbGwgaXQgZnJvbSBodHRwczovL2NsaS5naXRodWIuY29tLycpO1xuICB9XG59XG5cbi8qKlxuICogQ2hlY2tzIGlmIEdpdCBpcyBpbnN0YWxsZWQgb24gdGhlIHN5c3RlbS5cbiAqXG4gKiBUaHJvd3MgYW4gZXJyb3IgaWYgR2l0IGlzIG5vdCBpbnN0YWxsZWQuXG4gKlxuICogQHRocm93cyBFcnJvciBpZiBHaXQgaXMgbm90IGluc3RhbGxlZC5cbiAqL1xuZXhwb3J0IGFzeW5jIGZ1bmN0aW9uIGNoZWNrR2l0SW5zdGFsbGVkKCk6IFByb21pc2U8dm9pZD4ge1xuICB0cnkge1xuICAgIGF3YWl0IGV4ZWNGcm9tUm9vdCgnZ2l0IC0tdmVyc2lvbicsIHsgaXNRdWlldDogdHJ1ZSB9KTtcbiAgfSBjYXRjaCB7XG4gICAgdGhyb3cgbmV3IEVycm9yKCdHaXQgaXMgbm90IGluc3RhbGxlZC4gUGxlYXNlIGluc3RhbGwgaXQgZnJvbSBodHRwczovL2dpdC1zY20uY29tLycpO1xuICB9XG59XG5cbi8qKlxuICogQ2hlY2tzIGlmIHRoZSBHaXQgcmVwb3NpdG9yeSBpcyBjbGVhbiwgbWVhbmluZyB0aGVyZSBhcmUgbm8gdW5jb21taXR0ZWQgY2hhbmdlcy5cbiAqXG4gKiBUaHJvd3MgYW4gZXJyb3IgaWYgdGhlIEdpdCByZXBvc2l0b3J5IGlzIG5vdCBjbGVhbi5cbiAqXG4gKiBAdGhyb3dzIEVycm9yIGlmIHRoZSBHaXQgcmVwb3NpdG9yeSBpcyBub3QgY2xlYW4uXG4gKi9cbmV4cG9ydCBhc3luYyBmdW5jdGlvbiBjaGVja0dpdFJlcG9DbGVhbigpOiBQcm9taXNlPHZvaWQ+IHtcbiAgdHJ5IHtcbiAgICBjb25zdCBzdGRvdXQgPSBhd2FpdCBleGVjRnJvbVJvb3QoJ2dpdCBzdGF0dXMgLS1wb3JjZWxhaW4gLS11bnRyYWNrZWQtZmlsZXM9YWxsJywgeyBpc1F1aWV0OiB0cnVlIH0pO1xuICAgIGlmIChzdGRvdXQpIHtcbiAgICAgIHRocm93IG5ldyBFcnJvcigpO1xuICAgIH1cbiAgfSBjYXRjaCB7XG4gICAgdGhyb3cgbmV3IEVycm9yKCdHaXQgcmVwb3NpdG9yeSBpcyBub3QgY2xlYW4uIFBsZWFzZSBjb21taXQgb3Igc3Rhc2ggeW91ciBjaGFuZ2VzIGJlZm9yZSByZWxlYXNpbmcgYSBuZXcgdmVyc2lvbi4nKTtcbiAgfVxufVxuXG4vKipcbiAqIENvcGllcyB0aGUgdXBkYXRlZCBtYW5pZmVzdCBmaWxlIHRvIHRoZSBkaXN0cmlidXRpb24gYnVpbGQgZm9sZGVyLlxuICpcbiAqIEByZXR1cm5zIEEge0BsaW5rIFByb21pc2V9IHRoYXQgcmVzb2x2ZXMgd2hlbiB0aGUgY29weSBvcGVyYXRpb24gaXMgY29tcGxldGUuXG4gKi9cbmV4cG9ydCBhc3luYyBmdW5jdGlvbiBjb3B5VXBkYXRlZE1hbmlmZXN0KCk6IFByb21pc2U8dm9pZD4ge1xuICBhd2FpdCBjcChcbiAgICByZXNvbHZlUGF0aEZyb21Sb290U2FmZShPYnNpZGlhblBsdWdpblJlcG9QYXRocy5NYW5pZmVzdEpzb24pLFxuICAgIHJlc29sdmVQYXRoRnJvbVJvb3RTYWZlKGpvaW4oT2JzaWRpYW5QbHVnaW5SZXBvUGF0aHMuRGlzdEJ1aWxkLCBPYnNpZGlhblBsdWdpblJlcG9QYXRocy5NYW5pZmVzdEpzb24pKSxcbiAgICB7IGZvcmNlOiB0cnVlIH1cbiAgKTtcbn1cblxuLyoqXG4gKiBHZW5lcmF0ZXMgYSBuZXcgdmVyc2lvbiBzdHJpbmcgYmFzZWQgb24gdGhlIGN1cnJlbnQgdmVyc2lvbiBhbmQgdGhlIHNwZWNpZmllZCB1cGRhdGUgdHlwZS5cbiAqXG4gKiBAcGFyYW0gdmVyc2lvblVwZGF0ZVR5cGUgLSBUaGUgdHlwZSBvZiB2ZXJzaW9uIHVwZGF0ZSAobWFqb3IsIG1pbm9yLCBwYXRjaCwgYmV0YSwgb3IgbWFudWFsKS5cbiAqIEByZXR1cm5zIEEge0BsaW5rIFByb21pc2V9IHRoYXQgcmVzb2x2ZXMgdG8gdGhlIG5ldyB2ZXJzaW9uIHN0cmluZy5cbiAqIEB0aHJvd3MgRXJyb3IgaWYgdGhlIGN1cnJlbnQgdmVyc2lvbiBmb3JtYXQgaXMgaW52YWxpZC5cbiAqL1xuZXhwb3J0IGFzeW5jIGZ1bmN0aW9uIGdldE5ld1ZlcnNpb24odmVyc2lvblVwZGF0ZVR5cGU6IHN0cmluZyk6IFByb21pc2U8c3RyaW5nPiB7XG4gIGNvbnN0IHZlcnNpb25UeXBlID0gZ2V0VmVyc2lvblVwZGF0ZVR5cGUodmVyc2lvblVwZGF0ZVR5cGUpO1xuICBpZiAodmVyc2lvblR5cGUgPT09IFZlcnNpb25VcGRhdGVUeXBlLk1hbnVhbCkge1xuICAgIHJldHVybiB2ZXJzaW9uVXBkYXRlVHlwZTtcbiAgfVxuXG4gIGNvbnN0IHBhY2thZ2VKc29uID0gYXdhaXQgcmVhZFBhY2thZ2VKc29uKCk7XG4gIGNvbnN0IGN1cnJlbnRWZXJzaW9uID0gcGFja2FnZUpzb24udmVyc2lvbiA/PyAnJztcblxuICBjb25zdCBtYXRjaCA9IC9eKD88TWFqb3I+XFxkKylcXC4oPzxNaW5vcj5cXGQrKVxcLig/PFBhdGNoPlxcZCspKD86LWJldGFcXC4oPzxCZXRhPlxcZCspKT8kLy5leGVjKGN1cnJlbnRWZXJzaW9uKTtcbiAgaWYgKCFtYXRjaCkge1xuICAgIHRocm93IG5ldyBFcnJvcihgSW52YWxpZCBjdXJyZW50IHZlcnNpb24gZm9ybWF0OiAke2N1cnJlbnRWZXJzaW9ufWApO1xuICB9XG5cbiAgbGV0IG1ham9yID0gTnVtYmVyKG1hdGNoLmdyb3Vwcz8uWydNYWpvciddID8/ICcnKTtcbiAgbGV0IG1pbm9yID0gTnVtYmVyKG1hdGNoLmdyb3Vwcz8uWydNaW5vciddID8/ICcnKTtcbiAgbGV0IHBhdGNoID0gTnVtYmVyKG1hdGNoLmdyb3Vwcz8uWydQYXRjaCddID8/ICcnKTtcbiAgbGV0IGJldGEgPSBOdW1iZXIobWF0Y2guZ3JvdXBzPy5bJ0JldGEnXSA/PyAnJyk7XG5cbiAgc3dpdGNoICh2ZXJzaW9uVHlwZSkge1xuICAgIGNhc2UgVmVyc2lvblVwZGF0ZVR5cGUuQmV0YTpcbiAgICAgIGlmIChiZXRhID09PSAwKSB7XG4gICAgICAgIHBhdGNoKys7XG4gICAgICB9XG4gICAgICBiZXRhKys7XG4gICAgICBicmVhaztcbiAgICBjYXNlIFZlcnNpb25VcGRhdGVUeXBlLk1ham9yOlxuICAgICAgbWFqb3IrKztcbiAgICAgIG1pbm9yID0gMDtcbiAgICAgIHBhdGNoID0gMDtcbiAgICAgIGJldGEgPSAwO1xuICAgICAgYnJlYWs7XG4gICAgY2FzZSBWZXJzaW9uVXBkYXRlVHlwZS5NaW5vcjpcbiAgICAgIG1pbm9yKys7XG4gICAgICBwYXRjaCA9IDA7XG4gICAgICBiZXRhID0gMDtcbiAgICAgIGJyZWFrO1xuICAgIGNhc2UgVmVyc2lvblVwZGF0ZVR5cGUuUGF0Y2g6XG4gICAgICBpZiAoYmV0YSA9PT0gMCkge1xuICAgICAgICBwYXRjaCsrO1xuICAgICAgfSBlbHNlIHtcbiAgICAgICAgYmV0YSA9IDA7XG4gICAgICB9XG4gICAgICBicmVhaztcbiAgICBkZWZhdWx0OlxuICAgICAgdGhyb3cgbmV3IEVycm9yKGBJbnZhbGlkIHZlcnNpb24gdXBkYXRlIHR5cGU6ICR7dmVyc2lvblR5cGV9YCk7XG4gIH1cblxuICByZXR1cm4gYCR7U3RyaW5nKG1ham9yKX0uJHtTdHJpbmcobWlub3IpfS4ke1N0cmluZyhwYXRjaCl9JHtiZXRhID4gMCA/IGAtYmV0YS4ke1N0cmluZyhiZXRhKX1gIDogJyd9YDtcbn1cblxuLyoqXG4gKiBSZXRyaWV2ZXMgdGhlIHJlbGVhc2Ugbm90ZXMgZm9yIGEgc3BlY2lmaWMgdmVyc2lvbiBmcm9tIHRoZSBjaGFuZ2Vsb2cuXG4gKlxuICogQHBhcmFtIG5ld1ZlcnNpb24gLSBUaGUgbmV3IHZlcnNpb24gbnVtYmVyIGZvciB3aGljaCB0byBnZXQgdGhlIHJlbGVhc2Ugbm90ZXMuXG4gKiBAcmV0dXJucyBBIHtAbGluayBQcm9taXNlfSB0aGF0IHJlc29sdmVzIHRvIHRoZSByZWxlYXNlIG5vdGVzIGZvciB0aGUgc3BlY2lmaWVkIHZlcnNpb24uXG4gKi9cbmV4cG9ydCBhc3luYyBmdW5jdGlvbiBnZXRSZWxlYXNlTm90ZXMobmV3VmVyc2lvbjogc3RyaW5nKTogUHJvbWlzZTxzdHJpbmc+IHtcbiAgY29uc3QgY2hhbmdlbG9nUGF0aCA9IHJlc29sdmVQYXRoRnJvbVJvb3RTYWZlKE9ic2lkaWFuUGx1Z2luUmVwb1BhdGhzLkNoYW5nZWxvZ01kKTtcbiAgY29uc3QgY29udGVudCA9IGF3YWl0IHJlYWRGaWxlKGNoYW5nZWxvZ1BhdGgsICd1dGYtOCcpO1xuICBjb25zdCBuZXdWZXJzaW9uRXNjYXBlZCA9IHJlcGxhY2VBbGwobmV3VmVyc2lvbiwgJy4nLCAnXFxcXC4nKTtcbiAgY29uc3QgbWF0Y2ggPSBuZXcgUmVnRXhwKGBcXG4jIyAke25ld1ZlcnNpb25Fc2NhcGVkfVxcblxcbigoLnxcXG4pKz8pXFxuXFxuIyNgKS5leGVjKGNvbnRlbnQpO1xuICBsZXQgcmVsZWFzZU5vdGVzID0gbWF0Y2g/LlsxXSA/IGAke21hdGNoWzFdfVxcblxcbmAgOiAnJztcblxuICBjb25zdCB0YWdzID0gKGF3YWl0IGV4ZWNGcm9tUm9vdCgnZ2l0IHRhZyAtLXNvcnQ9LWNyZWF0b3JkYXRlJywgeyBpc1F1aWV0OiB0cnVlIH0pKS5zcGxpdCgvXFxyP1xcbi8pO1xuICBjb25zdCBwcmV2aW91c1ZlcnNpb24gPSB0YWdzWzFdO1xuICBsZXQgY2hhbmdlc1VybDogc3RyaW5nO1xuXG4gIGNvbnN0IHJlcG9VcmwgPSBhd2FpdCBleGVjRnJvbVJvb3QoJ2doIHJlcG8gdmlldyAtLWpzb24gdXJsIC1xIC51cmwnLCB7IGlzUXVpZXQ6IHRydWUgfSk7XG5cbiAgaWYgKHByZXZpb3VzVmVyc2lvbikge1xuICAgIGNoYW5nZXNVcmwgPSBgJHtyZXBvVXJsfS9jb21wYXJlLyR7cHJldmlvdXNWZXJzaW9ufS4uLiR7bmV3VmVyc2lvbn1gO1xuICB9IGVsc2Uge1xuICAgIGNoYW5nZXNVcmwgPSBgJHtyZXBvVXJsfS9jb21taXRzLyR7bmV3VmVyc2lvbn1gO1xuICB9XG5cbiAgcmVsZWFzZU5vdGVzICs9IGAqKkZ1bGwgQ2hhbmdlbG9nKio6ICR7Y2hhbmdlc1VybH1gO1xuICByZXR1cm4gcmVsZWFzZU5vdGVzO1xufVxuXG4vKipcbiAqIERldGVybWluZXMgdGhlIHR5cGUgb2YgdmVyc2lvbiB1cGRhdGUgYmFzZWQgb24gdGhlIGlucHV0IHN0cmluZy5cbiAqXG4gKiBAcGFyYW0gdmVyc2lvblVwZGF0ZVR5cGUgLSBUaGUgaW5wdXQgc3RyaW5nIHJlcHJlc2VudGluZyB0aGUgdmVyc2lvbiB1cGRhdGUgdHlwZS5cbiAqIEByZXR1cm5zIFRoZSBjb3JyZXNwb25kaW5nIGBWZXJzaW9uVXBkYXRlVHlwZWAuXG4gKi9cbmV4cG9ydCBmdW5jdGlvbiBnZXRWZXJzaW9uVXBkYXRlVHlwZSh2ZXJzaW9uVXBkYXRlVHlwZTogc3RyaW5nKTogVmVyc2lvblVwZGF0ZVR5cGUge1xuICBjb25zdCB2ZXJzaW9uVXBkYXRlVHlwZUVudW0gPSB2ZXJzaW9uVXBkYXRlVHlwZSBhcyBWZXJzaW9uVXBkYXRlVHlwZTtcbiAgc3dpdGNoICh2ZXJzaW9uVXBkYXRlVHlwZUVudW0pIHtcbiAgICBjYXNlIFZlcnNpb25VcGRhdGVUeXBlLkJldGE6XG4gICAgY2FzZSBWZXJzaW9uVXBkYXRlVHlwZS5NYWpvcjpcbiAgICBjYXNlIFZlcnNpb25VcGRhdGVUeXBlLk1pbm9yOlxuICAgIGNhc2UgVmVyc2lvblVwZGF0ZVR5cGUuUGF0Y2g6XG4gICAgICByZXR1cm4gdmVyc2lvblVwZGF0ZVR5cGVFbnVtO1xuXG4gICAgZGVmYXVsdDpcbiAgICAgIGlmICgvXlxcZCtcXC5cXGQrXFwuXFxkKyg/Oi1bXFx3XFxkLi1dKyk/JC8udGVzdCh2ZXJzaW9uVXBkYXRlVHlwZSkpIHtcbiAgICAgICAgcmV0dXJuIFZlcnNpb25VcGRhdGVUeXBlLk1hbnVhbDtcbiAgICAgIH1cblxuICAgICAgcmV0dXJuIFZlcnNpb25VcGRhdGVUeXBlLkludmFsaWQ7XG4gIH1cbn1cblxuLyoqXG4gKiBQdXNoZXMgY29tbWl0cyBhbmQgdGFncyB0byB0aGUgcmVtb3RlIEdpdCByZXBvc2l0b3J5LlxuICpcbiAqIEByZXR1cm5zIEEge0BsaW5rIFByb21pc2V9IHRoYXQgcmVzb2x2ZXMgd2hlbiB0aGUgcHVzaCBvcGVyYXRpb24gaXMgY29tcGxldGUuXG4gKi9cbmV4cG9ydCBhc3luYyBmdW5jdGlvbiBnaXRQdXNoKCk6IFByb21pc2U8dm9pZD4ge1xuICBhd2FpdCBleGVjRnJvbVJvb3QoJ2dpdCBwdXNoIC0tZm9sbG93LXRhZ3MgLS1mb3JjZScsIHsgaXNRdWlldDogdHJ1ZSB9KTtcbn1cblxuLyoqXG4gKiBQdWJsaXNoZXMgYSBHaXRIdWIgcmVsZWFzZSBmb3IgdGhlIG5ldyB2ZXJzaW9uLlxuICpcbiAqIEhhbmRsZXMgdGhlIGNyZWF0aW9uIG9mIGEgcmVsZWFzZSBhbmQgdXBsb2FkaW5nIGZpbGVzIGZvciBlaXRoZXIgYW4gT2JzaWRpYW4gcGx1Z2luIG9yIGFub3RoZXIgcHJvamVjdC5cbiAqXG4gKiBAcGFyYW0gbmV3VmVyc2lvbiAtIFRoZSBuZXcgdmVyc2lvbiBudW1iZXIgZm9yIHRoZSByZWxlYXNlLlxuICogQHBhcmFtIGlzT2JzaWRpYW5QbHVnaW4gLSBBIGJvb2xlYW4gaW5kaWNhdGluZyBpZiB0aGUgcHJvamVjdCBpcyBhbiBPYnNpZGlhbiBwbHVnaW4uXG4gKiBAcmV0dXJucyBBIHtAbGluayBQcm9taXNlfSB0aGF0IHJlc29sdmVzIHdoZW4gdGhlIHJlbGVhc2UgaGFzIGJlZW4gcHVibGlzaGVkLlxuICovXG5leHBvcnQgYXN5bmMgZnVuY3Rpb24gcHVibGlzaEdpdEh1YlJlbGVhc2UobmV3VmVyc2lvbjogc3RyaW5nLCBpc09ic2lkaWFuUGx1Z2luOiBib29sZWFuKTogUHJvbWlzZTx2b2lkPiB7XG4gIGxldCBmaWxlUGF0aHM6IHN0cmluZ1tdO1xuXG4gIGlmIChpc09ic2lkaWFuUGx1Z2luKSB7XG4gICAgY29uc3QgYnVpbGRGb2xkZXIgPSByZXNvbHZlUGF0aEZyb21Sb290U2FmZShPYnNpZGlhblBsdWdpblJlcG9QYXRocy5EaXN0QnVpbGQpO1xuICAgIGNvbnN0IGZpbGVOYW1lcyA9IGF3YWl0IHJlYWRkaXJQb3NpeChidWlsZEZvbGRlcik7XG4gICAgZmlsZVBhdGhzID0gZmlsZU5hbWVzLm1hcCgoZmlsZU5hbWUpID0+IGpvaW4oYnVpbGRGb2xkZXIsIGZpbGVOYW1lKSk7XG4gIH0gZWxzZSB7XG4gICAgY29uc3QgcmVzdWx0SnNvbiA9IGF3YWl0IGV4ZWNGcm9tUm9vdChbJ25wbScsICdwYWNrJywgJy0tcGFjay1kZXN0aW5hdGlvbicsIE9ic2lkaWFuRGV2VXRpbHNSZXBvUGF0aHMuRGlzdCwgJy0tanNvbiddLCB7IGlzUXVpZXQ6IHRydWUgfSk7XG4gICAgY29uc3QgcmVzdWx0ID0gSlNPTi5wYXJzZShyZXN1bHRKc29uKSBhcyBbeyBmaWxlbmFtZTogc3RyaW5nIH1dO1xuICAgIGZpbGVQYXRocyA9IFtcbiAgICAgIGpvaW4oT2JzaWRpYW5EZXZVdGlsc1JlcG9QYXRocy5EaXN0LCByZXN1bHRbMF0uZmlsZW5hbWUpLFxuICAgICAgam9pbihPYnNpZGlhbkRldlV0aWxzUmVwb1BhdGhzLkRpc3QsIE9ic2lkaWFuRGV2VXRpbHNSZXBvUGF0aHMuU3R5bGVzQ3NzKVxuICAgIF07XG4gIH1cblxuICBmaWxlUGF0aHMgPSBmaWxlUGF0aHMuZmlsdGVyKChmaWxlUGF0aCkgPT4gZXhpc3RzU3luYyhyZXNvbHZlUGF0aEZyb21Sb290U2FmZShmaWxlUGF0aCkpKTtcblxuICBhd2FpdCBleGVjRnJvbVJvb3QoW1xuICAgICdnaCcsXG4gICAgJ3JlbGVhc2UnLFxuICAgICdjcmVhdGUnLFxuICAgIG5ld1ZlcnNpb24sXG4gICAgLi4uZmlsZVBhdGhzLFxuICAgICctLXRpdGxlJyxcbiAgICBgdiR7bmV3VmVyc2lvbn1gLFxuICAgIC4uLihpc0JldGEobmV3VmVyc2lvbikgPyBbJy0tcHJlcmVsZWFzZSddIDogW10pLFxuICAgICctLW5vdGVzLWZpbGUnLFxuICAgICctJ1xuICBdLCB7XG4gICAgaXNRdWlldDogdHJ1ZSxcbiAgICBzdGRpbjogYXdhaXQgZ2V0UmVsZWFzZU5vdGVzKG5ld1ZlcnNpb24pXG4gIH0pO1xufVxuXG4vKipcbiAqIFVwZGF0ZXMgdGhlIGNoYW5nZWxvZyBmaWxlIHdpdGggbmV3IHZlcnNpb24gaW5mb3JtYXRpb24gYW5kIGNvbW1pdCBtZXNzYWdlcy5cbiAqXG4gKiBUaGlzIGZ1bmN0aW9uIHJlYWRzIHRoZSBjdXJyZW50IGNoYW5nZWxvZywgYXBwZW5kcyBuZXcgZW50cmllcyBmb3IgdGhlIGxhdGVzdCB2ZXJzaW9uLFxuICogYW5kIHByb21wdHMgdGhlIHVzZXIgdG8gcmV2aWV3IHRoZSBjaGFuZ2VzLlxuICpcbiAqIEBwYXJhbSBuZXdWZXJzaW9uIC0gVGhlIG5ldyB2ZXJzaW9uIG51bWJlciB0byBiZSBhZGRlZCB0byB0aGUgY2hhbmdlbG9nLlxuICogQHJldHVybnMgQSB7QGxpbmsgUHJvbWlzZX0gdGhhdCByZXNvbHZlcyB3aGVuIHRoZSBjaGFuZ2Vsb2cgdXBkYXRlIGlzIGNvbXBsZXRlLlxuICovXG5leHBvcnQgYXN5bmMgZnVuY3Rpb24gdXBkYXRlQ2hhbmdlbG9nKG5ld1ZlcnNpb246IHN0cmluZyk6IFByb21pc2U8dm9pZD4ge1xuICBjb25zdCBIRUFERVJfTElORVNfQ09VTlQgPSAyO1xuICBjb25zdCBjaGFuZ2Vsb2dQYXRoID0gcmVzb2x2ZVBhdGhGcm9tUm9vdFNhZmUoT2JzaWRpYW5QbHVnaW5SZXBvUGF0aHMuQ2hhbmdlbG9nTWQpO1xuICBsZXQgcHJldmlvdXNDaGFuZ2Vsb2dMaW5lczogc3RyaW5nW107XG4gIGlmIChleGlzdHNTeW5jKGNoYW5nZWxvZ1BhdGgpKSB7XG4gICAgY29uc3QgY29udGVudCA9IGF3YWl0IHJlYWRGaWxlKGNoYW5nZWxvZ1BhdGgsICd1dGYtOCcpO1xuICAgIHByZXZpb3VzQ2hhbmdlbG9nTGluZXMgPSBjb250ZW50LnNwbGl0KCdcXG4nKS5zbGljZShIRUFERVJfTElORVNfQ09VTlQpO1xuICAgIGlmIChwcmV2aW91c0NoYW5nZWxvZ0xpbmVzLmF0KC0xKSA9PT0gJycpIHtcbiAgICAgIHByZXZpb3VzQ2hhbmdlbG9nTGluZXMucG9wKCk7XG4gICAgfVxuICB9IGVsc2Uge1xuICAgIHByZXZpb3VzQ2hhbmdlbG9nTGluZXMgPSBbXTtcbiAgfVxuXG4gIGNvbnN0IGxhc3RUYWcgPSByZXBsYWNlQWxsKHByZXZpb3VzQ2hhbmdlbG9nTGluZXNbMF0gPz8gJycsICcjIyAnLCAnJyk7XG4gIGNvbnN0IGNvbW1pdFJhbmdlID0gbGFzdFRhZyA/IGAke2xhc3RUYWd9Li5IRUFEYCA6ICdIRUFEJztcbiAgY29uc3QgY29tbWl0TWVzc2FnZXNTdHIgPSBhd2FpdCBleGVjRnJvbVJvb3QoYGdpdCBsb2cgJHtjb21taXRSYW5nZX0gLS1mb3JtYXQ9JUIgLS1maXJzdC1wYXJlbnQgLXpgLCB7IGlzUXVpZXQ6IHRydWUgfSk7XG4gIGNvbnN0IGNvbW1pdE1lc3NhZ2VzID0gY29tbWl0TWVzc2FnZXNTdHIuc3BsaXQoJ1xcMCcpLmZpbHRlcihCb29sZWFuKS5tYXAodG9TaW5nbGVMaW5lKTtcblxuICBsZXQgbmV3Q2hhbmdlTG9nID0gYCMgQ0hBTkdFTE9HXFxuXFxuIyMgJHtuZXdWZXJzaW9ufVxcblxcbmA7XG5cbiAgZm9yIChjb25zdCBtZXNzYWdlIG9mIGNvbW1pdE1lc3NhZ2VzKSB7XG4gICAgbmV3Q2hhbmdlTG9nICs9IGAtICR7bWVzc2FnZX1cXG5gO1xuICB9XG5cbiAgaWYgKHByZXZpb3VzQ2hhbmdlbG9nTGluZXMubGVuZ3RoID4gMCkge1xuICAgIG5ld0NoYW5nZUxvZyArPSAnXFxuJztcbiAgICBmb3IgKGNvbnN0IGxpbmUgb2YgcHJldmlvdXNDaGFuZ2Vsb2dMaW5lcykge1xuICAgICAgbmV3Q2hhbmdlTG9nICs9IGAke2xpbmV9XFxuYDtcbiAgICB9XG4gIH1cblxuICBhd2FpdCB3cml0ZUZpbGUoY2hhbmdlbG9nUGF0aCwgbmV3Q2hhbmdlTG9nLCAndXRmLTgnKTtcblxuICBjb25zdCBjb2RlVmVyc2lvbiA9IGF3YWl0IGV4ZWNGcm9tUm9vdCgnY29kZSAtLXZlcnNpb24nLCB7XG4gICAgaXNRdWlldDogdHJ1ZSxcbiAgICBzaG91bGRJZ25vcmVFeGl0Q29kZTogdHJ1ZVxuICB9KTtcbiAgY29uc3QgdmVyc2lvbkRlYnVnZ2VyID0gZ2V0TGliRGVidWdnZXIoJ1ZlcnNpb24nKTtcbiAgaWYgKGNvZGVWZXJzaW9uKSB7XG4gICAgdmVyc2lvbkRlYnVnZ2VyKGBQbGVhc2UgdXBkYXRlIHRoZSAke09ic2lkaWFuUGx1Z2luUmVwb1BhdGhzLkNoYW5nZWxvZ01kfSBmaWxlLiBDbG9zZSBWaXN1YWwgU3R1ZGlvIENvZGUgd2hlbiB5b3UgYXJlIGRvbmUuLi5gKTtcbiAgICBhd2FpdCBleGVjRnJvbVJvb3QoWydjb2RlJywgJy13JywgY2hhbmdlbG9nUGF0aF0sIHtcbiAgICAgIGlzUXVpZXQ6IHRydWUsXG4gICAgICBzaG91bGRJZ25vcmVFeGl0Q29kZTogdHJ1ZVxuICAgIH0pO1xuICB9IGVsc2Uge1xuICAgIHZlcnNpb25EZWJ1Z2dlcignQ291bGQgbm90IGZpbmQgVmlzdWFsIFN0dWRpbyBDb2RlIGluIHlvdXIgUEFUSC4gVXNpbmcgY29uc29sZSBtb2RlIGluc3RlYWQuJyk7XG4gICAgYXdhaXQgY3JlYXRlSW50ZXJmYWNlKHByb2Nlc3Muc3RkaW4sIHByb2Nlc3Muc3Rkb3V0KS5xdWVzdGlvbihcbiAgICAgIGBQbGVhc2UgdXBkYXRlIHRoZSAke09ic2lkaWFuUGx1Z2luUmVwb1BhdGhzLkNoYW5nZWxvZ01kfSBmaWxlLiBQcmVzcyBFbnRlciB3aGVuIHlvdSBhcmUgZG9uZS4uLmBcbiAgICApO1xuICB9XG59XG5cbi8qKlxuICogVXBkYXRlcyB0aGUgdmVyc2lvbiBvZiB0aGUgcHJvamVjdCBiYXNlZCBvbiB0aGUgc3BlY2lmaWVkIHVwZGF0ZSB0eXBlLlxuICpcbiAqIFRoaXMgZnVuY3Rpb24gcGVyZm9ybXMgYSBzZXJpZXMgb2YgdGFza3MgdG8gaGFuZGxlIHZlcnNpb24gdXBkYXRlczpcbiAqIDEuIFZhbGlkYXRlcyB0aGUgdmVyc2lvbiB1cGRhdGUgdHlwZS5cbiAqIDIuIENoZWNrcyBpZiBHaXQgYW5kIEdpdEh1YiBDTEkgYXJlIGluc3RhbGxlZC5cbiAqIDMuIFZlcmlmaWVzIHRoYXQgdGhlIEdpdCByZXBvc2l0b3J5IGlzIGNsZWFuLlxuICogNC4gUnVucyBzcGVsbGNoZWNrIGFuZCBsaW50aW5nLlxuICogNS4gQnVpbGRzIHRoZSBwcm9qZWN0LlxuICogNi4gVXBkYXRlcyB2ZXJzaW9uIGluIGZpbGVzIGFuZCBjaGFuZ2Vsb2cuXG4gKiA3LiBBZGRzIHVwZGF0ZWQgZmlsZXMgdG8gR2l0LCB0YWdzIHRoZSBjb21taXQsIGFuZCBwdXNoZXMgdG8gdGhlIHJlcG9zaXRvcnkuXG4gKiA4LiBJZiBhbiBPYnNpZGlhbiBwbHVnaW4sIGNvcGllcyB0aGUgdXBkYXRlZCBtYW5pZmVzdCBhbmQgcHVibGlzaGVzIGEgR2l0SHViIHJlbGVhc2UuXG4gKlxuICogQHBhcmFtIHZlcnNpb25VcGRhdGVUeXBlIC0gVGhlIHR5cGUgb2YgdmVyc2lvbiB1cGRhdGUgdG8gcGVyZm9ybSAobWFqb3IsIG1pbm9yLCBwYXRjaCwgYmV0YSwgb3IgeC55LnpbLWJldGE6dV0pLlxuICogQHBhcmFtIHByZXBhcmVHaXRIdWJSZWxlYXNlIC0gQSBjYWxsYmFjayBmdW5jdGlvbiB0byBwcmVwYXJlIHRoZSBHaXRIdWIgcmVsZWFzZS5cbiAqIEByZXR1cm5zIEEge0BsaW5rIFByb21pc2V9IHRoYXQgcmVzb2x2ZXMgd2hlbiB0aGUgdmVyc2lvbiB1cGRhdGUgaXMgY29tcGxldGUuXG4gKi9cbmV4cG9ydCBhc3luYyBmdW5jdGlvbiB1cGRhdGVWZXJzaW9uKHZlcnNpb25VcGRhdGVUeXBlPzogc3RyaW5nLCBwcmVwYXJlR2l0SHViUmVsZWFzZT86IChuZXdWZXJzaW9uOiBzdHJpbmcpID0+IFByb21pc2U8dm9pZD4pOiBQcm9taXNlPHZvaWQ+IHtcbiAgaWYgKCF2ZXJzaW9uVXBkYXRlVHlwZSkge1xuICAgIGNvbnN0IG5wbU9sZFZlcnNpb24gPSBwcm9jZXNzLmVudlsnbnBtX29sZF92ZXJzaW9uJ107XG4gICAgY29uc3QgbnBtTmV3VmVyc2lvbiA9IHByb2Nlc3MuZW52WyducG1fbmV3X3ZlcnNpb24nXTtcblxuICAgIGlmIChucG1PbGRWZXJzaW9uICYmIG5wbU5ld1ZlcnNpb24pIHtcbiAgICAgIGF3YWl0IHVwZGF0ZVZlcnNpb25JbkZpbGVzKG5wbU9sZFZlcnNpb24pO1xuICAgICAgYXdhaXQgdXBkYXRlVmVyc2lvbihucG1OZXdWZXJzaW9uLCBwcmVwYXJlR2l0SHViUmVsZWFzZSk7XG4gICAgICByZXR1cm47XG4gICAgfVxuXG4gICAgdGhyb3cgbmV3IEVycm9yKCdObyB2ZXJzaW9uIHVwZGF0ZSB0eXBlIHByb3ZpZGVkJyk7XG4gIH1cblxuICBjb25zdCBpc09ic2lkaWFuUGx1Z2luID0gZXhpc3RzU3luYyhyZXNvbHZlUGF0aEZyb21Sb290U2FmZShPYnNpZGlhblBsdWdpblJlcG9QYXRocy5NYW5pZmVzdEpzb24pKSAmJiAoYXdhaXQgcmVhZFBhY2thZ2VKc29uKCkpLm5hbWUgIT09ICdvYnNpZGlhbi1kZXYtdXRpbHMnO1xuXG4gIHZhbGlkYXRlKHZlcnNpb25VcGRhdGVUeXBlKTtcbiAgYXdhaXQgY2hlY2tHaXRJbnN0YWxsZWQoKTtcbiAgYXdhaXQgY2hlY2tHaXRSZXBvQ2xlYW4oKTtcbiAgYXdhaXQgY2hlY2tHaXRIdWJDbGlJbnN0YWxsZWQoKTtcbiAgYXdhaXQgbnBtUnVuKCdmb3JtYXQ6Y2hlY2snKTtcbiAgYXdhaXQgbnBtUnVuKCdzcGVsbGNoZWNrJyk7XG4gIGF3YWl0IG5wbVJ1bignbGludDptZCcpO1xuICBhd2FpdCBucG1SdW4oJ2J1aWxkJyk7XG4gIGF3YWl0IG5wbVJ1bignbGludCcpO1xuXG4gIGNvbnN0IG5ld1ZlcnNpb24gPSBhd2FpdCBnZXROZXdWZXJzaW9uKHZlcnNpb25VcGRhdGVUeXBlKTtcbiAgYXdhaXQgdXBkYXRlVmVyc2lvbkluRmlsZXMobmV3VmVyc2lvbik7XG4gIGlmIChpc09ic2lkaWFuUGx1Z2luKSB7XG4gICAgYXdhaXQgdXBkYXRlVmVyc2lvbkluRmlsZXNGb3JQbHVnaW4obmV3VmVyc2lvbik7XG4gIH1cblxuICBhd2FpdCB1cGRhdGVDaGFuZ2Vsb2cobmV3VmVyc2lvbik7XG4gIGF3YWl0IGFkZFVwZGF0ZWRGaWxlc1RvR2l0KG5ld1ZlcnNpb24pO1xuICBhd2FpdCBhZGRHaXRUYWcobmV3VmVyc2lvbik7XG4gIGF3YWl0IGdpdFB1c2goKTtcbiAgYXdhaXQgcHJlcGFyZUdpdEh1YlJlbGVhc2U/LihuZXdWZXJzaW9uKTtcbiAgYXdhaXQgcHVibGlzaEdpdEh1YlJlbGVhc2UobmV3VmVyc2lvbiwgaXNPYnNpZGlhblBsdWdpbik7XG59XG5cbi8qKlxuICogVXBkYXRlcyB0aGUgdmVyc2lvbiBpbiB2YXJpb3VzIGZpbGVzLCBpbmNsdWRpbmcgYHBhY2thZ2UuanNvbmAsIGBwYWNrYWdlLWxvY2suanNvbmAsXG4gKiBhbmQgT2JzaWRpYW4gcGx1Z2luIG1hbmlmZXN0cyBpZiBhcHBsaWNhYmxlLlxuICpcbiAqIEBwYXJhbSBuZXdWZXJzaW9uIC0gVGhlIG5ldyB2ZXJzaW9uIHN0cmluZyB0byB1cGRhdGUgaW4gdGhlIGZpbGVzLlxuICogQHJldHVybnMgQSB7QGxpbmsgUHJvbWlzZX0gdGhhdCByZXNvbHZlcyB3aGVuIHRoZSB1cGRhdGUgaXMgY29tcGxldGUuXG4gKi9cbmV4cG9ydCBhc3luYyBmdW5jdGlvbiB1cGRhdGVWZXJzaW9uSW5GaWxlcyhuZXdWZXJzaW9uOiBzdHJpbmcpOiBQcm9taXNlPHZvaWQ+IHtcbiAgYXdhaXQgZWRpdFBhY2thZ2VKc29uKChwYWNrYWdlSnNvbikgPT4ge1xuICAgIHBhY2thZ2VKc29uLnZlcnNpb24gPSBuZXdWZXJzaW9uO1xuICB9KTtcblxuICBhd2FpdCBlZGl0UGFja2FnZUxvY2tKc29uKHVwZGF0ZSwgeyBzaG91bGRTa2lwSWZNaXNzaW5nOiB0cnVlIH0pO1xuICBhd2FpdCBlZGl0TnBtU2hyaW5rV3JhcEpzb24odXBkYXRlLCB7IHNob3VsZFNraXBJZk1pc3Npbmc6IHRydWUgfSk7XG5cbiAgZnVuY3Rpb24gdXBkYXRlKHBhY2thZ2VMb2NrSnNvbjogUGFja2FnZUxvY2tKc29uKTogdm9pZCB7XG4gICAgcGFja2FnZUxvY2tKc29uLnZlcnNpb24gPSBuZXdWZXJzaW9uO1xuICAgIGNvbnN0IGRlZmF1bHRQYWNrYWdlID0gcGFja2FnZUxvY2tKc29uLnBhY2thZ2VzPy5bJyddO1xuICAgIGlmIChkZWZhdWx0UGFja2FnZSkge1xuICAgICAgZGVmYXVsdFBhY2thZ2UudmVyc2lvbiA9IG5ld1ZlcnNpb247XG4gICAgfVxuICB9XG59XG5cbi8qKlxuICogVmFsaWRhdGVzIHRoZSB2ZXJzaW9uIHVwZGF0ZSB0eXBlIHRvIGVuc3VyZSBpdCBpcyBlaXRoZXIgYSByZWNvZ25pemVkIHR5cGVcbiAqIG9yIGEgdmFsaWQgbWFudWFsIHZlcnNpb24gc3RyaW5nLlxuICpcbiAqIEBwYXJhbSB2ZXJzaW9uVXBkYXRlVHlwZSAtIFRoZSB2ZXJzaW9uIHVwZGF0ZSB0eXBlIHRvIHZhbGlkYXRlLlxuICogQHRocm93cyBFcnJvciBpZiB0aGUgdmVyc2lvbiB1cGRhdGUgdHlwZSBpcyBpbnZhbGlkLlxuICovXG5leHBvcnQgZnVuY3Rpb24gdmFsaWRhdGUodmVyc2lvblVwZGF0ZVR5cGU6IHN0cmluZyk6IHZvaWQge1xuICBpZiAoZ2V0VmVyc2lvblVwZGF0ZVR5cGUodmVyc2lvblVwZGF0ZVR5cGUpID09PSBWZXJzaW9uVXBkYXRlVHlwZS5JbnZhbGlkKSB7XG4gICAgdGhyb3cgbmV3IEVycm9yKCdJbnZhbGlkIHZlcnNpb24gdXBkYXRlIHR5cGUuIFBsZWFzZSB1c2UgXFwnbWFqb3JcXCcsIFxcJ21pbm9yXFwnLCBcXCdwYXRjaFxcJywgb3IgXFwneC55LnpbLXN1ZmZpeF1cXCcgZm9ybWF0LicpO1xuICB9XG59XG5cbi8qKlxuICogRmV0Y2hlcyB0aGUgbGF0ZXN0IHZlcnNpb24gb2YgT2JzaWRpYW4gZnJvbSB0aGUgR2l0SHViIHJlbGVhc2VzIEFQSS5cbiAqXG4gKiBAcmV0dXJucyBBIHtAbGluayBQcm9taXNlfSB0aGF0IHJlc29sdmVzIHRvIHRoZSBsYXRlc3QgdmVyc2lvbiBvZiBPYnNpZGlhbi5cbiAqL1xuYXN5bmMgZnVuY3Rpb24gZ2V0TGF0ZXN0T2JzaWRpYW5WZXJzaW9uKCk6IFByb21pc2U8c3RyaW5nPiB7XG4gIC8vIGVzbGludC1kaXNhYmxlLW5leHQtbGluZSBuby1yZXN0cmljdGVkLWdsb2JhbHMgLS0gV2UgcnVuIHRoaXMgb3V0c2lkZSBvZiBPYnNpZGlhbiwgc28gd2UgZG9uJ3QgaGF2ZSBgcmVxdWVzdFVybCgpYC5cbiAgY29uc3QgcmVzcG9uc2UgPSBhd2FpdCBmZXRjaCgnaHR0cHM6Ly9hcGkuZ2l0aHViLmNvbS9yZXBvcy9vYnNpZGlhbm1kL29ic2lkaWFuLXJlbGVhc2VzL3JlbGVhc2VzL2xhdGVzdCcpO1xuICBjb25zdCBvYnNpZGlhblJlbGVhc2VzSnNvbiA9IGF3YWl0IHJlc3BvbnNlLmpzb24oKSBhcyBQYXJ0aWFsPE9ic2lkaWFuUmVsZWFzZXNKc29uPjtcbiAgcmV0dXJuIG9ic2lkaWFuUmVsZWFzZXNKc29uLm5hbWUgPz8gdGhyb3dFeHByZXNzaW9uKG5ldyBFcnJvcignQ291bGQgbm90IGZpbmQgdGhlIG5hbWUgb2YgdGhlIGxhdGVzdCBPYnNpZGlhbiByZWxlYXNlJykpO1xufVxuXG5mdW5jdGlvbiBpc0JldGEodmVyc2lvbjogc3RyaW5nKTogYm9vbGVhbiB7XG4gIHJldHVybiB2ZXJzaW9uLmluY2x1ZGVzKFZlcnNpb25VcGRhdGVUeXBlLkJldGEpO1xufVxuXG5mdW5jdGlvbiB0b1NpbmdsZUxpbmUoc3RyOiBzdHJpbmcpOiBzdHJpbmcge1xuICBjb25zdCBsaW5lcyA9IHN0ci5zcGxpdCgvXFxyP1xcbi8pLmZpbHRlcihCb29sZWFuKTtcbiAgcmV0dXJuIGxpbmVzLmpvaW4oJyAnKTtcbn1cblxuYXN5bmMgZnVuY3Rpb24gdXBkYXRlVmVyc2lvbkluRmlsZXNGb3JQbHVnaW4obmV3VmVyc2lvbjogc3RyaW5nKTogUHJvbWlzZTx2b2lkPiB7XG4gIGNvbnN0IG1hbmlmZXN0QmV0YUpzb25QYXRoID0gcmVzb2x2ZVBhdGhGcm9tUm9vdFNhZmUoT2JzaWRpYW5QbHVnaW5SZXBvUGF0aHMuTWFuaWZlc3RCZXRhSnNvbik7XG4gIGlmIChpc0JldGEobmV3VmVyc2lvbikpIHtcbiAgICBhd2FpdCBjcChcbiAgICAgIHJlc29sdmVQYXRoRnJvbVJvb3RTYWZlKE9ic2lkaWFuUGx1Z2luUmVwb1BhdGhzLk1hbmlmZXN0SnNvbiksXG4gICAgICBtYW5pZmVzdEJldGFKc29uUGF0aCxcbiAgICAgIHsgZm9yY2U6IHRydWUgfVxuICAgICk7XG4gICAgYXdhaXQgZWRpdEpzb248TWFuaWZlc3Q+KE9ic2lkaWFuUGx1Z2luUmVwb1BhdGhzLk1hbmlmZXN0QmV0YUpzb24sIChtYW5pZmVzdCkgPT4ge1xuICAgICAgbWFuaWZlc3QudmVyc2lvbiA9IG5ld1ZlcnNpb247XG4gICAgfSk7XG4gIH0gZWxzZSB7XG4gICAgY29uc3QgbGF0ZXN0T2JzaWRpYW5WZXJzaW9uID0gYXdhaXQgZ2V0TGF0ZXN0T2JzaWRpYW5WZXJzaW9uKCk7XG5cbiAgICBhd2FpdCBlZGl0SnNvbjxNYW5pZmVzdD4oT2JzaWRpYW5QbHVnaW5SZXBvUGF0aHMuTWFuaWZlc3RKc29uLCAobWFuaWZlc3QpID0+IHtcbiAgICAgIG1hbmlmZXN0Lm1pbkFwcFZlcnNpb24gPSBsYXRlc3RPYnNpZGlhblZlcnNpb247XG4gICAgICBtYW5pZmVzdC52ZXJzaW9uID0gbmV3VmVyc2lvbjtcbiAgICB9KTtcblxuICAgIGF3YWl0IGVkaXRKc29uPFJlY29yZDxzdHJpbmcsIHN0cmluZz4+KE9ic2lkaWFuUGx1Z2luUmVwb1BhdGhzLlZlcnNpb25zSnNvbiwgKHZlcnNpb25zKSA9PiB7XG4gICAgICB2ZXJzaW9uc1tuZXdWZXJzaW9uXSA9IGxhdGVzdE9ic2lkaWFuVmVyc2lvbjtcbiAgICB9KTtcblxuICAgIGlmIChleGlzdHNTeW5jKG1hbmlmZXN0QmV0YUpzb25QYXRoKSkge1xuICAgICAgYXdhaXQgcm0obWFuaWZlc3RCZXRhSnNvblBhdGgpO1xuICAgIH1cbiAgfVxuXG4gIGF3YWl0IGNvcHlVcGRhdGVkTWFuaWZlc3QoKTtcbn1cbiJdLAogICJtYXBwaW5ncyI6ICI7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7QUFBQTtBQUFBO0FBQUE7QUFBQTtBQUFBO0FBQUE7QUFBQTtBQUFBO0FBQUE7QUFBQTtBQUFBO0FBQUE7QUFBQTtBQUFBO0FBQUE7QUFBQTtBQUFBO0FBQUE7QUFBQTtBQUFBO0FBV0EsbUJBQStCO0FBQy9CLG1CQUFnQztBQUNoQyxxQ0FBd0M7QUFDeEMsa0JBQXFCO0FBQ3JCLG9CQUEyQjtBQUMzQixnQkFB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