swig-cli
Version:
Swig is a simple CLI tool for automating dev workflows via compositions of series and parallel tasks.
3 lines • 77.3 kB
JavaScript
#!/usr/bin/env node
import{spawn as e}from"node:child_process";import s from"node:fs";import*as t from"node:path";import i from"node:path";import{pathToFileURL as n}from"node:url";function o(e,...s){console.log(e,...s)}var r;!function(e){e.RESET="[0m",e.RED="[31m",e.GREEN="[32m",e.YELLOW="[33m",e.CYAN="[96m",e.GRAY="[90m",e.PURPLE="[35m"}(r||(r={}));const a=(e,s)=>`${s}${e}${r.RESET}`,l=e=>a(e,r.RED),m=e=>a(e,r.CYAN),c=e=>a(e,r.GRAY),h=e=>a(e,r.YELLOW);function p(e){if(!e)throw new Error("rawVersionString is required");try{const s=e.replace(/[^0-9.]/g,"").split(".");return{raw:e,major:s.length>0?parseInt(s[0],10):0,minor:s.length>1?parseInt(s[1],10):0,patch:s.length>2?parseInt(s[2],10):0}}catch(e){return}}function g(e){return e.major<18||18===e.major&&e.minor<19}const d=e=>{if("function"!=typeof e)return!1;return!(!1===Object.getOwnPropertyDescriptor(e,"prototype")?.writable)};function u(e){return Array.isArray(e)&&2===e.length&&"string"==typeof e[0]&&d(e[1])}const f=["swigfile.cjs","swigfile.mjs","swigfile.js","swigfile.ts"];class w{value;isCommand;constructor(e,s){this.value=e,this.isCommand=s}matches=e=>this.value===e.id}class x{isCommonJS="function"==typeof require&&"object"==typeof module&&module.exports;isEsm=!this.isCommonJS;versionString="1.0.4";cwd=process.cwd();seriesCounter=1;parallelCounter=1;listCommand={id:"list",names:["list","ls","l"],alternateNames:["-l","--list"],description:"List available tasks (default)",example:"swig list"};helpCommand={id:"help",names:["help","h"],alternateNames:["-h","--help"],description:"Show help message",example:"swig help"};versionCommand={id:"version",names:["version","v"],alternateNames:["-v","--version"],description:"Print version number",example:"swig version"};filterCommand={id:"filter",names:["filter","f"],alternateNames:["-f","--filter"],description:"Filter and list tasks by name",example:"swig filter pattern"};commandDescriptors=[{id:"task",names:["<taskName>"],alternateNames:[],description:'Run a "task", which is an async function exported from your swigfile',example:"swig taskName"},this.listCommand,this.helpCommand,this.versionCommand,this.filterCommand];constructor(){}async runMainAsync(){try{await this.main(),this.okExit()}catch(e){console.error(e),this.failureExit("An unexpected error occurred")}}series=(e,...s)=>async()=>{for(const t of[e,...s])await this.runTask(this.getLogNameAndTask(t))};parallel=(...e)=>async()=>{const s=e.map((e=>this.runTask(this.getLogNameAndTask(e)))),t=(await Promise.allSettled(s)).filter((e=>"rejected"===e.status));if(t.length>0){throw t.map((e=>e.reason))}};async runTask(e){const s=Date.now();this.logFormattedStartMessage(e.logName,s),await e.task();const t=Date.now(),i=t-s;this.logFormattedEndMessage(e.logName,t,i)}getLogNameAndTask(e){if(this.throwIfNotTaskOrNamedTask(e),u(e))return{logName:e[0],task:e[1]};let s=e.name;return"innerSeries"===s?(s=`nested_series_${this.seriesCounter.toString()}`,this.seriesCounter++):"innerParallel"===s?(s=`nested_parallel_${this.parallelCounter.toString()}`,this.parallelCounter++):s||(s="anonymous"),{logName:s,task:e}}throwIfNotTaskOrNamedTask(e){if(!u(e)&&!d(e))throw new Error(`A param passed to "series" or "parallel" was not a Task (function) or a NamedTask ([string,function] tuple): ${e}`)}getTimestampPrefix(e){const s=String(e.getHours()).padStart(2,"0"),t=String(e.getMinutes()).padStart(2,"0"),i=String(e.getSeconds()).padStart(2,"0"),n=String(e.getMilliseconds()).padStart(3,"0");return c(`[${s}:${t}:${i}.${n}]`)}logFormattedStartMessage(e,s){o(`${`${this.getTimestampPrefix(new Date(s))} `}Starting 🚀 ${m(e)}`)}logFormattedEndMessage(e,s,t){var i;o(`${`${this.getTimestampPrefix(new Date(s))} `}Finished ✅ ${m(e)} after ${i=this.humanizeTime(t),a(i,r.PURPLE)}`)}humanizeTime(e){let s,t;if(e<1e3)return`${e} ms`;e<6e4?(s=e/1e3,t="second"):e<36e5?(s=e/6e4,t="minute"):(s=e/36e5,t="hour");let i=s.toFixed(2);return i.endsWith(".00")?i=i.slice(0,-3):i.endsWith("0")&&(i=i.slice(0,-1)),"1"!==i&&(t+="s"),`${i} ${t}`}getTaskFilePath(){for(const e of f){const i=t.resolve(this.cwd,e);if(s.existsSync(i))return this.isEsm?n(i).href:i}return null}getStartMessage(e,s){const i=s.isCommand?"Command":"Task";c("use "),c("for more info");const n=e?t.basename(e):"";m(this.isEsm?"ESM":"CommonJS");const o=`Version: ${m(this.versionString)}`;return`[ ${i}: ${m(s.value)} ][ Swigfile: ${m(n)} ][ ${o} ]`}getFinishedMessage(e,s){const t=Date.now()-e;var i;return`[ ${`Result: ${s?l("failed"):(i="success",a(i,r.GREEN))}`} ][ ${`Total duration: ${a(this.humanizeTime(t),s?r.YELLOW:r.GREEN)}`} ]`}getCliParam(){const e=process.argv[2];if(!e)return new w(this.listCommand.id,!0);const s=this.commandDescriptors.find((s=>s.names.includes(e.toLowerCase())||s.alternateNames.includes(e.toLowerCase())));if(s)return new w(s.id,!0);return e.replace(/[^a-zA-Z0-9_]/g,"")!==e&&this.failureExit(`Invalid task name: ${e}`),new w(e,!1)}showTaskList(e,s,t){const i=e.map((([e])=>e));o("Available tasks:");for(const e of i)t&&!e.toLowerCase().includes(t.toLowerCase())||o(` ${m(e)}`);return o(this.getFinishedMessage(s)),this.okExit()}showHelpMessage(){o("Usage: swig <command or taskName> [options]"),o("Commands:");for(const e of this.commandDescriptors)o(` ${e.names.join(", ")}${c(` - ${e.description}`)}`),o(` ${c(e.example)}`);return o("Initialize or update a swig project: npx swig-cli-init@latest"),this.okExit()}showVersionMessage(){return o(this.versionString),this.okExit()}getFuncByTaskName(e,s){return e.find((([e])=>e===s))?.[1]}async main(){const e=Date.now(),s=this.getCliParam(),t=this.getTaskFilePath();if(s.value===this.versionCommand.id)return this.showVersionMessage();if(o(this.getStartMessage(t?t.toString():"",s)),s.value===this.helpCommand.id)return this.showHelpMessage();if(!t)return this.failureExit(`Task file not found - must be one of the following: ${f.join(", ")}`);let i,n;const r=t.toString();try{i=await import(r),n=Object.entries(i).filter((([,e])=>d(e)))}catch(e){return r&&r.endsWith(".ts")&&e instanceof Error&&e.message.includes("exports is not defined")&&console.log(`${h("Suggestion:")} try adjusting your tsconfig.json compilerOptions (especially the "module" setting)`),console.error(e),this.failureExit(`Could not import task file ${r}`)}if(s.matches(this.listCommand))return this.showTaskList(n,e);if(s.matches(this.filterCommand)){const s=process.argv[3];return this.showTaskList(n,e,s)}const a=this.getFuncByTaskName(n,s.value);if(!a)return this.failureExit(`Task '${s.value}' not found. Tasks must be exported functions in your swigfile. Try 'swig list' to see available tasks.`);let m=!1;try{await a()}catch(e){m=!0;let s="Error",t=e;Array.isArray(t)&&(1===t.length?t=t[0]:t.length>1&&(s=`Errors (${t.length})`)),o(l(s)),console.error(t)}finally{o(this.getFinishedMessage(e,m)),m&&this.failureExit()}}failureExit(e){e&&console.error(`${l("Error:")} ${e}`),process.exit(1)}okExit(){process.exit(0)}}const y="./node_modules/swig-cli/dist/esm/swigCli.js",S="./node_modules/ts-node/dist/bin-esm.js";class E{swigfilePath="";swigfileName="";packageJsonType="commonjs";swigfileExtension="js";hasTsx=!1;constructor(){}main(){const e=this.populateSwigfileInfo();return e&&(this.swigfilePath,this.swigfileExtension),this.populatePackageJsonTypeOrThrow(),this.packageJsonType,e&&this.warnIfPossibleSwigfileSyntaxMismatch(),this.spawnSwig()}async spawnSwig(){const e=process.argv.slice(2),t="ts"===this.swigfileExtension;let i=y,n=S;t&&"esm"===this.packageJsonType?(i=y,n=S):t&&"commonjs"===this.packageJsonType&&(i="./node_modules/swig-cli/dist/cjs/swigCli.cjs",n="./node_modules/ts-node/dist/bin.js"),!t||this.hasTsx||s.existsSync(n)||this.exitWithError("typescript detected but a dev dependency is missing.\nChoose and install either tsx or ts-node using 'npm i -D tsx' or 'npm i -D ts-node'.");const o=p(process.version),r="node";let a=[i,...e];if(t&&this.hasTsx){const e=function(){try{const e="./node_modules/tsx/package.json";if(!s.existsSync(e))return;const t=s.readFileSync(e,{encoding:"utf-8"});return p(JSON.parse(t).version)}catch(e){return}}();let t="--import";o&&function(e){return 18===e.major&&(17===e.minor||18===e.minor)}(o)&&this.logWarning("Tsx does not work with NodeJS 18.17.x and 18.18.x - downgrade to 18.16.1 (or anywhere below that version) or 18.19.0 (or anywhere above that version)."),o&&g(o)&&(t="--loader"),"--import"===t&&((!e||e.major<4)&&this.logWarning("You may need to upgrade your tsx version to at least 4.x for typescript functionality to work with your version of NodeJS."),"commonjs"===this.packageJsonType&&this.logWarning("Using tsx with a CommonJS project is not fully supported - try ts-node instead, or re-configure your project to use ESM")),a=["--no-warnings",t,"tsx",...a]}else t&&"esm"===this.packageJsonType?a=o&&g(o)?[n,"-T",i,...e]:["--no-warnings","--experimental-loader","ts-node/esm",i,...e]:t&&(a=[n,"-T",i,...e]);return a.join(" "),this.spawnSwigCliAsync(r,a)}populateSwigfileInfo(){let e;for(const t of f)if(e=`./${t}`,s.existsSync(e))return this.swigfilePath=e,this.swigfileName=i.basename(this.swigfilePath),this.swigfileExtension=this.swigfileName.split(".")[1],!0;return!1}populatePackageJsonTypeOrThrow(){const e="./package.json";s.existsSync(e)||this.exitWithError("no package.json found - cannot detect project type");const t=s.readFileSync(e,{encoding:"utf-8"}),i=JSON.parse(t);this.packageJsonType=i.type&&"module"===i.type.toLowerCase()?"esm":"commonjs",i.devDependencies&&i.devDependencies["swig-cli"]||i.dependencies&&i.dependencies["swig-cli"]||this.exitWithError("swig-cli was not found in the project dependencies or devDependencies - install with: npm i -D swig-cli"),(i.devDependencies&&i.devDependencies.tsx||i.dependencies&&i.dependencies.tsx)&&(this.hasTsx=!0)}warnIfPossibleSwigfileSyntaxMismatch(){const e=s.readFileSync(this.swigfilePath,{encoding:"utf-8"});if(""===e.trim())return;const t=this.stripComments(e),i=this.fileStringHasEsm(t),n=this.fileStringHasCommonJs(t),o=i&&n;return!i&&!n||"ts"===this.swigfileExtension&&"commonjs"===this.packageJsonType?void 0:"ts"===this.swigfileExtension&&"esm"===this.packageJsonType&&(o||n)?(this.logWarning(`${this.swigfileName} needs to use only ESM syntax if the package.json type is set to "module".`),void this.logOptionsMatrix()):o?(this.logWarning(`${this.swigfileName} appears to have both ESM and CommonJS syntax, but it should have only one or the other.`),void this.logOptionsMatrix()):void("mjs"===this.swigfileExtension&&i&&!n||"cjs"===this.swigfileExtension&&!i&&n||"js"===this.swigfileExtension&&"esm"===this.packageJsonType&&i||"js"===this.swigfileExtension&&"commonjs"===this.packageJsonType&&n||"ts"===this.swigfileExtension&&"commonjs"===this.packageJsonType||"ts"===this.swigfileExtension&&"esm"===this.packageJsonType&&i||(this.logWarning(`${this.swigfileExtension} appears to use ${i?"ESM":"CommonJS"} syntax and your package.json type is set to ${this.packageJsonType}.`),this.logOptionsMatrix()))}fileStringHasEsm(e){return[/^\s*import\s+\w+\s+from\s+['"].+['"]/m,/^\s*import\s*\{[^}]+\}\s+from\s+['"].+['"]/m,/^\s*export\s+const\s+\w+/m,/^\s*export\s+default\s+\w+/m,/^\s*export\s+function\s+\w+/m,/^\s*export\s+class\s+\w+/m].some((s=>s.test(e)))}fileStringHasCommonJs(e){return[/^\s*const\s+\w+\s+=\s+require\(['"].+['"]\)/m,/^\s*module\.exports\s+=/m,/^\s*exports\.\w+\s+=/m].some((s=>s.test(e)))}stripComments(e){return e.replace(/\/\/.*$|\/\*[\s\S]*?\*\/|([^\\:]|^)\/\/.*$/gm,"")}logWarning(e){o(`${h("[swig-cli] Warning:")} ${e}`)}exitWithError(e){o(`${l("[swig-cli] Error:")} ${e}`),process.exit(1)}logOptionsMatrix(){o("\nAvailable configurations:\n"),function(e){if(0===e.length||0===e[0].length)return;const s=e[0].length,t=[];for(let i=0;i<s;i++)t[i]=Math.max(...e.map((e=>e[i]?.length||0)));const i=" "+t.map((e=>"-".repeat(e))).join(" + ");for(let s=0;s<e.length;s++)o(" "+e[s].map(((e,s)=>e.padEnd(t[s]," "))).join(" | ")),0===s&&o(i)}([["swigfile","package.json type","syntax","notes"],[".cjs","any","CommonJS",""],[".mjs","any","ESM",""],[".js","module","ESM",""],[".js","commonjs","CommonJS",""],[".ts","commonjs","CommonJS","Must have valid `tsconfig.json` options. Must use `ts-node` and NOT `tsx` for CommonJS."],[".ts","module","ESM","Must have valid `tsconfig.json` options. Must have either `tsx` or `ts-node` installed."]]),o("")}spawnSwigCliAsync(s,t){return new Promise((i=>{const n={code:1},o=e(s,t,{stdio:"inherit"});o.pid;const r=e=>{o.kill(),o.unref(),n.code=e,i(n)};process.on("exit",r);const a=["SIGINT","SIGTERM","SIGQUIT"],l=e=>{o.kill(e)};a.forEach((e=>{process.on(e,l)})),o.on("exit",((e,s)=>{n.code=e??1,process.removeListener("exit",r),a.forEach((e=>{process.removeListener(e,l)})),o.removeAllListeners(),i(n)})),o.on("error",(e=>{throw e}))}))}}const k=process.argv.slice(2)[0];["h","help","-h","--help","v","version","-v","--version"].includes(k)?(new x).runMainAsync():(new E).main().then((e=>{e.error&&console.error(e.error),process.exit(e.code)})).catch((e=>{console.error(e),process.exit(42)}));export{E as default};
//# sourceMappingURL=data:application/json;charset=utf-8;base64,eyJ2ZXJzaW9uIjozLCJmaWxlIjoiU3dpZ1N0YXJ0dXBXcmFwcGVyLmpzIiwic291cmNlcyI6WyIuLi8uLi9zcmMvdXRpbHMudHMiLCIuLi8uLi9zcmMvaW5kZXgudHMiLCIuLi8uLi9zcmMvU3dpZy50cyIsIi4uLy4uL3NyYy9Td2lnU3RhcnR1cFdyYXBwZXIudHMiXSwic291cmNlc0NvbnRlbnQiOlsiaW1wb3J0IGZzIGZyb20gJ25vZGU6ZnMnXG5cbmV4cG9ydCBjb25zdCB0cmFjZUVuYWJsZWQgPSBmYWxzZVxuXG5leHBvcnQgZnVuY3Rpb24gbG9nKG1lc3NhZ2U/OiB1bmtub3duLCAuLi5vcHRpb25hbFBhcmFtczogdW5rbm93bltdKSB7XG4gIGNvbnNvbGUubG9nKG1lc3NhZ2UsIC4uLm9wdGlvbmFsUGFyYW1zKVxufVxuXG4vLyBlc2xpbnQtZGlzYWJsZS1uZXh0LWxpbmUgQHR5cGVzY3JpcHQtZXNsaW50L25vLXVudXNlZC12YXJzXG5leHBvcnQgZnVuY3Rpb24gdHJhY2UobWVzc2FnZT86IHVua25vd24sIC4uLm9wdGlvbmFsUGFyYW1zOiB1bmtub3duW10pIHtcbiAgaWYgKHRyYWNlRW5hYmxlZCkge1xuICAgIGNvbnNvbGUubG9nKG1lc3NhZ2UsIC4uLm9wdGlvbmFsUGFyYW1zKVxuICB9XG59XG5cbmV4cG9ydCBlbnVtIEFuc2lDb2xvciB7XG4gIFJFU0VUID0gJ1xceDFiWzBtJyxcbiAgUkVEID0gJ1xceDFiWzMxbScsXG4gIEdSRUVOID0gJ1xceDFiWzMybScsXG4gIFlFTExPVyA9ICdcXHgxYlszM20nLFxuICBDWUFOID0gJ1xceDFiWzk2bScsXG4gIEdSQVkgPSAnXFx4MWJbOTBtJyxcbiAgUFVSUExFID0gJ1xceDFiWzM1bSdcbn1cblxuZXhwb3J0IGNvbnN0IGNvbG9yID0gKHN0cjogc3RyaW5nLCBjb2xvckFuc2lDb2RlOiBBbnNpQ29sb3IpOiBzdHJpbmcgPT4ge1xuICByZXR1cm4gYCR7Y29sb3JBbnNpQ29kZX0ke3N0cn0ke0Fuc2lDb2xvci5SRVNFVH1gXG59XG5cbmV4cG9ydCBjb25zdCByZWQgPSAoc3RyOiBzdHJpbmcpID0+IGNvbG9yKHN0ciwgQW5zaUNvbG9yLlJFRClcbmV4cG9ydCBjb25zdCBncmVlbiA9IChzdHI6IHN0cmluZykgPT4gY29sb3Ioc3RyLCBBbnNpQ29sb3IuR1JFRU4pXG5leHBvcnQgY29uc3QgY3lhbiA9IChzdHI6IHN0cmluZykgPT4gY29sb3Ioc3RyLCBBbnNpQ29sb3IuQ1lBTilcbmV4cG9ydCBjb25zdCBncmF5ID0gKHN0cjogc3RyaW5nKSA9PiBjb2xvcihzdHIsIEFuc2lDb2xvci5HUkFZKVxuZXhwb3J0IGNvbnN0IHB1cnBsZSA9IChzdHI6IHN0cmluZykgPT4gY29sb3Ioc3RyLCBBbnNpQ29sb3IuUFVSUExFKVxuZXhwb3J0IGNvbnN0IHllbGxvdyA9IChzdHI6IHN0cmluZykgPT4gY29sb3Ioc3RyLCBBbnNpQ29sb3IuWUVMTE9XKVxuXG5pbnRlcmZhY2UgU2ltcGxlVmVyc2lvbiB7XG4gIHJhdzogc3RyaW5nXG4gIG1ham9yOiBudW1iZXJcbiAgbWlub3I6IG51bWJlclxuICBwYXRjaDogbnVtYmVyXG59XG5cbmV4cG9ydCBmdW5jdGlvbiBnZXROb2RlVmVyc2lvbigpOiBTaW1wbGVWZXJzaW9uIHwgdW5kZWZpbmVkIHtcbiAgcmV0dXJuIHBhcnNlVmVyc2lvblN0cmluZyhwcm9jZXNzLnZlcnNpb24pXG59XG5cbmV4cG9ydCBmdW5jdGlvbiBwYXJzZVZlcnNpb25TdHJpbmcocmF3VmVyc2lvblN0cmluZzogc3RyaW5nIHwgdW5kZWZpbmVkKTogU2ltcGxlVmVyc2lvbiB8IHVuZGVmaW5lZCB7XG4gIGlmICghcmF3VmVyc2lvblN0cmluZykge1xuICAgIHRocm93IG5ldyBFcnJvcihgcmF3VmVyc2lvblN0cmluZyBpcyByZXF1aXJlZGApXG4gIH1cbiAgdHJ5IHtcbiAgICBjb25zdCBwYXJ0cyA9IHJhd1ZlcnNpb25TdHJpbmcucmVwbGFjZSgvW14wLTkuXS9nLCAnJykuc3BsaXQoJy4nKVxuICAgIHJldHVybiB7XG4gICAgICByYXc6IHJhd1ZlcnNpb25TdHJpbmcsXG4gICAgICBtYWpvcjogcGFydHMubGVuZ3RoID4gMCA/IHBhcnNlSW50KHBhcnRzWzBdLCAxMCkgOiAwLFxuICAgICAgbWlub3I6IHBhcnRzLmxlbmd0aCA+IDEgPyBwYXJzZUludChwYXJ0c1sxXSwgMTApIDogMCxcbiAgICAgIHBhdGNoOiBwYXJ0cy5sZW5ndGggPiAyID8gcGFyc2VJbnQocGFydHNbMl0sIDEwKSA6IDBcbiAgICB9XG4gIH0gY2F0Y2ggKGVycikge1xuICAgIHRyYWNlKGB1bmFibGUgdG8gZGV0ZXJtaW5lIE5vZGVKUyB2ZXJzaW9uYCwgZXJyKVxuICAgIHJldHVybiB1bmRlZmluZWRcbiAgfVxufVxuXG5leHBvcnQgZnVuY3Rpb24gZ2V0VHN4VmVyc2lvbigpOiBTaW1wbGVWZXJzaW9uIHwgdW5kZWZpbmVkIHtcbiAgdHJ5IHtcbiAgICBjb25zdCBwYWNrYWdlSnNvblBhdGggPSAnLi9ub2RlX21vZHVsZXMvdHN4L3BhY2thZ2UuanNvbidcbiAgICBpZiAoIWZzLmV4aXN0c1N5bmMocGFja2FnZUpzb25QYXRoKSkge1xuICAgICAgcmV0dXJuIHVuZGVmaW5lZFxuICAgIH1cbiAgICBjb25zdCBwYWNrYWdlSnNvbkNvbnRlbnRzID0gZnMucmVhZEZpbGVTeW5jKHBhY2thZ2VKc29uUGF0aCwgeyBlbmNvZGluZzogJ3V0Zi04JyB9KVxuICAgIGNvbnN0IHBhY2thZ2VKc29uID0gSlNPTi5wYXJzZShwYWNrYWdlSnNvbkNvbnRlbnRzKVxuICAgIGNvbnN0IHZlcnNpb25TdHJpbmcgPSBwYWNrYWdlSnNvbi52ZXJzaW9uXG4gICAgcmV0dXJuIHBhcnNlVmVyc2lvblN0cmluZyh2ZXJzaW9uU3RyaW5nKVxuICB9IGNhdGNoIChlcnIpIHtcbiAgICB0cmFjZShgZXJyb3IgZ2V0dGluZyB0c3ggdmVyc2lvbmAsIGVycilcbiAgICByZXR1cm4gdW5kZWZpbmVkXG4gIH1cbn1cblxuZXhwb3J0IGZ1bmN0aW9uIGlzTm9kZUxlc3NUaGFuMThEb3QxOShub2RlVmVyc2lvbjogU2ltcGxlVmVyc2lvbik6IGJvb2xlYW4ge1xuICByZXR1cm4gbm9kZVZlcnNpb24ubWFqb3IgPCAxOCB8fCAobm9kZVZlcnNpb24ubWFqb3IgPT09IDE4ICYmIG5vZGVWZXJzaW9uLm1pbm9yIDwgMTkpXG59XG5cbmV4cG9ydCBmdW5jdGlvbiBpc05vZGVWZXJzaW9uSW5jb21wYXRpYmxlV2l0aFRzeChub2RlVmVyc2lvbjogU2ltcGxlVmVyc2lvbik6IGJvb2xlYW4ge1xuICByZXR1cm4gbm9kZVZlcnNpb24ubWFqb3IgPT09IDE4ICYmIChub2RlVmVyc2lvbi5taW5vciA9PT0gMTcgfHwgbm9kZVZlcnNpb24ubWlub3IgPT09IDE4KVxufVxuXG5leHBvcnQgaW50ZXJmYWNlIFNwYXduUmVzdWx0IHtcbiAgY29kZTogbnVtYmVyXG4gIGVycm9yPzogRXJyb3Jcbn1cblxuZXhwb3J0IGZ1bmN0aW9uIGxvZ1RhYmxlKGRhdGE6IHN0cmluZ1tdW10pOiB2b2lkIHtcbiAgaWYgKGRhdGEubGVuZ3RoID09PSAwIHx8IGRhdGFbMF0ubGVuZ3RoID09PSAwKSByZXR1cm5cblxuICBjb25zdCBudW1Db2x1bW5zID0gZGF0YVswXS5sZW5ndGhcbiAgY29uc3QgY29sdW1uV2lkdGhzOiBudW1iZXJbXSA9IFtdXG4gIGZvciAobGV0IGkgPSAwOyBpIDwgbnVtQ29sdW1uczsgaSsrKSB7XG4gICAgY29sdW1uV2lkdGhzW2ldID0gTWF0aC5tYXgoLi4uZGF0YS5tYXAocm93ID0+IHJvd1tpXT8ubGVuZ3RoIHx8IDApKVxuICB9XG5cbiAgY29uc3QgbGluZVNlcGFyYXRvciA9ICcgJyArIGNvbHVtbldpZHRocy5tYXAod2lkdGggPT4gJy0nLnJlcGVhdCh3aWR0aCkpLmpvaW4oJyArICcpXG5cbiAgZm9yIChsZXQgaSA9IDA7IGkgPCBkYXRhLmxlbmd0aDsgaSsrKSB7XG4gICAgY29uc3QgcGFkZGVkUm93QXJyYXkgPSBkYXRhW2ldLm1hcCgoY2VsbCwgY29sSWR4KSA9PiBjZWxsLnBhZEVuZChjb2x1bW5XaWR0aHNbY29sSWR4XSwgJyAnKSlcbiAgICBsb2coJyAnICsgcGFkZGVkUm93QXJyYXkuam9pbignIHwgJykpXG4gICAgaWYgKGkgPT09IDApIGxvZyhsaW5lU2VwYXJhdG9yKVxuICB9XG59XG5cbmV4cG9ydCBjb25zdCBpc0Z1bmN0aW9uID0gKHg6IHVua25vd24pOiBib29sZWFuID0+IHtcbiAgaWYgKHR5cGVvZiB4ICE9PSAnZnVuY3Rpb24nKSB7XG4gICAgcmV0dXJuIGZhbHNlXG4gIH1cbiAgY29uc3QgaXNDbGFzcyA9IE9iamVjdC5nZXRPd25Qcm9wZXJ0eURlc2NyaXB0b3IoeCwgJ3Byb3RvdHlwZScpPy53cml0YWJsZSA9PT0gZmFsc2VcbiAgcmV0dXJuICFpc0NsYXNzXG59XG4iLCJpbXBvcnQgeyBnZXRTd2lnSW5zdGFuY2UgfSBmcm9tICcuL3NpbmdsZXRvbk1hbmFnZXIuanMnXG5pbXBvcnQgeyBpc0Z1bmN0aW9uIH0gZnJvbSAnLi91dGlscy5qcydcblxuLyoqXG4gKiBBbnkgZnVuY3Rpb24gdGhhdCBpcyBhc3luYyBvciByZXR1cm5zIGEgUHJvbWlzZS5cbiAqIFNlZSB7QGxpbmsgVGFza09yTmFtZWRUYXNrfSBmb3IgbW9yZSBpbmZvLlxuICovXG5leHBvcnQgdHlwZSBUYXNrID0gKCkgPT4gUHJvbWlzZTx1bmtub3duPlxuXG4vKipcbiAqIEEgdHVwbGUgKGFycmF5IHdpdGggMiB2YWx1ZXMpIG9mIGBbc3RyaW5nLCBUYXNrXWAgdGhhdCBjYW4gYmUgdXNlZCB0byBwcm92aWRlIGEgbGFiZWwgZm9yIGFuIGFub255bW91cyBmdW5jdGlvbi5cbiAqIFNlZSB7QGxpbmsgVGFza09yTmFtZWRUYXNrfSBmb3IgbW9yZSBpbmZvLlxuICovXG5leHBvcnQgdHlwZSBOYW1lZFRhc2sgPSBbc3RyaW5nLCBUYXNrXVxuXG4vKipcbiAqIEhlbHBlciBtZXRob2QgdG8gZGV0ZXJtaW5lIGlmIHNvbWV0aGluZyBpcyBhIGZ1bmN0aW9uLiBUaGlzIGlzIGFjY29tcGxpc2hlZCBieSByZXR1cm5pbmcgZmFsc2UgaWYgYHR5cGVvZmAgaXMgZXhwbGljaXRseVxuICogc29tZXRoaW5nIG90aGVyIHRoYW4gXCJmdW5jdGlvblwiIGFuZCBhbHNvIHRyaWVzIHRvIGVuc3VyZSBpdCdzIG5vdCBhY3R1YWxseSBhIGNsYXNzIGJ5IGNoZWNraW5nIHRoZSBleGlzdGVuY2UgYW5kIHdyaXRhYmlsaXR5XG4gKiBvZiB0aGUgYXJndW1lbnQncyBcInByb3RvdHlwZVwiIHByb3BlcnR5LlxuICovXG5leHBvcnQgeyBpc0Z1bmN0aW9uIH0gZnJvbSAnLi91dGlscy5qcydcblxuLyoqXG4gKiBUeXBlIGd1YXJkIG1ldGhvZCBmb3Ige0BsaW5rIE5hbWVkVGFza30uXG4gKi9cbmV4cG9ydCBmdW5jdGlvbiBpc05hbWVkVGFzayh2YWx1ZTogdW5rbm93bik6IHZhbHVlIGlzIE5hbWVkVGFzayB7XG4gIHJldHVybiAoXG4gICAgQXJyYXkuaXNBcnJheSh2YWx1ZSlcbiAgICAmJiB2YWx1ZS5sZW5ndGggPT09IDJcbiAgICAmJiB0eXBlb2YgdmFsdWVbMF0gPT09ICdzdHJpbmcnXG4gICAgJiYgaXNGdW5jdGlvbih2YWx1ZVsxXSlcbiAgKVxufVxuXG4vKipcbiAqIGBgYGphdmFzY3JpcHRcbiAqIFRhc2sgfCBOYW1lZFRhc2tcbiAqIGBgYFxuICogICAtIEFueSBmdW5jdGlvbiB0aGF0IGlzIGFzeW5jIG9yIHJldHVybnMgYSBQcm9taXNlXG4gKiAgIC0gQSB0dXBsZSAoYXJyYXkgd2l0aCAyIHZhbHVlcykgb2YgYFtzdHJpbmcsIFRhc2tdYCB0aGF0IGNhbiBiZSB1c2VkIHRvIHByb3ZpZGUgYSBsYWJlbCBmb3IgYW4gYW5vbnltb3VzIGZ1bmN0aW9uXG4gKiBcbiAqIEV4YW1wbGUgdXNlIG9mIHtAbGluayBTd2lnI3Nlcmllc30gYW5kIHtAbGluayBTd2lnI3BhcmFsbGVsfSB3aXRoIHtAbGluayBUYXNrfSBhbmQge0BsaW5rIE5hbWVkVGFza30gcGFyYW1zOlxuICogXG4gKiBgYGBqYXZhc2NyaXB0XG4gKiBzZXJpZXMoXG4gKiAgIHRhc2sxLFxuICogICBbJ3Rhc2syJywgYXN5bmMgKCkgPT4ge31dLFxuICogICB0YXNrMyxcbiAqICAgcGFyYWxsZWwodGFzazQsIFsndGFzazUnLCBhc3luYyAoKSA9PiB7fV0pXG4gKiApXG4gKiBgYGBcbiAqL1xuZXhwb3J0IHR5cGUgVGFza09yTmFtZWRUYXNrID0gVGFzayB8IE5hbWVkVGFza1xuXG4vKipcbiAqIENhbGwgYSBsaXN0IG9mIGFzeW5jIGZ1bmN0aW9ucyB0aGF0IGFyZSBlYWNoIGEge0BsaW5rIFRhc2tPck5hbWVkVGFza30gKHNlZSBiZWxvdykgaW4gb3JkZXIsIHdhaXRpbmcgZm9yIGVhY2ggdG8gY29tcGxldGUgYmVmb3JlIHN0YXJ0aW5nIHRoZSBuZXh0LlxuICogXG4gKiBJZiBhbnkgb2YgdGhlIGZ1bmN0aW9ucyB0aHJvd3MgYW4gZXJyb3IsIHRoZSByZW1haW5pbmcgZnVuY3Rpb25zIHdpbGwgbm90IGJlIGV4ZWN1dGVkLlxuICogXG4gKiBZb3UgbWF5IGFyYml0cmFyaWx5IG5lc3Qgc2VyaWVzIGFuZCBwYXJhbGxlbCBjYWxscyBzaW5jZSB0aGV5IGFyZSBqdXN0IGFzeW5jIGZ1bmN0aW9ucyB0aGF0IHJldHVybiBhIHtAbGluayBUYXNrfS5cbiAqIFxuICogYGBgamF2YXNjcmlwdFxuICogVGFzayB8IE5hbWVkVGFza1xuICogYGBgXG4gKiAgIC0gQW55IGZ1bmN0aW9uIHRoYXQgaXMgYXN5bmMgb3IgcmV0dXJucyBhIFByb21pc2VcbiAqICAgLSBBIHR1cGxlIChhcnJheSB3aXRoIDIgdmFsdWVzKSBvZiBgW3N0cmluZywgVGFza11gIHRoYXQgY2FuIGJlIHVzZWQgdG8gcHJvdmlkZSBhIGxhYmVsIGZvciBhbiBhbm9ueW1vdXMgZnVuY3Rpb25cbiAqIFxuICogRXhhbXBsZSB1c2Ugb2Yge0BsaW5rIHNlcmllc30gYW5kIHtAbGluayBwYXJhbGxlbH0gd2l0aCB7QGxpbmsgVGFza30gYW5kIHtAbGluayBOYW1lZFRhc2t9IHBhcmFtczpcbiAqIFxuICogYGBgamF2YXNjcmlwdFxuICogc2VyaWVzKFxuICogICB0YXNrMSxcbiAqICAgWyd0YXNrMicsIGFzeW5jICgpID0+IHt9XSxcbiAqICAgdGFzazMsXG4gKiAgIHBhcmFsbGVsKHRhc2s0LCBbJ3Rhc2s1JywgYXN5bmMgKCkgPT4ge31dKVxuICogKVxuICogYGBgXG4gKi9cbmV4cG9ydCBjb25zdCBzZXJpZXMgPSAoZmlyc3Q6IFRhc2tPck5hbWVkVGFzaywgLi4ucmVzdDogVGFza09yTmFtZWRUYXNrW10pOiBUYXNrID0+IHtcbiAgY29uc3QgaW5uZXJTZXJpZXMgPSBhc3luYyAoKSA9PiB7XG4gICAgY29uc3Qgc3dpZ0luc3RhbmNlID0gZ2V0U3dpZ0luc3RhbmNlKClcbiAgICBjb25zdCBpbnN0YW5jZVNlcmllcyA9IHN3aWdJbnN0YW5jZS5zZXJpZXMoZmlyc3QsIC4uLnJlc3QpXG4gICAgcmV0dXJuIGluc3RhbmNlU2VyaWVzKClcbiAgfVxuICAvLyBTZXR0aW5nIHRoZSBuYW1lIGV4cGxpY2l0bHkgd2lsbCBwcmV2ZW50IGl0IGZyb20gYmVjb21pbmcgJ2Fub255bW91cycgd2hlbiBleGVjdXRlZCBsYXRlclxuICBPYmplY3QuZGVmaW5lUHJvcGVydHkoaW5uZXJTZXJpZXMsICduYW1lJywgeyB2YWx1ZTogJ2lubmVyU2VyaWVzJywgd3JpdGFibGU6IGZhbHNlIH0pXG4gIHJldHVybiBpbm5lclNlcmllc1xufVxuXG4vKipcbiAqIENhbGwgYSBsaXN0IG9mIGFzeW5jIGZ1bmN0aW9ucyB0aGF0IGFyZSBlYWNoIGEge0BsaW5rIFRhc2tPck5hbWVkVGFza30gKHNlZSBiZWxvdykgaW4gcGFyYWxsZWwuXG4gKiBcbiAqIEVycm9ycyB3aWxsIG5vdCBzdG9wIHRoZSBleGVjdXRpb24gb2Ygb3RoZXIgZnVuY3Rpb25zIGluIHRoZSBjdXJyZW50IChpbm5lcikgcGFyYWxsZWwgbWV0aG9kIGdyb3VwLFxuICogYnV0IGV4ZWN1dGlvbiBvZiBmdXJ0aGVyIG91dGVyIHNlcmllcy9wYXJhbGxlbCBjYWxscyB3aWxsIHN0b3AgYWZ0ZXIgdGhlIGN1cnJlbnQgaW5uZXIgcGFyYWxsZWwgZnVuY3Rpb25zIGNvbXBsZXRlLlxuICogXG4gKiBZb3UgbWF5IGFyYml0cmFyaWx5IG5lc3Qgc2VyaWVzIGFuZCBwYXJhbGxlbCBjYWxscyBzaW5jZSB0aGV5IGFyZSBqdXN0IGFzeW5jIGZ1bmN0aW9ucyB0aGF0IHJldHVybiBhIHtAbGluayBUYXNrfS5cbiAqIFxuICogYGBgamF2YXNjcmlwdFxuICogVGFzayB8IE5hbWVkVGFza1xuICogYGBgXG4gKiAgIC0gQW55IGZ1bmN0aW9uIHRoYXQgaXMgYXN5bmMgb3IgcmV0dXJucyBhIFByb21pc2VcbiAqICAgLSBBIHR1cGxlIChhcnJheSB3aXRoIDIgdmFsdWVzKSBvZiBgW3N0cmluZywgVGFza11gIHRoYXQgY2FuIGJlIHVzZWQgdG8gcHJvdmlkZSBhIGxhYmVsIGZvciBhbiBhbm9ueW1vdXMgZnVuY3Rpb25cbiAqIFxuICogRXhhbXBsZSB1c2Ugb2Yge0BsaW5rIHNlcmllc30gYW5kIHtAbGluayBwYXJhbGxlbH0gd2l0aCB7QGxpbmsgVGFza30gYW5kIHtAbGluayBOYW1lZFRhc2t9IHBhcmFtczpcbiAqIFxuICogYGBgamF2YXNjcmlwdFxuICogc2VyaWVzKFxuICogICB0YXNrMSxcbiAqICAgWyd0YXNrMicsIGFzeW5jICgpID0+IHt9XSxcbiAqICAgdGFzazMsXG4gKiAgIHBhcmFsbGVsKHRhc2s0LCBbJ3Rhc2s1JywgYXN5bmMgKCkgPT4ge31dKVxuICogKVxuICogYGBgXG4gKi9cbmV4cG9ydCBjb25zdCBwYXJhbGxlbCA9IChmaXJzdDogVGFza09yTmFtZWRUYXNrLCAuLi5yZXN0OiBUYXNrT3JOYW1lZFRhc2tbXSk6IFRhc2sgPT4ge1xuICBjb25zdCBpbm5lclBhcmFsbGVsID0gYXN5bmMgKCkgPT4ge1xuICAgIGNvbnN0IHN3aWdJbnN0YW5jZSA9IGdldFN3aWdJbnN0YW5jZSgpXG4gICAgY29uc3QgaW5zdGFuY2VQYXJhbGxlbCA9IHN3aWdJbnN0YW5jZS5wYXJhbGxlbChmaXJzdCwgLi4ucmVzdClcbiAgICByZXR1cm4gaW5zdGFuY2VQYXJhbGxlbCgpXG4gIH1cbiAgLy8gU2V0dGluZyB0aGUgbmFtZSBleHBsaWNpdGx5IHdpbGwgcHJldmVudCBpdCBmcm9tIGJlY29taW5nICdhbm9ueW1vdXMnIHdoZW4gZXhlY3V0ZWQgbGF0ZXJcbiAgT2JqZWN0LmRlZmluZVByb3BlcnR5KGlubmVyUGFyYWxsZWwsICduYW1lJywgeyB2YWx1ZTogJ2lubmVyUGFyYWxsZWwnLCB3cml0YWJsZTogZmFsc2UgfSlcbiAgcmV0dXJuIGlubmVyUGFyYWxsZWxcbn1cbiIsImltcG9ydCBmcyBmcm9tICdub2RlOmZzJ1xuaW1wb3J0ICogYXMgcGF0aCBmcm9tICdub2RlOnBhdGgnXG5pbXBvcnQgeyBwYXRoVG9GaWxlVVJMIH0gZnJvbSAnbm9kZTp1cmwnXG5pbXBvcnQgeyBUYXNrLCBUYXNrT3JOYW1lZFRhc2ssIGlzTmFtZWRUYXNrIH0gZnJvbSAnLi9pbmRleC5qcydcbmltcG9ydCB7IEFuc2lDb2xvciwgY29sb3IsIGN5YW4sIGdyYXksIGdyZWVuLCBpc0Z1bmN0aW9uLCBsb2csIHB1cnBsZSwgcmVkLCB5ZWxsb3cgfSBmcm9tICcuL3V0aWxzLmpzJ1xuXG5jb25zdCBzaG93TW9kZUluU3RhcnRNZXNzYWdlID0gZmFsc2VcbmNvbnN0IHNob3dIZWxwSW5TdGFydE1lc3NhZ2UgPSBmYWxzZVxuXG5pbnRlcmZhY2UgTG9nTmFtZUFuZFRhc2sgeyBsb2dOYW1lOiBzdHJpbmcsIHRhc2s6IFRhc2sgfVxuXG5pbnRlcmZhY2UgQ29tbWFuZERlc2NyaXB0b3IgeyBpZDogc3RyaW5nLCBuYW1lczogc3RyaW5nW10sIGFsdGVybmF0ZU5hbWVzOiBzdHJpbmdbXSwgZGVzY3JpcHRpb246IHN0cmluZywgZXhhbXBsZTogc3RyaW5nIH1cblxudHlwZSBUYXNrc01hcCA9IFtzdHJpbmcsICgoKSA9PiB2b2lkIHwgUHJvbWlzZTx2b2lkPildW11cblxuZXhwb3J0IGNvbnN0IHBvc3NpYmxlVGFza0ZpbGVOYW1lcyA9IFsnc3dpZ2ZpbGUuY2pzJywgJ3N3aWdmaWxlLm1qcycsICdzd2lnZmlsZS5qcycsICdzd2lnZmlsZS50cyddXG5cbmNsYXNzIENsaVBhcmFtIHtcbiAgdmFsdWU6IHN0cmluZ1xuICBpc0NvbW1hbmQ6IGJvb2xlYW5cblxuICBjb25zdHJ1Y3Rvcih2YWx1ZTogc3RyaW5nLCBpc0NvbW1hbmQ6IGJvb2xlYW4pIHtcbiAgICB0aGlzLnZhbHVlID0gdmFsdWVcbiAgICB0aGlzLmlzQ29tbWFuZCA9IGlzQ29tbWFuZFxuICB9XG5cbiAgbWF0Y2hlczogKGNvbW1hbmREZXNjcmlwdG9yOiBDb21tYW5kRGVzY3JpcHRvcikgPT4gYm9vbGVhbiA9IChjb21tYW5kRGVzY3JpcHRvcjogQ29tbWFuZERlc2NyaXB0b3IpID0+IHtcbiAgICByZXR1cm4gdGhpcy52YWx1ZSA9PT0gY29tbWFuZERlc2NyaXB0b3IuaWRcbiAgfVxufVxuXG5leHBvcnQgZGVmYXVsdCBjbGFzcyBTd2lnIHtcbiAgaXNDb21tb25KUyA9IHR5cGVvZiByZXF1aXJlID09PSBcImZ1bmN0aW9uXCIgJiYgdHlwZW9mIG1vZHVsZSA9PT0gXCJvYmplY3RcIiAmJiBtb2R1bGUuZXhwb3J0c1xuICBpc0VzbSA9ICF0aGlzLmlzQ29tbW9uSlNcbiAgcHJpdmF0ZSB2ZXJzaW9uU3RyaW5nOiBzdHJpbmcgPSAnX19WRVJTSU9OX18nIC8vIFRoaXMgaXMgcmVwbGFjZWQgaW4gdGhlIGJ1aWxkIHNjcmlwdFxuICBwcml2YXRlIGN3ZCA9IHByb2Nlc3MuY3dkKClcbiAgcHJpdmF0ZSBzZXJpZXNDb3VudGVyID0gMVxuICBwcml2YXRlIHBhcmFsbGVsQ291bnRlciA9IDFcbiAgcHJpdmF0ZSBsaXN0Q29tbWFuZCA9IHsgaWQ6ICdsaXN0JywgbmFtZXM6IFsnbGlzdCcsICdscycsICdsJ10sIGFsdGVybmF0ZU5hbWVzOiBbJy1sJywgJy0tbGlzdCddLCBkZXNjcmlwdGlvbjogJ0xpc3QgYXZhaWxhYmxlIHRhc2tzIChkZWZhdWx0KScsIGV4YW1wbGU6ICdzd2lnIGxpc3QnIH1cbiAgcHJpdmF0ZSBoZWxwQ29tbWFuZCA9IHsgaWQ6ICdoZWxwJywgbmFtZXM6IFsnaGVscCcsICdoJ10sIGFsdGVybmF0ZU5hbWVzOiBbJy1oJywgJy0taGVscCddLCBkZXNjcmlwdGlvbjogJ1Nob3cgaGVscCBtZXNzYWdlJywgZXhhbXBsZTogJ3N3aWcgaGVscCcgfVxuICBwcml2YXRlIHZlcnNpb25Db21tYW5kID0geyBpZDogJ3ZlcnNpb24nLCBuYW1lczogWyd2ZXJzaW9uJywgJ3YnXSwgYWx0ZXJuYXRlTmFtZXM6IFsnLXYnLCAnLS12ZXJzaW9uJ10sIGRlc2NyaXB0aW9uOiAnUHJpbnQgdmVyc2lvbiBudW1iZXInLCBleGFtcGxlOiAnc3dpZyB2ZXJzaW9uJyB9XG4gIHByaXZhdGUgZmlsdGVyQ29tbWFuZCA9IHsgaWQ6ICdmaWx0ZXInLCBuYW1lczogWydmaWx0ZXInLCAnZiddLCBhbHRlcm5hdGVOYW1lczogWyctZicsICctLWZpbHRlciddLCBkZXNjcmlwdGlvbjogJ0ZpbHRlciBhbmQgbGlzdCB0YXNrcyBieSBuYW1lJywgZXhhbXBsZTogJ3N3aWcgZmlsdGVyIHBhdHRlcm4nIH1cbiAgcHJpdmF0ZSBjb21tYW5kRGVzY3JpcHRvcnM6IENvbW1hbmREZXNjcmlwdG9yW10gPSBbXG4gICAgeyBpZDogJ3Rhc2snLCBuYW1lczogWyc8dGFza05hbWU+J10sIGFsdGVybmF0ZU5hbWVzOiBbXSwgZGVzY3JpcHRpb246ICdSdW4gYSBcInRhc2tcIiwgd2hpY2ggaXMgYW4gYXN5bmMgZnVuY3Rpb24gZXhwb3J0ZWQgZnJvbSB5b3VyIHN3aWdmaWxlJywgZXhhbXBsZTogJ3N3aWcgdGFza05hbWUnIH0sXG4gICAgdGhpcy5saXN0Q29tbWFuZCxcbiAgICB0aGlzLmhlbHBDb21tYW5kLFxuICAgIHRoaXMudmVyc2lvbkNvbW1hbmQsXG4gICAgdGhpcy5maWx0ZXJDb21tYW5kXG4gIF1cblxuICBjb25zdHJ1Y3RvcigpIHsgfVxuXG4gIC8vIEdldCBhbiBpbnN0YW5jZSB3aXRoIHNpbmdsZXRvbk1hbmFnZXIudHMgYW5kIHRoZW4gcnVuIHRoaXMgbWV0aG9kIHRvIHN0YXJ0IHRoZSBDTEkuXG4gIGFzeW5jIHJ1bk1haW5Bc3luYygpIHtcbiAgICB0cnkge1xuICAgICAgYXdhaXQgdGhpcy5tYWluKClcbiAgICAgIHRoaXMub2tFeGl0KClcbiAgICB9IGNhdGNoIChlcnIpIHtcbiAgICAgIGNvbnNvbGUuZXJyb3IoZXJyKVxuICAgICAgdGhpcy5mYWlsdXJlRXhpdCgnQW4gdW5leHBlY3RlZCBlcnJvciBvY2N1cnJlZCcpXG4gICAgfVxuICB9XG5cbiAgLy8gRG9uJ3QgY2FsbCB0aGlzIGRpcmVjdGx5IC0gc2VlIG1vZHVsZSBleHBvcnRzIGluIHNyYy9pbmRleC50cy4gQWxzbyBzZWUgVGFza09yTmFtZWRUYXNrIGZvciBtb3JlIGluZm8uXG4gIHNlcmllcyA9IChmaXJzdDogVGFza09yTmFtZWRUYXNrLCAuLi5yZXN0OiBUYXNrT3JOYW1lZFRhc2tbXSk6IFRhc2sgPT4ge1xuICAgIGNvbnN0IGlubmVyU2VyaWVzID0gYXN5bmMgKCkgPT4ge1xuICAgICAgZm9yIChjb25zdCB0YXNrIG9mIFtmaXJzdCwgLi4ucmVzdF0pIHtcbiAgICAgICAgYXdhaXQgdGhpcy5ydW5UYXNrKHRoaXMuZ2V0TG9nTmFtZUFuZFRhc2sodGFzaykpXG4gICAgICB9XG4gICAgfVxuICAgIHJldHVybiBpbm5lclNlcmllc1xuICB9XG5cbiAgLy8gRG9uJ3QgY2FsbCB0aGlzIGRpcmVjdGx5IC0gc2VlIG1vZHVsZSBleHBvcnRzIGluIHNyYy9pbmRleC50cy4gQWxzbyBzZWUgVGFza09yTmFtZWRUYXNrIGZvciBtb3JlIGluZm8uXG4gIHBhcmFsbGVsID0gKC4uLnRhc2tzOiBUYXNrT3JOYW1lZFRhc2tbXSk6IFRhc2sgPT4ge1xuICAgIGNvbnN0IGlubmVyUGFyYWxsZWwgPSBhc3luYyAoKSA9PiB7XG4gICAgICBjb25zdCBwcm9taXNlczogUHJvbWlzZTx2b2lkPltdID0gdGFza3MubWFwKHRhc2sgPT4ge1xuICAgICAgICByZXR1cm4gdGhpcy5ydW5UYXNrKHRoaXMuZ2V0TG9nTmFtZUFuZFRhc2sodGFzaykpXG4gICAgICB9KVxuICAgICAgY29uc3QgcmVzdWx0cyA9IGF3YWl0IFByb21pc2UuYWxsU2V0dGxlZChwcm9taXNlcylcbiAgICAgIGNvbnN0IHJlamVjdGVkID0gcmVzdWx0cy5maWx0ZXIocmVzdWx0ID0+IHJlc3VsdC5zdGF0dXMgPT09ICdyZWplY3RlZCcpIGFzIFByb21pc2VSZWplY3RlZFJlc3VsdFtdXG4gICAgICBpZiAocmVqZWN0ZWQubGVuZ3RoID4gMCkge1xuICAgICAgICBjb25zdCBlcnJvcnMgPSByZWplY3RlZC5tYXAoKHJlc3VsdCkgPT4gcmVzdWx0LnJlYXNvbilcbiAgICAgICAgdGhyb3cgZXJyb3JzXG4gICAgICB9XG4gICAgfVxuICAgIHJldHVybiBpbm5lclBhcmFsbGVsXG4gIH1cblxuICBwcml2YXRlIGFzeW5jIHJ1blRhc2sobG9nTmFtZUFuZFRhc2s6IExvZ05hbWVBbmRUYXNrKSB7XG4gICAgY29uc3Qgc3RhcnRUaW1lc3RhbXAgPSBEYXRlLm5vdygpXG4gICAgdGhpcy5sb2dGb3JtYXR0ZWRTdGFydE1lc3NhZ2UobG9nTmFtZUFuZFRhc2subG9nTmFtZSwgc3RhcnRUaW1lc3RhbXApXG4gICAgYXdhaXQgbG9nTmFtZUFuZFRhc2sudGFzaygpXG4gICAgY29uc3QgZW5kVGltZXN0YW1wID0gRGF0ZS5ub3coKVxuICAgIGNvbnN0IGR1cmF0aW9uID0gZW5kVGltZXN0YW1wIC0gc3RhcnRUaW1lc3RhbXBcbiAgICB0aGlzLmxvZ0Zvcm1hdHRlZEVuZE1lc3NhZ2UobG9nTmFtZUFuZFRhc2subG9nTmFtZSwgZW5kVGltZXN0YW1wLCBkdXJhdGlvbilcbiAgfVxuXG4gIHByaXZhdGUgZ2V0TG9nTmFtZUFuZFRhc2sodGFza09yTmFtZWRUYXNrOiBUYXNrT3JOYW1lZFRhc2spOiBMb2dOYW1lQW5kVGFzayB7XG4gICAgdGhpcy50aHJvd0lmTm90VGFza09yTmFtZWRUYXNrKHRhc2tPck5hbWVkVGFzaylcblxuICAgIGlmIChpc05hbWVkVGFzayh0YXNrT3JOYW1lZFRhc2spKSB7XG4gICAgICByZXR1cm4geyBsb2dOYW1lOiB0YXNrT3JOYW1lZFRhc2tbMF0sIHRhc2s6IHRhc2tPck5hbWVkVGFza1sxXSB9XG4gICAgfVxuXG4gICAgbGV0IG5hbWUgPSB0YXNrT3JOYW1lZFRhc2submFtZVxuXG4gICAgaWYgKG5hbWUgPT09ICdpbm5lclNlcmllcycpIHtcbiAgICAgIG5hbWUgPSBgbmVzdGVkX3Nlcmllc18ke3RoaXMuc2VyaWVzQ291bnRlci50b1N0cmluZygpfWBcbiAgICAgIHRoaXMuc2VyaWVzQ291bnRlcisrXG4gICAgfSBlbHNlIGlmIChuYW1lID09PSAnaW5uZXJQYXJhbGxlbCcpIHtcbiAgICAgIG5hbWUgPSBgbmVzdGVkX3BhcmFsbGVsXyR7dGhpcy5wYXJhbGxlbENvdW50ZXIudG9TdHJpbmcoKX1gXG4gICAgICB0aGlzLnBhcmFsbGVsQ291bnRlcisrXG4gICAgfSBlbHNlIGlmICghbmFtZSkge1xuICAgICAgbmFtZSA9ICdhbm9ueW1vdXMnXG4gICAgfVxuXG4gICAgcmV0dXJuIHsgbG9nTmFtZTogbmFtZSwgdGFzazogdGFza09yTmFtZWRUYXNrIH1cbiAgfVxuXG4gIHByaXZhdGUgdGhyb3dJZk5vdFRhc2tPck5hbWVkVGFzayh0YXNrT3JOYW1lZFRhc2s6IFRhc2tPck5hbWVkVGFzaykge1xuICAgIGlmICghaXNOYW1lZFRhc2sodGFza09yTmFtZWRUYXNrKSAmJiAhaXNGdW5jdGlvbih0YXNrT3JOYW1lZFRhc2spKSB7XG4gICAgICB0aHJvdyBuZXcgRXJyb3IoYEEgcGFyYW0gcGFzc2VkIHRvIFwic2VyaWVzXCIgb3IgXCJwYXJhbGxlbFwiIHdhcyBub3QgYSBUYXNrIChmdW5jdGlvbikgb3IgYSBOYW1lZFRhc2sgKFtzdHJpbmcsZnVuY3Rpb25dIHR1cGxlKTogJHt0YXNrT3JOYW1lZFRhc2t9YClcbiAgICB9XG4gIH1cblxuICBwcml2YXRlIGdldFRpbWVzdGFtcFByZWZpeChkYXRlOiBEYXRlKSB7XG4gICAgY29uc3QgaG91cnMgPSBTdHJpbmcoZGF0ZS5nZXRIb3VycygpKS5wYWRTdGFydCgyLCAnMCcpXG4gICAgY29uc3QgbWludXRlcyA9IFN0cmluZyhkYXRlLmdldE1pbnV0ZXMoKSkucGFkU3RhcnQoMiwgJzAnKVxuICAgIGNvbnN0IHNlY29uZHMgPSBTdHJpbmcoZGF0ZS5nZXRTZWNvbmRzKCkpLnBhZFN0YXJ0KDIsICcwJylcbiAgICBjb25zdCBtaWxsaXNlY29uZHMgPSBTdHJpbmcoZGF0ZS5nZXRNaWxsaXNlY29uZHMoKSkucGFkU3RhcnQoMywgJzAnKVxuICAgIHJldHVybiBncmF5KGBbJHtob3Vyc306JHttaW51dGVzfToke3NlY29uZHN9LiR7bWlsbGlzZWNvbmRzfV1gKVxuICB9XG5cbiAgcHJpdmF0ZSBsb2dGb3JtYXR0ZWRTdGFydE1lc3NhZ2UodGFza05hbWU6IHN0cmluZywgc3RhcnRUaW1lc3RhbXA6IG51bWJlcikge1xuICAgIGNvbnN0IHByZWZpeCA9IGAke3RoaXMuZ2V0VGltZXN0YW1wUHJlZml4KG5ldyBEYXRlKHN0YXJ0VGltZXN0YW1wKSl9IGBcbiAgICBsb2coYCR7cHJlZml4fVN0YXJ0aW5nIPCfmoAgJHtjeWFuKHRhc2tOYW1lKX1gKVxuICB9XG5cbiAgcHJpdmF0ZSBsb2dGb3JtYXR0ZWRFbmRNZXNzYWdlKHRhc2tOYW1lOiBzdHJpbmcsIGVuZFRpbWVzdGFtcDogbnVtYmVyLCBkdXJhdGlvbjogbnVtYmVyKSB7XG4gICAgY29uc3QgcHJlZml4ID0gYCR7dGhpcy5nZXRUaW1lc3RhbXBQcmVmaXgobmV3IERhdGUoZW5kVGltZXN0YW1wKSl9IGBcbiAgICBsb2coYCR7cHJlZml4fUZpbmlzaGVkIOKchSAke2N5YW4odGFza05hbWUpfSBhZnRlciAke3B1cnBsZSh0aGlzLmh1bWFuaXplVGltZShkdXJhdGlvbikpfWApXG4gIH1cblxuICBwcml2YXRlIGh1bWFuaXplVGltZShtaWxsaXNlY29uZHM6IG51bWJlcik6IHN0cmluZyB7XG4gICAgbGV0IHZhbHVlOiBudW1iZXJcbiAgICBsZXQgdW5pdDogc3RyaW5nXG5cbiAgICBpZiAobWlsbGlzZWNvbmRzIDwgMTAwMCkge1xuICAgICAgcmV0dXJuIGAke21pbGxpc2Vjb25kc30gbXNgXG4gICAgfVxuXG4gICAgaWYgKG1pbGxpc2Vjb25kcyA8IDYwMDAwKSB7XG4gICAgICB2YWx1ZSA9IG1pbGxpc2Vjb25kcyAvIDEwMDBcbiAgICAgIHVuaXQgPSAnc2Vjb25kJ1xuICAgIH0gZWxzZSBpZiAobWlsbGlzZWNvbmRzIDwgMzYwMDAwMCkge1xuICAgICAgdmFsdWUgPSBtaWxsaXNlY29uZHMgLyA2MDAwMFxuICAgICAgdW5pdCA9ICdtaW51dGUnXG4gICAgfSBlbHNlIHtcbiAgICAgIHZhbHVlID0gbWlsbGlzZWNvbmRzIC8gMzYwMDAwMFxuICAgICAgdW5pdCA9ICdob3VyJ1xuICAgIH1cblxuICAgIGxldCBzdHJpbmdWYWx1ZSA9IHZhbHVlLnRvRml4ZWQoMilcblxuICAgIGlmIChzdHJpbmdWYWx1ZS5lbmRzV2l0aCgnLjAwJykpIHtcbiAgICAgIHN0cmluZ1ZhbHVlID0gc3RyaW5nVmFsdWUuc2xpY2UoMCwgLTMpXG4gICAgfSBlbHNlIGlmIChzdHJpbmdWYWx1ZS5lbmRzV2l0aCgnMCcpKSB7XG4gICAgICBzdHJpbmdWYWx1ZSA9IHN0cmluZ1ZhbHVlLnNsaWNlKDAsIC0xKVxuICAgIH1cblxuICAgIGlmIChzdHJpbmdWYWx1ZSAhPT0gJzEnKSB7XG4gICAgICB1bml0ICs9ICdzJ1xuICAgIH1cblxuICAgIHJldHVybiBgJHtzdHJpbmdWYWx1ZX0gJHt1bml0fWBcbiAgfVxuXG4gIHByaXZhdGUgZ2V0VGFza0ZpbGVQYXRoKCk6IHN0cmluZyB8IG51bGwge1xuICAgIGZvciAoY29uc3QgZmlsZW5hbWUgb2YgcG9zc2libGVUYXNrRmlsZU5hbWVzKSB7XG4gICAgICBjb25zdCBmaWxlUGF0aCA9IHBhdGgucmVzb2x2ZSh0aGlzLmN3ZCwgZmlsZW5hbWUpXG4gICAgICBpZiAoZnMuZXhpc3RzU3luYyhmaWxlUGF0aCkpIHtcbiAgICAgICAgaWYgKHRoaXMuaXNFc20pIHtcbiAgICAgICAgICByZXR1cm4gcGF0aFRvRmlsZVVSTChmaWxlUGF0aCkuaHJlZlxuICAgICAgICB9XG4gICAgICAgIHJldHVybiBmaWxlUGF0aFxuICAgICAgfVxuICAgIH1cbiAgICByZXR1cm4gbnVsbFxuICB9XG5cbiAgcHJpdmF0ZSBnZXRTdGFydE1lc3NhZ2UodGFza0ZpbGVQYXRoOiBzdHJpbmcsIGNsaVBhcmFtOiBDbGlQYXJhbSk6IHN0cmluZyB7XG4gICAgY29uc3QgY29tbWFuZE9yVGFza01lc3NhZ2UgPSBjbGlQYXJhbS5pc0NvbW1hbmQgPyAnQ29tbWFuZCcgOiAnVGFzaydcbiAgICBjb25zdCBoZWxwTWVzc2FnZSA9IGBbICR7Z3JheSgndXNlICcpfXN3aWcgaGVscCAke2dyYXkoJ2ZvciBtb3JlIGluZm8nKX0gXWBcbiAgICBjb25zdCB0YXNrRmlsZW5hbWUgPSB0YXNrRmlsZVBhdGggPyBwYXRoLmJhc2VuYW1lKHRhc2tGaWxlUGF0aCkgOiAnJ1xuICAgIGNvbnN0IG1vZGVNZXNzYWdlID0gYFsgTW9kZTogJHtjeWFuKHRoaXMuaXNFc20gPyAnRVNNJyA6ICdDb21tb25KUycpfSBdYFxuICAgIGNvbnN0IHZlcnNpb25NZXNzYWdlID0gYFZlcnNpb246ICR7Y3lhbih0aGlzLnZlcnNpb25TdHJpbmcpfWBcbiAgICByZXR1cm4gYFsgJHtjb21tYW5kT3JUYXNrTWVzc2FnZX06ICR7Y3lhbihjbGlQYXJhbS52YWx1ZSl9IF1bIFN3aWdmaWxlOiAke2N5YW4odGFza0ZpbGVuYW1lKX0gXVsgJHt2ZXJzaW9uTWVzc2FnZX0gXSR7c2hvd01vZGVJblN0YXJ0TWVzc2FnZSA/IG1vZGVNZXNzYWdlIDogJyd9JHtzaG93SGVscEluU3RhcnRNZXNzYWdlID8gaGVscE1lc3NhZ2UgOiAnJ31gXG4gIH1cblxuICBwcml2YXRlIGdldEZpbmlzaGVkTWVzc2FnZShtYWluU3RhcnRUaW1lOiBudW1iZXIsIGhhc0Vycm9ycz86IGJvb2xlYW4pOiBzdHJpbmcge1xuICAgIGNvbnN0IHRvdGFsRHVyYXRpb24gPSBEYXRlLm5vdygpIC0gbWFpblN0YXJ0VGltZVxuICAgIGNvbnN0IHN0YXR1c01lc3NhZ2UgPSBgUmVzdWx0OiAke2hhc0Vycm9ycyA/IHJlZCgnZmFpbGVkJykgOiBncmVlbignc3VjY2VzcycpfWBcbiAgICBjb25zdCBkdXJhdGlvbk1lc3NhZ2UgPSBgVG90YWwgZHVyYXRpb246ICR7Y29sb3IodGhpcy5odW1hbml6ZVRpbWUodG90YWxEdXJhdGlvbiksIGhhc0Vycm9ycyA/IEFuc2lDb2xvci5ZRUxMT1cgOiBBbnNpQ29sb3IuR1JFRU4pfWBcbiAgICByZXR1cm4gYFsgJHtzdGF0dXNNZXNzYWdlfSBdWyAke2R1cmF0aW9uTWVzc2FnZX0gXWBcbiAgfVxuXG4gIHByaXZhdGUgZ2V0Q2xpUGFyYW0oKTogQ2xpUGFyYW0ge1xuICAgIGNvbnN0IGNsaUFyZyA9IHByb2Nlc3MuYXJndlsyXVxuXG4gICAgaWYgKCFjbGlBcmcpIHtcbiAgICAgIHJldHVybiBuZXcgQ2xpUGFyYW0odGhpcy5saXN0Q29tbWFuZC5pZCwgdHJ1ZSlcbiAgICB9XG5cbiAgICBjb25zdCBjb21tYW5kRGVzY3JpcHRvciA9IHRoaXMuY29tbWFuZERlc2NyaXB0b3JzLmZpbmQoZCA9PiBkLm5hbWVzLmluY2x1ZGVzKGNsaUFyZy50b0xvd2VyQ2FzZSgpKSB8fCBkLmFsdGVybmF0ZU5hbWVzLmluY2x1ZGVzKGNsaUFyZy50b0xvd2VyQ2FzZSgpKSlcbiAgICBpZiAoY29tbWFuZERlc2NyaXB0b3IpIHtcbiAgICAgIHJldHVybiBuZXcgQ2xpUGFyYW0oY29tbWFuZERlc2NyaXB0b3IuaWQsIHRydWUpXG4gICAgfVxuXG4gICAgY29uc3QgYXJnV2l0aEludmFsaWRGdW5jdGlvbkNoYXJzU3RyaXBwZWQgPSBjbGlBcmcucmVwbGFjZSgvW15hLXpBLVowLTlfXS9nLCAnJylcbiAgICBpZiAoYXJnV2l0aEludmFsaWRGdW5jdGlvbkNoYXJzU3RyaXBwZWQgIT09IGNsaUFyZykge1xuICAgICAgdGhpcy5mYWlsdXJlRXhpdChgSW52YWxpZCB0YXNrIG5hbWU6ICR7Y2xpQXJnfWApXG4gICAgfVxuXG4gICAgcmV0dXJuIG5ldyBDbGlQYXJhbShjbGlBcmcsIGZhbHNlKVxuICB9XG5cbiAgcHJpdmF0ZSBzaG93VGFza0xpc3QodGFza3M6IFRhc2tzTWFwLCBtYWluU3RhcnRUaW1lOiBudW1iZXIsIGZpbHRlcj86IHN0cmluZykge1xuICAgIGNvbnN0IHRhc2tOYW1lcyA9IHRhc2tzLm1hcCgoW25hbWUsXSkgPT4gbmFtZSlcbiAgICBsb2coYEF2YWlsYWJsZSB0YXNrczpgKVxuICAgIGZvciAoY29uc3QgdGFza05hbWUgb2YgdGFza05hbWVzKSB7XG4gICAgICBpZiAoZmlsdGVyICYmICF0YXNrTmFtZS50b0xvd2VyQ2FzZSgpLmluY2x1ZGVzKGZpbHRlci50b0xvd2VyQ2FzZSgpKSkge1xuICAgICAgICBjb250aW51ZVxuICAgICAgfVxuICAgICAgbG9nKGAgICR7Y3lhbih0YXNrTmFtZSl9YClcbiAgICB9XG4gICAgbG9nKHRoaXMuZ2V0RmluaXNoZWRNZXNzYWdlKG1haW5TdGFydFRpbWUpKVxuICAgIHJldHVybiB0aGlzLm9rRXhpdCgpXG4gIH1cblxuICBwcml2YXRlIHNob3dIZWxwTWVzc2FnZSgpIHtcbiAgICBsb2coYFVzYWdlOiBzd2lnIDxjb21tYW5kIG9yIHRhc2tOYW1lPiBbb3B0aW9uc11gKVxuICAgIGxvZyhgQ29tbWFuZHM6YClcbiAgICBmb3IgKGNvbnN0IGNvbW1hbmREZXNjcmlwdG9yIG9mIHRoaXMuY29tbWFuZERlc2NyaXB0b3JzKSB7XG4gICAgICBsb2coYCAgJHtjb21tYW5kRGVzY3JpcHRvci5uYW1lcy5qb2luKCcsICcpfSR7Z3JheShgIC0gJHtjb21tYW5kRGVzY3JpcHRvci5kZXNjcmlwdGlvbn1gKX1gKVxuICAgICAgbG9nKGAgICAgJHtncmF5KGNvbW1hbmREZXNjcmlwdG9yLmV4YW1wbGUpfWApXG4gICAgfVxuICAgIGxvZyhgSW5pdGlhbGl6ZSBvciB1cGRhdGUgYSBzd2lnIHByb2plY3Q6IG5weCBzd2lnLWNsaS1pbml0QGxhdGVzdGApXG4gICAgcmV0dXJuIHRoaXMub2tFeGl0KClcbiAgfVxuXG4gIHByaXZhdGUgc2hvd1ZlcnNpb25NZXNzYWdlKCkge1xuICAgIGxvZyh0aGlzLnZlcnNpb25TdHJpbmcpXG4gICAgcmV0dXJuIHRoaXMub2tFeGl0KClcbiAgfVxuXG4gIHByaXZhdGUgZ2V0RnVuY0J5VGFza05hbWUodGFza3M6IFRhc2tzTWFwLCB0YXNrTmFtZTogc3RyaW5nKSB7XG4gICAgcmV0dXJuIHRhc2tzLmZpbmQoKFtuYW1lLF0pID0+IG5hbWUgPT09IHRhc2tOYW1lKT8uWzFdXG4gIH1cblxuICBwcml2YXRlIGFzeW5jIG1haW4oKSB7XG4gICAgY29uc3QgbWFpblN0YXJ0VGltZSA9IERhdGUubm93KClcblxuICAgIGNvbnN0IGNsaVBhcmFtOiBDbGlQYXJhbSA9IHRoaXMuZ2V0Q2xpUGFyYW0oKVxuXG4gICAgY29uc3QgdGFza0ZpbGVQYXRoT3JVcmw6IHN0cmluZyB8IFVSTCB8IG51bGwgPSB0aGlzLmdldFRhc2tGaWxlUGF0aCgpIC8vIHN0cmluZyBvciBVUkwgdG8gc3VwcG9ydCBib3RoIEVTTSBhbmQgQ0pTXG5cbiAgICBpZiAoY2xpUGFyYW0udmFsdWUgPT09IHRoaXMudmVyc2lvbkNvbW1hbmQuaWQpIHtcbiAgICAgIHJldHVybiB0aGlzLnNob3dWZXJzaW9uTWVzc2FnZSgpXG4gICAgfVxuXG4gICAgbG9nKHRoaXMuZ2V0U3RhcnRNZXNzYWdlKHRhc2tGaWxlUGF0aE9yVXJsID8gdGFza0ZpbGVQYXRoT3JVcmwudG9TdHJpbmcoKSA6ICcnLCBjbGlQYXJhbSkpXG5cbiAgICBpZiAoY2xpUGFyYW0udmFsdWUgPT09IHRoaXMuaGVscENvbW1hbmQuaWQpIHtcbiAgICAgIHJldHVybiB0aGlzLnNob3dIZWxwTWVzc2FnZSgpXG4gICAgfVxuXG4gICAgaWYgKCF0YXNrRmlsZVBhdGhPclVybCkge1xuICAgICAgcmV0dXJuIHRoaXMuZmFpbHVyZUV4aXQoYFRhc2sgZmlsZSBub3QgZm91bmQgLSBtdXN0IGJlIG9uZSBvZiB0aGUgZm9sbG93aW5nOiAke3Bvc3NpYmxlVGFza0ZpbGVOYW1lcy5qb2luKCcsICcpfWApXG4gICAgfVxuXG4gICAgbGV0IG1vZHVsZTogb2JqZWN0XG4gICAgbGV0IHRhc2tzOiBUYXNrc01hcFxuICAgIGNvbnN0IHN3aWdmaWxlUGF0aCA9IHRhc2tGaWxlUGF0aE9yVXJsLnRvU3RyaW5nKClcbiAgICB0cnkge1xuICAgICAgbW9kdWxlID0gYXdhaXQgaW1wb3J0KHN3aWdmaWxlUGF0aClcbiAgICAgIHRhc2tzID0gT2JqZWN0LmVudHJpZXMobW9kdWxlKS5maWx0ZXIoKFssIHZhbHVlXSkgPT4gaXNGdW5jdGlvbih2YWx1ZSkpXG4gICAgfSBjYXRjaCAoZXJyKSB7XG4gICAgICBpZiAoc3dpZ2ZpbGVQYXRoICYmIHN3aWdmaWxlUGF0aC5lbmRzV2l0aCgnLnRzJykgJiYgZXJyIGluc3RhbmNlb2YgRXJyb3IgJiYgZXJyLm1lc3NhZ2UuaW5jbHVkZXMoJ2V4cG9ydHMgaXMgbm90IGRlZmluZWQnKSkge1xuICAgICAgICBjb25zb2xlLmxvZyhgJHt5ZWxsb3coJ1N1Z2dlc3Rpb246Jyl9IHRyeSBhZGp1c3RpbmcgeW91ciB0c2NvbmZpZy5qc29uIGNvbXBpbGVyT3B0aW9ucyAoZXNwZWNpYWxseSB0aGUgXCJtb2R1bGVcIiBzZXR0aW5nKWApXG4gICAgICB9XG4gICAgICBjb25zb2xlLmVycm9yKGVycilcbiAgICAgIHJldHVybiB0aGlzLmZhaWx1cmVFeGl0KGBDb3VsZCBub3QgaW1wb3J0IHRhc2sgZmlsZSAke3N3aWdmaWxlUGF0aH1gKVxuICAgIH1cblxuICAgIGlmIChjbGlQYXJhbS5tYXRjaGVzKHRoaXMubGlzdENvbW1hbmQpKSB7XG4gICAgICByZXR1cm4gdGhpcy5zaG93VGFza0xpc3QodGFza3MsIG1haW5TdGFydFRpbWUpXG4gICAgfVxuICAgIGlmIChjbGlQYXJhbS5tYXRjaGVzKHRoaXMuZmlsdGVyQ29tbWFuZCkpIHtcbiAgICAgIGNvbnN0IGZpbHRlciA9IHByb2Nlc3MuYXJndlszXVxuICAgICAgcmV0dXJuIHRoaXMuc2hvd1Rhc2tMaXN0KHRhc2tzLCBtYWluU3RhcnRUaW1lLCBmaWx0ZXIpXG4gICAgfVxuXG4gICAgY29uc3Qgcm9vdEZ1bmMgPSB0aGlzLmdldEZ1bmNCeVRhc2tOYW1lKHRhc2tzLCBjbGlQYXJhbS52YWx1ZSlcbiAgICBpZiAoIXJvb3RGdW5jKSB7XG4gICAgICByZXR1cm4gdGhpcy5mYWlsdXJlRXhpdChgVGFzayAnJHtjbGlQYXJhbS52YWx1ZX0nIG5vdCBmb3VuZC4gVGFza3MgbXVzdCBiZSBleHBvcnRlZCBmdW5jdGlvbnMgaW4geW91ciBzd2lnZmlsZS4gVHJ5ICdzd2lnIGxpc3QnIHRvIHNlZSBhdmFpbGFibGUgdGFza3MuYClcbiAgICB9XG5cbiAgICBsZXQgaGFzRXJyb3JzID0gZmFsc2VcbiAgICB0cnkge1xuICAgICAgYXdhaXQgcm9vdEZ1bmMoKVxuICAgIH0gY2F0Y2ggKHJhd0VycjogdW5rbm93bikge1xuICAgICAgaGFzRXJyb3JzID0gdHJ1ZVxuICAgICAgbGV0IGxhYmVsID0gJ0Vycm9yJ1xuICAgICAgbGV0IGVyciA9IHJhd0VyclxuICAgICAgaWYgKEFycmF5LmlzQXJyYXkoZXJyKSkge1xuICAgICAgICBpZiAoZXJyLmxlbmd0aCA9PT0gMSkge1xuICAgICAgICAgIGVyciA9IGVyclswXVxuICAgICAgICB9IGVsc2UgaWYgKGVyci5sZW5ndGggPiAxKSB7XG4gICAgICAgICAgbGFiZWwgPSBgRXJyb3JzICgke2Vyci5sZW5ndGh9KWBcbiAgICAgICAgfVxuICAgICAgfVxuICAgICAgbG9nKHJlZChsYWJlbCkpXG4gICAgICBjb25zb2xlLmVycm9yKGVycilcbiAgICB9IGZpbmFsbHkge1xuICAgICAgbG9nKHRoaXMuZ2V0RmluaXNoZWRNZXNzYWdlKG1haW5TdGFydFRpbWUsIGhhc0Vycm9ycykpXG4gICAgICBpZiAoaGFzRXJyb3JzKSB7XG4gICAgICAgIHRoaXMuZmFpbHVyZUV4aXQoKVxuICAgICAgfVxuICAgIH1cbiAgfVxuXG4gIHByaXZhdGUgZmFpbHVyZUV4aXQobWVzc2FnZT86IHN0cmluZykge1xuICAgIGlmIChtZXNzYWdlKSB7IGNvbnNvbGUuZXJyb3IoYCR7cmVkKCdFcnJvcjonKX0gJHttZXNzYWdlfWApIH1cbiAgICBwcm9jZXNzLmV4aXQoMSlcbiAgfVxuXG4gIHByaXZhdGUgb2tFeGl0KCkge1xuICAgIHByb2Nlc3MuZXhpdCgwKVxuICB9XG59XG4iLCIjIS91c3IvYmluL2VudiBub2RlXG5cbmltcG9ydCB7IHNwYXduIH0gZnJvbSAnbm9kZTpjaGlsZF9wcm9jZXNzJ1xuaW1wb3J0IGZzIGZyb20gJ25vZGU6ZnMnXG5pbXBvcnQgcGF0aCBmcm9tICdub2RlOnBhdGgnXG5pbXBvcnQgU3dpZywgeyBwb3NzaWJsZVRhc2tGaWxlTmFtZXMgfSBmcm9tICcuL1N3aWcuanMnXG5pbXBvcnQgeyBTcGF3blJlc3VsdCwgZ2V0Tm9kZVZlcnNpb24sIGdldFRzeFZlcnNpb24sIGlzTm9kZUxlc3NUaGFuMThEb3QxOSwgaXNOb2RlVmVyc2lvbkluY29tcGF0aWJsZVdpdGhUc3gsIGxvZywgbG9nVGFibGUsIHJlZCwgdHJhY2UsIHllbGxvdyB9IGZyb20gJy4vdXRpbHMuanMnXG5cbnR5cGUgUHJvamVjdFR5cGUgPSAnZXNtJyB8ICdjb21tb25qcydcbnR5cGUgU3dpZ2ZpbGVFeHRlbnNpb24gPSAnbWpzJyB8ICdjanMnIHwgJ2pzJyB8ICd0cydcblxuY29uc3Qgc3dpZ1NjcmlwdEVzbSA9ICcuL25vZGVfbW9kdWxlcy9zd2lnLWNsaS9kaXN0L2VzbS9zd2lnQ2xpLmpzJ1xuY29uc3Qgc3dpZ1NjcmlwdENqcyA9ICcuL25vZGVfbW9kdWxlcy9zd2lnLWNsaS9kaXN0L2Nqcy9zd2lnQ2xpLmNqcydcbmNvbnN0IHRzTm9kZUJpbkNqcyA9ICcuL25vZGVfbW9kdWxlcy90cy1ub2RlL2Rpc3QvYmluLmpzJ1xuY29uc3QgdHNOb2RlQmluRXNtID0gJy4vbm9kZV9tb2R1bGVzL3RzLW5vZGUvZGlzdC9iaW4tZXNtLmpzJ1xuXG5leHBvcnQgZGVmYXVsdCBjbGFzcyBTd2lnU3RhcnR1cFdyYXBwZXIge1xuICBwcml2YXRlIHN3aWdmaWxlUGF0aDogc3RyaW5nID0gJydcbiAgcHJpdmF0ZSBzd2lnZmlsZU5hbWU6IHN0cmluZyA9ICcnXG4gIHByaXZhdGUgcGFja2FnZUpzb25UeXBlOiBQcm9qZWN0VHlwZSA9ICdjb21tb25qcydcbiAgcHJpdmF0ZSBzd2lnZmlsZUV4dGVuc2lvbjogU3dpZ2ZpbGVFeHRlbnNpb24gPSAnanMnXG4gIHByaXZhdGUgaGFzVHN4OiBib29sZWFuID0gZmFsc2VcblxuICBjb25zdHJ1Y3RvcigpIHsgfVxuXG4gIG1haW4oKTogUHJvbWlzZTxTcGF3blJlc3VsdD4ge1xuICAgIHRyYWNlKCctIFN3aWdTdGFydHVwV3JhcHBlciBpcyBjaGVja2luZyBhIGZldyB0aGluZ3MuLi4nKVxuXG4gICAgY29uc3QgaGFzU3dpZ2ZpbGUgPSB0aGlzLnBvcHVsYXRlU3dpZ2ZpbGVJbmZvKClcbiAgICBpZiAoaGFzU3dpZ2ZpbGUpIHtcbiAgICAgIHRyYWNlKGAtIHN3aWdmaWxlOiAke3RoaXMuc3dpZ2ZpbGVQYXRofWApXG4gICAgICB0cmFjZShgLSBzd2lnZmlsZSBleHRlbnNpb246ICR7dGhpcy5zd2lnZmlsZUV4dGVuc2lvbn1gKVxuICAgIH1cblxuICAgIHRoaXMucG9wdWxhdGVQYWNrYWdlSnNvblR5cGVPclRocm93KClcbiAgICB0cmFjZShgLSBwYWNrYWdlLmpzb24gdHlwZTogJHt0aGlzLnBhY2thZ2VKc29uVHlwZX1gKVxuXG4gICAgaWYgKGhhc1N3aWdmaWxlKSB7XG4gICAgICB0aGlzLndhcm5JZlBvc3NpYmxlU3dpZ2ZpbGVTeW50YXhNaXNtYXRjaCgpXG4gICAgfSBlbHNlIHtcbiAgICAgIHRyYWNlKGAtIHN3aWdmaWxlIG5vdCBmb3VuZCAtIHNraXBwaW5nIHN5bnRheCBjaGVja2ApXG4gICAgfVxuXG4gICAgcmV0dXJuIHRoaXMuc3Bhd25Td2lnKClcbiAgfVxuXG4gIHByaXZhdGUgYXN5bmMgc3Bhd25Td2lnKCk6IFByb21pc2U8U3Bhd25SZXN1bHQ+IHtcbiAgICBjb25zdCBwcmVzZXJ2ZWRBcmdzID0gcHJvY2Vzcy5hcmd2LnNsaWNlKDIpXG4gICAgY29uc3QgaXNUeXBlc2NyaXB0ID0gdGhpcy5zd2lnZmlsZUV4dGVuc2lvbiA9PT0gJ3RzJ1xuXG4gICAgbGV0IHN3aWdTY3JpcHQgPSBzd2lnU2NyaXB0RXNtXG4gICAgbGV0IHRzTm9kZUJpbiA9IHRzTm9kZUJpbkVzbVxuXG4gICAgaWYgKGlzVHlwZXNjcmlwdCAmJiB0aGlzLnBhY2thZ2VKc29uVHlwZSA9PT0gJ2VzbScpIHtcbiAgICAgIHN3aWdTY3JpcHQgPSBzd2lnU2NyaXB0RXNtXG4gICAgICB0c05vZGVCaW4gPSB0c05vZGVCaW5Fc21cbiAgICB9IGVsc2UgaWYgKGlzVHlwZXNjcmlwdCAmJiB0aGlzLnBhY2thZ2VKc29uVHlwZSA9PT0gJ2NvbW1vbmpzJykge1xuICAgICAgc3dpZ1NjcmlwdCA9IHN3aWdTY3JpcHRDanNcbiAgICAgIHRzTm9kZUJpbiA9IHRzTm9kZUJpbkNqc1xuICAgIH1cblxuICAgIGlmIChpc1R5cGVzY3JpcHQgJiYgIXRoaXMuaGFzVHN4ICYmICFmcy5leGlzdHNTeW5jKHRzTm9kZUJpbikpIHtcbiAgICAgIHRoaXMuZXhpdFdpdGhFcnJvcihgdHlwZXNjcmlwdCBkZXRlY3RlZCBidXQgYSBkZXYgZGVwZW5kZW5jeSBpcyBtaXNzaW5nLlxcbkNob29zZSBhbmQgaW5zdGFsbCBlaXRoZXIgdHN4IG9yIHRzLW5vZGUgdXNpbmcgJ25wbSBpIC1EIHRzeCcgb3IgJ25wbSBpIC1EIHRzLW5vZGUnLmApXG4gICAgfVxuXG4gICAgY29uc3Qgbm9kZVZlcnNpb24gPSBnZXROb2RlVmVyc2lvbigpXG4gICAgdHJhY2UoYE5vZGVKUyB2ZXJzaW9uOiAke25vZGVWZXJzaW9uPy5yYXd9YClcblxuICAgIGNvbnN0IGNvbW1hbmQgPSAnbm9kZSdcbiAgICBsZXQgc3Bhd25BcmdzID0gW3N3aWdTY3JpcHQsIC4uLnByZXNlcnZlZEFyZ3NdXG4gICAgaWYgKGlzVHlwZXNjcmlwdCAmJiB0aGlzLmhhc1RzeCkge1xuICAgICAgY29uc3QgdHN4VmVyc2lvbiA9IGdldFRzeFZlcnNpb24oKVxuICAgICAgbGV0IGxvYWRlckZsYWcgPSAnLS1pbXBvcnQnXG4gICAgICAvLyBOb2RlSlMgPCAxOC4xOSByZXF1aXJlcyB0aGUgb2xkIFwiLS1sb2FkZXJcIiBmbGFnXG4gICAgICAvLyBJbXBvcnRhbnQ6IHRoZXJlJ3MgYSBidWcgaW4gZWl0aGVyIE5vZGVKUyBvciB0c3ggdGhhdCBwcmV2ZW50cyB0aGUgc3dpZ2ZpbGUgaW1wb3J0IHdoaWNoIGFmZmVjdHMgTm9kZUpTIHZlcnNpb25zXG4gICAgICAvLyBncmVhdGVyIHRoYW4gMTguMTYuMSBhbmQgbGVzcyB0aGFuIDE4LjE5LjAgKGV4Y2x1c2l2ZSkuIFRoZXNlIHZlcnNpb25zIGFyZSBub3Qgc3VwcG9ydGVkIHdpdGggdHN4LlxuICAgICAgaWYgKG5vZGVWZXJzaW9uICYmIGlzTm9kZVZlcnNpb25JbmNvbXBhdGlibGVXaXRoVHN4KG5vZGVWZXJzaW9uKSkge1xuICAgICAgICB0aGlzLmxvZ1dhcm5pbmcoYFRzeCBkb2VzIG5vdCB3b3JrIHdpdGggTm9kZUpTIDE4LjE3LnggYW5kIDE4LjE4LnggLSBkb3duZ3JhZGUgdG8gMTguMTYuMSAob3IgYW55d2hlcmUgYmVsb3cgdGhhdCB2ZXJzaW9uKSBvciAxOC4xOS4wIChvciBhbnl3aGVyZSBhYm92ZSB0aGF0IHZlcnNpb24pLmApXG4gICAgICB9XG4gICAgICBpZiAobm9kZVZlcnNpb24gJiYgaXNOb2RlTGVzc1RoYW4xOERvdDE5KG5vZGVWZXJzaW9uKSkge1xuICAgICAgICBsb2FkZXJGbGFnID0gJy0tbG9hZGVyJ1xuICAgICAgfVxuICAgICAgaWYgKGxvYWRlckZsYWcgPT09ICctLWltcG9ydCcpIHtcbiAgICAgICAgaWYgKCF0c3hWZXJzaW9uIHx8IHRzeFZlcnNpb24ubWFqb3IgPCA0KSB7XG4gICAgICAgICAgdGhpcy5sb2dXYXJuaW5nKGBZb3UgbWF5IG5lZWQgdG8gdXBncmFkZSB5b3VyIHRzeCB2ZXJzaW9uIHRvIGF0IGxlYXN0IDQueCBmb3IgdHlwZXNjcmlwdCBmdW5jdGlvbmFsaXR5IHRvIHdvcmsgd2l0aCB5b3VyIHZlcnNpb24gb2YgTm9kZUpTLmApXG4gICAgICAgIH1cbiAgICAgICAgaWYgKHRoaXMucGFja2FnZUpzb25UeXBlID09PSAnY29tbW9uanMnKSB7XG4gICAgICAgICAgdGhpcy5sb2dXYXJuaW5nKGBVc2luZyB0c3ggd2l0aCBhIENvbW1vbkpTIHByb2plY3QgaXMgbm90IGZ1bGx5IHN1cHBvcnRlZCAtIHRyeSB0cy1ub2RlIGluc3RlYWQsIG9yIHJlLWNvbmZpZ3VyZSB5b3VyIHByb2plY3QgdG8gdXNlIEVTTWApXG4gICAgICAgIH1cbiAgICAgIH1cblxuICAgICAgc3Bhd25BcmdzID0gWyctLW5vLXdhcm5pbmdzJywgbG9hZGVyRmxhZywgJ3RzeCcsIC4uLnNwYXduQXJnc11cbiAgICB9IGVsc2UgaWYgKGlzVHlwZXNjcmlwdCAmJiB0aGlzLnBhY2thZ2VKc29uVHlwZSA9PT0gJ2VzbScpIHtcbiAgICAgIC8vIHRzLW5vZGUgaXMgc3VwZXIgYnJva2VuIGZvciBsb2FkZXJzIGFuZCBlc20gKGFuZCBoYXMgYmVlbiBmb3IgYSBsb25nIHRpbWUuLi4pIC0gdGhpcyBpcyB0aGUgYmVzdCB3ZSBjYW4gZG8gZm9yIG5vdyBmb3IgdGhlIG5ldyBub2RlIDE4LjE5IGlzc3Vlc1xuICAgICAgaWYgKG5vZGVWZXJzaW9uICYmIGlzTm9kZUxlc3NUaGFuMThEb3QxOShub2RlVmVyc2lvbikpIHtcbiAgICAgICAgc3Bhd25BcmdzID0gW3RzTm9kZUJpbiwgJy1UJywgc3dpZ1NjcmlwdCwgLi4ucHJlc2VydmVkQXJnc11cbiAgICAgIH0gZWxzZSB7XG4gICAgICAgIHNwYXduQXJncyA9IFsnLS1uby13YXJuaW5ncycsICctLWV4cGVyaW1lbnRhbC1sb2FkZXInLCAndHMtbm9kZS9lc20nLCBzd2lnU2NyaXB0LCAuLi5wcmVzZXJ2ZWRBcmdzXVxuICAgICAgfVxuICAgIH0gZWxzZSBpZiAoaXNUeXBlc2NyaXB0KSB7XG4gICAgICBzcGF3bkFyZ3MgPSBbdHNOb2RlQmluLCAnLVQnLCBzd2lnU2NyaXB0LCAuLi5wcmVzZXJ2ZWRBcmdzXVxuICAgIH1cblxuICAgIHRyYWNlKGAtIHN3aWctY2xpIHNwYXduIGNvbW1hbmQ6ICR7Y29tbWFuZH0gJHtzcGF3bkFyZ3Muam9pbignICcpfWApXG5cbiAgICByZXR1cm4gdGhpcy5zcGF3blN3aWdDbGlBc3luYyhjb21tYW5kLCBzcGF3bkFyZ3MpXG4gIH1cblxuICBwcml2YXRlIHBvcHVsYXRlU3dpZ2ZpbGVJbmZvKCk6IGJvb2xlYW4ge1xuICAgIGxldCBzd2lnZmlsZVBhdGg6IHN0cmluZ1xuICAgIGZvciAoY29uc3QgZmlsZW5hbWUgb2YgcG9zc2libGVUYXNrRmlsZU5hbWVzKSB7XG4gICAgICBzd2lnZmlsZVBhdGggPSBgLi8ke2ZpbGVuYW1lfWBcbiAgICAgIGlmIChmcy5leGlzdHNTeW5jKHN3aWdmaWxlUGF0aCkpIHtcbiAgICAgICAgdGhpcy5zd2lnZmlsZVBhdGggPSBzd2lnZmlsZVBhdGhcbiAgICAgICAgdGhpcy5zd2lnZmlsZU5hbWUgPSBwYXRoLmJhc2VuYW1lKHRoaXMuc3dpZ2ZpbGVQYXRoKVxuICAgICAgICB0aGlzLnN3aWdmaWxlRXh0ZW5zaW9uID0gdGhpcy5zd2lnZmlsZU5hbWUuc3BsaXQoJy4nKVsxXSBhcyBTd2lnZmlsZUV4dGVuc2lvblxuICAgICAgICByZXR1cm4gdHJ1ZVxuICAgICAgfVxuICAgIH1cblxuICAgIHJldHVybiBmYWxzZVxuICB9XG5cbiAgcHJpdmF0ZSBwb3B1bGF0ZVBhY2thZ2VKc29uVHlwZU9yVGhyb3coKSB7XG4gICAgY29uc3QgcGFja2FnZUpzb25QYXRoID0gJy4vcGFja2FnZS5qc29uJ1xuICAgIGlmICghZnMuZXhpc3RzU3luYyhwYWNrYWdlSnNvblBhdGgpKSB7XG4gICAgICB0aGlzLmV4aXRXaXRoRXJyb3IoJ25vIHBhY2thZ2UuanNvbiBmb3VuZCAtIGNhbm5vdCBkZXRlY3QgcHJvamVjdCB0eXBlJylcbiAgICB9XG4gICAgY29uc3QgcGFja2FnZUpzb25Db250ZW50cyA9IGZzLnJlYWRGaWxlU3luYyhwYWNrYWdlSnNvblBhdGgsIHsgZW5jb2Rpbmc6ICd1dGYtOCcgfSlcbiAgICBjb25zdCBwYWNrYWdlSnNvbiA9IEpTT04ucGFyc2UocGFja2FnZUpzb25Db250ZW50cylcbiAgICB0aGlzLnBhY2thZ2VKc29uVHlwZSA9IHBhY2thZ2VKc29uLnR5cGUgJiYgcGFja2FnZUpzb24udHlwZS50b0xvd2VyQ2FzZSgpID09PSAnbW9kdWxlJyA/ICdlc20nIDogJ2NvbW1vbmpzJ1xuXG4gICAgLy8gQ2hlY2sgdGhhdCBzd2lnLWNsaSBpcyBpbnN0YWxsZWQgYXMgYSBkZXBlbmRlbmN5IG9yIGRldkRlcGVuZGVuY3lcbiAgICBpZiAoKHBhY2thZ2VKc29uLmRldkRlcGVuZGVuY2llcyAmJiBwYWNrYWdlSnNvbi5kZXZEZXBlbmRlbmNpZXNbJ3N3aWctY2xpJ10pIHx8IChwYWNrYWdlSnNvbi5kZXBlbmRlbmNpZXMgJiYgcGFja2FnZUpzb24uZGVwZW5kZW5jaWVzWydzd2lnLWNsaSddKSkge1xuICAgICAgdHJhY2UoJy0gc3dpZy1jbGkgaXMgaW5zdGFsbGVkIGFzIGEgZGVwZW5kZW5jeSBpbiB0aGUgcHJvamVjdCcpXG4gICAgfSBlbHNlIHtcbiAgICAgIHRoaXMuZXhpdFdpdGhFcnJvcihgc3dpZy1jbGkgd2FzIG5vdCBmb3VuZCBpbiB0aGUgcHJvamVjdCBkZXBlbmRlbmNpZXMgb3IgZGV2RGVwZW5kZW5jaWVzIC0gaW5zdGFsbCB3aXRoOiBucG0gaSAtRCBzd2lnLWNsaWApXG4gICAgfVxuXG4gICAgaWYgKChwYWNrYWdlSnNvbi5kZXZEZXBlbmRlbmNpZXMgJiYgcGFja2FnZUpzb24uZGV2RGVwZW5kZW5jaWVzWyd0c3gnXSkgfHwgKHBhY2thZ2VKc29uLmRlcGVuZGVuY2llcyAmJiBwYWNrYWdlSnNvbi5kZXBlbmRlbmNpZXNbJ3RzeCddKSkge1xuICAgICAgdGhpcy5oYXNUc3ggPSB0cnVlXG4gICAgICB0cmFjZSgnLSB0c3ggaXMgaW5zdGFsbGVkIGFzIGEgZGVwZW5kZW5jeSBpbiB0aGUgcHJvamVjdCcpXG4gICAgfVxuICB9XG5cbiAgcHJpdmF0ZSB3YXJuSWZQb3NzaWJsZVN3aWdmaWxlU3ludGF4TWlzbWF0Y2goKSB7XG4gICAgY29uc3Qgc3dpZ2ZpbGVDb250ZW50cyA9IGZzLnJlYWRGaWxlU3luYyh0aGlzLnN3aWdmaWxlUGF0aCwgeyBlbmNvZGluZzogJ3V0Zi04JyB9KVxuXG4gICAgaWYgKHN3aWdmaWxlQ29udGVudHMudHJpbSgpID09PSAnJykgcmV0dXJuXG5cbiAgICBjb25zdCBzd2lnZmlsZUNvbnRlbnRzV2l0aG91dENvbW1lbnRzID0gdGhpcy5zdHJpcENvbW1lbnRzKHN3aWdmaWxlQ29udGVudHMpXG5cbiAgICBjb25zdCBoYXNFc21TeW50YXggPSB0aGlzLmZpbGVTdHJpbmdIYXNFc20oc3dpZ2ZpbGVDb250ZW50c1dpdGhvdXRDb21tZW50cylcbiAgICBjb25zdCBoYXNDb21tb25Kc1N5bnRheCA9IHRoaXMuZmlsZVN0cmluZ0hhc0NvbW1vbkpzKHN3aWdmaWxlQ29udGVudHNXaXRob3V0Q29tbWVu