From c8a360ab8e8bdf5cb7f6566040b5afeab0589a74 Mon Sep 17 00:00:00 2001 From: PaulDalek Date: Wed, 17 Jan 2024 19:56:15 +0100 Subject: [PATCH] Further pool related fixes. --- .env.sample | 2 +- .gitignore | 1 + README.md | 16 +++++++++++++--- dist/index.cjs | 2 -- dist/index.esm.js | 2 -- dist/index.esm.js.map | 1 - lib/browser.js | 25 +++++++++++++++++++------ lib/export.js | 5 +++-- lib/pool.js | 6 ++++-- lib/schemas/config.js | 24 ++++++++++++------------ package-lock.json | 4 ++-- package.json | 2 +- templates/template.html | 35 +++++++++++++++++++---------------- 13 files changed, 75 insertions(+), 50 deletions(-) delete mode 100644 dist/index.cjs delete mode 100644 dist/index.esm.js delete mode 100644 dist/index.esm.js.map diff --git a/.env.sample b/.env.sample index 25498824..69ae794a 100644 --- a/.env.sample +++ b/.env.sample @@ -4,6 +4,7 @@ EXPORT_DEFAULT_CONSTR = chart EXPORT_DEFAULT_HEIGHT = 400 EXPORT_DEFAULT_WIDTH = 600 EXPORT_DEFAULT_SCALE = 1 +EXPORT_RASTERIZATION_TIMEOUT = 1500 # Highcharts config HIGHCHARTS_VERSION = latest @@ -45,7 +46,6 @@ HIGHCHARTS_POOL_ACQUIRE_TIMEOUT = 5000 HIGHCHARTS_POOL_CREATE_TIMEOUT = 5000 HIGHCHARTS_POOL_DESTROY_TIMEOUT = 5000 HIGHCHARTS_POOL_IDLE_TIMEOUT = 30000 -HIGHCHARTS_POOL_RASTERIZATION_TIMEOUT = 1500 HIGHCHARTS_POOL_CREATE_RETRY_INTERVAL = 200 HIGHCHARTS_POOL_REAPER_INTERVAL = 1000 HIGHCHARTS_POOL_BENCHMARKING = false diff --git a/.gitignore b/.gitignore index 6d240d33..44d1686c 100644 --- a/.gitignore +++ b/.gitignore @@ -2,6 +2,7 @@ node_modules/ log/ tests/_temp tmp/ +dist/ .DS_Store .cache diff --git a/README.md b/README.md index 006f777c..ba8a8255 100644 --- a/README.md +++ b/README.md @@ -262,13 +262,16 @@ Loading an additional JSON configuration file can be done by using the `--loadCo These are set as variables in your environment. They take precedence over options from the `lib/schemas/config.js` file. On Linux, use e.g. `export`. ### Export config + - `EXPORT_DEFAULT_TYPE`: The format of the file to export to. Can be jpeg, png, pdf or svg. - `EXPORT_DEFAULT_CONSTR`: The constructor to use. Can be chart, stockChart, mapChart or ganttChart. - `EXPORT_DEFAULT_HEIGHT`: The height of the exported chart. Overrides the option in the chart settings. - `EXPORT_DEFAULT_WIDTH`: The width of the exported chart. Overrides the option in the chart settings. - `EXPORT_DEFAULT_SCALE`: The scale of the exported chart. Ranges between 0.1 and 5.0. +- `EXPORT_RASTERIZATION_TIMEOUT`: The number of milliseconds to wait for rendering a webpage. ### Highcharts config + - `HIGHCHARTS_VERSION`: Highcharts version to use. - `HIGHCHARTS_CDN`: The CDN URL of Highcharts scripts to use. - `HIGHCHARTS_FORCE_FETCH`: Should refetch all the scripts after each server rerun. @@ -277,21 +280,25 @@ These are set as variables in your environment. They take precedence over option - `HIGHCHARTS_INDICATORS`: Highcharts indicators to fetch. ### Custom code config + - `HIGHCHARTS_ALLOW_CODE_EXECUTION`: If set to true, allow for the execution of arbitrary code when exporting. - `HIGHCHARTS_ALLOW_FILE_RESOURCES`: Allow injecting resources from the filesystem. Has no effect when running as a server. ### Server config + - `HIGHCHARTS_SERVER_ENABLE`: If set to true, starts a server on 0.0.0.0. - `HIGHCHARTS_SERVER_HOST`: The hostname of the server. Also starts a server listening on the supplied hostname. - `HIGHCHARTS_SERVER_PORT`: The port to use for the server. Defaults to 7801. ### Server SSL config + - `HIGHCHARTS_SERVER_SSL_ENABLE`: Enables the SSL protocol. - `HIGHCHARTS_SERVER_SSL_FORCE`: If set to true, forces the server to only serve over HTTPS. - `HIGHCHARTS_SERVER_SSL_PORT`: The port on which to run the SSL server. - `HIGHCHARTS_SERVER_SSL_CERT_PATH`: The path to the SSL certificate/key. ### Server rate limiting config + - `HIGHCHARTS_RATE_LIMIT_ENABLE`: Enables rate limiting. - `HIGHCHARTS_RATE_LIMIT_MAX`: Max requests allowed in a one minute. - `HIGHCHARTS_RATE_LIMIT_WINDOW`: The time window in minutes for rate limiting. @@ -301,6 +308,7 @@ These are set as variables in your environment. They take precedence over option - `HIGHCHARTS_RATE_LIMIT_SKIP_TOKEN`: Allows bypassing the rate limiter and should be provided with skipKey argument. ### Pool config + - `HIGHCHARTS_POOL_MIN_WORKERS`: The number of initial workers to spawn. - `HIGHCHARTS_POOL_MAX_WORKERS`: The number of max workers to spawn. - `HIGHCHARTS_POOL_WORK_LIMIT`: The pieces of work that can be performed before restarting process. @@ -308,25 +316,28 @@ These are set as variables in your environment. They take precedence over option - `HIGHCHARTS_POOL_CREATE_TIMEOUT`: The number of milliseconds to wait for creating a resource. - `HIGHCHARTS_POOL_DESTROY_TIMEOUT`: The number of milliseconds to wait for destroying a resource. - `HIGHCHARTS_POOL_IDLE_TIMEOUT`: The number of milliseconds after an idle resource is destroyed. -- `HIGHCHARTS_POOL_RASTERIZATION_TIMEOUT`: The number of milliseconds to wait for rendering a webpage. - `HIGHCHARTS_POOL_CREATE_RETRY_INTERVAL`: The number of milliseconds after the create process is retried in case of fail. - `HIGHCHARTS_POOL_REAPER_INTERVAL`: The number of milliseconds after the check for idle resources to destroy is triggered. - `HIGHCHARTS_POOL_BENCHMARKING`: Enable benchmarking. - `HIGHCHARTS_POOL_LISTEN_TO_PROCESS_EXITS`: Set to false in order to skip attaching process.exit handlers. ### Logging config + - `HIGHCHARTS_LOG_LEVEL`: The log level (0: silent, 1: error, 2: warning, 3: notice, 4: verbose). - `HIGHCHARTS_LOG_FILE`: A name of a log file. The --logDest also needs to be set to enable file logging. - `HIGHCHARTS_LOG_DEST`: The path to store log files. Also enables file logging. ### UI config + - `HIGHCHARTS_UI_ENABLE`: Enables the UI for the export server. - `HIGHCHARTS_UI_ROUTE`: The route to attach the UI to. ### Other config + - `HIGHCHARTS_NO_LOGO`: Skip printing the logo on a startup. Will be replaced by a simple text. ### Proxy config + - `PROXY_SERVER_HOST`: The host of the proxy server to use if exists. - `PROXY_SERVER_PORT`: The port of the proxy server to use if exists. - `PROXY_SERVER_TIMEOUT`: The timeout for the proxy server to use if exists. @@ -350,6 +361,7 @@ _Available options:_ - `--globalOptions`: A stringified JSON or a filename with options to be passed into the Highcharts.setOptions (defaults to `false`). - `--themeOptions`: A stringified JSON or a filename with theme options to be passed into the Highcharts.setOptions (defaults to `false`). - `--batch`: Starts a batch job. A string that contains input/output pairs: "in=out;in=out;.." (defaults to `false`). +- `--rasterizationTimeout`: The number of milliseconds to wait for rendering a webpage (defaults to `1500`). - `--allowCodeExecution`: If set to true, allow for the execution of arbitrary code when exporting (defaults to `false`). - `--allowFileResources`: Allow injecting resources from the filesystem. Has no effect when running as a server (defaults to `true`). - `--customCode`: Custom code to be called before chart initialization. Can be a function, a code that will be wrapped within a function or a filename with the js extension (defaults to `false`). @@ -375,7 +387,6 @@ _Available options:_ - `--createTimeout`: The number of milliseconds to wait for creating a resource (defaults to `5000`). - `--destroyTimeout`: The number of milliseconds to wait for destroying a resource (defaults to `5000`). - `--idleTimeout`: The number of milliseconds after an idle resource is destroyed (defaults to `30000`). -- `--rasterizationTimeout`: The number of milliseconds to wait for rendering a webpage (defaults to `1500`). - `--createRetryInterval`: The number of milliseconds after the create process is retried in case of fail (defaults to `200`). - `--reaperInterval`: The number of milliseconds after the check for idle resources to destroy is triggered (defaults to `1000`). - `--benchmarking`: Enable benchmarking (defaults to `true`). @@ -636,7 +647,6 @@ This package supports both CommonJS and ES modules. - `createTimeout` (default 5000) - The maximum allowed time for each resource create, in milliseconds. - `destroyTimeout` (default 5000) - The maximum allowed time for each resource destroy, in milliseconds. - `idleTimeout` (default 30000) - The maximum allowed time after an idle resource is destroyed, in milliseconds. - - `rasterizationTimeout` (default 1500) - The number of milliseconds to wait for rendering a webpage. - `createRetryInterval` (default 200) - The number of milliseconds after the create process is retried in case of fail. - `reaperInterval` (default 1000) - The number of milliseconds after the check for idle resources to destroy is triggered. - `benchmarking` (default false) - Enable benchmarking. diff --git a/dist/index.cjs b/dist/index.cjs deleted file mode 100644 index 0a8ea4f4..00000000 --- a/dist/index.cjs +++ /dev/null @@ -1,2 +0,0 @@ -"use strict";require("colors");var e=require("fs"),t=require("path"),o=require("body-parser"),r=require("cors"),i=require("express"),n=require("multer"),s=require("http"),a=require("https"),l=require("dotenv"),c=require("express-rate-limit"),p=require("url"),u=require("https-proxy-agent"),d=require("uuid"),h=require("tarn"),g=require("puppeteer"),m=require("node:path"),f=require("node:crypto");require("prompts");var v="undefined"!=typeof document?document.currentScript:null;function y(e){var t=Object.create(null);return e&&Object.keys(e).forEach((function(o){if("default"!==o){var r=Object.getOwnPropertyDescriptor(e,o);Object.defineProperty(t,o,r.get?r:{enumerable:!0,get:function(){return e[o]}})}})),t.default=e,Object.freeze(t)}var b=y(p);l.config();const w={puppeteer:{args:{value:[],type:"string[]",description:"Array of arguments to send to puppeteer."}},highcharts:{version:{value:"latest",envLink:"HIGHCHARTS_VERSION",type:"string",description:"Highcharts version to use."},cdnURL:{value:"https://code.highcharts.com/",envLink:"HIGHCHARTS_CDN",type:"string",description:"The CDN URL of Highcharts scripts to use."},coreScripts:{envLink:"HIGHCHARTS_CORE_SCRIPTS",value:["highcharts","highcharts-more","highcharts-3d"],type:"string[]",description:"Highcharts core scripts to fetch."},modules:{envLink:"HIGHCHARTS_MODULES",value:["stock","map","gantt","exporting","export-data","parallel-coordinates","accessibility","annotations-advanced","boost-canvas","boost","data","draggable-points","static-scale","broken-axis","heatmap","tilemap","timeline","treemap","treegraph","item-series","drilldown","histogram-bellcurve","bullet","funnel","funnel3d","pyramid3d","networkgraph","pareto","pattern-fill","pictorial","price-indicator","sankey","arc-diagram","dependency-wheel","series-label","solid-gauge","sonification","stock-tools","streamgraph","sunburst","variable-pie","variwide","vector","venn","windbarb","wordcloud","xrange","no-data-to-display","drag-panes","debugger","dumbbell","lollipop","cylinder","organization","dotplot","marker-clusters","hollowcandlestick","heikinashi"],type:"string[]",description:"Highcharts modules to fetch."},indicators:{envLink:"HIGHCHARTS_INDICATORS",value:["indicators-all"],type:"string[]",description:"Highcharts indicators to fetch."},scripts:{value:["https://cdnjs.cloudflare.com/ajax/libs/moment.js/2.29.4/moment.min.js","https://cdnjs.cloudflare.com/ajax/libs/moment-timezone/0.5.34/moment-timezone-with-data.min.js"],type:"string[]",description:"Additional direct scripts/optional dependencies (e.g. moment.js)."},forceFetch:{envLink:"HIGHCHARTS_FORCE_FETCH",value:!1,type:"boolean",description:"Should all the scripts be refetched after rerunning the server."}},export:{infile:{value:!1,type:"string",description:"The input file name along with a type (json or svg). It can be a correct JSON or SVG file."},instr:{value:!1,type:"string",description:"An input in a form of a stringified JSON or SVG file. Overrides the --infile."},options:{value:!1,type:"string",description:"An alias for the --instr option."},outfile:{value:!1,type:"string",description:"The output filename along with a type (jpeg, png, pdf or svg). Ignores the --type flag."},type:{envLink:"EXPORT_DEFAULT_TYPE",value:"png",type:"string",description:"The format of the file to export to. Can be jpeg, png, pdf or svg."},constr:{envLink:"EXPORT_DEFAULT_CONSTR",value:"chart",type:"string",description:"The constructor to use. Can be chart, stockChart, mapChart or ganttChart."},defaultHeight:{envLink:"EXPORT_DEFAULT_HEIGHT",value:400,type:"number",description:"The default height of the exported chart. Used when not found any value set."},defaultWidth:{envLink:"EXPORT_DEFAULT_WIDTH",value:600,type:"number",description:"The default width of the exported chart. Used when not found any value set."},defaultScale:{envLink:"EXPORT_DEFAULT_SCALE",value:1,type:"number",description:"The default scale of the exported chart. Ranges between 1 and 5."},height:{type:"number",value:!1,description:"The default height of the exported chart. Overrides the option in the chart settings."},width:{type:"number",value:!1,description:"The width of the exported chart. Overrides the option in the chart settings."},scale:{value:!1,type:"number",description:"The scale of the exported chart. Ranges between 1 and 5."},globalOptions:{value:!1,type:"string",description:"A stringified JSON or a filename with options to be passed into the Highcharts.setOptions."},themeOptions:{value:!1,type:"string",description:"A stringified JSON or a filename with theme options to be passed into the Highcharts.setOptions."},batch:{value:!1,type:"string",description:'Starts a batch job. A string that contains input/output pairs: "in=out;in=out;..".'}},customCode:{allowCodeExecution:{envLink:"HIGHCHARTS_ALLOW_CODE_EXECUTION",value:!1,type:"boolean",description:"If set to true, allow for the execution of arbitrary code when exporting."},allowFileResources:{envLink:"HIGHCHARTS_ALLOW_FILE_RESOURCES",value:!0,type:"boolean",description:"Allow injecting resources from the filesystem. Has no effect when running as a server."},customCode:{value:!1,type:"string",description:"A function to be called before chart initialization. Can be a filename with the js extension."},callback:{value:!1,type:"string",description:"A JavaScript file with a function to run on construction."},resources:{value:!1,type:"string",description:"An additional resource in a form of stringified JSON. It can contain files, js and css sections."},loadConfig:{value:!1,type:"string",description:"A file that contains a pre-defined config to use."},createConfig:{value:!1,type:"string",description:"Allows to set options through a prompt and save in a provided config file."}},server:{enable:{envLink:"HIGHCHARTS_SERVER_ENABLE",value:!1,type:"boolean",cliName:"enableServer",description:"If set to true, starts a server on 0.0.0.0."},host:{envLink:"HIGHCHARTS_SERVER_HOST",value:"0.0.0.0",type:"string",description:"The hostname of the server. Also starts a server listening on the supplied hostname."},port:{envLink:"HIGHCHARTS_SERVER_PORT",value:7801,type:"number",description:"The port to use for the server. Defaults to 7801."},ssl:{enable:{envLink:"HIGHCHARTS_SERVER_SSL_ENABLE",value:!1,type:"boolean",cliName:"enableSsl",description:"Enables the SSL protocol."},force:{envLink:"HIGHCHARTS_SERVER_SSL_FORCE",value:!1,type:"boolean",cliName:"sslForced",description:"If set to true, forces the server to only serve over HTTPS."},port:{envLink:"HIGHCHARTS_SERVER_SSL_PORT",value:443,type:"number",cliName:"sslPort",description:"The port on which to run the SSL server."},certPath:{envLink:"HIGHCHARTS_SSL_CERT_PATH",value:"",type:"string",description:"The path to the SSL certificate/key."}},rateLimiting:{enable:{envLink:"HIGHCHARTS_RATE_LIMIT_ENABLE",value:!1,type:"boolean",cliName:"enableRateLimiting",description:"Enables rate limiting."},maxRequests:{envLink:"HIGHCHARTS_RATE_LIMIT_MAX",value:10,type:"number",description:"Max requests allowed in a one minute."},window:{envLink:"HIGHCHARTS_RATE_LIMIT_WINDOW",value:1,type:"number",description:"The time window in minutes for rate limiting."},delay:{envLink:"HIGHCHARTS_RATE_LIMIT_DELAY",value:0,type:"number",description:"The amount to delay each successive request before hitting the max."},trustProxy:{envLink:"HIGHCHARTS_RATE_LIMIT_TRUST_PROXY",value:!1,type:"boolean",description:"Set this to true if behind a load balancer."},skipKey:{envLink:"HIGHCHARTS_RATE_LIMIT_SKIP_KEY",value:"",type:"number|string",description:"Allows bypassing the rate limiter and should be provided with skipToken argument."},skipToken:{envLink:"HIGHCHARTS_RATE_LIMIT_SKIP_TOKEN",value:"",type:"number|string",description:"Allows bypassing the rate limiter and should be provided with skipKey argument."}}},pool:{minWorkers:{envLink:"HIGHCHARTS_POOL_MIN_WORKERS",value:4,type:"number",description:"The number of initial workers to spawn."},maxWorkers:{envLink:"HIGHCHARTS_POOL_MAX_WORKERS",value:8,type:"number",description:"The number of max workers to spawn."},workLimit:{envLink:"HIGHCHARTS_POOL_WORK_LIMIT",value:40,type:"number",description:"The pieces of work that can be performed before restarting process."},acquireTimeout:{envLink:"HIGHCHARTS_POOL_ACQUIRE_TIMEOUT",value:5e3,type:"number",description:"The number of milliseconds to wait for acquiring a resource."},createTimeout:{envLink:"HIGHCHARTS_POOL_CREATE_TIMEOUT",value:5e3,type:"number",description:"The number of milliseconds to wait for creating a resource."},destroyTimeout:{envLink:"HIGHCHARTS_POOL_DESTROY_TIMEOUT",value:5e3,type:"number",description:"The number of milliseconds to wait for destroying a resource."},idleTimeout:{envLink:"HIGHCHARTS_POOL_IDLE_TIMEOUT",value:3e4,type:"number",description:"The number of milliseconds after an idle resource is destroyed."},rasterizationTimeout:{envLink:"HIGHCHARTS_POOL_RASTERIZATION_TIMEOUT",value:1500,type:"number",description:"The number of milliseconds to wait for rendering a webpage."},createRetryInterval:{envLink:"HIGHCHARTS_POOL_CREATE_RETRY_INTERVAL",value:200,type:"number",description:"The number of milliseconds after the create process is retried in case of fail."},reaperInterval:{envLink:"HIGHCHARTS_POOL_REAPER_INTERVAL",value:1e3,type:"number",description:"The number of milliseconds after the check for idle resources to destroy is triggered."},benchmarking:{envLink:"HIGHCHARTS_POOL_BENCHMARKING",value:!1,type:"boolean",description:"Enable benchmarking."},listenToProcessExits:{envLink:"HIGHCHARTS_POOL_LISTEN_TO_PROCESS_EXITS",value:!0,type:"boolean",description:"Set to false in order to skip attaching process.exit handlers."}},logging:{level:{envLink:"HIGHCHARTS_LOG_LEVEL",value:4,type:"number",cliName:"logLevel",description:"The log level (0: silent, 1: error, 2: warning, 3: notice, 4: verbose)."},file:{envLink:"HIGHCHARTS_LOG_FILE",value:"highcharts-export-server.log",type:"string",cliName:"logFile",description:"A name of a log file. The --logDest also needs to be set to enable file logging."},dest:{envLink:"HIGHCHARTS_LOG_DEST",value:"log/",type:"string",cliName:"logDest",description:"The path to store log files. Also enables file logging."}},ui:{enable:{envLink:"HIGHCHARTS_UI_ENABLE",value:!1,type:"boolean",cliName:"enableUi",description:"Enables the UI for the export server."},route:{envLink:"HIGHCHARTS_UI_ROUTE",value:"/",type:"string",cliName:"uiRoute",description:"The route to attach the UI to."}},other:{noLogo:{envLink:"HIGHCHARTS_NO_LOGO",value:!1,type:"boolean",description:"Skip printing the logo on a startup. Will be replaced by a simple text."}},payload:{}};w.puppeteer.args.value.join(","),w.highcharts.version.value,w.highcharts.cdnURL.value,w.highcharts.modules.value,w.highcharts.scripts.value.join(","),w.highcharts.forceFetch.value,w.export.type.value,w.export.constr.value,w.export.defaultHeight.value,w.export.defaultWidth.value,w.export.defaultScale.value,w.customCode.allowCodeExecution.value,w.customCode.allowFileResources.value,w.server.enable.value,w.server.host.value,w.server.port.value,w.server.ssl.enable.value,w.server.ssl.force.value,w.server.ssl.port.value,w.server.ssl.certPath.value,w.server.rateLimiting.enable.value,w.server.rateLimiting.maxRequests.value,w.server.rateLimiting.window.value,w.server.rateLimiting.delay.value,w.server.rateLimiting.trustProxy.value,w.server.rateLimiting.skipKey.value,w.server.rateLimiting.skipToken.value,w.pool.minWorkers.value,w.pool.maxWorkers.value,w.pool.workLimit.value,w.pool.acquireTimeout.value,w.pool.createTimeout.value,w.pool.destroyTimeout.value,w.pool.idleTimeout.value,w.pool.rasterizationTimeout.value,w.pool.createRetryInterval.value,w.pool.reaperInterval.value,w.pool.benchmarking.value,w.pool.listenToProcessExits.value,w.logging.level.value,w.logging.file.value,w.logging.dest.value,w.ui.enable.value,w.ui.route.value,w.other.noLogo.value;const T=["options","globalOptions","themeOptions","resources","payload"],x={},k=(e,t="")=>{Object.keys(e).forEach((o=>{if(!["puppeteer","highcharts"].includes(o)){const r=e[o];void 0===r.value?k(r,`${t}.${o}`):x[r.cliName||o]=`${t}.${o}`.substring(1)}}))};k(w);let S={toConsole:!0,toFile:!1,pathCreated:!1,levelsDesc:[{title:"error",color:"red"},{title:"warning",color:"yellow"},{title:"notice",color:"blue"},{title:"verbose",color:"gray"}],listeners:[]};for(const[e,t]of Object.entries(w.logging))S[e]=t.value;const H=(...t)=>{const[o,...r]=t,{level:i,levelsDesc:n}=S;if(0===o||o>i||i>n.length)return;const s=`${(new Date).toString().split("(")[0].trim()} [${n[o-1].title}] -`;S.listeners.forEach((e=>{e(s,r.join(" "))})),S.toFile&&(S.pathCreated||(!e.existsSync(S.dest)&&e.mkdirSync(S.dest),S.pathCreated=!0),e.appendFile(`${S.dest}${S.file}`,[s].concat(r).join(" ")+"\n",(e=>{e&&(console.log(`[logger] Unable to write to log file: ${e}`),S.toFile=!1)}))),S.toConsole&&console.log.apply(void 0,[s.toString()[S.levelsDesc[o-1].color]].concat(r))},R=p.fileURLToPath(new URL("../.","undefined"==typeof document?require("url").pathToFileURL(__filename).href:v&&v.src||new URL("index.cjs",document.baseURI).href)),E=(e,t=/\s\s+/g,o=" ")=>e.replaceAll(t,o).trim(),L=(e,t)=>{const o=["png","jpeg","pdf","svg"];if(t){const r=t.split(".").pop();o.includes(r)&&e!==r&&(e=r)}return{"image/png":"png","image/jpeg":"jpeg","application/pdf":"pdf","image/svg+xml":"svg"}[e]||o.find((t=>t===e))||"png"},O=(t=!1,o)=>{const r=["js","css","files"];let i=t,n=!1;if(o&&t.endsWith(".json"))try{t?t&&t.endsWith(".json")?i=_(e.readFileSync(t,"utf8")):(i=_(t),!0===i&&(i=_(e.readFileSync("resources.json","utf8")))):i=_(e.readFileSync("resources.json","utf8"))}catch(e){return H(3,"[cli] No resources found.")}else i=_(t),o||delete i.files;for(const e in i)r.includes(e)?n||(n=!0):delete i[e];return n?(i.files&&(i.files=i.files.map((e=>e.trim())),(!i.files||i.files.length<=0)&&delete i.files),i):H(3,"[cli] No resources found.")};function _(e,t){try{const o=JSON.parse("string"!=typeof e?JSON.stringify(e):e);return"string"!=typeof o&&t?JSON.stringify(o):o}catch(e){return!1}}const C=e=>{if(null===e||"object"!=typeof e)return e;const t=Array.isArray(e)?[]:{};for(const o in e)Object.prototype.hasOwnProperty.call(e,o)&&(t[o]=C(e[o]));return t},I=(e,t)=>JSON.stringify(e,((e,o)=>("string"==typeof o&&((o=o.trim()).startsWith("function(")||o.startsWith("function ("))&&o.endsWith("}")&&(o=t?`EXP_FUN${(o+"").replaceAll(/\n|\t|\r/g," ")}EXP_FUN`:void 0),"function"==typeof o?`EXP_FUN${(o+"").replaceAll(/\n|\t|\r/g," ")}EXP_FUN`:o))).replaceAll(/"EXP_FUN|EXP_FUN"/g,"");function A(){console.log("Usage of CLI arguments:".bold,"\n------",`\nFor more detailed information visit readme at: ${"https://github.com/highcharts/node-export-server#readme".bold.yellow}.`);const e=t=>{for(const[o,r]of Object.entries(t))if(Object.prototype.hasOwnProperty.call(r,"value")){let e=` --${r.cliName||o} ${("<"+r.type+">").green} `;if(e.length<48)for(let t=e.length;t<48;t++)e+=".";console.log(e,r.description,`[Default: ${r.value.toString().bold}]`.blue)}else e(r)};Object.keys(w).forEach((t=>{["puppeteer","highcharts"].includes(t)||(console.log(`\n${t.toUpperCase()}`.red),e(w[t]))})),console.log("\n")}const $=e=>!["false","undefined","null","NaN","0",""].includes(e)&&!!e,j=(t,o)=>{if(t&&"string"==typeof t)return(t=t.trim()).endsWith(".js")?!!o&&j(e.readFileSync(t,"utf8")):t.startsWith("function()")||t.startsWith("function ()")||t.startsWith("()=>")||t.startsWith("() =>")?`(${t})()`:t.replace(/;$/,"")};var P=(e,t)=>{const o="Too many requests, you have been rate limited. Please try again later.",r={max:t.maxRequests||30,window:t.window||1,delay:t.delay||0,trustProxy:t.trustProxy||!1,skipKey:t.skipKey||!1,skipToken:t.skipToken||!1};r.trustProxy&&e.enable("trust proxy");const i=c({windowMs:60*r.window*1e3,max:r.max,delayMs:r.delay,handler:(e,t)=>{t.format({json:()=>{t.status(429).send({message:o})},default:()=>{t.status(429).send(o)}})},skip:e=>!1!==r.skipKey&&!1!==r.skipToken&&e.query.key===r.skipKey&&e.query.access_token===r.skipToken&&(H(4,"[rate-limiting] Skipping rate limiter."),!0)});e.use(i),H(3,E(`[rate-limiting] Enabled rate limiting: ${r.max} requests\n per ${r.window} minute per IP, trusting proxy:\n ${r.trustProxy}.`))};async function N(e,t={}){return new Promise(((o,r)=>{const i=(e=>e.startsWith("https")?a:s)(e);i.get(e,t,(e=>{let t="";e.on("data",(e=>{t+=e})),e.on("end",(()=>{t||r("Nothing was fetched from the URL."),e.text=t,o(e)}))})).on("error",(e=>{r(e)}))}))}l.config();const F=t.join(R,".cache"),U={cdnURL:"https://code.highcharts.com/",activeManifest:{},sources:"",hcVersion:""};let G=!1;const q=()=>U.hcVersion=U.sources.substr(0,U.sources.indexOf("*/")).replace("/*","").replace("*/","").replace(/\n/g,"").trim(),W=async(e,t)=>{try{e.endsWith(".js")&&(e=e.substring(0,e.length-3)),H(4,`[cache] Fetching script - ${e}.js`);const o=t?{agent:t,timeout:+process.env.PROXY_SERVER_TIMEOUT||5e3}:{},r=await N(`${e}.js`,o);if(200===r.statusCode)return r.text;throw`${r.statusCode}`}catch(t){throw H(1,`[cache] Error fetching script ${e}.js: ${t}.`),t}},M=async(t,o)=>{const{coreScripts:r,modules:i,indicators:n,scripts:s}=t,a="latest"!==t.version&&t.version?`${t.version}/`:"";H(3,"[cache] Updating cache to Highcharts ",a);const l=[...r.map((e=>`${a}${e}`)),...i.map((e=>"map"===e?`maps/${a}modules/${e}`:`${a}modules/${e}`)),...n.map((e=>`stock/${a}indicators/${e}`))];let c;const p=process.env.PROXY_SERVER_HOST,d=process.env.PROXY_SERVER_PORT;p&&d&&(c=new u({host:p,port:+d}));const h={};try{return U.sources=(await Promise.all([...l.map((async e=>{const o=await W(`${t.cdnURL||U.cdnURL}${e}`,c);return"string"==typeof o&&(h[e.replace(/(.*)\/|(.*)modules\/|stock\/(.*)indicators\/|maps\/(.*)modules\//gi,"")]=1),o})),...s.map((e=>W(e,c)))])).join(";\n"),q(),e.writeFileSync(o,U.sources),h}catch(e){H(1,"[cache] Unable to update local Highcharts cache.")}},D=async o=>{let r;const i=t.join(F,"manifest.json"),n=t.join(F,"sources.js");if(G=o,!e.existsSync(F)&&e.mkdirSync(F),!e.existsSync(i)||o.forceFetch)H(3,"[cache] Fetching and caching Highcharts dependencies."),r=await M(o,n);else{let t=!1;const s=JSON.parse(e.readFileSync(i));if(s.modules&&Array.isArray(s.modules)){const e={};s.modules.forEach((t=>e[t]=1)),s.modules=e}const{modules:a,coreScripts:l,indicators:c}=o,p=a.length+l.length+c.length;s.version!==o.version?(H(3,"[cache] Highcharts version mismatch in cache, need to re-fetch."),t=!0):Object.keys(s.modules||{}).length!==p?(H(3,"[cache] Cache and requested modules does not match, need to re-fetch."),t=!0):t=(o.modules||[]).some((e=>{if(!s.modules[e])return H(3,`[cache] The ${e} missing in cache, need to re-fetch.`),!0})),t?r=await M(o,n):(H(3,"[cache] Dependency cache is up to date, proceeding."),U.sources=e.readFileSync(n,"utf8"),r=s.modules,q())}await(async(o,r)=>{const i={version:o.version,modules:r||{}};U.activeManifest=i,H(4,"[cache] writing new manifest");try{e.writeFileSync(t.join(F,"manifest.json"),JSON.stringify(i),"utf8")}catch(e){H(1,`[cache] Error writing cache manifest: ${e}.`)}})(o,r)};var V=async e=>!!G&&await D(Object.assign(G,{version:e})),z=()=>U,J=()=>U.hcVersion;const K=f.randomBytes(64).toString("base64url"),X=m.join("tmp",`puppeteer-${K}`),B=[`--user-data-dir=${m.join(X,"profile")}`,"--autoplay-policy=user-gesture-required","--disable-background-networking","--disable-background-timer-throttling","--disable-backgrounding-occluded-windows","--disable-breakpad","--disable-client-side-phishing-detection","--disable-component-update","--disable-default-apps","--disable-dev-shm-usage","--disable-domain-reliability","--disable-extensions","--disable-features=AudioServiceOutOfProcess","--disable-hang-monitor","--disable-ipc-flooding-protection","--disable-notifications","--disable-offer-store-unmasked-wallet-cards","--disable-popup-blocking","--disable-print-preview","--disable-prompt-on-repost","--disable-renderer-backgrounding","--disable-session-crashed-bubble","--disable-setuid-sandbox","--disable-speech-api","--disable-sync","--hide-crash-restore-bubble","--hide-scrollbars","--ignore-gpu-blacklist","--metrics-recording-only","--mute-audio","--no-default-browser-check","--no-first-run","--no-pings","--no-sandbox","--no-zygote","--password-store=basic","--use-mock-keychain"],Y=b.fileURLToPath(new URL(".","undefined"==typeof document?require("url").pathToFileURL(__filename).href:v&&v.src||new URL("index.cjs",document.baseURI).href)),Q=e.readFileSync(Y+"/../templates/template.html","utf8");let Z;const ee=async e=>{await e.setContent(Q),await e.addScriptTag({path:Y+"/../.cache/sources.js"}),await e.evaluate((()=>window.setupHighcharts())),e.on("pageerror",(async t=>{H(1,"[page error]",t),await e.$eval("#container",((e,t)=>{window._displayErrors&&(e.innerHTML=t)}),`

Chart input data error

${t.toString()}`)}))},te=async()=>{if(!Z)return!1;const e=await Z.newPage();return await ee(e),e},oe=async()=>{Z.connected&&await Z.close()};const re=b.fileURLToPath(new URL(".","undefined"==typeof document?require("url").pathToFileURL(__filename).href:v&&v.src||new URL("index.cjs",document.baseURI).href)),ie=async(e,t,o)=>await e.evaluate(((e,t)=>window.triggerExport(e,t)),t,o);var ne=async(o,r,i)=>{const n=[],s=async e=>{for(const e of n)await e.dispose();await e.evaluate((()=>{const[,...e]=document.getElementsByTagName("script"),[,...t]=document.getElementsByTagName("style"),[...o]=document.getElementsByTagName("link");for(const r of[...e,...t,...o])r.remove()}))};try{const a=()=>{};H(4,"[export] Determining export path.");const l=i.export;await o.evaluate((()=>requestAnimationFrame((()=>{}))));const c=l?.options?.chart?.displayErrors&&z().activeManifest.modules.debugger;await o.evaluate((e=>window._displayErrors=e),c);const p=()=>{};let u;if(r.indexOf&&(r.indexOf("=0||r.indexOf("=0)){if(H(4,"[export] Treating as SVG."),"svg"===l.type)return r;u=!0;const e=()=>{};await o.setContent((e=>`\n\n\n \n \n Highcarts Export\n \n \n \n
\n ${e}\n
\n \n\n\n`)(r)),e()}else if(H(4,"[export] Treating as config."),l.strInj){const e=()=>{};await ie(o,{chart:{height:l.height,width:l.width}},i),e()}else{r.chart.height=l.height,r.chart.width=l.width;const e=()=>{};await ie(o,r,i),e()}p();const d=()=>{},h=i.customCode.resources;if(h){if(h.js&&n.push(await o.addScriptTag({content:h.js})),h.files)for(const t of h.files)try{const r=!t.startsWith("http");n.push(await o.addScriptTag(r?{content:e.readFileSync(t,"utf8")}:{url:t}))}catch(e){H(4,"[export] JS file not found.")}const r=()=>{};if(h.css){let e=h.css.match(/@import\s*([^;]*);/g);if(e)for(let r of e)r&&(r=r.replace("url(","").replace("@import","").replace(/"/g,"").replace(/'/g,"").replace(/;/,"").replace(/\)/g,"").trim(),r.startsWith("http")?n.push(await o.addStyleTag({url:r})):i.customCode.allowFileResources&&n.push(await o.addStyleTag({path:t.join(re,r)})));n.push(await o.addStyleTag({content:h.css.replace(/@import\s*([^;]*);/g,"")||" "}))}r()}d();const g=u?await o.$eval("#chart-container svg:first-of-type",(async(e,t)=>({chartHeight:e.height.baseVal.value*t,chartWidth:e.width.baseVal.value*t})),parseFloat(l.scale)):await o.evaluate((async()=>{const{chartHeight:e,chartWidth:t}=window.Highcharts.charts[0];return{chartHeight:e,chartWidth:t}})),m=()=>{},f=Math.ceil(g?.chartHeight||l.height),v=Math.ceil(g?.chartWidth||l.width);await o.setViewport({height:f,width:v,deviceScaleFactor:u?1:parseFloat(l.scale)});const y=u?e=>{document.body.style.zoom=e,document.body.style.margin="0px"}:()=>{document.body.style.zoom=1};await o.evaluate(y,parseFloat(l.scale));const{height:b,width:w,x:T,y:x}=await(e=>e.$eval("#chart-container",(e=>{const{x:t,y:o,width:r,height:i}=e.getBoundingClientRect();return{x:t,y:o,width:r,height:Math.trunc(i>1?i:500)}})))(o);let k;u||await o.setViewport({width:Math.round(w),height:Math.round(b),deviceScaleFactor:parseFloat(l.scale)}),m();const S=()=>{};if("svg"===l.type)k=await(async e=>await e.$eval("#container svg:first-of-type",(e=>e.outerHTML)))(o);else if("png"===l.type||"jpeg"===l.type)k=await(async(e,t,o,r,i)=>await Promise.race([e.screenshot({type:t,encoding:o,clip:r,omitBackground:"png"==t}),new Promise(((e,t)=>setTimeout((()=>t(new Error("Rasterization timeout"))),i||1500)))]))(o,l.type,"base64",{width:v,height:f,x:T,y:x},i.pool.rasterizationTimeout);else{if("pdf"!==l.type)throw`Unsupported output format ${l.type}`;k=await(async(e,t,o,r)=>await e.pdf({height:t+1,width:o,encoding:r}))(o,f,v,"base64")}return await o.evaluate((()=>{const e=Highcharts.charts;if(e.length)for(const t of e)t&&t.destroy(),Highcharts.charts.shift()})),S(),a(),await s(o),k}catch(e){return await s(o),H(1,`[export] Error encountered during export: ${e}`),e}};let se,ae=0,le=0,ce=0,pe=0,ue=0,de={},he=!1;const ge={create:async()=>{const e=d.v4();let t=!1;const o=(new Date).getTime();try{if(t=await te(),!t||t.isClosed())throw"[pool] Invalid page";H(3,`[pool] Successfully created a worker ${e} - took ${(new Date).getTime()-o} ms.`)}catch(e){throw H(1,`[pool] Error creating a new page in pool entry creation! ${e}`),"Error creating page"}return{id:e,page:t,workCount:Math.round(Math.random()*(de.workLimit/2))}},validate:async e=>de.workLimit&&++e.workCount>de.workLimit?(H(3,"[pool] Worker failed validation:",`exceeded work limit (limit is ${de.workLimit})`),!1):(await(async e=>{try{await e.goto("about:blank"),await ee(e)}catch(e){H(3,"[browser] Could not clear page")}})(e.page),!0),destroy:e=>{H(3,`[pool] Destroying pool entry ${e.id}.`),e.page&&e.page.close()},log:(e,t)=>console.log(`${t}: ${e}`)},me=async e=>{se=e.puppeteerArgs;try{await(async e=>{const t=[...B,...e||[]];if(!Z){let e=0;const o=async()=>{try{H(3,"[browser] attempting to get a browser instance (try",e+")"),Z=await g.launch({headless:"new",args:t,userDataDir:"./tmp/"})}catch(t){H(0,"[browser]",t),++e<25?(H(3,"[browser] failed:",t),await new Promise((e=>setTimeout(e,4e3))),await o()):H(0,"Max retries reached")}};try{await o()}catch(e){return H(0,"[browser] Unable to open browser"),!1}if(!Z)return H(0,"[browser] Unable to open browser"),!1}return Z})(se)}catch(e){H(0,"[pool|browser]",e)}if(de=e&&e.pool?{...e.pool}:{},H(3,"[pool] Initializing pool:",`min ${de.minWorkers}, max ${de.maxWorkers}.`),he)return H(4,"[pool] Already initialized, please kill it before creating a new one.");de.listenToProcessExits&&(H(4,"[pool] Attaching exit listeners to the process."),process.on("exit",(async()=>{await fe()})),process.on("SIGINT",((e,t)=>{H(4,`The ${e} event with code: ${t}.`),process.exit(1)})),process.on("SIGTERM",((e,t)=>{H(4,`The ${e} event with code: ${t}.`),process.exit(1)})),process.on("uncaughtException",(async(e,t)=>{H(4,`The ${t} error, message: ${e.message}.`)})));try{he=new h.Pool({...ge,min:de.minWorkers,max:de.maxWorkers,acquireTimeoutMillis:de.acquireTimeout,createTimeoutMillis:de.createTimeout,destroyTimeoutMillis:de.destroyTimeout,idleTimeoutMillis:de.idleTimeout,createRetryIntervalMillis:de.createRetryInterval,reapIntervalMillis:de.reaperInterval,propagateCreateError:!1}),he.on("createFail",((e,t)=>{H(1,`[pool] Error when creating worker of an event id ${e}:`,t)})),he.on("acquireFail",((e,t)=>{H(1,`[pool] Error when acquiring worker of an event id ${e}:`,t)})),he.on("destroyFail",((e,t,o)=>{H(1,`[pool] Error when destroying worker of an id ${t.id}, event id ${e}:`,o)})),he.on("release",(e=>{H(4,`[pool] Releasing a worker of an id ${e.id}`)})),he.on("destroySuccess",((e,t)=>{H(4,`[pool] Destroyed a worker of an id ${t.id}`)}));const e=[];for(let t=0;t{he.release(e)})),H(3,`[pool] The pool is ready with ${de.minWorkers} initial resources waiting.`)}catch(e){throw H(1,`[pool] Couldn't create the worker pool ${e}`),e}};async function fe(){return H(3,"[pool] Killing all workers."),he.destroyed?(await oe(),!0):(await he.destroy(),await oe(),!0)}const ve=async(e,t)=>{let o;const r=e=>{throw++pe,o&&he.release(o),"In pool.postWork: "+e};if(H(4,"[pool] Work received, starting to process."),de.benchmarking&&ye(),++le,!he)return H(1,"[pool] Work received, but pool has not been started."),r("Pool is not inited but work was posted to it!");try{H(4,"[pool] Acquiring worker"),o=await he.acquire().promise}catch(e){return r(`[pool] Error when acquiring available entry: ${e}`)}if(H(4,"[pool] Acquired worker handle"),!o.page)return r("Resolved worker page is invalid: pool setup is wonky");try{let i=(new Date).getTime();H(4,`[pool] Starting work on pool entry ${o.id}.`);const n=await ne(o.page,e,t);if(n instanceof Error)return"Rasterization timeout"===n.message&&(o.page.close(),o.page=await te()),r(n);he.release(o);const s=(new Date).getTime()-i;return ce+=s,ue=ce/++ae,H(4,`[pool] Work completed in ${s} ms.`),{data:n,options:t}}catch(e){r(`Error trying to perform puppeteer export: ${e}.`)}};function ye(){const{min:e,max:t,size:o,available:r,borrowed:i,pending:n,spareResourceCapacity:s}=he;H(4,`[pool] The minimum number of resources allowed by pool: ${e}.`),H(4,`[pool] The maximum number of resources allowed by pool: ${t}.`),H(4,`[pool] The number of all resources in pool (free or in use): ${o}.`),H(4,`[pool] The number of resources that are currently available: ${r}.`),H(4,`[pool] The number of resources that are currently acquired: ${i}.`),H(4,`[pool] The number of callers waiting to acquire a resource: ${n}.`),H(4,`[pool] The number of how many more resources can the pool manage/create: ${s}.`)}var be=()=>({min:he.min,max:he.max,size:he.size,available:he.available,borrowed:he.borrowed,pending:he.pending,spareResourceCapacity:he.spareResourceCapacity}),we=()=>le,Te=()=>pe,xe=()=>ue,ke=()=>ae;const Se=process.env.npm_package_version,He=new Date;let Re={};const Ee=()=>Re,Le=(e,t,o=[])=>{const r=C(e);for(const[e,n]of Object.entries(t))r[e]="object"!=typeof(i=n)||Array.isArray(i)||null===i||o.includes(e)||void 0===r[e]?void 0!==n?n:r[e]:Le(r[e],n,o);var i;return r};function Oe(e,t={},o=""){Object.keys(e).forEach((r=>{if(!["puppeteer","highcharts"].includes(r)){const i=e[r],n=t&&t[r];let s;void 0===i.value?Oe(i,n,`${o}.${r}`):(void 0!==n&&(i.value=n),i.envLink&&("boolean"===i.type?i.value=$([process.env[i.envLink],i.value].find((e=>e||"false"===e))):"number"===i.type?(s=+process.env[i.envLink],i.value=s>=0?s:i.value):i.type.indexOf("]")>=0&&process.env[i.envLink]?i.value=process.env[i.envLink].split(","):i.value=process.env[i.envLink]||i.value))}}))}function _e(e){let t={};for(const[o,r]of Object.entries(e))t[o]=Object.prototype.hasOwnProperty.call(r,"value")?r.value:_e(r);return t}let Ce=!1;const Ie=async(t,o)=>{H(4,"[chart] Starting exporting process.");const r=((e,t={})=>{let o={};return e.svg?(o=C(t),o.export.type=e.type||e.export.type,o.export.scale=e.scale||e.export.scale,o.export.outfile=e.outfile||e.export.outfile,o.payload={svg:e.svg}):o=Le(t,e,T),o.export.outfile=o.export?.outfile||`chart.${o.export?.type||"png"}`,o})(t,Ee()),i=r.export;return r.payload?.svg&&""!==r.payload.svg?Pe(r.payload.svg.trim(),r,o):i.infile&&i.infile.length?(H(4,"[chart] Attempting to export from an input file."),e.readFile(i.infile,"utf8",((e,t)=>e?H(1,`[chart] Error loading input file: ${e}.`):(r.export.instr=t,Pe(r.export.instr.trim(),r,o))))):i.instr&&""!==i.instr||i.options&&""!==i.options?(H(4,"[chart] Attempting to export from a raw input."),$(r.customCode?.allowCodeExecution)?je(r,o):"string"==typeof i.instr?Pe(i.instr.trim(),r,o):$e(r,i.instr||i.options,o)):(H(1,E(`[chart] No input specified.\n ${JSON.stringify(i,void 0," ")}.`)),o&&o(!1,{error:!0,message:"No input specified."}))},Ae=e=>{const{chart:t,exporting:o}=e.export?.options||_(e.export?.instr),r=_(e.export?.globalOptions);let i=e.export?.scale||o?.scale||r?.exporting?.scale||e.export?.defaultScale||1;return i=Math.max(.1,Math.min(i,5)),i=((e,t=1)=>{const o=Math.pow(10,t||0);return Math.round(+e*o)/o})(i,2),{height:e.export?.height||o?.sourceHeight||t?.height||r?.exporting?.sourceHeight||r?.chart?.height||e.export?.defaultHeight||400,width:e.export?.width||o?.sourceWidth||t?.width||r?.exporting?.sourceWidth||r?.chart?.width||e.export?.defaultWidth||600,scale:i}},$e=(t,o,r,i)=>{let{export:n,customCode:s}=t;const a="boolean"==typeof s.allowCodeExecution?s.allowCodeExecution:Ce;if(s){if(a)if("string"==typeof t.customCode.resources)t.customCode.resources=O(t.customCode.resources,$(t.customCode.allowFileResources));else if(!t.customCode.resources)try{const o=e.readFileSync("resources.json","utf8");t.customCode.resources=O(o,$(t.customCode.allowFileResources))}catch(e){H(3,"[chart] The default resources.json file not found.")}}else s=t.customCode={};if(!a&&s){if(s.callback||s.resources||s.customCode)return r&&r(!1,{error:!0,message:E("The callback, resources and customCode have been disabled for this\n server.")});s.callback=!1,s.resources=!1,s.customCode=!1}if(o&&(o.chart=o.chart||{},o.exporting=o.exporting||{},o.exporting.enabled=!1),n.constr=n.constr||"chart",n.type=L(n.type,n.outfile),"svg"===n.type&&(n.width=!1),["globalOptions","themeOptions"].forEach((t=>{try{n&&n[t]&&("string"==typeof n[t]&&n[t].endsWith(".json")?n[t]=_(e.readFileSync(n[t],"utf8"),!0):n[t]=_(n[t],!0))}catch(e){n[t]={},H(1,`[chart] The ${t} not found.`)}})),s.allowCodeExecution&&(s.customCode=j(s.customCode,s.allowFileResources)),s&&s.callback&&s.callback?.indexOf("{")<0)if(s.allowFileResources)try{s.callback=e.readFileSync(s.callback,"utf8")}catch(e){H(2,`[chart] Error loading callback: ${e}.`),s.callback=!1}else s.callback=!1;t.export={...t.export,...Ae(t)},ve(n.strInj||o||i,t).then((e=>r(e))).catch((e=>(H(0,"[chart] When posting work:",e),r(!1,e))))},je=(e,t)=>{try{let o,r=e.export.instr||e.export.options;return"string"!=typeof r&&(o=r=I(r,e.customCode?.allowCodeExecution)),o=r.replaceAll(/\t|\n|\r/g,"").trim(),";"===o[o.length-1]&&(o=o.substring(0,o.length-1)),e.export.strInj=o,$e(e,!1,t)}catch(o){const r=E(`Malformed input detected for ${e.export?.requestId||"?"}:\n Please make sure that your JSON/JavaScript options\n are sent using the "options" attribute, and that if you're using\n SVG, it is unescaped.`);return H(1,r),t&&t(!1,JSON.stringify({error:!0,message:r}))}},Pe=(e,t,o)=>{const{allowCodeExecution:r}=t.customCode;if(e.indexOf("=0||e.indexOf("=0)return H(4,"[chart] Parsing input as SVG."),$e(t,!1,o,e);try{const r=JSON.parse(e.replaceAll(/\t|\n|\r/g," "));return $e(t,r,o)}catch(e){return $(r)?je(t,o):o&&o(!1,{error:!0,message:E("Only JSON configurations and SVG is allowed for this server. If\n this is your server, JavaScript exporting can be enabled by starting\n the server with the --allowCodeExecution flag.")})}},Ne={png:"image/png",jpeg:"image/jpeg",gif:"image/gif",pdf:"application/pdf",svg:"image/svg+xml"};let Fe=0;const Ue=[],Ge=[],qe=(e,t,o,r)=>{let i=!0;const{id:n,uniqueId:s,type:a,body:l}=r;return e.some((e=>{if(e){let r=e(t,o,n,s,a,l);return void 0!==r&&!0!==r&&(i=r),!0}})),i},We=(e,t)=>{(()=>{const e=process.hrtime.bigint()})();const o=Ee(),r=e.body,i=++Fe,n=d.v4().replace(/-/g,"");let s=L(r.type);if(!r)return t.status(400).send(E("Body is required. Sending a body? Make sure your Content-type header\n is correct. Accepted is application/json and multipart/form-data."));let a=_(r.infile||r.options||r.data);if(!a&&!r.svg)return H(2,E(`Request ${n} from ${e.headers["x-forwarded-for"]||e.connection.remoteAddress} was incorrect. Check your payload.`)),t.status(400).send(E("No correct chart data found. Please make sure you are using\n application/json or multipart/form-data headers, and that the chart\n data is in the 'infile', 'options' or 'data' attribute if sending\n JSON or in the 'svg' if sending SVG."));let l=!1;if(l=qe(Ue,e,t,{id:i,uniqueId:n,type:s,body:r}),!0!==l)return t.send(l);let c=!1;e.socket.on("close",(()=>{c=!0})),H(4,`[export] Got an incoming HTTP request ${n}.`),r.constr="string"==typeof r.constr&&r.constr||"chart";const p={export:{instr:a,type:s,constr:r.constr[0].toLowerCase()+r.constr.substr(1),height:r.height,width:r.width,scale:r.scale||o.export.scale,globalOptions:_(r.globalOptions,!0),themeOptions:_(r.themeOptions,!0)},customCode:{allowCodeExecution:Ce,allowFileResources:!1,resources:_(r.resources,!0),callback:r.callback,customCode:r.customCode}};a&&(p.export.instr=I(a,p.customCode.allowCodeExecution));const u=Le(o,p);if(u.export.options=a,u.payload={svg:r.svg||!1,b64:r.b64||!1,dataOptions:_(r.dataOptions,!0),noDownload:r.noDownload||!1,requestId:n},r.svg&&(h=u.payload.svg,["localhost","(10).(.*).(.*).(.*)","(127).(.*).(.*).(.*)","(172).(1[6-9]|2[0-9]|3[0-1]).(.*).(.*)","(192).(168).(.*).(.*)"].some((e=>h.match(`xlink:href="(?:(http://|https://))?${e}`)))))return t.status(400).send("SVG potentially contain at least one forbidden URL in xlink:href element.");var h;Ie(u,((o,a)=>(e.socket.removeAllListeners("close"),c?H(3,E("[export] The client closed the connection before the chart was done\n processing.")):a?(H(1,E(`[export] Work: ${n} could not be completed, sending:\n ${a}`)),t.status(400).send(a.message)):o&&o.data?(s=o.options.export.type,qe(Ge,e,t,{id:i,body:o.data}),o.data?r.b64?"pdf"===s?t.send(Buffer.from(o.data,"utf8").toString("base64")):t.send(o.data):(t.header("Content-Type",Ne[s]||"image/png"),r.noDownload||t.attachment(`${e.params.filename||e.body.filename||"chart"}.${s||"png"}`),"svg"===s?t.send(o.data):t.send(Buffer.from(o.data,"base64"))):void 0):(H(1,E(`[export] Unexpected return from chart generation, please check your\n data Request: ${n} is ${o.data}.`)),t.status(400).send("Unexpected return from chart generation, please check your data.")))))};const Me=i();Me.disable("x-powered-by"),Me.use(r());const De=n.memoryStorage(),Ve=n({storage:De,limits:{fieldsSize:"50MB"}});Me.use(Ve.any()),Me.use(o.json({limit:"50mb"})),Me.use(o.urlencoded({extended:!0,limit:"50mb"})),Me.use(o.urlencoded({extended:!1,limit:"50mb"}));const ze=e=>H(1,`[server] Socket error: ${e}`),Je=e=>{e.on("clientError",ze),e.on("error",ze),e.on("connection",(e=>e.on("error",(e=>ze(e)))))},Ke=async o=>{if(!o.enable)return!1;if(!o.ssl.enable&&!o.ssl.force){const e=s.createServer(Me);Je(e),e.listen(o.port,o.host),H(3,`[server] Started HTTP server on ${o.host}:${o.port}.`)}if(o.ssl.enable){let r,i;try{r=await e.promises.readFile(t.posix.join(o.ssl.certPath,"server.key"),"utf8"),i=await e.promises.readFile(t.posix.join(o.ssl.certPath,"server.crt"),"utf8")}catch(e){H(1,`[server] Unable to load key/certificate from ${o.ssl.certPath}.`)}if(r&&i){const e=a.createServer(Me);Je(e),e.listen(o.ssl.port,o.host),H(3,`[server] Started HTTPS server on ${o.host}:${o.ssl.port}.`)}}o.rateLimiting&&o.rateLimiting.enable&&![0,NaN].includes(o.rateLimiting.maxRequests)&&P(Me,o.rateLimiting),Me.use(i.static(t.posix.join(R,"public"))),(e=>{!!e&&e.get("/health",((e,t)=>{t.send({status:"OK",bootTime:He,uptime:Math.floor(((new Date).getTime()-He.getTime())/1e3/60)+" minutes",version:Se,highchartsVersion:J(),averageProcessingTime:xe(),performedExports:ke(),failedExports:Te(),exportAttempts:we(),sucessRatio:ke()/we()*100,pool:be()})}))})(Me),(e=>{e.post("/",We),e.post("/:filename",We)})(Me),(e=>{!!e&&e.get("/",((e,o)=>{o.sendFile(t.join(R,"public","index.html"))}))})(Me),(e=>{!!e&&e.post("/change_hc_version/:newVersion",(async(e,t)=>{const o=process.env.HIGHCHARTS_ADMIN_TOKEN;if(!o||!o.length)return t.send({error:!0,message:"Server not configured to do run-time version changes: HIGHCHARTS_ADMIN_TOKEN not set"});const r=e.get("hc-auth");if(!r||r!==o)return t.send({error:!0,message:"Invalid or missing token: set token in the hc-auth header"});const i=e.params.newVersion;if(i){try{await V(i)}catch(e){t.send({error:!0,message:e})}t.send({version:J()})}else t.send({error:!0,message:"No new version supplied"})}))})(Me)};var Xe={startServer:Ke,getExpress:()=>i,getApp:()=>Me,use:(e,...t)=>{Me.use(e,...t)},get:(e,...t)=>{Me.get(e,...t)},post:(e,...t)=>{Me.post(e,...t)},enableRateLimiting:e=>P(Me,e)},Be={log:H,mapToNewConfig:e=>{const t={};for(const[o,r]of Object.entries(e)){const e=x[o]?x[o].split("."):[];e.reduce(((t,o,i)=>t[o]=e.length-1===i?r:t[o]||{}),t)}return t},setOptions:(t,o)=>(o?.length&&(Re=function(t){const o=t.findIndex((e=>"loadConfig"===e.replace(/-/g,"")));if(o>-1&&t[o+1]){const r=t[o+1];try{if(r&&r.endsWith(".json"))return JSON.parse(e.readFileSync(r))}catch(e){H(1,`[config] Unable to load config from the ${r}: ${e}`)}}return{}}(o)),Oe(w,Re),Re=_e(w),t&&(Re=Le(Re,t,T)),o?.length&&(Re=function(e,t,o){for(let o=0;o(i.length-1===a&&void 0!==n[s]&&(t[++o]?n[s]=t[o]||n[s]:(console.log(`Missing argument value for ${r}!`.red,"\n"),e=A())),n[s])),e)}return e}(Re,o)),Re),singleExport:t=>{t.export.instr=t.export.instr||t.export.options,Ie(t,((t,o)=>{o&&(H(1,`[cli] ${o.message}`),process.exit(1));const{outfile:r,type:i}=t.options.export;e.writeFileSync(r||`chart.${i}`,"svg"!==i?Buffer.from(t.data,"base64"):t.data),fe()}))},startExport:Ie,batchExport:t=>{const o=[];for(let r of t.export.batch.split(";"))r=r.split("="),2===r.length&&o.push(new Promise(((o,i)=>{Ie({...t,export:{...t.export,infile:r[0],outfile:r[1]}},((t,r)=>{if(r)return i(r);e.writeFileSync(t.options.export.outfile,Buffer.from(t.data,"base64")),o()}))})));Promise.all(o).then((()=>{fe()})).catch((e=>{H(1,`[chart] Error encountered during batch export: ${e}`),fe()}))},server:Xe,startServer:Ke,killPool:fe,initPool:async(e={})=>{var t,o;return t=e.customCode&&e.customCode.allowCodeExecution,Ce=$(t),(o=e.logging&&parseInt(e.logging.level))>=0&&o<=S.levelsDesc.length&&(S.level=o),e.logging&&e.logging.dest&&((e,t)=>{if(S={...S,dest:e||S.dest,file:t||S.file,toFile:!0},0===S.dest.length)return H(1,"[logger] File logging init: no path supplied.");S.dest.endsWith("/")||(S.dest+="/")})(e.logging.dest,e.logging.file||"highcharts-export-server.log"),await D(e.highcharts||{version:"latest"}),await me({pool:e.pool||{minWorkers:1,maxWorkers:1},puppeteerArgs:e.puppeteer?.args||[]}),e}};module.exports=Be; -//# sourceMappingURL=data:application/json;charset=utf-8;base64,eyJ2ZXJzaW9uIjozLCJmaWxlIjoiaW5kZXguY2pzIiwic291cmNlcyI6WyIuLi9saWIvc2NoZW1hcy9jb25maWcuanMiLCIuLi9saWIvbG9nZ2VyLmpzIiwiLi4vbGliL3V0aWxzLmpzIiwiLi4vbGliL3NlcnZlci9yYXRlX2xpbWl0LmpzIiwiLi4vbGliL2ZldGNoLmpzIiwiLi4vbGliL2NhY2hlLmpzIiwiLi4vbGliL2Jyb3dzZXIuanMiLCIuLi9saWIvZXhwb3J0LmpzIiwiLi4vbGliL2JlbmNobWFyay5qcyIsIi4uL3RlbXBsYXRlcy9zdmdfZXhwb3J0L3N2Z19leHBvcnQuanMiLCIuLi9saWIvcG9vbC5qcyIsIi4uL2xpYi9zZXJ2ZXIvcm91dGVzL2hlYWx0aC5qcyIsIi4uL2xpYi9jb25maWcuanMiLCIuLi9saWIvY2hhcnQuanMiLCIuLi9saWIvc2VydmVyL3JvdXRlcy9leHBvcnQuanMiLCIuLi9saWIvc2VydmVyL3NlcnZlci5qcyIsIi4uL2xpYi9zZXJ2ZXIvcm91dGVzL3VpLmpzIiwiLi4vbGliL3NlcnZlci9yb3V0ZXMvY2hhbmdlX2hjX3ZlcnNpb24uanMiLCIuLi9saWIvaW5kZXguanMiXSwic291cmNlc0NvbnRlbnQiOlsiLyoqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKipcclxuXHJcbkhpZ2hjaGFydHMgRXhwb3J0IFNlcnZlclxyXG5cclxuQ29weXJpZ2h0IChjKSAyMDE2LTIwMjMsIEhpZ2hzb2Z0XHJcblxyXG5MaWNlbmNlZCB1bmRlciB0aGUgTUlUIGxpY2VuY2UuXHJcblxyXG5BZGRpdGlvbmFsbHkgYSB2YWxpZCBIaWdoY2hhcnRzIGxpY2Vuc2UgaXMgcmVxdWlyZWQgZm9yIHVzZS5cclxuXHJcblNlZSBMSUNFTlNFIGZpbGUgaW4gcm9vdCBmb3IgZGV0YWlscy5cclxuXHJcbioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKiovXHJcblxyXG4vLyBMb2FkIC5lbnYgaW50byBlbnZpcm9ubWVudCB2YXJpYWJsZXNcclxuaW1wb3J0IGRvdGVudiBmcm9tICdkb3RlbnYnO1xyXG5cclxuZG90ZW52LmNvbmZpZygpO1xyXG5cclxuLy8gVGhpcyBpcyB0aGUgY29uZmlndXJhdGlvbiBvYmplY3Qgd2l0aCBhbGwgb3B0aW9ucyBhbmQgdGhlaXIgZGVmYXVsdCB2YWx1ZXMsXHJcbi8vIGFsc28gZnJvbSB0aGUgLmVudiBmaWxlIGlmIG9uZSBleGlzdHNcclxuZXhwb3J0IGNvbnN0IGRlZmF1bHRDb25maWcgPSB7XHJcbiAgcHVwcGV0ZWVyOiB7XHJcbiAgICBhcmdzOiB7XHJcbiAgICAgIHZhbHVlOiBbXSxcclxuICAgICAgdHlwZTogJ3N0cmluZ1tdJyxcclxuICAgICAgZGVzY3JpcHRpb246ICdBcnJheSBvZiBhcmd1bWVudHMgdG8gc2VuZCB0byBwdXBwZXRlZXIuJ1xyXG4gICAgfVxyXG4gIH0sXHJcbiAgaGlnaGNoYXJ0czoge1xyXG4gICAgdmVyc2lvbjoge1xyXG4gICAgICB2YWx1ZTogJ2xhdGVzdCcsXHJcbiAgICAgIGVudkxpbms6ICdISUdIQ0hBUlRTX1ZFUlNJT04nLFxyXG4gICAgICB0eXBlOiAnc3RyaW5nJyxcclxuICAgICAgZGVzY3JpcHRpb246ICdIaWdoY2hhcnRzIHZlcnNpb24gdG8gdXNlLidcclxuICAgIH0sXHJcbiAgICBjZG5VUkw6IHtcclxuICAgICAgdmFsdWU6ICdodHRwczovL2NvZGUuaGlnaGNoYXJ0cy5jb20vJyxcclxuICAgICAgZW52TGluazogJ0hJR0hDSEFSVFNfQ0ROJyxcclxuICAgICAgdHlwZTogJ3N0cmluZycsXHJcbiAgICAgIGRlc2NyaXB0aW9uOiAnVGhlIENETiBVUkwgb2YgSGlnaGNoYXJ0cyBzY3JpcHRzIHRvIHVzZS4nXHJcbiAgICB9LFxyXG4gICAgY29yZVNjcmlwdHM6IHtcclxuICAgICAgZW52TGluazogJ0hJR0hDSEFSVFNfQ09SRV9TQ1JJUFRTJyxcclxuICAgICAgdmFsdWU6IFsnaGlnaGNoYXJ0cycsICdoaWdoY2hhcnRzLW1vcmUnLCAnaGlnaGNoYXJ0cy0zZCddLFxyXG4gICAgICB0eXBlOiAnc3RyaW5nW10nLFxyXG4gICAgICBkZXNjcmlwdGlvbjogJ0hpZ2hjaGFydHMgY29yZSBzY3JpcHRzIHRvIGZldGNoLidcclxuICAgIH0sXHJcbiAgICBtb2R1bGVzOiB7XHJcbiAgICAgIGVudkxpbms6ICdISUdIQ0hBUlRTX01PRFVMRVMnLFxyXG4gICAgICB2YWx1ZTogW1xyXG4gICAgICAgICdzdG9jaycsXHJcbiAgICAgICAgJ21hcCcsXHJcbiAgICAgICAgJ2dhbnR0JyxcclxuICAgICAgICAnZXhwb3J0aW5nJyxcclxuICAgICAgICAnZXhwb3J0LWRhdGEnLFxyXG4gICAgICAgICdwYXJhbGxlbC1jb29yZGluYXRlcycsXHJcbiAgICAgICAgJ2FjY2Vzc2liaWxpdHknLFxyXG4gICAgICAgICdhbm5vdGF0aW9ucy1hZHZhbmNlZCcsXHJcbiAgICAgICAgJ2Jvb3N0LWNhbnZhcycsXHJcbiAgICAgICAgJ2Jvb3N0JyxcclxuICAgICAgICAnZGF0YScsXHJcbiAgICAgICAgJ2RyYWdnYWJsZS1wb2ludHMnLFxyXG4gICAgICAgICdzdGF0aWMtc2NhbGUnLFxyXG4gICAgICAgICdicm9rZW4tYXhpcycsXHJcbiAgICAgICAgJ2hlYXRtYXAnLFxyXG4gICAgICAgICd0aWxlbWFwJyxcclxuICAgICAgICAndGltZWxpbmUnLFxyXG4gICAgICAgICd0cmVlbWFwJyxcclxuICAgICAgICAndHJlZWdyYXBoJyxcclxuICAgICAgICAnaXRlbS1zZXJpZXMnLFxyXG4gICAgICAgICdkcmlsbGRvd24nLFxyXG4gICAgICAgICdoaXN0b2dyYW0tYmVsbGN1cnZlJyxcclxuICAgICAgICAnYnVsbGV0JyxcclxuICAgICAgICAnZnVubmVsJyxcclxuICAgICAgICAnZnVubmVsM2QnLFxyXG4gICAgICAgICdweXJhbWlkM2QnLFxyXG4gICAgICAgICduZXR3b3JrZ3JhcGgnLFxyXG4gICAgICAgICdwYXJldG8nLFxyXG4gICAgICAgICdwYXR0ZXJuLWZpbGwnLFxyXG4gICAgICAgICdwaWN0b3JpYWwnLFxyXG4gICAgICAgICdwcmljZS1pbmRpY2F0b3InLFxyXG4gICAgICAgICdzYW5rZXknLFxyXG4gICAgICAgICdhcmMtZGlhZ3JhbScsXHJcbiAgICAgICAgJ2RlcGVuZGVuY3ktd2hlZWwnLFxyXG4gICAgICAgICdzZXJpZXMtbGFiZWwnLFxyXG4gICAgICAgICdzb2xpZC1nYXVnZScsXHJcbiAgICAgICAgJ3NvbmlmaWNhdGlvbicsXHJcbiAgICAgICAgJ3N0b2NrLXRvb2xzJyxcclxuICAgICAgICAnc3RyZWFtZ3JhcGgnLFxyXG4gICAgICAgICdzdW5idXJzdCcsXHJcbiAgICAgICAgJ3ZhcmlhYmxlLXBpZScsXHJcbiAgICAgICAgJ3Zhcml3aWRlJyxcclxuICAgICAgICAndmVjdG9yJyxcclxuICAgICAgICAndmVubicsXHJcbiAgICAgICAgJ3dpbmRiYXJiJyxcclxuICAgICAgICAnd29yZGNsb3VkJyxcclxuICAgICAgICAneHJhbmdlJyxcclxuICAgICAgICAnbm8tZGF0YS10by1kaXNwbGF5JyxcclxuICAgICAgICAnZHJhZy1wYW5lcycsXHJcbiAgICAgICAgJ2RlYnVnZ2VyJyxcclxuICAgICAgICAnZHVtYmJlbGwnLFxyXG4gICAgICAgICdsb2xsaXBvcCcsXHJcbiAgICAgICAgJ2N5bGluZGVyJyxcclxuICAgICAgICAnb3JnYW5pemF0aW9uJyxcclxuICAgICAgICAnZG90cGxvdCcsXHJcbiAgICAgICAgJ21hcmtlci1jbHVzdGVycycsXHJcbiAgICAgICAgJ2hvbGxvd2NhbmRsZXN0aWNrJyxcclxuICAgICAgICAnaGVpa2luYXNoaSdcclxuICAgICAgXSxcclxuICAgICAgdHlwZTogJ3N0cmluZ1tdJyxcclxuICAgICAgZGVzY3JpcHRpb246ICdIaWdoY2hhcnRzIG1vZHVsZXMgdG8gZmV0Y2guJ1xyXG4gICAgfSxcclxuICAgIGluZGljYXRvcnM6IHtcclxuICAgICAgZW52TGluazogJ0hJR0hDSEFSVFNfSU5ESUNBVE9SUycsXHJcbiAgICAgIHZhbHVlOiBbJ2luZGljYXRvcnMtYWxsJ10sXHJcbiAgICAgIHR5cGU6ICdzdHJpbmdbXScsXHJcbiAgICAgIGRlc2NyaXB0aW9uOiAnSGlnaGNoYXJ0cyBpbmRpY2F0b3JzIHRvIGZldGNoLidcclxuICAgIH0sXHJcbiAgICBzY3JpcHRzOiB7XHJcbiAgICAgIHZhbHVlOiBbXHJcbiAgICAgICAgJ2h0dHBzOi8vY2RuanMuY2xvdWRmbGFyZS5jb20vYWpheC9saWJzL21vbWVudC5qcy8yLjI5LjQvbW9tZW50Lm1pbi5qcycsXHJcbiAgICAgICAgJ2h0dHBzOi8vY2RuanMuY2xvdWRmbGFyZS5jb20vYWpheC9saWJzL21vbWVudC10aW1lem9uZS8wLjUuMzQvbW9tZW50LXRpbWV6b25lLXdpdGgtZGF0YS5taW4uanMnXHJcbiAgICAgIF0sXHJcbiAgICAgIHR5cGU6ICdzdHJpbmdbXScsXHJcbiAgICAgIGRlc2NyaXB0aW9uOlxyXG4gICAgICAgICdBZGRpdGlvbmFsIGRpcmVjdCBzY3JpcHRzL29wdGlvbmFsIGRlcGVuZGVuY2llcyAoZS5nLiBtb21lbnQuanMpLidcclxuICAgIH0sXHJcbiAgICBmb3JjZUZldGNoOiB7XHJcbiAgICAgIGVudkxpbms6ICdISUdIQ0hBUlRTX0ZPUkNFX0ZFVENIJyxcclxuICAgICAgdmFsdWU6IGZhbHNlLFxyXG4gICAgICB0eXBlOiAnYm9vbGVhbicsXHJcbiAgICAgIGRlc2NyaXB0aW9uOlxyXG4gICAgICAgICdTaG91bGQgYWxsIHRoZSBzY3JpcHRzIGJlIHJlZmV0Y2hlZCBhZnRlciByZXJ1bm5pbmcgdGhlIHNlcnZlci4nXHJcbiAgICB9XHJcbiAgfSxcclxuICBleHBvcnQ6IHtcclxuICAgIGluZmlsZToge1xyXG4gICAgICB2YWx1ZTogZmFsc2UsXHJcbiAgICAgIHR5cGU6ICdzdHJpbmcnLFxyXG4gICAgICBkZXNjcmlwdGlvbjpcclxuICAgICAgICAnVGhlIGlucHV0IGZpbGUgbmFtZSBhbG9uZyB3aXRoIGEgdHlwZSAoanNvbiBvciBzdmcpLiBJdCBjYW4gYmUgYSBjb3JyZWN0IEpTT04gb3IgU1ZHIGZpbGUuJ1xyXG4gICAgfSxcclxuICAgIGluc3RyOiB7XHJcbiAgICAgIHZhbHVlOiBmYWxzZSxcclxuICAgICAgdHlwZTogJ3N0cmluZycsXHJcbiAgICAgIGRlc2NyaXB0aW9uOlxyXG4gICAgICAgICdBbiBpbnB1dCBpbiBhIGZvcm0gb2YgYSBzdHJpbmdpZmllZCBKU09OIG9yIFNWRyBmaWxlLiBPdmVycmlkZXMgdGhlIC0taW5maWxlLidcclxuICAgIH0sXHJcbiAgICBvcHRpb25zOiB7XHJcbiAgICAgIHZhbHVlOiBmYWxzZSxcclxuICAgICAgdHlwZTogJ3N0cmluZycsXHJcbiAgICAgIGRlc2NyaXB0aW9uOiAnQW4gYWxpYXMgZm9yIHRoZSAtLWluc3RyIG9wdGlvbi4nXHJcbiAgICB9LFxyXG4gICAgb3V0ZmlsZToge1xyXG4gICAgICB2YWx1ZTogZmFsc2UsXHJcbiAgICAgIHR5cGU6ICdzdHJpbmcnLFxyXG4gICAgICBkZXNjcmlwdGlvbjpcclxuICAgICAgICAnVGhlIG91dHB1dCBmaWxlbmFtZSBhbG9uZyB3aXRoIGEgdHlwZSAoanBlZywgcG5nLCBwZGYgb3Igc3ZnKS4gSWdub3JlcyB0aGUgLS10eXBlIGZsYWcuJ1xyXG4gICAgfSxcclxuICAgIHR5cGU6IHtcclxuICAgICAgZW52TGluazogJ0VYUE9SVF9ERUZBVUxUX1RZUEUnLFxyXG4gICAgICB2YWx1ZTogJ3BuZycsXHJcbiAgICAgIHR5cGU6ICdzdHJpbmcnLFxyXG4gICAgICBkZXNjcmlwdGlvbjpcclxuICAgICAgICAnVGhlIGZvcm1hdCBvZiB0aGUgZmlsZSB0byBleHBvcnQgdG8uIENhbiBiZSBqcGVnLCBwbmcsIHBkZiBvciBzdmcuJ1xyXG4gICAgfSxcclxuICAgIGNvbnN0cjoge1xyXG4gICAgICBlbnZMaW5rOiAnRVhQT1JUX0RFRkFVTFRfQ09OU1RSJyxcclxuICAgICAgdmFsdWU6ICdjaGFydCcsXHJcbiAgICAgIHR5cGU6ICdzdHJpbmcnLFxyXG4gICAgICBkZXNjcmlwdGlvbjpcclxuICAgICAgICAnVGhlIGNvbnN0cnVjdG9yIHRvIHVzZS4gQ2FuIGJlIGNoYXJ0LCBzdG9ja0NoYXJ0LCBtYXBDaGFydCBvciBnYW50dENoYXJ0LidcclxuICAgIH0sXHJcbiAgICBkZWZhdWx0SGVpZ2h0OiB7XHJcbiAgICAgIGVudkxpbms6ICdFWFBPUlRfREVGQVVMVF9IRUlHSFQnLFxyXG4gICAgICB2YWx1ZTogNDAwLFxyXG4gICAgICB0eXBlOiAnbnVtYmVyJyxcclxuICAgICAgZGVzY3JpcHRpb246XHJcbiAgICAgICAgJ1RoZSBkZWZhdWx0IGhlaWdodCBvZiB0aGUgZXhwb3J0ZWQgY2hhcnQuIFVzZWQgd2hlbiBub3QgZm91bmQgYW55IHZhbHVlIHNldC4nXHJcbiAgICB9LFxyXG4gICAgZGVmYXVsdFdpZHRoOiB7XHJcbiAgICAgIGVudkxpbms6ICdFWFBPUlRfREVGQVVMVF9XSURUSCcsXHJcbiAgICAgIHZhbHVlOiA2MDAsXHJcbiAgICAgIHR5cGU6ICdudW1iZXInLFxyXG4gICAgICBkZXNjcmlwdGlvbjpcclxuICAgICAgICAnVGhlIGRlZmF1bHQgd2lkdGggb2YgdGhlIGV4cG9ydGVkIGNoYXJ0LiBVc2VkIHdoZW4gbm90IGZvdW5kIGFueSB2YWx1ZSBzZXQuJ1xyXG4gICAgfSxcclxuICAgIGRlZmF1bHRTY2FsZToge1xyXG4gICAgICBlbnZMaW5rOiAnRVhQT1JUX0RFRkFVTFRfU0NBTEUnLFxyXG4gICAgICB2YWx1ZTogMSxcclxuICAgICAgdHlwZTogJ251bWJlcicsXHJcbiAgICAgIGRlc2NyaXB0aW9uOlxyXG4gICAgICAgICdUaGUgZGVmYXVsdCBzY2FsZSBvZiB0aGUgZXhwb3J0ZWQgY2hhcnQuIFJhbmdlcyBiZXR3ZWVuIDEgYW5kIDUuJ1xyXG4gICAgfSxcclxuICAgIGhlaWdodDoge1xyXG4gICAgICB0eXBlOiAnbnVtYmVyJyxcclxuICAgICAgdmFsdWU6IGZhbHNlLFxyXG4gICAgICBkZXNjcmlwdGlvbjpcclxuICAgICAgICAnVGhlIGRlZmF1bHQgaGVpZ2h0IG9mIHRoZSBleHBvcnRlZCBjaGFydC4gT3ZlcnJpZGVzIHRoZSBvcHRpb24gaW4gdGhlIGNoYXJ0IHNldHRpbmdzLidcclxuICAgIH0sXHJcbiAgICB3aWR0aDoge1xyXG4gICAgICB0eXBlOiAnbnVtYmVyJyxcclxuICAgICAgdmFsdWU6IGZhbHNlLFxyXG4gICAgICBkZXNjcmlwdGlvbjpcclxuICAgICAgICAnVGhlIHdpZHRoIG9mIHRoZSBleHBvcnRlZCBjaGFydC4gT3ZlcnJpZGVzIHRoZSBvcHRpb24gaW4gdGhlIGNoYXJ0IHNldHRpbmdzLidcclxuICAgIH0sXHJcbiAgICBzY2FsZToge1xyXG4gICAgICB2YWx1ZTogZmFsc2UsXHJcbiAgICAgIHR5cGU6ICdudW1iZXInLFxyXG4gICAgICBkZXNjcmlwdGlvbjogJ1RoZSBzY2FsZSBvZiB0aGUgZXhwb3J0ZWQgY2hhcnQuIFJhbmdlcyBiZXR3ZWVuIDEgYW5kIDUuJ1xyXG4gICAgfSxcclxuICAgIGdsb2JhbE9wdGlvbnM6IHtcclxuICAgICAgdmFsdWU6IGZhbHNlLFxyXG4gICAgICB0eXBlOiAnc3RyaW5nJyxcclxuICAgICAgZGVzY3JpcHRpb246XHJcbiAgICAgICAgJ0Egc3RyaW5naWZpZWQgSlNPTiBvciBhIGZpbGVuYW1lIHdpdGggb3B0aW9ucyB0byBiZSBwYXNzZWQgaW50byB0aGUgSGlnaGNoYXJ0cy5zZXRPcHRpb25zLidcclxuICAgIH0sXHJcbiAgICB0aGVtZU9wdGlvbnM6IHtcclxuICAgICAgdmFsdWU6IGZhbHNlLFxyXG4gICAgICB0eXBlOiAnc3RyaW5nJyxcclxuICAgICAgZGVzY3JpcHRpb246XHJcbiAgICAgICAgJ0Egc3RyaW5naWZpZWQgSlNPTiBvciBhIGZpbGVuYW1lIHdpdGggdGhlbWUgb3B0aW9ucyB0byBiZSBwYXNzZWQgaW50byB0aGUgSGlnaGNoYXJ0cy5zZXRPcHRpb25zLidcclxuICAgIH0sXHJcbiAgICBiYXRjaDoge1xyXG4gICAgICB2YWx1ZTogZmFsc2UsXHJcbiAgICAgIHR5cGU6ICdzdHJpbmcnLFxyXG4gICAgICBkZXNjcmlwdGlvbjpcclxuICAgICAgICAnU3RhcnRzIGEgYmF0Y2ggam9iLiBBIHN0cmluZyB0aGF0IGNvbnRhaW5zIGlucHV0L291dHB1dCBwYWlyczogXCJpbj1vdXQ7aW49b3V0Oy4uXCIuJ1xyXG4gICAgfVxyXG4gIH0sXHJcbiAgY3VzdG9tQ29kZToge1xyXG4gICAgYWxsb3dDb2RlRXhlY3V0aW9uOiB7XHJcbiAgICAgIGVudkxpbms6ICdISUdIQ0hBUlRTX0FMTE9XX0NPREVfRVhFQ1VUSU9OJyxcclxuICAgICAgdmFsdWU6IGZhbHNlLFxyXG4gICAgICB0eXBlOiAnYm9vbGVhbicsXHJcbiAgICAgIGRlc2NyaXB0aW9uOlxyXG4gICAgICAgICdJZiBzZXQgdG8gdHJ1ZSwgYWxsb3cgZm9yIHRoZSBleGVjdXRpb24gb2YgYXJiaXRyYXJ5IGNvZGUgd2hlbiBleHBvcnRpbmcuJ1xyXG4gICAgfSxcclxuICAgIGFsbG93RmlsZVJlc291cmNlczoge1xyXG4gICAgICBlbnZMaW5rOiAnSElHSENIQVJUU19BTExPV19GSUxFX1JFU09VUkNFUycsXHJcbiAgICAgIHZhbHVlOiB0cnVlLFxyXG4gICAgICB0eXBlOiAnYm9vbGVhbicsXHJcbiAgICAgIGRlc2NyaXB0aW9uOlxyXG4gICAgICAgICdBbGxvdyBpbmplY3RpbmcgcmVzb3VyY2VzIGZyb20gdGhlIGZpbGVzeXN0ZW0uIEhhcyBubyBlZmZlY3Qgd2hlbiBydW5uaW5nIGFzIGEgc2VydmVyLidcclxuICAgIH0sXHJcbiAgICBjdXN0b21Db2RlOiB7XHJcbiAgICAgIHZhbHVlOiBmYWxzZSxcclxuICAgICAgdHlwZTogJ3N0cmluZycsXHJcbiAgICAgIGRlc2NyaXB0aW9uOlxyXG4gICAgICAgICdBIGZ1bmN0aW9uIHRvIGJlIGNhbGxlZCBiZWZvcmUgY2hhcnQgaW5pdGlhbGl6YXRpb24uIENhbiBiZSBhIGZpbGVuYW1lIHdpdGggdGhlIGpzIGV4dGVuc2lvbi4nXHJcbiAgICB9LFxyXG4gICAgY2FsbGJhY2s6IHtcclxuICAgICAgdmFsdWU6IGZhbHNlLFxyXG4gICAgICB0eXBlOiAnc3RyaW5nJyxcclxuICAgICAgZGVzY3JpcHRpb246ICdBIEphdmFTY3JpcHQgZmlsZSB3aXRoIGEgZnVuY3Rpb24gdG8gcnVuIG9uIGNvbnN0cnVjdGlvbi4nXHJcbiAgICB9LFxyXG4gICAgcmVzb3VyY2VzOiB7XHJcbiAgICAgIHZhbHVlOiBmYWxzZSxcclxuICAgICAgdHlwZTogJ3N0cmluZycsXHJcbiAgICAgIGRlc2NyaXB0aW9uOlxyXG4gICAgICAgICdBbiBhZGRpdGlvbmFsIHJlc291cmNlIGluIGEgZm9ybSBvZiBzdHJpbmdpZmllZCBKU09OLiBJdCBjYW4gY29udGFpbiBmaWxlcywganMgYW5kIGNzcyBzZWN0aW9ucy4nXHJcbiAgICB9LFxyXG4gICAgbG9hZENvbmZpZzoge1xyXG4gICAgICB2YWx1ZTogZmFsc2UsXHJcbiAgICAgIHR5cGU6ICdzdHJpbmcnLFxyXG4gICAgICBkZXNjcmlwdGlvbjogJ0EgZmlsZSB0aGF0IGNvbnRhaW5zIGEgcHJlLWRlZmluZWQgY29uZmlnIHRvIHVzZS4nXHJcbiAgICB9LFxyXG4gICAgY3JlYXRlQ29uZmlnOiB7XHJcbiAgICAgIHZhbHVlOiBmYWxzZSxcclxuICAgICAgdHlwZTogJ3N0cmluZycsXHJcbiAgICAgIGRlc2NyaXB0aW9uOlxyXG4gICAgICAgICdBbGxvd3MgdG8gc2V0IG9wdGlvbnMgdGhyb3VnaCBhIHByb21wdCBhbmQgc2F2ZSBpbiBhIHByb3ZpZGVkIGNvbmZpZyBmaWxlLidcclxuICAgIH1cclxuICB9LFxyXG4gIHNlcnZlcjoge1xyXG4gICAgZW5hYmxlOiB7XHJcbiAgICAgIGVudkxpbms6ICdISUdIQ0hBUlRTX1NFUlZFUl9FTkFCTEUnLFxyXG4gICAgICB2YWx1ZTogZmFsc2UsXHJcbiAgICAgIHR5cGU6ICdib29sZWFuJyxcclxuICAgICAgY2xpTmFtZTogJ2VuYWJsZVNlcnZlcicsXHJcbiAgICAgIGRlc2NyaXB0aW9uOiAnSWYgc2V0IHRvIHRydWUsIHN0YXJ0cyBhIHNlcnZlciBvbiAwLjAuMC4wLidcclxuICAgIH0sXHJcbiAgICBob3N0OiB7XHJcbiAgICAgIGVudkxpbms6ICdISUdIQ0hBUlRTX1NFUlZFUl9IT1NUJyxcclxuICAgICAgdmFsdWU6ICcwLjAuMC4wJyxcclxuICAgICAgdHlwZTogJ3N0cmluZycsXHJcbiAgICAgIGRlc2NyaXB0aW9uOlxyXG4gICAgICAgICdUaGUgaG9zdG5hbWUgb2YgdGhlIHNlcnZlci4gQWxzbyBzdGFydHMgYSBzZXJ2ZXIgbGlzdGVuaW5nIG9uIHRoZSBzdXBwbGllZCBob3N0bmFtZS4nXHJcbiAgICB9LFxyXG4gICAgcG9ydDoge1xyXG4gICAgICBlbnZMaW5rOiAnSElHSENIQVJUU19TRVJWRVJfUE9SVCcsXHJcbiAgICAgIHZhbHVlOiA3ODAxLFxyXG4gICAgICB0eXBlOiAnbnVtYmVyJyxcclxuICAgICAgZGVzY3JpcHRpb246ICdUaGUgcG9ydCB0byB1c2UgZm9yIHRoZSBzZXJ2ZXIuIERlZmF1bHRzIHRvIDc4MDEuJ1xyXG4gICAgfSxcclxuICAgIHNzbDoge1xyXG4gICAgICBlbmFibGU6IHtcclxuICAgICAgICBlbnZMaW5rOiAnSElHSENIQVJUU19TRVJWRVJfU1NMX0VOQUJMRScsXHJcbiAgICAgICAgdmFsdWU6IGZhbHNlLFxyXG4gICAgICAgIHR5cGU6ICdib29sZWFuJyxcclxuICAgICAgICBjbGlOYW1lOiAnZW5hYmxlU3NsJyxcclxuICAgICAgICBkZXNjcmlwdGlvbjogJ0VuYWJsZXMgdGhlIFNTTCBwcm90b2NvbC4nXHJcbiAgICAgIH0sXHJcbiAgICAgIGZvcmNlOiB7XHJcbiAgICAgICAgZW52TGluazogJ0hJR0hDSEFSVFNfU0VSVkVSX1NTTF9GT1JDRScsXHJcbiAgICAgICAgdmFsdWU6IGZhbHNlLFxyXG4gICAgICAgIHR5cGU6ICdib29sZWFuJyxcclxuICAgICAgICBjbGlOYW1lOiAnc3NsRm9yY2VkJyxcclxuICAgICAgICBkZXNjcmlwdGlvbjpcclxuICAgICAgICAgICdJZiBzZXQgdG8gdHJ1ZSwgZm9yY2VzIHRoZSBzZXJ2ZXIgdG8gb25seSBzZXJ2ZSBvdmVyIEhUVFBTLidcclxuICAgICAgfSxcclxuICAgICAgcG9ydDoge1xyXG4gICAgICAgIGVudkxpbms6ICdISUdIQ0hBUlRTX1NFUlZFUl9TU0xfUE9SVCcsXHJcbiAgICAgICAgdmFsdWU6IDQ0MyxcclxuICAgICAgICB0eXBlOiAnbnVtYmVyJyxcclxuICAgICAgICBjbGlOYW1lOiAnc3NsUG9ydCcsXHJcbiAgICAgICAgZGVzY3JpcHRpb246ICdUaGUgcG9ydCBvbiB3aGljaCB0byBydW4gdGhlIFNTTCBzZXJ2ZXIuJ1xyXG4gICAgICB9LFxyXG4gICAgICBjZXJ0UGF0aDoge1xyXG4gICAgICAgIGVudkxpbms6ICdISUdIQ0hBUlRTX1NTTF9DRVJUX1BBVEgnLFxyXG4gICAgICAgIHZhbHVlOiAnJyxcclxuICAgICAgICB0eXBlOiAnc3RyaW5nJyxcclxuICAgICAgICBkZXNjcmlwdGlvbjogJ1RoZSBwYXRoIHRvIHRoZSBTU0wgY2VydGlmaWNhdGUva2V5LidcclxuICAgICAgfVxyXG4gICAgfSxcclxuICAgIHJhdGVMaW1pdGluZzoge1xyXG4gICAgICBlbmFibGU6IHtcclxuICAgICAgICBlbnZMaW5rOiAnSElHSENIQVJUU19SQVRFX0xJTUlUX0VOQUJMRScsXHJcbiAgICAgICAgdmFsdWU6IGZhbHNlLFxyXG4gICAgICAgIHR5cGU6ICdib29sZWFuJyxcclxuICAgICAgICBjbGlOYW1lOiAnZW5hYmxlUmF0ZUxpbWl0aW5nJyxcclxuICAgICAgICBkZXNjcmlwdGlvbjogJ0VuYWJsZXMgcmF0ZSBsaW1pdGluZy4nXHJcbiAgICAgIH0sXHJcbiAgICAgIG1heFJlcXVlc3RzOiB7XHJcbiAgICAgICAgZW52TGluazogJ0hJR0hDSEFSVFNfUkFURV9MSU1JVF9NQVgnLFxyXG4gICAgICAgIHZhbHVlOiAxMCxcclxuICAgICAgICB0eXBlOiAnbnVtYmVyJyxcclxuICAgICAgICBkZXNjcmlwdGlvbjogJ01heCByZXF1ZXN0cyBhbGxvd2VkIGluIGEgb25lIG1pbnV0ZS4nXHJcbiAgICAgIH0sXHJcbiAgICAgIHdpbmRvdzoge1xyXG4gICAgICAgIGVudkxpbms6ICdISUdIQ0hBUlRTX1JBVEVfTElNSVRfV0lORE9XJyxcclxuICAgICAgICB2YWx1ZTogMSxcclxuICAgICAgICB0eXBlOiAnbnVtYmVyJyxcclxuICAgICAgICBkZXNjcmlwdGlvbjogJ1RoZSB0aW1lIHdpbmRvdyBpbiBtaW51dGVzIGZvciByYXRlIGxpbWl0aW5nLidcclxuICAgICAgfSxcclxuICAgICAgZGVsYXk6IHtcclxuICAgICAgICBlbnZMaW5rOiAnSElHSENIQVJUU19SQVRFX0xJTUlUX0RFTEFZJyxcclxuICAgICAgICB2YWx1ZTogMCxcclxuICAgICAgICB0eXBlOiAnbnVtYmVyJyxcclxuICAgICAgICBkZXNjcmlwdGlvbjpcclxuICAgICAgICAgICdUaGUgYW1vdW50IHRvIGRlbGF5IGVhY2ggc3VjY2Vzc2l2ZSByZXF1ZXN0IGJlZm9yZSBoaXR0aW5nIHRoZSBtYXguJ1xyXG4gICAgICB9LFxyXG4gICAgICB0cnVzdFByb3h5OiB7XHJcbiAgICAgICAgZW52TGluazogJ0hJR0hDSEFSVFNfUkFURV9MSU1JVF9UUlVTVF9QUk9YWScsXHJcbiAgICAgICAgdmFsdWU6IGZhbHNlLFxyXG4gICAgICAgIHR5cGU6ICdib29sZWFuJyxcclxuICAgICAgICBkZXNjcmlwdGlvbjogJ1NldCB0aGlzIHRvIHRydWUgaWYgYmVoaW5kIGEgbG9hZCBiYWxhbmNlci4nXHJcbiAgICAgIH0sXHJcbiAgICAgIHNraXBLZXk6IHtcclxuICAgICAgICBlbnZMaW5rOiAnSElHSENIQVJUU19SQVRFX0xJTUlUX1NLSVBfS0VZJyxcclxuICAgICAgICB2YWx1ZTogJycsXHJcbiAgICAgICAgdHlwZTogJ251bWJlcnxzdHJpbmcnLFxyXG4gICAgICAgIGRlc2NyaXB0aW9uOlxyXG4gICAgICAgICAgJ0FsbG93cyBieXBhc3NpbmcgdGhlIHJhdGUgbGltaXRlciBhbmQgc2hvdWxkIGJlIHByb3ZpZGVkIHdpdGggc2tpcFRva2VuIGFyZ3VtZW50LidcclxuICAgICAgfSxcclxuICAgICAgc2tpcFRva2VuOiB7XHJcbiAgICAgICAgZW52TGluazogJ0hJR0hDSEFSVFNfUkFURV9MSU1JVF9TS0lQX1RPS0VOJyxcclxuICAgICAgICB2YWx1ZTogJycsXHJcbiAgICAgICAgdHlwZTogJ251bWJlcnxzdHJpbmcnLFxyXG4gICAgICAgIGRlc2NyaXB0aW9uOlxyXG4gICAgICAgICAgJ0FsbG93cyBieXBhc3NpbmcgdGhlIHJhdGUgbGltaXRlciBhbmQgc2hvdWxkIGJlIHByb3ZpZGVkIHdpdGggc2tpcEtleSBhcmd1bWVudC4nXHJcbiAgICAgIH1cclxuICAgIH1cclxuICB9LFxyXG4gIHBvb2w6IHtcclxuICAgIG1pbldvcmtlcnM6IHtcclxuICAgICAgZW52TGluazogJ0hJR0hDSEFSVFNfUE9PTF9NSU5fV09SS0VSUycsXHJcbiAgICAgIHZhbHVlOiA0LFxyXG4gICAgICB0eXBlOiAnbnVtYmVyJyxcclxuICAgICAgZGVzY3JpcHRpb246ICdUaGUgbnVtYmVyIG9mIGluaXRpYWwgd29ya2VycyB0byBzcGF3bi4nXHJcbiAgICB9LFxyXG4gICAgbWF4V29ya2Vyczoge1xyXG4gICAgICBlbnZMaW5rOiAnSElHSENIQVJUU19QT09MX01BWF9XT1JLRVJTJyxcclxuICAgICAgdmFsdWU6IDgsXHJcbiAgICAgIHR5cGU6ICdudW1iZXInLFxyXG4gICAgICBkZXNjcmlwdGlvbjogJ1RoZSBudW1iZXIgb2YgbWF4IHdvcmtlcnMgdG8gc3Bhd24uJ1xyXG4gICAgfSxcclxuICAgIHdvcmtMaW1pdDoge1xyXG4gICAgICBlbnZMaW5rOiAnSElHSENIQVJUU19QT09MX1dPUktfTElNSVQnLFxyXG4gICAgICB2YWx1ZTogNDAsXHJcbiAgICAgIHR5cGU6ICdudW1iZXInLFxyXG4gICAgICBkZXNjcmlwdGlvbjpcclxuICAgICAgICAnVGhlIHBpZWNlcyBvZiB3b3JrIHRoYXQgY2FuIGJlIHBlcmZvcm1lZCBiZWZvcmUgcmVzdGFydGluZyBwcm9jZXNzLidcclxuICAgIH0sXHJcbiAgICBhY3F1aXJlVGltZW91dDoge1xyXG4gICAgICBlbnZMaW5rOiAnSElHSENIQVJUU19QT09MX0FDUVVJUkVfVElNRU9VVCcsXHJcbiAgICAgIHZhbHVlOiA1MDAwLFxyXG4gICAgICB0eXBlOiAnbnVtYmVyJyxcclxuICAgICAgZGVzY3JpcHRpb246XHJcbiAgICAgICAgJ1RoZSBudW1iZXIgb2YgbWlsbGlzZWNvbmRzIHRvIHdhaXQgZm9yIGFjcXVpcmluZyBhIHJlc291cmNlLidcclxuICAgIH0sXHJcbiAgICBjcmVhdGVUaW1lb3V0OiB7XHJcbiAgICAgIGVudkxpbms6ICdISUdIQ0hBUlRTX1BPT0xfQ1JFQVRFX1RJTUVPVVQnLFxyXG4gICAgICB2YWx1ZTogNTAwMCxcclxuICAgICAgdHlwZTogJ251bWJlcicsXHJcbiAgICAgIGRlc2NyaXB0aW9uOiAnVGhlIG51bWJlciBvZiBtaWxsaXNlY29uZHMgdG8gd2FpdCBmb3IgY3JlYXRpbmcgYSByZXNvdXJjZS4nXHJcbiAgICB9LFxyXG4gICAgZGVzdHJveVRpbWVvdXQ6IHtcclxuICAgICAgZW52TGluazogJ0hJR0hDSEFSVFNfUE9PTF9ERVNUUk9ZX1RJTUVPVVQnLFxyXG4gICAgICB2YWx1ZTogNTAwMCxcclxuICAgICAgdHlwZTogJ251bWJlcicsXHJcbiAgICAgIGRlc2NyaXB0aW9uOlxyXG4gICAgICAgICdUaGUgbnVtYmVyIG9mIG1pbGxpc2Vjb25kcyB0byB3YWl0IGZvciBkZXN0cm95aW5nIGEgcmVzb3VyY2UuJ1xyXG4gICAgfSxcclxuICAgIGlkbGVUaW1lb3V0OiB7XHJcbiAgICAgIGVudkxpbms6ICdISUdIQ0hBUlRTX1BPT0xfSURMRV9USU1FT1VUJyxcclxuICAgICAgdmFsdWU6IDMwMDAwLFxyXG4gICAgICB0eXBlOiAnbnVtYmVyJyxcclxuICAgICAgZGVzY3JpcHRpb246XHJcbiAgICAgICAgJ1RoZSBudW1iZXIgb2YgbWlsbGlzZWNvbmRzIGFmdGVyIGFuIGlkbGUgcmVzb3VyY2UgaXMgZGVzdHJveWVkLidcclxuICAgIH0sXHJcbiAgICByYXN0ZXJpemF0aW9uVGltZW91dDoge1xyXG4gICAgICBlbnZMaW5rOiAnSElHSENIQVJUU19QT09MX1JBU1RFUklaQVRJT05fVElNRU9VVCcsXHJcbiAgICAgIHZhbHVlOiAxNTAwLFxyXG4gICAgICB0eXBlOiAnbnVtYmVyJyxcclxuICAgICAgZGVzY3JpcHRpb246ICdUaGUgbnVtYmVyIG9mIG1pbGxpc2Vjb25kcyB0byB3YWl0IGZvciByZW5kZXJpbmcgYSB3ZWJwYWdlLidcclxuICAgIH0sXHJcbiAgICBjcmVhdGVSZXRyeUludGVydmFsOiB7XHJcbiAgICAgIGVudkxpbms6ICdISUdIQ0hBUlRTX1BPT0xfQ1JFQVRFX1JFVFJZX0lOVEVSVkFMJyxcclxuICAgICAgdmFsdWU6IDIwMCxcclxuICAgICAgdHlwZTogJ251bWJlcicsXHJcbiAgICAgIGRlc2NyaXB0aW9uOlxyXG4gICAgICAgICdUaGUgbnVtYmVyIG9mIG1pbGxpc2Vjb25kcyBhZnRlciB0aGUgY3JlYXRlIHByb2Nlc3MgaXMgcmV0cmllZCBpbiBjYXNlIG9mIGZhaWwuJ1xyXG4gICAgfSxcclxuICAgIHJlYXBlckludGVydmFsOiB7XHJcbiAgICAgIGVudkxpbms6ICdISUdIQ0hBUlRTX1BPT0xfUkVBUEVSX0lOVEVSVkFMJyxcclxuICAgICAgdmFsdWU6IDEwMDAsXHJcbiAgICAgIHR5cGU6ICdudW1iZXInLFxyXG4gICAgICBkZXNjcmlwdGlvbjpcclxuICAgICAgICAnVGhlIG51bWJlciBvZiBtaWxsaXNlY29uZHMgYWZ0ZXIgdGhlIGNoZWNrIGZvciBpZGxlIHJlc291cmNlcyB0byBkZXN0cm95IGlzIHRyaWdnZXJlZC4nXHJcbiAgICB9LFxyXG4gICAgYmVuY2htYXJraW5nOiB7XHJcbiAgICAgIGVudkxpbms6ICdISUdIQ0hBUlRTX1BPT0xfQkVOQ0hNQVJLSU5HJyxcclxuICAgICAgdmFsdWU6IGZhbHNlLFxyXG4gICAgICB0eXBlOiAnYm9vbGVhbicsXHJcbiAgICAgIGRlc2NyaXB0aW9uOiAnRW5hYmxlIGJlbmNobWFya2luZy4nXHJcbiAgICB9LFxyXG4gICAgbGlzdGVuVG9Qcm9jZXNzRXhpdHM6IHtcclxuICAgICAgZW52TGluazogJ0hJR0hDSEFSVFNfUE9PTF9MSVNURU5fVE9fUFJPQ0VTU19FWElUUycsXHJcbiAgICAgIHZhbHVlOiB0cnVlLFxyXG4gICAgICB0eXBlOiAnYm9vbGVhbicsXHJcbiAgICAgIGRlc2NyaXB0aW9uOlxyXG4gICAgICAgICdTZXQgdG8gZmFsc2UgaW4gb3JkZXIgdG8gc2tpcCBhdHRhY2hpbmcgcHJvY2Vzcy5leGl0IGhhbmRsZXJzLidcclxuICAgIH1cclxuICB9LFxyXG4gIGxvZ2dpbmc6IHtcclxuICAgIGxldmVsOiB7XHJcbiAgICAgIGVudkxpbms6ICdISUdIQ0hBUlRTX0xPR19MRVZFTCcsXHJcbiAgICAgIHZhbHVlOiA0LFxyXG4gICAgICB0eXBlOiAnbnVtYmVyJyxcclxuICAgICAgY2xpTmFtZTogJ2xvZ0xldmVsJyxcclxuICAgICAgZGVzY3JpcHRpb246XHJcbiAgICAgICAgJ1RoZSBsb2cgbGV2ZWwgKDA6IHNpbGVudCwgMTogZXJyb3IsIDI6IHdhcm5pbmcsIDM6IG5vdGljZSwgNDogdmVyYm9zZSkuJ1xyXG4gICAgfSxcclxuICAgIGZpbGU6IHtcclxuICAgICAgZW52TGluazogJ0hJR0hDSEFSVFNfTE9HX0ZJTEUnLFxyXG4gICAgICB2YWx1ZTogJ2hpZ2hjaGFydHMtZXhwb3J0LXNlcnZlci5sb2cnLFxyXG4gICAgICB0eXBlOiAnc3RyaW5nJyxcclxuICAgICAgY2xpTmFtZTogJ2xvZ0ZpbGUnLFxyXG4gICAgICBkZXNjcmlwdGlvbjpcclxuICAgICAgICAnQSBuYW1lIG9mIGEgbG9nIGZpbGUuIFRoZSAtLWxvZ0Rlc3QgYWxzbyBuZWVkcyB0byBiZSBzZXQgdG8gZW5hYmxlIGZpbGUgbG9nZ2luZy4nXHJcbiAgICB9LFxyXG4gICAgZGVzdDoge1xyXG4gICAgICBlbnZMaW5rOiAnSElHSENIQVJUU19MT0dfREVTVCcsXHJcbiAgICAgIHZhbHVlOiAnbG9nLycsXHJcbiAgICAgIHR5cGU6ICdzdHJpbmcnLFxyXG4gICAgICBjbGlOYW1lOiAnbG9nRGVzdCcsXHJcbiAgICAgIGRlc2NyaXB0aW9uOiAnVGhlIHBhdGggdG8gc3RvcmUgbG9nIGZpbGVzLiBBbHNvIGVuYWJsZXMgZmlsZSBsb2dnaW5nLidcclxuICAgIH1cclxuICB9LFxyXG4gIHVpOiB7XHJcbiAgICBlbmFibGU6IHtcclxuICAgICAgZW52TGluazogJ0hJR0hDSEFSVFNfVUlfRU5BQkxFJyxcclxuICAgICAgdmFsdWU6IGZhbHNlLFxyXG4gICAgICB0eXBlOiAnYm9vbGVhbicsXHJcbiAgICAgIGNsaU5hbWU6ICdlbmFibGVVaScsXHJcbiAgICAgIGRlc2NyaXB0aW9uOiAnRW5hYmxlcyB0aGUgVUkgZm9yIHRoZSBleHBvcnQgc2VydmVyLidcclxuICAgIH0sXHJcbiAgICByb3V0ZToge1xyXG4gICAgICBlbnZMaW5rOiAnSElHSENIQVJUU19VSV9ST1VURScsXHJcbiAgICAgIHZhbHVlOiAnLycsXHJcbiAgICAgIHR5cGU6ICdzdHJpbmcnLFxyXG4gICAgICBjbGlOYW1lOiAndWlSb3V0ZScsXHJcbiAgICAgIGRlc2NyaXB0aW9uOiAnVGhlIHJvdXRlIHRvIGF0dGFjaCB0aGUgVUkgdG8uJ1xyXG4gICAgfVxyXG4gIH0sXHJcbiAgb3RoZXI6IHtcclxuICAgIG5vTG9nbzoge1xyXG4gICAgICBlbnZMaW5rOiAnSElHSENIQVJUU19OT19MT0dPJyxcclxuICAgICAgdmFsdWU6IGZhbHNlLFxyXG4gICAgICB0eXBlOiAnYm9vbGVhbicsXHJcbiAgICAgIGRlc2NyaXB0aW9uOlxyXG4gICAgICAgICdTa2lwIHByaW50aW5nIHRoZSBsb2dvIG9uIGEgc3RhcnR1cC4gV2lsbCBiZSByZXBsYWNlZCBieSBhIHNpbXBsZSB0ZXh0LidcclxuICAgIH1cclxuICB9LFxyXG4gIHBheWxvYWQ6IHt9XHJcbn07XHJcblxyXG4vLyBUaGUgY29uZmlnIGRlc2NyaXB0aW9ucyBvYmplY3QgZm9yIHRoZSBwcm9tcHRzIGZ1bmN0aW9uYWxpdHkuIEl0IGNvbnRhaW5zXHJcbi8vIGluZm9ybWF0aW9uIGxpa2U6XHJcbi8vICogVHlwZSBvZiBhIHByb21wdFxyXG4vLyAqIE5hbWUgb2YgYW4gb3B0aW9uXHJcbi8vICogU2hvcnQgZGVzY3JpcHRpb24gb2YgYSBjaG9zZW4gb3B0aW9uXHJcbi8vICogSW5pdGlhbCB2YWx1ZVxyXG5leHBvcnQgY29uc3QgcHJvbXB0c0NvbmZpZyA9IHtcclxuICBwdXBwZXRlZXI6IFtcclxuICAgIHtcclxuICAgICAgdHlwZTogJ2xpc3QnLFxyXG4gICAgICBuYW1lOiAnYXJncycsXHJcbiAgICAgIG1lc3NhZ2U6ICdQdXBwZXRlZXIgYXJndW1lbnRzJyxcclxuICAgICAgaW5pdGlhbDogZGVmYXVsdENvbmZpZy5wdXBwZXRlZXIuYXJncy52YWx1ZS5qb2luKCcsJyksXHJcbiAgICAgIHNlcGFyYXRvcjogJywnXHJcbiAgICB9XHJcbiAgXSxcclxuICBoaWdoY2hhcnRzOiBbXHJcbiAgICB7XHJcbiAgICAgIHR5cGU6ICd0ZXh0JyxcclxuICAgICAgbmFtZTogJ3ZlcnNpb24nLFxyXG4gICAgICBtZXNzYWdlOiAnSGlnaGNoYXJ0cyB2ZXJzaW9uJyxcclxuICAgICAgaW5pdGlhbDogZGVmYXVsdENvbmZpZy5oaWdoY2hhcnRzLnZlcnNpb24udmFsdWVcclxuICAgIH0sXHJcbiAgICB7XHJcbiAgICAgIHR5cGU6ICd0ZXh0JyxcclxuICAgICAgbmFtZTogJ2NkblVSTCcsXHJcbiAgICAgIG1lc3NhZ2U6ICdUaGUgdXJsIG9mIENETicsXHJcbiAgICAgIGluaXRpYWw6IGRlZmF1bHRDb25maWcuaGlnaGNoYXJ0cy5jZG5VUkwudmFsdWVcclxuICAgIH0sXHJcbiAgICB7XHJcbiAgICAgIHR5cGU6ICdtdWx0aXNlbGVjdCcsXHJcbiAgICAgIG5hbWU6ICdtb2R1bGVzJyxcclxuICAgICAgbWVzc2FnZTogJ0F2YWlsYWJsZSBtb2R1bGVzJyxcclxuICAgICAgaW5zdHJ1Y3Rpb25zOiAnU3BhY2U6IFNlbGVjdCBzcGVjaWZpYywgQTogU2VsZWN0IGFsbCwgRW50ZXI6IENvbmZpcm0uJyxcclxuICAgICAgY2hvaWNlczogZGVmYXVsdENvbmZpZy5oaWdoY2hhcnRzLm1vZHVsZXMudmFsdWVcclxuICAgIH0sXHJcbiAgICB7XHJcbiAgICAgIHR5cGU6ICdsaXN0JyxcclxuICAgICAgbmFtZTogJ3NjcmlwdHMnLFxyXG4gICAgICBtZXNzYWdlOiAnQ3VzdG9tIHNjcmlwdHMnLFxyXG4gICAgICBpbml0aWFsOiBkZWZhdWx0Q29uZmlnLmhpZ2hjaGFydHMuc2NyaXB0cy52YWx1ZS5qb2luKCcsJyksXHJcbiAgICAgIHNlcGFyYXRvcjogJywnXHJcbiAgICB9LFxyXG4gICAge1xyXG4gICAgICB0eXBlOiAndG9nZ2xlJyxcclxuICAgICAgbmFtZTogJ2ZvcmNlRmV0Y2gnLFxyXG4gICAgICBtZXNzYWdlOiAnU2hvdWxkIHJlZmV0Y2ggYWxsIHRoZSBzY3JpcHRzIGFmdGVyIGVhY2ggc2VydmVyIHJlcnVuJyxcclxuICAgICAgaW5pdGlhbDogZGVmYXVsdENvbmZpZy5oaWdoY2hhcnRzLmZvcmNlRmV0Y2gudmFsdWVcclxuICAgIH1cclxuICBdLFxyXG4gIGV4cG9ydDogW1xyXG4gICAge1xyXG4gICAgICB0eXBlOiAnc2VsZWN0JyxcclxuICAgICAgbmFtZTogJ3R5cGUnLFxyXG4gICAgICBtZXNzYWdlOiAnVGhlIGRlZmF1bHQgdHlwZSBvZiBhIGZpbGUgdG8gZXhwb3J0IHRvJyxcclxuICAgICAgaGludDogYERlZmF1bHQ6ICR7ZGVmYXVsdENvbmZpZy5leHBvcnQudHlwZS52YWx1ZX1gLFxyXG4gICAgICBpbml0aWFsOiAwLFxyXG4gICAgICBjaG9pY2VzOiBbJ3BuZycsICdqcGVnJywgJ3BkZicsICdzdmcnXVxyXG4gICAgfSxcclxuICAgIHtcclxuICAgICAgdHlwZTogJ3NlbGVjdCcsXHJcbiAgICAgIG5hbWU6ICdjb25zdHInLFxyXG4gICAgICBtZXNzYWdlOiAnVGhlIGRlZmF1bHQgY29uc3RydWN0b3IgZm9yIEhpZ2hjaGFydHMgdG8gdXNlJyxcclxuICAgICAgaGludDogYERlZmF1bHQ6ICR7ZGVmYXVsdENvbmZpZy5leHBvcnQuY29uc3RyLnZhbHVlfWAsXHJcbiAgICAgIGluaXRpYWw6IDAsXHJcbiAgICAgIGNob2ljZXM6IFsnY2hhcnQnLCAnc3RvY2tDaGFydCcsICdtYXBDaGFydCcsICdnYW50dENoYXJ0J11cclxuICAgIH0sXHJcbiAgICB7XHJcbiAgICAgIHR5cGU6ICdudW1iZXInLFxyXG4gICAgICBuYW1lOiAnZGVmYXVsdEhlaWdodCcsXHJcbiAgICAgIG1lc3NhZ2U6ICdUaGUgZGVmYXVsdCBmYWxsYmFjayBoZWlnaHQgb2YgdGhlIGV4cG9ydGVkIGNoYXJ0JyxcclxuICAgICAgaW5pdGlhbDogZGVmYXVsdENvbmZpZy5leHBvcnQuZGVmYXVsdEhlaWdodC52YWx1ZVxyXG4gICAgfSxcclxuICAgIHtcclxuICAgICAgdHlwZTogJ251bWJlcicsXHJcbiAgICAgIG5hbWU6ICdkZWZhdWx0V2lkdGgnLFxyXG4gICAgICBtZXNzYWdlOiAnVGhlIGRlZmF1bHQgZmFsbGJhY2sgd2lkdGggb2YgdGhlIGV4cG9ydGVkIGNoYXJ0JyxcclxuICAgICAgaW5pdGlhbDogZGVmYXVsdENvbmZpZy5leHBvcnQuZGVmYXVsdFdpZHRoLnZhbHVlXHJcbiAgICB9LFxyXG4gICAge1xyXG4gICAgICB0eXBlOiAnbnVtYmVyJyxcclxuICAgICAgbmFtZTogJ2RlZmF1bHRTY2FsZScsXHJcbiAgICAgIG1lc3NhZ2U6ICdUaGUgZGVmYXVsdCBmYWxsYmFjayBzY2FsZSBvZiB0aGUgZXhwb3J0ZWQgY2hhcnQnLFxyXG4gICAgICBpbml0aWFsOiBkZWZhdWx0Q29uZmlnLmV4cG9ydC5kZWZhdWx0U2NhbGUudmFsdWUsXHJcbiAgICAgIG1pbjogMC4xLFxyXG4gICAgICBtYXg6IDVcclxuICAgIH1cclxuICBdLFxyXG4gIGN1c3RvbUNvZGU6IFtcclxuICAgIHtcclxuICAgICAgdHlwZTogJ3RvZ2dsZScsXHJcbiAgICAgIG5hbWU6ICdhbGxvd0NvZGVFeGVjdXRpb24nLFxyXG4gICAgICBtZXNzYWdlOiAnQWxsb3cgdG8gZXhlY3V0ZSBjdXN0b20gY29kZScsXHJcbiAgICAgIGluaXRpYWw6IGRlZmF1bHRDb25maWcuY3VzdG9tQ29kZS5hbGxvd0NvZGVFeGVjdXRpb24udmFsdWVcclxuICAgIH0sXHJcbiAgICB7XHJcbiAgICAgIHR5cGU6ICd0b2dnbGUnLFxyXG4gICAgICBuYW1lOiAnYWxsb3dGaWxlUmVzb3VyY2VzJyxcclxuICAgICAgbWVzc2FnZTogJ0FsbG93IGZpbGUgcmVzb3VyY2VzJyxcclxuICAgICAgaW5pdGlhbDogZGVmYXVsdENvbmZpZy5jdXN0b21Db2RlLmFsbG93RmlsZVJlc291cmNlcy52YWx1ZVxyXG4gICAgfVxyXG4gIF0sXHJcbiAgc2VydmVyOiBbXHJcbiAgICB7XHJcbiAgICAgIHR5cGU6ICd0b2dnbGUnLFxyXG4gICAgICBuYW1lOiAnZW5hYmxlJyxcclxuICAgICAgbWVzc2FnZTogJ1N0YXJ0cyBhIHNlcnZlciBvbiAwLjAuMC4wJyxcclxuICAgICAgaW5pdGlhbDogZGVmYXVsdENvbmZpZy5zZXJ2ZXIuZW5hYmxlLnZhbHVlXHJcbiAgICB9LFxyXG4gICAge1xyXG4gICAgICB0eXBlOiAndGV4dCcsXHJcbiAgICAgIG5hbWU6ICdob3N0JyxcclxuICAgICAgbWVzc2FnZTogJ0EgaG9zdG5hbWUgb2YgYSBzZXJ2ZXInLFxyXG4gICAgICBpbml0aWFsOiBkZWZhdWx0Q29uZmlnLnNlcnZlci5ob3N0LnZhbHVlXHJcbiAgICB9LFxyXG4gICAge1xyXG4gICAgICB0eXBlOiAnbnVtYmVyJyxcclxuICAgICAgbmFtZTogJ3BvcnQnLFxyXG4gICAgICBtZXNzYWdlOiAnQSBwb3J0IG9mIGEgc2VydmVyJyxcclxuICAgICAgaW5pdGlhbDogZGVmYXVsdENvbmZpZy5zZXJ2ZXIucG9ydC52YWx1ZVxyXG4gICAgfSxcclxuICAgIHtcclxuICAgICAgdHlwZTogJ3RvZ2dsZScsXHJcbiAgICAgIG5hbWU6ICdzc2wuZW5hYmxlJyxcclxuICAgICAgbWVzc2FnZTogJ0VuYWJsZSBTU0wgcHJvdG9jb2wnLFxyXG4gICAgICBpbml0aWFsOiBkZWZhdWx0Q29uZmlnLnNlcnZlci5zc2wuZW5hYmxlLnZhbHVlXHJcbiAgICB9LFxyXG4gICAge1xyXG4gICAgICB0eXBlOiAndG9nZ2xlJyxcclxuICAgICAgbmFtZTogJ3NzbC5mb3JjZScsXHJcbiAgICAgIG1lc3NhZ2U6ICdGb3JjZSB0byBvbmx5IHNlcnZlIG92ZXIgSFRUUFMnLFxyXG4gICAgICBpbml0aWFsOiBkZWZhdWx0Q29uZmlnLnNlcnZlci5zc2wuZm9yY2UudmFsdWVcclxuICAgIH0sXHJcbiAgICB7XHJcbiAgICAgIHR5cGU6ICdudW1iZXInLFxyXG4gICAgICBuYW1lOiAnc3NsLnBvcnQnLFxyXG4gICAgICBtZXNzYWdlOiAnUG9ydCBvbiB3aGljaCB0byBydW4gdGhlIFNTTCBzZXJ2ZXInLFxyXG4gICAgICBpbml0aWFsOiBkZWZhdWx0Q29uZmlnLnNlcnZlci5zc2wucG9ydC52YWx1ZVxyXG4gICAgfSxcclxuICAgIHtcclxuICAgICAgdHlwZTogJ3RleHQnLFxyXG4gICAgICBuYW1lOiAnc3NsLmNlcnRQYXRoJyxcclxuICAgICAgbWVzc2FnZTogJ0EgcGF0aCB3aGVyZSB0byBmaW5kIHRoZSBTU0wgY2VydGlmaWNhdGUva2V5JyxcclxuICAgICAgaW5pdGlhbDogZGVmYXVsdENvbmZpZy5zZXJ2ZXIuc3NsLmNlcnRQYXRoLnZhbHVlXHJcbiAgICB9LFxyXG4gICAge1xyXG4gICAgICB0eXBlOiAndG9nZ2xlJyxcclxuICAgICAgbmFtZTogJ3JhdGVMaW1pdGluZy5lbmFibGUnLFxyXG4gICAgICBtZXNzYWdlOiAnRW5hYmxlIHJhdGUgbGltaXRpbmcnLFxyXG4gICAgICBpbml0aWFsOiBkZWZhdWx0Q29uZmlnLnNlcnZlci5yYXRlTGltaXRpbmcuZW5hYmxlLnZhbHVlXHJcbiAgICB9LFxyXG4gICAge1xyXG4gICAgICB0eXBlOiAnbnVtYmVyJyxcclxuICAgICAgbmFtZTogJ3JhdGVMaW1pdGluZy5tYXhSZXF1ZXN0cycsXHJcbiAgICAgIG1lc3NhZ2U6ICdNYXggcmVxdWVzdHMgYWxsb3dlZCBpbiBhIG9uZSBtaW51dGUnLFxyXG4gICAgICBpbml0aWFsOiBkZWZhdWx0Q29uZmlnLnNlcnZlci5yYXRlTGltaXRpbmcubWF4UmVxdWVzdHMudmFsdWVcclxuICAgIH0sXHJcbiAgICB7XHJcbiAgICAgIHR5cGU6ICdudW1iZXInLFxyXG4gICAgICBuYW1lOiAncmF0ZUxpbWl0aW5nLndpbmRvdycsXHJcbiAgICAgIG1lc3NhZ2U6ICdUaGUgdGltZSB3aW5kb3cgaW4gbWludXRlcyBmb3IgcmF0ZSBsaW1pdGluZycsXHJcbiAgICAgIGluaXRpYWw6IGRlZmF1bHRDb25maWcuc2VydmVyLnJhdGVMaW1pdGluZy53aW5kb3cudmFsdWVcclxuICAgIH0sXHJcbiAgICB7XHJcbiAgICAgIHR5cGU6ICdudW1iZXInLFxyXG4gICAgICBuYW1lOiAncmF0ZUxpbWl0aW5nLmRlbGF5JyxcclxuICAgICAgbWVzc2FnZTpcclxuICAgICAgICAnVGhlIGFtb3VudCB0byBkZWxheSBlYWNoIHN1Y2Nlc3NpdmUgcmVxdWVzdCBiZWZvcmUgaGl0dGluZyB0aGUgbWF4JyxcclxuICAgICAgaW5pdGlhbDogZGVmYXVsdENvbmZpZy5zZXJ2ZXIucmF0ZUxpbWl0aW5nLmRlbGF5LnZhbHVlXHJcbiAgICB9LFxyXG4gICAge1xyXG4gICAgICB0eXBlOiAndG9nZ2xlJyxcclxuICAgICAgbmFtZTogJ3JhdGVMaW1pdGluZy50cnVzdFByb3h5JyxcclxuICAgICAgbWVzc2FnZTogJ1NldCB0aGlzIHRvIHRydWUgaWYgYmVoaW5kIGEgbG9hZCBiYWxhbmNlcicsXHJcbiAgICAgIGluaXRpYWw6IGRlZmF1bHRDb25maWcuc2VydmVyLnJhdGVMaW1pdGluZy50cnVzdFByb3h5LnZhbHVlXHJcbiAgICB9LFxyXG4gICAge1xyXG4gICAgICB0eXBlOiAndGV4dCcsXHJcbiAgICAgIG5hbWU6ICdyYXRlTGltaXRpbmcuc2tpcEtleScsXHJcbiAgICAgIG1lc3NhZ2U6XHJcbiAgICAgICAgJ0FsbG93cyBieXBhc3NpbmcgdGhlIHJhdGUgbGltaXRlciBhbmQgc2hvdWxkIGJlIHByb3ZpZGVkIHdpdGggc2tpcFRva2VuIGFyZ3VtZW50JyxcclxuICAgICAgaW5pdGlhbDogZGVmYXVsdENvbmZpZy5zZXJ2ZXIucmF0ZUxpbWl0aW5nLnNraXBLZXkudmFsdWVcclxuICAgIH0sXHJcbiAgICB7XHJcbiAgICAgIHR5cGU6ICd0ZXh0JyxcclxuICAgICAgbmFtZTogJ3JhdGVMaW1pdGluZy5za2lwVG9rZW4nLFxyXG4gICAgICBtZXNzYWdlOlxyXG4gICAgICAgICdBbGxvd3MgYnlwYXNzaW5nIHRoZSByYXRlIGxpbWl0ZXIgYW5kIHNob3VsZCBiZSBwcm92aWRlZCB3aXRoIHNraXBLZXkgYXJndW1lbnQnLFxyXG4gICAgICBpbml0aWFsOiBkZWZhdWx0Q29uZmlnLnNlcnZlci5yYXRlTGltaXRpbmcuc2tpcFRva2VuLnZhbHVlXHJcbiAgICB9XHJcbiAgXSxcclxuICBwb29sOiBbXHJcbiAgICB7XHJcbiAgICAgIHR5cGU6ICdudW1iZXInLFxyXG4gICAgICBuYW1lOiAnbWluV29ya2VycycsXHJcbiAgICAgIG1lc3NhZ2U6ICdUaGUgbnVtYmVyIG9mIGluaXRpYWwgd29ya2VycyB0byBzcGF3bicsXHJcbiAgICAgIGluaXRpYWw6IGRlZmF1bHRDb25maWcucG9vbC5taW5Xb3JrZXJzLnZhbHVlXHJcbiAgICB9LFxyXG4gICAge1xyXG4gICAgICB0eXBlOiAnbnVtYmVyJyxcclxuICAgICAgbmFtZTogJ21heFdvcmtlcnMnLFxyXG4gICAgICBtZXNzYWdlOiAnVGhlIG51bWJlciBvZiBtYXggd29ya2VycyB0byBzcGF3bicsXHJcbiAgICAgIGluaXRpYWw6IGRlZmF1bHRDb25maWcucG9vbC5tYXhXb3JrZXJzLnZhbHVlXHJcbiAgICB9LFxyXG4gICAge1xyXG4gICAgICB0eXBlOiAnbnVtYmVyJyxcclxuICAgICAgbmFtZTogJ3dvcmtMaW1pdCcsXHJcbiAgICAgIG1lc3NhZ2U6XHJcbiAgICAgICAgJ1RoZSBwaWVjZXMgb2Ygd29yayB0aGF0IGNhbiBiZSBwZXJmb3JtZWQgYmVmb3JlIHJlc3RhcnRpbmcgYSBwdXBwZXRlZXIgcHJvY2VzcycsXHJcbiAgICAgIGluaXRpYWw6IGRlZmF1bHRDb25maWcucG9vbC53b3JrTGltaXQudmFsdWVcclxuICAgIH0sXHJcbiAgICB7XHJcbiAgICAgIHR5cGU6ICdudW1iZXInLFxyXG4gICAgICBuYW1lOiAnYWNxdWlyZVRpbWVvdXQnLFxyXG4gICAgICBtZXNzYWdlOiAnVGhlIG51bWJlciBvZiBtaWxsaXNlY29uZHMgdG8gd2FpdCBmb3IgYWNxdWlyaW5nIGEgcmVzb3VyY2UnLFxyXG4gICAgICBpbml0aWFsOiBkZWZhdWx0Q29uZmlnLnBvb2wuYWNxdWlyZVRpbWVvdXQudmFsdWVcclxuICAgIH0sXHJcbiAgICB7XHJcbiAgICAgIHR5cGU6ICdudW1iZXInLFxyXG4gICAgICBuYW1lOiAnY3JlYXRlVGltZW91dCcsXHJcbiAgICAgIG1lc3NhZ2U6ICdUaGUgbnVtYmVyIG9mIG1pbGxpc2Vjb25kcyB0byB3YWl0IGZvciBjcmVhdGluZyBhIHJlc291cmNlJyxcclxuICAgICAgaW5pdGlhbDogZGVmYXVsdENvbmZpZy5wb29sLmNyZWF0ZVRpbWVvdXQudmFsdWVcclxuICAgIH0sXHJcbiAgICB7XHJcbiAgICAgIHR5cGU6ICdudW1iZXInLFxyXG4gICAgICBuYW1lOiAnZGVzdHJveVRpbWVvdXQnLFxyXG4gICAgICBtZXNzYWdlOiAnVGhlIG51bWJlciBvZiBtaWxsaXNlY29uZHMgdG8gd2FpdCBmb3IgZGVzdHJveWluZyBhIHJlc291cmNlJyxcclxuICAgICAgaW5pdGlhbDogZGVmYXVsdENvbmZpZy5wb29sLmRlc3Ryb3lUaW1lb3V0LnZhbHVlXHJcbiAgICB9LFxyXG4gICAge1xyXG4gICAgICB0eXBlOiAnbnVtYmVyJyxcclxuICAgICAgbmFtZTogJ2lkbGVUaW1lb3V0JyxcclxuICAgICAgbWVzc2FnZTogJ1RoZSBudW1iZXIgb2YgbWlsbGlzZWNvbmRzIGFmdGVyIGFuIGlkbGUgcmVzb3VyY2UgaXMgZGVzdHJveWVkJyxcclxuICAgICAgaW5pdGlhbDogZGVmYXVsdENvbmZpZy5wb29sLmlkbGVUaW1lb3V0LnZhbHVlXHJcbiAgICB9LFxyXG4gICAge1xyXG4gICAgICB0eXBlOiAnbnVtYmVyJyxcclxuICAgICAgbmFtZTogJ3Jhc3Rlcml6YXRpb25UaW1lb3V0JyxcclxuICAgICAgbWVzc2FnZTogJ1RoZSBudW1iZXIgb2YgbWlsbGlzZWNvbmRzIHRvIHdhaXQgZm9yIHJlbmRlcmluZyBhIHdlYnBhZ2UnLFxyXG4gICAgICBpbml0aWFsOiBkZWZhdWx0Q29uZmlnLnBvb2wucmFzdGVyaXphdGlvblRpbWVvdXQudmFsdWVcclxuICAgIH0sXHJcbiAgICB7XHJcbiAgICAgIHR5cGU6ICdudW1iZXInLFxyXG4gICAgICBuYW1lOiAnY3JlYXRlUmV0cnlJbnRlcnZhbCcsXHJcbiAgICAgIG1lc3NhZ2U6XHJcbiAgICAgICAgJ1RoZSBudW1iZXIgb2YgbWlsbGlzZWNvbmRzIGFmdGVyIHRoZSBjcmVhdGUgcHJvY2VzcyBpcyByZXRyaWVkIGluIGNhc2Ugb2YgZmFpbCcsXHJcbiAgICAgIGluaXRpYWw6IGRlZmF1bHRDb25maWcucG9vbC5jcmVhdGVSZXRyeUludGVydmFsLnZhbHVlXHJcbiAgICB9LFxyXG4gICAge1xyXG4gICAgICB0eXBlOiAnbnVtYmVyJyxcclxuICAgICAgbmFtZTogJ3JlYXBlckludGVydmFsJyxcclxuICAgICAgbWVzc2FnZTpcclxuICAgICAgICAnVGhlIG51bWJlciBvZiBtaWxsaXNlY29uZHMgYWZ0ZXIgdGhlIGNoZWNrIGZvciBpZGxlIHJlc291cmNlcyB0byBkZXN0cm95IGlzIHRyaWdnZXJlZCcsXHJcbiAgICAgIGluaXRpYWw6IGRlZmF1bHRDb25maWcucG9vbC5yZWFwZXJJbnRlcnZhbC52YWx1ZVxyXG4gICAgfSxcclxuICAgIHtcclxuICAgICAgdHlwZTogJ3RvZ2dsZScsXHJcbiAgICAgIG5hbWU6ICdiZW5jaG1hcmtpbmcnLFxyXG4gICAgICBtZXNzYWdlOiAnU2V0IGJlbmNobWFya2luZycsXHJcbiAgICAgIGluaXRpYWw6IGRlZmF1bHRDb25maWcucG9vbC5iZW5jaG1hcmtpbmcudmFsdWVcclxuICAgIH0sXHJcbiAgICB7XHJcbiAgICAgIHR5cGU6ICd0b2dnbGUnLFxyXG4gICAgICBuYW1lOiAnbGlzdGVuVG9Qcm9jZXNzRXhpdHMnLFxyXG4gICAgICBtZXNzYWdlOiAnU2V0IHRvIGZhbHNlIGluIG9yZGVyIHRvIHNraXAgYXR0YWNoaW5nIHByb2Nlc3MuZXhpdCBoYW5kbGVycycsXHJcbiAgICAgIGluaXRpYWw6IGRlZmF1bHRDb25maWcucG9vbC5saXN0ZW5Ub1Byb2Nlc3NFeGl0cy52YWx1ZVxyXG4gICAgfVxyXG4gIF0sXHJcbiAgbG9nZ2luZzogW1xyXG4gICAge1xyXG4gICAgICB0eXBlOiAnbnVtYmVyJyxcclxuICAgICAgbmFtZTogJ2xldmVsJyxcclxuICAgICAgbWVzc2FnZTpcclxuICAgICAgICAnVGhlIGxvZyBsZXZlbCAoMDogc2lsZW50LCAxOiBlcnJvciwgMjogd2FybmluZywgMzogbm90aWNlLCA0OiB2ZXJib3NlKScsXHJcbiAgICAgIGluaXRpYWw6IGRlZmF1bHRDb25maWcubG9nZ2luZy5sZXZlbC52YWx1ZSxcclxuICAgICAgcm91bmQ6IDAsXHJcbiAgICAgIG1pbjogMCxcclxuICAgICAgbWF4OiA0XHJcbiAgICB9LFxyXG4gICAge1xyXG4gICAgICB0eXBlOiAndGV4dCcsXHJcbiAgICAgIG5hbWU6ICdmaWxlJyxcclxuICAgICAgbWVzc2FnZTpcclxuICAgICAgICAnQSBuYW1lIG9mIGEgbG9nIGZpbGUuIFRoZSAtLWxvZ0Rlc3QgYWxzbyBuZWVkcyB0byBiZSBzZXQgdG8gZW5hYmxlIGZpbGUgbG9nZ2luZycsXHJcbiAgICAgIGluaXRpYWw6IGRlZmF1bHRDb25maWcubG9nZ2luZy5maWxlLnZhbHVlXHJcbiAgICB9LFxyXG4gICAge1xyXG4gICAgICB0eXBlOiAndGV4dCcsXHJcbiAgICAgIG5hbWU6ICdkZXN0JyxcclxuICAgICAgbWVzc2FnZTogJ0EgcGF0aCB0byBsb2cgZmlsZXMuIEl0IGVuYWJsZXMgZmlsZSBsb2dnaW5nJyxcclxuICAgICAgaW5pdGlhbDogZGVmYXVsdENvbmZpZy5sb2dnaW5nLmRlc3QudmFsdWVcclxuICAgIH1cclxuICBdLFxyXG4gIHVpOiBbXHJcbiAgICB7XHJcbiAgICAgIHR5cGU6ICd0b2dnbGUnLFxyXG4gICAgICBuYW1lOiAnZW5hYmxlJyxcclxuICAgICAgbWVzc2FnZTogJ0VuYWJsZSBVSSBmb3IgdGhlIGV4cG9ydCBzZXJ2ZXInLFxyXG4gICAgICBpbml0aWFsOiBkZWZhdWx0Q29uZmlnLnVpLmVuYWJsZS52YWx1ZVxyXG4gICAgfSxcclxuICAgIHtcclxuICAgICAgdHlwZTogJ3RleHQnLFxyXG4gICAgICBuYW1lOiAncm91dGUnLFxyXG4gICAgICBtZXNzYWdlOiAnQSByb3V0ZSB0byBhdHRhY2ggdGhlIFVJIHRvJyxcclxuICAgICAgaW5pdGlhbDogZGVmYXVsdENvbmZpZy51aS5yb3V0ZS52YWx1ZVxyXG4gICAgfVxyXG4gIF0sXHJcbiAgb3RoZXI6IFtcclxuICAgIHtcclxuICAgICAgdHlwZTogJ3RvZ2dsZScsXHJcbiAgICAgIG5hbWU6ICdub0xvZ28nLFxyXG4gICAgICBtZXNzYWdlOlxyXG4gICAgICAgICdTa2lwIHByaW50aW5nIHRoZSBsb2dvIG9uIGEgc3RhcnR1cC4gV2lsbCBiZSByZXBsYWNlZCBieSBhIHNpbXBsZSB0ZXh0JyxcclxuICAgICAgaW5pdGlhbDogZGVmYXVsdENvbmZpZy5vdGhlci5ub0xvZ28udmFsdWVcclxuICAgIH1cclxuICBdXHJcbn07XHJcblxyXG4vLyBBYnNvbHV0ZSBwcm9wcyB0aGF0LCBpbiBjYXNlIG9mIG1lcmdpbmcgcmVjdXJzaXZlbHksIG5lZWQgdG8gYmUgZm9yY2UgbWVyZ2VkXHJcbmV4cG9ydCBjb25zdCBhYnNvbHV0ZVByb3BzID0gW1xyXG4gICdvcHRpb25zJyxcclxuICAnZ2xvYmFsT3B0aW9ucycsXHJcbiAgJ3RoZW1lT3B0aW9ucycsXHJcbiAgJ3Jlc291cmNlcycsXHJcbiAgJ3BheWxvYWQnXHJcbl07XHJcblxyXG4vLyBBcmd1bWVudCBuZXN0aW5nIGxldmVsIG9mIGFsbCBleHBvcnQgc2VydmVyIG9wdGlvbnNcclxuZXhwb3J0IGNvbnN0IG5lc3RlZEFyZ3MgPSB7fTtcclxuXHJcbi8qKlxyXG4gKiBDcmVhdGVzIG5lc3RlZCBhcmd1bWVudHMgY2hhaW4gZm9yIGFsbCBvcHRpb25zXHJcbiAqXHJcbiAqIEBwYXJhbSB7b2JqZWN0fSBvYmogLSBUaGUgb2JqZWN0IGJhc2VkIG9uIHdoaWNoIHRoZSBpbml0aWFsIGNvbmZpZ3VyYXRpb24gYmVcclxuICogbWFkZS5cclxuICogQHBhcmFtIHtzdHJpbmcgfSBwcm9wQ2hhaW4gLSBSZXF1aXJlZCBmb3IgY3JlYXRpbmcgYSBzdHJpbmcgY2hhaW4gb2ZcclxuICogcHJvcGVydGllcyBmb3IgbmVzdGVkIGFyZ3VtZW50cy5cclxuICovXHJcbmNvbnN0IGNyZWF0ZU5lc3RlZEFyZ3MgPSAob2JqLCBwcm9wQ2hhaW4gPSAnJykgPT4ge1xyXG4gIE9iamVjdC5rZXlzKG9iaikuZm9yRWFjaCgoaykgPT4ge1xyXG4gICAgaWYgKCFbJ3B1cHBldGVlcicsICdoaWdoY2hhcnRzJ10uaW5jbHVkZXMoaykpIHtcclxuICAgICAgY29uc3QgZW50cnkgPSBvYmpba107XHJcbiAgICAgIGlmICh0eXBlb2YgZW50cnkudmFsdWUgPT09ICd1bmRlZmluZWQnKSB7XHJcbiAgICAgICAgLy8gR28gZGVlcGVyIGluIHRoZSBuZXN0ZWQgYXJndW1lbnRzXHJcbiAgICAgICAgY3JlYXRlTmVzdGVkQXJncyhlbnRyeSwgYCR7cHJvcENoYWlufS4ke2t9YCk7XHJcbiAgICAgIH0gZWxzZSB7XHJcbiAgICAgICAgLy8gQ3JlYXRlIHRoZSBjaGFpbiBvZiBuZXN0ZWQgYXJndW1lbnRzXHJcbiAgICAgICAgbmVzdGVkQXJnc1tlbnRyeS5jbGlOYW1lIHx8IGtdID0gYCR7cHJvcENoYWlufS4ke2t9YC5zdWJzdHJpbmcoMSk7XHJcbiAgICAgIH1cclxuICAgIH1cclxuICB9KTtcclxufTtcclxuXHJcbmNyZWF0ZU5lc3RlZEFyZ3MoZGVmYXVsdENvbmZpZyk7XHJcbiIsIi8qKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqXHJcblxyXG5IaWdoY2hhcnRzIEV4cG9ydCBTZXJ2ZXJcclxuXHJcbkNvcHlyaWdodCAoYykgMjAxNi0yMDIzLCBIaWdoc29mdFxyXG5cclxuTGljZW5jZWQgdW5kZXIgdGhlIE1JVCBsaWNlbmNlLlxyXG5cclxuQWRkaXRpb25hbGx5IGEgdmFsaWQgSGlnaGNoYXJ0cyBsaWNlbnNlIGlzIHJlcXVpcmVkIGZvciB1c2UuXHJcblxyXG5TZWUgTElDRU5TRSBmaWxlIGluIHJvb3QgZm9yIGRldGFpbHMuXHJcblxyXG4qKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqL1xyXG5cclxuaW1wb3J0IHsgYXBwZW5kRmlsZSwgZXhpc3RzU3luYywgbWtkaXJTeW5jIH0gZnJvbSAnZnMnO1xyXG5cclxuaW1wb3J0IHsgZGVmYXVsdENvbmZpZyB9IGZyb20gJy4vc2NoZW1hcy9jb25maWcuanMnO1xyXG5cclxuLy8gVGhlIGRlZmF1bHQgbG9nZ2luZyBjb25maWdcclxubGV0IGxvZ2dpbmcgPSB7XHJcbiAgLy8gRmxhZ3MgZm9yIGxvZ2dpbmcgc3RhdHVzXHJcbiAgdG9Db25zb2xlOiB0cnVlLFxyXG4gIHRvRmlsZTogZmFsc2UsXHJcbiAgcGF0aENyZWF0ZWQ6IGZhbHNlLFxyXG4gIC8vIExvZyBsZXZlbHNcclxuICBsZXZlbHNEZXNjOiBbXHJcbiAgICB7XHJcbiAgICAgIHRpdGxlOiAnZXJyb3InLFxyXG4gICAgICBjb2xvcjogJ3JlZCdcclxuICAgIH0sXHJcbiAgICB7XHJcbiAgICAgIHRpdGxlOiAnd2FybmluZycsXHJcbiAgICAgIGNvbG9yOiAneWVsbG93J1xyXG4gICAgfSxcclxuICAgIHtcclxuICAgICAgdGl0bGU6ICdub3RpY2UnLFxyXG4gICAgICBjb2xvcjogJ2JsdWUnXHJcbiAgICB9LFxyXG4gICAge1xyXG4gICAgICB0aXRsZTogJ3ZlcmJvc2UnLFxyXG4gICAgICBjb2xvcjogJ2dyYXknXHJcbiAgICB9XHJcbiAgXSxcclxuICAvLyBMb2cgbGlzdGVuZXJzXHJcbiAgbGlzdGVuZXJzOiBbXVxyXG59O1xyXG5cclxuLy8gR2F0aGVyIGluaXQgbG9nZ2luZyBvcHRpb25zXHJcbmZvciAoY29uc3QgW2tleSwgb3B0aW9uXSBvZiBPYmplY3QuZW50cmllcyhkZWZhdWx0Q29uZmlnLmxvZ2dpbmcpKSB7XHJcbiAgbG9nZ2luZ1trZXldID0gb3B0aW9uLnZhbHVlO1xyXG59XHJcblxyXG4vKipcclxuICogTG9ncyBhIG1lc3NhZ2UuIEFjY2VwdHMgYSB2YXJpYWJsZSBhbW91bnQgb2YgYXJndW1lbnRzLiBBcmd1bWVudHMgYWZ0ZXJcclxuICogYGxldmVsYCB3aWxsIGJlIHBhc3NlZCBkaXJlY3RseSB0byBjb25zb2xlLmxvZywgYW5kL29yIHdpbGwgYmUgam9pbmVkXHJcbiAqIGFuZCBhcHBlbmRlZCB0byB0aGUgbG9nIGZpbGUuXHJcbiAqXHJcbiAqIEBwYXJhbSB7YW55fSBhcmdzIC0gQW4gYXJyYXkgb2YgYXJndW1lbnRzIHdoZXJlIHRoZSBmaXJzdCBpcyB0aGUgbG9nIGxldmVsXHJcbiAqIGFuZCB0aGUgcmVzdCBhcmUgc3RyaW5ncyB0byBidWlsZCBhIG1lc3NhZ2Ugd2l0aC5cclxuICovXHJcbmV4cG9ydCBjb25zdCBsb2cgPSAoLi4uYXJncykgPT4ge1xyXG4gIGNvbnN0IFtuZXdMZXZlbCwgLi4udGV4dHNdID0gYXJncztcclxuXHJcbiAgLy8gQ3VycmVudCBsb2dnaW5nIG9wdGlvbnNcclxuICBjb25zdCB7IGxldmVsLCBsZXZlbHNEZXNjIH0gPSBsb2dnaW5nO1xyXG5cclxuICAvLyBDaGVjayBpZiBsb2cgbGV2ZWwgaXMgd2l0aGluIGEgY29ycmVjdCByYW5nZVxyXG4gIGlmIChuZXdMZXZlbCA9PT0gMCB8fCBuZXdMZXZlbCA+IGxldmVsIHx8IGxldmVsID4gbGV2ZWxzRGVzYy5sZW5ndGgpIHtcclxuICAgIHJldHVybjtcclxuICB9XHJcblxyXG4gIC8vIEdldCByaWQgb2YgdGhlIEdNVCB0ZXh0IGluZm9ybWF0aW9uXHJcbiAgY29uc3QgbmV3RGF0ZSA9IG5ldyBEYXRlKCkudG9TdHJpbmcoKS5zcGxpdCgnKCcpWzBdLnRyaW0oKTtcclxuXHJcbiAgLy8gQ3JlYXRlIGEgbWVzc2FnZSdzIHByZWZpeFxyXG4gIGNvbnN0IHByZWZpeCA9IGAke25ld0RhdGV9IFske2xldmVsc0Rlc2NbbmV3TGV2ZWwgLSAxXS50aXRsZX1dIC1gO1xyXG5cclxuICAvLyBDYWxsIGF2YWlsYWJsZSBsb2cgbGlzdGVuZXJzXHJcbiAgbG9nZ2luZy5saXN0ZW5lcnMuZm9yRWFjaCgoZm4pID0+IHtcclxuICAgIGZuKHByZWZpeCwgdGV4dHMuam9pbignICcpKTtcclxuICB9KTtcclxuXHJcbiAgLy8gTG9nIHRvIGZpbGVcclxuICBpZiAobG9nZ2luZy50b0ZpbGUpIHtcclxuICAgIGlmICghbG9nZ2luZy5wYXRoQ3JlYXRlZCkge1xyXG4gICAgICAvLyBDcmVhdGUgaWYgZG9lcyBub3QgZXhpc3RcclxuICAgICAgIWV4aXN0c1N5bmMobG9nZ2luZy5kZXN0KSAmJiBta2RpclN5bmMobG9nZ2luZy5kZXN0KTtcclxuXHJcbiAgICAgIC8vIFdlIG5vdyBhc3N1bWUgdGhlIHBhdGggaXMgYXZhaWxhYmxlLCBlLmcuIGl0J3MgdGhlIHJlc3BvbnNpYmlsaXR5XHJcbiAgICAgIC8vIG9mIHRoZSB1c2VyIHRvIGNyZWF0ZSB0aGUgcGF0aCB3aXRoIHRoZSBjb3JyZWN0IGFjY2VzcyByaWdodHMuXHJcbiAgICAgIGxvZ2dpbmcucGF0aENyZWF0ZWQgPSB0cnVlO1xyXG4gICAgfVxyXG5cclxuICAgIC8vIEFkZCB0aGUgY29udGVudCB0byBhIGZpbGVcclxuICAgIGFwcGVuZEZpbGUoXHJcbiAgICAgIGAke2xvZ2dpbmcuZGVzdH0ke2xvZ2dpbmcuZmlsZX1gLFxyXG4gICAgICBbcHJlZml4XS5jb25jYXQodGV4dHMpLmpvaW4oJyAnKSArICdcXG4nLFxyXG4gICAgICAoZXJyb3IpID0+IHtcclxuICAgICAgICBpZiAoZXJyb3IpIHtcclxuICAgICAgICAgIGNvbnNvbGUubG9nKGBbbG9nZ2VyXSBVbmFibGUgdG8gd3JpdGUgdG8gbG9nIGZpbGU6ICR7ZXJyb3J9YCk7XHJcbiAgICAgICAgICBsb2dnaW5nLnRvRmlsZSA9IGZhbHNlO1xyXG4gICAgICAgIH1cclxuICAgICAgfVxyXG4gICAgKTtcclxuICB9XHJcblxyXG4gIC8vIExvZyB0byBjb25zb2xlXHJcbiAgaWYgKGxvZ2dpbmcudG9Db25zb2xlKSB7XHJcbiAgICBjb25zb2xlLmxvZy5hcHBseShcclxuICAgICAgdW5kZWZpbmVkLFxyXG4gICAgICBbcHJlZml4LnRvU3RyaW5nKClbbG9nZ2luZy5sZXZlbHNEZXNjW25ld0xldmVsIC0gMV0uY29sb3JdXS5jb25jYXQodGV4dHMpXHJcbiAgICApO1xyXG4gIH1cclxufTtcclxuXHJcbi8qKlxyXG4gKiBTZXRzIHRoZSBmaWxlIGxvZ2dpbmcgY29uZmlndXJhdGlvbi5cclxuICpcclxuICogQHBhcmFtIHtzdHJpbmd9IGxvZ0Rlc3QgLSBBIHBhdGggdG8gbG9nIHRvLlxyXG4gKiBAcGFyYW0ge3N0cmluZ30gbG9nRmlsZSAtIFRoZSBuYW1lIG9mIHRoZSBsb2cgZmlsZS5cclxuICovXHJcbmV4cG9ydCBjb25zdCBlbmFibGVGaWxlTG9nZ2luZyA9IChsb2dEZXN0LCBsb2dGaWxlKSA9PiB7XHJcbiAgLy8gVXBkYXRlIGxvZ2dpbmcgb3B0aW9uc1xyXG4gIGxvZ2dpbmcgPSB7XHJcbiAgICAuLi5sb2dnaW5nLFxyXG4gICAgZGVzdDogbG9nRGVzdCB8fCBsb2dnaW5nLmRlc3QsXHJcbiAgICBmaWxlOiBsb2dGaWxlIHx8IGxvZ2dpbmcuZmlsZSxcclxuICAgIHRvRmlsZTogdHJ1ZVxyXG4gIH07XHJcblxyXG4gIGlmIChsb2dnaW5nLmRlc3QubGVuZ3RoID09PSAwKSB7XHJcbiAgICByZXR1cm4gbG9nKDEsICdbbG9nZ2VyXSBGaWxlIGxvZ2dpbmcgaW5pdDogbm8gcGF0aCBzdXBwbGllZC4nKTtcclxuICB9XHJcblxyXG4gIGlmICghbG9nZ2luZy5kZXN0LmVuZHNXaXRoKCcvJykpIHtcclxuICAgIGxvZ2dpbmcuZGVzdCArPSAnLyc7XHJcbiAgfVxyXG59O1xyXG5cclxuLyoqXHJcbiAqIEFkZHMgYSBsb2cgbGlzdGVuZXIuXHJcbiAqXHJcbiAqIEBwYXJhbSB7ZnVuY3Rpb259IGZuIC0gVGhlIGZ1bmN0aW9uIHRvIGNhbGwgd2hlbiBnZXR0aW5nIGEgbG9nIGV2ZW50LlxyXG4gKi9cclxuZXhwb3J0IGNvbnN0IGxpc3RlbiA9IChmbikgPT4ge1xyXG4gIGxvZ2dpbmcubGlzdGVuZXJzLnB1c2goZm4pO1xyXG59O1xyXG5cclxuLyoqXHJcbiAqIFNldHMgdGhlIGN1cnJlbnQgbG9nIGxldmVsLiBMb2cgbGV2ZWxzIGFyZTpcclxuICogLSAwID0gbm8gbG9nZ2luZ1xyXG4gKiAtIDEgPSBlcnJvclxyXG4gKiAtIDIgPSB3YXJuaW5nXHJcbiAqIC0gMyA9IG5vdGljZVxyXG4gKiAtIDQgPSB2ZXJib3NlXHJcbiAqXHJcbiAqIEBwYXJhbSB7bnVtYmVyfSBuZXdMZXZlbCAtIFRoZSBuZXcgbG9nIGxldmVsICgwIC0gNCkuXHJcbiAqL1xyXG5leHBvcnQgY29uc3Qgc2V0TG9nTGV2ZWwgPSAobmV3TGV2ZWwpID0+IHtcclxuICBpZiAobmV3TGV2ZWwgPj0gMCAmJiBuZXdMZXZlbCA8PSBsb2dnaW5nLmxldmVsc0Rlc2MubGVuZ3RoKSB7XHJcbiAgICBsb2dnaW5nLmxldmVsID0gbmV3TGV2ZWw7XHJcbiAgfVxyXG59O1xyXG5cclxuLyoqXHJcbiAqIEVuYWJsZXMgb3IgZGlzYWJsZXMgbG9nZ2luZyB0byB0aGUgc3Rkb3V0LlxyXG4gKlxyXG4gKiBAcGFyYW0ge2Jvb2xlYW59IGVuYWJsZWQgLSBXaGV0aGVyIGxvZyB0byBjb25zb2xlIG9yIG5vdC5cclxuICovXHJcbmV4cG9ydCBjb25zdCB0b2dnbGVTVERPdXQgPSAoZW5hYmxlZCkgPT4ge1xyXG4gIGxvZ2dpbmcudG9Db25zb2xlID0gZW5hYmxlZDtcclxufTtcclxuXHJcbmV4cG9ydCBkZWZhdWx0IHtcclxuICBsb2csXHJcbiAgZW5hYmxlRmlsZUxvZ2dpbmcsXHJcbiAgbGlzdGVuLFxyXG4gIHNldExvZ0xldmVsLFxyXG4gIHRvZ2dsZVNURE91dFxyXG59O1xyXG4iLCIvKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKlxyXG5cclxuSGlnaGNoYXJ0cyBFeHBvcnQgU2VydmVyXHJcblxyXG5Db3B5cmlnaHQgKGMpIDIwMTYtMjAyMywgSGlnaHNvZnRcclxuXHJcbkxpY2VuY2VkIHVuZGVyIHRoZSBNSVQgbGljZW5jZS5cclxuXHJcbkFkZGl0aW9uYWxseSBhIHZhbGlkIEhpZ2hjaGFydHMgbGljZW5zZSBpcyByZXF1aXJlZCBmb3IgdXNlLlxyXG5cclxuU2VlIExJQ0VOU0UgZmlsZSBpbiByb290IGZvciBkZXRhaWxzLlxyXG5cclxuKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKi9cclxuXHJcbmltcG9ydCB7IHJlYWRGaWxlU3luYyB9IGZyb20gJ2ZzJztcclxuaW1wb3J0IHsgZmlsZVVSTFRvUGF0aCB9IGZyb20gJ3VybCc7XHJcblxyXG5pbXBvcnQgeyBkZWZhdWx0Q29uZmlnIH0gZnJvbSAnLi4vbGliL3NjaGVtYXMvY29uZmlnLmpzJztcclxuaW1wb3J0IHsgbG9nIH0gZnJvbSAnLi9sb2dnZXIuanMnO1xyXG5cclxuY29uc3QgTUFYX0JBQ0tPRkZfQVRURU1QVFMgPSA2O1xyXG5cclxuZXhwb3J0IGNvbnN0IF9fZGlybmFtZSA9IGZpbGVVUkxUb1BhdGgobmV3IFVSTCgnLi4vLicsIGltcG9ydC5tZXRhLnVybCkpO1xyXG5cclxuLyoqXHJcbiAqIENsZWFycyB0ZXh0IGZyb20gd2hpdGVzcGFjZXMgd2l0aCBhIHJlZ2V4IHJ1bGUuXHJcbiAqXHJcbiAqIEBwYXJhbSB7c3RyaW5nfSBydWxlIC0gVGhlIHJ1bGUgZm9yIGNsZWFyaW5nIGEgc3RyaW5nLCBkZWZhdWx0IHRvIC9cXHNcXHMrL2cuXHJcbiAqIEByZXR1cm4ge3N0cmluZ30gLSBDbGVhcmVkIHRleHQuXHJcbiAqL1xyXG5leHBvcnQgY29uc3QgY2xlYXJUZXh0ID0gKHRleHQsIHJ1bGUgPSAvXFxzXFxzKy9nLCByZXBsYWNlciA9ICcgJykgPT5cclxuICB0ZXh0LnJlcGxhY2VBbGwocnVsZSwgcmVwbGFjZXIpLnRyaW0oKTtcclxuXHJcbi8qKlxyXG4gKiBEZWxheXMgY2FsbGluZyB0aGUgZnVuY3Rpb24gYnkgdGltZSBjYWxjdWxhdGVkIGJhc2VkIG9uIHRoZSBiYWNrb2ZmXHJcbiAqIGFsZ29yaXRobS5cclxuICpcclxuICogQHBhcmFtIHtmdW5jdGlvbn0gZm4gLSBBIGZ1bmN0aW9uIHRvIHRyeSB0byBjYWxsIHdpdGggdGhlIGJhY2tvZmYgYWxnb3JpdGhtXHJcbiAqIG9uLlxyXG4gKiBAcGFyYW0ge251bWJlcn0gYXR0ZW1wdCAtIFRoZSBudW1iZXIgb2YgYW4gYXR0ZW1wdCwgd2hlcmUgdGhlIGZpcnN0IG9uZSBpcyAwLlxyXG4gKi9cclxuZXhwb3J0IGNvbnN0IGV4cEJhY2tvZmYgPSBhc3luYyAoZm4sIGF0dGVtcHQgPSAwLCAuLi5hcmdzKSA9PiB7XHJcbiAgdHJ5IHtcclxuICAgIC8vIFRyeSB0byBjYWxsIHRoZSBmdW5jdGlvblxyXG4gICAgcmV0dXJuIGF3YWl0IGZuKC4uLmFyZ3MpO1xyXG4gIH0gY2F0Y2ggKGVycm9yKSB7XHJcbiAgICAvLyBDYWxjdWxhdGUgZGVsYXkgaW4gbXNcclxuICAgIGNvbnN0IGRlbGF5SW5NcyA9IDIgKiogYXR0ZW1wdCAqIDEwMDA7XHJcblxyXG4gICAgLy8gSWYgdGhlIGF0dGVtcHQgZXhjZWVkcyB0aGUgbWF4aW11bSBhdHRlbXB0cyBvZiByZWFwZWF0LCB0aHJvdyBhbiBlcnJvclxyXG4gICAgaWYgKCsrYXR0ZW1wdCA+PSBNQVhfQkFDS09GRl9BVFRFTVBUUykge1xyXG4gICAgICB0aHJvdyBlcnJvcjtcclxuICAgIH1cclxuXHJcbiAgICAvLyBXYWl0IGdpdmVuIGFtb3VudCBvZiB0aW1lXHJcbiAgICBhd2FpdCBuZXcgUHJvbWlzZSgocmVzcG9uc2UpID0+IHNldFRpbWVvdXQocmVzcG9uc2UsIGRlbGF5SW5NcykpO1xyXG4gICAgbG9nKFxyXG4gICAgICAzLFxyXG4gICAgICBgW3Bvb2xdIFdhaXRlZCAke2RlbGF5SW5Nc31tcyB1bnRpbCBuZXh0IGNhbGwgZm9yIHRoZSByZXNvdXJjZSBpZDogJHthcmdzWzBdfS5gXHJcbiAgICApO1xyXG5cclxuICAgIC8vIFRyeSBhZ2FpblxyXG4gICAgcmV0dXJuIGV4cEJhY2tvZmYoZm4sIGF0dGVtcHQsIC4uLmFyZ3MpO1xyXG4gIH1cclxufTtcclxuXHJcbi8qKlxyXG4gKiBGaXhlcyB0byBzdXBwb3J0ZWQgdHlwZSBmb3JtYXQgaWYgTUlNRS5cclxuICpcclxuICogQHBhcmFtIHtzdHJpbmd9IHR5cGUgLSBUeXBlIHRvIGJlIGNvcnJlY3RlZC5cclxuICogQHBhcmFtIHtzdHJpbmd9IG91dGZpbGUgLSBOYW1lIG9mIHRoZSBvdXRmaWxlLlxyXG4gKi9cclxuZXhwb3J0IGNvbnN0IGZpeFR5cGUgPSAodHlwZSwgb3V0ZmlsZSkgPT4ge1xyXG4gIC8vIE1JTUUgdHlwZXNcclxuICBjb25zdCBtaW1lVHlwZXMgPSB7XHJcbiAgICAnaW1hZ2UvcG5nJzogJ3BuZycsXHJcbiAgICAnaW1hZ2UvanBlZyc6ICdqcGVnJyxcclxuICAgICdhcHBsaWNhdGlvbi9wZGYnOiAncGRmJyxcclxuICAgICdpbWFnZS9zdmcreG1sJzogJ3N2ZydcclxuICB9O1xyXG5cclxuICAvLyBGb3JtYXRzXHJcbiAgY29uc3QgZm9ybWF0cyA9IFsncG5nJywgJ2pwZWcnLCAncGRmJywgJ3N2ZyddO1xyXG5cclxuICAvLyBDaGVjayBpZiB0eXBlIGFuZCBvdXRmaWxlJ3MgZXh0ZW5zaW9ucyBhcmUgdGhlIHNhbWVcclxuICBpZiAob3V0ZmlsZSkge1xyXG4gICAgY29uc3Qgb3V0VHlwZSA9IG91dGZpbGUuc3BsaXQoJy4nKS5wb3AoKTtcclxuXHJcbiAgICAvLyBDaGVjayBpZiBleHRlbnNpb24gaGFzIGEgY29ycmVjdCB0eXBlXHJcbiAgICBpZiAoZm9ybWF0cy5pbmNsdWRlcyhvdXRUeXBlKSAmJiB0eXBlICE9PSBvdXRUeXBlKSB7XHJcbiAgICAgIHR5cGUgPSBvdXRUeXBlO1xyXG4gICAgfVxyXG4gIH1cclxuXHJcbiAgLy8gUmV0dXJuIGEgY29ycmVjdCB0eXBlXHJcbiAgcmV0dXJuIG1pbWVUeXBlc1t0eXBlXSB8fCBmb3JtYXRzLmZpbmQoKHQpID0+IHQgPT09IHR5cGUpIHx8ICdwbmcnO1xyXG59O1xyXG5cclxuLyoqXHJcbiAqIEhhbmRsZXMgdGhlIHByb3ZpZGVkIHJlc291cmNlcy5cclxuICpcclxuICogQHBhcmFtIHtzdHJpbmd9IHJlc291cmNlcyAtIFRoZSBzdHJpbmdpZmllZCByZXNvdXJjZXMuXHJcbiAqIEBwYXJhbSB7c3RyaW5nfSBhbGxvd0ZpbGVSZXNvdXJjZXMgLSBEZWNpZGUgaWYgcmVzb3VyY2VzIGZyb20gZmlsZSBhcmVcclxuICogYWxsb3dlZC5cclxuICovXHJcbmV4cG9ydCBjb25zdCBoYW5kbGVSZXNvdXJjZXMgPSAocmVzb3VyY2VzID0gZmFsc2UsIGFsbG93RmlsZVJlc291cmNlcykgPT4ge1xyXG4gIGNvbnN0IGFsbG93ZWRQcm9wcyA9IFsnanMnLCAnY3NzJywgJ2ZpbGVzJ107XHJcblxyXG4gIGxldCBoYW5kbGVkUmVzb3VyY2VzID0gcmVzb3VyY2VzO1xyXG4gIGxldCBjb3JyZWN0UmVzb3VyY2VzID0gZmFsc2U7XHJcblxyXG4gIC8vIFRyeSB0byBsb2FkIHJlc291cmNlcyBmcm9tIGEgZmlsZVxyXG4gIGlmIChhbGxvd0ZpbGVSZXNvdXJjZXMgJiYgcmVzb3VyY2VzLmVuZHNXaXRoKCcuanNvbicpKSB7XHJcbiAgICB0cnkge1xyXG4gICAgICBpZiAoIXJlc291cmNlcykge1xyXG4gICAgICAgIGhhbmRsZWRSZXNvdXJjZXMgPSBpc0NvcnJlY3RKU09OKFxyXG4gICAgICAgICAgcmVhZEZpbGVTeW5jKCdyZXNvdXJjZXMuanNvbicsICd1dGY4JylcclxuICAgICAgICApO1xyXG4gICAgICB9IGVsc2UgaWYgKHJlc291cmNlcyAmJiByZXNvdXJjZXMuZW5kc1dpdGgoJy5qc29uJykpIHtcclxuICAgICAgICBoYW5kbGVkUmVzb3VyY2VzID0gaXNDb3JyZWN0SlNPTihyZWFkRmlsZVN5bmMocmVzb3VyY2VzLCAndXRmOCcpKTtcclxuICAgICAgfSBlbHNlIHtcclxuICAgICAgICBoYW5kbGVkUmVzb3VyY2VzID0gaXNDb3JyZWN0SlNPTihyZXNvdXJjZXMpO1xyXG4gICAgICAgIGlmIChoYW5kbGVkUmVzb3VyY2VzID09PSB0cnVlKSB7XHJcbiAgICAgICAgICBoYW5kbGVkUmVzb3VyY2VzID0gaXNDb3JyZWN0SlNPTihcclxuICAgICAgICAgICAgcmVhZEZpbGVTeW5jKCdyZXNvdXJjZXMuanNvbicsICd1dGY4JylcclxuICAgICAgICAgICk7XHJcbiAgICAgICAgfVxyXG4gICAgICB9XHJcbiAgICB9IGNhdGNoIChub3RpY2UpIHtcclxuICAgICAgcmV0dXJuIGxvZygzLCBgW2NsaV0gTm8gcmVzb3VyY2VzIGZvdW5kLmApO1xyXG4gICAgfVxyXG4gIH0gZWxzZSB7XHJcbiAgICAvLyBUcnkgdG8gZ2V0IEpTT05cclxuICAgIGhhbmRsZWRSZXNvdXJjZXMgPSBpc0NvcnJlY3RKU09OKHJlc291cmNlcyk7XHJcblxyXG4gICAgLy8gR2V0IHJpZCBvZiB0aGUgZmlsZXMgc2VjdGlvblxyXG4gICAgaWYgKCFhbGxvd0ZpbGVSZXNvdXJjZXMpIHtcclxuICAgICAgZGVsZXRlIGhhbmRsZWRSZXNvdXJjZXMuZmlsZXM7XHJcbiAgICB9XHJcbiAgfVxyXG5cclxuICAvLyBGaWx0ZXIgZnJvbSB1bm5lY2Vzc2FyeSBwcm9wZXJ0aWVzXHJcbiAgZm9yIChjb25zdCBwcm9wTmFtZSBpbiBoYW5kbGVkUmVzb3VyY2VzKSB7XHJcbiAgICBpZiAoIWFsbG93ZWRQcm9wcy5pbmNsdWRlcyhwcm9wTmFtZSkpIHtcclxuICAgICAgZGVsZXRlIGhhbmRsZWRSZXNvdXJjZXNbcHJvcE5hbWVdO1xyXG4gICAgfSBlbHNlIGlmICghY29ycmVjdFJlc291cmNlcykge1xyXG4gICAgICBjb3JyZWN0UmVzb3VyY2VzID0gdHJ1ZTtcclxuICAgIH1cclxuICB9XHJcblxyXG4gIC8vIENoZWNrIGlmIGF0IGxlYXN0IG9uZSBvZiBhbGxvd2VkIHByb3BlcnRpZXMgaXMgcHJlc2VudFxyXG4gIGlmICghY29ycmVjdFJlc291cmNlcykge1xyXG4gICAgcmV0dXJuIGxvZygzLCBgW2NsaV0gTm8gcmVzb3VyY2VzIGZvdW5kLmApO1xyXG4gIH1cclxuXHJcbiAgLy8gSGFuZGxlIGZpbGVzIHNlY3Rpb25cclxuICBpZiAoaGFuZGxlZFJlc291cmNlcy5maWxlcykge1xyXG4gICAgaGFuZGxlZFJlc291cmNlcy5maWxlcyA9IGhhbmRsZWRSZXNvdXJjZXMuZmlsZXMubWFwKChpdGVtKSA9PiBpdGVtLnRyaW0oKSk7XHJcbiAgICBpZiAoIWhhbmRsZWRSZXNvdXJjZXMuZmlsZXMgfHwgaGFuZGxlZFJlc291cmNlcy5maWxlcy5sZW5ndGggPD0gMCkge1xyXG4gICAgICBkZWxldGUgaGFuZGxlZFJlc291cmNlcy5maWxlcztcclxuICAgIH1cclxuICB9XHJcblxyXG4gIC8vIFJldHVybiByZXNvdXJjZXNcclxuICByZXR1cm4gaGFuZGxlZFJlc291cmNlcztcclxufTtcclxuXHJcbi8qKlxyXG4gKiBDaGVja3MgaWYgcHJvdmlkZWQgZGF0YSBpcyBvciBjYW4gYmUgYSBjb3JyZWN0IEpTT04uXHJcbiAqXHJcbiAqIEBwYXJhbSB7YW55fSBkYXRhIC0gRGF0YSB0byBiZSBjaGVja2VkLlxyXG4gKiBAcGFyYW0ge2Jvb2xlYW59IHRvU3RyaW5nIC0gSWYgdHJ1ZSwgcmV0dXJuIHN0cmluZ2lmaWVkIHJlcHJlc2VudGF0aW9uLlxyXG4gKi9cclxuZXhwb3J0IGZ1bmN0aW9uIGlzQ29ycmVjdEpTT04oZGF0YSwgdG9TdHJpbmcpIHtcclxuICB0cnkge1xyXG4gICAgLy8gR2V0IHRoZSBzdHJpbmcgcmVwcmVzZW50YXRpb24gaWYgbm90IGFscmVhZHkgYmVmb3JlIHBhcnNpbmdcclxuICAgIGNvbnN0IHBhcnNlZERhdGEgPSBKU09OLnBhcnNlKFxyXG4gICAgICB0eXBlb2YgZGF0YSAhPT0gJ3N0cmluZycgPyBKU09OLnN0cmluZ2lmeShkYXRhKSA6IGRhdGFcclxuICAgICk7XHJcblxyXG4gICAgLy8gUmV0dXJuIGEgc3RyaW5naWZpZWQgcmVwcmVzZW50YXRpb24gb2YgYSBKU09OIGlmIHJlcXVpcmVkXHJcbiAgICBpZiAodHlwZW9mIHBhcnNlZERhdGEgIT09ICdzdHJpbmcnICYmIHRvU3RyaW5nKSB7XHJcbiAgICAgIHJldHVybiBKU09OLnN0cmluZ2lmeShwYXJzZWREYXRhKTtcclxuICAgIH1cclxuXHJcbiAgICAvLyBSZXR1cm4gYSBKU09OXHJcbiAgICByZXR1cm4gcGFyc2VkRGF0YTtcclxuICB9IGNhdGNoIChlcnJvcikge1xyXG4gICAgcmV0dXJuIGZhbHNlO1xyXG4gIH1cclxufVxyXG5cclxuLyoqXHJcbiAqIENoZWNrcyBpZiBpdGVtIGlzIGFuIG9iamVjdC5cclxuICpcclxuICogQHBhcmFtIHthbnl9IGl0ZW0gLSBJdGVtIHRvIGJlIGNoZWNrZWQuXHJcbiAqL1xyXG5leHBvcnQgY29uc3QgaXNPYmplY3QgPSAoaXRlbSkgPT5cclxuICB0eXBlb2YgaXRlbSA9PT0gJ29iamVjdCcgJiYgIUFycmF5LmlzQXJyYXkoaXRlbSkgJiYgaXRlbSAhPT0gbnVsbDtcclxuXHJcbi8qKlxyXG4gKiBDaGVja3MgaWYgc3RyaW5nIGNvbnRhaW5zIHByaXZhdGUgcmFuZ2UgdXJscy5cclxuICpcclxuICogQGV4cG9ydCB1dGlsc1xyXG4gKiBAcGFyYW0gaXRlbSB7c3RyaW5nfSBpdGVtIHRvIGJlIGNoZWNrZWRcclxuICovXHJcbmV4cG9ydCBjb25zdCBpc1ByaXZhdGVSYW5nZVVybEZvdW5kID0gKGl0ZW0pID0+IHtcclxuICByZXR1cm4gW1xyXG4gICAgJ2xvY2FsaG9zdCcsXHJcbiAgICAnKDEwKS4oLiopLiguKikuKC4qKScsXHJcbiAgICAnKDEyNykuKC4qKS4oLiopLiguKiknLFxyXG4gICAgJygxNzIpLigxWzYtOV18MlswLTldfDNbMC0xXSkuKC4qKS4oLiopJyxcclxuICAgICcoMTkyKS4oMTY4KS4oLiopLiguKiknXHJcbiAgXS5zb21lKChpcFJlZ0V4KSA9PlxyXG4gICAgaXRlbS5tYXRjaChgeGxpbms6aHJlZj1cIig/OihodHRwOi8vfGh0dHBzOi8vKSk/JHtpcFJlZ0V4fWApXHJcbiAgKTtcclxufTtcclxuXHJcbi8qKlxyXG4gKiBDcmVhdGVzIGFuZCByZXR1cm5zIGEgZGVlcCBjb3B5IG9mIHRoZSBnaXZlbiBvYmplY3QuXHJcbiAqXHJcbiAqIEBwYXJhbSB7b2JqZWN0fSBvYmplY3QgLSBPYmplY3QgdG8gY29weS5cclxuICogQHJldHVybiB7b2JqZWN0fSAtIERlZXAgY29weSBvZiB0aGUgb2JqZWN0LlxyXG4gKi9cclxuZXhwb3J0IGNvbnN0IGRlZXBDb3B5ID0gKG9iaikgPT4ge1xyXG4gIGlmIChvYmogPT09IG51bGwgfHwgdHlwZW9mIG9iaiAhPT0gJ29iamVjdCcpIHtcclxuICAgIHJldHVybiBvYmo7XHJcbiAgfVxyXG5cclxuICBjb25zdCBjb3B5ID0gQXJyYXkuaXNBcnJheShvYmopID8gW10gOiB7fTtcclxuXHJcbiAgZm9yIChjb25zdCBrZXkgaW4gb2JqKSB7XHJcbiAgICBpZiAoT2JqZWN0LnByb3RvdHlwZS5oYXNPd25Qcm9wZXJ0eS5jYWxsKG9iaiwga2V5KSkge1xyXG4gICAgICBjb3B5W2tleV0gPSBkZWVwQ29weShvYmpba2V5XSk7XHJcbiAgICB9XHJcbiAgfVxyXG5cclxuICByZXR1cm4gY29weTtcclxufTtcclxuXHJcbi8qKlxyXG4gKiBTdHJpbmdpZmllcyBvYmplY3Qgd2l0aCBvcHRpb25zLiBQb3NzaWJsZSB0byBwcmVzZXJ2ZSBmdW5jdGlvbnMgd2l0aFxyXG4gKiBhbGxvd0Z1bmN0aW9ucyBmbGFnLlxyXG4gKlxyXG4gKiBAcGFyYW0ge29iamVjdH0gb3B0aW9ucyAtIE9wdGlvbnMgdG8gc3RyaW5naWZ5LlxyXG4gKiBAcGFyYW0ge2Jvb2xlYW59IGFsbG93RnVuY3Rpb25zIC0gRmxhZyBmb3Iga2VlcGluZyBmdW5jdGlvbnMuXHJcbiAqL1xyXG5leHBvcnQgY29uc3Qgb3B0aW9uc1N0cmluZ2lmeSA9IChvcHRpb25zLCBhbGxvd0Z1bmN0aW9ucykgPT4ge1xyXG4gIGNvbnN0IHJlcGxhY2VyQ2FsbGJhY2sgPSAobmFtZSwgdmFsdWUpID0+IHtcclxuICAgIGlmICh0eXBlb2YgdmFsdWUgPT09ICdzdHJpbmcnKSB7XHJcbiAgICAgIHZhbHVlID0gdmFsdWUudHJpbSgpO1xyXG5cclxuICAgICAgLy8gSWYgYWxsb3dGdW5jdGlvbnMgaXMgc2V0IHRvIHRydWUsIHByZXNlcnZlIGZ1bmN0aW9uc1xyXG4gICAgICBpZiAoXHJcbiAgICAgICAgKHZhbHVlLnN0YXJ0c1dpdGgoJ2Z1bmN0aW9uKCcpIHx8IHZhbHVlLnN0YXJ0c1dpdGgoJ2Z1bmN0aW9uICgnKSkgJiZcclxuICAgICAgICB2YWx1ZS5lbmRzV2l0aCgnfScpXHJcbiAgICAgICkge1xyXG4gICAgICAgIHZhbHVlID0gYWxsb3dGdW5jdGlvbnNcclxuICAgICAgICAgID8gYEVYUF9GVU4keyh2YWx1ZSArICcnKS5yZXBsYWNlQWxsKC9cXG58XFx0fFxcci9nLCAnICcpfUVYUF9GVU5gXHJcbiAgICAgICAgICA6IHVuZGVmaW5lZDtcclxuICAgICAgfVxyXG4gICAgfVxyXG5cclxuICAgIHJldHVybiB0eXBlb2YgdmFsdWUgPT09ICdmdW5jdGlvbidcclxuICAgICAgPyBgRVhQX0ZVTiR7KHZhbHVlICsgJycpLnJlcGxhY2VBbGwoL1xcbnxcXHR8XFxyL2csICcgJyl9RVhQX0ZVTmBcclxuICAgICAgOiB2YWx1ZTtcclxuICB9O1xyXG5cclxuICAvLyBTdHJpbmdpZnkgb3B0aW9ucyBhbmQgaWYgcmVxdWlyZWQsIHJlcGxhY2Ugc3BlY2lhbCBmdW5jdGlvbnMgbWFya3NcclxuICByZXR1cm4gSlNPTi5zdHJpbmdpZnkob3B0aW9ucywgcmVwbGFjZXJDYWxsYmFjaykucmVwbGFjZUFsbChcclxuICAgIC9cIkVYUF9GVU58RVhQX0ZVTlwiL2csXHJcbiAgICAnJ1xyXG4gICk7XHJcbn07XHJcblxyXG4vKipcclxuICogUHJpbnRzIHRoZSBleHBvcnQgc2VydmVyIGxvZ28uXHJcbiAqXHJcbiAqIEBwYXJhbSB7Ym9vbGVhbn0gbm9Mb2dvIC0gV2hldGhlciB0byBkaXNwbGF5IGxvZ28gb3IgdGV4dC5cclxuICovXHJcbmV4cG9ydCBjb25zdCBwcmludExvZ28gPSAobm9Mb2dvKSA9PiB7XHJcbiAgLy8gR2V0IHBhY2thZ2UgdmVyc2lvbiBlaXRoZXIgZnJvbSBlbnYgb3IgZnJvbSBwYWNrYWdlLmpzb25cclxuICBjb25zdCBwYWNrYWdlVmVyc2lvbiA9XHJcbiAgICBwcm9jZXNzLmVudi5ucG1fcGFja2FnZV92ZXJzaW9uIHx8XHJcbiAgICBKU09OLnBhcnNlKHJlYWRGaWxlU3luYyhuZXcgVVJMKCcuLi9wYWNrYWdlLmpzb24nLCBpbXBvcnQubWV0YS51cmwpKSlcclxuICAgICAgLnZlcnNpb247XHJcblxyXG4gIC8vIFByaW50IHRleHQgb25seVxyXG4gIGlmIChub0xvZ28pIHtcclxuICAgIGNvbnNvbGUubG9nKGBTdGFydGluZyBoaWdoY2hhcnRzIGV4cG9ydCBzZXJ2ZXIgdiR7cGFja2FnZVZlcnNpb259Li4uYCk7XHJcbiAgICByZXR1cm47XHJcbiAgfVxyXG5cclxuICAvLyBQcmludCB0aGUgbG9nb1xyXG4gIGNvbnNvbGUubG9nKFxyXG4gICAgcmVhZEZpbGVTeW5jKF9fZGlybmFtZSArICcvbXNnL3N0YXJ0dXAubXNnJykudG9TdHJpbmcoKS5ib2xkLnllbGxvdyxcclxuICAgIGB2JHtwYWNrYWdlVmVyc2lvbn1gXHJcbiAgKTtcclxufTtcclxuXHJcbi8qKlxyXG4gKiBQcmludHMgdGhlIENMSSB1c2FnZS4gSWYgcmVxdWlyZWQsIGl0IGNhbiBsaXN0IHByb3BlcnRpZXMgcmVjdXJzaXZlbHlcclxuICovXHJcbmV4cG9ydCBmdW5jdGlvbiBwcmludFVzYWdlKCkge1xyXG4gIGNvbnN0IHBhZCA9IDQ4O1xyXG4gIGNvbnN0IHJlYWRtZSA9ICdodHRwczovL2dpdGh1Yi5jb20vaGlnaGNoYXJ0cy9ub2RlLWV4cG9ydC1zZXJ2ZXIjcmVhZG1lJztcclxuXHJcbiAgLy8gRGlzcGxheSByZWFkbWUgaW5mb3JtYXRpb25cclxuICBjb25zb2xlLmxvZyhcclxuICAgICdVc2FnZSBvZiBDTEkgYXJndW1lbnRzOicuYm9sZCxcclxuICAgICdcXG4tLS0tLS0nLFxyXG4gICAgYFxcbkZvciBtb3JlIGRldGFpbGVkIGluZm9ybWF0aW9uIHZpc2l0IHJlYWRtZSBhdDogJHtyZWFkbWUuYm9sZC55ZWxsb3d9LmBcclxuICApO1xyXG5cclxuICBjb25zdCBjeWNsZUNhdGVnb3JpZXMgPSAoY2F0ZWdvcmllcykgPT4ge1xyXG4gICAgZm9yIChjb25zdCBbbmFtZSwgb3B0aW9uXSBvZiBPYmplY3QuZW50cmllcyhjYXRlZ29yaWVzKSkge1xyXG4gICAgICAvLyBJZiBjYXRlZ29yeSBoYXMgbW9yZSBsZXZlbHMsIGdvIGZ1cnRoZXJcclxuICAgICAgaWYgKCFPYmplY3QucHJvdG90eXBlLmhhc093blByb3BlcnR5LmNhbGwob3B0aW9uLCAndmFsdWUnKSkge1xyXG4gICAgICAgIGN5Y2xlQ2F0ZWdvcmllcyhvcHRpb24pO1xyXG4gICAgICB9IGVsc2Uge1xyXG4gICAgICAgIGxldCBkZXNjTmFtZSA9IGAgIC0tJHtvcHRpb24uY2xpTmFtZSB8fCBuYW1lfSAke1xyXG4gICAgICAgICAgKCc8JyArIG9wdGlvbi50eXBlICsgJz4nKS5ncmVlblxyXG4gICAgICAgIH0gYDtcclxuICAgICAgICBpZiAoZGVzY05hbWUubGVuZ3RoIDwgcGFkKSB7XHJcbiAgICAgICAgICBmb3IgKGxldCBpID0gZGVzY05hbWUubGVuZ3RoOyBpIDwgcGFkOyBpKyspIHtcclxuICAgICAgICAgICAgZGVzY05hbWUgKz0gJy4nO1xyXG4gICAgICAgICAgfVxyXG4gICAgICAgIH1cclxuXHJcbiAgICAgICAgLy8gRGlzcGxheSBjb3JyZWN0bHkgYWxpZ25lZCBtZXNzYWdlc1xyXG4gICAgICAgIGNvbnNvbGUubG9nKFxyXG4gICAgICAgICAgZGVzY05hbWUsXHJcbiAgICAgICAgICBvcHRpb24uZGVzY3JpcHRpb24sXHJcbiAgICAgICAgICBgW0RlZmF1bHQ6ICR7b3B0aW9uLnZhbHVlLnRvU3RyaW5nKCkuYm9sZH1dYC5ibHVlXHJcbiAgICAgICAgKTtcclxuICAgICAgfVxyXG4gICAgfVxyXG4gIH07XHJcblxyXG4gIC8vIEN5Y2xlIHRocm91Z2ggb3B0aW9ucyBvZiBlYWNoIGNhdGVnb3JpZXMgYW5kIGRpc3BsYXkgdGhlIHVzYWdlIGluZm9cclxuICBPYmplY3Qua2V5cyhkZWZhdWx0Q29uZmlnKS5mb3JFYWNoKChjYXRlZ29yeSkgPT4ge1xyXG4gICAgLy8gT25seSBwdXBwZXRlZXIgYW5kIGhpZ2hjaGFydHMgY2F0ZWdvcmllcyBjYW5ub3QgYmUgY29uZmlndXJlZCB0aHJvdWdoIENMSVxyXG4gICAgaWYgKCFbJ3B1cHBldGVlcicsICdoaWdoY2hhcnRzJ10uaW5jbHVkZXMoY2F0ZWdvcnkpKSB7XHJcbiAgICAgIGNvbnNvbGUubG9nKGBcXG4ke2NhdGVnb3J5LnRvVXBwZXJDYXNlKCl9YC5yZWQpO1xyXG4gICAgICBjeWNsZUNhdGVnb3JpZXMoZGVmYXVsdENvbmZpZ1tjYXRlZ29yeV0pO1xyXG4gICAgfVxyXG4gIH0pO1xyXG4gIGNvbnNvbGUubG9nKCdcXG4nKTtcclxufVxyXG5cclxuLyoqXHJcbiAqIFJvdW5kcyBudW1iZXIgdG8gcGFzc2VkIHByZWNpc2lvbi5cclxuICpcclxuICogQHBhcmFtIHtudW1iZXJ9IHZhbHVlIC0gTnVtYmVyIHRvIHJvdW5kLlxyXG4gKiBAcGFyYW0ge251bWJlcn0gcHJlY2lzaW9uIC0gQSBwcmVjaXNpb24gb2Ygcm91bmRpbmcuXHJcbiAqL1xyXG5leHBvcnQgY29uc3Qgcm91bmROdW1iZXIgPSAodmFsdWUsIHByZWNpc2lvbiA9IDEpID0+IHtcclxuICBjb25zdCBtdWx0aXBsaWVyID0gTWF0aC5wb3coMTAsIHByZWNpc2lvbiB8fCAwKTtcclxuICByZXR1cm4gTWF0aC5yb3VuZCgrdmFsdWUgKiBtdWx0aXBsaWVyKSAvIG11bHRpcGxpZXI7XHJcbn07XHJcblxyXG4vKipcclxuICogQ2FzdHMgdGhlIGl0ZW0gdG8gYm9vbGVhbi5cclxuICpcclxuICogQHBhcmFtIHthbnl9IGl0ZW0gLSBJdGVtIHRvIGJlIGNhc3QuXHJcbiAqL1xyXG5leHBvcnQgY29uc3QgdG9Cb29sZWFuID0gKGl0ZW0pID0+XHJcbiAgWydmYWxzZScsICd1bmRlZmluZWQnLCAnbnVsbCcsICdOYU4nLCAnMCcsICcnXS5pbmNsdWRlcyhpdGVtKVxyXG4gICAgPyBmYWxzZVxyXG4gICAgOiAhIWl0ZW07XHJcblxyXG4vKipcclxuICogSWYgbmVjZXNzYXJ5LCBwbGFjZXMgYSBjdXN0b20gY29kZSBpbnNpZGUgYSBmdW5jdGlvbi5cclxuICpcclxuICogQHBhcmFtIHthbnl9IGN1c3RvbUNvZGUgLSBUaGUgY3VzdG9tQ29kZS5cclxuICovXHJcbmV4cG9ydCBjb25zdCB3cmFwQXJvdW5kID0gKGN1c3RvbUNvZGUsIGFsbG93RmlsZVJlc291cmNlcykgPT4ge1xyXG4gIGlmIChjdXN0b21Db2RlICYmIHR5cGVvZiBjdXN0b21Db2RlID09PSAnc3RyaW5nJykge1xyXG4gICAgY3VzdG9tQ29kZSA9IGN1c3RvbUNvZGUudHJpbSgpO1xyXG5cclxuICAgIGlmIChjdXN0b21Db2RlLmVuZHNXaXRoKCcuanMnKSkge1xyXG4gICAgICByZXR1cm4gYWxsb3dGaWxlUmVzb3VyY2VzXHJcbiAgICAgICAgPyB3cmFwQXJvdW5kKHJlYWRGaWxlU3luYyhjdXN0b21Db2RlLCAndXRmOCcpKVxyXG4gICAgICAgIDogZmFsc2U7XHJcbiAgICB9IGVsc2UgaWYgKFxyXG4gICAgICBjdXN0b21Db2RlLnN0YXJ0c1dpdGgoJ2Z1bmN0aW9uKCknKSB8fFxyXG4gICAgICBjdXN0b21Db2RlLnN0YXJ0c1dpdGgoJ2Z1bmN0aW9uICgpJykgfHxcclxuICAgICAgY3VzdG9tQ29kZS5zdGFydHNXaXRoKCcoKT0+JykgfHxcclxuICAgICAgY3VzdG9tQ29kZS5zdGFydHNXaXRoKCcoKSA9PicpXHJcbiAgICApIHtcclxuICAgICAgcmV0dXJuIGAoJHtjdXN0b21Db2RlfSkoKWA7XHJcbiAgICB9XHJcbiAgICByZXR1cm4gY3VzdG9tQ29kZS5yZXBsYWNlKC87JC8sICcnKTtcclxuICB9XHJcbn07XHJcblxyXG4vKipcclxuICogVXRpbGl0eSB0byBtZWFzdXJlIHRpbWUuXHJcbiAqL1xyXG5leHBvcnQgY29uc3QgbWVhc3VyZVRpbWUgPSAoKSA9PiB7XHJcbiAgY29uc3Qgc3RhcnQgPSBwcm9jZXNzLmhydGltZS5iaWdpbnQoKTtcclxuICByZXR1cm4gKCkgPT4gTnVtYmVyKHByb2Nlc3MuaHJ0aW1lLmJpZ2ludCgpIC0gc3RhcnQpIC8gMTAwMDAwMDtcclxufTtcclxuXHJcbmV4cG9ydCBkZWZhdWx0IHtcclxuICBfX2Rpcm5hbWUsXHJcbiAgY2xlYXJUZXh0LFxyXG4gIGV4cEJhY2tvZmYsXHJcbiAgZml4VHlwZSxcclxuICBoYW5kbGVSZXNvdXJjZXMsXHJcbiAgaXNDb3JyZWN0SlNPTixcclxuICBpc09iamVjdCxcclxuICBpc1ByaXZhdGVSYW5nZVVybEZvdW5kLFxyXG4gIG9wdGlvbnNTdHJpbmdpZnksXHJcbiAgcHJpbnRMb2dvLFxyXG4gIHByaW50VXNhZ2UsXHJcbiAgcm91bmROdW1iZXIsXHJcbiAgdG9Cb29sZWFuLFxyXG4gIHdyYXBBcm91bmQsXHJcbiAgbWVhc3VyZVRpbWVcclxufTtcclxuIiwiLyoqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKipcclxuXHJcbkhpZ2hjaGFydHMgRXhwb3J0IFNlcnZlclxyXG5cclxuQ29weXJpZ2h0IChjKSAyMDE2LTIwMjMsIEhpZ2hzb2Z0XHJcblxyXG5MaWNlbmNlZCB1bmRlciB0aGUgTUlUIGxpY2VuY2UuXHJcblxyXG5BZGRpdGlvbmFsbHkgYSB2YWxpZCBIaWdoY2hhcnRzIGxpY2Vuc2UgaXMgcmVxdWlyZWQgZm9yIHVzZS5cclxuXHJcblNlZSBMSUNFTlNFIGZpbGUgaW4gcm9vdCBmb3IgZGV0YWlscy5cclxuXHJcbioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKiovXHJcblxyXG5pbXBvcnQgcmF0ZUxpbWl0IGZyb20gJ2V4cHJlc3MtcmF0ZS1saW1pdCc7XHJcblxyXG5pbXBvcnQgeyBjbGVhclRleHQgfSBmcm9tICcuLi91dGlscy5qcyc7XHJcbmltcG9ydCB7IGxvZyB9IGZyb20gJy4uL2xvZ2dlci5qcyc7XHJcblxyXG4vKipcclxuICogRW5hYmxlcyByYXRlIGxpbWl0aW5nIGZvciBhIGdpdmVuIGFwcC5cclxuICpcclxuICogQHBhcmFtIHtvYmplY3R9IGFwcCAtIFRoZSBleHByZXNzIGFwcC5cclxuICogQHBhcmFtIHtvYmplY3R9IGxpbWl0Q29uZmlnIC0gVGhlIG9wdGlvbnMgZm9yIHRoZSByYXRlIGxpbWl0aW5nLlxyXG4gKi9cclxuZXhwb3J0IGRlZmF1bHQgKGFwcCwgbGltaXRDb25maWcpID0+IHtcclxuICBjb25zdCBtc2cgPVxyXG4gICAgJ1RvbyBtYW55IHJlcXVlc3RzLCB5b3UgaGF2ZSBiZWVuIHJhdGUgbGltaXRlZC4gUGxlYXNlIHRyeSBhZ2FpbiBsYXRlci4nO1xyXG5cclxuICAvLyBPcHRpb25zIGZvciB0aGUgcmF0ZSBsaW1pdGVyXHJcbiAgY29uc3QgcmF0ZU9wdGlvbnMgPSB7XHJcbiAgICBtYXg6IGxpbWl0Q29uZmlnLm1heFJlcXVlc3RzIHx8IDMwLFxyXG4gICAgd2luZG93OiBsaW1pdENvbmZpZy53aW5kb3cgfHwgMSxcclxuICAgIGRlbGF5OiBsaW1pdENvbmZpZy5kZWxheSB8fCAwLFxyXG4gICAgdHJ1c3RQcm94eTogbGltaXRDb25maWcudHJ1c3RQcm94eSB8fCBmYWxzZSxcclxuICAgIHNraXBLZXk6IGxpbWl0Q29uZmlnLnNraXBLZXkgfHwgZmFsc2UsXHJcbiAgICBza2lwVG9rZW46IGxpbWl0Q29uZmlnLnNraXBUb2tlbiB8fCBmYWxzZVxyXG4gIH07XHJcblxyXG4gIC8vIFNldCBpZiBiZWhpbmQgYSBwcm94eVxyXG4gIGlmIChyYXRlT3B0aW9ucy50cnVzdFByb3h5KSB7XHJcbiAgICBhcHAuZW5hYmxlKCd0cnVzdCBwcm94eScpO1xyXG4gIH1cclxuXHJcbiAgLy8gQ3JlYXRlIGEgbGltaXRlclxyXG4gIGNvbnN0IGxpbWl0ZXIgPSByYXRlTGltaXQoe1xyXG4gICAgd2luZG93TXM6IHJhdGVPcHRpb25zLndpbmRvdyAqIDYwICogMTAwMCxcclxuICAgIC8vIExpbWl0IGVhY2ggSVAgdG8gMTAwIHJlcXVlc3RzIHBlciB3aW5kb3dNc1xyXG4gICAgbWF4OiByYXRlT3B0aW9ucy5tYXgsXHJcbiAgICAvLyBEaXNhYmxlIGRlbGF5aW5nLCBmdWxsIHNwZWVkIHVudGlsIHRoZSBtYXggbGltaXQgaXMgcmVhY2hlZFxyXG4gICAgZGVsYXlNczogcmF0ZU9wdGlvbnMuZGVsYXksXHJcbiAgICBoYW5kbGVyOiAocmVxdWVzdCwgcmVzcG9uc2UpID0+IHtcclxuICAgICAgcmVzcG9uc2UuZm9ybWF0KHtcclxuICAgICAgICBqc29uOiAoKSA9PiB7XHJcbiAgICAgICAgICByZXNwb25zZS5zdGF0dXMoNDI5KS5zZW5kKHsgbWVzc2FnZTogbXNnIH0pO1xyXG4gICAgICAgIH0sXHJcbiAgICAgICAgZGVmYXVsdDogKCkgPT4ge1xyXG4gICAgICAgICAgcmVzcG9uc2Uuc3RhdHVzKDQyOSkuc2VuZChtc2cpO1xyXG4gICAgICAgIH1cclxuICAgICAgfSk7XHJcbiAgICB9LFxyXG4gICAgc2tpcDogKHJlcXVlc3QpID0+IHtcclxuICAgICAgLy8gQWxsb3cgYnlwYXNzaW5nIHRoZSBsaW1pdGVyIGlmIGEgdmFsaWQga2V5L3Rva2VuIGhhcyBiZWVuIHNlbnRcclxuICAgICAgaWYgKFxyXG4gICAgICAgIHJhdGVPcHRpb25zLnNraXBLZXkgIT09IGZhbHNlICYmXHJcbiAgICAgICAgcmF0ZU9wdGlvbnMuc2tpcFRva2VuICE9PSBmYWxzZSAmJlxyXG4gICAgICAgIHJlcXVlc3QucXVlcnkua2V5ID09PSByYXRlT3B0aW9ucy5za2lwS2V5ICYmXHJcbiAgICAgICAgcmVxdWVzdC5xdWVyeS5hY2Nlc3NfdG9rZW4gPT09IHJhdGVPcHRpb25zLnNraXBUb2tlblxyXG4gICAgICApIHtcclxuICAgICAgICBsb2coNCwgJ1tyYXRlLWxpbWl0aW5nXSBTa2lwcGluZyByYXRlIGxpbWl0ZXIuJyk7XHJcbiAgICAgICAgcmV0dXJuIHRydWU7XHJcbiAgICAgIH1cclxuICAgICAgcmV0dXJuIGZhbHNlO1xyXG4gICAgfVxyXG4gIH0pO1xyXG5cclxuICAvLyBVc2UgYSBsaW1pdGVyIGFzIGEgbWlkZGxld2FyZVxyXG4gIGFwcC51c2UobGltaXRlcik7XHJcblxyXG4gIGxvZyhcclxuICAgIDMsXHJcbiAgICBjbGVhclRleHQoXHJcbiAgICAgIGBbcmF0ZS1saW1pdGluZ10gRW5hYmxlZCByYXRlIGxpbWl0aW5nOiAke3JhdGVPcHRpb25zLm1heH0gcmVxdWVzdHNcclxuICAgICAgcGVyICR7cmF0ZU9wdGlvbnMud2luZG93fSBtaW51dGUgcGVyIElQLCB0cnVzdGluZyBwcm94eTpcclxuICAgICAgJHtyYXRlT3B0aW9ucy50cnVzdFByb3h5fS5gXHJcbiAgICApXHJcbiAgKTtcclxufTtcclxuIiwiLyoqXHJcbiAqIFRoaXMgbW9kdWxlIGV4cG9ydHMgdHdvIGZ1bmN0aW9uczogZmV0Y2ggKGZvciBHRVQgcmVxdWVzdHMpIGFuZCBwb3N0IChmb3IgUE9TVCByZXF1ZXN0cykuXHJcbiAqL1xyXG5cclxuaW1wb3J0IGh0dHAgZnJvbSAnaHR0cCc7XHJcbmltcG9ydCBodHRwcyBmcm9tICdodHRwcyc7XHJcblxyXG4vKipcclxuICogRGV0ZXJtaW5lcyB0aGUgcHJvdG9jb2wgb2YgdGhlIGdpdmVuIFVSTCAoZWl0aGVyIGBodHRwYCBvciBgaHR0cHNgKS5cclxuICpcclxuICogQGZ1bmN0aW9uXHJcbiAqIEBwYXJhbSB7c3RyaW5nfSB1cmwgLSBUaGUgVVJMIHdob3NlIHByb3RvY29sIG5lZWRzIHRvIGJlIGRldGVybWluZWQuXHJcbiAqIEByZXR1cm5zIHtPYmplY3R9IFJldHVybnMgdGhlIGBodHRwc2AgbW9kdWxlIGlmIHRoZSBVUkwgc3RhcnRzIHdpdGggJ2h0dHBzJyxcclxuICogb3RoZXJ3aXNlIHJldHVybnMgdGhlIGBodHRwYCBtb2R1bGUuXHJcbiAqIEBwcml2YXRlXHJcbiAqXHJcbiAqIEBleGFtcGxlXHJcbiAqXHJcbiAqIGNvbnN0IHByb3RvY29sID0gZ2V0UHJvdG9jb2woJ2h0dHBzOi8vZXhhbXBsZS5jb20nKTtcclxuICogY29uc29sZS5sb2cocHJvdG9jb2wpOyAvLyBPdXRwdXRzIHRoZSAnaHR0cHMnIG1vZHVsZVxyXG4gKi9cclxuY29uc3QgZ2V0UHJvdG9jb2wgPSAodXJsKSA9PiB7XHJcbiAgcmV0dXJuIHVybC5zdGFydHNXaXRoKCdodHRwcycpID8gaHR0cHMgOiBodHRwO1xyXG59O1xyXG5cclxuLyoqXHJcbiAqIFNlbmRzIGEgR0VUIHJlcXVlc3QgdG8gdGhlIHNwZWNpZmllZCBVUkwgd2l0aCBvcHRpb25hbCByZXF1ZXN0IG9wdGlvbnMuXHJcbiAqXHJcbiAqIEBmdW5jdGlvblxyXG4gKiBAYXN5bmNcclxuICogQHBhcmFtIHtzdHJpbmd9IHVybCAtIFRoZSBVUkwgdG8gZmV0Y2guXHJcbiAqIEBwYXJhbSB7T2JqZWN0fSBbcmVxdWVzdE9wdGlvbnM9e31dIC0gT3B0aW9uYWwgcmVxdWVzdCBvcHRpb25zIGFuZCBoZWFkZXJzLlxyXG4gKiBAcmV0dXJucyB7UHJvbWlzZTxPYmplY3Q+fSBSZXR1cm5zIGEgcHJvbWlzZSB0aGF0IHJlc29sdmVzIHdpdGggdGhlIHJlc3BvbnNlIG9iamVjdC5cclxuICogVGhlIHJlc3BvbnNlIG9iamVjdCBjb250YWlucyBhIGAudGV4dGAgcHJvcGVydHkgd2l0aCB0aGUgcmF3IHJlc3BvbnNlIGRhdGEuXHJcbiAqIEB0aHJvd3Mge0Vycm9yfSBUaHJvd3MgYW4gZXJyb3IgaWYgdGhlIHJlcXVlc3QgZmFpbHMgb3IgaWYgbm8gZGF0YSBpcyBmZXRjaGVkIGZyb20gdGhlIFVSTC5cclxuICpcclxuICogQGV4YW1wbGVcclxuICpcclxuICogYXN5bmMgZnVuY3Rpb24gZ2V0RGF0YSgpIHtcclxuICogICB0cnkge1xyXG4gKiAgICAgY29uc3QgcmVzcG9uc2UgPSBhd2FpdCBmZXRjaCgnaHR0cHM6Ly9hcGkuZXhhbXBsZS5jb20vZGF0YScpO1xyXG4gKiAgICAgY29uc29sZS5sb2cocmVzcG9uc2UudGV4dCk7XHJcbiAqICAgfSBjYXRjaCAoZXJyb3IpIHtcclxuICogICAgIGNvbnNvbGUuZXJyb3IoJ0Vycm9yIGZldGNoaW5nIGRhdGE6JywgZXJyb3IpO1xyXG4gKiAgIH1cclxuICogfVxyXG4gKlxyXG4gKiBnZXREYXRhKCk7XHJcbiAqL1xyXG5hc3luYyBmdW5jdGlvbiBmZXRjaCh1cmwsIHJlcXVlc3RPcHRpb25zID0ge30pIHtcclxuICByZXR1cm4gbmV3IFByb21pc2UoKHJlc29sdmUsIHJlamVjdCkgPT4ge1xyXG4gICAgY29uc3QgcHJvdG9jb2wgPSBnZXRQcm90b2NvbCh1cmwpO1xyXG5cclxuICAgIHByb3RvY29sXHJcbiAgICAgIC5nZXQodXJsLCByZXF1ZXN0T3B0aW9ucywgKHJlcykgPT4ge1xyXG4gICAgICAgIGxldCBkYXRhID0gJyc7XHJcblxyXG4gICAgICAgIC8vIEEgY2h1bmsgb2YgZGF0YSBoYXMgYmVlbiByZWNlaXZlZC5cclxuICAgICAgICByZXMub24oJ2RhdGEnLCAoY2h1bmspID0+IHtcclxuICAgICAgICAgIGRhdGEgKz0gY2h1bms7XHJcbiAgICAgICAgfSk7XHJcblxyXG4gICAgICAgIC8vIFRoZSB3aG9sZSByZXNwb25zZSBoYXMgYmVlbiByZWNlaXZlZC5cclxuICAgICAgICByZXMub24oJ2VuZCcsICgpID0+IHtcclxuICAgICAgICAgIGlmICghZGF0YSkge1xyXG4gICAgICAgICAgICByZWplY3QoJ05vdGhpbmcgd2FzIGZldGNoZWQgZnJvbSB0aGUgVVJMLicpO1xyXG4gICAgICAgICAgfVxyXG5cclxuICAgICAgICAgIHJlcy50ZXh0ID0gZGF0YTtcclxuICAgICAgICAgIHJlc29sdmUocmVzKTtcclxuICAgICAgICB9KTtcclxuICAgICAgfSlcclxuICAgICAgLm9uKCdlcnJvcicsIChlcnJvcikgPT4ge1xyXG4gICAgICAgIHJlamVjdChlcnJvcik7XHJcbiAgICAgIH0pO1xyXG4gIH0pO1xyXG59XHJcblxyXG4vKipcclxuICogU2VuZHMgYSBQT1NUIHJlcXVlc3QgdG8gdGhlIHNwZWNpZmllZCBVUkwgd2l0aCB0aGUgZ2l2ZW4gYm9keSBhbmQgcmVxdWVzdCBvcHRpb25zLlxyXG4gKlxyXG4gKiBAZnVuY3Rpb25cclxuICogQGFzeW5jXHJcbiAqIEBwYXJhbSB7c3RyaW5nfSB1cmwgLSBUaGUgVVJMIHRvIHdoaWNoIHRoZSByZXF1ZXN0IHNob3VsZCBiZSBzZW50LlxyXG4gKiBAcGFyYW0ge09iamVjdH0gW2JvZHk9e31dIC0gVGhlIGRhdGEgdG8gYmUgc2VudCBhcyB0aGUgcmVxdWVzdCBib2R5LCBpbiBKU09OIGZvcm1hdC5cclxuICogQHBhcmFtIHtPYmplY3R9IFtyZXF1ZXN0T3B0aW9ucz17fV0gLSBPcHRpb25hbCByZXF1ZXN0IG9wdGlvbnMgYW5kIGhlYWRlcnMuXHJcbiAqIEByZXR1cm5zIHtQcm9taXNlPE9iamVjdD59IC0gUmV0dXJucyBhIHByb21pc2UgdGhhdCByZXNvbHZlcyB3aXRoIHRoZSBwYXJzZWQgSlNPTiByZXNwb25zZS5cclxuICogQHRocm93cyB7RXJyb3J9IFRocm93cyBhbiBlcnJvciBpZiB0aGUgcmVxdWVzdCBmYWlscyBvciBpZiB0aGUgcmVzcG9uc2UgY2Fubm90IGJlIHBhcnNlZC5cclxuICpcclxuICogQGV4YW1wbGVcclxuICpcclxuICogYXN5bmMgZnVuY3Rpb24gc2VuZERhdGEoKSB7XHJcbiAqICAgY29uc3QgZGF0YVRvU2VuZCA9IHtcclxuICogICAgIGtleTE6ICd2YWx1ZTEnLFxyXG4gKiAgICAga2V5MjogJ3ZhbHVlMicsXHJcbiAqICAgfTtcclxuICogICB0cnkge1xyXG4gKiAgICAgY29uc3QgcmVzcG9uc2UgPSBhd2FpdCBwb3N0KCdodHRwczovL2FwaS5leGFtcGxlLmNvbS9kYXRhJywgZGF0YVRvU2VuZCk7XHJcbiAqICAgICBjb25zb2xlLmxvZyhyZXNwb25zZSk7XHJcbiAqICAgfSBjYXRjaCAoZXJyb3IpIHtcclxuICogICAgIGNvbnNvbGUuZXJyb3IoJ0Vycm9yIHNlbmRpbmcgZGF0YTonLCBlcnJvcik7XHJcbiAqICAgfVxyXG4gKiB9XHJcbiAqXHJcbiAqIHNlbmREYXRhKCk7XHJcbiAqL1xyXG5hc3luYyBmdW5jdGlvbiBwb3N0KHVybCwgYm9keSA9IHt9LCByZXF1ZXN0T3B0aW9ucyA9IHt9KSB7XHJcbiAgcmV0dXJuIG5ldyBQcm9taXNlKChyZXNvbHZlLCByZWplY3QpID0+IHtcclxuICAgIGNvbnN0IHByb3RvY29sID0gZ2V0UHJvdG9jb2wodXJsKTtcclxuICAgIGNvbnN0IGRhdGEgPSBKU09OLnN0cmluZ2lmeShib2R5KTtcclxuXHJcbiAgICAvLyBTZXQgZGVmYXVsdCBoZWFkZXJzIGFuZCBtZXJnZSB3aXRoIHJlcXVlc3RPcHRpb25zXHJcbiAgICBjb25zdCBvcHRpb25zID0gT2JqZWN0LmFzc2lnbihcclxuICAgICAge1xyXG4gICAgICAgIG1ldGhvZDogJ1BPU1QnLFxyXG4gICAgICAgIGhlYWRlcnM6IHtcclxuICAgICAgICAgICdDb250ZW50LVR5cGUnOiAnYXBwbGljYXRpb24vanNvbicsXHJcbiAgICAgICAgICAnQ29udGVudC1MZW5ndGgnOiBkYXRhLmxlbmd0aFxyXG4gICAgICAgIH1cclxuICAgICAgfSxcclxuICAgICAgcmVxdWVzdE9wdGlvbnNcclxuICAgICk7XHJcblxyXG4gICAgY29uc3QgcmVxID0gcHJvdG9jb2xcclxuICAgICAgLnJlcXVlc3QodXJsLCBvcHRpb25zLCAocmVzKSA9PiB7XHJcbiAgICAgICAgbGV0IHJlc3BvbnNlRGF0YSA9ICcnO1xyXG5cclxuICAgICAgICAvLyBBIGNodW5rIG9mIGRhdGEgaGFzIGJlZW4gcmVjZWl2ZWQuXHJcbiAgICAgICAgcmVzLm9uKCdkYXRhJywgKGNodW5rKSA9PiB7XHJcbiAgICAgICAgICByZXNwb25zZURhdGEgKz0gY2h1bms7XHJcbiAgICAgICAgfSk7XHJcblxyXG4gICAgICAgIC8vIFRoZSB3aG9sZSByZXNwb25zZSBoYXMgYmVlbiByZWNlaXZlZC5cclxuICAgICAgICByZXMub24oJ2VuZCcsICgpID0+IHtcclxuICAgICAgICAgIHRyeSB7XHJcbiAgICAgICAgICAgIHJlcy50ZXh0ID0gcmVzcG9uc2VEYXRhO1xyXG4gICAgICAgICAgICByZXNvbHZlKHJlcyk7XHJcbiAgICAgICAgICB9IGNhdGNoIChlcnJvcikge1xyXG4gICAgICAgICAgICByZWplY3QoZXJyb3IpO1xyXG4gICAgICAgICAgfVxyXG4gICAgICAgIH0pO1xyXG4gICAgICB9KVxyXG4gICAgICAub24oJ2Vycm9yJywgKGVycm9yKSA9PiB7XHJcbiAgICAgICAgcmVqZWN0KGVycm9yKTtcclxuICAgICAgfSk7XHJcblxyXG4gICAgLy8gV3JpdGUgdGhlIHJlcXVlc3QgYm9keSBhbmQgZW5kIHRoZSByZXF1ZXN0LlxyXG4gICAgcmVxLndyaXRlKGRhdGEpO1xyXG4gICAgcmVxLmVuZCgpO1xyXG4gIH0pO1xyXG59XHJcblxyXG5leHBvcnQgZGVmYXVsdCBmZXRjaDtcclxuZXhwb3J0IHsgZmV0Y2gsIHBvc3QgfTtcclxuIiwiLyoqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKipcclxuXHJcbkhpZ2hjaGFydHMgRXhwb3J0IFNlcnZlclxyXG5cclxuQ29weXJpZ2h0IChjKSAyMDE2LTIwMjMsIEhpZ2hzb2Z0XHJcblxyXG5MaWNlbmNlZCB1bmRlciB0aGUgTUlUIGxpY2VuY2UuXHJcblxyXG5BZGRpdGlvbmFsbHkgYSB2YWxpZCBIaWdoY2hhcnRzIGxpY2Vuc2UgaXMgcmVxdWlyZWQgZm9yIHVzZS5cclxuXHJcblNlZSBMSUNFTlNFIGZpbGUgaW4gcm9vdCBmb3IgZGV0YWlscy5cclxuXHJcbioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKiovXHJcblxyXG4vLyBUaGUgY2FjaGUgbWFuYWdlciBtYW5hZ2VzIHRoZSBIaWdoY2hhcnRzIGxpYnJhcnkgYW5kIGl0cyBkZXBlbmRlbmNpZXMuXHJcbi8vIFRoZSBjYWNoZSBpdHNlbGYgaXMgc3RvcmVkIGluIC5jYWNoZSwgYW5kIGlzIGNoZWNrZWQgYnkgdGhlIGNvbmZpZyBzeXN0ZW1cclxuLy8gYmVmb3JlIHN0YXJ0aW5nIHRoZSBzZXJ2aWNlXHJcblxyXG5pbXBvcnQgeyBleGlzdHNTeW5jLCBta2RpclN5bmMsIHJlYWRGaWxlU3luYywgd3JpdGVGaWxlU3luYyB9IGZyb20gJ2ZzJztcclxuaW1wb3J0IHsgam9pbiB9IGZyb20gJ3BhdGgnO1xyXG5cclxuaW1wb3J0IGRvdGVudiBmcm9tICdkb3RlbnYnO1xyXG5pbXBvcnQgSHR0cHNQcm94eUFnZW50IGZyb20gJ2h0dHBzLXByb3h5LWFnZW50JztcclxuaW1wb3J0IHsgZmV0Y2ggfSBmcm9tICcuL2ZldGNoLmpzJztcclxuXHJcbmltcG9ydCB7IGxvZyB9IGZyb20gJy4vbG9nZ2VyLmpzJztcclxuaW1wb3J0IHsgX19kaXJuYW1lIH0gZnJvbSAnLi4vbGliL3V0aWxzLmpzJztcclxuXHJcbmRvdGVudi5jb25maWcoKTtcclxuXHJcbmNvbnN0IGNhY2hlUGF0aCA9IGpvaW4oX19kaXJuYW1lLCAnLmNhY2hlJyk7XHJcblxyXG5jb25zdCBjYWNoZSA9IHtcclxuICBjZG5VUkw6ICdodHRwczovL2NvZGUuaGlnaGNoYXJ0cy5jb20vJyxcclxuICBhY3RpdmVNYW5pZmVzdDoge30sXHJcbiAgc291cmNlczogJycsXHJcbiAgaGNWZXJzaW9uOiAnJ1xyXG59O1xyXG5cclxuLy8gVE9ETzogVGhlIGNvbmZpZyBzaG91bGQgYmUgYWNjZXNzc2libGUgZ2xvYmFsbHkgc28gd2UgZG9uJ3QgaGF2ZSB0byBkbyB0aGlzIHNvcnQgb2YgdGhpbmcuLlxyXG5sZXQgYXBwbGllZENvbmZpZyA9IGZhbHNlO1xyXG5cclxuLyoqXHJcbiAqIEV4dHJhY3RzIHRoZSBIaWdoY2hhcnRzIHZlcnNpb24gZnJvbSB0aGUgY2FjaGVcclxuICovXHJcbmNvbnN0IGV4dHJhY3RWZXJzaW9uID0gKCkgPT5cclxuICAoY2FjaGUuaGNWZXJzaW9uID0gY2FjaGUuc291cmNlc1xyXG4gICAgLnN1YnN0cigwLCBjYWNoZS5zb3VyY2VzLmluZGV4T2YoJyovJykpXHJcbiAgICAucmVwbGFjZSgnLyonLCAnJylcclxuICAgIC5yZXBsYWNlKCcqLycsICcnKVxyXG4gICAgLnJlcGxhY2UoL1xcbi9nLCAnJylcclxuICAgIC50cmltKCkpO1xyXG5cclxuLyoqXHJcbiAqIFNhdmVzIHRoZSBIaWdoY2hhcnRzIHBhcnQgb2YgYSBjb25maWcgdG8gYSBtYW5pZmVzdCBmaWxlIGluIHRoZSBjYWNoZVxyXG4gKlxyXG4gKiBAcGFyYW0ge29iamVjdH0gY29uZmlnIC0gSGlnaGNoYXJ0cyByZWxhdGVkIGNvbmZpZ3VyYXRpb24gb2JqZWN0LlxyXG4gKiBAcGFyYW0ge29iamVjdH0gZmV0Y2hlZE1vZHVsZXMgLSBBbiBvYmplY3QgdGhhdCBjb250YWlucyBtYXBwZWQgbmFtZXMgb2ZcclxuICogZmV0Y2hlZCBIaWdoY2hhcnRzIG1vZHVsZXMgdG8gdXNlLlxyXG4gKi9cclxuY29uc3Qgc2F2ZUNvbmZpZ1RvTWFuaWZlc3QgPSBhc3luYyAoY29uZmlnLCBmZXRjaGVkTW9kdWxlcykgPT4ge1xyXG4gIGNvbnN0IG5ld01hbmlmZXN0ID0ge1xyXG4gICAgdmVyc2lvbjogY29uZmlnLnZlcnNpb24sXHJcbiAgICBtb2R1bGVzOiBmZXRjaGVkTW9kdWxlcyB8fCB7fVxyXG4gIH07XHJcblxyXG4gIC8vIFVwZGF0ZSBjYWNoZSBvYmplY3Qgd2l0aCB0aGUgY3VycmVudCBtb2R1bGVzXHJcbiAgY2FjaGUuYWN0aXZlTWFuaWZlc3QgPSBuZXdNYW5pZmVzdDtcclxuXHJcbiAgbG9nKDQsICdbY2FjaGVdIHdyaXRpbmcgbmV3IG1hbmlmZXN0Jyk7XHJcblxyXG4gIHRyeSB7XHJcbiAgICB3cml0ZUZpbGVTeW5jKFxyXG4gICAgICBqb2luKGNhY2hlUGF0aCwgJ21hbmlmZXN0Lmpzb24nKSxcclxuICAgICAgSlNPTi5zdHJpbmdpZnkobmV3TWFuaWZlc3QpLFxyXG4gICAgICAndXRmOCdcclxuICAgICk7XHJcbiAgfSBjYXRjaCAoZXJyb3IpIHtcclxuICAgIGxvZygxLCBgW2NhY2hlXSBFcnJvciB3cml0aW5nIGNhY2hlIG1hbmlmZXN0OiAke2Vycm9yfS5gKTtcclxuICB9XHJcbn07XHJcblxyXG4vKipcclxuICogRmV0Y2hlcyBhIHNpbmdsZSBzY3JpcHQuXHJcbiAqXHJcbiAqIEBwYXJhbSB7c3RyaW5nfSBzY3JpcHQgLSBBIHBhdGggdG8gc2NyaXB0IHRvIGdldC5cclxuICogQHBhcmFtIHtvYmplY3R9IHByb3h5QWdlbnQgLSBUaGUgcHJveHkgYWdlbnQgdG8gdXNlIGZvciBhIHJlcXVlc3QuXHJcbiAqL1xyXG5jb25zdCBmZXRjaFNjcmlwdCA9IGFzeW5jIChzY3JpcHQsIHByb3h5QWdlbnQpID0+IHtcclxuICB0cnkge1xyXG4gICAgLy8gR2V0IHJpZCBvZiB0aGUgLmpzIGZyb20gdGhlIGN1c3RvbSBzdHJpbmdzXHJcbiAgICBpZiAoc2NyaXB0LmVuZHNXaXRoKCcuanMnKSkge1xyXG4gICAgICBzY3JpcHQgPSBzY3JpcHQuc3Vic3RyaW5nKDAsIHNjcmlwdC5sZW5ndGggLSAzKTtcclxuICAgIH1cclxuXHJcbiAgICBsb2coNCwgYFtjYWNoZV0gRmV0Y2hpbmcgc2NyaXB0IC0gJHtzY3JpcHR9LmpzYCk7XHJcblxyXG4gICAgLy8gSWYgZXhpc3RzLCBhZGQgcHJveHkgYWdlbnQgdG8gcmVxdWVzdCBvcHRpb25zXHJcbiAgICBjb25zdCByZXF1ZXN0T3B0aW9ucyA9IHByb3h5QWdlbnRcclxuICAgICAgPyB7XHJcbiAgICAgICAgICBhZ2VudDogcHJveHlBZ2VudCxcclxuICAgICAgICAgIHRpbWVvdXQ6ICtwcm9jZXNzLmVudlsnUFJPWFlfU0VSVkVSX1RJTUVPVVQnXSB8fCA1MDAwXHJcbiAgICAgICAgfVxyXG4gICAgICA6IHt9O1xyXG5cclxuICAgIC8vIEZldGNoIHRoZSBzY3JpcHRcclxuICAgIGNvbnN0IHJlc3BvbnNlID0gYXdhaXQgZmV0Y2goYCR7c2NyaXB0fS5qc2AsIHJlcXVlc3RPcHRpb25zKTtcclxuXHJcbiAgICAvLyBJZiBPSywgcmV0dXJuIGl0cyB0ZXh0IHJlcHJlc2VudGF0aW9uXHJcbiAgICBpZiAocmVzcG9uc2Uuc3RhdHVzQ29kZSA9PT0gMjAwKSB7XHJcbiAgICAgIHJldHVybiByZXNwb25zZS50ZXh0O1xyXG4gICAgfVxyXG5cclxuICAgIHRocm93IGAke3Jlc3BvbnNlLnN0YXR1c0NvZGV9YDtcclxuICB9IGNhdGNoIChlcnJvcikge1xyXG4gICAgbG9nKDEsIGBbY2FjaGVdIEVycm9yIGZldGNoaW5nIHNjcmlwdCAke3NjcmlwdH0uanM6ICR7ZXJyb3J9LmApO1xyXG4gICAgdGhyb3cgZXJyb3I7XHJcbiAgfVxyXG59O1xyXG5cclxuLyoqXHJcbiAqIFVwZGF0ZXMgdGhlIEhpZ2hjaGFydHMgY2FjaGUuXHJcbiAqXHJcbiAqIEBwYXJhbSB7b2JqZWN0fSBjb25maWcgLSBIaWdoY2hhcnRzIHJlbGF0ZWQgY29uZmlndXJhdGlvbiBvYmplY3QuXHJcbiAqIEBwYXJhbSB7c3RyaW5nfSBzb3VyY2VQYXRoIC0gQSBwYXRoIHRvIHRoZSBmaWxlIHdoZXJlIHNhdmUgdXBkYXRlZCBzb3VyY2VzLlxyXG4gKiBAcmV0dXJuIHtvYmplY3R9IEFuIG9iamVjdCB0aGF0IGNvbnRhaW5zIG1hcHBlZCBuYW1lcyBvZiBmZXRjaGVkIEhpZ2hjaGFydHNcclxuICogbW9kdWxlcyB0byB1c2UuXHJcbiAqL1xyXG5jb25zdCB1cGRhdGVDYWNoZSA9IGFzeW5jIChjb25maWcsIHNvdXJjZVBhdGgpID0+IHtcclxuICBjb25zdCB7IGNvcmVTY3JpcHRzLCBtb2R1bGVzLCBpbmRpY2F0b3JzLCBzY3JpcHRzOiBjdXN0b21TY3JpcHRzIH0gPSBjb25maWc7XHJcbiAgY29uc3QgaGNWZXJzaW9uID1cclxuICAgIGNvbmZpZy52ZXJzaW9uID09PSAnbGF0ZXN0JyB8fCAhY29uZmlnLnZlcnNpb24gPyAnJyA6IGAke2NvbmZpZy52ZXJzaW9ufS9gO1xyXG5cclxuICBsb2coMywgJ1tjYWNoZV0gVXBkYXRpbmcgY2FjaGUgdG8gSGlnaGNoYXJ0cyAnLCBoY1ZlcnNpb24pO1xyXG5cclxuICAvLyBHYXRoZXIgYWxsIHNjcmlwdHMgdG8gZmV0Y2hcclxuICBjb25zdCBhbGxTY3JpcHRzID0gW1xyXG4gICAgLi4uY29yZVNjcmlwdHMubWFwKChjKSA9PiBgJHtoY1ZlcnNpb259JHtjfWApLFxyXG4gICAgLi4ubW9kdWxlcy5tYXAoKG0pID0+XHJcbiAgICAgIG0gPT09ICdtYXAnID8gYG1hcHMvJHtoY1ZlcnNpb259bW9kdWxlcy8ke219YCA6IGAke2hjVmVyc2lvbn1tb2R1bGVzLyR7bX1gXHJcbiAgICApLFxyXG4gICAgLi4uaW5kaWNhdG9ycy5tYXAoKGkpID0+IGBzdG9jay8ke2hjVmVyc2lvbn1pbmRpY2F0b3JzLyR7aX1gKVxyXG4gIF07XHJcblxyXG4gIC8vIENvbmZpZ3VyZSBwcm94eSBpZiBleGlzdHNcclxuICBsZXQgcHJveHlBZ2VudDtcclxuICBjb25zdCBwcm94eUhvc3QgPSBwcm9jZXNzLmVudlsnUFJPWFlfU0VSVkVSX0hPU1QnXTtcclxuICBjb25zdCBwcm94eVBvcnQgPSBwcm9jZXNzLmVudlsnUFJPWFlfU0VSVkVSX1BPUlQnXTtcclxuXHJcbiAgaWYgKHByb3h5SG9zdCAmJiBwcm94eVBvcnQpIHtcclxuICAgIHByb3h5QWdlbnQgPSBuZXcgSHR0cHNQcm94eUFnZW50KHtcclxuICAgICAgaG9zdDogcHJveHlIb3N0LFxyXG4gICAgICBwb3J0OiArcHJveHlQb3J0XHJcbiAgICB9KTtcclxuICB9XHJcblxyXG4gIGNvbnN0IGZldGNoZWRNb2R1bGVzID0ge307XHJcbiAgdHJ5IHtcclxuICAgIGNhY2hlLnNvdXJjZXMgPSAvLyBUT0RPOiBjb252ZXJ0IHRvIGZvciBsb29wXHJcbiAgICAgIChcclxuICAgICAgICBhd2FpdCBQcm9taXNlLmFsbChbXHJcbiAgICAgICAgICAuLi5hbGxTY3JpcHRzLm1hcChhc3luYyAoc2NyaXB0KSA9PiB7XHJcbiAgICAgICAgICAgIGNvbnN0IHRleHQgPSBhd2FpdCBmZXRjaFNjcmlwdChcclxuICAgICAgICAgICAgICBgJHtjb25maWcuY2RuVVJMIHx8IGNhY2hlLmNkblVSTH0ke3NjcmlwdH1gLFxyXG4gICAgICAgICAgICAgIHByb3h5QWdlbnRcclxuICAgICAgICAgICAgKTtcclxuXHJcbiAgICAgICAgICAgIC8vIElmIGZldGNoZWQgY29ycmVjdGx5LCBzZXQgaXRcclxuICAgICAgICAgICAgaWYgKHR5cGVvZiB0ZXh0ID09PSAnc3RyaW5nJykge1xyXG4gICAgICAgICAgICAgIGZldGNoZWRNb2R1bGVzW1xyXG4gICAgICAgICAgICAgICAgc2NyaXB0LnJlcGxhY2UoXHJcbiAgICAgICAgICAgICAgICAgIC8oLiopXFwvfCguKiltb2R1bGVzXFwvfHN0b2NrXFwvKC4qKWluZGljYXRvcnNcXC98bWFwc1xcLyguKiltb2R1bGVzXFwvL2dpLFxyXG4gICAgICAgICAgICAgICAgICAnJ1xyXG4gICAgICAgICAgICAgICAgKVxyXG4gICAgICAgICAgICAgIF0gPSAxO1xyXG4gICAgICAgICAgICB9XHJcblxyXG4gICAgICAgICAgICByZXR1cm4gdGV4dDtcclxuICAgICAgICAgIH0pLFxyXG4gICAgICAgICAgLi4uY3VzdG9tU2NyaXB0cy5tYXAoKHNjcmlwdCkgPT4gZmV0Y2hTY3JpcHQoc2NyaXB0LCBwcm94eUFnZW50KSlcclxuICAgICAgICBdKVxyXG4gICAgICApLmpvaW4oJztcXG4nKTtcclxuICAgIGV4dHJhY3RWZXJzaW9uKCk7XHJcblxyXG4gICAgLy8gU2F2ZSB0aGUgZmV0Y2hlZCBtb2R1bGVzIGludG8gY2FjaGVzJyBzb3VyY2UgSlNPTlxyXG4gICAgd3JpdGVGaWxlU3luYyhzb3VyY2VQYXRoLCBjYWNoZS5zb3VyY2VzKTtcclxuICAgIHJldHVybiBmZXRjaGVkTW9kdWxlcztcclxuICB9IGNhdGNoIChlcnJvcikge1xyXG4gICAgbG9nKDEsICdbY2FjaGVdIFVuYWJsZSB0byB1cGRhdGUgbG9jYWwgSGlnaGNoYXJ0cyBjYWNoZS4nKTtcclxuICB9XHJcbn07XHJcblxyXG5leHBvcnQgY29uc3QgdXBkYXRlVmVyc2lvbiA9IGFzeW5jIChuZXdWZXJzaW9uKSA9PlxyXG4gIGFwcGxpZWRDb25maWdcclxuICAgID8gYXdhaXQgY2hlY2tDYWNoZShcclxuICAgICAgICBPYmplY3QuYXNzaWduKGFwcGxpZWRDb25maWcsIHtcclxuICAgICAgICAgIHZlcnNpb246IG5ld1ZlcnNpb25cclxuICAgICAgICB9KVxyXG4gICAgICApXHJcbiAgICA6IGZhbHNlO1xyXG5cclxuLyoqXHJcbiAqIEZldGNoZXMgYW55IG1pc3NpbmcgSGlnaGNoYXJ0cyBhbmQgZGVwZW5kZW5jaWVzXHJcbiAqXHJcbiAqIEBwYXJhbSB7b2JqZWN0fSBjb25maWcgLSBIaWdoY2hhcnRzIHJlbGF0ZWQgY29uZmlndXJhdGlvbiBvYmplY3QuXHJcbiAqL1xyXG5leHBvcnQgY29uc3QgY2hlY2tDYWNoZSA9IGFzeW5jIChjb25maWcpID0+IHtcclxuICBsZXQgZmV0Y2hlZE1vZHVsZXM7XHJcbiAgLy8gUHJlcGFyZSBwYXRocyB0byBtYW5pZmVzdCBhbmQgc291cmNlcyBmcm9tIHRoZSAuY2FjaGUgZm9sZGVyXHJcbiAgY29uc3QgbWFuaWZlc3RQYXRoID0gam9pbihjYWNoZVBhdGgsICdtYW5pZmVzdC5qc29uJyk7XHJcbiAgY29uc3Qgc291cmNlUGF0aCA9IGpvaW4oY2FjaGVQYXRoLCAnc291cmNlcy5qcycpO1xyXG5cclxuICAvLyBUT0RPOiBkZWFsIHdpdGggdHJ5aW5nIHRvIHN3aXRjaCB0byB0aGUgcnVubmluZyB2ZXJzaW9uXHJcbiAgLy8gY29uc3QgYWN0aXZlVmVyc2lvbiA9IGFwcGxpZWRDb25maWcgPyBhcHBsaWVkQ29uZmlnLnZlcnNpb24gOiBmYWxzZTtcclxuXHJcbiAgYXBwbGllZENvbmZpZyA9IGNvbmZpZztcclxuXHJcbiAgLy8gQ3JlYXRlIHRoZSAuY2FjaGUgZGVzdGluYXRpb24gaWYgaXQgZG9lc24ndCBleGlzdCBhbHJlYWR5XHJcbiAgIWV4aXN0c1N5bmMoY2FjaGVQYXRoKSAmJiBta2RpclN5bmMoY2FjaGVQYXRoKTtcclxuXHJcbiAgLy8gRmV0Y2ggYWxsIHRoZSBzY3JpcHRzIGVpdGhlciBpZiBtYW5pZmVzdC5qc29uIGRvZXMgbm90IGV4aXN0XHJcbiAgLy8gb3IgaWYgdGhlIGZvcmNlRmV0Y2ggb3B0aW9uIGlzIGVuYWJsZWRcclxuICBpZiAoIWV4aXN0c1N5bmMobWFuaWZlc3RQYXRoKSB8fCBjb25maWcuZm9yY2VGZXRjaCkge1xyXG4gICAgbG9nKDMsICdbY2FjaGVdIEZldGNoaW5nIGFuZCBjYWNoaW5nIEhpZ2hjaGFydHMgZGVwZW5kZW5jaWVzLicpO1xyXG4gICAgZmV0Y2hlZE1vZHVsZXMgPSBhd2FpdCB1cGRhdGVDYWNoZShjb25maWcsIHNvdXJjZVBhdGgpO1xyXG4gIH0gZWxzZSB7XHJcbiAgICBsZXQgcmVxdWVzdFVwZGF0ZSA9IGZhbHNlO1xyXG5cclxuICAgIC8vIFJlYWQgdGhlIG1hbmlmZXN0IEpTT05cclxuICAgIGNvbnN0IG1hbmlmZXN0ID0gSlNPTi5wYXJzZShyZWFkRmlsZVN5bmMobWFuaWZlc3RQYXRoKSk7XHJcblxyXG4gICAgLy8gQ2hlY2sgaWYgdGhlIG1vZHVsZXMgaXMgYW4gYXJyYXksIGlmIHNvLCB3ZSByZXdyaXRlIGl0IHRvIGEgbWFwIHRvIG1ha2VcclxuICAgIC8vIGl0IGVhc2llciB0byByZXNvbHZlIG1vZHVsZXMuXHJcbiAgICBpZiAobWFuaWZlc3QubW9kdWxlcyAmJiBBcnJheS5pc0FycmF5KG1hbmlmZXN0Lm1vZHVsZXMpKSB7XHJcbiAgICAgIGNvbnN0IG1vZHVsZU1hcCA9IHt9O1xyXG4gICAgICBtYW5pZmVzdC5tb2R1bGVzLmZvckVhY2goKG0pID0+IChtb2R1bGVNYXBbbV0gPSAxKSk7XHJcbiAgICAgIG1hbmlmZXN0Lm1vZHVsZXMgPSBtb2R1bGVNYXA7XHJcbiAgICB9XHJcblxyXG4gICAgY29uc3QgeyBtb2R1bGVzLCBjb3JlU2NyaXB0cywgaW5kaWNhdG9ycyB9ID0gY29uZmlnO1xyXG4gICAgY29uc3QgbnVtYmVyT2ZNb2R1bGVzID1cclxuICAgICAgbW9kdWxlcy5sZW5ndGggKyBjb3JlU2NyaXB0cy5sZW5ndGggKyBpbmRpY2F0b3JzLmxlbmd0aDtcclxuXHJcbiAgICAvLyBDb21wYXJlIHRoZSBsb2FkZWQgY29uZmlnIHdpdGggdGhlIGNvbnRlbnRzIGluIC5jYWNoZS5cclxuICAgIC8vIElmIHRoZXJlIGFyZSBjaGFuZ2VzLCBmZXRjaCByZXF1ZXN0ZWQgbW9kdWxlcyBhbmQgcHJvZHVjdHMsXHJcbiAgICAvLyBhbmQgYmFrZSB0aGVtIGludG8gYSBnaWFudCBibG9iLiBTYXZlIHRoZSBibG9iLlxyXG4gICAgaWYgKG1hbmlmZXN0LnZlcnNpb24gIT09IGNvbmZpZy52ZXJzaW9uKSB7XHJcbiAgICAgIGxvZygzLCAnW2NhY2hlXSBIaWdoY2hhcnRzIHZlcnNpb24gbWlzbWF0Y2ggaW4gY2FjaGUsIG5lZWQgdG8gcmUtZmV0Y2guJyk7XHJcbiAgICAgIHJlcXVlc3RVcGRhdGUgPSB0cnVlO1xyXG4gICAgfSBlbHNlIGlmIChPYmplY3Qua2V5cyhtYW5pZmVzdC5tb2R1bGVzIHx8IHt9KS5sZW5ndGggIT09IG51bWJlck9mTW9kdWxlcykge1xyXG4gICAgICBsb2coXHJcbiAgICAgICAgMyxcclxuICAgICAgICAnW2NhY2hlXSBDYWNoZSBhbmQgcmVxdWVzdGVkIG1vZHVsZXMgZG9lcyBub3QgbWF0Y2gsIG5lZWQgdG8gcmUtZmV0Y2guJ1xyXG4gICAgICApO1xyXG4gICAgICByZXF1ZXN0VXBkYXRlID0gdHJ1ZTtcclxuICAgIH0gZWxzZSB7XHJcbiAgICAgIC8vIENoZWNrIGVhY2ggbW9kdWxlLCBpZiBhbnl0aGluZyBpcyBtaXNzaW5nIHJlZmV0Y2ggZXZlcnl0aGluZ1xyXG4gICAgICByZXF1ZXN0VXBkYXRlID0gKGNvbmZpZy5tb2R1bGVzIHx8IFtdKS5zb21lKChtb2R1bGVOYW1lKSA9PiB7XHJcbiAgICAgICAgaWYgKCFtYW5pZmVzdC5tb2R1bGVzW21vZHVsZU5hbWVdKSB7XHJcbiAgICAgICAgICBsb2coXHJcbiAgICAgICAgICAgIDMsXHJcbiAgICAgICAgICAgIGBbY2FjaGVdIFRoZSAke21vZHVsZU5hbWV9IG1pc3NpbmcgaW4gY2FjaGUsIG5lZWQgdG8gcmUtZmV0Y2guYFxyXG4gICAgICAgICAgKTtcclxuICAgICAgICAgIHJldHVybiB0cnVlO1xyXG4gICAgICAgIH1cclxuICAgICAgfSk7XHJcbiAgICB9XHJcblxyXG4gICAgaWYgKHJlcXVlc3RVcGRhdGUpIHtcclxuICAgICAgZmV0Y2hlZE1vZHVsZXMgPSBhd2FpdCB1cGRhdGVDYWNoZShjb25maWcsIHNvdXJjZVBhdGgpO1xyXG4gICAgfSBlbHNlIHtcclxuICAgICAgbG9nKDMsICdbY2FjaGVdIERlcGVuZGVuY3kgY2FjaGUgaXMgdXAgdG8gZGF0ZSwgcHJvY2VlZGluZy4nKTtcclxuXHJcbiAgICAgIC8vIExvYWQgdGhlIHNvdXJjZXNcclxuICAgICAgY2FjaGUuc291cmNlcyA9IHJlYWRGaWxlU3luYyhzb3VyY2VQYXRoLCAndXRmOCcpO1xyXG5cclxuICAgICAgLy8gR2V0IGN1cnJlbnQgbW9kdWxlcyBtYXBcclxuICAgICAgZmV0Y2hlZE1vZHVsZXMgPSBtYW5pZmVzdC5tb2R1bGVzO1xyXG4gICAgICBleHRyYWN0VmVyc2lvbigpO1xyXG4gICAgfVxyXG4gIH1cclxuXHJcbiAgLy8gRmluYWxseSwgc2F2ZSB0aGUgbmV3IG1hbmlmZXN0LCB3aGljaCBpcyBiYXNpY2FsbHkgb3VyIGN1cnJlbnQgY29uZmlnXHJcbiAgLy8gaW4gYSBzbGlnaHRseSBkaWZmZXJlbnQgZm9ybWF0XHJcbiAgYXdhaXQgc2F2ZUNvbmZpZ1RvTWFuaWZlc3QoY29uZmlnLCBmZXRjaGVkTW9kdWxlcyk7XHJcbn07XHJcblxyXG5leHBvcnQgZGVmYXVsdCB7XHJcbiAgY2hlY2tDYWNoZSxcclxuICB1cGRhdGVWZXJzaW9uLFxyXG4gIGdldENhY2hlOiAoKSA9PiBjYWNoZSxcclxuICBoaWdoY2hhcnRzOiAoKSA9PiBjYWNoZS5zb3VyY2VzLFxyXG4gIHZlcnNpb246ICgpID0+IGNhY2hlLmhjVmVyc2lvblxyXG59O1xyXG4iLCIvKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKlxyXG5cclxuSGlnaGNoYXJ0cyBFeHBvcnQgU2VydmVyXHJcblxyXG5Db3B5cmlnaHQgKGMpIDIwMTYtMjAyMywgSGlnaHNvZnRcclxuXHJcbkxpY2VuY2VkIHVuZGVyIHRoZSBNSVQgbGljZW5jZS5cclxuXHJcbkFkZGl0aW9uYWxseSBhIHZhbGlkIEhpZ2hjaGFydHMgbGljZW5zZSBpcyByZXF1aXJlZCBmb3IgdXNlLlxyXG5cclxuU2VlIExJQ0VOU0UgZmlsZSBpbiByb290IGZvciBkZXRhaWxzLlxyXG5cclxuKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKi9cclxuXHJcbmltcG9ydCBwdXBwZXRlZXIgZnJvbSAncHVwcGV0ZWVyJztcclxuaW1wb3J0IGZzIGZyb20gJ2ZzJztcclxuaW1wb3J0ICogYXMgdXJsIGZyb20gJ3VybCc7XHJcbmltcG9ydCB7IGxvZyB9IGZyb20gJy4vbG9nZ2VyLmpzJztcclxuaW1wb3J0IHBhdGggZnJvbSAnbm9kZTpwYXRoJztcclxuXHJcbi8vIFdvcmthcm91bmQgZm9yIGh0dHBzOi8vYnVncy5jaHJvbWl1bS5vcmcvcC9jaHJvbWl1bS9pc3N1ZXMvZGV0YWlsP2lkPTE0NjMzMjhcclxuLy8gTm90IGlkZWFsIC0gbGVhdmVzIHRyYXNoIGluIHRoZSBGU1xyXG5pbXBvcnQgeyByYW5kb21CeXRlcyB9IGZyb20gJ25vZGU6Y3J5cHRvJztcclxuXHJcbmNvbnN0IFJBTkRPTV9QSUQgPSByYW5kb21CeXRlcyg2NCkudG9TdHJpbmcoJ2Jhc2U2NHVybCcpO1xyXG5jb25zdCBQVVBQRVRFRVJfRElSID0gcGF0aC5qb2luKCd0bXAnLCBgcHVwcGV0ZWVyLSR7UkFORE9NX1BJRH1gKTtcclxuY29uc3QgREFUQV9ESVIgPSBwYXRoLmpvaW4oUFVQUEVURUVSX0RJUiwgJ3Byb2ZpbGUnKTtcclxuXHJcbi8vIFRoZSBtaW5pbWFsIGFyZ3MgdG8gc3BlZWQgdXAgdGhlIGJyb3dzZXJcclxuY29uc3QgbWluaW1hbEFyZ3MgPSBbXHJcbiAgYC0tdXNlci1kYXRhLWRpcj0ke0RBVEFfRElSfWAsXHJcbiAgJy0tYXV0b3BsYXktcG9saWN5PXVzZXItZ2VzdHVyZS1yZXF1aXJlZCcsXHJcbiAgJy0tZGlzYWJsZS1iYWNrZ3JvdW5kLW5ldHdvcmtpbmcnLFxyXG4gICctLWRpc2FibGUtYmFja2dyb3VuZC10aW1lci10aHJvdHRsaW5nJyxcclxuICAnLS1kaXNhYmxlLWJhY2tncm91bmRpbmctb2NjbHVkZWQtd2luZG93cycsXHJcbiAgJy0tZGlzYWJsZS1icmVha3BhZCcsXHJcbiAgJy0tZGlzYWJsZS1jbGllbnQtc2lkZS1waGlzaGluZy1kZXRlY3Rpb24nLFxyXG4gICctLWRpc2FibGUtY29tcG9uZW50LXVwZGF0ZScsXHJcbiAgJy0tZGlzYWJsZS1kZWZhdWx0LWFwcHMnLFxyXG4gICctLWRpc2FibGUtZGV2LXNobS11c2FnZScsXHJcbiAgJy0tZGlzYWJsZS1kb21haW4tcmVsaWFiaWxpdHknLFxyXG4gICctLWRpc2FibGUtZXh0ZW5zaW9ucycsXHJcbiAgJy0tZGlzYWJsZS1mZWF0dXJlcz1BdWRpb1NlcnZpY2VPdXRPZlByb2Nlc3MnLFxyXG4gICctLWRpc2FibGUtaGFuZy1tb25pdG9yJyxcclxuICAnLS1kaXNhYmxlLWlwYy1mbG9vZGluZy1wcm90ZWN0aW9uJyxcclxuICAnLS1kaXNhYmxlLW5vdGlmaWNhdGlvbnMnLFxyXG4gICctLWRpc2FibGUtb2ZmZXItc3RvcmUtdW5tYXNrZWQtd2FsbGV0LWNhcmRzJyxcclxuICAnLS1kaXNhYmxlLXBvcHVwLWJsb2NraW5nJyxcclxuICAnLS1kaXNhYmxlLXByaW50LXByZXZpZXcnLFxyXG4gICctLWRpc2FibGUtcHJvbXB0LW9uLXJlcG9zdCcsXHJcbiAgJy0tZGlzYWJsZS1yZW5kZXJlci1iYWNrZ3JvdW5kaW5nJyxcclxuICAnLS1kaXNhYmxlLXNlc3Npb24tY3Jhc2hlZC1idWJibGUnLFxyXG4gICctLWRpc2FibGUtc2V0dWlkLXNhbmRib3gnLFxyXG4gICctLWRpc2FibGUtc3BlZWNoLWFwaScsXHJcbiAgJy0tZGlzYWJsZS1zeW5jJyxcclxuICAnLS1oaWRlLWNyYXNoLXJlc3RvcmUtYnViYmxlJyxcclxuICAnLS1oaWRlLXNjcm9sbGJhcnMnLFxyXG4gICctLWlnbm9yZS1ncHUtYmxhY2tsaXN0JyxcclxuICAnLS1tZXRyaWNzLXJlY29yZGluZy1vbmx5JyxcclxuICAnLS1tdXRlLWF1ZGlvJyxcclxuICAnLS1uby1kZWZhdWx0LWJyb3dzZXItY2hlY2snLFxyXG4gICctLW5vLWZpcnN0LXJ1bicsXHJcbiAgJy0tbm8tcGluZ3MnLFxyXG4gICctLW5vLXNhbmRib3gnLFxyXG4gICctLW5vLXp5Z290ZScsXHJcbiAgJy0tcGFzc3dvcmQtc3RvcmU9YmFzaWMnLFxyXG4gICctLXVzZS1tb2NrLWtleWNoYWluJ1xyXG5dO1xyXG5cclxuY29uc3QgX19kaXJuYW1lID0gdXJsLmZpbGVVUkxUb1BhdGgobmV3IFVSTCgnLicsIGltcG9ydC5tZXRhLnVybCkpO1xyXG5cclxuY29uc3QgdGVtcGxhdGUgPSBmcy5yZWFkRmlsZVN5bmMoXHJcbiAgX19kaXJuYW1lICsgJy8uLi90ZW1wbGF0ZXMvdGVtcGxhdGUuaHRtbCcsXHJcbiAgJ3V0ZjgnXHJcbik7XHJcblxyXG5sZXQgYnJvd3NlcjtcclxuXHJcbmNvbnN0IHNldFBhZ2VDb250ZW50ID0gYXN5bmMgKHBhZ2UpID0+IHtcclxuICBhd2FpdCBwYWdlLnNldENvbnRlbnQodGVtcGxhdGUpO1xyXG4gIGF3YWl0IHBhZ2UuYWRkU2NyaXB0VGFnKHsgcGF0aDogX19kaXJuYW1lICsgJy8uLi8uY2FjaGUvc291cmNlcy5qcycgfSk7XHJcbiAgLy8gZXNsaW50LWRpc2FibGUtbmV4dC1saW5lIG5vLXVuZGVmXHJcbiAgYXdhaXQgcGFnZS5ldmFsdWF0ZSgoKSA9PiB3aW5kb3cuc2V0dXBIaWdoY2hhcnRzKCkpO1xyXG5cclxuICBwYWdlLm9uKCdwYWdlZXJyb3InLCBhc3luYyAoZXJyKSA9PiB7XHJcbiAgICAvLyBUT0RPOiBDb25zaWRlciBhZGRpbmcgYSBzd2l0Y2ggaGVyZSB0aGF0IHR1cm5zIG9uIGxvZygwKSBsb2dnaW5nXHJcbiAgICAvLyBvbiBwYWdlIGVycm9ycy5cclxuICAgIGxvZygxLCAnW3BhZ2UgZXJyb3JdJywgZXJyKTtcclxuICAgIGF3YWl0IHBhZ2UuJGV2YWwoXHJcbiAgICAgICcjY29udGFpbmVyJyxcclxuICAgICAgKGVsZW1lbnQsIGVycm9yTWVzc2FnZSkgPT4ge1xyXG4gICAgICAgIC8vIGVzbGludC1kaXNhYmxlLW5leHQtbGluZSBuby11bmRlZlxyXG4gICAgICAgIGlmICh3aW5kb3cuX2Rpc3BsYXlFcnJvcnMpIHtcclxuICAgICAgICAgIGVsZW1lbnQuaW5uZXJIVE1MID0gZXJyb3JNZXNzYWdlO1xyXG4gICAgICAgIH1cclxuICAgICAgfSxcclxuICAgICAgYDxoMT5DaGFydCBpbnB1dCBkYXRhIGVycm9yPC9oMT4ke2Vyci50b1N0cmluZygpfWBcclxuICAgICk7XHJcbiAgfSk7XHJcbn07XHJcblxyXG5leHBvcnQgY29uc3QgbmV3UGFnZSA9IGFzeW5jICgpID0+IHtcclxuICBpZiAoIWJyb3dzZXIpIHJldHVybiBmYWxzZTtcclxuXHJcbiAgY29uc3QgcGFnZSA9IGF3YWl0IGJyb3dzZXIubmV3UGFnZSgpO1xyXG5cclxuICAvLyBTZXQgdGhlIGNvbnRlbnRcclxuICBhd2FpdCBzZXRQYWdlQ29udGVudChwYWdlKTtcclxuICByZXR1cm4gcGFnZTtcclxufTtcclxuXHJcbmV4cG9ydCBjb25zdCBjbGVhclBhZ2UgPSBhc3luYyAocGFnZSkgPT4ge1xyXG4gIHRyeSB7XHJcbiAgICAvLyBOYXZpZ2F0ZSB0byBhYm91dDpibGFua1xyXG4gICAgYXdhaXQgcGFnZS5nb3RvKCdhYm91dDpibGFuaycpO1xyXG5cclxuICAgIC8vIFNldCB0aGUgY29udGVudCBhbmQgYW5kIHNjcmlwdHMgYWdhaW5cclxuICAgIGF3YWl0IHNldFBhZ2VDb250ZW50KHBhZ2UpO1xyXG4gIH0gY2F0Y2ggKGVycm9yKSB7XHJcbiAgICBsb2coMywgJ1ticm93c2VyXSBDb3VsZCBub3QgY2xlYXIgcGFnZScpO1xyXG4gIH1cclxufTtcclxuXHJcbmV4cG9ydCBjb25zdCBjcmVhdGUgPSBhc3luYyAocHVwcGV0ZWVyQXJncykgPT4ge1xyXG4gIGNvbnN0IGFsbEFyZ3MgPSBbLi4ubWluaW1hbEFyZ3MsIC4uLihwdXBwZXRlZXJBcmdzIHx8IFtdKV07XHJcblxyXG4gIC8vIENyZWF0ZSBhIGJyb3dzZXJcclxuICBpZiAoIWJyb3dzZXIpIHtcclxuICAgIGxldCB0cnlDb3VudCA9IDA7XHJcblxyXG4gICAgY29uc3Qgb3BlbiA9IGFzeW5jICgpID0+IHtcclxuICAgICAgdHJ5IHtcclxuICAgICAgICBsb2coXHJcbiAgICAgICAgICAzLFxyXG4gICAgICAgICAgJ1ticm93c2VyXSBhdHRlbXB0aW5nIHRvIGdldCBhIGJyb3dzZXIgaW5zdGFuY2UgKHRyeScsXHJcbiAgICAgICAgICB0cnlDb3VudCArICcpJ1xyXG4gICAgICAgICk7XHJcblxyXG4gICAgICAgIGJyb3dzZXIgPSBhd2FpdCBwdXBwZXRlZXIubGF1bmNoKHtcclxuICAgICAgICAgIGhlYWRsZXNzOiAnbmV3JyxcclxuICAgICAgICAgIGFyZ3M6IGFsbEFyZ3MsXHJcbiAgICAgICAgICB1c2VyRGF0YURpcjogJy4vdG1wLydcclxuICAgICAgICB9KTtcclxuICAgICAgfSBjYXRjaCAoZSkge1xyXG4gICAgICAgIGxvZygwLCAnW2Jyb3dzZXJdJywgZSk7XHJcbiAgICAgICAgaWYgKCsrdHJ5Q291bnQgPCAyNSkge1xyXG4gICAgICAgICAgbG9nKDMsICdbYnJvd3Nlcl0gZmFpbGVkOicsIGUpO1xyXG4gICAgICAgICAgYXdhaXQgbmV3IFByb21pc2UoKHJlc3BvbnNlKSA9PiBzZXRUaW1lb3V0KHJlc3BvbnNlLCA0MDAwKSk7XHJcbiAgICAgICAgICBhd2FpdCBvcGVuKCk7XHJcbiAgICAgICAgfSBlbHNlIHtcclxuICAgICAgICAgIGxvZygwLCAnTWF4IHJldHJpZXMgcmVhY2hlZCcpO1xyXG4gICAgICAgIH1cclxuICAgICAgfVxyXG4gICAgfTtcclxuXHJcbiAgICB0cnkge1xyXG4gICAgICBhd2FpdCBvcGVuKCk7XHJcbiAgICB9IGNhdGNoIChlKSB7XHJcbiAgICAgIGxvZygwLCAnW2Jyb3dzZXJdIFVuYWJsZSB0byBvcGVuIGJyb3dzZXInKTtcclxuICAgICAgcmV0dXJuIGZhbHNlO1xyXG4gICAgfVxyXG5cclxuICAgIGlmICghYnJvd3Nlcikge1xyXG4gICAgICBsb2coMCwgJ1ticm93c2VyXSBVbmFibGUgdG8gb3BlbiBicm93c2VyJyk7XHJcbiAgICAgIHJldHVybiBmYWxzZTtcclxuICAgIH1cclxuICB9XHJcblxyXG4gIC8vIFJldHVybiBhIGJyb3dzZXIgcHJvbWlzZVxyXG4gIHJldHVybiBicm93c2VyO1xyXG59O1xyXG5cclxuZXhwb3J0IGNvbnN0IGdldCA9IGFzeW5jICgpID0+IHtcclxuICBpZiAoIWJyb3dzZXIpIHtcclxuICAgIHRocm93ICdObyB2YWxpZCBicm93c2VyIGhhcyBiZWVuIGNyZWF0ZWQnO1xyXG4gIH1cclxuXHJcbiAgcmV0dXJuIGJyb3dzZXI7XHJcbn07XHJcblxyXG5leHBvcnQgY29uc3QgY2xvc2UgPSBhc3luYyAoKSA9PiB7XHJcbiAgLy8gQ2xvc2UgdGhlIGJyb3dzZXIgd2hlbiBjb25ubmVjdGVkXHJcbiAgaWYgKGJyb3dzZXIuY29ubmVjdGVkKSB7XHJcbiAgICBhd2FpdCBicm93c2VyLmNsb3NlKCk7XHJcbiAgfVxyXG59O1xyXG5cclxuZXhwb3J0IGRlZmF1bHQge1xyXG4gIG5ld1BhZ2UsXHJcbiAgY2xlYXJQYWdlLFxyXG4gIGdldCxcclxuICBjbG9zZVxyXG59O1xyXG4iLCIvKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKlxyXG5cclxuSGlnaGNoYXJ0cyBFeHBvcnQgU2VydmVyXHJcblxyXG5Db3B5cmlnaHQgKGMpIDIwMTYtMjAyMywgSGlnaHNvZnRcclxuXHJcbkxpY2VuY2VkIHVuZGVyIHRoZSBNSVQgbGljZW5jZS5cclxuXHJcbkFkZGl0aW9uYWxseSBhIHZhbGlkIEhpZ2hjaGFydHMgbGljZW5zZSBpcyByZXF1aXJlZCBmb3IgdXNlLlxyXG5cclxuU2VlIExJQ0VOU0UgZmlsZSBpbiByb290IGZvciBkZXRhaWxzLlxyXG5cclxuKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKi9cclxuXHJcbi8vIFRPRE86IHJlbW92ZSB0aGlzIHRlbXAgYmVuY2htYXJrIHN0dWZmLiBJIGhhZCB0aGlzIGlkZWEgb2YgZG9pbmcgYSBnZW5lcmFsIGJlbmNobWFya2luZ1xyXG4vLyBzeXN0ZW0sIGJ1dCBpdCBhZGRzIHNvIG11Y2ggYmxvYXQgaW4gdGhlIGNvZGUgdGhhdCBpdCBzaG91bGRuJ3QgYmUgdGhlcmUuXHJcblxyXG5pbXBvcnQgYmVuY2htYXJrIGZyb20gJy4vYmVuY2htYXJrLmpzJztcclxuaW1wb3J0IGNhY2hlIGZyb20gJy4vY2FjaGUuanMnO1xyXG5pbXBvcnQgeyBsb2cgfSBmcm9tICcuL2xvZ2dlci5qcyc7XHJcbmltcG9ydCBzdmdUZW1wbGF0ZSBmcm9tICcuLy4uL3RlbXBsYXRlcy9zdmdfZXhwb3J0L3N2Z19leHBvcnQuanMnO1xyXG5cclxuaW1wb3J0IHsgcmVhZEZpbGVTeW5jIH0gZnJvbSAnZnMnO1xyXG5pbXBvcnQgcGF0aCBmcm9tICdwYXRoJztcclxuaW1wb3J0ICogYXMgdXJsIGZyb20gJ3VybCc7XHJcblxyXG5jb25zdCBfX2Jhc2VkaXIgPSB1cmwuZmlsZVVSTFRvUGF0aChuZXcgVVJMKCcuJywgaW1wb3J0Lm1ldGEudXJsKSk7XHJcblxyXG4vLyBjb25zdCBqc29uVGVtcGxhdGUgPSByZXF1aXJlKCcuLy4uL3RlbXBsYXRlcy9qc29uX2V4cG9ydC9qc29uX2V4cG9ydC5qcycpO1xyXG5cclxuLyoqXHJcbiAqIEdldHMgdGhlIGNsaXAgcmVnaW9uIGZvciB0aGUgY2hhcnQgRE9NIG5vZGUuXHJcbiAqXHJcbiAqIEBwYXJhbSB7b2JqZWN0fSBwYWdlIC0gQSBwYWdlIG9mIGEgYnJvd3NlciBpbnN0YW5jZS5cclxuICogQHJldHVybiB7b2JqZWN0fSAtIEEgY2xpcHBlZCByZWdpb24uXHJcbiAqL1xyXG5jb25zdCBnZXRDbGlwUmVnaW9uID0gKHBhZ2UpID0+XHJcbiAgcGFnZS4kZXZhbCgnI2NoYXJ0LWNvbnRhaW5lcicsIChlbGVtZW50KSA9PiB7XHJcbiAgICBjb25zdCB7IHgsIHksIHdpZHRoLCBoZWlnaHQgfSA9IGVsZW1lbnQuZ2V0Qm91bmRpbmdDbGllbnRSZWN0KCk7XHJcbiAgICByZXR1cm4ge1xyXG4gICAgICB4LFxyXG4gICAgICB5LFxyXG4gICAgICB3aWR0aCxcclxuICAgICAgaGVpZ2h0OiBNYXRoLnRydW5jKGhlaWdodCA+IDEgPyBoZWlnaHQgOiA1MDApXHJcbiAgICB9O1xyXG4gIH0pO1xyXG5cclxuLyoqXHJcbiAqIFJhc3Rlcml6ZXMgdGhlIHBhZ2UgdG8gYW4gaW1hZ2UgKFBORyBvciBKUEVHKVxyXG4gKlxyXG4gKiBAcGFyYW0ge29iamVjdH0gcGFnZSAtIEEgcGFnZSBvZiBhIGJyb3dzZXIgaW5zdGFuY2UuXHJcbiAqIEBwYXJhbSB7c3RyaW5nfSB0eXBlIC0gVGhlIHR5cGUgb2YgYSByZXN1bHQgaW1hZ2UuXHJcbiAqIEBwYXJhbSB7c3RyaW5nfSBlbmNvZGluZyAtIFRoZSB0eXBlIG9mIGVuY29kaW5nIHVzZWQuXHJcbiAqIEBwYXJhbSB7c3RyaW5nfSBjbGlwIC0gVGhlIGNsaXAgcmVnaW9uLlxyXG4gKiBAcGFyYW0ge251bWJlcn0gcmFzdGVyaXphdGlvblRpbWVvdXQgLSBUaGUgcmFzdGVyaXphdGlvbiB0aW1lb3V0IGluIG1pbGxpc2Vjb25kcy5cclxuICogQHJldHVybnMge3N0cmluZ30gLSBBIHN0cmluZyByZXByZXNlbnRhdGlvbiBvZiBhIHNjcmVlbnNob3QuXHJcbiAqL1xyXG5jb25zdCBjcmVhdGVJbWFnZSA9IGFzeW5jIChwYWdlLCB0eXBlLCBlbmNvZGluZywgY2xpcCwgcmFzdGVyaXphdGlvblRpbWVvdXQpID0+XHJcbiAgYXdhaXQgUHJvbWlzZS5yYWNlKFtcclxuICAgIHBhZ2Uuc2NyZWVuc2hvdCh7XHJcbiAgICAgIHR5cGUsXHJcbiAgICAgIGVuY29kaW5nLFxyXG4gICAgICBjbGlwLFxyXG5cclxuICAgICAgLy8gIzQ0NywgIzQ2MyAtIGFsd2F5cyByZW5kZXIgb24gYSB0cmFuc3BhcmVudCBwYWdlIGlmXHJcbiAgICAgIC8vIHRoZSBleHBlY3RlZCB0eXBlIGZvcm1hdCBpcyBQTkdcclxuICAgICAgb21pdEJhY2tncm91bmQ6IHR5cGUgPT0gJ3BuZydcclxuICAgIH0pLFxyXG4gICAgbmV3IFByb21pc2UoKHJlc29sdmUsIHJlamVjdCkgPT5cclxuICAgICAgc2V0VGltZW91dChcclxuICAgICAgICAoKSA9PiByZWplY3QobmV3IEVycm9yKCdSYXN0ZXJpemF0aW9uIHRpbWVvdXQnKSksXHJcbiAgICAgICAgcmFzdGVyaXphdGlvblRpbWVvdXQgfHwgMTUwMFxyXG4gICAgICApXHJcbiAgICApXHJcbiAgXSk7XHJcblxyXG4vKipcclxuICogVHVybnMgcGFnZSBpbnRvIGEgUERGLlxyXG4gKlxyXG4gKiBAcGFyYW0ge29iamVjdH0gcGFnZSAtIEEgcGFnZSBvZiBhIGJyb3dzZXIgaW5zdGFuY2UuXHJcbiAqIEBwYXJhbSB7bnVtYmVyfSBoZWlnaHQgLSBUaGUgaGVpZ2h0IG9mIGEgY2hhcnQuXHJcbiAqIEBwYXJhbSB7bnVtYmVyfSB3aWR0aCAtIFRoZSB3aWR0aCBvZiBhIGNoYXJ0LlxyXG4gKiBAcGFyYW0ge3N0cmluZ30gZW5jb2RpbmcgLSBUaGUgdHlwZSBvZiBlbmNvZGluZyB1c2VkLlxyXG4gKiBAcmV0dXJuIHtvYmplY3R9IC0gQSBidWZmZXIgd2l0aCBQREYgcmVwcmVzZW50YXRpb24uXHJcbiAqL1xyXG5jb25zdCBjcmVhdGVQREYgPSBhc3luYyAocGFnZSwgaGVpZ2h0LCB3aWR0aCwgZW5jb2RpbmcpID0+XHJcbiAgYXdhaXQgcGFnZS5wZGYoe1xyXG4gICAgLy8gVGhpcyB3aWxsIHJlbW92ZSBhbiBleHRyYSBlbXB0eSBwYWdlIGluIFBERiBleHBvcnRzXHJcbiAgICBoZWlnaHQ6IGhlaWdodCArIDEsXHJcbiAgICB3aWR0aCxcclxuICAgIGVuY29kaW5nXHJcbiAgfSk7XHJcblxyXG4vKipcclxuICogRXhwb3J0cyBhcyBhIFNWRy5cclxuICpcclxuICogQHBhcmFtIHtvYmplY3R9IHBhZ2UgLSBBIHBhZ2Ugb2YgYSBicm93c2VyIGluc3RhbmNlLlxyXG4gKiBAcmV0dXJuIHtvYmplY3R9IC0gVGhlIG91dGVySFRNTCBlbGVtZW50IHdpdGggdGhlIFNWRyByZXByZXNlbnRhdGlvbi5cclxuICovXHJcbmNvbnN0IGNyZWF0ZVNWRyA9IGFzeW5jIChwYWdlKSA9PlxyXG4gIGF3YWl0IHBhZ2UuJGV2YWwoXHJcbiAgICAnI2NvbnRhaW5lciBzdmc6Zmlyc3Qtb2YtdHlwZScsXHJcbiAgICAoZWxlbWVudCkgPT4gZWxlbWVudC5vdXRlckhUTUxcclxuICApO1xyXG5cclxuLyoqIExvYWQgY29uZmlnIGludG8gYSBwYWdlIGFuZCByZW5kZXIgYSBjaGFydCAqL1xyXG5jb25zdCBzZXRBc0NvbmZpZyA9IGFzeW5jIChwYWdlLCBjaGFydCwgb3B0aW9ucykgPT5cclxuICBhd2FpdCBwYWdlLmV2YWx1YXRlKFxyXG4gICAgLy8gZXNsaW50LWRpc2FibGUtbmV4dC1saW5lIG5vLXVuZGVmXHJcbiAgICAoY2hhcnQsIG9wdGlvbnMpID0+IHdpbmRvdy50cmlnZ2VyRXhwb3J0KGNoYXJ0LCBvcHRpb25zKSxcclxuICAgIGNoYXJ0LFxyXG4gICAgb3B0aW9uc1xyXG4gICk7XHJcblxyXG4vKiogTG9hZCBTVkcgaW50byBhIHBhZ2UgKi9cclxuLy8gY29uc3Qgc2V0QXNTVkcgPSBhc3luYyAocGFnZSwgc3ZnU3RyKSA9PiB0cnVlO1xyXG5cclxuLyoqXHJcbiAqIERvZXMgYW4gZXhwb3J0IGZvciBhIGdpdmVuIGJyb3dzZXIuXHJcbiAqXHJcbiAqIEBwYXJhbSB7b2JqZWN0fSBicm93c2VyIC0gQSBicm93c2VyIGluc3RhbmNlLlxyXG4gKiBAcGFyYW0ge29iamVjdH0gY2hhcnQgLSBDaGFydCdzIG9wdGlvbnMuXHJcbiAqIEBwYXJhbSB7b2JqZWN0fSBvcHRpb25zIC0gQWxsIG9wdGlvbnMgb2JqZWN0LlxyXG4gKiBAcmV0dXJuIHtvYmplY3R9IC0gVGhlIGRhdGEgcmV0dXJuZWQgZnJvbSBvbmUgb2YgdGhlIG1ldGhvZHMgZm9yIGV4cG9ydGluZ1xyXG4gKiBhIHNwZWNpZmljIHR5cGUgb2YgYW4gaW1hZ2UuXHJcbiAqL1xyXG5leHBvcnQgZGVmYXVsdCBhc3luYyAocGFnZSwgY2hhcnQsIG9wdGlvbnMpID0+IHtcclxuICAvKipcclxuICAgKiBLZWVwcyB0cmFjayBvZiBhbGwgcmVzb3VyY2VzIGFkZGVkIG9uIHRoZSBwYWdlIHdpdGggYWRkWFhYVGFnLiBldGNcclxuICAgKiBJdCdzIFZJVEFMIHRoYXQgYWxsIGFkZGVkIHJlc291cmNlcyBlbmRzIHVwIGhlcmUgc28gd2UgY2FuIGNsZWFyIHRoaW5nc1xyXG4gICAqIG91dCB3aGVuIGRvaW5nIGEgbmV3IGV4cG9ydCBpbiB0aGUgc2FtZSBwYWdlIVxyXG4gICAqL1xyXG4gIGNvbnN0IGluamVjdGVkUmVzb3VyY2VzID0gW107XHJcblxyXG4gIC8qKiBDbGVhciBvdXQgYWxsIHN0YXRlIHNldCBvbiB0aGUgcGFnZSB3aXRoIGFkZFNjcmlwdFRhZy9hZGRTdHlsZVRhZy4gKi9cclxuICBjb25zdCBjbGVhckluamVjdGVkID0gYXN5bmMgKHBhZ2UpID0+IHtcclxuICAgIGZvciAoY29uc3QgcmVzIG9mIGluamVjdGVkUmVzb3VyY2VzKSB7XHJcbiAgICAgIGF3YWl0IHJlcy5kaXNwb3NlKCk7XHJcbiAgICB9XHJcblxyXG4gICAgLy8gUmVzZXQgYWxsIENTUyBhbmQgc2NyaXB0IHRhZ3NcclxuICAgIGF3YWl0IHBhZ2UuZXZhbHVhdGUoKCkgPT4ge1xyXG4gICAgICAvLyBlc2xpbnQtZGlzYWJsZS1uZXh0LWxpbmUgbm8tdW5kZWZcclxuICAgICAgY29uc3QgWywgLi4uc2NyaXB0c1RvUmVtb3ZlXSA9IGRvY3VtZW50LmdldEVsZW1lbnRzQnlUYWdOYW1lKCdzY3JpcHQnKTtcclxuICAgICAgLy8gZXNsaW50LWRpc2FibGUtbmV4dC1saW5lIG5vLXVuZGVmXHJcbiAgICAgIGNvbnN0IFssIC4uLnN0eWxlc1RvUmVtb3ZlXSA9IGRvY3VtZW50LmdldEVsZW1lbnRzQnlUYWdOYW1lKCdzdHlsZScpO1xyXG4gICAgICAvLyBlc2xpbnQtZGlzYWJsZS1uZXh0LWxpbmUgbm8tdW5kZWZcclxuICAgICAgY29uc3QgWy4uLmxpbmtzVG9SZW1vdmVdID0gZG9jdW1lbnQuZ2V0RWxlbWVudHNCeVRhZ05hbWUoJ2xpbmsnKTtcclxuXHJcbiAgICAgIC8vIFJlbW92ZSB0YWdzXHJcbiAgICAgIGZvciAoY29uc3QgZWxlbWVudCBvZiBbXHJcbiAgICAgICAgLi4uc2NyaXB0c1RvUmVtb3ZlLFxyXG4gICAgICAgIC4uLnN0eWxlc1RvUmVtb3ZlLFxyXG4gICAgICAgIC4uLmxpbmtzVG9SZW1vdmVcclxuICAgICAgXSkge1xyXG4gICAgICAgIGVsZW1lbnQucmVtb3ZlKCk7XHJcbiAgICAgIH1cclxuICAgIH0pO1xyXG4gIH07XHJcblxyXG4gIHRyeSB7XHJcbiAgICBjb25zdCBleHBvcnRCZW5jaCA9IGJlbmNobWFyaygnUHVwcGV0ZWVyJyk7XHJcblxyXG4gICAgbG9nKDQsICdbZXhwb3J0XSBEZXRlcm1pbmluZyBleHBvcnQgcGF0aC4nKTtcclxuXHJcbiAgICBjb25zdCBleHBvcnRPcHRpb25zID0gb3B0aW9ucy5leHBvcnQ7XHJcblxyXG4gICAgLy8gRm9yY2UgYSByQUZcclxuICAgIC8vIFNlZSBodHRwczovL2dpdGh1Yi5jb20vcHVwcGV0ZWVyL3B1cHBldGVlci9pc3N1ZXMvNzUwN1xyXG4gICAgLy8gZXNsaW50LWRpc2FibGUtbmV4dC1saW5lIG5vLXVuZGVmXHJcbiAgICBhd2FpdCBwYWdlLmV2YWx1YXRlKCgpID0+IHJlcXVlc3RBbmltYXRpb25GcmFtZSgoKSA9PiB7fSkpO1xyXG5cclxuICAgIC8vIERlY2lkZSB3aGV0aGVyIGRpc3BsYXkgZXJyb3Igb3IgZGViYnVnZXIgd3JhcHBlciBhcm91bmQgaXRcclxuICAgIGNvbnN0IGRpc3BsYXlFcnJvcnMgPVxyXG4gICAgICBleHBvcnRPcHRpb25zPy5vcHRpb25zPy5jaGFydD8uZGlzcGxheUVycm9ycyAmJlxyXG4gICAgICBjYWNoZS5nZXRDYWNoZSgpLmFjdGl2ZU1hbmlmZXN0Lm1vZHVsZXMuZGVidWdnZXI7XHJcblxyXG4gICAgLy8gZXNsaW50LWRpc2FibGUtbmV4dC1saW5lIG5vLXVuZGVmXHJcbiAgICBhd2FpdCBwYWdlLmV2YWx1YXRlKChkKSA9PiAod2luZG93Ll9kaXNwbGF5RXJyb3JzID0gZCksIGRpc3BsYXlFcnJvcnMpO1xyXG5cclxuICAgIGNvbnN0IHN2Z0JlbmNoID0gYmVuY2htYXJrKCdTVkcgaGFuZGxpbmcnKTtcclxuXHJcbiAgICBsZXQgaXNTVkc7XHJcblxyXG4gICAgaWYgKFxyXG4gICAgICBjaGFydC5pbmRleE9mICYmXHJcbiAgICAgIChjaGFydC5pbmRleE9mKCc8c3ZnJykgPj0gMCB8fCBjaGFydC5pbmRleE9mKCc8P3htbCcpID49IDApXHJcbiAgICApIHtcclxuICAgICAgLy8gU1ZHIElOUFVUIEhBTkRMSU5HXHJcblxyXG4gICAgICBsb2coNCwgJ1tleHBvcnRdIFRyZWF0aW5nIGFzIFNWRy4nKTtcclxuXHJcbiAgICAgIC8vIElmIGlucHV0IGlzIGFsc28gc3ZnLCBqdXN0IHJldHVybiBpdFxyXG4gICAgICBpZiAoZXhwb3J0T3B0aW9ucy50eXBlID09PSAnc3ZnJykge1xyXG4gICAgICAgIHJldHVybiBjaGFydDtcclxuICAgICAgfVxyXG5cclxuICAgICAgaXNTVkcgPSB0cnVlO1xyXG4gICAgICBjb25zdCBzZXRQYWdlQmVuY2ggPSBiZW5jaG1hcmsoJ1NldHRpbmcgY29udGVudCcpO1xyXG4gICAgICBhd2FpdCBwYWdlLnNldENvbnRlbnQoc3ZnVGVtcGxhdGUoY2hhcnQpKTtcclxuICAgICAgc2V0UGFnZUJlbmNoKCk7XHJcbiAgICB9IGVsc2Uge1xyXG4gICAgICAvLyBKU09OIENvbmZpZyBoYW5kbGluZ1xyXG5cclxuICAgICAgbG9nKDQsICdbZXhwb3J0XSBUcmVhdGluZyBhcyBjb25maWcuJyk7XHJcblxyXG4gICAgICAvLyBOZWVkIHRvIHBlcmZvcm0gc3RyYWlnaHQgaW5qZWN0XHJcbiAgICAgIGlmIChleHBvcnRPcHRpb25zLnN0ckluaikge1xyXG4gICAgICAgIC8vIEluamVjdGlvbiBiYXNlZCBjb25maWd1cmF0aW9uIGV4cG9ydFxyXG4gICAgICAgIGNvbnN0IHNldFBhZ2VCZW5jaCA9IGJlbmNobWFyaygnU2V0dGluZyBwYWdlIGNvbnRlbnQgKGluamVjdCknKTtcclxuXHJcbiAgICAgICAgYXdhaXQgc2V0QXNDb25maWcoXHJcbiAgICAgICAgICBwYWdlLFxyXG4gICAgICAgICAge1xyXG4gICAgICAgICAgICBjaGFydDoge1xyXG4gICAgICAgICAgICAgIGhlaWdodDogZXhwb3J0T3B0aW9ucy5oZWlnaHQsXHJcbiAgICAgICAgICAgICAgd2lkdGg6IGV4cG9ydE9wdGlvbnMud2lkdGhcclxuICAgICAgICAgICAgfVxyXG4gICAgICAgICAgfSxcclxuICAgICAgICAgIG9wdGlvbnNcclxuICAgICAgICApO1xyXG5cclxuICAgICAgICBzZXRQYWdlQmVuY2goKTtcclxuICAgICAgfSBlbHNlIHtcclxuICAgICAgICAvLyBCYXNpYyBjb25maWd1cmF0aW9uIGV4cG9ydFxyXG5cclxuICAgICAgICBjaGFydC5jaGFydC5oZWlnaHQgPSBleHBvcnRPcHRpb25zLmhlaWdodDtcclxuICAgICAgICBjaGFydC5jaGFydC53aWR0aCA9IGV4cG9ydE9wdGlvbnMud2lkdGg7XHJcblxyXG4gICAgICAgIGNvbnN0IHNldENvbnRlbnRCZW5jaCA9IGJlbmNobWFyaygnU2V0dGluZyBwYWdlIGNvbnRlbnQgKGNvbmZpZyknKTtcclxuICAgICAgICBhd2FpdCBzZXRBc0NvbmZpZyhwYWdlLCBjaGFydCwgb3B0aW9ucyk7XHJcbiAgICAgICAgc2V0Q29udGVudEJlbmNoKCk7XHJcbiAgICAgIH1cclxuICAgIH1cclxuXHJcbiAgICBzdmdCZW5jaCgpO1xyXG4gICAgY29uc3QgcmVzQmVuY2ggPSBiZW5jaG1hcmsoJ0FwcGx5aW5nIHJlc291cmNlcycpO1xyXG5cclxuICAgIC8vIFVzZSByZXNvdXJjZXNcclxuICAgIGNvbnN0IHJlc291cmNlcyA9IG9wdGlvbnMuY3VzdG9tQ29kZS5yZXNvdXJjZXM7XHJcbiAgICBpZiAocmVzb3VyY2VzKSB7XHJcbiAgICAgIC8vIExvYWQgY3VzdG9tIEpTIGNvZGVcclxuICAgICAgaWYgKHJlc291cmNlcy5qcykge1xyXG4gICAgICAgIGluamVjdGVkUmVzb3VyY2VzLnB1c2goXHJcbiAgICAgICAgICBhd2FpdCBwYWdlLmFkZFNjcmlwdFRhZyh7XHJcbiAgICAgICAgICAgIGNvbnRlbnQ6IHJlc291cmNlcy5qc1xyXG4gICAgICAgICAgfSlcclxuICAgICAgICApO1xyXG4gICAgICB9XHJcblxyXG4gICAgICAvLyBMb2FkIHNjcmlwdHMgZnJvbSBhbGwgY3VzdG9tIGZpbGVzXHJcbiAgICAgIGlmIChyZXNvdXJjZXMuZmlsZXMpIHtcclxuICAgICAgICBmb3IgKGNvbnN0IGZpbGUgb2YgcmVzb3VyY2VzLmZpbGVzKSB7XHJcbiAgICAgICAgICB0cnkge1xyXG4gICAgICAgICAgICBjb25zdCBpc0xvY2FsID0gIWZpbGUuc3RhcnRzV2l0aCgnaHR0cCcpID8gdHJ1ZSA6IGZhbHNlO1xyXG5cclxuICAgICAgICAgICAgLy8gQWRkIGVhY2ggY3VzdG9tIHNjcmlwdCBmcm9tIHJlc291cmNlcycgZmlsZXNcclxuICAgICAgICAgICAgaW5qZWN0ZWRSZXNvdXJjZXMucHVzaChcclxuICAgICAgICAgICAgICBhd2FpdCBwYWdlLmFkZFNjcmlwdFRhZyhcclxuICAgICAgICAgICAgICAgIGlzTG9jYWxcclxuICAgICAgICAgICAgICAgICAgPyB7XHJcbiAgICAgICAgICAgICAgICAgICAgICBjb250ZW50OiByZWFkRmlsZVN5bmMoZmlsZSwgJ3V0ZjgnKVxyXG4gICAgICAgICAgICAgICAgICAgIH1cclxuICAgICAgICAgICAgICAgICAgOiB7XHJcbiAgICAgICAgICAgICAgICAgICAgICB1cmw6IGZpbGVcclxuICAgICAgICAgICAgICAgICAgICB9XHJcbiAgICAgICAgICAgICAgKVxyXG4gICAgICAgICAgICApO1xyXG4gICAgICAgICAgfSBjYXRjaCAobm90aWNlKSB7XHJcbiAgICAgICAgICAgIGxvZyg0LCAnW2V4cG9ydF0gSlMgZmlsZSBub3QgZm91bmQuJyk7XHJcbiAgICAgICAgICB9XHJcbiAgICAgICAgfVxyXG4gICAgICB9XHJcblxyXG4gICAgICBjb25zdCBjc3NCZW5jaCA9IGJlbmNobWFyaygnTG9hZGluZyBjc3MnKTtcclxuXHJcbiAgICAgIC8vIExvYWQgQ1NTXHJcbiAgICAgIGlmIChyZXNvdXJjZXMuY3NzKSB7XHJcbiAgICAgICAgbGV0IGNzc0ltcG9ydHMgPSByZXNvdXJjZXMuY3NzLm1hdGNoKC9AaW1wb3J0XFxzKihbXjtdKik7L2cpO1xyXG4gICAgICAgIGlmIChjc3NJbXBvcnRzKSB7XHJcbiAgICAgICAgICAvLyBIYW5kbGUgY3NzIHNlY3Rpb25cclxuICAgICAgICAgIGZvciAobGV0IGNzc0ltcG9ydFBhdGggb2YgY3NzSW1wb3J0cykge1xyXG4gICAgICAgICAgICBpZiAoY3NzSW1wb3J0UGF0aCkge1xyXG4gICAgICAgICAgICAgIGNzc0ltcG9ydFBhdGggPSBjc3NJbXBvcnRQYXRoXHJcbiAgICAgICAgICAgICAgICAucmVwbGFjZSgndXJsKCcsICcnKVxyXG4gICAgICAgICAgICAgICAgLnJlcGxhY2UoJ0BpbXBvcnQnLCAnJylcclxuICAgICAgICAgICAgICAgIC5yZXBsYWNlKC9cIi9nLCAnJylcclxuICAgICAgICAgICAgICAgIC5yZXBsYWNlKC8nL2csICcnKVxyXG4gICAgICAgICAgICAgICAgLnJlcGxhY2UoLzsvLCAnJylcclxuICAgICAgICAgICAgICAgIC5yZXBsYWNlKC9cXCkvZywgJycpXHJcbiAgICAgICAgICAgICAgICAudHJpbSgpO1xyXG5cclxuICAgICAgICAgICAgICAvLyBBZGQgZWFjaCBjdXN0b20gY3NzIGZyb20gcmVzb3VyY2VzXHJcbiAgICAgICAgICAgICAgaWYgKGNzc0ltcG9ydFBhdGguc3RhcnRzV2l0aCgnaHR0cCcpKSB7XHJcbiAgICAgICAgICAgICAgICBpbmplY3RlZFJlc291cmNlcy5wdXNoKFxyXG4gICAgICAgICAgICAgICAgICBhd2FpdCBwYWdlLmFkZFN0eWxlVGFnKHtcclxuICAgICAgICAgICAgICAgICAgICB1cmw6IGNzc0ltcG9ydFBhdGhcclxuICAgICAgICAgICAgICAgICAgfSlcclxuICAgICAgICAgICAgICAgICk7XHJcbiAgICAgICAgICAgICAgfSBlbHNlIGlmIChvcHRpb25zLmN1c3RvbUNvZGUuYWxsb3dGaWxlUmVzb3VyY2VzKSB7XHJcbiAgICAgICAgICAgICAgICBpbmplY3RlZFJlc291cmNlcy5wdXNoKFxyXG4gICAgICAgICAgICAgICAgICBhd2FpdCBwYWdlLmFkZFN0eWxlVGFnKHtcclxuICAgICAgICAgICAgICAgICAgICBwYXRoOiBwYXRoLmpvaW4oX19iYXNlZGlyLCBjc3NJbXBvcnRQYXRoKVxyXG4gICAgICAgICAgICAgICAgICB9KVxyXG4gICAgICAgICAgICAgICAgKTtcclxuICAgICAgICAgICAgICB9XHJcbiAgICAgICAgICAgIH1cclxuICAgICAgICAgIH1cclxuICAgICAgICB9XHJcblxyXG4gICAgICAgIC8vIFRoZSByZXN0IG9mIHRoZSBDU1Mgc2VjdGlvbiB3aWxsIGJlIGNvbnRlbnQgYnkgbm93XHJcbiAgICAgICAgaW5qZWN0ZWRSZXNvdXJjZXMucHVzaChcclxuICAgICAgICAgIGF3YWl0IHBhZ2UuYWRkU3R5bGVUYWcoe1xyXG4gICAgICAgICAgICBjb250ZW50OiByZXNvdXJjZXMuY3NzLnJlcGxhY2UoL0BpbXBvcnRcXHMqKFteO10qKTsvZywgJycpIHx8ICcgJ1xyXG4gICAgICAgICAgfSlcclxuICAgICAgICApO1xyXG4gICAgICB9XHJcblxyXG4gICAgICBjc3NCZW5jaCgpO1xyXG4gICAgfVxyXG5cclxuICAgIHJlc0JlbmNoKCk7XHJcblxyXG4gICAgLy8gR2V0IHRoZSByZWFsIGNoYXJ0IHNpemVcclxuICAgIGNvbnN0IHNpemUgPSBpc1NWR1xyXG4gICAgICA/IGF3YWl0IHBhZ2UuJGV2YWwoXHJcbiAgICAgICAgICAnI2NoYXJ0LWNvbnRhaW5lciBzdmc6Zmlyc3Qtb2YtdHlwZScsXHJcbiAgICAgICAgICBhc3luYyAoZWxlbWVudCwgc2NhbGUpID0+IHtcclxuICAgICAgICAgICAgcmV0dXJuIHtcclxuICAgICAgICAgICAgICBjaGFydEhlaWdodDogZWxlbWVudC5oZWlnaHQuYmFzZVZhbC52YWx1ZSAqIHNjYWxlLFxyXG4gICAgICAgICAgICAgIGNoYXJ0V2lkdGg6IGVsZW1lbnQud2lkdGguYmFzZVZhbC52YWx1ZSAqIHNjYWxlXHJcbiAgICAgICAgICAgIH07XHJcbiAgICAgICAgICB9LFxyXG4gICAgICAgICAgcGFyc2VGbG9hdChleHBvcnRPcHRpb25zLnNjYWxlKVxyXG4gICAgICAgIClcclxuICAgICAgOiBhd2FpdCBwYWdlLmV2YWx1YXRlKGFzeW5jICgpID0+IHtcclxuICAgICAgICAgIC8vIGVzbGludC1kaXNhYmxlLW5leHQtbGluZSBuby11bmRlZlxyXG4gICAgICAgICAgY29uc3QgeyBjaGFydEhlaWdodCwgY2hhcnRXaWR0aCB9ID0gd2luZG93LkhpZ2hjaGFydHMuY2hhcnRzWzBdO1xyXG4gICAgICAgICAgcmV0dXJuIHtcclxuICAgICAgICAgICAgY2hhcnRIZWlnaHQsXHJcbiAgICAgICAgICAgIGNoYXJ0V2lkdGhcclxuICAgICAgICAgIH07XHJcbiAgICAgICAgfSk7XHJcblxyXG4gICAgY29uc3QgdnBCZW5jaCA9IGJlbmNobWFyaygnU2V0dGluZyB2aWV3cG9ydCcpO1xyXG5cclxuICAgIC8vIFNldCBmaW5hbCBoZWlnaHQgYW5kIHdpZHRoIGZvciB2aWV3cG9ydFxyXG4gICAgY29uc3Qgdmlld3BvcnRIZWlnaHQgPSBNYXRoLmNlaWwoc2l6ZT8uY2hhcnRIZWlnaHQgfHwgZXhwb3J0T3B0aW9ucy5oZWlnaHQpO1xyXG4gICAgY29uc3Qgdmlld3BvcnRXaWR0aCA9IE1hdGguY2VpbChzaXplPy5jaGFydFdpZHRoIHx8IGV4cG9ydE9wdGlvbnMud2lkdGgpO1xyXG5cclxuICAgIC8vIFNldCB0aGUgdmlld3BvcnQgZm9yIHRoZSBmaXJzdCB0aW1lXHJcbiAgICAvLyBOT1RFOiB0aGUgY2FsbCB0byBzZXRWaWV3cG9ydCBpcyBleHBlbnNpdmUgLSBjYW4gd2UgZ2V0IGF3YXkgd2l0aCBvbmx5XHJcbiAgICAvLyBjYWxsaW5nIGl0IG9uY2UsIGUuZy4gbW92aW5nIHRoaXMgb25lIGludG8gdGhlIGlzU1ZHIGNvbmRpdGlvbiBiZWxvdz9cclxuICAgIGF3YWl0IHBhZ2Uuc2V0Vmlld3BvcnQoe1xyXG4gICAgICBoZWlnaHQ6IHZpZXdwb3J0SGVpZ2h0LFxyXG4gICAgICB3aWR0aDogdmlld3BvcnRXaWR0aCxcclxuICAgICAgZGV2aWNlU2NhbGVGYWN0b3I6IGlzU1ZHID8gMSA6IHBhcnNlRmxvYXQoZXhwb3J0T3B0aW9ucy5zY2FsZSlcclxuICAgIH0pO1xyXG5cclxuICAgIC8vIFByZXBhcmUgYSB6b29tIGNhbGxiYWNrIGZvciB0aGUgbmV4dCBldmFsdWF0ZSBjYWxsXHJcbiAgICBjb25zdCB6b29tQ2FsbGJhY2sgPSBpc1NWR1xyXG4gICAgICA/IC8vIEluIGNhc2Ugb2YgU1ZHIHRoZSB6b29tIG11c3QgYmUgc2V0IGRpcmVjdGx5IGZvciBib2R5XHJcbiAgICAgICAgKHNjYWxlKSA9PiB7XHJcbiAgICAgICAgICAvLyBTZXQgdGhlIHpvb20gYXMgc2NhbGVcclxuICAgICAgICAgIC8vIGVzbGludC1kaXNhYmxlLW5leHQtbGluZSBuby11bmRlZlxyXG4gICAgICAgICAgZG9jdW1lbnQuYm9keS5zdHlsZS56b29tID0gc2NhbGU7XHJcblxyXG4gICAgICAgICAgLy8gU2V0IHRoZSBtYXJnaW4gdG8gMHB4XHJcbiAgICAgICAgICAvLyBlc2xpbnQtZGlzYWJsZS1uZXh0LWxpbmUgbm8tdW5kZWZcclxuICAgICAgICAgIGRvY3VtZW50LmJvZHkuc3R5bGUubWFyZ2luID0gJzBweCc7XHJcbiAgICAgICAgfVxyXG4gICAgICA6IC8vIE5vIG5lZWQgZm9yIHN1Y2ggc2NhbGUgbWFuaXB1bGF0aW9uIGluIGNhc2Ugb2Ygb3RoZXIgdHlwZXMgb2YgZXhwb3J0c1xyXG4gICAgICAgICgpID0+IHtcclxuICAgICAgICAgIC8vIFJlc2V0IHRoZSB6b29tIGZvciBvdGhlciBleHBvcnRzIHRoYW4gdG8gU1ZHc1xyXG4gICAgICAgICAgLy8gZXNsaW50LWRpc2FibGUtbmV4dC1saW5lIG5vLXVuZGVmXHJcbiAgICAgICAgICBkb2N1bWVudC5ib2R5LnN0eWxlLnpvb20gPSAxO1xyXG4gICAgICAgIH07XHJcblxyXG4gICAgLy8gU2V0IHRoZSB6b29tIGFjY29yZGluZ2x5XHJcbiAgICBhd2FpdCBwYWdlLmV2YWx1YXRlKHpvb21DYWxsYmFjaywgcGFyc2VGbG9hdChleHBvcnRPcHRpb25zLnNjYWxlKSk7XHJcblxyXG4gICAgLy8gR2V0IHRoZSBjbGlwIHJlZ2lvbiBmb3IgdGhlIHBhZ2VcclxuICAgIGNvbnN0IHsgaGVpZ2h0LCB3aWR0aCwgeCwgeSB9ID0gYXdhaXQgZ2V0Q2xpcFJlZ2lvbihwYWdlKTtcclxuXHJcbiAgICBpZiAoIWlzU1ZHKSB7XHJcbiAgICAgIC8vIFNldCB0aGUgZmluYWwgdmlld3BvcnQgbm93IHRoYXQgd2UgaGF2ZSB0aGUgcmVhbCBoZWlnaHRcclxuICAgICAgYXdhaXQgcGFnZS5zZXRWaWV3cG9ydCh7XHJcbiAgICAgICAgd2lkdGg6IE1hdGgucm91bmQod2lkdGgpLFxyXG4gICAgICAgIGhlaWdodDogTWF0aC5yb3VuZChoZWlnaHQpLFxyXG4gICAgICAgIGRldmljZVNjYWxlRmFjdG9yOiBwYXJzZUZsb2F0KGV4cG9ydE9wdGlvbnMuc2NhbGUpXHJcbiAgICAgIH0pO1xyXG4gICAgfVxyXG5cclxuICAgIHZwQmVuY2goKTtcclxuXHJcbiAgICBsZXQgZGF0YTtcclxuXHJcbiAgICBjb25zdCBleHBCZW5jaG1hcmsgPSBiZW5jaG1hcmsoJ1Jhc3Rlcml6aW5nIGNoYXJ0Jyk7XHJcblxyXG4gICAgLy8gUkFTVEVSSVpBVElPTlxyXG4gICAgaWYgKGV4cG9ydE9wdGlvbnMudHlwZSA9PT0gJ3N2ZycpIHtcclxuICAgICAgLy8gU1ZHXHJcbiAgICAgIGRhdGEgPSBhd2FpdCBjcmVhdGVTVkcocGFnZSk7XHJcbiAgICB9IGVsc2UgaWYgKGV4cG9ydE9wdGlvbnMudHlwZSA9PT0gJ3BuZycgfHwgZXhwb3J0T3B0aW9ucy50eXBlID09PSAnanBlZycpIHtcclxuICAgICAgLy8gUE5HIG9yIEpQRUdcclxuICAgICAgZGF0YSA9IGF3YWl0IGNyZWF0ZUltYWdlKFxyXG4gICAgICAgIHBhZ2UsXHJcbiAgICAgICAgZXhwb3J0T3B0aW9ucy50eXBlLFxyXG4gICAgICAgICdiYXNlNjQnLFxyXG4gICAgICAgIHtcclxuICAgICAgICAgIHdpZHRoOiB2aWV3cG9ydFdpZHRoLFxyXG4gICAgICAgICAgaGVpZ2h0OiB2aWV3cG9ydEhlaWdodCxcclxuICAgICAgICAgIHgsXHJcbiAgICAgICAgICB5XHJcbiAgICAgICAgfSxcclxuICAgICAgICBvcHRpb25zLnBvb2wucmFzdGVyaXphdGlvblRpbWVvdXRcclxuICAgICAgKTtcclxuICAgIH0gZWxzZSBpZiAoZXhwb3J0T3B0aW9ucy50eXBlID09PSAncGRmJykge1xyXG4gICAgICAvLyBQREZcclxuICAgICAgZGF0YSA9IGF3YWl0IGNyZWF0ZVBERihwYWdlLCB2aWV3cG9ydEhlaWdodCwgdmlld3BvcnRXaWR0aCwgJ2Jhc2U2NCcpO1xyXG4gICAgfSBlbHNlIHtcclxuICAgICAgdGhyb3cgYFVuc3VwcG9ydGVkIG91dHB1dCBmb3JtYXQgJHtleHBvcnRPcHRpb25zLnR5cGV9YDtcclxuICAgIH1cclxuXHJcbiAgICAvLyBEZXN0cm95IG9sZCBjaGFydHMgYWZ0ZXIgdGhlIGV4cG9ydCBpcyBkb25lXHJcbiAgICBhd2FpdCBwYWdlLmV2YWx1YXRlKCgpID0+IHtcclxuICAgICAgLy8gZXNsaW50LWRpc2FibGUtbmV4dC1saW5lIG5vLXVuZGVmXHJcbiAgICAgIGNvbnN0IG9sZENoYXJ0cyA9IEhpZ2hjaGFydHMuY2hhcnRzO1xyXG5cclxuICAgICAgLy8gQ2hlY2sgaW4gYW55IGFscmVhZHkgZXhpc3RpbmcgY2hhcnRzXHJcbiAgICAgIGlmIChvbGRDaGFydHMubGVuZ3RoKSB7XHJcbiAgICAgICAgLy8gRGVzdHJveSBvbGQgY2hhcnRzXHJcbiAgICAgICAgZm9yIChjb25zdCBvbGRDaGFydCBvZiBvbGRDaGFydHMpIHtcclxuICAgICAgICAgIG9sZENoYXJ0ICYmIG9sZENoYXJ0LmRlc3Ryb3koKTtcclxuICAgICAgICAgIC8vIGVzbGludC1kaXNhYmxlLW5leHQtbGluZSBuby11bmRlZlxyXG4gICAgICAgICAgSGlnaGNoYXJ0cy5jaGFydHMuc2hpZnQoKTtcclxuICAgICAgICB9XHJcbiAgICAgIH1cclxuICAgIH0pO1xyXG5cclxuICAgIGV4cEJlbmNobWFyaygpO1xyXG4gICAgZXhwb3J0QmVuY2goKTtcclxuXHJcbiAgICBhd2FpdCBjbGVhckluamVjdGVkKHBhZ2UpO1xyXG5cclxuICAgIHJldHVybiBkYXRhO1xyXG4gIH0gY2F0Y2ggKGVycm9yKSB7XHJcbiAgICBhd2FpdCBjbGVhckluamVjdGVkKHBhZ2UpO1xyXG4gICAgbG9nKDEsIGBbZXhwb3J0XSBFcnJvciBlbmNvdW50ZXJlZCBkdXJpbmcgZXhwb3J0OiAke2Vycm9yfWApO1xyXG5cclxuICAgIHJldHVybiBlcnJvcjtcclxuICB9XHJcbn07XHJcbiIsIi8qKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqXHJcblxyXG5IaWdoY2hhcnRzIEV4cG9ydCBTZXJ2ZXJcclxuXHJcbkNvcHlyaWdodCAoYykgMjAxNi0yMDIyLCBIaWdoc29mdFxyXG5cclxuTGljZW5jZWQgdW5kZXIgdGhlIE1JVCBsaWNlbmNlLlxyXG5cclxuQWRkaXRpb25hbGx5IGEgdmFsaWQgSGlnaGNoYXJ0cyBsaWNlbnNlIGlzIHJlcXVpcmVkIGZvciB1c2UuXHJcblxyXG5TZWUgTElDRU5TRSBmaWxlIGluIHJvb3QgZm9yIGRldGFpbHMuXHJcblxyXG4qKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqL1xyXG5cclxuaW1wb3J0IHsgbG9nIH0gZnJvbSAnLi9sb2dnZXIuanMnO1xyXG5jb25zdCB0aW1lcnMgPSB7fTtcclxuXHJcbi8vIFRPRE86IFJlYWQgZnJvbSBjb25maWdcclxubGV0IGVuYWJsZWQgPSBmYWxzZTtcclxuXHJcbmV4cG9ydCBkZWZhdWx0IChpZCkgPT4ge1xyXG4gIGlmICghZW5hYmxlZCkge1xyXG4gICAgcmV0dXJuICgpID0+IHt9O1xyXG4gIH1cclxuXHJcbiAgdGltZXJzW2lkXSA9IG5ldyBEYXRlKCk7XHJcbiAgcmV0dXJuICgpID0+IHtcclxuICAgIGxvZyhcclxuICAgICAgMyxcclxuICAgICAgYFtiZW5jaG1hcmtdIC0gJHtpZH06ICR7bmV3IERhdGUoKS5nZXRUaW1lKCkgLSB0aW1lcnNbaWRdLmdldFRpbWUoKX1tc2BcclxuICAgICk7XHJcbiAgfTtcclxufTtcclxuIiwiLyoqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKipcclxuXHJcbkhpZ2hjaGFydHMgRXhwb3J0IFNlcnZlclxyXG5cclxuQ29weXJpZ2h0IChjKSAyMDE2LTIwMjMsIEhpZ2hzb2Z0XHJcblxyXG5MaWNlbmNlZCB1bmRlciB0aGUgTUlUIGxpY2VuY2UuXHJcblxyXG5BZGRpdGlvbmFsbHkgYSB2YWxpZCBIaWdoY2hhcnRzIGxpY2Vuc2UgaXMgcmVxdWlyZWQgZm9yIHVzZS5cclxuXHJcblNlZSBMSUNFTlNFIGZpbGUgaW4gcm9vdCBmb3IgZGV0YWlscy5cclxuXHJcbioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKiovXHJcblxyXG5pbXBvcnQgY3NzVGVtcGxhdGUgZnJvbSAnLi9jc3MuanMnO1xyXG5cclxuZXhwb3J0IGRlZmF1bHQgKGNoYXJ0KSA9PiBgXHJcbjwhRE9DVFlQRSBodG1sPlxyXG48aHRtbCBsYW5nPSdlbi1VUyc+XHJcbiAgPGhlYWQ+XHJcbiAgICA8bWV0YSBodHRwLWVxdWl2PVwiQ29udGVudC1UeXBlXCIgY29udGVudD1cInRleHQvaHRtbDsgY2hhcnNldD11dGYtOFwiPlxyXG4gICAgPHRpdGxlPkhpZ2hjYXJ0cyBFeHBvcnQ8L3RpdGxlPlxyXG4gIDwvaGVhZD5cclxuICA8c3R5bGU+XHJcbiAgICAke2Nzc1RlbXBsYXRlKCl9XHJcbiAgPC9zdHlsZT5cclxuICA8Ym9keT5cclxuICAgIDxkaXYgaWQ9XCJjaGFydC1jb250YWluZXJcIj5cclxuICAgICAgJHtjaGFydH1cclxuICAgIDwvZGl2PlxyXG4gIDwvYm9keT5cclxuPC9odG1sPlxyXG5cclxuYDtcclxuIiwiLyoqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKipcclxuXHJcbkhpZ2hjaGFydHMgRXhwb3J0IFNlcnZlclxyXG5cclxuQ29weXJpZ2h0IChjKSAyMDE2LTIwMjMsIEhpZ2hzb2Z0XHJcblxyXG5MaWNlbmNlZCB1bmRlciB0aGUgTUlUIGxpY2VuY2UuXHJcblxyXG5BZGRpdGlvbmFsbHkgYSB2YWxpZCBIaWdoY2hhcnRzIGxpY2Vuc2UgaXMgcmVxdWlyZWQgZm9yIHVzZS5cclxuXHJcblNlZSBMSUNFTlNFIGZpbGUgaW4gcm9vdCBmb3IgZGV0YWlscy5cclxuXHJcbioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKiovXHJcblxyXG5pbXBvcnQgeyB2NCBhcyB1dWlkIH0gZnJvbSAndXVpZCc7XHJcbmltcG9ydCB7IFBvb2wgfSBmcm9tICd0YXJuJztcclxuaW1wb3J0IHtcclxuICBjbG9zZSxcclxuICBjcmVhdGUgYXMgY3JlYXRlQnJvd3NlcixcclxuICBuZXdQYWdlIGFzIGJyb3dzZXJOZXdQYWdlLFxyXG4gIGNsZWFyUGFnZVxyXG59IGZyb20gJy4vYnJvd3Nlci5qcyc7XHJcbmltcG9ydCB7IGxvZyB9IGZyb20gJy4vbG9nZ2VyLmpzJztcclxuXHJcbmltcG9ydCBwdXBwZXRlZXJFeHBvcnQgZnJvbSAnLi9leHBvcnQuanMnO1xyXG5cclxubGV0IHBlcmZvcm1lZEV4cG9ydHMgPSAwO1xyXG5sZXQgZXhwb3J0QXR0ZW1wdHMgPSAwO1xyXG5sZXQgdGltZVNwZW50ID0gMDtcclxubGV0IGRyb3BwZWRFeHBvcnRzID0gMDtcclxubGV0IHNwZW50QXZlcmFnZSA9IDA7XHJcbmxldCBwb29sQ29uZmlnID0ge307XHJcblxyXG4vLyBUaGUgcG9vbCBpbnN0YW5jZVxyXG5sZXQgcG9vbCA9IGZhbHNlO1xyXG5cclxuLy8gQ3VzdG9tIHB1cHBldGVlciBhcmd1bWVudHNcclxubGV0IHB1cHBldGVlckFyZ3M7XHJcblxyXG5jb25zdCBmYWN0b3J5ID0ge1xyXG4gIC8qKlxyXG4gICAqIENyZWF0ZXMgYSBuZXcgd29ya2VyLlxyXG4gICAqXHJcbiAgICogQHJldHVybiB7b2JqZWN0fSAtIEFuIG9iamVjdCB3aXRoIHRoZSBpZCBvZiBhIHJlc291cmNlLCB0aGUgd29yayBjb3VudCBhbmRcclxuICAgKiBhIHJlZmVyZW5jZSB0byB0aGUgYnJvd3NlciBwYWdlLlxyXG4gICAqL1xyXG4gIGNyZWF0ZTogYXN5bmMgKCkgPT4ge1xyXG4gICAgY29uc3QgaWQgPSB1dWlkKCk7XHJcbiAgICBsZXQgcGFnZSA9IGZhbHNlO1xyXG5cclxuICAgIGNvbnN0IHMgPSBuZXcgRGF0ZSgpLmdldFRpbWUoKTtcclxuXHJcbiAgICB0cnkge1xyXG4gICAgICBwYWdlID0gYXdhaXQgYnJvd3Nlck5ld1BhZ2UoKTtcclxuXHJcbiAgICAgIGlmICghcGFnZSB8fCBwYWdlLmlzQ2xvc2VkKCkpIHtcclxuICAgICAgICB0aHJvdyAnW3Bvb2xdIEludmFsaWQgcGFnZSc7XHJcbiAgICAgIH1cclxuXHJcbiAgICAgIGxvZyhcclxuICAgICAgICAzLFxyXG4gICAgICAgIGBbcG9vbF0gU3VjY2Vzc2Z1bGx5IGNyZWF0ZWQgYSB3b3JrZXIgJHtpZH0gLSB0b29rICR7XHJcbiAgICAgICAgICBuZXcgRGF0ZSgpLmdldFRpbWUoKSAtIHNcclxuICAgICAgICB9IG1zLmBcclxuICAgICAgKTtcclxuICAgIH0gY2F0Y2ggKGVycm9yKSB7XHJcbiAgICAgIGxvZyhcclxuICAgICAgICAxLFxyXG4gICAgICAgIGBbcG9vbF0gRXJyb3IgY3JlYXRpbmcgYSBuZXcgcGFnZSBpbiBwb29sIGVudHJ5IGNyZWF0aW9uISAke2Vycm9yfWBcclxuICAgICAgKTtcclxuXHJcbiAgICAgIHRocm93ICdFcnJvciBjcmVhdGluZyBwYWdlJztcclxuICAgIH1cclxuXHJcbiAgICByZXR1cm4ge1xyXG4gICAgICBpZCxcclxuICAgICAgcGFnZSxcclxuICAgICAgLy8gVHJ5IHRvIGRpc3RyaWJ1dGUgdGhlIGluaXRpYWwgd29yayBjb3VudFxyXG4gICAgICB3b3JrQ291bnQ6IE1hdGgucm91bmQoTWF0aC5yYW5kb20oKSAqIChwb29sQ29uZmlnLndvcmtMaW1pdCAvIDIpKVxyXG4gICAgfTtcclxuICB9LFxyXG5cclxuICAvKipcclxuICAgKiBWYWxpZGF0ZXMgYSB3b3JrZXIuXHJcbiAgICpcclxuICAgKiBAcGFyYW0ge29iamVjdH0gd29ya2VySGFuZGxlIC0gQSBicm93c2VyJ3MgaW5zdGFuY2UuXHJcbiAgICpcclxuICAgKiBAcmV0dXJuIHtib29sZWFufSAtIEJvb2wgdGhhdCBpbmRpY2F0ZXMgaWYgYSByZXNvdXJjZSBpcyB2YWxpZCBvciBub3QuXHJcbiAgICovXHJcbiAgdmFsaWRhdGU6IGFzeW5jICh3b3JrZXJIYW5kbGUpID0+IHtcclxuICAgIGlmIChcclxuICAgICAgcG9vbENvbmZpZy53b3JrTGltaXQgJiZcclxuICAgICAgKyt3b3JrZXJIYW5kbGUud29ya0NvdW50ID4gcG9vbENvbmZpZy53b3JrTGltaXRcclxuICAgICkge1xyXG4gICAgICBsb2coXHJcbiAgICAgICAgMyxcclxuICAgICAgICBgW3Bvb2xdIFdvcmtlciBmYWlsZWQgdmFsaWRhdGlvbjpgLFxyXG4gICAgICAgIGBleGNlZWRlZCB3b3JrIGxpbWl0IChsaW1pdCBpcyAke3Bvb2xDb25maWcud29ya0xpbWl0fSlgXHJcbiAgICAgICk7XHJcbiAgICAgIHJldHVybiBmYWxzZTtcclxuICAgIH1cclxuXHJcbiAgICAvLyBDbGVhciBwYWdlXHJcbiAgICBhd2FpdCBjbGVhclBhZ2Uod29ya2VySGFuZGxlLnBhZ2UpO1xyXG4gICAgcmV0dXJuIHRydWU7XHJcbiAgfSxcclxuXHJcbiAgLyoqXHJcbiAgICogRGVzdHJveXMgYSB3b3JrZXIuXHJcbiAgICpcclxuICAgKiBAcGFyYW0ge29iamVjdH0gd29ya2VySGFuZGxlIC0gQSBicm93c2VyJ3MgaW5zdGFuY2UuXHJcbiAgICovXHJcbiAgZGVzdHJveTogKHdvcmtlckhhbmRsZSkgPT4ge1xyXG4gICAgbG9nKDMsIGBbcG9vbF0gRGVzdHJveWluZyBwb29sIGVudHJ5ICR7d29ya2VySGFuZGxlLmlkfS5gKTtcclxuXHJcbiAgICBpZiAod29ya2VySGFuZGxlLnBhZ2UpIHtcclxuICAgICAgLy8gV2UgZG9uJ3QgcmVhbGx5IG5lZWQgdG8gd2FpdCBhcm91bmQgZm9yIHRoaXMuXHJcbiAgICAgIHdvcmtlckhhbmRsZS5wYWdlLmNsb3NlKCk7XHJcbiAgICB9XHJcbiAgfSxcclxuXHJcbiAgLy8gTG9nZ2VyIGZ1bmN0aW9uXHJcbiAgbG9nOiAobWVzc2FnZSwgbG9nTGV2ZWwpID0+IGNvbnNvbGUubG9nKGAke2xvZ0xldmVsfTogJHttZXNzYWdlfWApXHJcbn07XHJcblxyXG4vKipcclxuICogSW5pdHMgdGhlIHBvb2wgb2YgcmVzb3VyY2VzLlxyXG4gKlxyXG4gKiBAcGFyYW0ge29iamVjdH0gY29uZmlnIC0gUG9vbCBjb25maWd1cmF0aW9uIGFsb25nIHdpdGggY3VzdG9tIHB1cHBldGVlclxyXG4gKiBhcmd1bWVudHMgZm9yIHRoZSBwdXBwZXRlZXIubGF1bmNoIGZ1bmN0aW9uLlxyXG4gKi9cclxuZXhwb3J0IGNvbnN0IGluaXQgPSBhc3luYyAoY29uZmlnKSA9PiB7XHJcbiAgLy8gVGhlIG5ld2VzdCBwdXBwZXRlZXIgYXJndW1lbnRzIGZvciB0aGUgYnJvd3NlciBjcmVhdGlvblxyXG4gIHB1cHBldGVlckFyZ3MgPSBjb25maWcucHVwcGV0ZWVyQXJncztcclxuXHJcbiAgLy8gV2FpdCB1bnRpbCB3ZSd2ZSBzdWNlc3NmdWxseSBjcmVhdGVkIGEgYnJvd3NlciBpbnN0YW5jZS5cclxuICB0cnkge1xyXG4gICAgYXdhaXQgY3JlYXRlQnJvd3NlcihwdXBwZXRlZXJBcmdzKTtcclxuICB9IGNhdGNoIChlKSB7XHJcbiAgICBsb2coMCwgJ1twb29sfGJyb3dzZXJdJywgZSk7XHJcbiAgfVxyXG5cclxuICAvLyBGb3IgdGhlIG1vZHVsZSBzY29wZSB1c2FnZVxyXG4gIHBvb2xDb25maWcgPSBjb25maWcgJiYgY29uZmlnLnBvb2wgPyB7IC4uLmNvbmZpZy5wb29sIH0gOiB7fTtcclxuXHJcbiAgbG9nKFxyXG4gICAgMyxcclxuICAgICdbcG9vbF0gSW5pdGlhbGl6aW5nIHBvb2w6JyxcclxuICAgIGBtaW4gJHtwb29sQ29uZmlnLm1pbldvcmtlcnN9LCBtYXggJHtwb29sQ29uZmlnLm1heFdvcmtlcnN9LmBcclxuICApO1xyXG5cclxuICBpZiAocG9vbCkge1xyXG4gICAgcmV0dXJuIGxvZyhcclxuICAgICAgNCxcclxuICAgICAgJ1twb29sXSBBbHJlYWR5IGluaXRpYWxpemVkLCBwbGVhc2Uga2lsbCBpdCBiZWZvcmUgY3JlYXRpbmcgYSBuZXcgb25lLidcclxuICAgICk7XHJcbiAgfVxyXG5cclxuICAvLyBBdHRhY2ggcHJvY2VzcycgZXhpdCBsaXN0ZW5lcnNcclxuICBpZiAocG9vbENvbmZpZy5saXN0ZW5Ub1Byb2Nlc3NFeGl0cykge1xyXG4gICAgYXR0YWNoUHJvY2Vzc0V4aXRMaXN0ZW5lcnMoKTtcclxuICB9XHJcblxyXG4gIHRyeSB7XHJcbiAgICAvLyBDcmVhdGUgYSBwb29sIGFsb25nIHdpdGggYSBtaW5pbWFsIG51bWJlciBvZiByZXNvdXJjZXNcclxuICAgIHBvb2wgPSBuZXcgUG9vbCh7XHJcbiAgICAgIC8vIEdldCB0aGUgY3JlYXRlL3ZhbGlkYXRlL2Rlc3Ryb3kvbG9nIGZ1bmN0aW9uc1xyXG4gICAgICAuLi5mYWN0b3J5LFxyXG4gICAgICBtaW46IHBvb2xDb25maWcubWluV29ya2VycyxcclxuICAgICAgbWF4OiBwb29sQ29uZmlnLm1heFdvcmtlcnMsXHJcbiAgICAgIGFjcXVpcmVUaW1lb3V0TWlsbGlzOiBwb29sQ29uZmlnLmFjcXVpcmVUaW1lb3V0LFxyXG4gICAgICBjcmVhdGVUaW1lb3V0TWlsbGlzOiBwb29sQ29uZmlnLmNyZWF0ZVRpbWVvdXQsXHJcbiAgICAgIGRlc3Ryb3lUaW1lb3V0TWlsbGlzOiBwb29sQ29uZmlnLmRlc3Ryb3lUaW1lb3V0LFxyXG4gICAgICBpZGxlVGltZW91dE1pbGxpczogcG9vbENvbmZpZy5pZGxlVGltZW91dCxcclxuICAgICAgY3JlYXRlUmV0cnlJbnRlcnZhbE1pbGxpczogcG9vbENvbmZpZy5jcmVhdGVSZXRyeUludGVydmFsLFxyXG4gICAgICByZWFwSW50ZXJ2YWxNaWxsaXM6IHBvb2xDb25maWcucmVhcGVySW50ZXJ2YWwsXHJcbiAgICAgIHByb3BhZ2F0ZUNyZWF0ZUVycm9yOiBmYWxzZVxyXG4gICAgfSk7XHJcblxyXG4gICAgLy8gU2V0IGV2ZW50c1xyXG4gICAgcG9vbC5vbignY3JlYXRlRmFpbCcsIChldmVudElkLCBlcnIpID0+IHtcclxuICAgICAgbG9nKFxyXG4gICAgICAgIDEsXHJcbiAgICAgICAgYFtwb29sXSBFcnJvciB3aGVuIGNyZWF0aW5nIHdvcmtlciBvZiBhbiBldmVudCBpZCAke2V2ZW50SWR9OmAsXHJcbiAgICAgICAgZXJyXHJcbiAgICAgICk7XHJcbiAgICB9KTtcclxuXHJcbiAgICBwb29sLm9uKCdhY3F1aXJlRmFpbCcsIChldmVudElkLCBlcnIpID0+IHtcclxuICAgICAgbG9nKFxyXG4gICAgICAgIDEsXHJcbiAgICAgICAgYFtwb29sXSBFcnJvciB3aGVuIGFjcXVpcmluZyB3b3JrZXIgb2YgYW4gZXZlbnQgaWQgJHtldmVudElkfTpgLFxyXG4gICAgICAgIGVyclxyXG4gICAgICApO1xyXG4gICAgfSk7XHJcblxyXG4gICAgcG9vbC5vbignZGVzdHJveUZhaWwnLCAoZXZlbnRJZCwgcmVzb3VyY2UsIGVycikgPT4ge1xyXG4gICAgICBsb2coXHJcbiAgICAgICAgMSxcclxuICAgICAgICBgW3Bvb2xdIEVycm9yIHdoZW4gZGVzdHJveWluZyB3b3JrZXIgb2YgYW4gaWQgJHtyZXNvdXJjZS5pZH0sIGV2ZW50IGlkICR7ZXZlbnRJZH06YCxcclxuICAgICAgICBlcnJcclxuICAgICAgKTtcclxuICAgIH0pO1xyXG5cclxuICAgIHBvb2wub24oJ3JlbGVhc2UnLCAocmVzb3VyY2UpID0+IHtcclxuICAgICAgbG9nKDQsIGBbcG9vbF0gUmVsZWFzaW5nIGEgd29ya2VyIG9mIGFuIGlkICR7cmVzb3VyY2UuaWR9YCk7XHJcbiAgICB9KTtcclxuXHJcbiAgICBwb29sLm9uKCdkZXN0cm95U3VjY2VzcycsIChldmVudElkLCByZXNvdXJjZSkgPT4ge1xyXG4gICAgICBsb2coNCwgYFtwb29sXSBEZXN0cm95ZWQgYSB3b3JrZXIgb2YgYW4gaWQgJHtyZXNvdXJjZS5pZH1gKTtcclxuICAgIH0pO1xyXG5cclxuICAgIGNvbnN0IGluaXRpYWxSZXNvdXJjZXMgPSBbXTtcclxuICAgIC8vIENyZWF0ZSBhbiBpbml0aWFsIG51bWJlciBvZiByZXNvdXJjZXNcclxuICAgIGZvciAobGV0IGkgPSAwOyBpIDwgcG9vbENvbmZpZy5taW5Xb3JrZXJzOyBpKyspIHtcclxuICAgICAgdHJ5IHtcclxuICAgICAgICBjb25zdCByZXNvdXJjZSA9IGF3YWl0IHBvb2wuYWNxdWlyZSgpLnByb21pc2U7XHJcbiAgICAgICAgaW5pdGlhbFJlc291cmNlcy5wdXNoKHJlc291cmNlKTtcclxuICAgICAgfSBjYXRjaCAoZXJyb3IpIHtcclxuICAgICAgICBsb2coMSwgYFtwb29sXSBDb3VsZG4ndCBjcmVhdGUgYW4gaW5pdGlhbCByZXNvdXJjZSAke2Vycm9yfWApO1xyXG4gICAgICB9XHJcbiAgICB9XHJcblxyXG4gICAgLy8gUmVsZWFzZSB0aGUgaW5pdGlhbCBudW1iZXIgb2YgcmVzb3VyY2VzIGJhY2sgdG8gdGhlIHBvb2xcclxuICAgIGluaXRpYWxSZXNvdXJjZXMuZm9yRWFjaCgocmVzb3VyY2UpID0+IHtcclxuICAgICAgcG9vbC5yZWxlYXNlKHJlc291cmNlKTtcclxuICAgIH0pO1xyXG5cclxuICAgIGxvZyhcclxuICAgICAgMyxcclxuICAgICAgYFtwb29sXSBUaGUgcG9vbCBpcyByZWFkeSB3aXRoICR7cG9vbENvbmZpZy5taW5Xb3JrZXJzfSBpbml0aWFsIHJlc291cmNlcyB3YWl0aW5nLmBcclxuICAgICk7XHJcbiAgfSBjYXRjaCAoZXJyb3IpIHtcclxuICAgIGxvZygxLCBgW3Bvb2xdIENvdWxkbid0IGNyZWF0ZSB0aGUgd29ya2VyIHBvb2wgJHtlcnJvcn1gKTtcclxuICAgIHRocm93IGVycm9yO1xyXG4gIH1cclxufTtcclxuXHJcbi8qKlxyXG4gKiBBdHRhY2hlcyBwcm9jZXNzJyBleGl0IGxpc3RlbmVycy5cclxuICovXHJcbmV4cG9ydCBmdW5jdGlvbiBhdHRhY2hQcm9jZXNzRXhpdExpc3RlbmVycygpIHtcclxuICBsb2coNCwgJ1twb29sXSBBdHRhY2hpbmcgZXhpdCBsaXN0ZW5lcnMgdG8gdGhlIHByb2Nlc3MuJyk7XHJcblxyXG4gIC8vIEtpbGwgYWxsIHBvb2wgcmVzb3VyY2VzIG9uIGV4aXRcclxuICBwcm9jZXNzLm9uKCdleGl0JywgYXN5bmMgKCkgPT4ge1xyXG4gICAgYXdhaXQga2lsbFBvb2woKTtcclxuICB9KTtcclxuXHJcbiAgLy8gSGFuZGxlciBmb3IgdGhlIFNJR0lOVFxyXG4gIHByb2Nlc3Mub24oJ1NJR0lOVCcsIChuYW1lLCBjb2RlKSA9PiB7XHJcbiAgICBsb2coNCwgYFRoZSAke25hbWV9IGV2ZW50IHdpdGggY29kZTogJHtjb2RlfS5gKTtcclxuICAgIHByb2Nlc3MuZXhpdCgxKTtcclxuICB9KTtcclxuXHJcbiAgLy8gSGFuZGxlciBmb3IgdGhlIFNJR1RFUk1cclxuICBwcm9jZXNzLm9uKCdTSUdURVJNJywgKG5hbWUsIGNvZGUpID0+IHtcclxuICAgIGxvZyg0LCBgVGhlICR7bmFtZX0gZXZlbnQgd2l0aCBjb2RlOiAke2NvZGV9LmApO1xyXG4gICAgcHJvY2Vzcy5leGl0KDEpO1xyXG4gIH0pO1xyXG5cclxuICAvLyBIYW5kbGVyIGZvciB0aGUgdW5jYXVnaHRFeGNlcHRpb25cclxuICBwcm9jZXNzLm9uKCd1bmNhdWdodEV4Y2VwdGlvbicsIGFzeW5jIChlcnJvciwgbmFtZSkgPT4ge1xyXG4gICAgbG9nKDQsIGBUaGUgJHtuYW1lfSBlcnJvciwgbWVzc2FnZTogJHtlcnJvci5tZXNzYWdlfS5gKTtcclxuICB9KTtcclxufVxyXG5cclxuLyoqXHJcbiAqIEtpbGxzIHRoZSBwb29sIGFuZCBmbHVzaCB0aGUgYnJvd3NlciBpbnN0YW5jZS5cclxuICovXHJcbmV4cG9ydCBhc3luYyBmdW5jdGlvbiBraWxsUG9vbCgpIHtcclxuICBsb2coMywgJ1twb29sXSBLaWxsaW5nIGFsbCB3b3JrZXJzLicpO1xyXG5cclxuICAvLyBSZXR1cm4gdHJ1ZSB3aGVuIHRoZSBwb29sIGlzIGFscmVhZHkgZGVzdHJveWVkXHJcbiAgaWYgKHBvb2wuZGVzdHJveWVkKSB7XHJcbiAgICAvLyBDbG9zZSB0aGUgYnJvd3NlciBpbnN0YW5jZSBpZiBzdGlsbCBjb25uZWN0ZWRcclxuICAgIGF3YWl0IGNsb3NlKCk7XHJcbiAgICByZXR1cm4gdHJ1ZTtcclxuICB9XHJcblxyXG4gIC8vIElmIHN0aWxsIGFsaXZlLCBkZXN0cm95IHRoZSBwb29sIG9mIHBhZ2VzIGJlZm9yZSBjbG9zaW5nIGEgYnJvd3NlclxyXG4gIGF3YWl0IHBvb2wuZGVzdHJveSgpO1xyXG5cclxuICAvLyBDbG9zZSB0aGUgYnJvd3NlciBpbnN0YW5jZVxyXG4gIGF3YWl0IGNsb3NlKCk7XHJcbiAgcmV0dXJuIHRydWU7XHJcbn1cclxuXHJcbi8qKlxyXG4gKiBQb3N0cyB3b3JrIHRvIHRoZSBwb29sLlxyXG4gKlxyXG4gKiBAcGFyYW0ge29iamVjdH0gY2hhcnQgLSBDaGFydCdzIG9wdGlvbnMuXHJcbiAqIEBwYXJhbSB7b2JqZWN0fSBvcHRpb25zIC0gQWxsIG9wdGlvbnMgb2JqZWN0LlxyXG4gKi9cclxuZXhwb3J0IGNvbnN0IHBvc3RXb3JrID0gYXN5bmMgKGNoYXJ0LCBvcHRpb25zKSA9PiB7XHJcbiAgbGV0IHdvcmtlckhhbmRsZTtcclxuXHJcbiAgLy8gSGFuZGxlIGZhaWwgY29uZGl0aW9uc1xyXG4gIGNvbnN0IGZhaWwgPSAobXNnKSA9PiB7XHJcbiAgICArK2Ryb3BwZWRFeHBvcnRzO1xyXG5cclxuICAgIGlmICh3b3JrZXJIYW5kbGUpIHtcclxuICAgICAgcG9vbC5yZWxlYXNlKHdvcmtlckhhbmRsZSk7XHJcbiAgICB9XHJcblxyXG4gICAgdGhyb3cgJ0luIHBvb2wucG9zdFdvcms6ICcgKyBtc2c7XHJcbiAgfTtcclxuXHJcbiAgbG9nKDQsICdbcG9vbF0gV29yayByZWNlaXZlZCwgc3RhcnRpbmcgdG8gcHJvY2Vzcy4nKTtcclxuXHJcbiAgaWYgKHBvb2xDb25maWcuYmVuY2htYXJraW5nKSB7XHJcbiAgICBnZXRQb29sSW5mbygpO1xyXG4gIH1cclxuXHJcbiAgKytleHBvcnRBdHRlbXB0cztcclxuXHJcbiAgaWYgKCFwb29sKSB7XHJcbiAgICBsb2coMSwgJ1twb29sXSBXb3JrIHJlY2VpdmVkLCBidXQgcG9vbCBoYXMgbm90IGJlZW4gc3RhcnRlZC4nKTtcclxuICAgIHJldHVybiBmYWlsKCdQb29sIGlzIG5vdCBpbml0ZWQgYnV0IHdvcmsgd2FzIHBvc3RlZCB0byBpdCEnKTtcclxuICB9XHJcblxyXG4gIC8vIEFjcXVpcmUgdGhlIHdvcmtlciBhbG9uZyB3aXRoIHRoZSBpZCBvZiByZXNvdXJjZSBhbmQgd29yayBjb3VudFxyXG4gIHRyeSB7XHJcbiAgICBsb2coNCwgJ1twb29sXSBBY3F1aXJpbmcgd29ya2VyJyk7XHJcbiAgICB3b3JrZXJIYW5kbGUgPSBhd2FpdCBwb29sLmFjcXVpcmUoKS5wcm9taXNlO1xyXG4gIH0gY2F0Y2ggKGVycm9yKSB7XHJcbiAgICByZXR1cm4gZmFpbChgW3Bvb2xdIEVycm9yIHdoZW4gYWNxdWlyaW5nIGF2YWlsYWJsZSBlbnRyeTogJHtlcnJvcn1gKTtcclxuICB9XHJcblxyXG4gIGxvZyg0LCAnW3Bvb2xdIEFjcXVpcmVkIHdvcmtlciBoYW5kbGUnKTtcclxuXHJcbiAgaWYgKCF3b3JrZXJIYW5kbGUucGFnZSkge1xyXG4gICAgcmV0dXJuIGZhaWwoJ1Jlc29sdmVkIHdvcmtlciBwYWdlIGlzIGludmFsaWQ6IHBvb2wgc2V0dXAgaXMgd29ua3knKTtcclxuICB9XHJcblxyXG4gIHRyeSB7XHJcbiAgICAvLyBTYXZlIHRoZSBzdGFydCB0aW1lXHJcbiAgICBsZXQgd29ya1N0YXJ0ID0gbmV3IERhdGUoKS5nZXRUaW1lKCk7XHJcblxyXG4gICAgbG9nKDQsIGBbcG9vbF0gU3RhcnRpbmcgd29yayBvbiBwb29sIGVudHJ5ICR7d29ya2VySGFuZGxlLmlkfS5gKTtcclxuXHJcbiAgICAvLyBQZXJmb3JtIGFuIGV4cG9ydCBvbiBhIHB1cHBldGVlciBsZXZlbFxyXG4gICAgY29uc3QgcmVzdWx0ID0gYXdhaXQgcHVwcGV0ZWVyRXhwb3J0KHdvcmtlckhhbmRsZS5wYWdlLCBjaGFydCwgb3B0aW9ucyk7XHJcblxyXG4gICAgLy8gQ2hlY2sgaWYgaXQncyBhbiBlcnJvclxyXG4gICAgaWYgKHJlc3VsdCBpbnN0YW5jZW9mIEVycm9yKSB7XHJcbiAgICAgIC8vIFRPRE86IElmIHRoZSBleHBvcnQgZmFpbGVkIGJlY2F1c2UgcHVwcGV0ZWVyIHRpbWVkIG91dCwgd2UgbmVlZCB0byBmb3JjZSBraWxsIHRoZSB3b3JrZXIgc28gd2UgZ2V0IGEgbmV3IHBhZ2UuIFRoYXQgbmVlZHMgdG8gYmUgaGFuZGxlZCBiZXR0ZXIgdGhhbiB0aGlzIGhhY2suXHJcbiAgICAgIGlmIChyZXN1bHQubWVzc2FnZSA9PT0gJ1Jhc3Rlcml6YXRpb24gdGltZW91dCcpIHtcclxuICAgICAgICB3b3JrZXJIYW5kbGUucGFnZS5jbG9zZSgpO1xyXG4gICAgICAgIHdvcmtlckhhbmRsZS5wYWdlID0gYXdhaXQgYnJvd3Nlck5ld1BhZ2UoKTtcclxuICAgICAgfVxyXG5cclxuICAgICAgcmV0dXJuIGZhaWwocmVzdWx0KTtcclxuICAgIH1cclxuXHJcbiAgICAvLyBSZWxlYXNlIHRoZSByZXNvdXJjZSBiYWNrIHRvIHRoZSBwb29sXHJcbiAgICBwb29sLnJlbGVhc2Uod29ya2VySGFuZGxlKTtcclxuXHJcbiAgICAvLyBVc2VkIGZvciBzdGF0aXN0aWNzIGluIGF2ZXJhZ2VUaW1lIGFuZCBwcm9jZXNzZWRXb3JrQ291bnQsIHdoaWNoXHJcbiAgICAvLyBpbiB0dXJuIGlzIHVzZWQgYnkgdGhlIC9oZWFsdGggcm91dGUuXHJcbiAgICBjb25zdCB3b3JrRW5kID0gbmV3IERhdGUoKS5nZXRUaW1lKCk7XHJcbiAgICBjb25zdCBleHBvcnRUaW1lID0gd29ya0VuZCAtIHdvcmtTdGFydDtcclxuICAgIHRpbWVTcGVudCArPSBleHBvcnRUaW1lO1xyXG4gICAgc3BlbnRBdmVyYWdlID0gdGltZVNwZW50IC8gKytwZXJmb3JtZWRFeHBvcnRzO1xyXG5cclxuICAgIGxvZyg0LCBgW3Bvb2xdIFdvcmsgY29tcGxldGVkIGluICR7ZXhwb3J0VGltZX0gbXMuYCk7XHJcblxyXG4gICAgLy8gT3RoZXJ3aXNlIHJldHVybiB0aGUgcmVzdWx0XHJcbiAgICByZXR1cm4ge1xyXG4gICAgICBkYXRhOiByZXN1bHQsXHJcbiAgICAgIG9wdGlvbnNcclxuICAgIH07XHJcbiAgfSBjYXRjaCAoZXJyb3IpIHtcclxuICAgIGZhaWwoYEVycm9yIHRyeWluZyB0byBwZXJmb3JtIHB1cHBldGVlciBleHBvcnQ6ICR7ZXJyb3J9LmApO1xyXG4gIH1cclxufTtcclxuXHJcbi8qKlxyXG4gKiBHZXRzIHRoZSBwb29sLlxyXG4gKi9cclxuZXhwb3J0IGZ1bmN0aW9uIGdldFBvb2woKSB7XHJcbiAgcmV0dXJuIHBvb2w7XHJcbn1cclxuXHJcbmV4cG9ydCBjb25zdCBnZXRQb29sSW5mb0pTT04gPSAoKSA9PiAoe1xyXG4gIG1pbjogcG9vbC5taW4sXHJcbiAgbWF4OiBwb29sLm1heCxcclxuICBzaXplOiBwb29sLnNpemUsXHJcbiAgYXZhaWxhYmxlOiBwb29sLmF2YWlsYWJsZSxcclxuICBib3Jyb3dlZDogcG9vbC5ib3Jyb3dlZCxcclxuICBwZW5kaW5nOiBwb29sLnBlbmRpbmcsXHJcbiAgc3BhcmVSZXNvdXJjZUNhcGFjaXR5OiBwb29sLnNwYXJlUmVzb3VyY2VDYXBhY2l0eVxyXG59KTtcclxuXHJcbi8qKlxyXG4gKiBHZXRzIHRoZSBwb29sJ3MgaW5mb3JtYXRpb24uXHJcbiAqL1xyXG5leHBvcnQgZnVuY3Rpb24gZ2V0UG9vbEluZm8oKSB7XHJcbiAgY29uc3Qge1xyXG4gICAgbWluLFxyXG4gICAgbWF4LFxyXG4gICAgc2l6ZSxcclxuICAgIGF2YWlsYWJsZSxcclxuICAgIGJvcnJvd2VkLFxyXG4gICAgcGVuZGluZyxcclxuICAgIHNwYXJlUmVzb3VyY2VDYXBhY2l0eVxyXG4gIH0gPSBwb29sO1xyXG5cclxuICBsb2coNCwgYFtwb29sXSBUaGUgbWluaW11bSBudW1iZXIgb2YgcmVzb3VyY2VzIGFsbG93ZWQgYnkgcG9vbDogJHttaW59LmApO1xyXG4gIGxvZyg0LCBgW3Bvb2xdIFRoZSBtYXhpbXVtIG51bWJlciBvZiByZXNvdXJjZXMgYWxsb3dlZCBieSBwb29sOiAke21heH0uYCk7XHJcbiAgbG9nKFxyXG4gICAgNCxcclxuICAgIGBbcG9vbF0gVGhlIG51bWJlciBvZiBhbGwgcmVzb3VyY2VzIGluIHBvb2wgKGZyZWUgb3IgaW4gdXNlKTogJHtzaXplfS5gXHJcbiAgKTtcclxuICBsb2coXHJcbiAgICA0LFxyXG4gICAgYFtwb29sXSBUaGUgbnVtYmVyIG9mIHJlc291cmNlcyB0aGF0IGFyZSBjdXJyZW50bHkgYXZhaWxhYmxlOiAke2F2YWlsYWJsZX0uYFxyXG4gICk7XHJcbiAgbG9nKFxyXG4gICAgNCxcclxuICAgIGBbcG9vbF0gVGhlIG51bWJlciBvZiByZXNvdXJjZXMgdGhhdCBhcmUgY3VycmVudGx5IGFjcXVpcmVkOiAke2JvcnJvd2VkfS5gXHJcbiAgKTtcclxuICBsb2coXHJcbiAgICA0LFxyXG4gICAgYFtwb29sXSBUaGUgbnVtYmVyIG9mIGNhbGxlcnMgd2FpdGluZyB0byBhY3F1aXJlIGEgcmVzb3VyY2U6ICR7cGVuZGluZ30uYFxyXG4gICk7XHJcbiAgbG9nKFxyXG4gICAgNCxcclxuICAgIGBbcG9vbF0gVGhlIG51bWJlciBvZiBob3cgbWFueSBtb3JlIHJlc291cmNlcyBjYW4gdGhlIHBvb2wgbWFuYWdlL2NyZWF0ZTogJHtzcGFyZVJlc291cmNlQ2FwYWNpdHl9LmBcclxuICApO1xyXG59XHJcblxyXG5leHBvcnQgZGVmYXVsdCB7XHJcbiAgaW5pdCxcclxuICBraWxsUG9vbCxcclxuICBwb3N0V29yayxcclxuICBnZXRQb29sLFxyXG4gIGdldFBvb2xJbmZvLFxyXG4gIGdldFBvb2xJbmZvSlNPTixcclxuICB3b3JrQXR0ZW1wdHM6ICgpID0+IGV4cG9ydEF0dGVtcHRzLFxyXG4gIGRyb3BwZWRXb3JrOiAoKSA9PiBkcm9wcGVkRXhwb3J0cyxcclxuICBhdmVyYWdlVGltZTogKCkgPT4gc3BlbnRBdmVyYWdlLFxyXG4gIHByb2Nlc3NlZFdvcmtDb3VudDogKCkgPT4gcGVyZm9ybWVkRXhwb3J0c1xyXG59O1xyXG4iLCIvKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKlxyXG5cclxuSGlnaGNoYXJ0cyBFeHBvcnQgU2VydmVyXHJcblxyXG5Db3B5cmlnaHQgKGMpIDIwMTYtMjAyMywgSGlnaHNvZnRcclxuXHJcbkxpY2VuY2VkIHVuZGVyIHRoZSBNSVQgbGljZW5jZS5cclxuXHJcbkFkZGl0aW9uYWxseSBhIHZhbGlkIEhpZ2hjaGFydHMgbGljZW5zZSBpcyByZXF1aXJlZCBmb3IgdXNlLlxyXG5cclxuU2VlIExJQ0VOU0UgZmlsZSBpbiByb290IGZvciBkZXRhaWxzLlxyXG5cclxuKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKi9cclxuXHJcbmltcG9ydCBjYWNoZSBmcm9tICcuLi8uLi9jYWNoZS5qcyc7XHJcbmltcG9ydCBwb29sIGZyb20gJy4uLy4uL3Bvb2wuanMnO1xyXG5cclxuY29uc3QgcGFja2FnZVZlcnNpb24gPSBwcm9jZXNzLmVudi5ucG1fcGFja2FnZV92ZXJzaW9uO1xyXG5jb25zdCBzZXJ2ZXJTdGFydFRpbWUgPSBuZXcgRGF0ZSgpO1xyXG5cclxuLyoqXHJcbiAqIEFkZHMgdGhlIC9oZWFsdGggcm91dGUgd2hpY2ggb3V0cHV0cyBiYXNpYyBzdGF0cyBmb3IgdGhlIHNlcnZlclxyXG4gKi9cclxuZXhwb3J0IGRlZmF1bHQgKGFwcCkgPT5cclxuICAhYXBwXHJcbiAgICA/IGZhbHNlXHJcbiAgICA6IGFwcC5nZXQoJy9oZWFsdGgnLCAocmVxdWVzdCwgcmVzcG9uc2UpID0+IHtcclxuICAgICAgICByZXNwb25zZS5zZW5kKHtcclxuICAgICAgICAgIHN0YXR1czogJ09LJyxcclxuICAgICAgICAgIGJvb3RUaW1lOiBzZXJ2ZXJTdGFydFRpbWUsXHJcbiAgICAgICAgICB1cHRpbWU6XHJcbiAgICAgICAgICAgIE1hdGguZmxvb3IoXHJcbiAgICAgICAgICAgICAgKG5ldyBEYXRlKCkuZ2V0VGltZSgpIC0gc2VydmVyU3RhcnRUaW1lLmdldFRpbWUoKSkgLyAxMDAwIC8gNjBcclxuICAgICAgICAgICAgKSArICcgbWludXRlcycsXHJcbiAgICAgICAgICB2ZXJzaW9uOiBwYWNrYWdlVmVyc2lvbixcclxuICAgICAgICAgIGhpZ2hjaGFydHNWZXJzaW9uOiBjYWNoZS52ZXJzaW9uKCksXHJcbiAgICAgICAgICBhdmVyYWdlUHJvY2Vzc2luZ1RpbWU6IHBvb2wuYXZlcmFnZVRpbWUoKSxcclxuICAgICAgICAgIHBlcmZvcm1lZEV4cG9ydHM6IHBvb2wucHJvY2Vzc2VkV29ya0NvdW50KCksXHJcbiAgICAgICAgICBmYWlsZWRFeHBvcnRzOiBwb29sLmRyb3BwZWRXb3JrKCksXHJcbiAgICAgICAgICBleHBvcnRBdHRlbXB0czogcG9vbC53b3JrQXR0ZW1wdHMoKSxcclxuICAgICAgICAgIHN1Y2Vzc1JhdGlvOiAocG9vbC5wcm9jZXNzZWRXb3JrQ291bnQoKSAvIHBvb2wud29ya0F0dGVtcHRzKCkpICogMTAwLFxyXG4gICAgICAgICAgLy8gZXNsaW50LWRpc2FibGUtbmV4dC1saW5lIGltcG9ydC9uby1uYW1lZC1hcy1kZWZhdWx0LW1lbWJlclxyXG4gICAgICAgICAgcG9vbDogcG9vbC5nZXRQb29sSW5mb0pTT04oKVxyXG4gICAgICAgIH0pO1xyXG4gICAgICB9KTtcclxuIiwiLyoqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKipcclxuXHJcbkhpZ2hjaGFydHMgRXhwb3J0IFNlcnZlclxyXG5cclxuQ29weXJpZ2h0IChjKSAyMDE2LTIwMjMsIEhpZ2hzb2Z0XHJcblxyXG5MaWNlbmNlZCB1bmRlciB0aGUgTUlUIGxpY2VuY2UuXHJcblxyXG5BZGRpdGlvbmFsbHkgYSB2YWxpZCBIaWdoY2hhcnRzIGxpY2Vuc2UgaXMgcmVxdWlyZWQgZm9yIHVzZS5cclxuXHJcblNlZSBMSUNFTlNFIGZpbGUgaW4gcm9vdCBmb3IgZGV0YWlscy5cclxuXHJcbioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKiovXHJcblxyXG5pbXBvcnQgeyBleGlzdHNTeW5jLCByZWFkRmlsZVN5bmMsIHByb21pc2VzIGFzIGZzUHJvbWlzZXMgfSBmcm9tICdmcyc7XHJcblxyXG5pbXBvcnQgcHJvbXB0cyBmcm9tICdwcm9tcHRzJztcclxuXHJcbmltcG9ydCB7IGxvZyB9IGZyb20gJy4vbG9nZ2VyLmpzJztcclxuaW1wb3J0IHsgZGVlcENvcHksIGlzT2JqZWN0LCBwcmludFVzYWdlLCB0b0Jvb2xlYW4gfSBmcm9tICcuL3V0aWxzLmpzJztcclxuaW1wb3J0IHtcclxuICBhYnNvbHV0ZVByb3BzLFxyXG4gIGRlZmF1bHRDb25maWcsXHJcbiAgbmVzdGVkQXJncyxcclxuICBwcm9tcHRzQ29uZmlnXHJcbn0gZnJvbSAnLi9zY2hlbWFzL2NvbmZpZy5qcyc7XHJcblxyXG5sZXQgZ2VuZXJhbE9wdGlvbnMgPSB7fTtcclxuXHJcbi8qKlxyXG4gKiBHZXR0ZXIgZm9yIHRoZSBnZW5lcmFsIG9wdGlvbnMuXHJcbiAqXHJcbiAqIEByZXR1cm4ge29iamVjdH0gLSBHZW5lcmFsIG9wdGlvbnMgb2JqZWN0LlxyXG4gKi9cclxuZXhwb3J0IGNvbnN0IGdldE9wdGlvbnMgPSAoKSA9PiBnZW5lcmFsT3B0aW9ucztcclxuXHJcbi8qKlxyXG4gKiBJbml0aWFsaXplcyBhbmQgc2V0cyB0aGUgZ2VuZXJhbCBvcHRpb25zIGZvciB0aGUgc2VydmVyIGluc3RhY2UuXHJcbiAqXHJcbiAqIEBwYXJhbSB7b2JqZWN0fSB1c2VyT3B0aW9ucyAtIEFkZGl0aW9uYWwgdXNlciBvcHRpb25zIChlLmcuIGZyb20gdGhlIG5vZGVcclxuICogbW9kdWxlIHVzYWdlKS5cclxuICogQHBhcmFtIHtzdHJpbmdbXX0gYXJncyAtIENMSSBhcmd1bWVudHMuXHJcbiAqIEByZXR1cm4ge29iamVjdH0gLSBHZW5lcmFsIG9wdGlvbnMgb2JqZWN0LlxyXG4gKi9cclxuZXhwb3J0IGNvbnN0IHNldE9wdGlvbnMgPSAodXNlck9wdGlvbnMsIGFyZ3MpID0+IHtcclxuICAvLyBPbmx5IGZvciB0aGUgQ0xJIHVzYWdlXHJcbiAgaWYgKGFyZ3M/Lmxlbmd0aCkge1xyXG4gICAgLy8gR2V0IHRoZSBhZGRpdGlvbmFsIG9wdGlvbnMgZnJvbSB0aGUgY3VzdG9tIEpTT04gZmlsZVxyXG4gICAgZ2VuZXJhbE9wdGlvbnMgPSBsb2FkQ29uZmlnRmlsZShhcmdzKTtcclxuICB9XHJcblxyXG4gIC8vIFVwZGF0ZSB0aGUgZGVmYXVsdCBjb25maWcgd2l0aCBhIGNvcnJlY3Qgb3B0aW9uIHZhbHVlc1xyXG4gIHVwZGF0ZURlZmF1bHRDb25maWcoZGVmYXVsdENvbmZpZywgZ2VuZXJhbE9wdGlvbnMpO1xyXG5cclxuICAvLyBTZXQgdmFsdWVzIGZvciBzZXJ2ZXIncyBvcHRpb25zIGFuZCByZXR1cm5zIHRoZW1cclxuICBnZW5lcmFsT3B0aW9ucyA9IGluaXRPcHRpb25zKGRlZmF1bHRDb25maWcpO1xyXG5cclxuICAvLyBBcHBseSB1c2VyIG9wdGlvbnMgaWYgdGhlcmUgYXJlIGFueVxyXG4gIGlmICh1c2VyT3B0aW9ucykge1xyXG4gICAgLy8gTWVyZ2UgdXNlciBvcHRpb25zXHJcbiAgICBnZW5lcmFsT3B0aW9ucyA9IG1lcmdlQ29uZmlnT3B0aW9ucyhcclxuICAgICAgZ2VuZXJhbE9wdGlvbnMsXHJcbiAgICAgIHVzZXJPcHRpb25zLFxyXG4gICAgICBhYnNvbHV0ZVByb3BzXHJcbiAgICApO1xyXG4gIH1cclxuXHJcbiAgLy8gT25seSBmb3IgdGhlIENMSSB1c2FnZVxyXG4gIGlmIChhcmdzPy5sZW5ndGgpIHtcclxuICAgIC8vIFBhaXIgcHJvdmlkZWQgYXJndW1lbnRzXHJcbiAgICBnZW5lcmFsT3B0aW9ucyA9IHBhaXJBcmd1bWVudFZhbHVlKGdlbmVyYWxPcHRpb25zLCBhcmdzLCBkZWZhdWx0Q29uZmlnKTtcclxuICB9XHJcblxyXG4gIC8vIFJldHVybiBmaW5hbCBnZW5lcmFsIG9wdGlvbnNcclxuICByZXR1cm4gZ2VuZXJhbE9wdGlvbnM7XHJcbn07XHJcblxyXG4vKipcclxuICogRGlzcGxheXMgYSBwcm9tcHQgZm9yIHRoZSBtYW51YWwgY29uZmlndXJhdGlvbi5cclxuICpcclxuICogQHBhcmFtIHtzdHJpbmd9IGNvbmZpZ0ZpbGVOYW1lIC0gVGhlIG5hbWUgb2YgYSBjb25maWd1cmF0aW9uIGZpbGUuXHJcbiAqL1xyXG5leHBvcnQgY29uc3QgbWFudWFsQ29uZmlnID0gYXN5bmMgKGNvbmZpZ0ZpbGVOYW1lKSA9PiB7XHJcbiAgLy8gUHJlcGFyZSBhIGNvbmZpZyBvYmplY3RcclxuICBsZXQgY29uZmlnRmlsZSA9IHt9O1xyXG5cclxuICAvLyBDaGVjayBpZiBwcm92aWRlZCBjb25maWcgZmlsZSBleGlzdHNcclxuICBpZiAoZXhpc3RzU3luYyhjb25maWdGaWxlTmFtZSkpIHtcclxuICAgIGNvbmZpZ0ZpbGUgPSBKU09OLnBhcnNlKHJlYWRGaWxlU3luYyhjb25maWdGaWxlTmFtZSwgJ3V0ZjgnKSk7XHJcbiAgfVxyXG5cclxuICAvLyBRdWVzdGlvbiBhYm91dCBhIGNvbmZpZ3VyYXRpb24gY2F0ZWdvcnlcclxuICBjb25zdCBvblN1Ym1pdCA9IGFzeW5jIChwLCBjYXRlZ29yaWVzKSA9PiB7XHJcbiAgICBsZXQgcXVlc3Rpb25zQ291bnRlciA9IDA7XHJcbiAgICBsZXQgYWxsUXVlc3Rpb25zID0gW107XHJcblxyXG4gICAgLy8gQ3JlYXRlIGEgY29ycmVzcG9uZGluZyBwcm9wZXJ0eSBpbiB0aGUgbWFudWFsQ29uZmlnIG9iamVjdFxyXG4gICAgZm9yIChjb25zdCBzZWN0aW9uIG9mIGNhdGVnb3JpZXMpIHtcclxuICAgICAgLy8gTWFyayBlYWNoIG9wdGlvbiB3aXRoIGEgc2VjdGlvblxyXG4gICAgICBwcm9tcHRzQ29uZmlnW3NlY3Rpb25dID0gcHJvbXB0c0NvbmZpZ1tzZWN0aW9uXS5tYXAoKG9wdGlvbikgPT4gKHtcclxuICAgICAgICAuLi5vcHRpb24sXHJcbiAgICAgICAgc2VjdGlvblxyXG4gICAgICB9KSk7XHJcblxyXG4gICAgICAvLyBDb2xsZWN0IHRoZSBxdWVzdGlvbnNcclxuICAgICAgYWxsUXVlc3Rpb25zID0gWy4uLmFsbFF1ZXN0aW9ucywgLi4ucHJvbXB0c0NvbmZpZ1tzZWN0aW9uXV07XHJcbiAgICB9XHJcblxyXG4gICAgYXdhaXQgcHJvbXB0cyhhbGxRdWVzdGlvbnMsIHtcclxuICAgICAgb25TdWJtaXQ6IGFzeW5jIChwcm9tcHQsIGFuc3dlcikgPT4ge1xyXG4gICAgICAgIC8vIEdldCB0aGUgZGVmYXVsdCBtb2R1bGVzXHJcbiAgICAgICAgaWYgKHByb21wdC5uYW1lID09PSAnbW9kdWxlcycpIHtcclxuICAgICAgICAgIGFuc3dlciA9IGFuc3dlci5sZW5ndGhcclxuICAgICAgICAgICAgPyBhbnN3ZXIubWFwKChtb2R1bGUpID0+IHByb21wdC5jaG9pY2VzW21vZHVsZV0pXHJcbiAgICAgICAgICAgIDogcHJvbXB0LmNob2ljZXM7XHJcblxyXG4gICAgICAgICAgY29uZmlnRmlsZVtwcm9tcHQuc2VjdGlvbl1bcHJvbXB0Lm5hbWVdID0gYW5zd2VyO1xyXG4gICAgICAgIH0gZWxzZSB7XHJcbiAgICAgICAgICBjb25maWdGaWxlW3Byb21wdC5zZWN0aW9uXSA9IHJlY3Vyc2l2ZVByb3BzKFxyXG4gICAgICAgICAgICBPYmplY3QuYXNzaWduKHt9LCBjb25maWdGaWxlW3Byb21wdC5zZWN0aW9uXSB8fCB7fSksXHJcbiAgICAgICAgICAgIHByb21wdC5uYW1lLnNwbGl0KCcuJyksXHJcbiAgICAgICAgICAgIGFuc3dlclxyXG4gICAgICAgICAgKTtcclxuICAgICAgICB9XHJcblxyXG4gICAgICAgIGlmICgrK3F1ZXN0aW9uc0NvdW50ZXIgPT09IGFsbFF1ZXN0aW9ucy5sZW5ndGgpIHtcclxuICAgICAgICAgIHRyeSB7XHJcbiAgICAgICAgICAgIGF3YWl0IGZzUHJvbWlzZXMud3JpdGVGaWxlKFxyXG4gICAgICAgICAgICAgIGNvbmZpZ0ZpbGVOYW1lLFxyXG4gICAgICAgICAgICAgIEpTT04uc3RyaW5naWZ5KGNvbmZpZ0ZpbGUsIG51bGwsIDIpLFxyXG4gICAgICAgICAgICAgICd1dGY4J1xyXG4gICAgICAgICAgICApO1xyXG4gICAgICAgICAgfSBjYXRjaCAoZXJyb3IpIHtcclxuICAgICAgICAgICAgbG9nKDEsIGBbY29uZmlnXSBFcnJvciB3aGlsZSBjcmVhdGluZyBjb25maWcuanNvbjogJHtlcnJvcn1gKTtcclxuICAgICAgICAgIH1cclxuICAgICAgICAgIHJldHVybiB0cnVlO1xyXG4gICAgICAgIH1cclxuICAgICAgfVxyXG4gICAgfSk7XHJcblxyXG4gICAgcmV0dXJuIHRydWU7XHJcbiAgfTtcclxuXHJcbiAgLy8gRmluZCB0aGUgY2F0ZWdvcmllc1xyXG4gIGNvbnN0IGNob2ljZXMgPSBPYmplY3Qua2V5cyhwcm9tcHRzQ29uZmlnKS5tYXAoKGNob2ljZSkgPT4gKHtcclxuICAgIHRpdGxlOiBgJHtjaG9pY2V9IG9wdGlvbnNgLFxyXG4gICAgdmFsdWU6IGNob2ljZVxyXG4gIH0pKTtcclxuXHJcbiAgLy8gQ2F0ZWdvcnkgcHJvbXB0XHJcbiAgcmV0dXJuIHByb21wdHMoXHJcbiAgICB7XHJcbiAgICAgIHR5cGU6ICdtdWx0aXNlbGVjdCcsXHJcbiAgICAgIG5hbWU6ICdjYXRlZ29yeScsXHJcbiAgICAgIG1lc3NhZ2U6ICdXaGljaCBjYXRlZ29yeSBkbyB5b3Ugd2FudCB0byBjb25maWd1cmU/JyxcclxuICAgICAgaGludDogJ1NwYWNlOiBTZWxlY3Qgc3BlY2lmaWMsIEE6IFNlbGVjdCBhbGwsIEVudGVyOiBDb25maXJtLicsXHJcbiAgICAgIGluc3RydWN0aW9uczogJycsXHJcbiAgICAgIGNob2ljZXNcclxuICAgIH0sXHJcbiAgICB7IG9uU3VibWl0IH1cclxuICApO1xyXG59O1xyXG5cclxuLyoqXHJcbiAqIE1hcHMgdGhlIG9sZCBvcHRpb25zIHRvIHRoZSBuZXcgY29uZmlnIHN0cnVjdHVyZS5cclxuICpcclxuICogQHBhcmFtIHtvYmplY3R9IG9sZE9wdGlvbnMgLSBPcHRpb25zIHRvIGJlIG1hcHBlZC5cclxuICovXHJcbmV4cG9ydCBjb25zdCBtYXBUb05ld0NvbmZpZyA9IChvbGRPcHRpb25zKSA9PiB7XHJcbiAgY29uc3QgbmV3T3B0aW9ucyA9IHt9O1xyXG4gIC8vIEN5Y2xlIHRocm91Z2ggb2xkLXN0cnVjdHVyZWQgb3B0aW9uc1xyXG4gIGZvciAoY29uc3QgW2tleSwgdmFsdWVdIG9mIE9iamVjdC5lbnRyaWVzKG9sZE9wdGlvbnMpKSB7XHJcbiAgICBjb25zdCBwcm9wZXJ0aWVzQ2hhaW4gPSBuZXN0ZWRBcmdzW2tleV0gPyBuZXN0ZWRBcmdzW2tleV0uc3BsaXQoJy4nKSA6IFtdO1xyXG5cclxuICAgIC8vIFBvcHVsYXRlIG9iamVjdCBpbiBjb3JyZWN0IHByb3BlcnRpZXMgbGV2ZWxzXHJcbiAgICBwcm9wZXJ0aWVzQ2hhaW4ucmVkdWNlKFxyXG4gICAgICAob2JqLCBwcm9wLCBpbmRleCkgPT5cclxuICAgICAgICAob2JqW3Byb3BdID1cclxuICAgICAgICAgIHByb3BlcnRpZXNDaGFpbi5sZW5ndGggLSAxID09PSBpbmRleCA/IHZhbHVlIDogb2JqW3Byb3BdIHx8IHt9KSxcclxuICAgICAgbmV3T3B0aW9uc1xyXG4gICAgKTtcclxuICB9XHJcbiAgcmV0dXJuIG5ld09wdGlvbnM7XHJcbn07XHJcblxyXG4vKipcclxuICogTWVyZ2VzIHRoZSBuZXcgb3B0aW9ucyB0byB0aGUgb3B0aW9ucyBvYmplY3QuIEl0IG9taXRzIHVuZGVmaW5lZCB2YWx1ZXMuXHJcbiAqXHJcbiAqIEBwYXJhbSB7b2JqZWN0fSBvcHRpb25zIC0gT2xkIG9wdGlvbnMuXHJcbiAqIEBwYXJhbSB7b2JqZWN0fSBuZXdPcHRpb25zIC0gTmV3IG9wdGlvbnMuXHJcbiAqIEBwYXJhbSB7c3RyaW5nW119IGFic29sdXRlUHJvcHMgLSBBcnJheSBvZiBvYmplY3QgbmFtZXMgdGhhdCBzaG91bGQgYmUgZm9yY2VcclxuICogbWVyZ2VkLlxyXG4gKi9cclxuZXhwb3J0IGNvbnN0IG1lcmdlQ29uZmlnT3B0aW9ucyA9IChvcHRpb25zLCBuZXdPcHRpb25zLCBhYnNvbHV0ZVByb3BzID0gW10pID0+IHtcclxuICBjb25zdCBtZXJnZWRPcHRpb25zID0gZGVlcENvcHkob3B0aW9ucyk7XHJcblxyXG4gIGZvciAoY29uc3QgW2tleSwgdmFsdWVdIG9mIE9iamVjdC5lbnRyaWVzKG5ld09wdGlvbnMpKSB7XHJcbiAgICBtZXJnZWRPcHRpb25zW2tleV0gPVxyXG4gICAgICBpc09iamVjdCh2YWx1ZSkgJiZcclxuICAgICAgIWFic29sdXRlUHJvcHMuaW5jbHVkZXMoa2V5KSAmJlxyXG4gICAgICBtZXJnZWRPcHRpb25zW2tleV0gIT09IHVuZGVmaW5lZFxyXG4gICAgICAgID8gbWVyZ2VDb25maWdPcHRpb25zKG1lcmdlZE9wdGlvbnNba2V5XSwgdmFsdWUsIGFic29sdXRlUHJvcHMpXHJcbiAgICAgICAgOiB2YWx1ZSAhPT0gdW5kZWZpbmVkXHJcbiAgICAgICAgPyB2YWx1ZVxyXG4gICAgICAgIDogbWVyZ2VkT3B0aW9uc1trZXldO1xyXG4gIH1cclxuXHJcbiAgcmV0dXJuIG1lcmdlZE9wdGlvbnM7XHJcbn07XHJcblxyXG4vKipcclxuICogSW5pdGlhbGl6ZXMgb3B0aW9ucyBmb3IgdGhlIGBzdGFydEV4cG9ydGAgbWV0aG9kIGJ5IG1lcmdpbmcgdXNlciBvcHRpb25zXHJcbiAqIHdpdGggdGhlIGdlbmVyYWwgb3B0aW9ucy5cclxuICpcclxuICogQHBhcmFtIHthbnl9IGV4cG9ydE9wdGlvbnMgLSBVc2VyIG9wdGlvbnMgZm9yIGV4cG9ydGluZy5cclxuICogQHBhcmFtIHthbnl9IGdlbmVyYWxPcHRpb25zIC0gR2VuZXJhbCBvcHRpb25zIGFyZSB1c2VkIGZvciB0aGUgZXhwb3J0IHNlcnZlci5cclxuICogQHJldHVybiB7b2JqZWN0fSAtIFVzZXIgb3B0aW9ucyBtZXJnZWQgd2l0aCBkZWZhdWx0IG9wdGlvbnMuXHJcbiAqL1xyXG5leHBvcnQgY29uc3QgaW5pdEV4cG9ydFNldHRpbmdzID0gKGV4cG9ydE9wdGlvbnMsIGdlbmVyYWxPcHRpb25zID0ge30pID0+IHtcclxuICBsZXQgb3B0aW9ucyA9IHt9O1xyXG5cclxuICBpZiAoZXhwb3J0T3B0aW9ucy5zdmcpIHtcclxuICAgIG9wdGlvbnMgPSBkZWVwQ29weShnZW5lcmFsT3B0aW9ucyk7XHJcbiAgICBvcHRpb25zLmV4cG9ydC50eXBlID0gZXhwb3J0T3B0aW9ucy50eXBlIHx8IGV4cG9ydE9wdGlvbnMuZXhwb3J0LnR5cGU7XHJcbiAgICBvcHRpb25zLmV4cG9ydC5zY2FsZSA9IGV4cG9ydE9wdGlvbnMuc2NhbGUgfHwgZXhwb3J0T3B0aW9ucy5leHBvcnQuc2NhbGU7XHJcbiAgICBvcHRpb25zLmV4cG9ydC5vdXRmaWxlID1cclxuICAgICAgZXhwb3J0T3B0aW9ucy5vdXRmaWxlIHx8IGV4cG9ydE9wdGlvbnMuZXhwb3J0Lm91dGZpbGU7XHJcbiAgICBvcHRpb25zLnBheWxvYWQgPSB7XHJcbiAgICAgIHN2ZzogZXhwb3J0T3B0aW9ucy5zdmdcclxuICAgIH07XHJcbiAgfSBlbHNlIHtcclxuICAgIG9wdGlvbnMgPSBtZXJnZUNvbmZpZ09wdGlvbnMoXHJcbiAgICAgIGdlbmVyYWxPcHRpb25zLFxyXG4gICAgICBleHBvcnRPcHRpb25zLFxyXG4gICAgICAvLyBPbWl0IGdvaW5nIGRvd24gcmVjdXJzaXZlbHkgd2l0aCB0aGUgYmVsb3dzXHJcbiAgICAgIGFic29sdXRlUHJvcHNcclxuICAgICk7XHJcbiAgfVxyXG5cclxuICBvcHRpb25zLmV4cG9ydC5vdXRmaWxlID1cclxuICAgIG9wdGlvbnMuZXhwb3J0Py5vdXRmaWxlIHx8IGBjaGFydC4ke29wdGlvbnMuZXhwb3J0Py50eXBlIHx8ICdwbmcnfWA7XHJcbiAgcmV0dXJuIG9wdGlvbnM7XHJcbn07XHJcblxyXG4vKipcclxuICogTG9hZHMgdGhlIGNvbmZpZ3VyYXRpb24gZnJvbSBhIGN1c3RvbSBKU09OIGZpbGUuXHJcbiAqXHJcbiAqIEBwYXJhbSB7c3RyaW5nW119IGFyZ3MgLSBDTEkgYXJndW1lbnRzLlxyXG4gKiBAcmV0dXJuIHtvYmplY3R9IC0gT3B0aW9ucyBvYmplY3QgZnJvbSB0aGUgSlNPTiBmaWxlLlxyXG4gKi9cclxuZnVuY3Rpb24gbG9hZENvbmZpZ0ZpbGUoYXJncykge1xyXG4gIC8vIENoZWNrIGlmIHRoZSAtLWxvYWRDb25maWcgb3B0aW9uIHdhcyB1c2VkXHJcbiAgY29uc3QgY29uZmlnSW5kZXggPSBhcmdzLmZpbmRJbmRleChcclxuICAgIChhcmcpID0+IGFyZy5yZXBsYWNlKC8tL2csICcnKSA9PT0gJ2xvYWRDb25maWcnXHJcbiAgKTtcclxuXHJcbiAgLy8gQ2hlY2sgaWYgdGhlIC0tbG9hZENvbmZpZyBoYXMgYSB2YWx1ZVxyXG4gIGlmIChjb25maWdJbmRleCA+IC0xICYmIGFyZ3NbY29uZmlnSW5kZXggKyAxXSkge1xyXG4gICAgY29uc3QgZmlsZU5hbWUgPSBhcmdzW2NvbmZpZ0luZGV4ICsgMV07XHJcbiAgICB0cnkge1xyXG4gICAgICAvLyBDaGVjayBpZiBhbiBhZGRpdGlvbmFsIGNvbmZpZyBmaWxlIGlzIGEgY29ycmVjdCBKU09OIGZpbGVcclxuICAgICAgaWYgKGZpbGVOYW1lICYmIGZpbGVOYW1lLmVuZHNXaXRoKCcuanNvbicpKSB7XHJcbiAgICAgICAgLy8gTG9hZCBhbiBvcHRpb25hbCBjdXN0b20gSlNPTiBjb25maWcgZmlsZVxyXG4gICAgICAgIHJldHVybiBKU09OLnBhcnNlKHJlYWRGaWxlU3luYyhmaWxlTmFtZSkpO1xyXG4gICAgICB9XHJcbiAgICB9IGNhdGNoIChlcnJvcikge1xyXG4gICAgICBsb2coMSwgYFtjb25maWddIFVuYWJsZSB0byBsb2FkIGNvbmZpZyBmcm9tIHRoZSAke2ZpbGVOYW1lfTogJHtlcnJvcn1gKTtcclxuICAgIH1cclxuICB9XHJcblxyXG4gIC8vIE5vIGFkZGl0aW9uYWwgb3B0aW9ucyB0byByZXR1cm5cclxuICByZXR1cm4ge307XHJcbn1cclxuXHJcbi8qKlxyXG4gKiBTZXR0aW5nIGNvcnJlY3QgdmFsdWVzIG9mIHRoZSBvcHRpb25zIGZyb20gdGhlIGRlZmF1bHQgY29uZmlnLlxyXG4gKlxyXG4gKiBAcGFyYW0ge29iamVjdH0gY29uZmlnT2JqIC0gVGhlIGNvbmZpZyBvYmplY3QgYmFzZWQgb24gd2hpY2ggdGhlIGluaXRpYWxcclxuICogY29uZmlndXJhdGlvbiBiZSBtYWRlLlxyXG4gKiBAcGFyYW0ge29iamVjdH0gY3VzdG9tT2JqIC0gVGhlIGN1c3RvbSBvYmplY3Qgd2hpY2ggY2FuIGNvbnRhaW4gYWRkaXRpb25hbFxyXG4gKiBvcHRpb24gdmFsdWVzIHRvIHNldC5cclxuICogQHBhcmFtIHtzdHJpbmd9IHByb3BDaGFpbiAtIFJlcXVpcmVkIGZvciBjcmVhdGluZyBhIHN0cmluZyBjaGFpbiBvZlxyXG4gKiBwcm9wZXJ0aWVzIGZvciBuZXN0ZWQgYXJndW1lbnRzLlxyXG4gKi9cclxuZnVuY3Rpb24gdXBkYXRlRGVmYXVsdENvbmZpZyhjb25maWdPYmosIGN1c3RvbU9iaiA9IHt9LCBwcm9wQ2hhaW4gPSAnJykge1xyXG4gIE9iamVjdC5rZXlzKGNvbmZpZ09iaikuZm9yRWFjaCgoa2V5KSA9PiB7XHJcbiAgICBpZiAoIVsncHVwcGV0ZWVyJywgJ2hpZ2hjaGFydHMnXS5pbmNsdWRlcyhrZXkpKSB7XHJcbiAgICAgIGNvbnN0IGVudHJ5ID0gY29uZmlnT2JqW2tleV07XHJcbiAgICAgIGNvbnN0IGN1c3RvbVZhbHVlID0gY3VzdG9tT2JqICYmIGN1c3RvbU9ialtrZXldO1xyXG4gICAgICBsZXQgbnVtRW52VmFsO1xyXG5cclxuICAgICAgaWYgKHR5cGVvZiBlbnRyeS52YWx1ZSA9PT0gJ3VuZGVmaW5lZCcpIHtcclxuICAgICAgICB1cGRhdGVEZWZhdWx0Q29uZmlnKGVudHJ5LCBjdXN0b21WYWx1ZSwgYCR7cHJvcENoYWlufS4ke2tleX1gKTtcclxuICAgICAgfSBlbHNlIHtcclxuICAgICAgICAvLyBJZiBhIHZhbHVlIGZyb20gYSBjdXN0b20gSlNPTiBleGlzdHMsIGl0IHRha2UgcHJlY2VkZW5jZVxyXG4gICAgICAgIGlmIChjdXN0b21WYWx1ZSAhPT0gdW5kZWZpbmVkKSB7XHJcbiAgICAgICAgICBlbnRyeS52YWx1ZSA9IGN1c3RvbVZhbHVlO1xyXG4gICAgICAgIH1cclxuXHJcbiAgICAgICAgLy8gSWYgYSB2YWx1ZSBmcm9tIGFuIGVudiB2YXJpYWJsZSBleGlzdHMsIGl0IHRha2UgcHJlY2VkZW5jZVxyXG4gICAgICAgIGlmIChlbnRyeS5lbnZMaW5rKSB7XHJcbiAgICAgICAgICAvLyBMb2FkIHRoZSBlbnYgdmFyXHJcbiAgICAgICAgICBpZiAoZW50cnkudHlwZSA9PT0gJ2Jvb2xlYW4nKSB7XHJcbiAgICAgICAgICAgIGVudHJ5LnZhbHVlID0gdG9Cb29sZWFuKFxyXG4gICAgICAgICAgICAgIFtwcm9jZXNzLmVudltlbnRyeS5lbnZMaW5rXSwgZW50cnkudmFsdWVdLmZpbmQoXHJcbiAgICAgICAgICAgICAgICAoZWwpID0+IGVsIHx8IGVsID09PSAnZmFsc2UnXHJcbiAgICAgICAgICAgICAgKVxyXG4gICAgICAgICAgICApO1xyXG4gICAgICAgICAgfSBlbHNlIGlmIChlbnRyeS50eXBlID09PSAnbnVtYmVyJykge1xyXG4gICAgICAgICAgICBudW1FbnZWYWwgPSArcHJvY2Vzcy5lbnZbZW50cnkuZW52TGlua107XHJcbiAgICAgICAgICAgIGVudHJ5LnZhbHVlID0gbnVtRW52VmFsID49IDAgPyBudW1FbnZWYWwgOiBlbnRyeS52YWx1ZTtcclxuICAgICAgICAgIH0gZWxzZSBpZiAoXHJcbiAgICAgICAgICAgIGVudHJ5LnR5cGUuaW5kZXhPZignXScpID49IDAgJiZcclxuICAgICAgICAgICAgcHJvY2Vzcy5lbnZbZW50cnkuZW52TGlua11cclxuICAgICAgICAgICkge1xyXG4gICAgICAgICAgICBlbnRyeS52YWx1ZSA9IHByb2Nlc3MuZW52W2VudHJ5LmVudkxpbmtdLnNwbGl0KCcsJyk7XHJcbiAgICAgICAgICB9IGVsc2Uge1xyXG4gICAgICAgICAgICBlbnRyeS52YWx1ZSA9IHByb2Nlc3MuZW52W2VudHJ5LmVudkxpbmtdIHx8IGVudHJ5LnZhbHVlO1xyXG4gICAgICAgICAgfVxyXG4gICAgICAgIH1cclxuICAgICAgfVxyXG4gICAgfVxyXG4gIH0pO1xyXG59XHJcblxyXG4vKipcclxuICogSW5pdHMgb3B0aW9ucyByZWN1cnNpdmVseS5cclxuICpcclxuICogQHBhcmFtIHthbnl9IGl0ZW1zIC0gSXRlbXMgdG8gdXBkYXRlIG9wdGlvbnMgZnJvbS5cclxuICogQHJldHVybiB7b2JqZWN0fSAtIFVwZGF0ZWQgb3B0aW9ucyBvYmplY3QuXHJcbiAqL1xyXG5mdW5jdGlvbiBpbml0T3B0aW9ucyhpdGVtcykge1xyXG4gIGxldCBvcHRpb25zID0ge307XHJcbiAgZm9yIChjb25zdCBbbmFtZSwgaXRlbV0gb2YgT2JqZWN0LmVudHJpZXMoaXRlbXMpKSB7XHJcbiAgICBvcHRpb25zW25hbWVdID0gT2JqZWN0LnByb3RvdHlwZS5oYXNPd25Qcm9wZXJ0eS5jYWxsKGl0ZW0sICd2YWx1ZScpXHJcbiAgICAgID8gaXRlbS52YWx1ZVxyXG4gICAgICA6IGluaXRPcHRpb25zKGl0ZW0pO1xyXG4gIH1cclxuICByZXR1cm4gb3B0aW9ucztcclxufVxyXG5cclxuLyoqXHJcbiAqIFBhaXJzIGFyZ3VtZW50IHdpdGggYSBjb3JyZXNwb25kaW5nIHZhbHVlLlxyXG4gKlxyXG4gKiBAcGFyYW0ge29iamVjdH0gb3B0aW9ucyAtIEFsbCBzZXJ2ZXIgb3B0aW9ucy5cclxuICogQHBhcmFtIHtzdHJpbmdbXX0gYXJncyAtIEFycmF5IG9mIGFyZ3VtZW50cyBmcm9tIGEgdXNlci5cclxuICogQHBhcmFtIHtvYmplY3R9IGRlZmF1bHRDb25maWcgLSBUaGUgZGVmYXVsdCBjb25maWcgb2JqZWN0LlxyXG4gKi9cclxuZnVuY3Rpb24gcGFpckFyZ3VtZW50VmFsdWUob3B0aW9ucywgYXJncywgZGVmYXVsdENvbmZpZykge1xyXG4gIGZvciAobGV0IGkgPSAwOyBpIDwgYXJncy5sZW5ndGg7IGkrKykge1xyXG4gICAgbGV0IG9wdGlvbiA9IGFyZ3NbaV0ucmVwbGFjZSgvLS9nLCAnJyk7XHJcblxyXG4gICAgLy8gRmluZCB0aGUgcmlnaHQgcGxhY2UgZm9yIHByb3BlcnR5J3MgdmFsdWVcclxuICAgIGNvbnN0IHByb3BlcnRpZXNDaGFpbiA9IG5lc3RlZEFyZ3Nbb3B0aW9uXVxyXG4gICAgICA/IG5lc3RlZEFyZ3Nbb3B0aW9uXS5zcGxpdCgnLicpXHJcbiAgICAgIDogW107XHJcblxyXG4gICAgcHJvcGVydGllc0NoYWluLnJlZHVjZSgob2JqLCBwcm9wLCBpbmRleCkgPT4ge1xyXG4gICAgICBpZiAocHJvcGVydGllc0NoYWluLmxlbmd0aCAtIDEgPT09IGluZGV4KSB7XHJcbiAgICAgICAgLy8gRmluZHMgYW4gb3B0aW9uIGFuZCBzZXQgYSBjb3JyZXNwb25kaW5nIHZhbHVlXHJcbiAgICAgICAgaWYgKHR5cGVvZiBvYmpbcHJvcF0gIT09ICd1bmRlZmluZWQnKSB7XHJcbiAgICAgICAgICBpZiAoYXJnc1srK2ldKSB7XHJcbiAgICAgICAgICAgIG9ialtwcm9wXSA9IGFyZ3NbaV0gfHwgb2JqW3Byb3BdO1xyXG4gICAgICAgICAgfSBlbHNlIHtcclxuICAgICAgICAgICAgY29uc29sZS5sb2coYE1pc3NpbmcgYXJndW1lbnQgdmFsdWUgZm9yICR7b3B0aW9ufSFgLnJlZCwgJ1xcbicpO1xyXG4gICAgICAgICAgICBvcHRpb25zID0gcHJpbnRVc2FnZShkZWZhdWx0Q29uZmlnKTtcclxuICAgICAgICAgIH1cclxuICAgICAgICB9XHJcbiAgICAgIH1cclxuICAgICAgcmV0dXJuIG9ialtwcm9wXTtcclxuICAgIH0sIG9wdGlvbnMpO1xyXG4gIH1cclxuXHJcbiAgcmV0dXJuIG9wdGlvbnM7XHJcbn1cclxuXHJcbi8qKlxyXG4gKiBSZWN1cnNpdmVseSBzZXRzIGEgcHJvcGVydHkgaW4gYSBjb3JyZWN0IGluZGVudGF0aW9uIGxldmVsIGJhc2VkIG9uIHRoZVxyXG4gKiBhcnJheSBvZiBuZXN0ZWQgcHJvcGVydGllcyBuYW1lcy5cclxuICpcclxuICogQHBhcmFtIHtvYmplY3R9IG9iamVjdFRvVXBkYXRlIC0gT2JqZWN0IHdoZXJlIGEgcHJvcGVydHkgbXVzdCBiZSBzZXQgb24gYVxyXG4gKiBjb3JyZWN0IGxldmVsLlxyXG4gKiBAcGFyYW0gIHtzdHJpbmdbXX1uZXN0ZWROYW1lcyAtIEFycmF5IG9mIG5hc3RlZCBuYW1lcyB0aGF0IGluZGljYXRlc1xyXG4gKiBpbmRlbnRhdGlvbiBsZXZlbC5cclxuICogQHBhcmFtIHthbnl9IHZhbHVlIC0gQSB2YWx1ZSB0byBhc3NpZ24gdG8gdGhlIHByb3BlcnR5LlxyXG4gKiBAcmV0dXJuIHtvYmplY3R9IC0gVXBkYXRlZCBvcHRpb25zIG9iamVjdC5cclxuICovXHJcbmZ1bmN0aW9uIHJlY3Vyc2l2ZVByb3BzKG9iamVjdFRvVXBkYXRlLCBuZXN0ZWROYW1lcywgdmFsdWUpIHtcclxuICB3aGlsZSAobmVzdGVkTmFtZXMubGVuZ3RoID4gMSkge1xyXG4gICAgY29uc3QgcHJvcE5hbWUgPSBuZXN0ZWROYW1lcy5zaGlmdCgpO1xyXG5cclxuICAgIC8vIENyZWF0ZSBhIHByb3BlcnR5IGluIG9iamVjdCBpZiBpdCBkb2Vzbid0IGV4aXN0XHJcbiAgICBpZiAoIU9iamVjdC5wcm90b3R5cGUuaGFzT3duUHJvcGVydHkuY2FsbChvYmplY3RUb1VwZGF0ZSwgcHJvcE5hbWUpKSB7XHJcbiAgICAgIG9iamVjdFRvVXBkYXRlW3Byb3BOYW1lXSA9IHt9O1xyXG4gICAgfVxyXG5cclxuICAgIC8vIENhbGwgZnVuY3Rpb24gYWdhaW4gaWYgdGhlcmUgc3RpbGwgbmFtZXMgdG8gZ29cclxuICAgIG9iamVjdFRvVXBkYXRlW3Byb3BOYW1lXSA9IHJlY3Vyc2l2ZVByb3BzKFxyXG4gICAgICBPYmplY3QuYXNzaWduKHt9LCBvYmplY3RUb1VwZGF0ZVtwcm9wTmFtZV0pLFxyXG4gICAgICBuZXN0ZWROYW1lcyxcclxuICAgICAgdmFsdWVcclxuICAgICk7XHJcblxyXG4gICAgcmV0dXJuIG9iamVjdFRvVXBkYXRlO1xyXG4gIH1cclxuXHJcbiAgLy8gQXNzaWduIHRoZSBmaW5hbCB2YWx1ZVxyXG4gIG9iamVjdFRvVXBkYXRlW25lc3RlZE5hbWVzWzBdXSA9IHZhbHVlO1xyXG4gIHJldHVybiBvYmplY3RUb1VwZGF0ZTtcclxufVxyXG5cclxuZXhwb3J0IGRlZmF1bHQge1xyXG4gIGdldE9wdGlvbnMsXHJcbiAgc2V0T3B0aW9ucyxcclxuICBtYW51YWxDb25maWcsXHJcbiAgbWFwVG9OZXdDb25maWcsXHJcbiAgbWVyZ2VDb25maWdPcHRpb25zLFxyXG4gIGluaXRFeHBvcnRTZXR0aW5nc1xyXG59O1xyXG4iLCIvKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKlxyXG5cclxuSGlnaGNoYXJ0cyBFeHBvcnQgU2VydmVyXHJcblxyXG5Db3B5cmlnaHQgKGMpIDIwMTYtMjAyMywgSGlnaHNvZnRcclxuXHJcbkxpY2VuY2VkIHVuZGVyIHRoZSBNSVQgbGljZW5jZS5cclxuXHJcbkFkZGl0aW9uYWxseSBhIHZhbGlkIEhpZ2hjaGFydHMgbGljZW5zZSBpcyByZXF1aXJlZCBmb3IgdXNlLlxyXG5cclxuU2VlIExJQ0VOU0UgZmlsZSBpbiByb290IGZvciBkZXRhaWxzLlxyXG5cclxuKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKi9cclxuXHJcbmltcG9ydCB7IHJlYWRGaWxlLCByZWFkRmlsZVN5bmMsIHdyaXRlRmlsZVN5bmMgfSBmcm9tICdmcyc7XHJcblxyXG5pbXBvcnQgeyBsb2cgfSBmcm9tICcuL2xvZ2dlci5qcyc7XHJcbmltcG9ydCB7IGtpbGxQb29sLCBwb3N0V29yayB9IGZyb20gJy4vcG9vbC5qcyc7XHJcbmltcG9ydCB7XHJcbiAgY2xlYXJUZXh0LFxyXG4gIGZpeFR5cGUsXHJcbiAgaGFuZGxlUmVzb3VyY2VzLFxyXG4gIGlzQ29ycmVjdEpTT04sXHJcbiAgb3B0aW9uc1N0cmluZ2lmeSxcclxuICByb3VuZE51bWJlcixcclxuICB0b0Jvb2xlYW4sXHJcbiAgd3JhcEFyb3VuZFxyXG59IGZyb20gJy4vdXRpbHMuanMnO1xyXG5pbXBvcnQgeyBpbml0RXhwb3J0U2V0dGluZ3MsIGdldE9wdGlvbnMgfSBmcm9tICcuL2NvbmZpZy5qcyc7XHJcblxyXG5sZXQgYWxsb3dDb2RlRXhlY3V0aW9uID0gZmFsc2U7XHJcblxyXG5leHBvcnQgY29uc3Qgc3RhcnRFeHBvcnQgPSBhc3luYyAoc2V0dGluZ3MsIGVuZENhbGxiYWNrKSA9PiB7XHJcbiAgLy8gU3RhcnRpbmcgZXhwb3J0aW5nIHByb2Nlc3MgbWVzc2FnZVxyXG4gIGxvZyg0LCAnW2NoYXJ0XSBTdGFydGluZyBleHBvcnRpbmcgcHJvY2Vzcy4nKTtcclxuXHJcbiAgLy8gSW5pdGlhbGl6ZSBvcHRpb25zXHJcbiAgY29uc3Qgb3B0aW9ucyA9IGluaXRFeHBvcnRTZXR0aW5ncyhzZXR0aW5ncywgZ2V0T3B0aW9ucygpKTtcclxuXHJcbiAgLy8gR2V0IHRoZSBleHBvcnQgb3B0aW9uc1xyXG4gIGNvbnN0IGV4cG9ydE9wdGlvbnMgPSBvcHRpb25zLmV4cG9ydDtcclxuXHJcbiAgLy8gSWYgU1ZHIGlzIGFuIGlucHV0IChhcmd1bWVudCBjYW4gYmUgc2VudCBvbmx5IGJ5IHRoZSByZXF1ZXN0KVxyXG4gIGlmIChvcHRpb25zLnBheWxvYWQ/LnN2ZyAmJiBvcHRpb25zLnBheWxvYWQuc3ZnICE9PSAnJykge1xyXG4gICAgcmV0dXJuIGV4cG9ydEFzU3RyaW5nKG9wdGlvbnMucGF5bG9hZC5zdmcudHJpbSgpLCBvcHRpb25zLCBlbmRDYWxsYmFjayk7XHJcbiAgfVxyXG5cclxuICAvLyBFeHBvcnQgdXNpbmcgb3B0aW9ucyBmcm9tIHRoZSBmaWxlXHJcbiAgaWYgKGV4cG9ydE9wdGlvbnMuaW5maWxlICYmIGV4cG9ydE9wdGlvbnMuaW5maWxlLmxlbmd0aCkge1xyXG4gICAgbG9nKDQsICdbY2hhcnRdIEF0dGVtcHRpbmcgdG8gZXhwb3J0IGZyb20gYW4gaW5wdXQgZmlsZS4nKTtcclxuXHJcbiAgICAvLyBUcnkgdG8gcmVhZCB0aGUgZmlsZVxyXG4gICAgcmV0dXJuIHJlYWRGaWxlKGV4cG9ydE9wdGlvbnMuaW5maWxlLCAndXRmOCcsIChlcnJvciwgaW5maWxlKSA9PiB7XHJcbiAgICAgIGlmIChlcnJvcikge1xyXG4gICAgICAgIHJldHVybiBsb2coMSwgYFtjaGFydF0gRXJyb3IgbG9hZGluZyBpbnB1dCBmaWxlOiAke2Vycm9yfS5gKTtcclxuICAgICAgfVxyXG5cclxuICAgICAgLy8gR2V0IHRoZSBzdHJpbmcgcmVwcmVzZW50YXRpb25cclxuICAgICAgb3B0aW9ucy5leHBvcnQuaW5zdHIgPSBpbmZpbGU7XHJcbiAgICAgIHJldHVybiBleHBvcnRBc1N0cmluZyhvcHRpb25zLmV4cG9ydC5pbnN0ci50cmltKCksIG9wdGlvbnMsIGVuZENhbGxiYWNrKTtcclxuICAgIH0pO1xyXG4gIH1cclxuXHJcbiAgLy8gRXhwb3J0IHdpdGggb3B0aW9ucyBmcm9tIHRoZSByYXcgcmVwcmVzZW50YXRpb25cclxuICBpZiAoXHJcbiAgICAoZXhwb3J0T3B0aW9ucy5pbnN0ciAmJiBleHBvcnRPcHRpb25zLmluc3RyICE9PSAnJykgfHxcclxuICAgIChleHBvcnRPcHRpb25zLm9wdGlvbnMgJiYgZXhwb3J0T3B0aW9ucy5vcHRpb25zICE9PSAnJylcclxuICApIHtcclxuICAgIGxvZyg0LCAnW2NoYXJ0XSBBdHRlbXB0aW5nIHRvIGV4cG9ydCBmcm9tIGEgcmF3IGlucHV0LicpO1xyXG5cclxuICAgIC8vIFBlcmZvcm0gYSBkaXJlY3QgaW5qZWN0IHdoZW4gZm9yY2VkXHJcbiAgICBpZiAodG9Cb29sZWFuKG9wdGlvbnMuY3VzdG9tQ29kZT8uYWxsb3dDb2RlRXhlY3V0aW9uKSkge1xyXG4gICAgICByZXR1cm4gZG9TdHJhaWdodEluamVjdChvcHRpb25zLCBlbmRDYWxsYmFjayk7XHJcbiAgICB9XHJcblxyXG4gICAgLy8gRWl0aGVyIHRyeSB0byBwYXJzZSB0byBKU09OIGZpcnN0IG9yIGRvIHRoZSBkaXJlY3QgZXhwb3J0XHJcbiAgICByZXR1cm4gdHlwZW9mIGV4cG9ydE9wdGlvbnMuaW5zdHIgPT09ICdzdHJpbmcnXHJcbiAgICAgID8gZXhwb3J0QXNTdHJpbmcoZXhwb3J0T3B0aW9ucy5pbnN0ci50cmltKCksIG9wdGlvbnMsIGVuZENhbGxiYWNrKVxyXG4gICAgICA6IGRvRXhwb3J0KFxyXG4gICAgICAgICAgb3B0aW9ucyxcclxuICAgICAgICAgIGV4cG9ydE9wdGlvbnMuaW5zdHIgfHwgZXhwb3J0T3B0aW9ucy5vcHRpb25zLFxyXG4gICAgICAgICAgZW5kQ2FsbGJhY2tcclxuICAgICAgICApO1xyXG4gIH1cclxuXHJcbiAgLy8gTm8gaW5wdXQgc3BlY2lmaWVkLCBwYXNzIGFuIGVycm9yIG1lc3NhZ2UgdG8gdGhlIGNhbGxiYWNrXHJcbiAgbG9nKFxyXG4gICAgMSxcclxuICAgIGNsZWFyVGV4dChcclxuICAgICAgYFtjaGFydF0gTm8gaW5wdXQgc3BlY2lmaWVkLlxyXG4gICAgICAke0pTT04uc3RyaW5naWZ5KGV4cG9ydE9wdGlvbnMsIHVuZGVmaW5lZCwgJyAgJyl9LmBcclxuICAgIClcclxuICApO1xyXG5cclxuICByZXR1cm4gKFxyXG4gICAgZW5kQ2FsbGJhY2sgJiZcclxuICAgIGVuZENhbGxiYWNrKGZhbHNlLCB7XHJcbiAgICAgIGVycm9yOiB0cnVlLFxyXG4gICAgICBtZXNzYWdlOiAnTm8gaW5wdXQgc3BlY2lmaWVkLidcclxuICAgIH0pXHJcbiAgKTtcclxufTtcclxuXHJcbmV4cG9ydCBjb25zdCBiYXRjaEV4cG9ydCA9IChvcHRpb25zKSA9PiB7XHJcbiAgY29uc3QgYmF0Y2hGdW5jdGlvbnMgPSBbXTtcclxuXHJcbiAgLy8gU3BsaXQgYW5kIHBhaXIgdGhlIC0tYmF0Y2ggYXJndW1lbnRzXHJcbiAgZm9yIChsZXQgcGFpciBvZiBvcHRpb25zLmV4cG9ydC5iYXRjaC5zcGxpdCgnOycpKSB7XHJcbiAgICBwYWlyID0gcGFpci5zcGxpdCgnPScpO1xyXG4gICAgaWYgKHBhaXIubGVuZ3RoID09PSAyKSB7XHJcbiAgICAgIGJhdGNoRnVuY3Rpb25zLnB1c2goXHJcbiAgICAgICAgbmV3IFByb21pc2UoKHJlc29sdmUsIHJlamVjdCkgPT4ge1xyXG4gICAgICAgICAgc3RhcnRFeHBvcnQoXHJcbiAgICAgICAgICAgIHtcclxuICAgICAgICAgICAgICAuLi5vcHRpb25zLFxyXG4gICAgICAgICAgICAgIGV4cG9ydDoge1xyXG4gICAgICAgICAgICAgICAgLi4ub3B0aW9ucy5leHBvcnQsXHJcbiAgICAgICAgICAgICAgICBpbmZpbGU6IHBhaXJbMF0sXHJcbiAgICAgICAgICAgICAgICBvdXRmaWxlOiBwYWlyWzFdXHJcbiAgICAgICAgICAgICAgfVxyXG4gICAgICAgICAgICB9LFxyXG4gICAgICAgICAgICAoaW5mbywgZXJyb3IpID0+IHtcclxuICAgICAgICAgICAgICAvLyBUaHJvdyBhbiBlcnJvclxyXG4gICAgICAgICAgICAgIGlmIChlcnJvcikge1xyXG4gICAgICAgICAgICAgICAgcmV0dXJuIHJlamVjdChlcnJvcik7XHJcbiAgICAgICAgICAgICAgfVxyXG5cclxuICAgICAgICAgICAgICAvLyBTYXZlIHRoZSBiYXNlNjQgZnJvbSBhIGJ1ZmZlciB0byBhIGNvcnJlY3QgaW1hZ2UgZmlsZVxyXG4gICAgICAgICAgICAgIHdyaXRlRmlsZVN5bmMoXHJcbiAgICAgICAgICAgICAgICBpbmZvLm9wdGlvbnMuZXhwb3J0Lm91dGZpbGUsXHJcbiAgICAgICAgICAgICAgICBCdWZmZXIuZnJvbShpbmZvLmRhdGEsICdiYXNlNjQnKVxyXG4gICAgICAgICAgICAgICk7XHJcblxyXG4gICAgICAgICAgICAgIHJlc29sdmUoKTtcclxuICAgICAgICAgICAgfVxyXG4gICAgICAgICAgKTtcclxuICAgICAgICB9KVxyXG4gICAgICApO1xyXG4gICAgfVxyXG4gIH1cclxuXHJcbiAgLy8gS2lsbCB0aGUgcG9vbCBhZnRlciBhbGwgZXhwb3J0cyBhcmUgZG9uZVxyXG4gIFByb21pc2UuYWxsKGJhdGNoRnVuY3Rpb25zKVxyXG4gICAgLnRoZW4oKCkgPT4ge1xyXG4gICAgICBraWxsUG9vbCgpO1xyXG4gICAgfSlcclxuICAgIC5jYXRjaCgoZXJyb3IpID0+IHtcclxuICAgICAgbG9nKDEsIGBbY2hhcnRdIEVycm9yIGVuY291bnRlcmVkIGR1cmluZyBiYXRjaCBleHBvcnQ6ICR7ZXJyb3J9YCk7XHJcbiAgICAgIGtpbGxQb29sKCk7XHJcbiAgICB9KTtcclxufTtcclxuXHJcbmV4cG9ydCBjb25zdCBzaW5nbGVFeHBvcnQgPSAob3B0aW9ucykgPT4ge1xyXG4gIC8vIFVzZSBpbnN0ciBvciBpdHMgYWxpYXMsIG9wdGlvbnNcclxuICBvcHRpb25zLmV4cG9ydC5pbnN0ciA9IG9wdGlvbnMuZXhwb3J0Lmluc3RyIHx8IG9wdGlvbnMuZXhwb3J0Lm9wdGlvbnM7XHJcblxyXG4gIC8vIFBlcmZvcm0gYW4gZXhwb3J0XHJcbiAgc3RhcnRFeHBvcnQob3B0aW9ucywgKGluZm8sIGVycm9yKSA9PiB7XHJcbiAgICAvLyBFeGl0IHByb2Nlc3Mgd2hlbiBlcnJvclxyXG4gICAgaWYgKGVycm9yKSB7XHJcbiAgICAgIGxvZygxLCBgW2NsaV0gJHtlcnJvci5tZXNzYWdlfWApO1xyXG4gICAgICBwcm9jZXNzLmV4aXQoMSk7XHJcbiAgICB9XHJcblxyXG4gICAgY29uc3QgeyBvdXRmaWxlLCB0eXBlIH0gPSBpbmZvLm9wdGlvbnMuZXhwb3J0O1xyXG5cclxuICAgIC8vIFNhdmUgdGhlIGJhc2U2NCBmcm9tIGEgYnVmZmVyIHRvIGEgY29ycmVjdCBpbWFnZSBmaWxlXHJcbiAgICB3cml0ZUZpbGVTeW5jKFxyXG4gICAgICBvdXRmaWxlIHx8IGBjaGFydC4ke3R5cGV9YCxcclxuICAgICAgdHlwZSAhPT0gJ3N2ZycgPyBCdWZmZXIuZnJvbShpbmZvLmRhdGEsICdiYXNlNjQnKSA6IGluZm8uZGF0YVxyXG4gICAgKTtcclxuXHJcbiAgICAvLyBLaWxsIHRoZSBwb29sXHJcbiAgICBraWxsUG9vbCgpO1xyXG4gIH0pO1xyXG59O1xyXG5cclxuLyoqXHJcbiAqIEZ1bmN0aW9uIGZvciBjaG9vc2luZyBjaGFydCBzaXplIGFuZCBzY2FsZSBiYXNlZCBvbiBvcHRpb25zIHByaW9yaXRpemF0aW9uLlxyXG4gKlxyXG4gKiBAcGFyYW0ge29iamVjdH0gb3B0aW9ucyAtIEFsbCBvcHRpb25zIG9iamVjdC5cclxuICogQHJldHVybiB7b2JqZWN0fSAtIEFuIG9iamVjdCB3aXRoIHVwZGF0ZWQgc2l6ZSBhbmQgc2NhbGUgZm9yIGEgY2hhcnQuXHJcbiAqL1xyXG5leHBvcnQgY29uc3QgZmluZENoYXJ0U2l6ZSA9IChvcHRpb25zKSA9PiB7XHJcbiAgY29uc3QgeyBjaGFydCwgZXhwb3J0aW5nIH0gPVxyXG4gICAgb3B0aW9ucy5leHBvcnQ/Lm9wdGlvbnMgfHwgaXNDb3JyZWN0SlNPTihvcHRpb25zLmV4cG9ydD8uaW5zdHIpO1xyXG5cclxuICAvLyBTZWUgaWYgZ2xvYmFsT3B0aW9ucyBob2xkcyBjaGFydCBvciBleHBvcnRpbmcgc2l6ZVxyXG4gIGNvbnN0IGdsb2JhbE9wdGlvbnMgPSBpc0NvcnJlY3RKU09OKG9wdGlvbnMuZXhwb3J0Py5nbG9iYWxPcHRpb25zKTtcclxuXHJcbiAgLy8gU2VjdXJlIHNjYWxlIHZhbHVlXHJcbiAgbGV0IHNjYWxlID1cclxuICAgIG9wdGlvbnMuZXhwb3J0Py5zY2FsZSB8fFxyXG4gICAgZXhwb3J0aW5nPy5zY2FsZSB8fFxyXG4gICAgZ2xvYmFsT3B0aW9ucz8uZXhwb3J0aW5nPy5zY2FsZSB8fFxyXG4gICAgb3B0aW9ucy5leHBvcnQ/LmRlZmF1bHRTY2FsZSB8fFxyXG4gICAgMTtcclxuXHJcbiAgLy8gdGhlIHNjYWxlIGNhbm5vdCBiZSBsb3dlciB0aGFuIDAuMSBhbmQgY2Fubm90IGJlIGhpZ2hlciB0aGFuIDUuMFxyXG4gIHNjYWxlID0gTWF0aC5tYXgoMC4xLCBNYXRoLm1pbihzY2FsZSwgNS4wKSk7XHJcblxyXG4gIC8vIHdlIHdhbnQgdG8gcm91bmQgdGhlIG51bWJlcnMgbGlrZSAwLjIzMjM0IC0+IDAuMjNcclxuICBzY2FsZSA9IHJvdW5kTnVtYmVyKHNjYWxlLCAyKTtcclxuXHJcbiAgLy8gRmluZCBjaGFydCBzaXplIGFuZCBzY2FsZVxyXG4gIHJldHVybiB7XHJcbiAgICBoZWlnaHQ6XHJcbiAgICAgIG9wdGlvbnMuZXhwb3J0Py5oZWlnaHQgfHxcclxuICAgICAgZXhwb3J0aW5nPy5zb3VyY2VIZWlnaHQgfHxcclxuICAgICAgY2hhcnQ/LmhlaWdodCB8fFxyXG4gICAgICBnbG9iYWxPcHRpb25zPy5leHBvcnRpbmc/LnNvdXJjZUhlaWdodCB8fFxyXG4gICAgICBnbG9iYWxPcHRpb25zPy5jaGFydD8uaGVpZ2h0IHx8XHJcbiAgICAgIG9wdGlvbnMuZXhwb3J0Py5kZWZhdWx0SGVpZ2h0IHx8XHJcbiAgICAgIDQwMCxcclxuICAgIHdpZHRoOlxyXG4gICAgICBvcHRpb25zLmV4cG9ydD8ud2lkdGggfHxcclxuICAgICAgZXhwb3J0aW5nPy5zb3VyY2VXaWR0aCB8fFxyXG4gICAgICBjaGFydD8ud2lkdGggfHxcclxuICAgICAgZ2xvYmFsT3B0aW9ucz8uZXhwb3J0aW5nPy5zb3VyY2VXaWR0aCB8fFxyXG4gICAgICBnbG9iYWxPcHRpb25zPy5jaGFydD8ud2lkdGggfHxcclxuICAgICAgb3B0aW9ucy5leHBvcnQ/LmRlZmF1bHRXaWR0aCB8fFxyXG4gICAgICA2MDAsXHJcbiAgICBzY2FsZVxyXG4gIH07XHJcbn07XHJcblxyXG4vKipcclxuICogRnVuY3Rpb24gZm9yIGZpbmFsIG9wdGlvbnMgcHJlcGFyYXRpb24gYmVmb3JlIGV4cG9ydC5cclxuICpcclxuICogQHBhcmFtIHtvYmplY3R9IG9wdGlvbnMgLSBBbGwgb3B0aW9ucyBvYmplY3QuXHJcbiAqIEBwYXJhbSB7b2JqZWN0fSBjaGFydEpzb24gLSBDaGFydCBKU09OLlxyXG4gKiBAcGFyYW0ge2Z1bmN0aW9ufSBlbmRDYWxsYmFjayAtIFRoZSBlbmQgY2FsbGJhY2suXHJcbiAqIEBwYXJhbSB7c3RyaW5nfSBzdmcgLSBUaGUgU1ZHIHJlcHJlc2VudGF0aW9uLlxyXG4gKi9cclxuY29uc3QgZG9FeHBvcnQgPSAob3B0aW9ucywgY2hhcnRKc29uLCBlbmRDYWxsYmFjaywgc3ZnKSA9PiB7XHJcbiAgbGV0IHsgZXhwb3J0OiBleHBvcnRPcHRpb25zLCBjdXN0b21Db2RlOiBjdXN0b21Db2RlT3B0aW9ucyB9ID0gb3B0aW9ucztcclxuXHJcbiAgY29uc3QgYWxsb3dDb2RlRXhlY3V0aW9uU2NvcGVkID1cclxuICAgIHR5cGVvZiBjdXN0b21Db2RlT3B0aW9ucy5hbGxvd0NvZGVFeGVjdXRpb24gPT09ICdib29sZWFuJ1xyXG4gICAgICA/IGN1c3RvbUNvZGVPcHRpb25zLmFsbG93Q29kZUV4ZWN1dGlvblxyXG4gICAgICA6IGFsbG93Q29kZUV4ZWN1dGlvbjtcclxuXHJcbiAgaWYgKCFjdXN0b21Db2RlT3B0aW9ucykge1xyXG4gICAgY3VzdG9tQ29kZU9wdGlvbnMgPSBvcHRpb25zLmN1c3RvbUNvZGUgPSB7fTtcclxuICB9IGVsc2UgaWYgKGFsbG93Q29kZUV4ZWN1dGlvblNjb3BlZCkge1xyXG4gICAgaWYgKHR5cGVvZiBvcHRpb25zLmN1c3RvbUNvZGUucmVzb3VyY2VzID09PSAnc3RyaW5nJykge1xyXG4gICAgICAvLyBQcm9jZXNzIHJlc291cmNlc1xyXG4gICAgICBvcHRpb25zLmN1c3RvbUNvZGUucmVzb3VyY2VzID0gaGFuZGxlUmVzb3VyY2VzKFxyXG4gICAgICAgIG9wdGlvbnMuY3VzdG9tQ29kZS5yZXNvdXJjZXMsXHJcbiAgICAgICAgdG9Cb29sZWFuKG9wdGlvbnMuY3VzdG9tQ29kZS5hbGxvd0ZpbGVSZXNvdXJjZXMpXHJcbiAgICAgICk7XHJcbiAgICB9IGVsc2UgaWYgKCFvcHRpb25zLmN1c3RvbUNvZGUucmVzb3VyY2VzKSB7XHJcbiAgICAgIHRyeSB7XHJcbiAgICAgICAgY29uc3QgcmVzb3VyY2VzID0gcmVhZEZpbGVTeW5jKCdyZXNvdXJjZXMuanNvbicsICd1dGY4Jyk7XHJcbiAgICAgICAgb3B0aW9ucy5jdXN0b21Db2RlLnJlc291cmNlcyA9IGhhbmRsZVJlc291cmNlcyhcclxuICAgICAgICAgIHJlc291cmNlcyxcclxuICAgICAgICAgIHRvQm9vbGVhbihvcHRpb25zLmN1c3RvbUNvZGUuYWxsb3dGaWxlUmVzb3VyY2VzKVxyXG4gICAgICAgICk7XHJcbiAgICAgIH0gY2F0Y2ggKGVycikge1xyXG4gICAgICAgIGxvZygzLCBgW2NoYXJ0XSBUaGUgZGVmYXVsdCByZXNvdXJjZXMuanNvbiBmaWxlIG5vdCBmb3VuZC5gKTtcclxuICAgICAgfVxyXG4gICAgfVxyXG4gIH1cclxuXHJcbiAgLy8gSWYgdGhlIGFsbG93Q29kZUV4ZWN1dGlvbiBmbGFnIGlzbid0IHNldCwgd2Ugc2hvdWxkIHJlZnVzZSB0aGUgdXNhZ2VcclxuICAvLyBvZiBjYWxsYmFjaywgcmVzb3VyY2VzLCBhbmQgY3VzdG9tIGNvZGUuIEFkZGl0aW9uYWxseSwgdGhlIHdvcmtlciB3aWxsXHJcbiAgLy8gcmVmdXNlIHRvIHJ1biBhcmJpdHJhcnkgSmF2YVNjcmlwdC4gUHJpb3JpdGl6ZWQgc2hvdWxkIGJlIHRoZSBzY29wZWRcclxuICAvLyBvcHRpb24sIHRoZW4gd2Ugc2hvdWxkIHRha2UgYSBsb29rIGF0IHRoZSBvdmVyYWxsIHBvb2wgb3B0aW9uLlxyXG4gIGlmICghYWxsb3dDb2RlRXhlY3V0aW9uU2NvcGVkICYmIGN1c3RvbUNvZGVPcHRpb25zKSB7XHJcbiAgICBpZiAoXHJcbiAgICAgIGN1c3RvbUNvZGVPcHRpb25zLmNhbGxiYWNrIHx8XHJcbiAgICAgIGN1c3RvbUNvZGVPcHRpb25zLnJlc291cmNlcyB8fFxyXG4gICAgICBjdXN0b21Db2RlT3B0aW9ucy5jdXN0b21Db2RlXHJcbiAgICApIHtcclxuICAgICAgLy8gU2VuZCBiYWNrIGEgZnJpZW5kbHkgbWVzc2FnZSBzYXlpbmcgdGhhdCB0aGUgZXhwb3J0ZXIgZG9lcyBub3Qgc3VwcG9ydFxyXG4gICAgICAvLyB0aGVzZSBzZXR0aW5ncy5cclxuICAgICAgcmV0dXJuIChcclxuICAgICAgICBlbmRDYWxsYmFjayAmJlxyXG4gICAgICAgIGVuZENhbGxiYWNrKGZhbHNlLCB7XHJcbiAgICAgICAgICBlcnJvcjogdHJ1ZSxcclxuICAgICAgICAgIG1lc3NhZ2U6IGNsZWFyVGV4dChcclxuICAgICAgICAgICAgYFRoZSBjYWxsYmFjaywgcmVzb3VyY2VzIGFuZCBjdXN0b21Db2RlIGhhdmUgYmVlbiBkaXNhYmxlZCBmb3IgdGhpc1xyXG4gICAgICAgICAgICBzZXJ2ZXIuYFxyXG4gICAgICAgICAgKVxyXG4gICAgICAgIH0pXHJcbiAgICAgICk7XHJcbiAgICB9XHJcblxyXG4gICAgLy8gUmVzZXQgYWxsIGFkZGl0aW9uYWwgY3VzdG9tIGNvZGVcclxuICAgIGN1c3RvbUNvZGVPcHRpb25zLmNhbGxiYWNrID0gZmFsc2U7XHJcbiAgICBjdXN0b21Db2RlT3B0aW9ucy5yZXNvdXJjZXMgPSBmYWxzZTtcclxuICAgIGN1c3RvbUNvZGVPcHRpb25zLmN1c3RvbUNvZGUgPSBmYWxzZTtcclxuICB9XHJcblxyXG4gIC8vIENsZWFuIHByb3BlcnRpZXMgdG8ga2VlcCBpdCBsZWFuIGFuZCBtZWFuXHJcbiAgaWYgKGNoYXJ0SnNvbikge1xyXG4gICAgY2hhcnRKc29uLmNoYXJ0ID0gY2hhcnRKc29uLmNoYXJ0IHx8IHt9O1xyXG4gICAgY2hhcnRKc29uLmV4cG9ydGluZyA9IGNoYXJ0SnNvbi5leHBvcnRpbmcgfHwge307XHJcbiAgICBjaGFydEpzb24uZXhwb3J0aW5nLmVuYWJsZWQgPSBmYWxzZTtcclxuICB9XHJcblxyXG4gIGV4cG9ydE9wdGlvbnMuY29uc3RyID0gZXhwb3J0T3B0aW9ucy5jb25zdHIgfHwgJ2NoYXJ0JztcclxuICBleHBvcnRPcHRpb25zLnR5cGUgPSBmaXhUeXBlKGV4cG9ydE9wdGlvbnMudHlwZSwgZXhwb3J0T3B0aW9ucy5vdXRmaWxlKTtcclxuICBpZiAoZXhwb3J0T3B0aW9ucy50eXBlID09PSAnc3ZnJykge1xyXG4gICAgZXhwb3J0T3B0aW9ucy53aWR0aCA9IGZhbHNlO1xyXG4gIH1cclxuXHJcbiAgLy8gUHJlcGFyZSBnbG9iYWwgYW5kIHRoZW1lIG9wdGlvbnNcclxuICBbJ2dsb2JhbE9wdGlvbnMnLCAndGhlbWVPcHRpb25zJ10uZm9yRWFjaCgob3B0aW9uc05hbWUpID0+IHtcclxuICAgIHRyeSB7XHJcbiAgICAgIGlmIChleHBvcnRPcHRpb25zICYmIGV4cG9ydE9wdGlvbnNbb3B0aW9uc05hbWVdKSB7XHJcbiAgICAgICAgaWYgKFxyXG4gICAgICAgICAgdHlwZW9mIGV4cG9ydE9wdGlvbnNbb3B0aW9uc05hbWVdID09PSAnc3RyaW5nJyAmJlxyXG4gICAgICAgICAgZXhwb3J0T3B0aW9uc1tvcHRpb25zTmFtZV0uZW5kc1dpdGgoJy5qc29uJylcclxuICAgICAgICApIHtcclxuICAgICAgICAgIGV4cG9ydE9wdGlvbnNbb3B0aW9uc05hbWVdID0gaXNDb3JyZWN0SlNPTihcclxuICAgICAgICAgICAgcmVhZEZpbGVTeW5jKGV4cG9ydE9wdGlvbnNbb3B0aW9uc05hbWVdLCAndXRmOCcpLFxyXG4gICAgICAgICAgICB0cnVlXHJcbiAgICAgICAgICApO1xyXG4gICAgICAgIH0gZWxzZSB7XHJcbiAgICAgICAgICBleHBvcnRPcHRpb25zW29wdGlvbnNOYW1lXSA9IGlzQ29ycmVjdEpTT04oXHJcbiAgICAgICAgICAgIGV4cG9ydE9wdGlvbnNbb3B0aW9uc05hbWVdLFxyXG4gICAgICAgICAgICB0cnVlXHJcbiAgICAgICAgICApO1xyXG4gICAgICAgIH1cclxuICAgICAgfVxyXG4gICAgfSBjYXRjaCAoZXJyb3IpIHtcclxuICAgICAgZXhwb3J0T3B0aW9uc1tvcHRpb25zTmFtZV0gPSB7fTtcclxuICAgICAgbG9nKDEsIGBbY2hhcnRdIFRoZSAke29wdGlvbnNOYW1lfSBub3QgZm91bmQuYCk7XHJcbiAgICB9XHJcbiAgfSk7XHJcblxyXG4gIC8vIFByZXBhcmUgY3VzdG9tQ29kZVxyXG4gIGlmIChjdXN0b21Db2RlT3B0aW9ucy5hbGxvd0NvZGVFeGVjdXRpb24pIHtcclxuICAgIGN1c3RvbUNvZGVPcHRpb25zLmN1c3RvbUNvZGUgPSB3cmFwQXJvdW5kKFxyXG4gICAgICBjdXN0b21Db2RlT3B0aW9ucy5jdXN0b21Db2RlLFxyXG4gICAgICBjdXN0b21Db2RlT3B0aW9ucy5hbGxvd0ZpbGVSZXNvdXJjZXNcclxuICAgICk7XHJcbiAgfVxyXG5cclxuICAvLyBHZXQgdGhlIGNhbGxiYWNrXHJcbiAgaWYgKFxyXG4gICAgY3VzdG9tQ29kZU9wdGlvbnMgJiZcclxuICAgIGN1c3RvbUNvZGVPcHRpb25zLmNhbGxiYWNrICYmXHJcbiAgICBjdXN0b21Db2RlT3B0aW9ucy5jYWxsYmFjaz8uaW5kZXhPZigneycpIDwgMFxyXG4gICkge1xyXG4gICAgLy8gVGhlIGFsbG93RmlsZVJlc291cmNlcyBpcyBhbHdheXMgc2V0IHRvIGZhbHNlIGZvciBIVFRQIHJlcXVlc3RzIHRvIGF2b2lkXHJcbiAgICAvLyBpbmplY3RpbmcgYXJiaXRyYXJ5IGZpbGVzIGZyb20gdGhlIGZzXHJcbiAgICBpZiAoY3VzdG9tQ29kZU9wdGlvbnMuYWxsb3dGaWxlUmVzb3VyY2VzKSB7XHJcbiAgICAgIHRyeSB7XHJcbiAgICAgICAgY3VzdG9tQ29kZU9wdGlvbnMuY2FsbGJhY2sgPSByZWFkRmlsZVN5bmMoXHJcbiAgICAgICAgICBjdXN0b21Db2RlT3B0aW9ucy5jYWxsYmFjayxcclxuICAgICAgICAgICd1dGY4J1xyXG4gICAgICAgICk7XHJcbiAgICAgIH0gY2F0Y2ggKGVycm9yKSB7XHJcbiAgICAgICAgbG9nKDIsIGBbY2hhcnRdIEVycm9yIGxvYWRpbmcgY2FsbGJhY2s6ICR7ZXJyb3J9LmApO1xyXG4gICAgICAgIGN1c3RvbUNvZGVPcHRpb25zLmNhbGxiYWNrID0gZmFsc2U7XHJcbiAgICAgIH1cclxuICAgIH0gZWxzZSB7XHJcbiAgICAgIGN1c3RvbUNvZGVPcHRpb25zLmNhbGxiYWNrID0gZmFsc2U7XHJcbiAgICB9XHJcbiAgfVxyXG5cclxuICAvLyBTaXplIHNlYXJjaFxyXG4gIG9wdGlvbnMuZXhwb3J0ID0ge1xyXG4gICAgLi4ub3B0aW9ucy5leHBvcnQsXHJcbiAgICAuLi5maW5kQ2hhcnRTaXplKG9wdGlvbnMpXHJcbiAgfTtcclxuXHJcbiAgLy8gUG9zdCB0aGUgd29yayB0byB0aGUgcG9vbFxyXG4gIHBvc3RXb3JrKGV4cG9ydE9wdGlvbnMuc3RySW5qIHx8IGNoYXJ0SnNvbiB8fCBzdmcsIG9wdGlvbnMpXHJcbiAgICAudGhlbigocmVzdWx0KSA9PiBlbmRDYWxsYmFjayhyZXN1bHQpKVxyXG4gICAgLmNhdGNoKChlcnJvcikgPT4ge1xyXG4gICAgICBsb2coMCwgJ1tjaGFydF0gV2hlbiBwb3N0aW5nIHdvcms6JywgZXJyb3IpO1xyXG4gICAgICByZXR1cm4gZW5kQ2FsbGJhY2soZmFsc2UsIGVycm9yKTtcclxuICAgIH0pO1xyXG59O1xyXG5cclxuLyoqXHJcbiAqIEZ1bmN0aW9uIGZvciBzdHJhaWdodCBpbmplY3RpbmcgdGhlIGNvZGUuXHJcbiAqIERhbmdlcm91cyBhbmQgbXVzdCBiZSB1c2VkIGRlbGliZXJhdGVseSBieSBzb21lb25lIHdobyBzZXRzIHVwIGEgc2VydmVyXHJcbiAqIChzZWUgIC0tYWxsb3dDb2RlRXhlY3V0aW9uKS5cclxuICpcclxuICogQHBhcmFtIHtvYmplY3R9IG9wdGlvbnMgLSBBbGwgb3B0aW9ucyBvYmplY3QuXHJcbiAqIEBwYXJhbSB7ZnVuY3Rpb259IGVuZENhbGxiYWNrIC0gVGhlIGZ1bmN0aW9uIHRvIGNhbGwgd2hlbiBleHBvcnRpbmcgaXMgZG9uZS5cclxuICovXHJcbmNvbnN0IGRvU3RyYWlnaHRJbmplY3QgPSAob3B0aW9ucywgZW5kQ2FsbGJhY2spID0+IHtcclxuICB0cnkge1xyXG4gICAgbGV0IHN0ckluajtcclxuICAgIGxldCBpbnN0ciA9IG9wdGlvbnMuZXhwb3J0Lmluc3RyIHx8IG9wdGlvbnMuZXhwb3J0Lm9wdGlvbnM7XHJcblxyXG4gICAgaWYgKHR5cGVvZiBpbnN0ciAhPT0gJ3N0cmluZycpIHtcclxuICAgICAgLy8gVHJ5IHRvIHN0cmluZ2lmeSBvcHRpb25zXHJcbiAgICAgIHN0ckluaiA9IGluc3RyID0gb3B0aW9uc1N0cmluZ2lmeShcclxuICAgICAgICBpbnN0cixcclxuICAgICAgICBvcHRpb25zLmN1c3RvbUNvZGU/LmFsbG93Q29kZUV4ZWN1dGlvblxyXG4gICAgICApO1xyXG4gICAgfVxyXG4gICAgc3RySW5qID0gaW5zdHIucmVwbGFjZUFsbCgvXFx0fFxcbnxcXHIvZywgJycpLnRyaW0oKTtcclxuXHJcbiAgICAvLyBHZXQgcmlkIG9mIHRoZSA7XHJcbiAgICBpZiAoc3RySW5qW3N0ckluai5sZW5ndGggLSAxXSA9PT0gJzsnKSB7XHJcbiAgICAgIHN0ckluaiA9IHN0ckluai5zdWJzdHJpbmcoMCwgc3RySW5qLmxlbmd0aCAtIDEpO1xyXG4gICAgfVxyXG5cclxuICAgIC8vIFNhdmUgYXMgc3RyaWdodCBpbmplY3Qgc3RyaW5nXHJcbiAgICBvcHRpb25zLmV4cG9ydC5zdHJJbmogPSBzdHJJbmo7XHJcbiAgICByZXR1cm4gZG9FeHBvcnQob3B0aW9ucywgZmFsc2UsIGVuZENhbGxiYWNrKTtcclxuICB9IGNhdGNoIChlcnJvcikge1xyXG4gICAgY29uc3QgbWVzc2FnZSA9IGNsZWFyVGV4dChcclxuICAgICAgYE1hbGZvcm1lZCBpbnB1dCBkZXRlY3RlZCBmb3IgJHtvcHRpb25zLmV4cG9ydD8ucmVxdWVzdElkIHx8ICc/J306XHJcbiAgICAgIFBsZWFzZSBtYWtlIHN1cmUgdGhhdCB5b3VyIEpTT04vSmF2YVNjcmlwdCBvcHRpb25zXHJcbiAgICAgIGFyZSBzZW50IHVzaW5nIHRoZSBcIm9wdGlvbnNcIiBhdHRyaWJ1dGUsIGFuZCB0aGF0IGlmIHlvdSdyZSB1c2luZ1xyXG4gICAgICBTVkcsIGl0IGlzIHVuZXNjYXBlZC5gXHJcbiAgICApO1xyXG5cclxuICAgIGxvZygxLCBtZXNzYWdlKTtcclxuICAgIHJldHVybiAoXHJcbiAgICAgIGVuZENhbGxiYWNrICYmXHJcbiAgICAgIGVuZENhbGxiYWNrKFxyXG4gICAgICAgIGZhbHNlLFxyXG4gICAgICAgIEpTT04uc3RyaW5naWZ5KHtcclxuICAgICAgICAgIGVycm9yOiB0cnVlLFxyXG4gICAgICAgICAgbWVzc2FnZVxyXG4gICAgICAgIH0pXHJcbiAgICAgIClcclxuICAgICk7XHJcbiAgfVxyXG59O1xyXG5cclxuLyoqXHJcbiAqIFByZXBhcmVzIGFuIGlucHV0IGJlZm9yZSBleHBvcnRpbmcuXHJcbiAqXHJcbiAqIEBwYXJhbSB7c3RyaW5nfSBzdHJpbmdUb0V4cG9ydCAtIFN0cmluZyByZXByZXNlbnRhdGlvbiBvZiBTVkcvZXhwb3J0IG9wdGlvbnMuXHJcbiAqIEBwYXJhbSB7b2JqZWN0fSBvcHRpb25zIC0gQWxsIG9wdGlvbnMgb2JqZWN0LlxyXG4gKiBAcGFyYW0ge2Z1bmN0aW9ufSBlbmRDYWxsYmFjayAtIFRoZSBmdW5jdGlvbiB0byBjYWxsIHdoZW4gZXhwb3J0aW5nIGlzIGRvbmUuXHJcbiAqL1xyXG5jb25zdCBleHBvcnRBc1N0cmluZyA9IChzdHJpbmdUb0V4cG9ydCwgb3B0aW9ucywgZW5kQ2FsbGJhY2spID0+IHtcclxuICBjb25zdCB7IGFsbG93Q29kZUV4ZWN1dGlvbiB9ID0gb3B0aW9ucy5jdXN0b21Db2RlO1xyXG5cclxuICAvLyBDaGVjayBpZiBpdCBpcyBTVkdcclxuICBpZiAoXHJcbiAgICBzdHJpbmdUb0V4cG9ydC5pbmRleE9mKCc8c3ZnJykgPj0gMCB8fFxyXG4gICAgc3RyaW5nVG9FeHBvcnQuaW5kZXhPZignPD94bWwnKSA+PSAwXHJcbiAgKSB7XHJcbiAgICBsb2coNCwgJ1tjaGFydF0gUGFyc2luZyBpbnB1dCBhcyBTVkcuJyk7XHJcbiAgICByZXR1cm4gZG9FeHBvcnQob3B0aW9ucywgZmFsc2UsIGVuZENhbGxiYWNrLCBzdHJpbmdUb0V4cG9ydCk7XHJcbiAgfVxyXG5cclxuICB0cnkge1xyXG4gICAgLy8gVHJ5IHRvIHBhcnNlIHRvIEpTT04gYW5kIGNhbGwgdGhlIGRvRXhwb3J0IGZ1bmN0aW9uXHJcbiAgICBjb25zdCBjaGFydEpTT04gPSBKU09OLnBhcnNlKHN0cmluZ1RvRXhwb3J0LnJlcGxhY2VBbGwoL1xcdHxcXG58XFxyL2csICcgJykpO1xyXG5cclxuICAgIC8vIElmIGEgY29ycmVjdCBKU09OLCBkbyB0aGUgZXhwb3J0XHJcbiAgICByZXR1cm4gZG9FeHBvcnQob3B0aW9ucywgY2hhcnRKU09OLCBlbmRDYWxsYmFjayk7XHJcbiAgfSBjYXRjaCAoZXJyb3IpIHtcclxuICAgIC8vIE5vdCBhIHZhbGlkIEpTT05cclxuICAgIGlmICh0b0Jvb2xlYW4oYWxsb3dDb2RlRXhlY3V0aW9uKSkge1xyXG4gICAgICByZXR1cm4gZG9TdHJhaWdodEluamVjdChvcHRpb25zLCBlbmRDYWxsYmFjayk7XHJcbiAgICB9IGVsc2Uge1xyXG4gICAgICAvLyBEbyBub3QgYWxsb3cgc3RyYWlnaHQgaW5qZWN0aW9uIHdpdGhvdXQgdGhlIGFsbG93Q29kZUV4ZWN1dGlvbiBmbGFnXHJcbiAgICAgIHJldHVybiAoXHJcbiAgICAgICAgZW5kQ2FsbGJhY2sgJiZcclxuICAgICAgICBlbmRDYWxsYmFjayhmYWxzZSwge1xyXG4gICAgICAgICAgZXJyb3I6IHRydWUsXHJcbiAgICAgICAgICBtZXNzYWdlOiBjbGVhclRleHQoXHJcbiAgICAgICAgICAgIGBPbmx5IEpTT04gY29uZmlndXJhdGlvbnMgYW5kIFNWRyBpcyBhbGxvd2VkIGZvciB0aGlzIHNlcnZlci4gSWZcclxuICAgICAgICAgICAgdGhpcyBpcyB5b3VyIHNlcnZlciwgSmF2YVNjcmlwdCBleHBvcnRpbmcgY2FuIGJlIGVuYWJsZWQgYnkgc3RhcnRpbmdcclxuICAgICAgICAgICAgdGhlIHNlcnZlciB3aXRoIHRoZSAtLWFsbG93Q29kZUV4ZWN1dGlvbiBmbGFnLmBcclxuICAgICAgICAgIClcclxuICAgICAgICB9KVxyXG4gICAgICApO1xyXG4gICAgfVxyXG4gIH1cclxufTtcclxuXHJcbmV4cG9ydCBjb25zdCBnZXRBbGxvd0NvZGVFeGVjdXRpb24gPSAoKSA9PiBhbGxvd0NvZGVFeGVjdXRpb247XHJcblxyXG5leHBvcnQgY29uc3Qgc2V0QWxsb3dDb2RlRXhlY3V0aW9uID0gKHZhbHVlKSA9PiB7XHJcbiAgYWxsb3dDb2RlRXhlY3V0aW9uID0gdG9Cb29sZWFuKHZhbHVlKTtcclxufTtcclxuXHJcbi8qKlxyXG4gKiBTdGFydHMgYW4gZXhwb3J0aW5nIHByb2Nlc3NcclxuICpcclxuICogQHBhcmFtIHtvYmplY3R9IHNldHRpbmdzIC0gU2V0dGluZ3MgZm9yIGV4cG9ydC5cclxuICogQHBhcmFtIHtmdW5jdGlvbn0gZW5kQ2FsbGJhY2sgLSBUaGUgZnVuY3Rpb24gdG8gY2FsbCB3aGVuIGV4cG9ydGluZyBpcyBkb25lLlxyXG4gKi9cclxuZXhwb3J0IGRlZmF1bHQge1xyXG4gIGJhdGNoRXhwb3J0LFxyXG4gIHNpbmdsZUV4cG9ydCxcclxuICBnZXRBbGxvd0NvZGVFeGVjdXRpb24sXHJcbiAgc2V0QWxsb3dDb2RlRXhlY3V0aW9uLFxyXG4gIHN0YXJ0RXhwb3J0LFxyXG4gIGZpbmRDaGFydFNpemVcclxufTtcclxuIiwiLyoqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKipcclxuXHJcbkhpZ2hjaGFydHMgRXhwb3J0IFNlcnZlclxyXG5cclxuQ29weXJpZ2h0IChjKSAyMDE2LTIwMjMsIEhpZ2hzb2Z0XHJcblxyXG5MaWNlbmNlZCB1bmRlciB0aGUgTUlUIGxpY2VuY2UuXHJcblxyXG5BZGRpdGlvbmFsbHkgYSB2YWxpZCBIaWdoY2hhcnRzIGxpY2Vuc2UgaXMgcmVxdWlyZWQgZm9yIHVzZS5cclxuXHJcblNlZSBMSUNFTlNFIGZpbGUgaW4gcm9vdCBmb3IgZGV0YWlscy5cclxuXHJcbioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKiovXHJcblxyXG5pbXBvcnQgeyB2NCBhcyB1dWlkIH0gZnJvbSAndXVpZCc7XHJcblxyXG5pbXBvcnQgeyBnZXRBbGxvd0NvZGVFeGVjdXRpb24sIHN0YXJ0RXhwb3J0IH0gZnJvbSAnLi4vLi4vY2hhcnQuanMnO1xyXG5pbXBvcnQgeyBnZXRPcHRpb25zLCBtZXJnZUNvbmZpZ09wdGlvbnMgfSBmcm9tICcuLi8uLi9jb25maWcuanMnO1xyXG5pbXBvcnQgeyBsb2cgfSBmcm9tICcuLi8uLi9sb2dnZXIuanMnO1xyXG5pbXBvcnQge1xyXG4gIGNsZWFyVGV4dCxcclxuICBmaXhUeXBlLFxyXG4gIGlzQ29ycmVjdEpTT04sXHJcbiAgaXNQcml2YXRlUmFuZ2VVcmxGb3VuZCxcclxuICBvcHRpb25zU3RyaW5naWZ5LFxyXG4gIG1lYXN1cmVUaW1lXHJcbn0gZnJvbSAnLi4vLi4vdXRpbHMuanMnO1xyXG5cclxuLy8gUmV2ZXJzZWQgTUlNRSB0eXBlc1xyXG5jb25zdCByZXZlcnNlZE1pbWUgPSB7XHJcbiAgcG5nOiAnaW1hZ2UvcG5nJyxcclxuICBqcGVnOiAnaW1hZ2UvanBlZycsXHJcbiAgZ2lmOiAnaW1hZ2UvZ2lmJyxcclxuICBwZGY6ICdhcHBsaWNhdGlvbi9wZGYnLFxyXG4gIHN2ZzogJ2ltYWdlL3N2Zyt4bWwnXHJcbn07XHJcblxyXG4vLyBUaGUgcmVxdWVzdHMgY291bnRlclxyXG5sZXQgcmVxdWVzdHNDb3VudGVyID0gMDtcclxuXHJcbmNvbnN0IGJlbmNobWFyayA9IGZhbHNlO1xyXG5cclxuLy8gVGhlIGFycmF5IG9mIGNhbGxiYWNrcyB0byBjYWxsIGJlZm9yZSBhIHJlcXVlc3RcclxuY29uc3QgYmVmb3JlUmVxdWVzdCA9IFtdO1xyXG5cclxuLy8gVGhlIGFycmF5IG9mIGNhbGxiYWNrcyB0byBjYWxsIGFmdGVyIGEgcmVxdWVzdFxyXG5jb25zdCBhZnRlclJlcXVlc3QgPSBbXTtcclxuXHJcbi8qKlxyXG4gKiBDYWxscyBjYWxsYmFja3MuXHJcbiAqXHJcbiAqIEBwYXJhbSB7QXJyYXl9IGNhbGxiYWNrcyAtIEFuIGFycmF5IG9mIGNhbGxiYWNrcy5cclxuICogQHBhcmFtIHtvYmplY3R9IHJlcXVlc3QgLSBUaGUgcmVxdWVzdC5cclxuICogQHBhcmFtIHtvYmplY3R9IHJlc3BvbnNlIC0gVGhlIHJlc3BvbnNlLlxyXG4gKiBAcGFyYW0ge29iamVjdH0gZGF0YSAtIFRoZSBkYXRhIHRvIHNlbmQgdG8gY2FsbGJhY2tzLlxyXG4gKiBAcmV0dXJuIHtvYmplY3R9IC0gVGhlIHJlc3VsdCBmcm9tIGEgY2FsbGJhY2suXHJcbiAqL1xyXG5jb25zdCBkb0NhbGxiYWNrcyA9IChjYWxsYmFja3MsIHJlcXVlc3QsIHJlc3BvbnNlLCBkYXRhKSA9PiB7XHJcbiAgbGV0IHJlc3VsdCA9IHRydWU7XHJcbiAgY29uc3QgeyBpZCwgdW5pcXVlSWQsIHR5cGUsIGJvZHkgfSA9IGRhdGE7XHJcblxyXG4gIGNhbGxiYWNrcy5zb21lKChjYWxsYmFjaykgPT4ge1xyXG4gICAgaWYgKGNhbGxiYWNrKSB7XHJcbiAgICAgIGxldCBjYWxsUmVzcG9uc2UgPSBjYWxsYmFjayhyZXF1ZXN0LCByZXNwb25zZSwgaWQsIHVuaXF1ZUlkLCB0eXBlLCBib2R5KTtcclxuXHJcbiAgICAgIGlmIChjYWxsUmVzcG9uc2UgIT09IHVuZGVmaW5lZCAmJiBjYWxsUmVzcG9uc2UgIT09IHRydWUpIHtcclxuICAgICAgICByZXN1bHQgPSBjYWxsUmVzcG9uc2U7XHJcbiAgICAgIH1cclxuXHJcbiAgICAgIHJldHVybiB0cnVlO1xyXG4gICAgfVxyXG4gIH0pO1xyXG5cclxuICByZXR1cm4gcmVzdWx0O1xyXG59O1xyXG5cclxuLyoqXHJcbiAqIEhhbmRsZXMgYW4gZXhwb3J0LlxyXG4gKlxyXG4gKiBAcGFyYW0ge29iamVjdH0gcmVxdWVzdCAtIFRoZSByZXF1ZXN0LlxyXG4gKiBAcGFyYW0ge29iamVjdH0gcmVzcG9uc2UgLSBUaGUgcmVzcG9uc2UuXHJcbiAqL1xyXG5jb25zdCBleHBvcnRIYW5kbGVyID0gKHJlcXVlc3QsIHJlc3BvbnNlKSA9PiB7XHJcbiAgLy8gU3RhcnQgY291bnRpbmcgdGltZVxyXG4gIGNvbnN0IHN0b3BDb3VudGVyID0gbWVhc3VyZVRpbWUoKTtcclxuXHJcbiAgLy8gR2V0IHRoZSBjdXJyZW50IHNlcnZlcidzIGdlbmVyYWwgb3B0aW9uc1xyXG4gIGNvbnN0IGRlZmF1bHRPcHRpb25zID0gZ2V0T3B0aW9ucygpO1xyXG5cclxuICAvLyBJbml0IGRlZmF1bHQgb3B0aW9uc1xyXG4gIGlmIChiZW5jaG1hcmspIHtcclxuICAgIGNvbnNvbGUubG9nKCdJbml0IGRlZmF1bHQgb3B0aW9uczonLCBzdG9wQ291bnRlcigpLCAnbXMuJyk7XHJcbiAgfVxyXG5cclxuICBjb25zdCBib2R5ID0gcmVxdWVzdC5ib2R5O1xyXG4gIGNvbnN0IGlkID0gKytyZXF1ZXN0c0NvdW50ZXI7XHJcbiAgY29uc3QgdW5pcXVlSWQgPSB1dWlkKCkucmVwbGFjZSgvLS9nLCAnJyk7XHJcbiAgbGV0IHR5cGUgPSBmaXhUeXBlKGJvZHkudHlwZSk7XHJcblxyXG4gIC8vIEZpeCB0eXBlXHJcbiAgaWYgKGJlbmNobWFyaykge1xyXG4gICAgY29uc29sZS5sb2coJ0ZpeCB0eXBlOicsIHN0b3BDb3VudGVyKCksICdtcy4nKTtcclxuICB9XHJcblxyXG4gIC8vIFRocm93ICdCYWQgUmVxdWVzdCcgaWYgdGhlcmUncyBubyBib2R5XHJcbiAgaWYgKCFib2R5KSB7XHJcbiAgICByZXR1cm4gcmVzcG9uc2Uuc3RhdHVzKDQwMCkuc2VuZChcclxuICAgICAgY2xlYXJUZXh0KFxyXG4gICAgICAgIGBCb2R5IGlzIHJlcXVpcmVkLiBTZW5kaW5nIGEgYm9keT8gTWFrZSBzdXJlIHlvdXIgQ29udGVudC10eXBlIGhlYWRlclxyXG4gICAgICAgIGlzIGNvcnJlY3QuIEFjY2VwdGVkIGlzIGFwcGxpY2F0aW9uL2pzb24gYW5kIG11bHRpcGFydC9mb3JtLWRhdGEuYFxyXG4gICAgICApXHJcbiAgICApO1xyXG4gIH1cclxuXHJcbiAgLy8gQWxsIG9mIHRoZSBiZWxvdyBjYW4gYmUgdXNlZFxyXG4gIGxldCBpbnN0ciA9IGlzQ29ycmVjdEpTT04oYm9keS5pbmZpbGUgfHwgYm9keS5vcHRpb25zIHx8IGJvZHkuZGF0YSk7XHJcblxyXG4gIC8vIElzIGNvcnJlY3QgSlNPTlxyXG4gIGlmIChiZW5jaG1hcmspIHtcclxuICAgIGNvbnNvbGUubG9nKCdJcyBjb3JyZWN0IEpTT046Jywgc3RvcENvdW50ZXIoKSwgJ21zLicpO1xyXG4gIH1cclxuXHJcbiAgLy8gVGhyb3cgJ0JhZCBSZXF1ZXN0JyBpZiB0aGVyZSdzIG5vIEpTT04gb3IgU1ZHIHRvIGV4cG9ydFxyXG4gIGlmICghaW5zdHIgJiYgIWJvZHkuc3ZnKSB7XHJcbiAgICBsb2coXHJcbiAgICAgIDIsXHJcbiAgICAgIGNsZWFyVGV4dChcclxuICAgICAgICBgUmVxdWVzdCAke3VuaXF1ZUlkfSBmcm9tICR7XHJcbiAgICAgICAgICByZXF1ZXN0LmhlYWRlcnNbJ3gtZm9yd2FyZGVkLWZvciddIHx8IHJlcXVlc3QuY29ubmVjdGlvbi5yZW1vdGVBZGRyZXNzXHJcbiAgICAgICAgfSB3YXMgaW5jb3JyZWN0LiBDaGVjayB5b3VyIHBheWxvYWQuYFxyXG4gICAgICApXHJcbiAgICApO1xyXG5cclxuICAgIHJldHVybiByZXNwb25zZS5zdGF0dXMoNDAwKS5zZW5kKFxyXG4gICAgICBjbGVhclRleHQoXHJcbiAgICAgICAgYE5vIGNvcnJlY3QgY2hhcnQgZGF0YSBmb3VuZC4gUGxlYXNlIG1ha2Ugc3VyZSB5b3UgYXJlIHVzaW5nXHJcbiAgICAgICAgYXBwbGljYXRpb24vanNvbiBvciBtdWx0aXBhcnQvZm9ybS1kYXRhIGhlYWRlcnMsIGFuZCB0aGF0IHRoZSBjaGFydFxyXG4gICAgICAgIGRhdGEgaXMgaW4gdGhlICdpbmZpbGUnLCAnb3B0aW9ucycgb3IgJ2RhdGEnIGF0dHJpYnV0ZSBpZiBzZW5kaW5nXHJcbiAgICAgICAgSlNPTiBvciBpbiB0aGUgJ3N2ZycgaWYgc2VuZGluZyBTVkcuYFxyXG4gICAgICApXHJcbiAgICApO1xyXG4gIH1cclxuXHJcbiAgbGV0IGNhbGxSZXNwb25zZSA9IGZhbHNlO1xyXG5cclxuICAvLyBDYWxsIHRoZSBiZWZvcmUgcmVxdWVzdCBmdW5jdGlvbnNcclxuICBjYWxsUmVzcG9uc2UgPSBkb0NhbGxiYWNrcyhiZWZvcmVSZXF1ZXN0LCByZXF1ZXN0LCByZXNwb25zZSwge1xyXG4gICAgaWQsXHJcbiAgICB1bmlxdWVJZCxcclxuICAgIHR5cGUsXHJcbiAgICBib2R5XHJcbiAgfSk7XHJcblxyXG4gIC8vIERvIGNhbGxiYWNrc1xyXG4gIGlmIChiZW5jaG1hcmspIHtcclxuICAgIGNvbnNvbGUubG9nKCdEbyBjYWxsYmFja3M6Jywgc3RvcENvdW50ZXIoKSwgJ21zLicpO1xyXG4gIH1cclxuXHJcbiAgLy8gQmxvY2sgdGhlIHJlcXVlc3QgaWYgb25lIG9mIGEgY2FsbGJhY2tzIGZhaWxlZFxyXG4gIGlmIChjYWxsUmVzcG9uc2UgIT09IHRydWUpIHtcclxuICAgIHJldHVybiByZXNwb25zZS5zZW5kKGNhbGxSZXNwb25zZSk7XHJcbiAgfVxyXG5cclxuICBsZXQgY29ubmVjdGlvbkFib3J0ZWQgPSBmYWxzZTtcclxuXHJcbiAgLy8gSW4gY2FzZSB0aGUgY29ubmVjdGlvbiBpcyBjbG9zZWQsIGZvcmNlIHRvIGFib3J0IGZ1cnRoZXIgYWN0aW9uc1xyXG4gIHJlcXVlc3Quc29ja2V0Lm9uKCdjbG9zZScsICgpID0+IHtcclxuICAgIGNvbm5lY3Rpb25BYm9ydGVkID0gdHJ1ZTtcclxuICB9KTtcclxuXHJcbiAgbG9nKDQsIGBbZXhwb3J0XSBHb3QgYW4gaW5jb21pbmcgSFRUUCByZXF1ZXN0ICR7dW5pcXVlSWR9LmApO1xyXG5cclxuICBib2R5LmNvbnN0ciA9ICh0eXBlb2YgYm9keS5jb25zdHIgPT09ICdzdHJpbmcnICYmIGJvZHkuY29uc3RyKSB8fCAnY2hhcnQnO1xyXG5cclxuICAvLyBHYXRoZXIgYW5kIG9yZ2FuaXplIG9wdGlvbnMgZnJvbSB0aGUgcGF5bG9hZFxyXG4gIGNvbnN0IHJlcXVlc3RPcHRpb25zID0ge1xyXG4gICAgZXhwb3J0OiB7XHJcbiAgICAgIGluc3RyLFxyXG4gICAgICB0eXBlLFxyXG4gICAgICBjb25zdHI6IGJvZHkuY29uc3RyWzBdLnRvTG93ZXJDYXNlKCkgKyBib2R5LmNvbnN0ci5zdWJzdHIoMSksXHJcbiAgICAgIGhlaWdodDogYm9keS5oZWlnaHQsXHJcbiAgICAgIHdpZHRoOiBib2R5LndpZHRoLFxyXG4gICAgICBzY2FsZTogYm9keS5zY2FsZSB8fCBkZWZhdWx0T3B0aW9ucy5leHBvcnQuc2NhbGUsXHJcbiAgICAgIGdsb2JhbE9wdGlvbnM6IGlzQ29ycmVjdEpTT04oYm9keS5nbG9iYWxPcHRpb25zLCB0cnVlKSxcclxuICAgICAgdGhlbWVPcHRpb25zOiBpc0NvcnJlY3RKU09OKGJvZHkudGhlbWVPcHRpb25zLCB0cnVlKVxyXG4gICAgfSxcclxuICAgIGN1c3RvbUNvZGU6IHtcclxuICAgICAgYWxsb3dDb2RlRXhlY3V0aW9uOiBnZXRBbGxvd0NvZGVFeGVjdXRpb24oKSxcclxuICAgICAgYWxsb3dGaWxlUmVzb3VyY2VzOiBmYWxzZSxcclxuICAgICAgcmVzb3VyY2VzOiBpc0NvcnJlY3RKU09OKGJvZHkucmVzb3VyY2VzLCB0cnVlKSxcclxuICAgICAgY2FsbGJhY2s6IGJvZHkuY2FsbGJhY2ssXHJcbiAgICAgIGN1c3RvbUNvZGU6IGJvZHkuY3VzdG9tQ29kZVxyXG4gICAgfVxyXG4gIH07XHJcblxyXG4gIC8vIE9yZ2FuaXplIG9wdGlvbnNcclxuICBpZiAoYmVuY2htYXJrKSB7XHJcbiAgICBjb25zb2xlLmxvZygnT3JnYW5pemUgb3B0aW9uczonLCBzdG9wQ291bnRlcigpLCAnbXMuJyk7XHJcbiAgfVxyXG5cclxuICBpZiAoaW5zdHIpIHtcclxuICAgIC8vIFN0cmluZ2lmeSBKU09OIHdpdGggb3B0aW9uc1xyXG4gICAgcmVxdWVzdE9wdGlvbnMuZXhwb3J0Lmluc3RyID0gb3B0aW9uc1N0cmluZ2lmeShcclxuICAgICAgaW5zdHIsXHJcbiAgICAgIHJlcXVlc3RPcHRpb25zLmN1c3RvbUNvZGUuYWxsb3dDb2RlRXhlY3V0aW9uXHJcbiAgICApO1xyXG5cclxuICAgIC8vIFN0cmluZ2lmeSBKU09OIHdpdGggb3B0aW9uc1xyXG4gICAgaWYgKGJlbmNobWFyaykge1xyXG4gICAgICBjb25zb2xlLmxvZygnU3RyaW5naWZ5IEpTT04gd2l0aCBvcHRpb25zOicsIHN0b3BDb3VudGVyKCksICdtcy4nKTtcclxuICAgIH1cclxuICB9XHJcblxyXG4gIC8vIE1lcmdlIHRoZSByZXF1ZXN0IG9wdGlvbnMgaW50byBkZWZhdWx0IG9uZXNcclxuICBjb25zdCBvcHRpb25zID0gbWVyZ2VDb25maWdPcHRpb25zKGRlZmF1bHRPcHRpb25zLCByZXF1ZXN0T3B0aW9ucyk7XHJcblxyXG4gIC8vIE1lcmdlIGNvbmZpZyBvcHRpb25zXHJcbiAgaWYgKGJlbmNobWFyaykge1xyXG4gICAgY29uc29sZS5sb2coJ01lcmdlIGNvbmZpZyBvcHRpb25zOicsIHN0b3BDb3VudGVyKCksICdtcy4nKTtcclxuICB9XHJcblxyXG4gIC8vIFNhdmUgdGhlIEpTT04gaWYgZXhpc3RzXHJcbiAgb3B0aW9ucy5leHBvcnQub3B0aW9ucyA9IGluc3RyO1xyXG5cclxuICAvLyBMYXN0bHksIGFkZCB0aGUgc2VydmVyIHNwZWNpZmljIGFyZ3VtZW50cyBpbnRvIG9wdGlvbnMgYXMgcGF5bG9hZFxyXG4gIG9wdGlvbnMucGF5bG9hZCA9IHtcclxuICAgIHN2ZzogYm9keS5zdmcgfHwgZmFsc2UsXHJcbiAgICBiNjQ6IGJvZHkuYjY0IHx8IGZhbHNlLFxyXG4gICAgZGF0YU9wdGlvbnM6IGlzQ29ycmVjdEpTT04oYm9keS5kYXRhT3B0aW9ucywgdHJ1ZSksXHJcbiAgICBub0Rvd25sb2FkOiBib2R5Lm5vRG93bmxvYWQgfHwgZmFsc2UsXHJcbiAgICByZXF1ZXN0SWQ6IHVuaXF1ZUlkXHJcbiAgfTtcclxuXHJcbiAgLy8gU2V0dGluZyBwYXlsb2FkXHJcbiAgaWYgKGJlbmNobWFyaykge1xyXG4gICAgY29uc29sZS5sb2coJ1NldHRpbmcgcGF5bG9hZDonLCBzdG9wQ291bnRlcigpLCAnbXMuJyk7XHJcbiAgfVxyXG5cclxuICAvLyBUZXN0IHhsaW5rOmhyZWYgZWxlbWVudHMgZnJvbSBwYXlsb2FkJ3MgU1ZHXHJcbiAgaWYgKGJvZHkuc3ZnICYmIGlzUHJpdmF0ZVJhbmdlVXJsRm91bmQob3B0aW9ucy5wYXlsb2FkLnN2ZykpIHtcclxuICAgIHJldHVybiByZXNwb25zZVxyXG4gICAgICAuc3RhdHVzKDQwMClcclxuICAgICAgLnNlbmQoXHJcbiAgICAgICAgJ1NWRyBwb3RlbnRpYWxseSBjb250YWluIGF0IGxlYXN0IG9uZSBmb3JiaWRkZW4gVVJMIGluIHhsaW5rOmhyZWYgZWxlbWVudC4nXHJcbiAgICAgICk7XHJcbiAgfVxyXG5cclxuICAvLyBDaGVjayBVUkwgcmFuZ2VcclxuICBpZiAoYmVuY2htYXJrKSB7XHJcbiAgICBjb25zb2xlLmxvZygnQ2hlY2sgVVJMIHJhbmdlOicsIHN0b3BDb3VudGVyKCksICdtcy4nKTtcclxuICB9XHJcblxyXG4gIC8vIFN0YXJ0IHRoZSBleHBvcnQgcHJvY2Vzc1xyXG4gIHN0YXJ0RXhwb3J0KG9wdGlvbnMsIChpbmZvLCBlcnJvcikgPT4ge1xyXG4gICAgLy8gUmVtb3ZlIHRoZSBjbG9zZSBldmVudCBmcm9tIHRoZSBzb2NrZXRcclxuICAgIHJlcXVlc3Quc29ja2V0LnJlbW92ZUFsbExpc3RlbmVycygnY2xvc2UnKTtcclxuXHJcbiAgICAvLyBBZnRlciBQdXBwZXRlZXIgZXhwb3J0aW5nXHJcbiAgICBpZiAoYmVuY2htYXJrKSB7XHJcbiAgICAgIGNvbnNvbGUubG9nKCdBZnRlciBQdXBwZXRlZXIgZXhwb3J0aW5nOicsIHN0b3BDb3VudGVyKCksICdtcy4nLCAnXFxuJyk7XHJcbiAgICB9XHJcblxyXG4gICAgLy8gSWYgdGhlIGNvbm5lY3Rpb24gd2FzIGNsb3NlZCwgZG8gbm90aGluZ1xyXG4gICAgaWYgKGNvbm5lY3Rpb25BYm9ydGVkKSB7XHJcbiAgICAgIHJldHVybiBsb2coXHJcbiAgICAgICAgMyxcclxuICAgICAgICBjbGVhclRleHQoXHJcbiAgICAgICAgICBgW2V4cG9ydF0gVGhlIGNsaWVudCBjbG9zZWQgdGhlIGNvbm5lY3Rpb24gYmVmb3JlIHRoZSBjaGFydCB3YXMgZG9uZVxyXG4gICAgICAgICAgcHJvY2Vzc2luZy5gXHJcbiAgICAgICAgKVxyXG4gICAgICApO1xyXG4gICAgfVxyXG5cclxuICAgIC8vIElmIGVycm9yLCByZXR1cm4gaXRcclxuICAgIGlmIChlcnJvcikge1xyXG4gICAgICBsb2coXHJcbiAgICAgICAgMSxcclxuICAgICAgICBjbGVhclRleHQoXHJcbiAgICAgICAgICBgW2V4cG9ydF0gV29yazogJHt1bmlxdWVJZH0gY291bGQgbm90IGJlIGNvbXBsZXRlZCwgc2VuZGluZzpcclxuICAgICAgICAgICR7ZXJyb3J9YFxyXG4gICAgICAgIClcclxuICAgICAgKTtcclxuICAgICAgcmV0dXJuIHJlc3BvbnNlLnN0YXR1cyg0MDApLnNlbmQoZXJyb3IubWVzc2FnZSk7XHJcbiAgICB9XHJcblxyXG4gICAgLy8gSWYgZGF0YSBpcyBtaXNzaW5nLCByZXR1cm4gdGhlIGVycm9yXHJcbiAgICBpZiAoIWluZm8gfHwgIWluZm8uZGF0YSkge1xyXG4gICAgICBsb2coXHJcbiAgICAgICAgMSxcclxuICAgICAgICBjbGVhclRleHQoXHJcbiAgICAgICAgICBgW2V4cG9ydF0gVW5leHBlY3RlZCByZXR1cm4gZnJvbSBjaGFydCBnZW5lcmF0aW9uLCBwbGVhc2UgY2hlY2sgeW91clxyXG4gICAgICAgICAgZGF0YSBSZXF1ZXN0OiAke3VuaXF1ZUlkfSBpcyAke2luZm8uZGF0YX0uYFxyXG4gICAgICAgIClcclxuICAgICAgKTtcclxuICAgICAgcmV0dXJuIHJlc3BvbnNlXHJcbiAgICAgICAgLnN0YXR1cyg0MDApXHJcbiAgICAgICAgLnNlbmQoXHJcbiAgICAgICAgICAnVW5leHBlY3RlZCByZXR1cm4gZnJvbSBjaGFydCBnZW5lcmF0aW9uLCBwbGVhc2UgY2hlY2sgeW91ciBkYXRhLidcclxuICAgICAgICApO1xyXG4gICAgfVxyXG5cclxuICAgIC8vIEdldCB0aGUgdHlwZSBmcm9tIG9wdGlvbnNcclxuICAgIHR5cGUgPSBpbmZvLm9wdGlvbnMuZXhwb3J0LnR5cGU7XHJcblxyXG4gICAgLy8gVGhlIGFmdGVyIHJlcXVlc3QgY2FsbGJhY2tzXHJcbiAgICBkb0NhbGxiYWNrcyhhZnRlclJlcXVlc3QsIHJlcXVlc3QsIHJlc3BvbnNlLCB7IGlkLCBib2R5OiBpbmZvLmRhdGEgfSk7XHJcblxyXG4gICAgaWYgKGluZm8uZGF0YSkge1xyXG4gICAgICAvLyBJZiBvbmx5IGJhc2U2NCBpcyByZXF1aXJlZCwgcmV0dXJuIGl0XHJcbiAgICAgIGlmIChib2R5LmI2NCkge1xyXG4gICAgICAgIC8vIENoZWNrIGlmIGl0IGlzIGFscmVhZHkgYmFzZTY0IG9yIGEgcmF3IFNWR1xyXG4gICAgICAgIGlmICh0eXBlID09PSAncGRmJykge1xyXG4gICAgICAgICAgcmV0dXJuIHJlc3BvbnNlLnNlbmQoXHJcbiAgICAgICAgICAgIEJ1ZmZlci5mcm9tKGluZm8uZGF0YSwgJ3V0ZjgnKS50b1N0cmluZygnYmFzZTY0JylcclxuICAgICAgICAgICk7XHJcbiAgICAgICAgfVxyXG4gICAgICAgIHJldHVybiByZXNwb25zZS5zZW5kKGluZm8uZGF0YSk7XHJcbiAgICAgIH1cclxuXHJcbiAgICAgIC8vIFNldCBjb3JyZWN0IGNvbnRlbnQgdHlwZVxyXG4gICAgICByZXNwb25zZS5oZWFkZXIoJ0NvbnRlbnQtVHlwZScsIHJldmVyc2VkTWltZVt0eXBlXSB8fCAnaW1hZ2UvcG5nJyk7XHJcblxyXG4gICAgICAvLyBEZWNpZGUgd2hldGhlciB0byBkb3dubG9hZCBvciBub3QgY2hhcnQgZmlsZVxyXG4gICAgICBpZiAoIWJvZHkubm9Eb3dubG9hZCkge1xyXG4gICAgICAgIHJlc3BvbnNlLmF0dGFjaG1lbnQoXHJcbiAgICAgICAgICBgJHtyZXF1ZXN0LnBhcmFtcy5maWxlbmFtZSB8fCByZXF1ZXN0LmJvZHkuZmlsZW5hbWUgfHwgJ2NoYXJ0J30uJHtcclxuICAgICAgICAgICAgdHlwZSB8fCAncG5nJ1xyXG4gICAgICAgICAgfWBcclxuICAgICAgICApO1xyXG4gICAgICB9XHJcblxyXG4gICAgICAvLyBJZiBTVkcsIHJldHVybiBwbGFpbiBjb250ZW50XHJcbiAgICAgIHJldHVybiB0eXBlID09PSAnc3ZnJ1xyXG4gICAgICAgID8gcmVzcG9uc2Uuc2VuZChpbmZvLmRhdGEpXHJcbiAgICAgICAgOiByZXNwb25zZS5zZW5kKEJ1ZmZlci5mcm9tKGluZm8uZGF0YSwgJ2Jhc2U2NCcpKTtcclxuICAgIH1cclxuICB9KTtcclxufTtcclxuXHJcbmV4cG9ydCBkZWZhdWx0IChhcHApID0+IHtcclxuICBhcHAucG9zdCgnLycsIGV4cG9ydEhhbmRsZXIpO1xyXG4gIGFwcC5wb3N0KCcvOmZpbGVuYW1lJywgZXhwb3J0SGFuZGxlcik7XHJcbn07XHJcbiIsIi8qKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqXHJcblxyXG5IaWdoY2hhcnRzIEV4cG9ydCBTZXJ2ZXJcclxuXHJcbkNvcHlyaWdodCAoYykgMjAxNi0yMDIzLCBIaWdoc29mdFxyXG5cclxuTGljZW5jZWQgdW5kZXIgdGhlIE1JVCBsaWNlbmNlLlxyXG5cclxuQWRkaXRpb25hbGx5IGEgdmFsaWQgSGlnaGNoYXJ0cyBsaWNlbnNlIGlzIHJlcXVpcmVkIGZvciB1c2UuXHJcblxyXG5TZWUgTElDRU5TRSBmaWxlIGluIHJvb3QgZm9yIGRldGFpbHMuXHJcblxyXG4qKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqL1xyXG5cclxuaW1wb3J0IHsgcHJvbWlzZXMgYXMgZnNQcm9taXNlcyB9IGZyb20gJ2ZzJztcclxuaW1wb3J0IHsgcG9zaXggfSBmcm9tICdwYXRoJztcclxuXHJcbmltcG9ydCBib2R5UGFyc2VyIGZyb20gJ2JvZHktcGFyc2VyJztcclxuaW1wb3J0IGNvcnMgZnJvbSAnY29ycyc7XHJcbmltcG9ydCBleHByZXNzIGZyb20gJ2V4cHJlc3MnO1xyXG5pbXBvcnQgbXVsdGVyIGZyb20gJ211bHRlcic7XHJcbmltcG9ydCBodHRwIGZyb20gJ2h0dHAnO1xyXG5pbXBvcnQgaHR0cHMgZnJvbSAnaHR0cHMnO1xyXG5cclxuaW1wb3J0IHsgbG9nIH0gZnJvbSAnLi4vbG9nZ2VyLmpzJztcclxuaW1wb3J0IHJhdGVMaW1pdCBmcm9tICcuL3JhdGVfbGltaXQuanMnO1xyXG5pbXBvcnQgeyBfX2Rpcm5hbWUgfSBmcm9tICcuLi91dGlscy5qcyc7XHJcblxyXG5pbXBvcnQgaGVhbHRoUm91dGUgZnJvbSAnLi9yb3V0ZXMvaGVhbHRoLmpzJztcclxuaW1wb3J0IGV4cG9ydFJvdXRlcyBmcm9tICcuL3JvdXRlcy9leHBvcnQuanMnO1xyXG5pbXBvcnQgdlN3aXRjaFJvdXRlIGZyb20gJy4vcm91dGVzL2NoYW5nZV9oY192ZXJzaW9uLmpzJztcclxuaW1wb3J0IHVpUm91dGUgZnJvbSAnLi9yb3V0ZXMvdWkuanMnO1xyXG5cclxuLy8gQ3JlYXRlIGV4cHJlc3MgYXBwXHJcbmNvbnN0IGFwcCA9IGV4cHJlc3MoKTtcclxuXHJcbi8vIERpc2FibGUgdGhlIFgtUG93ZXJlZC1CeSBoZWFkZXJcclxuYXBwLmRpc2FibGUoJ3gtcG93ZXJlZC1ieScpO1xyXG5cclxuLy8gRW5hYmxlIENPUlMgc3VwcG9ydFxyXG5hcHAudXNlKGNvcnMoKSk7XHJcblxyXG4vLyBFbmFibGUgcGFyc2luZyBvZiBmb3JtIGRhdGEgKGZpbGVzKSB3aXRoIE11bHRlciBwYWNrYWdlXHJcbmNvbnN0IHN0b3JhZ2UgPSBtdWx0ZXIubWVtb3J5U3RvcmFnZSgpO1xyXG5jb25zdCB1cGxvYWQgPSBtdWx0ZXIoe1xyXG4gIHN0b3JhZ2UsXHJcbiAgbGltaXRzOiB7XHJcbiAgICBmaWVsZHNTaXplOiAnNTBNQidcclxuICB9XHJcbn0pO1xyXG5cclxuYXBwLnVzZSh1cGxvYWQuYW55KCkpO1xyXG5cclxuLy8gRW5hYmxlIGJvZHkgcGFyc2VyXHJcbmFwcC51c2UoYm9keVBhcnNlci5qc29uKHsgbGltaXQ6ICc1MG1iJyB9KSk7XHJcbmFwcC51c2UoYm9keVBhcnNlci51cmxlbmNvZGVkKHsgZXh0ZW5kZWQ6IHRydWUsIGxpbWl0OiAnNTBtYicgfSkpO1xyXG5hcHAudXNlKGJvZHlQYXJzZXIudXJsZW5jb2RlZCh7IGV4dGVuZGVkOiBmYWxzZSwgbGltaXQ6ICc1MG1iJyB9KSk7XHJcblxyXG4vKipcclxuICogRXJyb3IgaGFuZGxlciBmdW5jdGlvbi5cclxuICpcclxuICogQHBhcmFtIHtvYmplY3R9IGVycm9yIC0gQW4gZXJyb3Igb2JqZWN0LlxyXG4gKiBAcmV0dXJuIHtzdHJpbmd9IC0gQW4gZXJyb3IgbWVzc2FnZS5cclxuICovXHJcbmNvbnN0IGVycm9ySGFuZGxlciA9IChlcnJvcikgPT4gbG9nKDEsIGBbc2VydmVyXSBTb2NrZXQgZXJyb3I6ICR7ZXJyb3J9YCk7XHJcblxyXG4vKipcclxuICogQXR0YWNoZXMgZXJyb3IgaGFuZGxlcnMgZm9yIGEgc2VydmVyLlxyXG4gKlxyXG4gKiBAcGFyYW0ge29iamVjdH0gc2VydmVyIC0gVGhlIGh0dHAvaHR0cHMgc2VydmVyLlxyXG4gKi9cclxuY29uc3QgYXR0YWNoRXJyb3JIYW5kbGVycyA9IChzZXJ2ZXIpID0+IHtcclxuICBzZXJ2ZXIub24oJ2NsaWVudEVycm9yJywgZXJyb3JIYW5kbGVyKTtcclxuICBzZXJ2ZXIub24oJ2Vycm9yJywgZXJyb3JIYW5kbGVyKTtcclxuICBzZXJ2ZXIub24oJ2Nvbm5lY3Rpb24nLCAoc29ja2V0KSA9PlxyXG4gICAgc29ja2V0Lm9uKCdlcnJvcicsIChlcnJvcikgPT4gZXJyb3JIYW5kbGVyKGVycm9yLCBzb2NrZXQpKVxyXG4gICk7XHJcbn07XHJcblxyXG5leHBvcnQgY29uc3Qgc3RhcnRTZXJ2ZXIgPSBhc3luYyAoc2VydmVyQ29uZmlnKSA9PiB7XHJcbiAgLy8gU3RvcCBpZiBub3QgZW5hYmxlZFxyXG4gIGlmICghc2VydmVyQ29uZmlnLmVuYWJsZSkge1xyXG4gICAgcmV0dXJuIGZhbHNlO1xyXG4gIH1cclxuXHJcbiAgLy8gLy8gR2V0IHRoZSBwb29sXHJcbiAgLy8gY29uc3QgcG9vbCA9IGdldFBvb2woKTtcclxuXHJcbiAgLy8gLy8gVHJ5IHRvIGNyZWF0ZSBicm93c2VyIGluc3RhbmNlIGJlZm9yZSBzdGFydGluZyB0aGUgc2VydmVyXHJcbiAgLy8gY29uc3QgcmVzb3VyY2UgPSBhd2FpdCBwb29sLmFjcXVpcmUoKTtcclxuXHJcbiAgLy8gLy8gSWYgbm90IGZvdW5kLCB0aHJvdyBhbiBlcnJvclxyXG4gIC8vIGlmICghcmVzb3VyY2UuYnJvd3Nlcikge1xyXG4gIC8vICAgbG9nKDEsIGBbc2VydmVyXSBDb3VsZCBub3QgYWNxdWlyZSBicm93c2VyIGluc3RhbmNlLmApO1xyXG4gIC8vICAgcHJvY2Vzcy5leGl0KDEpO1xyXG4gIC8vIH1cclxuXHJcbiAgLy8gLy8gUmVsZWFzZSB0aGUgcmVzb3VyY2VcclxuICAvLyBwb29sLnJlbGVhc2UocmVzb3VyY2UpO1xyXG5cclxuICAvLyBMaXN0ZW4gSFRUUCBzZXJ2ZXJcclxuICBpZiAoIXNlcnZlckNvbmZpZy5zc2wuZW5hYmxlICYmICFzZXJ2ZXJDb25maWcuc3NsLmZvcmNlKSB7XHJcbiAgICAvLyBNYWluIHNlcnZlciBpbnN0YW5jZSAoSFRUUClcclxuICAgIGNvbnN0IGh0dHBTZXJ2ZXIgPSBodHRwLmNyZWF0ZVNlcnZlcihhcHApO1xyXG4gICAgLy8gQXR0YWNoIGVycm9yIGhhbmRsZXJzIGFuZCBsaXN0ZW4gdG8gdGhlIHNlcnZlclxyXG4gICAgYXR0YWNoRXJyb3JIYW5kbGVycyhodHRwU2VydmVyKTtcclxuICAgIC8vIExpc3RlblxyXG4gICAgaHR0cFNlcnZlci5saXN0ZW4oc2VydmVyQ29uZmlnLnBvcnQsIHNlcnZlckNvbmZpZy5ob3N0KTtcclxuXHJcbiAgICBsb2coXHJcbiAgICAgIDMsXHJcbiAgICAgIGBbc2VydmVyXSBTdGFydGVkIEhUVFAgc2VydmVyIG9uICR7c2VydmVyQ29uZmlnLmhvc3R9OiR7c2VydmVyQ29uZmlnLnBvcnR9LmBcclxuICAgICk7XHJcbiAgfVxyXG5cclxuICAvLyBMaXN0ZW4gSFRUUFMgc2VydmVyXHJcbiAgaWYgKHNlcnZlckNvbmZpZy5zc2wuZW5hYmxlKSB7XHJcbiAgICAvLyBTZXQgdXAgYW4gU1NMIHNlcnZlciBhbHNvXHJcbiAgICBsZXQga2V5LCBjZXJ0O1xyXG5cclxuICAgIHRyeSB7XHJcbiAgICAgIC8vIEdldCB0aGUgU1NMIGtleVxyXG4gICAgICBrZXkgPSBhd2FpdCBmc1Byb21pc2VzLnJlYWRGaWxlKFxyXG4gICAgICAgIHBvc2l4LmpvaW4oc2VydmVyQ29uZmlnLnNzbC5jZXJ0UGF0aCwgJ3NlcnZlci5rZXknKSxcclxuICAgICAgICAndXRmOCdcclxuICAgICAgKTtcclxuXHJcbiAgICAgIC8vIEdldCB0aGUgU1NMIGNlcnRpZmljYXRlXHJcbiAgICAgIGNlcnQgPSBhd2FpdCBmc1Byb21pc2VzLnJlYWRGaWxlKFxyXG4gICAgICAgIHBvc2l4LmpvaW4oc2VydmVyQ29uZmlnLnNzbC5jZXJ0UGF0aCwgJ3NlcnZlci5jcnQnKSxcclxuICAgICAgICAndXRmOCdcclxuICAgICAgKTtcclxuICAgIH0gY2F0Y2ggKGVycm9yKSB7XHJcbiAgICAgIGxvZyhcclxuICAgICAgICAxLFxyXG4gICAgICAgIGBbc2VydmVyXSBVbmFibGUgdG8gbG9hZCBrZXkvY2VydGlmaWNhdGUgZnJvbSAke3NlcnZlckNvbmZpZy5zc2wuY2VydFBhdGh9LmBcclxuICAgICAgKTtcclxuICAgIH1cclxuXHJcbiAgICBpZiAoa2V5ICYmIGNlcnQpIHtcclxuICAgICAgLy8gTWFpbiBzZXJ2ZXIgaW5zdGFuY2UgKEhUVFBTKVxyXG4gICAgICBjb25zdCBodHRwc1NlcnZlciA9IGh0dHBzLmNyZWF0ZVNlcnZlcihhcHApO1xyXG4gICAgICAvLyBBdHRhY2ggZXJyb3IgaGFuZGxlcnMgYW5kIGxpc3RlbiB0byB0aGUgc2VydmVyXHJcbiAgICAgIGF0dGFjaEVycm9ySGFuZGxlcnMoaHR0cHNTZXJ2ZXIpO1xyXG4gICAgICAvLyBMaXN0ZW5cclxuICAgICAgaHR0cHNTZXJ2ZXIubGlzdGVuKHNlcnZlckNvbmZpZy5zc2wucG9ydCwgc2VydmVyQ29uZmlnLmhvc3QpO1xyXG5cclxuICAgICAgbG9nKFxyXG4gICAgICAgIDMsXHJcbiAgICAgICAgYFtzZXJ2ZXJdIFN0YXJ0ZWQgSFRUUFMgc2VydmVyIG9uICR7c2VydmVyQ29uZmlnLmhvc3R9OiR7c2VydmVyQ29uZmlnLnNzbC5wb3J0fS5gXHJcbiAgICAgICk7XHJcbiAgICB9XHJcbiAgfVxyXG5cclxuICAvLyBFbmFibGUgdGhlIHJhdGUgbGltaXRlciBpZiBjb25maWcgc2F5cyBzb1xyXG4gIGlmIChcclxuICAgIHNlcnZlckNvbmZpZy5yYXRlTGltaXRpbmcgJiZcclxuICAgIHNlcnZlckNvbmZpZy5yYXRlTGltaXRpbmcuZW5hYmxlICYmXHJcbiAgICAhWzAsIE5hTl0uaW5jbHVkZXMoc2VydmVyQ29uZmlnLnJhdGVMaW1pdGluZy5tYXhSZXF1ZXN0cylcclxuICApIHtcclxuICAgIHJhdGVMaW1pdChhcHAsIHNlcnZlckNvbmZpZy5yYXRlTGltaXRpbmcpO1xyXG4gIH1cclxuXHJcbiAgLy8gU2V0IHVwIHN0YXRpYyBmb2xkZXIncyByb3V0ZVxyXG4gIGFwcC51c2UoZXhwcmVzcy5zdGF0aWMocG9zaXguam9pbihfX2Rpcm5hbWUsICdwdWJsaWMnKSkpO1xyXG5cclxuICAvLyBTZXQgdXAgcm91dGVzXHJcbiAgaGVhbHRoUm91dGUoYXBwKTtcclxuICBleHBvcnRSb3V0ZXMoYXBwKTtcclxuICB1aVJvdXRlKGFwcCk7XHJcbiAgdlN3aXRjaFJvdXRlKGFwcCk7XHJcbn07XHJcblxyXG4vKipcclxuICogUmV0dXJucyB0aGUgZXhwcmVzcyBpbnN0YW5jZS5cclxuICovXHJcbmV4cG9ydCBjb25zdCBnZXRFeHByZXNzID0gKCkgPT4ge1xyXG4gIHJldHVybiBleHByZXNzO1xyXG59O1xyXG5cclxuLyoqXHJcbiAqIFJldHVybnMgdGhlIGFwcCBpbnN0YW5jZS5cclxuICovXHJcbmV4cG9ydCBjb25zdCBnZXRBcHAgPSAoKSA9PiB7XHJcbiAgcmV0dXJuIGFwcDtcclxufTtcclxuXHJcbi8qKlxyXG4gKiBBZGRzIGEgbWlkZGxld2FyZSB0byB0aGUgc2VydmVyLlxyXG4gKlxyXG4gKiBAcGFyYW0ge29iamVjdH0gcGF0aCAtIEFuIGVuZHBvaW50IHBhdGggdG8gYWRkIG1pZGRsZXdhcmVzIHRvLlxyXG4gKiBAcGFyYW0ge0FycmF5fSBtaWRkbGV3YXJlcyAtIEFuIHVubGltaXRlZCBudW1iZXIgb2YgbWlkZGxld2FyZXMgdG8gdXNlXHJcbiAqIGFnYWluc3QgdGhlIHNwZWNpZmljIGVuZHBvaW50LlxyXG4gKi9cclxuZXhwb3J0IGNvbnN0IHVzZSA9IChwYXRoLCAuLi5taWRkbGV3YXJlcykgPT4ge1xyXG4gIGFwcC51c2UocGF0aCwgLi4ubWlkZGxld2FyZXMpO1xyXG59O1xyXG5cclxuLyoqXHJcbiAqIEFkZHMgYSBnZXQgcm91dGUgdG8gdGhlIHNlcnZlci5cclxuICpcclxuICogQHBhcmFtIHtvYmplY3R9IHBhdGggLSBBbiBlbmRwb2ludCBwYXRoIHRvIGFkZCBtaWRkbGV3YXJlcyB0by5cclxuICogQHBhcmFtIHtBcnJheX0gbWlkZGxld2FyZXMgLSBBbiB1bmxpbWl0ZWQgbnVtYmVyIG9mIG1pZGRsZXdhcmVzIHRvIHVzZVxyXG4gKiBhZ2FpbnN0IHRoZSBzcGVjaWZpYyBlbmRwb2ludCBmb3IgR0VUIG1ldGhvZC5cclxuICovXHJcbmV4cG9ydCBjb25zdCBnZXQgPSAocGF0aCwgLi4ubWlkZGxld2FyZXMpID0+IHtcclxuICBhcHAuZ2V0KHBhdGgsIC4uLm1pZGRsZXdhcmVzKTtcclxufTtcclxuXHJcbi8qKlxyXG4gKiBBZGRzIGEgcG9zdCByb3V0ZSB0byB0aGUgc2VydmVyLlxyXG4gKlxyXG4gKiBAcGFyYW0ge29iamVjdH0gcGF0aCAtIEFuIGVuZHBvaW50IHBhdGggdG8gYWRkIG1pZGRsZXdhcmVzIHRvLlxyXG4gKiBAcGFyYW0ge0FycmF5fSBtaWRkbGV3YXJlcyAtIEFuIHVubGltaXRlZCBudW1iZXIgb2YgbWlkZGxld2FyZXMgdG8gdXNlXHJcbiAqIGFnYWluc3QgdGhlIHNwZWNpZmljIGVuZHBvaW50IGZvciBQT1NUIG1ldGhvZC5cclxuICovXHJcbmV4cG9ydCBjb25zdCBwb3N0ID0gKHBhdGgsIC4uLm1pZGRsZXdhcmVzKSA9PiB7XHJcbiAgYXBwLnBvc3QocGF0aCwgLi4ubWlkZGxld2FyZXMpO1xyXG59O1xyXG5cclxuLyoqXHJcbiAqIEZvcmNlZnVsbHkgZW5hYmxlcyByYXRlIGxpbWl0aW5nLlxyXG4gKlxyXG4gKiBAcGFyYW0ge29iamVjdH0gbGltaXRDb25maWcgLSBUaGUgb3B0aW9ucyBvYmplY3QgZm9yIHRoZSByYXRlIGxpbWl0ZXJcclxuICogY29uZmlndXJhdGlvbi5cclxuICovXHJcbmV4cG9ydCBjb25zdCBlbmFibGVSYXRlTGltaXRpbmcgPSAobGltaXRDb25maWcpID0+IHtcclxuICByZXR1cm4gcmF0ZUxpbWl0KGFwcCwgbGltaXRDb25maWcpO1xyXG59O1xyXG5cclxuZXhwb3J0IGRlZmF1bHQge1xyXG4gIHN0YXJ0U2VydmVyLFxyXG4gIGdldEV4cHJlc3MsXHJcbiAgZ2V0QXBwLFxyXG4gIHVzZSxcclxuICBnZXQsXHJcbiAgcG9zdCxcclxuICBlbmFibGVSYXRlTGltaXRpbmdcclxufTtcclxuIiwiLyoqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKipcclxuXHJcbkhpZ2hjaGFydHMgRXhwb3J0IFNlcnZlclxyXG5cclxuQ29weXJpZ2h0IChjKSAyMDE2LTIwMjMsIEhpZ2hzb2Z0XHJcblxyXG5MaWNlbmNlZCB1bmRlciB0aGUgTUlUIGxpY2VuY2UuXHJcblxyXG5BZGRpdGlvbmFsbHkgYSB2YWxpZCBIaWdoY2hhcnRzIGxpY2Vuc2UgaXMgcmVxdWlyZWQgZm9yIHVzZS5cclxuXHJcblNlZSBMSUNFTlNFIGZpbGUgaW4gcm9vdCBmb3IgZGV0YWlscy5cclxuXHJcbioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKiovXHJcblxyXG5pbXBvcnQgeyBqb2luIH0gZnJvbSAncGF0aCc7XHJcblxyXG5pbXBvcnQgeyBfX2Rpcm5hbWUgfSBmcm9tICcuLi8uLi91dGlscy5qcyc7XHJcbi8qKlxyXG4gKiBBZGRzIHRoZSAvIHJvdXRlIGZvciBhIFVJIHdoZW4gZW5hYmxlZCBmb3IgdGhlIGV4cG9ydCBzZXJ2ZXJcclxuICovXHJcbmV4cG9ydCBkZWZhdWx0IChhcHApID0+XHJcbiAgIWFwcFxyXG4gICAgPyBmYWxzZVxyXG4gICAgOiBhcHAuZ2V0KCcvJywgKHJlcXVlc3QsIHJlc3BvbnNlKSA9PiB7XHJcbiAgICAgICAgcmVzcG9uc2Uuc2VuZEZpbGUoam9pbihfX2Rpcm5hbWUsICdwdWJsaWMnLCAnaW5kZXguaHRtbCcpKTtcclxuICAgICAgfSk7XHJcbiIsIi8qKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqXHJcblxyXG5IaWdoY2hhcnRzIEV4cG9ydCBTZXJ2ZXJcclxuXHJcbkNvcHlyaWdodCAoYykgMjAxNi0yMDIzLCBIaWdoc29mdFxyXG5cclxuTGljZW5jZWQgdW5kZXIgdGhlIE1JVCBsaWNlbmNlLlxyXG5cclxuQWRkaXRpb25hbGx5IGEgdmFsaWQgSGlnaGNoYXJ0cyBsaWNlbnNlIGlzIHJlcXVpcmVkIGZvciB1c2UuXHJcblxyXG5TZWUgTElDRU5TRSBmaWxlIGluIHJvb3QgZm9yIGRldGFpbHMuXHJcblxyXG4qKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqL1xyXG5cclxuaW1wb3J0IGNhY2hlIGZyb20gJy4uLy4uL2NhY2hlLmpzJztcclxuXHJcbi8qKlxyXG4gKiBBZGRzIGEgcm91dGUgdGhhdCBjYW4gYmUgdXNlZCB0byBjaGFuZ2UgdGhlIEhDIHZlcnNpb24gb24gdGhlIHNlcnZlclxyXG4gKiBUT0RPOiBBZGQgYXV0aCB0b2tlbiBhbmQgY29ubmVjdCB0byBBUElcclxuICovXHJcbmV4cG9ydCBkZWZhdWx0IChhcHApID0+XHJcbiAgIWFwcFxyXG4gICAgPyBmYWxzZVxyXG4gICAgOiBhcHAucG9zdCgnL2NoYW5nZV9oY192ZXJzaW9uLzpuZXdWZXJzaW9uJywgYXN5bmMgKHJlcXVlc3QsIHJlc3BvbnNlKSA9PiB7XHJcbiAgICAgICAgY29uc3QgY3Rva2VuID0gcHJvY2Vzcy5lbnYuSElHSENIQVJUU19BRE1JTl9UT0tFTjtcclxuXHJcbiAgICAgICAgaWYgKCFjdG9rZW4gfHwgIWN0b2tlbi5sZW5ndGgpIHtcclxuICAgICAgICAgIHJldHVybiByZXNwb25zZS5zZW5kKHtcclxuICAgICAgICAgICAgZXJyb3I6IHRydWUsXHJcbiAgICAgICAgICAgIG1lc3NhZ2U6XHJcbiAgICAgICAgICAgICAgJ1NlcnZlciBub3QgY29uZmlndXJlZCB0byBkbyBydW4tdGltZSB2ZXJzaW9uIGNoYW5nZXM6IEhJR0hDSEFSVFNfQURNSU5fVE9LRU4gbm90IHNldCdcclxuICAgICAgICAgIH0pO1xyXG4gICAgICAgIH1cclxuXHJcbiAgICAgICAgY29uc3QgdG9rZW4gPSByZXF1ZXN0LmdldCgnaGMtYXV0aCcpO1xyXG5cclxuICAgICAgICBpZiAoIXRva2VuIHx8IHRva2VuICE9PSBjdG9rZW4pIHtcclxuICAgICAgICAgIHJldHVybiByZXNwb25zZS5zZW5kKHtcclxuICAgICAgICAgICAgZXJyb3I6IHRydWUsXHJcbiAgICAgICAgICAgIG1lc3NhZ2U6ICdJbnZhbGlkIG9yIG1pc3NpbmcgdG9rZW46IHNldCB0b2tlbiBpbiB0aGUgaGMtYXV0aCBoZWFkZXInXHJcbiAgICAgICAgICB9KTtcclxuICAgICAgICB9XHJcblxyXG4gICAgICAgIGNvbnN0IG5ld1ZlcnNpb24gPSByZXF1ZXN0LnBhcmFtcy5uZXdWZXJzaW9uO1xyXG5cclxuICAgICAgICBpZiAobmV3VmVyc2lvbikge1xyXG4gICAgICAgICAgdHJ5IHtcclxuICAgICAgICAgICAgLy8gZXNsaW50LWRpc2FibGUtbmV4dC1saW5lIGltcG9ydC9uby1uYW1lZC1hcy1kZWZhdWx0LW1lbWJlclxyXG4gICAgICAgICAgICBhd2FpdCBjYWNoZS51cGRhdGVWZXJzaW9uKG5ld1ZlcnNpb24pO1xyXG4gICAgICAgICAgfSBjYXRjaCAoZSkge1xyXG4gICAgICAgICAgICByZXNwb25zZS5zZW5kKHtcclxuICAgICAgICAgICAgICBlcnJvcjogdHJ1ZSxcclxuICAgICAgICAgICAgICBtZXNzYWdlOiBlXHJcbiAgICAgICAgICAgIH0pO1xyXG4gICAgICAgICAgfVxyXG5cclxuICAgICAgICAgIHJlc3BvbnNlLnNlbmQoe1xyXG4gICAgICAgICAgICB2ZXJzaW9uOiBjYWNoZS52ZXJzaW9uKClcclxuICAgICAgICAgIH0pO1xyXG4gICAgICAgIH0gZWxzZSB7XHJcbiAgICAgICAgICByZXNwb25zZS5zZW5kKHtcclxuICAgICAgICAgICAgZXJyb3I6IHRydWUsXHJcbiAgICAgICAgICAgIG1lc3NhZ2U6ICdObyBuZXcgdmVyc2lvbiBzdXBwbGllZCdcclxuICAgICAgICAgIH0pO1xyXG4gICAgICAgIH1cclxuICAgICAgfSk7XHJcbiIsIi8qKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqXHJcblxyXG5IaWdoY2hhcnRzIEV4cG9ydCBTZXJ2ZXJcclxuXHJcbkNvcHlyaWdodCAoYykgMjAxNi0yMDIzLCBIaWdoc29mdFxyXG5cclxuTGljZW5jZWQgdW5kZXIgdGhlIE1JVCBsaWNlbmNlLlxyXG5cclxuQWRkaXRpb25hbGx5IGEgdmFsaWQgSGlnaGNoYXJ0cyBsaWNlbnNlIGlzIHJlcXVpcmVkIGZvciB1c2UuXHJcblxyXG5TZWUgTElDRU5TRSBmaWxlIGluIHJvb3QgZm9yIGRldGFpbHMuXHJcblxyXG4qKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqL1xyXG5cclxuLy8gQWRkIHRoZSBtYWluIGRpcmVjdG9yeSBpbiB0aGUgZ2xvYmFsIG9iamVjdFxyXG5pbXBvcnQgJ2NvbG9ycyc7XHJcblxyXG5pbXBvcnQgc2VydmVyLCB7IHN0YXJ0U2VydmVyIH0gZnJvbSAnLi9zZXJ2ZXIvc2VydmVyLmpzJztcclxuaW1wb3J0IHtcclxuICBzZXRBbGxvd0NvZGVFeGVjdXRpb24sXHJcbiAgYmF0Y2hFeHBvcnQsXHJcbiAgc2luZ2xlRXhwb3J0LFxyXG4gIHN0YXJ0RXhwb3J0XHJcbn0gZnJvbSAnLi9jaGFydC5qcyc7XHJcbmltcG9ydCB7IG1hcFRvTmV3Q29uZmlnLCBzZXRPcHRpb25zIH0gZnJvbSAnLi9jb25maWcuanMnO1xyXG5pbXBvcnQgeyBsb2csIHNldExvZ0xldmVsLCBlbmFibGVGaWxlTG9nZ2luZyB9IGZyb20gJy4vbG9nZ2VyLmpzJztcclxuaW1wb3J0IHsga2lsbFBvb2wsIGluaXQgfSBmcm9tICcuL3Bvb2wuanMnO1xyXG5pbXBvcnQgeyBjaGVja0NhY2hlIH0gZnJvbSAnLi9jYWNoZS5qcyc7XHJcblxyXG5leHBvcnQgZGVmYXVsdCB7XHJcbiAgbG9nLFxyXG4gIG1hcFRvTmV3Q29uZmlnLFxyXG4gIHNldE9wdGlvbnMsXHJcbiAgc2luZ2xlRXhwb3J0LFxyXG4gIHN0YXJ0RXhwb3J0LFxyXG4gIGJhdGNoRXhwb3J0LFxyXG4gIHNlcnZlcixcclxuICBzdGFydFNlcnZlcixcclxuICBraWxsUG9vbCxcclxuICBpbml0UG9vbDogYXN5bmMgKG9wdGlvbnMgPSB7fSkgPT4ge1xyXG4gICAgLy8gU2V0IHRoZSBhbGxvd0NvZGVFeGVjdXRpb24gcGVyIGV4cG9ydCBtb2R1bGUgc2NvcGVcclxuICAgIHNldEFsbG93Q29kZUV4ZWN1dGlvbihcclxuICAgICAgb3B0aW9ucy5jdXN0b21Db2RlICYmIG9wdGlvbnMuY3VzdG9tQ29kZS5hbGxvd0NvZGVFeGVjdXRpb25cclxuICAgICk7XHJcblxyXG4gICAgLy8gU2V0IHRoZSBsb2cgbGV2ZWxcclxuICAgIHNldExvZ0xldmVsKG9wdGlvbnMubG9nZ2luZyAmJiBwYXJzZUludChvcHRpb25zLmxvZ2dpbmcubGV2ZWwpKTtcclxuXHJcbiAgICAvLyBTZXQgdGhlIGxvZyBmaWxlIHBhdGggYW5kIG5hbWVcclxuICAgIGlmIChvcHRpb25zLmxvZ2dpbmcgJiYgb3B0aW9ucy5sb2dnaW5nLmRlc3QpIHtcclxuICAgICAgZW5hYmxlRmlsZUxvZ2dpbmcoXHJcbiAgICAgICAgb3B0aW9ucy5sb2dnaW5nLmRlc3QsXHJcbiAgICAgICAgb3B0aW9ucy5sb2dnaW5nLmZpbGUgfHwgJ2hpZ2hjaGFydHMtZXhwb3J0LXNlcnZlci5sb2cnXHJcbiAgICAgICk7XHJcbiAgICB9XHJcblxyXG4gICAgLy8gQ2hlY2sgaWYgY2FjaGUgbmVlZHMgdG8gYmUgdXBkYXRlZFxyXG4gICAgYXdhaXQgY2hlY2tDYWNoZShvcHRpb25zLmhpZ2hjaGFydHMgfHwgeyB2ZXJzaW9uOiAnbGF0ZXN0JyB9KTtcclxuXHJcbiAgICAvLyBJbml0IHRoZSBwb29sXHJcbiAgICBhd2FpdCBpbml0KHtcclxuICAgICAgcG9vbDogb3B0aW9ucy5wb29sIHx8IHtcclxuICAgICAgICBtaW5Xb3JrZXJzOiAxLFxyXG4gICAgICAgIG1heFdvcmtlcnM6IDFcclxuICAgICAgfSxcclxuICAgICAgcHVwcGV0ZWVyQXJnczogb3B0aW9ucy5wdXBwZXRlZXI/LmFyZ3MgfHwgW11cclxuICAgIH0pO1xyXG5cclxuICAgIC8vIFJldHVybiB1cGRhdGVkIG9wdGlvbnNcclxuICAgIHJldHVybiBvcHRpb25zO1xyXG4gIH1cclxufTtcclxuIl0sIm5hbWVzIjpbImRvdGVudiIsImNvbmZpZyIsImRlZmF1bHRDb25maWciLCJwdXBwZXRlZXIiLCJhcmdzIiwidmFsdWUiLCJ0eXBlIiwiZGVzY3JpcHRpb24iLCJoaWdoY2hhcnRzIiwidmVyc2lvbiIsImVudkxpbmsiLCJjZG5VUkwiLCJjb3JlU2NyaXB0cyIsIm1vZHVsZXMiLCJpbmRpY2F0b3JzIiwic2NyaXB0cyIsImZvcmNlRmV0Y2giLCJleHBvcnQiLCJpbmZpbGUiLCJpbnN0ciIsIm9wdGlvbnMiLCJvdXRmaWxlIiwiY29uc3RyIiwiZGVmYXVsdEhlaWdodCIsImRlZmF1bHRXaWR0aCIsImRlZmF1bHRTY2FsZSIsImhlaWdodCIsIndpZHRoIiwic2NhbGUiLCJnbG9iYWxPcHRpb25zIiwidGhlbWVPcHRpb25zIiwiYmF0Y2giLCJjdXN0b21Db2RlIiwiYWxsb3dDb2RlRXhlY3V0aW9uIiwiYWxsb3dGaWxlUmVzb3VyY2VzIiwiY2FsbGJhY2siLCJyZXNvdXJjZXMiLCJsb2FkQ29uZmlnIiwiY3JlYXRlQ29uZmlnIiwic2VydmVyIiwiZW5hYmxlIiwiY2xpTmFtZSIsImhvc3QiLCJwb3J0Iiwic3NsIiwiZm9yY2UiLCJjZXJ0UGF0aCIsInJhdGVMaW1pdGluZyIsIm1heFJlcXVlc3RzIiwid2luZG93IiwiZGVsYXkiLCJ0cnVzdFByb3h5Iiwic2tpcEtleSIsInNraXBUb2tlbiIsInBvb2wiLCJtaW5Xb3JrZXJzIiwibWF4V29ya2VycyIsIndvcmtMaW1pdCIsImFjcXVpcmVUaW1lb3V0IiwiY3JlYXRlVGltZW91dCIsImRlc3Ryb3lUaW1lb3V0IiwiaWRsZVRpbWVvdXQiLCJyYXN0ZXJpemF0aW9uVGltZW91dCIsImNyZWF0ZVJldHJ5SW50ZXJ2YWwiLCJyZWFwZXJJbnRlcnZhbCIsImJlbmNobWFya2luZyIsImxpc3RlblRvUHJvY2Vzc0V4aXRzIiwibG9nZ2luZyIsImxldmVsIiwiZmlsZSIsImRlc3QiLCJ1aSIsInJvdXRlIiwib3RoZXIiLCJub0xvZ28iLCJwYXlsb2FkIiwiam9pbiIsImFic29sdXRlUHJvcHMiLCJuZXN0ZWRBcmdzIiwiY3JlYXRlTmVzdGVkQXJncyIsIm9iaiIsInByb3BDaGFpbiIsIk9iamVjdCIsImtleXMiLCJmb3JFYWNoIiwiayIsImluY2x1ZGVzIiwiZW50cnkiLCJzdWJzdHJpbmciLCJ0b0NvbnNvbGUiLCJ0b0ZpbGUiLCJwYXRoQ3JlYXRlZCIsImxldmVsc0Rlc2MiLCJ0aXRsZSIsImNvbG9yIiwibGlzdGVuZXJzIiwia2V5Iiwib3B0aW9uIiwiZW50cmllcyIsImxvZyIsIm5ld0xldmVsIiwidGV4dHMiLCJsZW5ndGgiLCJwcmVmaXgiLCJEYXRlIiwidG9TdHJpbmciLCJzcGxpdCIsInRyaW0iLCJmbiIsImV4aXN0c1N5bmMiLCJta2RpclN5bmMiLCJhcHBlbmRGaWxlIiwiY29uY2F0IiwiZXJyb3IiLCJjb25zb2xlIiwiYXBwbHkiLCJ1bmRlZmluZWQiLCJfX2Rpcm5hbWUiLCJmaWxlVVJMVG9QYXRoIiwiVVJMIiwiZG9jdW1lbnQiLCJyZXF1aXJlIiwicGF0aFRvRmlsZVVSTCIsIl9fZmlsZW5hbWUiLCJocmVmIiwiX2RvY3VtZW50Q3VycmVudFNjcmlwdCIsInNyYyIsImJhc2VVUkkiLCJjbGVhclRleHQiLCJ0ZXh0IiwicnVsZSIsInJlcGxhY2VyIiwicmVwbGFjZUFsbCIsImZpeFR5cGUiLCJmb3JtYXRzIiwib3V0VHlwZSIsInBvcCIsImZpbmQiLCJ0IiwiaGFuZGxlUmVzb3VyY2VzIiwiYWxsb3dlZFByb3BzIiwiaGFuZGxlZFJlc291cmNlcyIsImNvcnJlY3RSZXNvdXJjZXMiLCJlbmRzV2l0aCIsImlzQ29ycmVjdEpTT04iLCJyZWFkRmlsZVN5bmMiLCJub3RpY2UiLCJmaWxlcyIsInByb3BOYW1lIiwibWFwIiwiaXRlbSIsImRhdGEiLCJwYXJzZWREYXRhIiwiSlNPTiIsInBhcnNlIiwic3RyaW5naWZ5IiwiZGVlcENvcHkiLCJjb3B5IiwiQXJyYXkiLCJpc0FycmF5IiwicHJvdG90eXBlIiwiaGFzT3duUHJvcGVydHkiLCJjYWxsIiwib3B0aW9uc1N0cmluZ2lmeSIsImFsbG93RnVuY3Rpb25zIiwibmFtZSIsInN0YXJ0c1dpdGgiLCJwcmludFVzYWdlIiwiYm9sZCIsInllbGxvdyIsImN5Y2xlQ2F0ZWdvcmllcyIsImNhdGVnb3JpZXMiLCJkZXNjTmFtZSIsImdyZWVuIiwiaSIsImJsdWUiLCJjYXRlZ29yeSIsInRvVXBwZXJDYXNlIiwicmVkIiwidG9Cb29sZWFuIiwid3JhcEFyb3VuZCIsInJlcGxhY2UiLCJyYXRlTGltaXQiLCJhcHAiLCJsaW1pdENvbmZpZyIsIm1zZyIsInJhdGVPcHRpb25zIiwibWF4IiwibGltaXRlciIsIndpbmRvd01zIiwiZGVsYXlNcyIsImhhbmRsZXIiLCJyZXF1ZXN0IiwicmVzcG9uc2UiLCJmb3JtYXQiLCJqc29uIiwic3RhdHVzIiwic2VuZCIsIm1lc3NhZ2UiLCJkZWZhdWx0Iiwic2tpcCIsInF1ZXJ5IiwiYWNjZXNzX3Rva2VuIiwidXNlIiwiYXN5bmMiLCJmZXRjaCIsInVybCIsInJlcXVlc3RPcHRpb25zIiwiUHJvbWlzZSIsInJlc29sdmUiLCJyZWplY3QiLCJwcm90b2NvbCIsImh0dHBzIiwiaHR0cCIsImdldFByb3RvY29sIiwiZ2V0IiwicmVzIiwib24iLCJjaHVuayIsImNhY2hlUGF0aCIsImNhY2hlIiwiYWN0aXZlTWFuaWZlc3QiLCJzb3VyY2VzIiwiaGNWZXJzaW9uIiwiYXBwbGllZENvbmZpZyIsImV4dHJhY3RWZXJzaW9uIiwic3Vic3RyIiwiaW5kZXhPZiIsImZldGNoU2NyaXB0Iiwic2NyaXB0IiwicHJveHlBZ2VudCIsImFnZW50IiwidGltZW91dCIsInByb2Nlc3MiLCJlbnYiLCJzdGF0dXNDb2RlIiwidXBkYXRlQ2FjaGUiLCJzb3VyY2VQYXRoIiwiY3VzdG9tU2NyaXB0cyIsImFsbFNjcmlwdHMiLCJjIiwibSIsInByb3h5SG9zdCIsInByb3h5UG9ydCIsIkh0dHBzUHJveHlBZ2VudCIsImZldGNoZWRNb2R1bGVzIiwiYWxsIiwid3JpdGVGaWxlU3luYyIsImNoZWNrQ2FjaGUiLCJtYW5pZmVzdFBhdGgiLCJyZXF1ZXN0VXBkYXRlIiwibWFuaWZlc3QiLCJtb2R1bGVNYXAiLCJudW1iZXJPZk1vZHVsZXMiLCJzb21lIiwibW9kdWxlTmFtZSIsIm5ld01hbmlmZXN0Iiwic2F2ZUNvbmZpZ1RvTWFuaWZlc3QiLCJjYWNoZSQxIiwibmV3VmVyc2lvbiIsImFzc2lnbiIsIlJBTkRPTV9QSUQiLCJyYW5kb21CeXRlcyIsIlBVUFBFVEVFUl9ESVIiLCJwYXRoIiwibWluaW1hbEFyZ3MiLCJ0ZW1wbGF0ZSIsImZzIiwiYnJvd3NlciIsInNldFBhZ2VDb250ZW50IiwicGFnZSIsInNldENvbnRlbnQiLCJhZGRTY3JpcHRUYWciLCJldmFsdWF0ZSIsInNldHVwSGlnaGNoYXJ0cyIsImVyciIsIiRldmFsIiwiZWxlbWVudCIsImVycm9yTWVzc2FnZSIsIl9kaXNwbGF5RXJyb3JzIiwiaW5uZXJIVE1MIiwibmV3UGFnZSIsImNsb3NlIiwiY29ubmVjdGVkIiwiX19iYXNlZGlyIiwic2V0QXNDb25maWciLCJjaGFydCIsInRyaWdnZXJFeHBvcnQiLCJwdXBwZXRlZXJFeHBvcnQiLCJpbmplY3RlZFJlc291cmNlcyIsImNsZWFySW5qZWN0ZWQiLCJkaXNwb3NlIiwic2NyaXB0c1RvUmVtb3ZlIiwiZ2V0RWxlbWVudHNCeVRhZ05hbWUiLCJzdHlsZXNUb1JlbW92ZSIsImxpbmtzVG9SZW1vdmUiLCJyZW1vdmUiLCJleHBvcnRCZW5jaCIsImV4cG9ydE9wdGlvbnMiLCJyZXF1ZXN0QW5pbWF0aW9uRnJhbWUiLCJkaXNwbGF5RXJyb3JzIiwiZGVidWdnZXIiLCJkIiwic3ZnQmVuY2giLCJpc1NWRyIsInNldFBhZ2VCZW5jaCIsInN2Z1RlbXBsYXRlIiwic3RySW5qIiwic2V0Q29udGVudEJlbmNoIiwicmVzQmVuY2giLCJqcyIsInB1c2giLCJjb250ZW50IiwiaXNMb2NhbCIsImNzc0JlbmNoIiwiY3NzIiwiY3NzSW1wb3J0cyIsIm1hdGNoIiwiY3NzSW1wb3J0UGF0aCIsImFkZFN0eWxlVGFnIiwic2l6ZSIsImNoYXJ0SGVpZ2h0IiwiYmFzZVZhbCIsImNoYXJ0V2lkdGgiLCJwYXJzZUZsb2F0IiwiSGlnaGNoYXJ0cyIsImNoYXJ0cyIsInZwQmVuY2giLCJ2aWV3cG9ydEhlaWdodCIsIk1hdGgiLCJjZWlsIiwidmlld3BvcnRXaWR0aCIsInNldFZpZXdwb3J0IiwiZGV2aWNlU2NhbGVGYWN0b3IiLCJ6b29tQ2FsbGJhY2siLCJib2R5Iiwic3R5bGUiLCJ6b29tIiwibWFyZ2luIiwieCIsInkiLCJnZXRCb3VuZGluZ0NsaWVudFJlY3QiLCJ0cnVuYyIsImdldENsaXBSZWdpb24iLCJyb3VuZCIsImV4cEJlbmNobWFyayIsIm91dGVySFRNTCIsImNyZWF0ZVNWRyIsImVuY29kaW5nIiwiY2xpcCIsInJhY2UiLCJzY3JlZW5zaG90Iiwib21pdEJhY2tncm91bmQiLCJzZXRUaW1lb3V0IiwiRXJyb3IiLCJjcmVhdGVJbWFnZSIsInBkZiIsImNyZWF0ZVBERiIsIm9sZENoYXJ0cyIsIm9sZENoYXJ0IiwiZGVzdHJveSIsInNoaWZ0IiwicHVwcGV0ZWVyQXJncyIsInBlcmZvcm1lZEV4cG9ydHMiLCJleHBvcnRBdHRlbXB0cyIsInRpbWVTcGVudCIsImRyb3BwZWRFeHBvcnRzIiwic3BlbnRBdmVyYWdlIiwicG9vbENvbmZpZyIsImZhY3RvcnkiLCJjcmVhdGUiLCJpZCIsInV1aWQiLCJzIiwiZ2V0VGltZSIsImJyb3dzZXJOZXdQYWdlIiwiaXNDbG9zZWQiLCJ3b3JrQ291bnQiLCJyYW5kb20iLCJ2YWxpZGF0ZSIsIndvcmtlckhhbmRsZSIsImdvdG8iLCJjbGVhclBhZ2UiLCJsb2dMZXZlbCIsImluaXQiLCJhbGxBcmdzIiwidHJ5Q291bnQiLCJvcGVuIiwibGF1bmNoIiwiaGVhZGxlc3MiLCJ1c2VyRGF0YURpciIsImUiLCJjcmVhdGVCcm93c2VyIiwia2lsbFBvb2wiLCJjb2RlIiwiZXhpdCIsIlBvb2wiLCJtaW4iLCJhY3F1aXJlVGltZW91dE1pbGxpcyIsImNyZWF0ZVRpbWVvdXRNaWxsaXMiLCJkZXN0cm95VGltZW91dE1pbGxpcyIsImlkbGVUaW1lb3V0TWlsbGlzIiwiY3JlYXRlUmV0cnlJbnRlcnZhbE1pbGxpcyIsInJlYXBJbnRlcnZhbE1pbGxpcyIsInByb3BhZ2F0ZUNyZWF0ZUVycm9yIiwiZXZlbnRJZCIsInJlc291cmNlIiwiaW5pdGlhbFJlc291cmNlcyIsImFjcXVpcmUiLCJwcm9taXNlIiwicmVsZWFzZSIsImRlc3Ryb3llZCIsInBvc3RXb3JrIiwiZmFpbCIsImdldFBvb2xJbmZvIiwid29ya1N0YXJ0IiwicmVzdWx0IiwiZXhwb3J0VGltZSIsImF2YWlsYWJsZSIsImJvcnJvd2VkIiwicGVuZGluZyIsInNwYXJlUmVzb3VyY2VDYXBhY2l0eSIsInBvb2wkMSIsInBhY2thZ2VWZXJzaW9uIiwibnBtX3BhY2thZ2VfdmVyc2lvbiIsInNlcnZlclN0YXJ0VGltZSIsImdlbmVyYWxPcHRpb25zIiwiZ2V0T3B0aW9ucyIsIm1lcmdlQ29uZmlnT3B0aW9ucyIsIm5ld09wdGlvbnMiLCJtZXJnZWRPcHRpb25zIiwidXBkYXRlRGVmYXVsdENvbmZpZyIsImNvbmZpZ09iaiIsImN1c3RvbU9iaiIsImN1c3RvbVZhbHVlIiwibnVtRW52VmFsIiwiZWwiLCJpbml0T3B0aW9ucyIsIml0ZW1zIiwic3RhcnRFeHBvcnQiLCJzZXR0aW5ncyIsImVuZENhbGxiYWNrIiwic3ZnIiwiaW5pdEV4cG9ydFNldHRpbmdzIiwiZXhwb3J0QXNTdHJpbmciLCJyZWFkRmlsZSIsImRvU3RyYWlnaHRJbmplY3QiLCJkb0V4cG9ydCIsImZpbmRDaGFydFNpemUiLCJleHBvcnRpbmciLCJwcmVjaXNpb24iLCJtdWx0aXBsaWVyIiwicG93Iiwicm91bmROdW1iZXIiLCJzb3VyY2VIZWlnaHQiLCJzb3VyY2VXaWR0aCIsImNoYXJ0SnNvbiIsImN1c3RvbUNvZGVPcHRpb25zIiwiYWxsb3dDb2RlRXhlY3V0aW9uU2NvcGVkIiwiZW5hYmxlZCIsIm9wdGlvbnNOYW1lIiwidGhlbiIsImNhdGNoIiwicmVxdWVzdElkIiwic3RyaW5nVG9FeHBvcnQiLCJjaGFydEpTT04iLCJyZXZlcnNlZE1pbWUiLCJwbmciLCJqcGVnIiwiZ2lmIiwicmVxdWVzdHNDb3VudGVyIiwiYmVmb3JlUmVxdWVzdCIsImFmdGVyUmVxdWVzdCIsImRvQ2FsbGJhY2tzIiwiY2FsbGJhY2tzIiwidW5pcXVlSWQiLCJjYWxsUmVzcG9uc2UiLCJleHBvcnRIYW5kbGVyIiwic3RhcnQiLCJocnRpbWUiLCJiaWdpbnQiLCJtZWFzdXJlVGltZSIsImRlZmF1bHRPcHRpb25zIiwiaGVhZGVycyIsImNvbm5lY3Rpb24iLCJyZW1vdGVBZGRyZXNzIiwiY29ubmVjdGlvbkFib3J0ZWQiLCJzb2NrZXQiLCJ0b0xvd2VyQ2FzZSIsImI2NCIsImRhdGFPcHRpb25zIiwibm9Eb3dubG9hZCIsImlwUmVnRXgiLCJpbmZvIiwicmVtb3ZlQWxsTGlzdGVuZXJzIiwiQnVmZmVyIiwiZnJvbSIsImhlYWRlciIsImF0dGFjaG1lbnQiLCJwYXJhbXMiLCJmaWxlbmFtZSIsImV4cHJlc3MiLCJkaXNhYmxlIiwiY29ycyIsInN0b3JhZ2UiLCJtdWx0ZXIiLCJtZW1vcnlTdG9yYWdlIiwidXBsb2FkIiwibGltaXRzIiwiZmllbGRzU2l6ZSIsImFueSIsImJvZHlQYXJzZXIiLCJsaW1pdCIsInVybGVuY29kZWQiLCJleHRlbmRlZCIsImVycm9ySGFuZGxlciIsImF0dGFjaEVycm9ySGFuZGxlcnMiLCJzdGFydFNlcnZlciIsInNlcnZlckNvbmZpZyIsImh0dHBTZXJ2ZXIiLCJjcmVhdGVTZXJ2ZXIiLCJsaXN0ZW4iLCJjZXJ0IiwiZnNQcm9taXNlcyIsInBvc2l4IiwiaHR0cHNTZXJ2ZXIiLCJOYU4iLCJzdGF0aWMiLCJib290VGltZSIsInVwdGltZSIsImZsb29yIiwiaGlnaGNoYXJ0c1ZlcnNpb24iLCJhdmVyYWdlUHJvY2Vzc2luZ1RpbWUiLCJmYWlsZWRFeHBvcnRzIiwic3VjZXNzUmF0aW8iLCJoZWFsdGhSb3V0ZSIsInBvc3QiLCJleHBvcnRSb3V0ZXMiLCJzZW5kRmlsZSIsInVpUm91dGUiLCJjdG9rZW4iLCJISUdIQ0hBUlRTX0FETUlOX1RPS0VOIiwidG9rZW4iLCJ2U3dpdGNoUm91dGUiLCJnZXRFeHByZXNzIiwiZ2V0QXBwIiwibWlkZGxld2FyZXMiLCJlbmFibGVSYXRlTGltaXRpbmciLCJpbmRleCIsIm1hcFRvTmV3Q29uZmlnIiwib2xkT3B0aW9ucyIsInByb3BlcnRpZXNDaGFpbiIsInJlZHVjZSIsInByb3AiLCJzZXRPcHRpb25zIiwidXNlck9wdGlvbnMiLCJjb25maWdJbmRleCIsImZpbmRJbmRleCIsImFyZyIsImZpbGVOYW1lIiwibG9hZENvbmZpZ0ZpbGUiLCJwYWlyQXJndW1lbnRWYWx1ZSIsInNpbmdsZUV4cG9ydCIsImJhdGNoRXhwb3J0IiwiYmF0Y2hGdW5jdGlvbnMiLCJwYWlyIiwiaW5pdFBvb2wiLCJwYXJzZUludCIsImxvZ0Rlc3QiLCJsb2dGaWxlIiwiZW5hYmxlRmlsZUxvZ2dpbmciXSwibWFwcGluZ3MiOiI2dUJBaUJBQSxFQUFPQyxTQUlBLE1BQU1DLEVBQWdCLENBQzNCQyxVQUFXLENBQ1RDLEtBQU0sQ0FDSkMsTUFBTyxHQUNQQyxLQUFNLFdBQ05DLFlBQWEsNkNBR2pCQyxXQUFZLENBQ1ZDLFFBQVMsQ0FDUEosTUFBTyxTQUNQSyxRQUFTLHFCQUNUSixLQUFNLFNBQ05DLFlBQWEsOEJBRWZJLE9BQVEsQ0FDTk4sTUFBTywrQkFDUEssUUFBUyxpQkFDVEosS0FBTSxTQUNOQyxZQUFhLDZDQUVmSyxZQUFhLENBQ1hGLFFBQVMsMEJBQ1RMLE1BQU8sQ0FBQyxhQUFjLGtCQUFtQixpQkFDekNDLEtBQU0sV0FDTkMsWUFBYSxxQ0FFZk0sUUFBUyxDQUNQSCxRQUFTLHFCQUNUTCxNQUFPLENBQ0wsUUFDQSxNQUNBLFFBQ0EsWUFDQSxjQUNBLHVCQUNBLGdCQUNBLHVCQUNBLGVBQ0EsUUFDQSxPQUNBLG1CQUNBLGVBQ0EsY0FDQSxVQUNBLFVBQ0EsV0FDQSxVQUNBLFlBQ0EsY0FDQSxZQUNBLHNCQUNBLFNBQ0EsU0FDQSxX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 diff --git a/dist/index.esm.js b/dist/index.esm.js deleted file mode 100644 index 9a5d19d9..00000000 --- a/dist/index.esm.js +++ /dev/null @@ -1,2 +0,0 @@ -import"colors";import e,{existsSync as t,mkdirSync as o,appendFile as r,readFileSync as i,writeFileSync as n,readFile as s,promises as a}from"fs";import l,{join as c,posix as p}from"path";import u from"body-parser";import d from"cors";import h from"express";import g from"multer";import m from"http";import f from"https";import v from"dotenv";import y from"express-rate-limit";import*as b from"url";import{fileURLToPath as w}from"url";import T from"https-proxy-agent";import{v4 as x}from"uuid";import{Pool as k}from"tarn";import S from"puppeteer";import H from"node:path";import{randomBytes as E}from"node:crypto";import"prompts";v.config();const R={puppeteer:{args:{value:[],type:"string[]",description:"Array of arguments to send to puppeteer."}},highcharts:{version:{value:"latest",envLink:"HIGHCHARTS_VERSION",type:"string",description:"Highcharts version to use."},cdnURL:{value:"https://code.highcharts.com/",envLink:"HIGHCHARTS_CDN",type:"string",description:"The CDN URL of Highcharts scripts to use."},coreScripts:{envLink:"HIGHCHARTS_CORE_SCRIPTS",value:["highcharts","highcharts-more","highcharts-3d"],type:"string[]",description:"Highcharts core scripts to fetch."},modules:{envLink:"HIGHCHARTS_MODULES",value:["stock","map","gantt","exporting","export-data","parallel-coordinates","accessibility","annotations-advanced","boost-canvas","boost","data","draggable-points","static-scale","broken-axis","heatmap","tilemap","timeline","treemap","treegraph","item-series","drilldown","histogram-bellcurve","bullet","funnel","funnel3d","pyramid3d","networkgraph","pareto","pattern-fill","pictorial","price-indicator","sankey","arc-diagram","dependency-wheel","series-label","solid-gauge","sonification","stock-tools","streamgraph","sunburst","variable-pie","variwide","vector","venn","windbarb","wordcloud","xrange","no-data-to-display","drag-panes","debugger","dumbbell","lollipop","cylinder","organization","dotplot","marker-clusters","hollowcandlestick","heikinashi"],type:"string[]",description:"Highcharts modules to fetch."},indicators:{envLink:"HIGHCHARTS_INDICATORS",value:["indicators-all"],type:"string[]",description:"Highcharts indicators to fetch."},scripts:{value:["https://cdnjs.cloudflare.com/ajax/libs/moment.js/2.29.4/moment.min.js","https://cdnjs.cloudflare.com/ajax/libs/moment-timezone/0.5.34/moment-timezone-with-data.min.js"],type:"string[]",description:"Additional direct scripts/optional dependencies (e.g. moment.js)."},forceFetch:{envLink:"HIGHCHARTS_FORCE_FETCH",value:!1,type:"boolean",description:"Should all the scripts be refetched after rerunning the server."}},export:{infile:{value:!1,type:"string",description:"The input file name along with a type (json or svg). It can be a correct JSON or SVG file."},instr:{value:!1,type:"string",description:"An input in a form of a stringified JSON or SVG file. Overrides the --infile."},options:{value:!1,type:"string",description:"An alias for the --instr option."},outfile:{value:!1,type:"string",description:"The output filename along with a type (jpeg, png, pdf or svg). Ignores the --type flag."},type:{envLink:"EXPORT_DEFAULT_TYPE",value:"png",type:"string",description:"The format of the file to export to. Can be jpeg, png, pdf or svg."},constr:{envLink:"EXPORT_DEFAULT_CONSTR",value:"chart",type:"string",description:"The constructor to use. Can be chart, stockChart, mapChart or ganttChart."},defaultHeight:{envLink:"EXPORT_DEFAULT_HEIGHT",value:400,type:"number",description:"The default height of the exported chart. Used when not found any value set."},defaultWidth:{envLink:"EXPORT_DEFAULT_WIDTH",value:600,type:"number",description:"The default width of the exported chart. Used when not found any value set."},defaultScale:{envLink:"EXPORT_DEFAULT_SCALE",value:1,type:"number",description:"The default scale of the exported chart. Ranges between 1 and 5."},height:{type:"number",value:!1,description:"The default height of the exported chart. Overrides the option in the chart settings."},width:{type:"number",value:!1,description:"The width of the exported chart. Overrides the option in the chart settings."},scale:{value:!1,type:"number",description:"The scale of the exported chart. Ranges between 1 and 5."},globalOptions:{value:!1,type:"string",description:"A stringified JSON or a filename with options to be passed into the Highcharts.setOptions."},themeOptions:{value:!1,type:"string",description:"A stringified JSON or a filename with theme options to be passed into the Highcharts.setOptions."},batch:{value:!1,type:"string",description:'Starts a batch job. A string that contains input/output pairs: "in=out;in=out;..".'}},customCode:{allowCodeExecution:{envLink:"HIGHCHARTS_ALLOW_CODE_EXECUTION",value:!1,type:"boolean",description:"If set to true, allow for the execution of arbitrary code when exporting."},allowFileResources:{envLink:"HIGHCHARTS_ALLOW_FILE_RESOURCES",value:!0,type:"boolean",description:"Allow injecting resources from the filesystem. Has no effect when running as a server."},customCode:{value:!1,type:"string",description:"A function to be called before chart initialization. Can be a filename with the js extension."},callback:{value:!1,type:"string",description:"A JavaScript file with a function to run on construction."},resources:{value:!1,type:"string",description:"An additional resource in a form of stringified JSON. It can contain files, js and css sections."},loadConfig:{value:!1,type:"string",description:"A file that contains a pre-defined config to use."},createConfig:{value:!1,type:"string",description:"Allows to set options through a prompt and save in a provided config file."}},server:{enable:{envLink:"HIGHCHARTS_SERVER_ENABLE",value:!1,type:"boolean",cliName:"enableServer",description:"If set to true, starts a server on 0.0.0.0."},host:{envLink:"HIGHCHARTS_SERVER_HOST",value:"0.0.0.0",type:"string",description:"The hostname of the server. Also starts a server listening on the supplied hostname."},port:{envLink:"HIGHCHARTS_SERVER_PORT",value:7801,type:"number",description:"The port to use for the server. Defaults to 7801."},ssl:{enable:{envLink:"HIGHCHARTS_SERVER_SSL_ENABLE",value:!1,type:"boolean",cliName:"enableSsl",description:"Enables the SSL protocol."},force:{envLink:"HIGHCHARTS_SERVER_SSL_FORCE",value:!1,type:"boolean",cliName:"sslForced",description:"If set to true, forces the server to only serve over HTTPS."},port:{envLink:"HIGHCHARTS_SERVER_SSL_PORT",value:443,type:"number",cliName:"sslPort",description:"The port on which to run the SSL server."},certPath:{envLink:"HIGHCHARTS_SSL_CERT_PATH",value:"",type:"string",description:"The path to the SSL certificate/key."}},rateLimiting:{enable:{envLink:"HIGHCHARTS_RATE_LIMIT_ENABLE",value:!1,type:"boolean",cliName:"enableRateLimiting",description:"Enables rate limiting."},maxRequests:{envLink:"HIGHCHARTS_RATE_LIMIT_MAX",value:10,type:"number",description:"Max requests allowed in a one minute."},window:{envLink:"HIGHCHARTS_RATE_LIMIT_WINDOW",value:1,type:"number",description:"The time window in minutes for rate limiting."},delay:{envLink:"HIGHCHARTS_RATE_LIMIT_DELAY",value:0,type:"number",description:"The amount to delay each successive request before hitting the max."},trustProxy:{envLink:"HIGHCHARTS_RATE_LIMIT_TRUST_PROXY",value:!1,type:"boolean",description:"Set this to true if behind a load balancer."},skipKey:{envLink:"HIGHCHARTS_RATE_LIMIT_SKIP_KEY",value:"",type:"number|string",description:"Allows bypassing the rate limiter and should be provided with skipToken argument."},skipToken:{envLink:"HIGHCHARTS_RATE_LIMIT_SKIP_TOKEN",value:"",type:"number|string",description:"Allows bypassing the rate limiter and should be provided with skipKey argument."}}},pool:{minWorkers:{envLink:"HIGHCHARTS_POOL_MIN_WORKERS",value:4,type:"number",description:"The number of initial workers to spawn."},maxWorkers:{envLink:"HIGHCHARTS_POOL_MAX_WORKERS",value:8,type:"number",description:"The number of max workers to spawn."},workLimit:{envLink:"HIGHCHARTS_POOL_WORK_LIMIT",value:40,type:"number",description:"The pieces of work that can be performed before restarting process."},acquireTimeout:{envLink:"HIGHCHARTS_POOL_ACQUIRE_TIMEOUT",value:5e3,type:"number",description:"The number of milliseconds to wait for acquiring a resource."},createTimeout:{envLink:"HIGHCHARTS_POOL_CREATE_TIMEOUT",value:5e3,type:"number",description:"The number of milliseconds to wait for creating a resource."},destroyTimeout:{envLink:"HIGHCHARTS_POOL_DESTROY_TIMEOUT",value:5e3,type:"number",description:"The number of milliseconds to wait for destroying a resource."},idleTimeout:{envLink:"HIGHCHARTS_POOL_IDLE_TIMEOUT",value:3e4,type:"number",description:"The number of milliseconds after an idle resource is destroyed."},rasterizationTimeout:{envLink:"HIGHCHARTS_POOL_RASTERIZATION_TIMEOUT",value:1500,type:"number",description:"The number of milliseconds to wait for rendering a webpage."},createRetryInterval:{envLink:"HIGHCHARTS_POOL_CREATE_RETRY_INTERVAL",value:200,type:"number",description:"The number of milliseconds after the create process is retried in case of fail."},reaperInterval:{envLink:"HIGHCHARTS_POOL_REAPER_INTERVAL",value:1e3,type:"number",description:"The number of milliseconds after the check for idle resources to destroy is triggered."},benchmarking:{envLink:"HIGHCHARTS_POOL_BENCHMARKING",value:!1,type:"boolean",description:"Enable benchmarking."},listenToProcessExits:{envLink:"HIGHCHARTS_POOL_LISTEN_TO_PROCESS_EXITS",value:!0,type:"boolean",description:"Set to false in order to skip attaching process.exit handlers."}},logging:{level:{envLink:"HIGHCHARTS_LOG_LEVEL",value:4,type:"number",cliName:"logLevel",description:"The log level (0: silent, 1: error, 2: warning, 3: notice, 4: verbose)."},file:{envLink:"HIGHCHARTS_LOG_FILE",value:"highcharts-export-server.log",type:"string",cliName:"logFile",description:"A name of a log file. The --logDest also needs to be set to enable file logging."},dest:{envLink:"HIGHCHARTS_LOG_DEST",value:"log/",type:"string",cliName:"logDest",description:"The path to store log files. Also enables file logging."}},ui:{enable:{envLink:"HIGHCHARTS_UI_ENABLE",value:!1,type:"boolean",cliName:"enableUi",description:"Enables the UI for the export server."},route:{envLink:"HIGHCHARTS_UI_ROUTE",value:"/",type:"string",cliName:"uiRoute",description:"The route to attach the UI to."}},other:{noLogo:{envLink:"HIGHCHARTS_NO_LOGO",value:!1,type:"boolean",description:"Skip printing the logo on a startup. Will be replaced by a simple text."}},payload:{}};R.puppeteer.args.value.join(","),R.highcharts.version.value,R.highcharts.cdnURL.value,R.highcharts.modules.value,R.highcharts.scripts.value.join(","),R.highcharts.forceFetch.value,R.export.type.value,R.export.constr.value,R.export.defaultHeight.value,R.export.defaultWidth.value,R.export.defaultScale.value,R.customCode.allowCodeExecution.value,R.customCode.allowFileResources.value,R.server.enable.value,R.server.host.value,R.server.port.value,R.server.ssl.enable.value,R.server.ssl.force.value,R.server.ssl.port.value,R.server.ssl.certPath.value,R.server.rateLimiting.enable.value,R.server.rateLimiting.maxRequests.value,R.server.rateLimiting.window.value,R.server.rateLimiting.delay.value,R.server.rateLimiting.trustProxy.value,R.server.rateLimiting.skipKey.value,R.server.rateLimiting.skipToken.value,R.pool.minWorkers.value,R.pool.maxWorkers.value,R.pool.workLimit.value,R.pool.acquireTimeout.value,R.pool.createTimeout.value,R.pool.destroyTimeout.value,R.pool.idleTimeout.value,R.pool.rasterizationTimeout.value,R.pool.createRetryInterval.value,R.pool.reaperInterval.value,R.pool.benchmarking.value,R.pool.listenToProcessExits.value,R.logging.level.value,R.logging.file.value,R.logging.dest.value,R.ui.enable.value,R.ui.route.value,R.other.noLogo.value;const L=["options","globalOptions","themeOptions","resources","payload"],C={},O=(e,t="")=>{Object.keys(e).forEach((o=>{if(!["puppeteer","highcharts"].includes(o)){const r=e[o];void 0===r.value?O(r,`${t}.${o}`):C[r.cliName||o]=`${t}.${o}`.substring(1)}}))};O(R);let _={toConsole:!0,toFile:!1,pathCreated:!1,levelsDesc:[{title:"error",color:"red"},{title:"warning",color:"yellow"},{title:"notice",color:"blue"},{title:"verbose",color:"gray"}],listeners:[]};for(const[e,t]of Object.entries(R.logging))_[e]=t.value;const I=(...e)=>{const[i,...n]=e,{level:s,levelsDesc:a}=_;if(0===i||i>s||s>a.length)return;const l=`${(new Date).toString().split("(")[0].trim()} [${a[i-1].title}] -`;_.listeners.forEach((e=>{e(l,n.join(" "))})),_.toFile&&(_.pathCreated||(!t(_.dest)&&o(_.dest),_.pathCreated=!0),r(`${_.dest}${_.file}`,[l].concat(n).join(" ")+"\n",(e=>{e&&(console.log(`[logger] Unable to write to log file: ${e}`),_.toFile=!1)}))),_.toConsole&&console.log.apply(void 0,[l.toString()[_.levelsDesc[i-1].color]].concat(n))},A=w(new URL("../.",import.meta.url)),$=(e,t=/\s\s+/g,o=" ")=>e.replaceAll(t,o).trim(),P=(e,t)=>{const o=["png","jpeg","pdf","svg"];if(t){const r=t.split(".").pop();o.includes(r)&&e!==r&&(e=r)}return{"image/png":"png","image/jpeg":"jpeg","application/pdf":"pdf","image/svg+xml":"svg"}[e]||o.find((t=>t===e))||"png"},N=(e=!1,t)=>{const o=["js","css","files"];let r=e,n=!1;if(t&&e.endsWith(".json"))try{e?e&&e.endsWith(".json")?r=j(i(e,"utf8")):(r=j(e),!0===r&&(r=j(i("resources.json","utf8")))):r=j(i("resources.json","utf8"))}catch(e){return I(3,"[cli] No resources found.")}else r=j(e),t||delete r.files;for(const e in r)o.includes(e)?n||(n=!0):delete r[e];return n?(r.files&&(r.files=r.files.map((e=>e.trim())),(!r.files||r.files.length<=0)&&delete r.files),r):I(3,"[cli] No resources found.")};function j(e,t){try{const o=JSON.parse("string"!=typeof e?JSON.stringify(e):e);return"string"!=typeof o&&t?JSON.stringify(o):o}catch(e){return!1}}const G=e=>{if(null===e||"object"!=typeof e)return e;const t=Array.isArray(e)?[]:{};for(const o in e)Object.prototype.hasOwnProperty.call(e,o)&&(t[o]=G(e[o]));return t},U=(e,t)=>JSON.stringify(e,((e,o)=>("string"==typeof o&&((o=o.trim()).startsWith("function(")||o.startsWith("function ("))&&o.endsWith("}")&&(o=t?`EXP_FUN${(o+"").replaceAll(/\n|\t|\r/g," ")}EXP_FUN`:void 0),"function"==typeof o?`EXP_FUN${(o+"").replaceAll(/\n|\t|\r/g," ")}EXP_FUN`:o))).replaceAll(/"EXP_FUN|EXP_FUN"/g,"");function W(){console.log("Usage of CLI arguments:".bold,"\n------",`\nFor more detailed information visit readme at: ${"https://github.com/highcharts/node-export-server#readme".bold.yellow}.`);const e=t=>{for(const[o,r]of Object.entries(t))if(Object.prototype.hasOwnProperty.call(r,"value")){let e=` --${r.cliName||o} ${("<"+r.type+">").green} `;if(e.length<48)for(let t=e.length;t<48;t++)e+=".";console.log(e,r.description,`[Default: ${r.value.toString().bold}]`.blue)}else e(r)};Object.keys(R).forEach((t=>{["puppeteer","highcharts"].includes(t)||(console.log(`\n${t.toUpperCase()}`.red),e(R[t]))})),console.log("\n")}const M=e=>!["false","undefined","null","NaN","0",""].includes(e)&&!!e,F=(e,t)=>{if(e&&"string"==typeof e)return(e=e.trim()).endsWith(".js")?!!t&&F(i(e,"utf8")):e.startsWith("function()")||e.startsWith("function ()")||e.startsWith("()=>")||e.startsWith("() =>")?`(${e})()`:e.replace(/;$/,"")};var D=(e,t)=>{const o="Too many requests, you have been rate limited. Please try again later.",r={max:t.maxRequests||30,window:t.window||1,delay:t.delay||0,trustProxy:t.trustProxy||!1,skipKey:t.skipKey||!1,skipToken:t.skipToken||!1};r.trustProxy&&e.enable("trust proxy");const i=y({windowMs:60*r.window*1e3,max:r.max,delayMs:r.delay,handler:(e,t)=>{t.format({json:()=>{t.status(429).send({message:o})},default:()=>{t.status(429).send(o)}})},skip:e=>!1!==r.skipKey&&!1!==r.skipToken&&e.query.key===r.skipKey&&e.query.access_token===r.skipToken&&(I(4,"[rate-limiting] Skipping rate limiter."),!0)});e.use(i),I(3,$(`[rate-limiting] Enabled rate limiting: ${r.max} requests\n per ${r.window} minute per IP, trusting proxy:\n ${r.trustProxy}.`))};async function q(e,t={}){return new Promise(((o,r)=>{const i=(e=>e.startsWith("https")?f:m)(e);i.get(e,t,(e=>{let t="";e.on("data",(e=>{t+=e})),e.on("end",(()=>{t||r("Nothing was fetched from the URL."),e.text=t,o(e)}))})).on("error",(e=>{r(e)}))}))}v.config();const V=c(A,".cache"),J={cdnURL:"https://code.highcharts.com/",activeManifest:{},sources:"",hcVersion:""};let z=!1;const K=()=>J.hcVersion=J.sources.substr(0,J.sources.indexOf("*/")).replace("/*","").replace("*/","").replace(/\n/g,"").trim(),X=async(e,t)=>{try{e.endsWith(".js")&&(e=e.substring(0,e.length-3)),I(4,`[cache] Fetching script - ${e}.js`);const o=t?{agent:t,timeout:+process.env.PROXY_SERVER_TIMEOUT||5e3}:{},r=await q(`${e}.js`,o);if(200===r.statusCode)return r.text;throw`${r.statusCode}`}catch(t){throw I(1,`[cache] Error fetching script ${e}.js: ${t}.`),t}},B=async(e,t)=>{const{coreScripts:o,modules:r,indicators:i,scripts:s}=e,a="latest"!==e.version&&e.version?`${e.version}/`:"";I(3,"[cache] Updating cache to Highcharts ",a);const l=[...o.map((e=>`${a}${e}`)),...r.map((e=>"map"===e?`maps/${a}modules/${e}`:`${a}modules/${e}`)),...i.map((e=>`stock/${a}indicators/${e}`))];let c;const p=process.env.PROXY_SERVER_HOST,u=process.env.PROXY_SERVER_PORT;p&&u&&(c=new T({host:p,port:+u}));const d={};try{return J.sources=(await Promise.all([...l.map((async t=>{const o=await X(`${e.cdnURL||J.cdnURL}${t}`,c);return"string"==typeof o&&(d[t.replace(/(.*)\/|(.*)modules\/|stock\/(.*)indicators\/|maps\/(.*)modules\//gi,"")]=1),o})),...s.map((e=>X(e,c)))])).join(";\n"),K(),n(t,J.sources),d}catch(e){I(1,"[cache] Unable to update local Highcharts cache.")}},Y=async e=>{let r;const s=c(V,"manifest.json"),a=c(V,"sources.js");if(z=e,!t(V)&&o(V),!t(s)||e.forceFetch)I(3,"[cache] Fetching and caching Highcharts dependencies."),r=await B(e,a);else{let t=!1;const o=JSON.parse(i(s));if(o.modules&&Array.isArray(o.modules)){const e={};o.modules.forEach((t=>e[t]=1)),o.modules=e}const{modules:n,coreScripts:l,indicators:c}=e,p=n.length+l.length+c.length;o.version!==e.version?(I(3,"[cache] Highcharts version mismatch in cache, need to re-fetch."),t=!0):Object.keys(o.modules||{}).length!==p?(I(3,"[cache] Cache and requested modules does not match, need to re-fetch."),t=!0):t=(e.modules||[]).some((e=>{if(!o.modules[e])return I(3,`[cache] The ${e} missing in cache, need to re-fetch.`),!0})),t?r=await B(e,a):(I(3,"[cache] Dependency cache is up to date, proceeding."),J.sources=i(a,"utf8"),r=o.modules,K())}await(async(e,t)=>{const o={version:e.version,modules:t||{}};J.activeManifest=o,I(4,"[cache] writing new manifest");try{n(c(V,"manifest.json"),JSON.stringify(o),"utf8")}catch(e){I(1,`[cache] Error writing cache manifest: ${e}.`)}})(e,r)};var Q=async e=>!!z&&await Y(Object.assign(z,{version:e})),Z=()=>J,ee=()=>J.hcVersion;const te=E(64).toString("base64url"),oe=H.join("tmp",`puppeteer-${te}`),re=[`--user-data-dir=${H.join(oe,"profile")}`,"--autoplay-policy=user-gesture-required","--disable-background-networking","--disable-background-timer-throttling","--disable-backgrounding-occluded-windows","--disable-breakpad","--disable-client-side-phishing-detection","--disable-component-update","--disable-default-apps","--disable-dev-shm-usage","--disable-domain-reliability","--disable-extensions","--disable-features=AudioServiceOutOfProcess","--disable-hang-monitor","--disable-ipc-flooding-protection","--disable-notifications","--disable-offer-store-unmasked-wallet-cards","--disable-popup-blocking","--disable-print-preview","--disable-prompt-on-repost","--disable-renderer-backgrounding","--disable-session-crashed-bubble","--disable-setuid-sandbox","--disable-speech-api","--disable-sync","--hide-crash-restore-bubble","--hide-scrollbars","--ignore-gpu-blacklist","--metrics-recording-only","--mute-audio","--no-default-browser-check","--no-first-run","--no-pings","--no-sandbox","--no-zygote","--password-store=basic","--use-mock-keychain"],ie=b.fileURLToPath(new URL(".",import.meta.url)),ne=e.readFileSync(ie+"/../templates/template.html","utf8");let se;const ae=async e=>{await e.setContent(ne),await e.addScriptTag({path:ie+"/../.cache/sources.js"}),await e.evaluate((()=>window.setupHighcharts())),e.on("pageerror",(async t=>{I(1,"[page error]",t),await e.$eval("#container",((e,t)=>{window._displayErrors&&(e.innerHTML=t)}),`

Chart input data error

${t.toString()}`)}))},le=async()=>{if(!se)return!1;const e=await se.newPage();return await ae(e),e},ce=async()=>{se.connected&&await se.close()};const pe=b.fileURLToPath(new URL(".",import.meta.url)),ue=async(e,t,o)=>await e.evaluate(((e,t)=>window.triggerExport(e,t)),t,o);var de=async(e,t,o)=>{const r=[],n=async e=>{for(const e of r)await e.dispose();await e.evaluate((()=>{const[,...e]=document.getElementsByTagName("script"),[,...t]=document.getElementsByTagName("style"),[...o]=document.getElementsByTagName("link");for(const r of[...e,...t,...o])r.remove()}))};try{const s=()=>{};I(4,"[export] Determining export path.");const a=o.export;await e.evaluate((()=>requestAnimationFrame((()=>{}))));const c=a?.options?.chart?.displayErrors&&Z().activeManifest.modules.debugger;await e.evaluate((e=>window._displayErrors=e),c);const p=()=>{};let u;if(t.indexOf&&(t.indexOf("=0||t.indexOf("=0)){if(I(4,"[export] Treating as SVG."),"svg"===a.type)return t;u=!0;const o=()=>{};await e.setContent((e=>`\n\n\n \n \n Highcarts Export\n \n \n \n
\n ${e}\n
\n \n\n\n`)(t)),o()}else if(I(4,"[export] Treating as config."),a.strInj){const t=()=>{};await ue(e,{chart:{height:a.height,width:a.width}},o),t()}else{t.chart.height=a.height,t.chart.width=a.width;const r=()=>{};await ue(e,t,o),r()}p();const d=()=>{},h=o.customCode.resources;if(h){if(h.js&&r.push(await e.addScriptTag({content:h.js})),h.files)for(const t of h.files)try{const o=!t.startsWith("http");r.push(await e.addScriptTag(o?{content:i(t,"utf8")}:{url:t}))}catch(e){I(4,"[export] JS file not found.")}const t=()=>{};if(h.css){let t=h.css.match(/@import\s*([^;]*);/g);if(t)for(let i of t)i&&(i=i.replace("url(","").replace("@import","").replace(/"/g,"").replace(/'/g,"").replace(/;/,"").replace(/\)/g,"").trim(),i.startsWith("http")?r.push(await e.addStyleTag({url:i})):o.customCode.allowFileResources&&r.push(await e.addStyleTag({path:l.join(pe,i)})));r.push(await e.addStyleTag({content:h.css.replace(/@import\s*([^;]*);/g,"")||" "}))}t()}d();const g=u?await e.$eval("#chart-container svg:first-of-type",(async(e,t)=>({chartHeight:e.height.baseVal.value*t,chartWidth:e.width.baseVal.value*t})),parseFloat(a.scale)):await e.evaluate((async()=>{const{chartHeight:e,chartWidth:t}=window.Highcharts.charts[0];return{chartHeight:e,chartWidth:t}})),m=()=>{},f=Math.ceil(g?.chartHeight||a.height),v=Math.ceil(g?.chartWidth||a.width);await e.setViewport({height:f,width:v,deviceScaleFactor:u?1:parseFloat(a.scale)});const y=u?e=>{document.body.style.zoom=e,document.body.style.margin="0px"}:()=>{document.body.style.zoom=1};await e.evaluate(y,parseFloat(a.scale));const{height:b,width:w,x:T,y:x}=await(e=>e.$eval("#chart-container",(e=>{const{x:t,y:o,width:r,height:i}=e.getBoundingClientRect();return{x:t,y:o,width:r,height:Math.trunc(i>1?i:500)}})))(e);let k;u||await e.setViewport({width:Math.round(w),height:Math.round(b),deviceScaleFactor:parseFloat(a.scale)}),m();const S=()=>{};if("svg"===a.type)k=await(async e=>await e.$eval("#container svg:first-of-type",(e=>e.outerHTML)))(e);else if("png"===a.type||"jpeg"===a.type)k=await(async(e,t,o,r,i)=>await Promise.race([e.screenshot({type:t,encoding:o,clip:r,omitBackground:"png"==t}),new Promise(((e,t)=>setTimeout((()=>t(new Error("Rasterization timeout"))),i||1500)))]))(e,a.type,"base64",{width:v,height:f,x:T,y:x},o.pool.rasterizationTimeout);else{if("pdf"!==a.type)throw`Unsupported output format ${a.type}`;k=await(async(e,t,o,r)=>await e.pdf({height:t+1,width:o,encoding:r}))(e,f,v,"base64")}return await e.evaluate((()=>{const e=Highcharts.charts;if(e.length)for(const t of e)t&&t.destroy(),Highcharts.charts.shift()})),S(),s(),await n(e),k}catch(t){return await n(e),I(1,`[export] Error encountered during export: ${t}`),t}};let he,ge=0,me=0,fe=0,ve=0,ye=0,be={},we=!1;const Te={create:async()=>{const e=x();let t=!1;const o=(new Date).getTime();try{if(t=await le(),!t||t.isClosed())throw"[pool] Invalid page";I(3,`[pool] Successfully created a worker ${e} - took ${(new Date).getTime()-o} ms.`)}catch(e){throw I(1,`[pool] Error creating a new page in pool entry creation! ${e}`),"Error creating page"}return{id:e,page:t,workCount:Math.round(Math.random()*(be.workLimit/2))}},validate:async e=>be.workLimit&&++e.workCount>be.workLimit?(I(3,"[pool] Worker failed validation:",`exceeded work limit (limit is ${be.workLimit})`),!1):(await(async e=>{try{await e.goto("about:blank"),await ae(e)}catch(e){I(3,"[browser] Could not clear page")}})(e.page),!0),destroy:e=>{I(3,`[pool] Destroying pool entry ${e.id}.`),e.page&&e.page.close()},log:(e,t)=>console.log(`${t}: ${e}`)},xe=async e=>{he=e.puppeteerArgs;try{await(async e=>{const t=[...re,...e||[]];if(!se){let e=0;const o=async()=>{try{I(3,"[browser] attempting to get a browser instance (try",e+")"),se=await S.launch({headless:"new",args:t,userDataDir:"./tmp/"})}catch(t){I(0,"[browser]",t),++e<25?(I(3,"[browser] failed:",t),await new Promise((e=>setTimeout(e,4e3))),await o()):I(0,"Max retries reached")}};try{await o()}catch(e){return I(0,"[browser] Unable to open browser"),!1}if(!se)return I(0,"[browser] Unable to open browser"),!1}return se})(he)}catch(e){I(0,"[pool|browser]",e)}if(be=e&&e.pool?{...e.pool}:{},I(3,"[pool] Initializing pool:",`min ${be.minWorkers}, max ${be.maxWorkers}.`),we)return I(4,"[pool] Already initialized, please kill it before creating a new one.");be.listenToProcessExits&&(I(4,"[pool] Attaching exit listeners to the process."),process.on("exit",(async()=>{await ke()})),process.on("SIGINT",((e,t)=>{I(4,`The ${e} event with code: ${t}.`),process.exit(1)})),process.on("SIGTERM",((e,t)=>{I(4,`The ${e} event with code: ${t}.`),process.exit(1)})),process.on("uncaughtException",(async(e,t)=>{I(4,`The ${t} error, message: ${e.message}.`)})));try{we=new k({...Te,min:be.minWorkers,max:be.maxWorkers,acquireTimeoutMillis:be.acquireTimeout,createTimeoutMillis:be.createTimeout,destroyTimeoutMillis:be.destroyTimeout,idleTimeoutMillis:be.idleTimeout,createRetryIntervalMillis:be.createRetryInterval,reapIntervalMillis:be.reaperInterval,propagateCreateError:!1}),we.on("createFail",((e,t)=>{I(1,`[pool] Error when creating worker of an event id ${e}:`,t)})),we.on("acquireFail",((e,t)=>{I(1,`[pool] Error when acquiring worker of an event id ${e}:`,t)})),we.on("destroyFail",((e,t,o)=>{I(1,`[pool] Error when destroying worker of an id ${t.id}, event id ${e}:`,o)})),we.on("release",(e=>{I(4,`[pool] Releasing a worker of an id ${e.id}`)})),we.on("destroySuccess",((e,t)=>{I(4,`[pool] Destroyed a worker of an id ${t.id}`)}));const e=[];for(let t=0;t{we.release(e)})),I(3,`[pool] The pool is ready with ${be.minWorkers} initial resources waiting.`)}catch(e){throw I(1,`[pool] Couldn't create the worker pool ${e}`),e}};async function ke(){return I(3,"[pool] Killing all workers."),we.destroyed?(await ce(),!0):(await we.destroy(),await ce(),!0)}const Se=async(e,t)=>{let o;const r=e=>{throw++ve,o&&we.release(o),"In pool.postWork: "+e};if(I(4,"[pool] Work received, starting to process."),be.benchmarking&&He(),++me,!we)return I(1,"[pool] Work received, but pool has not been started."),r("Pool is not inited but work was posted to it!");try{I(4,"[pool] Acquiring worker"),o=await we.acquire().promise}catch(e){return r(`[pool] Error when acquiring available entry: ${e}`)}if(I(4,"[pool] Acquired worker handle"),!o.page)return r("Resolved worker page is invalid: pool setup is wonky");try{let i=(new Date).getTime();I(4,`[pool] Starting work on pool entry ${o.id}.`);const n=await de(o.page,e,t);if(n instanceof Error)return"Rasterization timeout"===n.message&&(o.page.close(),o.page=await le()),r(n);we.release(o);const s=(new Date).getTime()-i;return fe+=s,ye=fe/++ge,I(4,`[pool] Work completed in ${s} ms.`),{data:n,options:t}}catch(e){r(`Error trying to perform puppeteer export: ${e}.`)}};function He(){const{min:e,max:t,size:o,available:r,borrowed:i,pending:n,spareResourceCapacity:s}=we;I(4,`[pool] The minimum number of resources allowed by pool: ${e}.`),I(4,`[pool] The maximum number of resources allowed by pool: ${t}.`),I(4,`[pool] The number of all resources in pool (free or in use): ${o}.`),I(4,`[pool] The number of resources that are currently available: ${r}.`),I(4,`[pool] The number of resources that are currently acquired: ${i}.`),I(4,`[pool] The number of callers waiting to acquire a resource: ${n}.`),I(4,`[pool] The number of how many more resources can the pool manage/create: ${s}.`)}var Ee=()=>({min:we.min,max:we.max,size:we.size,available:we.available,borrowed:we.borrowed,pending:we.pending,spareResourceCapacity:we.spareResourceCapacity}),Re=()=>me,Le=()=>ve,Ce=()=>ye,Oe=()=>ge;const _e=process.env.npm_package_version,Ie=new Date;let Ae={};const $e=()=>Ae,Pe=(e,t,o=[])=>{const r=G(e);for(const[e,n]of Object.entries(t))r[e]="object"!=typeof(i=n)||Array.isArray(i)||null===i||o.includes(e)||void 0===r[e]?void 0!==n?n:r[e]:Pe(r[e],n,o);var i;return r};function Ne(e,t={},o=""){Object.keys(e).forEach((r=>{if(!["puppeteer","highcharts"].includes(r)){const i=e[r],n=t&&t[r];let s;void 0===i.value?Ne(i,n,`${o}.${r}`):(void 0!==n&&(i.value=n),i.envLink&&("boolean"===i.type?i.value=M([process.env[i.envLink],i.value].find((e=>e||"false"===e))):"number"===i.type?(s=+process.env[i.envLink],i.value=s>=0?s:i.value):i.type.indexOf("]")>=0&&process.env[i.envLink]?i.value=process.env[i.envLink].split(","):i.value=process.env[i.envLink]||i.value))}}))}function je(e){let t={};for(const[o,r]of Object.entries(e))t[o]=Object.prototype.hasOwnProperty.call(r,"value")?r.value:je(r);return t}let Ge=!1;const Ue=async(e,t)=>{I(4,"[chart] Starting exporting process.");const o=((e,t={})=>{let o={};return e.svg?(o=G(t),o.export.type=e.type||e.export.type,o.export.scale=e.scale||e.export.scale,o.export.outfile=e.outfile||e.export.outfile,o.payload={svg:e.svg}):o=Pe(t,e,L),o.export.outfile=o.export?.outfile||`chart.${o.export?.type||"png"}`,o})(e,$e()),r=o.export;return o.payload?.svg&&""!==o.payload.svg?De(o.payload.svg.trim(),o,t):r.infile&&r.infile.length?(I(4,"[chart] Attempting to export from an input file."),s(r.infile,"utf8",((e,r)=>e?I(1,`[chart] Error loading input file: ${e}.`):(o.export.instr=r,De(o.export.instr.trim(),o,t))))):r.instr&&""!==r.instr||r.options&&""!==r.options?(I(4,"[chart] Attempting to export from a raw input."),M(o.customCode?.allowCodeExecution)?Fe(o,t):"string"==typeof r.instr?De(r.instr.trim(),o,t):Me(o,r.instr||r.options,t)):(I(1,$(`[chart] No input specified.\n ${JSON.stringify(r,void 0," ")}.`)),t&&t(!1,{error:!0,message:"No input specified."}))},We=e=>{const{chart:t,exporting:o}=e.export?.options||j(e.export?.instr),r=j(e.export?.globalOptions);let i=e.export?.scale||o?.scale||r?.exporting?.scale||e.export?.defaultScale||1;return i=Math.max(.1,Math.min(i,5)),i=((e,t=1)=>{const o=Math.pow(10,t||0);return Math.round(+e*o)/o})(i,2),{height:e.export?.height||o?.sourceHeight||t?.height||r?.exporting?.sourceHeight||r?.chart?.height||e.export?.defaultHeight||400,width:e.export?.width||o?.sourceWidth||t?.width||r?.exporting?.sourceWidth||r?.chart?.width||e.export?.defaultWidth||600,scale:i}},Me=(e,t,o,r)=>{let{export:n,customCode:s}=e;const a="boolean"==typeof s.allowCodeExecution?s.allowCodeExecution:Ge;if(s){if(a)if("string"==typeof e.customCode.resources)e.customCode.resources=N(e.customCode.resources,M(e.customCode.allowFileResources));else if(!e.customCode.resources)try{const t=i("resources.json","utf8");e.customCode.resources=N(t,M(e.customCode.allowFileResources))}catch(e){I(3,"[chart] The default resources.json file not found.")}}else s=e.customCode={};if(!a&&s){if(s.callback||s.resources||s.customCode)return o&&o(!1,{error:!0,message:$("The callback, resources and customCode have been disabled for this\n server.")});s.callback=!1,s.resources=!1,s.customCode=!1}if(t&&(t.chart=t.chart||{},t.exporting=t.exporting||{},t.exporting.enabled=!1),n.constr=n.constr||"chart",n.type=P(n.type,n.outfile),"svg"===n.type&&(n.width=!1),["globalOptions","themeOptions"].forEach((e=>{try{n&&n[e]&&("string"==typeof n[e]&&n[e].endsWith(".json")?n[e]=j(i(n[e],"utf8"),!0):n[e]=j(n[e],!0))}catch(t){n[e]={},I(1,`[chart] The ${e} not found.`)}})),s.allowCodeExecution&&(s.customCode=F(s.customCode,s.allowFileResources)),s&&s.callback&&s.callback?.indexOf("{")<0)if(s.allowFileResources)try{s.callback=i(s.callback,"utf8")}catch(e){I(2,`[chart] Error loading callback: ${e}.`),s.callback=!1}else s.callback=!1;e.export={...e.export,...We(e)},Se(n.strInj||t||r,e).then((e=>o(e))).catch((e=>(I(0,"[chart] When posting work:",e),o(!1,e))))},Fe=(e,t)=>{try{let o,r=e.export.instr||e.export.options;return"string"!=typeof r&&(o=r=U(r,e.customCode?.allowCodeExecution)),o=r.replaceAll(/\t|\n|\r/g,"").trim(),";"===o[o.length-1]&&(o=o.substring(0,o.length-1)),e.export.strInj=o,Me(e,!1,t)}catch(o){const r=$(`Malformed input detected for ${e.export?.requestId||"?"}:\n Please make sure that your JSON/JavaScript options\n are sent using the "options" attribute, and that if you're using\n SVG, it is unescaped.`);return I(1,r),t&&t(!1,JSON.stringify({error:!0,message:r}))}},De=(e,t,o)=>{const{allowCodeExecution:r}=t.customCode;if(e.indexOf("=0||e.indexOf("=0)return I(4,"[chart] Parsing input as SVG."),Me(t,!1,o,e);try{const r=JSON.parse(e.replaceAll(/\t|\n|\r/g," "));return Me(t,r,o)}catch(e){return M(r)?Fe(t,o):o&&o(!1,{error:!0,message:$("Only JSON configurations and SVG is allowed for this server. If\n this is your server, JavaScript exporting can be enabled by starting\n the server with the --allowCodeExecution flag.")})}},qe={png:"image/png",jpeg:"image/jpeg",gif:"image/gif",pdf:"application/pdf",svg:"image/svg+xml"};let Ve=0;const Je=[],ze=[],Ke=(e,t,o,r)=>{let i=!0;const{id:n,uniqueId:s,type:a,body:l}=r;return e.some((e=>{if(e){let r=e(t,o,n,s,a,l);return void 0!==r&&!0!==r&&(i=r),!0}})),i},Xe=(e,t)=>{(()=>{const e=process.hrtime.bigint()})();const o=$e(),r=e.body,i=++Ve,n=x().replace(/-/g,"");let s=P(r.type);if(!r)return t.status(400).send($("Body is required. Sending a body? Make sure your Content-type header\n is correct. Accepted is application/json and multipart/form-data."));let a=j(r.infile||r.options||r.data);if(!a&&!r.svg)return I(2,$(`Request ${n} from ${e.headers["x-forwarded-for"]||e.connection.remoteAddress} was incorrect. Check your payload.`)),t.status(400).send($("No correct chart data found. Please make sure you are using\n application/json or multipart/form-data headers, and that the chart\n data is in the 'infile', 'options' or 'data' attribute if sending\n JSON or in the 'svg' if sending SVG."));let l=!1;if(l=Ke(Je,e,t,{id:i,uniqueId:n,type:s,body:r}),!0!==l)return t.send(l);let c=!1;e.socket.on("close",(()=>{c=!0})),I(4,`[export] Got an incoming HTTP request ${n}.`),r.constr="string"==typeof r.constr&&r.constr||"chart";const p={export:{instr:a,type:s,constr:r.constr[0].toLowerCase()+r.constr.substr(1),height:r.height,width:r.width,scale:r.scale||o.export.scale,globalOptions:j(r.globalOptions,!0),themeOptions:j(r.themeOptions,!0)},customCode:{allowCodeExecution:Ge,allowFileResources:!1,resources:j(r.resources,!0),callback:r.callback,customCode:r.customCode}};a&&(p.export.instr=U(a,p.customCode.allowCodeExecution));const u=Pe(o,p);if(u.export.options=a,u.payload={svg:r.svg||!1,b64:r.b64||!1,dataOptions:j(r.dataOptions,!0),noDownload:r.noDownload||!1,requestId:n},r.svg&&(d=u.payload.svg,["localhost","(10).(.*).(.*).(.*)","(127).(.*).(.*).(.*)","(172).(1[6-9]|2[0-9]|3[0-1]).(.*).(.*)","(192).(168).(.*).(.*)"].some((e=>d.match(`xlink:href="(?:(http://|https://))?${e}`)))))return t.status(400).send("SVG potentially contain at least one forbidden URL in xlink:href element.");var d;Ue(u,((o,a)=>(e.socket.removeAllListeners("close"),c?I(3,$("[export] The client closed the connection before the chart was done\n processing.")):a?(I(1,$(`[export] Work: ${n} could not be completed, sending:\n ${a}`)),t.status(400).send(a.message)):o&&o.data?(s=o.options.export.type,Ke(ze,e,t,{id:i,body:o.data}),o.data?r.b64?"pdf"===s?t.send(Buffer.from(o.data,"utf8").toString("base64")):t.send(o.data):(t.header("Content-Type",qe[s]||"image/png"),r.noDownload||t.attachment(`${e.params.filename||e.body.filename||"chart"}.${s||"png"}`),"svg"===s?t.send(o.data):t.send(Buffer.from(o.data,"base64"))):void 0):(I(1,$(`[export] Unexpected return from chart generation, please check your\n data Request: ${n} is ${o.data}.`)),t.status(400).send("Unexpected return from chart generation, please check your data.")))))};const Be=h();Be.disable("x-powered-by"),Be.use(d());const Ye=g.memoryStorage(),Qe=g({storage:Ye,limits:{fieldsSize:"50MB"}});Be.use(Qe.any()),Be.use(u.json({limit:"50mb"})),Be.use(u.urlencoded({extended:!0,limit:"50mb"})),Be.use(u.urlencoded({extended:!1,limit:"50mb"}));const Ze=e=>I(1,`[server] Socket error: ${e}`),et=e=>{e.on("clientError",Ze),e.on("error",Ze),e.on("connection",(e=>e.on("error",(e=>Ze(e)))))},tt=async e=>{if(!e.enable)return!1;if(!e.ssl.enable&&!e.ssl.force){const t=m.createServer(Be);et(t),t.listen(e.port,e.host),I(3,`[server] Started HTTP server on ${e.host}:${e.port}.`)}if(e.ssl.enable){let t,o;try{t=await a.readFile(p.join(e.ssl.certPath,"server.key"),"utf8"),o=await a.readFile(p.join(e.ssl.certPath,"server.crt"),"utf8")}catch(t){I(1,`[server] Unable to load key/certificate from ${e.ssl.certPath}.`)}if(t&&o){const t=f.createServer(Be);et(t),t.listen(e.ssl.port,e.host),I(3,`[server] Started HTTPS server on ${e.host}:${e.ssl.port}.`)}}e.rateLimiting&&e.rateLimiting.enable&&![0,NaN].includes(e.rateLimiting.maxRequests)&&D(Be,e.rateLimiting),Be.use(h.static(p.join(A,"public"))),(e=>{!!e&&e.get("/health",((e,t)=>{t.send({status:"OK",bootTime:Ie,uptime:Math.floor(((new Date).getTime()-Ie.getTime())/1e3/60)+" minutes",version:_e,highchartsVersion:ee(),averageProcessingTime:Ce(),performedExports:Oe(),failedExports:Le(),exportAttempts:Re(),sucessRatio:Oe()/Re()*100,pool:Ee()})}))})(Be),(e=>{e.post("/",Xe),e.post("/:filename",Xe)})(Be),(e=>{!!e&&e.get("/",((e,t)=>{t.sendFile(c(A,"public","index.html"))}))})(Be),(e=>{!!e&&e.post("/change_hc_version/:newVersion",(async(e,t)=>{const o=process.env.HIGHCHARTS_ADMIN_TOKEN;if(!o||!o.length)return t.send({error:!0,message:"Server not configured to do run-time version changes: HIGHCHARTS_ADMIN_TOKEN not set"});const r=e.get("hc-auth");if(!r||r!==o)return t.send({error:!0,message:"Invalid or missing token: set token in the hc-auth header"});const i=e.params.newVersion;if(i){try{await Q(i)}catch(e){t.send({error:!0,message:e})}t.send({version:ee()})}else t.send({error:!0,message:"No new version supplied"})}))})(Be)};var ot={startServer:tt,getExpress:()=>h,getApp:()=>Be,use:(e,...t)=>{Be.use(e,...t)},get:(e,...t)=>{Be.get(e,...t)},post:(e,...t)=>{Be.post(e,...t)},enableRateLimiting:e=>D(Be,e)},rt={log:I,mapToNewConfig:e=>{const t={};for(const[o,r]of Object.entries(e)){const e=C[o]?C[o].split("."):[];e.reduce(((t,o,i)=>t[o]=e.length-1===i?r:t[o]||{}),t)}return t},setOptions:(e,t)=>(t?.length&&(Ae=function(e){const t=e.findIndex((e=>"loadConfig"===e.replace(/-/g,"")));if(t>-1&&e[t+1]){const o=e[t+1];try{if(o&&o.endsWith(".json"))return JSON.parse(i(o))}catch(e){I(1,`[config] Unable to load config from the ${o}: ${e}`)}}return{}}(t)),Ne(R,Ae),Ae=je(R),e&&(Ae=Pe(Ae,e,L)),t?.length&&(Ae=function(e,t,o){for(let o=0;o(i.length-1===a&&void 0!==n[s]&&(t[++o]?n[s]=t[o]||n[s]:(console.log(`Missing argument value for ${r}!`.red,"\n"),e=W())),n[s])),e)}return e}(Ae,t)),Ae),singleExport:e=>{e.export.instr=e.export.instr||e.export.options,Ue(e,((e,t)=>{t&&(I(1,`[cli] ${t.message}`),process.exit(1));const{outfile:o,type:r}=e.options.export;n(o||`chart.${r}`,"svg"!==r?Buffer.from(e.data,"base64"):e.data),ke()}))},startExport:Ue,batchExport:e=>{const t=[];for(let o of e.export.batch.split(";"))o=o.split("="),2===o.length&&t.push(new Promise(((t,r)=>{Ue({...e,export:{...e.export,infile:o[0],outfile:o[1]}},((e,o)=>{if(o)return r(o);n(e.options.export.outfile,Buffer.from(e.data,"base64")),t()}))})));Promise.all(t).then((()=>{ke()})).catch((e=>{I(1,`[chart] Error encountered during batch export: ${e}`),ke()}))},server:ot,startServer:tt,killPool:ke,initPool:async(e={})=>{var t,o;return t=e.customCode&&e.customCode.allowCodeExecution,Ge=M(t),(o=e.logging&&parseInt(e.logging.level))>=0&&o<=_.levelsDesc.length&&(_.level=o),e.logging&&e.logging.dest&&((e,t)=>{if(_={..._,dest:e||_.dest,file:t||_.file,toFile:!0},0===_.dest.length)return I(1,"[logger] File logging init: no path supplied.");_.dest.endsWith("/")||(_.dest+="/")})(e.logging.dest,e.logging.file||"highcharts-export-server.log"),await Y(e.highcharts||{version:"latest"}),await xe({pool:e.pool||{minWorkers:1,maxWorkers:1},puppeteerArgs:e.puppeteer?.args||[]}),e}};export{rt as default}; -//# sourceMappingURL=index.esm.js.map diff --git a/dist/index.esm.js.map b/dist/index.esm.js.map deleted file mode 100644 index 4270a853..00000000 --- a/dist/index.esm.js.map +++ /dev/null @@ -1 +0,0 @@ -{"version":3,"file":"index.esm.js","sources":["../lib/schemas/config.js","../lib/logger.js","../lib/utils.js","../lib/server/rate_limit.js","../lib/fetch.js","../lib/cache.js","../lib/browser.js","../lib/export.js","../lib/benchmark.js","../templates/svg_export/svg_export.js","../lib/pool.js","../lib/server/routes/health.js","../lib/config.js","../lib/chart.js","../lib/server/routes/export.js","../lib/server/server.js","../lib/server/routes/ui.js","../lib/server/routes/change_hc_version.js","../lib/index.js"],"sourcesContent":["/*******************************************************************************\r\n\r\nHighcharts Export Server\r\n\r\nCopyright (c) 2016-2023, Highsoft\r\n\r\nLicenced under the MIT licence.\r\n\r\nAdditionally a valid Highcharts license is required for use.\r\n\r\nSee LICENSE file in root for details.\r\n\r\n*******************************************************************************/\r\n\r\n// Load .env into environment variables\r\nimport dotenv from 'dotenv';\r\n\r\ndotenv.config();\r\n\r\n// This is the configuration object with all options and their default values,\r\n// also from the .env file if one exists\r\nexport const defaultConfig = {\r\n puppeteer: {\r\n args: {\r\n value: [],\r\n type: 'string[]',\r\n description: 'Array of arguments to send to puppeteer.'\r\n }\r\n },\r\n highcharts: {\r\n version: {\r\n value: 'latest',\r\n envLink: 'HIGHCHARTS_VERSION',\r\n type: 'string',\r\n description: 'Highcharts version to use.'\r\n },\r\n cdnURL: {\r\n value: 'https://code.highcharts.com/',\r\n envLink: 'HIGHCHARTS_CDN',\r\n type: 'string',\r\n description: 'The CDN URL of Highcharts scripts to use.'\r\n },\r\n coreScripts: {\r\n envLink: 'HIGHCHARTS_CORE_SCRIPTS',\r\n value: ['highcharts', 'highcharts-more', 'highcharts-3d'],\r\n type: 'string[]',\r\n description: 'Highcharts core scripts to fetch.'\r\n },\r\n modules: {\r\n envLink: 'HIGHCHARTS_MODULES',\r\n value: [\r\n 'stock',\r\n 'map',\r\n 'gantt',\r\n 'exporting',\r\n 'export-data',\r\n 'parallel-coordinates',\r\n 'accessibility',\r\n 'annotations-advanced',\r\n 'boost-canvas',\r\n 'boost',\r\n 'data',\r\n 'draggable-points',\r\n 'static-scale',\r\n 'broken-axis',\r\n 'heatmap',\r\n 'tilemap',\r\n 'timeline',\r\n 'treemap',\r\n 'treegraph',\r\n 'item-series',\r\n 'drilldown',\r\n 'histogram-bellcurve',\r\n 'bullet',\r\n 'funnel',\r\n 'funnel3d',\r\n 'pyramid3d',\r\n 'networkgraph',\r\n 'pareto',\r\n 'pattern-fill',\r\n 'pictorial',\r\n 'price-indicator',\r\n 'sankey',\r\n 'arc-diagram',\r\n 'dependency-wheel',\r\n 'series-label',\r\n 'solid-gauge',\r\n 'sonification',\r\n 'stock-tools',\r\n 'streamgraph',\r\n 'sunburst',\r\n 'variable-pie',\r\n 'variwide',\r\n 'vector',\r\n 'venn',\r\n 'windbarb',\r\n 'wordcloud',\r\n 'xrange',\r\n 'no-data-to-display',\r\n 'drag-panes',\r\n 'debugger',\r\n 'dumbbell',\r\n 'lollipop',\r\n 'cylinder',\r\n 'organization',\r\n 'dotplot',\r\n 'marker-clusters',\r\n 'hollowcandlestick',\r\n 'heikinashi'\r\n ],\r\n type: 'string[]',\r\n description: 'Highcharts modules to fetch.'\r\n },\r\n indicators: {\r\n envLink: 'HIGHCHARTS_INDICATORS',\r\n value: ['indicators-all'],\r\n type: 'string[]',\r\n description: 'Highcharts indicators to fetch.'\r\n },\r\n scripts: {\r\n value: [\r\n 'https://cdnjs.cloudflare.com/ajax/libs/moment.js/2.29.4/moment.min.js',\r\n 'https://cdnjs.cloudflare.com/ajax/libs/moment-timezone/0.5.34/moment-timezone-with-data.min.js'\r\n ],\r\n type: 'string[]',\r\n description:\r\n 'Additional direct scripts/optional dependencies (e.g. moment.js).'\r\n },\r\n forceFetch: {\r\n envLink: 'HIGHCHARTS_FORCE_FETCH',\r\n value: false,\r\n type: 'boolean',\r\n description:\r\n 'Should all the scripts be refetched after rerunning the server.'\r\n }\r\n },\r\n export: {\r\n infile: {\r\n value: false,\r\n type: 'string',\r\n description:\r\n 'The input file name along with a type (json or svg). It can be a correct JSON or SVG file.'\r\n },\r\n instr: {\r\n value: false,\r\n type: 'string',\r\n description:\r\n 'An input in a form of a stringified JSON or SVG file. Overrides the --infile.'\r\n },\r\n options: {\r\n value: false,\r\n type: 'string',\r\n description: 'An alias for the --instr option.'\r\n },\r\n outfile: {\r\n value: false,\r\n type: 'string',\r\n description:\r\n 'The output filename along with a type (jpeg, png, pdf or svg). Ignores the --type flag.'\r\n },\r\n type: {\r\n envLink: 'EXPORT_DEFAULT_TYPE',\r\n value: 'png',\r\n type: 'string',\r\n description:\r\n 'The format of the file to export to. Can be jpeg, png, pdf or svg.'\r\n },\r\n constr: {\r\n envLink: 'EXPORT_DEFAULT_CONSTR',\r\n value: 'chart',\r\n type: 'string',\r\n description:\r\n 'The constructor to use. Can be chart, stockChart, mapChart or ganttChart.'\r\n },\r\n defaultHeight: {\r\n envLink: 'EXPORT_DEFAULT_HEIGHT',\r\n value: 400,\r\n type: 'number',\r\n description:\r\n 'The default height of the exported chart. Used when not found any value set.'\r\n },\r\n defaultWidth: {\r\n envLink: 'EXPORT_DEFAULT_WIDTH',\r\n value: 600,\r\n type: 'number',\r\n description:\r\n 'The default width of the exported chart. Used when not found any value set.'\r\n },\r\n defaultScale: {\r\n envLink: 'EXPORT_DEFAULT_SCALE',\r\n value: 1,\r\n type: 'number',\r\n description:\r\n 'The default scale of the exported chart. Ranges between 1 and 5.'\r\n },\r\n height: {\r\n type: 'number',\r\n value: false,\r\n description:\r\n 'The default height of the exported chart. Overrides the option in the chart settings.'\r\n },\r\n width: {\r\n type: 'number',\r\n value: false,\r\n description:\r\n 'The width of the exported chart. Overrides the option in the chart settings.'\r\n },\r\n scale: {\r\n value: false,\r\n type: 'number',\r\n description: 'The scale of the exported chart. Ranges between 1 and 5.'\r\n },\r\n globalOptions: {\r\n value: false,\r\n type: 'string',\r\n description:\r\n 'A stringified JSON or a filename with options to be passed into the Highcharts.setOptions.'\r\n },\r\n themeOptions: {\r\n value: false,\r\n type: 'string',\r\n description:\r\n 'A stringified JSON or a filename with theme options to be passed into the Highcharts.setOptions.'\r\n },\r\n batch: {\r\n value: false,\r\n type: 'string',\r\n description:\r\n 'Starts a batch job. A string that contains input/output pairs: \"in=out;in=out;..\".'\r\n }\r\n },\r\n customCode: {\r\n allowCodeExecution: {\r\n envLink: 'HIGHCHARTS_ALLOW_CODE_EXECUTION',\r\n value: false,\r\n type: 'boolean',\r\n description:\r\n 'If set to true, allow for the execution of arbitrary code when exporting.'\r\n },\r\n allowFileResources: {\r\n envLink: 'HIGHCHARTS_ALLOW_FILE_RESOURCES',\r\n value: true,\r\n type: 'boolean',\r\n description:\r\n 'Allow injecting resources from the filesystem. Has no effect when running as a server.'\r\n },\r\n customCode: {\r\n value: false,\r\n type: 'string',\r\n description:\r\n 'A function to be called before chart initialization. Can be a filename with the js extension.'\r\n },\r\n callback: {\r\n value: false,\r\n type: 'string',\r\n description: 'A JavaScript file with a function to run on construction.'\r\n },\r\n resources: {\r\n value: false,\r\n type: 'string',\r\n description:\r\n 'An additional resource in a form of stringified JSON. It can contain files, js and css sections.'\r\n },\r\n loadConfig: {\r\n value: false,\r\n type: 'string',\r\n description: 'A file that contains a pre-defined config to use.'\r\n },\r\n createConfig: {\r\n value: false,\r\n type: 'string',\r\n description:\r\n 'Allows to set options through a prompt and save in a provided config file.'\r\n }\r\n },\r\n server: {\r\n enable: {\r\n envLink: 'HIGHCHARTS_SERVER_ENABLE',\r\n value: false,\r\n type: 'boolean',\r\n cliName: 'enableServer',\r\n description: 'If set to true, starts a server on 0.0.0.0.'\r\n },\r\n host: {\r\n envLink: 'HIGHCHARTS_SERVER_HOST',\r\n value: '0.0.0.0',\r\n type: 'string',\r\n description:\r\n 'The hostname of the server. Also starts a server listening on the supplied hostname.'\r\n },\r\n port: {\r\n envLink: 'HIGHCHARTS_SERVER_PORT',\r\n value: 7801,\r\n type: 'number',\r\n description: 'The port to use for the server. Defaults to 7801.'\r\n },\r\n ssl: {\r\n enable: {\r\n envLink: 'HIGHCHARTS_SERVER_SSL_ENABLE',\r\n value: false,\r\n type: 'boolean',\r\n cliName: 'enableSsl',\r\n description: 'Enables the SSL protocol.'\r\n },\r\n force: {\r\n envLink: 'HIGHCHARTS_SERVER_SSL_FORCE',\r\n value: false,\r\n type: 'boolean',\r\n cliName: 'sslForced',\r\n description:\r\n 'If set to true, forces the server to only serve over HTTPS.'\r\n },\r\n port: {\r\n envLink: 'HIGHCHARTS_SERVER_SSL_PORT',\r\n value: 443,\r\n type: 'number',\r\n cliName: 'sslPort',\r\n description: 'The port on which to run the SSL server.'\r\n },\r\n certPath: {\r\n envLink: 'HIGHCHARTS_SSL_CERT_PATH',\r\n value: '',\r\n type: 'string',\r\n description: 'The path to the SSL certificate/key.'\r\n }\r\n },\r\n rateLimiting: {\r\n enable: {\r\n envLink: 'HIGHCHARTS_RATE_LIMIT_ENABLE',\r\n value: false,\r\n type: 'boolean',\r\n cliName: 'enableRateLimiting',\r\n description: 'Enables rate limiting.'\r\n },\r\n maxRequests: {\r\n envLink: 'HIGHCHARTS_RATE_LIMIT_MAX',\r\n value: 10,\r\n type: 'number',\r\n description: 'Max requests allowed in a one minute.'\r\n },\r\n window: {\r\n envLink: 'HIGHCHARTS_RATE_LIMIT_WINDOW',\r\n value: 1,\r\n type: 'number',\r\n description: 'The time window in minutes for rate limiting.'\r\n },\r\n delay: {\r\n envLink: 'HIGHCHARTS_RATE_LIMIT_DELAY',\r\n value: 0,\r\n type: 'number',\r\n description:\r\n 'The amount to delay each successive request before hitting the max.'\r\n },\r\n trustProxy: {\r\n envLink: 'HIGHCHARTS_RATE_LIMIT_TRUST_PROXY',\r\n value: false,\r\n type: 'boolean',\r\n description: 'Set this to true if behind a load balancer.'\r\n },\r\n skipKey: {\r\n envLink: 'HIGHCHARTS_RATE_LIMIT_SKIP_KEY',\r\n value: '',\r\n type: 'number|string',\r\n description:\r\n 'Allows bypassing the rate limiter and should be provided with skipToken argument.'\r\n },\r\n skipToken: {\r\n envLink: 'HIGHCHARTS_RATE_LIMIT_SKIP_TOKEN',\r\n value: '',\r\n type: 'number|string',\r\n description:\r\n 'Allows bypassing the rate limiter and should be provided with skipKey argument.'\r\n }\r\n }\r\n },\r\n pool: {\r\n minWorkers: {\r\n envLink: 'HIGHCHARTS_POOL_MIN_WORKERS',\r\n value: 4,\r\n type: 'number',\r\n description: 'The number of initial workers to spawn.'\r\n },\r\n maxWorkers: {\r\n envLink: 'HIGHCHARTS_POOL_MAX_WORKERS',\r\n value: 8,\r\n type: 'number',\r\n description: 'The number of max workers to spawn.'\r\n },\r\n workLimit: {\r\n envLink: 'HIGHCHARTS_POOL_WORK_LIMIT',\r\n value: 40,\r\n type: 'number',\r\n description:\r\n 'The pieces of work that can be performed before restarting process.'\r\n },\r\n acquireTimeout: {\r\n envLink: 'HIGHCHARTS_POOL_ACQUIRE_TIMEOUT',\r\n value: 5000,\r\n type: 'number',\r\n description:\r\n 'The number of milliseconds to wait for acquiring a resource.'\r\n },\r\n createTimeout: {\r\n envLink: 'HIGHCHARTS_POOL_CREATE_TIMEOUT',\r\n value: 5000,\r\n type: 'number',\r\n description: 'The number of milliseconds to wait for creating a resource.'\r\n },\r\n destroyTimeout: {\r\n envLink: 'HIGHCHARTS_POOL_DESTROY_TIMEOUT',\r\n value: 5000,\r\n type: 'number',\r\n description:\r\n 'The number of milliseconds to wait for destroying a resource.'\r\n },\r\n idleTimeout: {\r\n envLink: 'HIGHCHARTS_POOL_IDLE_TIMEOUT',\r\n value: 30000,\r\n type: 'number',\r\n description:\r\n 'The number of milliseconds after an idle resource is destroyed.'\r\n },\r\n rasterizationTimeout: {\r\n envLink: 'HIGHCHARTS_POOL_RASTERIZATION_TIMEOUT',\r\n value: 1500,\r\n type: 'number',\r\n description: 'The number of milliseconds to wait for rendering a webpage.'\r\n },\r\n createRetryInterval: {\r\n envLink: 'HIGHCHARTS_POOL_CREATE_RETRY_INTERVAL',\r\n value: 200,\r\n type: 'number',\r\n description:\r\n 'The number of milliseconds after the create process is retried in case of fail.'\r\n },\r\n reaperInterval: {\r\n envLink: 'HIGHCHARTS_POOL_REAPER_INTERVAL',\r\n value: 1000,\r\n type: 'number',\r\n description:\r\n 'The number of milliseconds after the check for idle resources to destroy is triggered.'\r\n },\r\n benchmarking: {\r\n envLink: 'HIGHCHARTS_POOL_BENCHMARKING',\r\n value: false,\r\n type: 'boolean',\r\n description: 'Enable benchmarking.'\r\n },\r\n listenToProcessExits: {\r\n envLink: 'HIGHCHARTS_POOL_LISTEN_TO_PROCESS_EXITS',\r\n value: true,\r\n type: 'boolean',\r\n description:\r\n 'Set to false in order to skip attaching process.exit handlers.'\r\n }\r\n },\r\n logging: {\r\n level: {\r\n envLink: 'HIGHCHARTS_LOG_LEVEL',\r\n value: 4,\r\n type: 'number',\r\n cliName: 'logLevel',\r\n description:\r\n 'The log level (0: silent, 1: error, 2: warning, 3: notice, 4: verbose).'\r\n },\r\n file: {\r\n envLink: 'HIGHCHARTS_LOG_FILE',\r\n value: 'highcharts-export-server.log',\r\n type: 'string',\r\n cliName: 'logFile',\r\n description:\r\n 'A name of a log file. The --logDest also needs to be set to enable file logging.'\r\n },\r\n dest: {\r\n envLink: 'HIGHCHARTS_LOG_DEST',\r\n value: 'log/',\r\n type: 'string',\r\n cliName: 'logDest',\r\n description: 'The path to store log files. Also enables file logging.'\r\n }\r\n },\r\n ui: {\r\n enable: {\r\n envLink: 'HIGHCHARTS_UI_ENABLE',\r\n value: false,\r\n type: 'boolean',\r\n cliName: 'enableUi',\r\n description: 'Enables the UI for the export server.'\r\n },\r\n route: {\r\n envLink: 'HIGHCHARTS_UI_ROUTE',\r\n value: '/',\r\n type: 'string',\r\n cliName: 'uiRoute',\r\n description: 'The route to attach the UI to.'\r\n }\r\n },\r\n other: {\r\n noLogo: {\r\n envLink: 'HIGHCHARTS_NO_LOGO',\r\n value: false,\r\n type: 'boolean',\r\n description:\r\n 'Skip printing the logo on a startup. Will be replaced by a simple text.'\r\n }\r\n },\r\n payload: {}\r\n};\r\n\r\n// The config descriptions object for the prompts functionality. It contains\r\n// information like:\r\n// * Type of a prompt\r\n// * Name of an option\r\n// * Short description of a chosen option\r\n// * Initial value\r\nexport const promptsConfig = {\r\n puppeteer: [\r\n {\r\n type: 'list',\r\n name: 'args',\r\n message: 'Puppeteer arguments',\r\n initial: defaultConfig.puppeteer.args.value.join(','),\r\n separator: ','\r\n }\r\n ],\r\n highcharts: [\r\n {\r\n type: 'text',\r\n name: 'version',\r\n message: 'Highcharts version',\r\n initial: defaultConfig.highcharts.version.value\r\n },\r\n {\r\n type: 'text',\r\n name: 'cdnURL',\r\n message: 'The url of CDN',\r\n initial: defaultConfig.highcharts.cdnURL.value\r\n },\r\n {\r\n type: 'multiselect',\r\n name: 'modules',\r\n message: 'Available modules',\r\n instructions: 'Space: Select specific, A: Select all, Enter: Confirm.',\r\n choices: defaultConfig.highcharts.modules.value\r\n },\r\n {\r\n type: 'list',\r\n name: 'scripts',\r\n message: 'Custom scripts',\r\n initial: defaultConfig.highcharts.scripts.value.join(','),\r\n separator: ','\r\n },\r\n {\r\n type: 'toggle',\r\n name: 'forceFetch',\r\n message: 'Should refetch all the scripts after each server rerun',\r\n initial: defaultConfig.highcharts.forceFetch.value\r\n }\r\n ],\r\n export: [\r\n {\r\n type: 'select',\r\n name: 'type',\r\n message: 'The default type of a file to export to',\r\n hint: `Default: ${defaultConfig.export.type.value}`,\r\n initial: 0,\r\n choices: ['png', 'jpeg', 'pdf', 'svg']\r\n },\r\n {\r\n type: 'select',\r\n name: 'constr',\r\n message: 'The default constructor for Highcharts to use',\r\n hint: `Default: ${defaultConfig.export.constr.value}`,\r\n initial: 0,\r\n choices: ['chart', 'stockChart', 'mapChart', 'ganttChart']\r\n },\r\n {\r\n type: 'number',\r\n name: 'defaultHeight',\r\n message: 'The default fallback height of the exported chart',\r\n initial: defaultConfig.export.defaultHeight.value\r\n },\r\n {\r\n type: 'number',\r\n name: 'defaultWidth',\r\n message: 'The default fallback width of the exported chart',\r\n initial: defaultConfig.export.defaultWidth.value\r\n },\r\n {\r\n type: 'number',\r\n name: 'defaultScale',\r\n message: 'The default fallback scale of the exported chart',\r\n initial: defaultConfig.export.defaultScale.value,\r\n min: 0.1,\r\n max: 5\r\n }\r\n ],\r\n customCode: [\r\n {\r\n type: 'toggle',\r\n name: 'allowCodeExecution',\r\n message: 'Allow to execute custom code',\r\n initial: defaultConfig.customCode.allowCodeExecution.value\r\n },\r\n {\r\n type: 'toggle',\r\n name: 'allowFileResources',\r\n message: 'Allow file resources',\r\n initial: defaultConfig.customCode.allowFileResources.value\r\n }\r\n ],\r\n server: [\r\n {\r\n type: 'toggle',\r\n name: 'enable',\r\n message: 'Starts a server on 0.0.0.0',\r\n initial: defaultConfig.server.enable.value\r\n },\r\n {\r\n type: 'text',\r\n name: 'host',\r\n message: 'A hostname of a server',\r\n initial: defaultConfig.server.host.value\r\n },\r\n {\r\n type: 'number',\r\n name: 'port',\r\n message: 'A port of a server',\r\n initial: defaultConfig.server.port.value\r\n },\r\n {\r\n type: 'toggle',\r\n name: 'ssl.enable',\r\n message: 'Enable SSL protocol',\r\n initial: defaultConfig.server.ssl.enable.value\r\n },\r\n {\r\n type: 'toggle',\r\n name: 'ssl.force',\r\n message: 'Force to only serve over HTTPS',\r\n initial: defaultConfig.server.ssl.force.value\r\n },\r\n {\r\n type: 'number',\r\n name: 'ssl.port',\r\n message: 'Port on which to run the SSL server',\r\n initial: defaultConfig.server.ssl.port.value\r\n },\r\n {\r\n type: 'text',\r\n name: 'ssl.certPath',\r\n message: 'A path where to find the SSL certificate/key',\r\n initial: defaultConfig.server.ssl.certPath.value\r\n },\r\n {\r\n type: 'toggle',\r\n name: 'rateLimiting.enable',\r\n message: 'Enable rate limiting',\r\n initial: defaultConfig.server.rateLimiting.enable.value\r\n },\r\n {\r\n type: 'number',\r\n name: 'rateLimiting.maxRequests',\r\n message: 'Max requests allowed in a one minute',\r\n initial: defaultConfig.server.rateLimiting.maxRequests.value\r\n },\r\n {\r\n type: 'number',\r\n name: 'rateLimiting.window',\r\n message: 'The time window in minutes for rate limiting',\r\n initial: defaultConfig.server.rateLimiting.window.value\r\n },\r\n {\r\n type: 'number',\r\n name: 'rateLimiting.delay',\r\n message:\r\n 'The amount to delay each successive request before hitting the max',\r\n initial: defaultConfig.server.rateLimiting.delay.value\r\n },\r\n {\r\n type: 'toggle',\r\n name: 'rateLimiting.trustProxy',\r\n message: 'Set this to true if behind a load balancer',\r\n initial: defaultConfig.server.rateLimiting.trustProxy.value\r\n },\r\n {\r\n type: 'text',\r\n name: 'rateLimiting.skipKey',\r\n message:\r\n 'Allows bypassing the rate limiter and should be provided with skipToken argument',\r\n initial: defaultConfig.server.rateLimiting.skipKey.value\r\n },\r\n {\r\n type: 'text',\r\n name: 'rateLimiting.skipToken',\r\n message:\r\n 'Allows bypassing the rate limiter and should be provided with skipKey argument',\r\n initial: defaultConfig.server.rateLimiting.skipToken.value\r\n }\r\n ],\r\n pool: [\r\n {\r\n type: 'number',\r\n name: 'minWorkers',\r\n message: 'The number of initial workers to spawn',\r\n initial: defaultConfig.pool.minWorkers.value\r\n },\r\n {\r\n type: 'number',\r\n name: 'maxWorkers',\r\n message: 'The number of max workers to spawn',\r\n initial: defaultConfig.pool.maxWorkers.value\r\n },\r\n {\r\n type: 'number',\r\n name: 'workLimit',\r\n message:\r\n 'The pieces of work that can be performed before restarting a puppeteer process',\r\n initial: defaultConfig.pool.workLimit.value\r\n },\r\n {\r\n type: 'number',\r\n name: 'acquireTimeout',\r\n message: 'The number of milliseconds to wait for acquiring a resource',\r\n initial: defaultConfig.pool.acquireTimeout.value\r\n },\r\n {\r\n type: 'number',\r\n name: 'createTimeout',\r\n message: 'The number of milliseconds to wait for creating a resource',\r\n initial: defaultConfig.pool.createTimeout.value\r\n },\r\n {\r\n type: 'number',\r\n name: 'destroyTimeout',\r\n message: 'The number of milliseconds to wait for destroying a resource',\r\n initial: defaultConfig.pool.destroyTimeout.value\r\n },\r\n {\r\n type: 'number',\r\n name: 'idleTimeout',\r\n message: 'The number of milliseconds after an idle resource is destroyed',\r\n initial: defaultConfig.pool.idleTimeout.value\r\n },\r\n {\r\n type: 'number',\r\n name: 'rasterizationTimeout',\r\n message: 'The number of milliseconds to wait for rendering a webpage',\r\n initial: defaultConfig.pool.rasterizationTimeout.value\r\n },\r\n {\r\n type: 'number',\r\n name: 'createRetryInterval',\r\n message:\r\n 'The number of milliseconds after the create process is retried in case of fail',\r\n initial: defaultConfig.pool.createRetryInterval.value\r\n },\r\n {\r\n type: 'number',\r\n name: 'reaperInterval',\r\n message:\r\n 'The number of milliseconds after the check for idle resources to destroy is triggered',\r\n initial: defaultConfig.pool.reaperInterval.value\r\n },\r\n {\r\n type: 'toggle',\r\n name: 'benchmarking',\r\n message: 'Set benchmarking',\r\n initial: defaultConfig.pool.benchmarking.value\r\n },\r\n {\r\n type: 'toggle',\r\n name: 'listenToProcessExits',\r\n message: 'Set to false in order to skip attaching process.exit handlers',\r\n initial: defaultConfig.pool.listenToProcessExits.value\r\n }\r\n ],\r\n logging: [\r\n {\r\n type: 'number',\r\n name: 'level',\r\n message:\r\n 'The log level (0: silent, 1: error, 2: warning, 3: notice, 4: verbose)',\r\n initial: defaultConfig.logging.level.value,\r\n round: 0,\r\n min: 0,\r\n max: 4\r\n },\r\n {\r\n type: 'text',\r\n name: 'file',\r\n message:\r\n 'A name of a log file. The --logDest also needs to be set to enable file logging',\r\n initial: defaultConfig.logging.file.value\r\n },\r\n {\r\n type: 'text',\r\n name: 'dest',\r\n message: 'A path to log files. It enables file logging',\r\n initial: defaultConfig.logging.dest.value\r\n }\r\n ],\r\n ui: [\r\n {\r\n type: 'toggle',\r\n name: 'enable',\r\n message: 'Enable UI for the export server',\r\n initial: defaultConfig.ui.enable.value\r\n },\r\n {\r\n type: 'text',\r\n name: 'route',\r\n message: 'A route to attach the UI to',\r\n initial: defaultConfig.ui.route.value\r\n }\r\n ],\r\n other: [\r\n {\r\n type: 'toggle',\r\n name: 'noLogo',\r\n message:\r\n 'Skip printing the logo on a startup. Will be replaced by a simple text',\r\n initial: defaultConfig.other.noLogo.value\r\n }\r\n ]\r\n};\r\n\r\n// Absolute props that, in case of merging recursively, need to be force merged\r\nexport const absoluteProps = [\r\n 'options',\r\n 'globalOptions',\r\n 'themeOptions',\r\n 'resources',\r\n 'payload'\r\n];\r\n\r\n// Argument nesting level of all export server options\r\nexport const nestedArgs = {};\r\n\r\n/**\r\n * Creates nested arguments chain for all options\r\n *\r\n * @param {object} obj - The object based on which the initial configuration be\r\n * made.\r\n * @param {string } propChain - Required for creating a string chain of\r\n * properties for nested arguments.\r\n */\r\nconst createNestedArgs = (obj, propChain = '') => {\r\n Object.keys(obj).forEach((k) => {\r\n if (!['puppeteer', 'highcharts'].includes(k)) {\r\n const entry = obj[k];\r\n if (typeof entry.value === 'undefined') {\r\n // Go deeper in the nested arguments\r\n createNestedArgs(entry, `${propChain}.${k}`);\r\n } else {\r\n // Create the chain of nested arguments\r\n nestedArgs[entry.cliName || k] = `${propChain}.${k}`.substring(1);\r\n }\r\n }\r\n });\r\n};\r\n\r\ncreateNestedArgs(defaultConfig);\r\n","/*******************************************************************************\r\n\r\nHighcharts Export Server\r\n\r\nCopyright (c) 2016-2023, Highsoft\r\n\r\nLicenced under the MIT licence.\r\n\r\nAdditionally a valid Highcharts license is required for use.\r\n\r\nSee LICENSE file in root for details.\r\n\r\n*******************************************************************************/\r\n\r\nimport { appendFile, existsSync, mkdirSync } from 'fs';\r\n\r\nimport { defaultConfig } from './schemas/config.js';\r\n\r\n// The default logging config\r\nlet logging = {\r\n // Flags for logging status\r\n toConsole: true,\r\n toFile: false,\r\n pathCreated: false,\r\n // Log levels\r\n levelsDesc: [\r\n {\r\n title: 'error',\r\n color: 'red'\r\n },\r\n {\r\n title: 'warning',\r\n color: 'yellow'\r\n },\r\n {\r\n title: 'notice',\r\n color: 'blue'\r\n },\r\n {\r\n title: 'verbose',\r\n color: 'gray'\r\n }\r\n ],\r\n // Log listeners\r\n listeners: []\r\n};\r\n\r\n// Gather init logging options\r\nfor (const [key, option] of Object.entries(defaultConfig.logging)) {\r\n logging[key] = option.value;\r\n}\r\n\r\n/**\r\n * Logs a message. Accepts a variable amount of arguments. Arguments after\r\n * `level` will be passed directly to console.log, and/or will be joined\r\n * and appended to the log file.\r\n *\r\n * @param {any} args - An array of arguments where the first is the log level\r\n * and the rest are strings to build a message with.\r\n */\r\nexport const log = (...args) => {\r\n const [newLevel, ...texts] = args;\r\n\r\n // Current logging options\r\n const { level, levelsDesc } = logging;\r\n\r\n // Check if log level is within a correct range\r\n if (newLevel === 0 || newLevel > level || level > levelsDesc.length) {\r\n return;\r\n }\r\n\r\n // Get rid of the GMT text information\r\n const newDate = new Date().toString().split('(')[0].trim();\r\n\r\n // Create a message's prefix\r\n const prefix = `${newDate} [${levelsDesc[newLevel - 1].title}] -`;\r\n\r\n // Call available log listeners\r\n logging.listeners.forEach((fn) => {\r\n fn(prefix, texts.join(' '));\r\n });\r\n\r\n // Log to file\r\n if (logging.toFile) {\r\n if (!logging.pathCreated) {\r\n // Create if does not exist\r\n !existsSync(logging.dest) && mkdirSync(logging.dest);\r\n\r\n // We now assume the path is available, e.g. it's the responsibility\r\n // of the user to create the path with the correct access rights.\r\n logging.pathCreated = true;\r\n }\r\n\r\n // Add the content to a file\r\n appendFile(\r\n `${logging.dest}${logging.file}`,\r\n [prefix].concat(texts).join(' ') + '\\n',\r\n (error) => {\r\n if (error) {\r\n console.log(`[logger] Unable to write to log file: ${error}`);\r\n logging.toFile = false;\r\n }\r\n }\r\n );\r\n }\r\n\r\n // Log to console\r\n if (logging.toConsole) {\r\n console.log.apply(\r\n undefined,\r\n [prefix.toString()[logging.levelsDesc[newLevel - 1].color]].concat(texts)\r\n );\r\n }\r\n};\r\n\r\n/**\r\n * Sets the file logging configuration.\r\n *\r\n * @param {string} logDest - A path to log to.\r\n * @param {string} logFile - The name of the log file.\r\n */\r\nexport const enableFileLogging = (logDest, logFile) => {\r\n // Update logging options\r\n logging = {\r\n ...logging,\r\n dest: logDest || logging.dest,\r\n file: logFile || logging.file,\r\n toFile: true\r\n };\r\n\r\n if (logging.dest.length === 0) {\r\n return log(1, '[logger] File logging init: no path supplied.');\r\n }\r\n\r\n if (!logging.dest.endsWith('/')) {\r\n logging.dest += '/';\r\n }\r\n};\r\n\r\n/**\r\n * Adds a log listener.\r\n *\r\n * @param {function} fn - The function to call when getting a log event.\r\n */\r\nexport const listen = (fn) => {\r\n logging.listeners.push(fn);\r\n};\r\n\r\n/**\r\n * Sets the current log level. Log levels are:\r\n * - 0 = no logging\r\n * - 1 = error\r\n * - 2 = warning\r\n * - 3 = notice\r\n * - 4 = verbose\r\n *\r\n * @param {number} newLevel - The new log level (0 - 4).\r\n */\r\nexport const setLogLevel = (newLevel) => {\r\n if (newLevel >= 0 && newLevel <= logging.levelsDesc.length) {\r\n logging.level = newLevel;\r\n }\r\n};\r\n\r\n/**\r\n * Enables or disables logging to the stdout.\r\n *\r\n * @param {boolean} enabled - Whether log to console or not.\r\n */\r\nexport const toggleSTDOut = (enabled) => {\r\n logging.toConsole = enabled;\r\n};\r\n\r\nexport default {\r\n log,\r\n enableFileLogging,\r\n listen,\r\n setLogLevel,\r\n toggleSTDOut\r\n};\r\n","/*******************************************************************************\r\n\r\nHighcharts Export Server\r\n\r\nCopyright (c) 2016-2023, Highsoft\r\n\r\nLicenced under the MIT licence.\r\n\r\nAdditionally a valid Highcharts license is required for use.\r\n\r\nSee LICENSE file in root for details.\r\n\r\n*******************************************************************************/\r\n\r\nimport { readFileSync } from 'fs';\r\nimport { fileURLToPath } from 'url';\r\n\r\nimport { defaultConfig } from '../lib/schemas/config.js';\r\nimport { log } from './logger.js';\r\n\r\nconst MAX_BACKOFF_ATTEMPTS = 6;\r\n\r\nexport const __dirname = fileURLToPath(new URL('../.', import.meta.url));\r\n\r\n/**\r\n * Clears text from whitespaces with a regex rule.\r\n *\r\n * @param {string} rule - The rule for clearing a string, default to /\\s\\s+/g.\r\n * @return {string} - Cleared text.\r\n */\r\nexport const clearText = (text, rule = /\\s\\s+/g, replacer = ' ') =>\r\n text.replaceAll(rule, replacer).trim();\r\n\r\n/**\r\n * Delays calling the function by time calculated based on the backoff\r\n * algorithm.\r\n *\r\n * @param {function} fn - A function to try to call with the backoff algorithm\r\n * on.\r\n * @param {number} attempt - The number of an attempt, where the first one is 0.\r\n */\r\nexport const expBackoff = async (fn, attempt = 0, ...args) => {\r\n try {\r\n // Try to call the function\r\n return await fn(...args);\r\n } catch (error) {\r\n // Calculate delay in ms\r\n const delayInMs = 2 ** attempt * 1000;\r\n\r\n // If the attempt exceeds the maximum attempts of reapeat, throw an error\r\n if (++attempt >= MAX_BACKOFF_ATTEMPTS) {\r\n throw error;\r\n }\r\n\r\n // Wait given amount of time\r\n await new Promise((response) => setTimeout(response, delayInMs));\r\n log(\r\n 3,\r\n `[pool] Waited ${delayInMs}ms until next call for the resource id: ${args[0]}.`\r\n );\r\n\r\n // Try again\r\n return expBackoff(fn, attempt, ...args);\r\n }\r\n};\r\n\r\n/**\r\n * Fixes to supported type format if MIME.\r\n *\r\n * @param {string} type - Type to be corrected.\r\n * @param {string} outfile - Name of the outfile.\r\n */\r\nexport const fixType = (type, outfile) => {\r\n // MIME types\r\n const mimeTypes = {\r\n 'image/png': 'png',\r\n 'image/jpeg': 'jpeg',\r\n 'application/pdf': 'pdf',\r\n 'image/svg+xml': 'svg'\r\n };\r\n\r\n // Formats\r\n const formats = ['png', 'jpeg', 'pdf', 'svg'];\r\n\r\n // Check if type and outfile's extensions are the same\r\n if (outfile) {\r\n const outType = outfile.split('.').pop();\r\n\r\n // Check if extension has a correct type\r\n if (formats.includes(outType) && type !== outType) {\r\n type = outType;\r\n }\r\n }\r\n\r\n // Return a correct type\r\n return mimeTypes[type] || formats.find((t) => t === type) || 'png';\r\n};\r\n\r\n/**\r\n * Handles the provided resources.\r\n *\r\n * @param {string} resources - The stringified resources.\r\n * @param {string} allowFileResources - Decide if resources from file are\r\n * allowed.\r\n */\r\nexport const handleResources = (resources = false, allowFileResources) => {\r\n const allowedProps = ['js', 'css', 'files'];\r\n\r\n let handledResources = resources;\r\n let correctResources = false;\r\n\r\n // Try to load resources from a file\r\n if (allowFileResources && resources.endsWith('.json')) {\r\n try {\r\n if (!resources) {\r\n handledResources = isCorrectJSON(\r\n readFileSync('resources.json', 'utf8')\r\n );\r\n } else if (resources && resources.endsWith('.json')) {\r\n handledResources = isCorrectJSON(readFileSync(resources, 'utf8'));\r\n } else {\r\n handledResources = isCorrectJSON(resources);\r\n if (handledResources === true) {\r\n handledResources = isCorrectJSON(\r\n readFileSync('resources.json', 'utf8')\r\n );\r\n }\r\n }\r\n } catch (notice) {\r\n return log(3, `[cli] No resources found.`);\r\n }\r\n } else {\r\n // Try to get JSON\r\n handledResources = isCorrectJSON(resources);\r\n\r\n // Get rid of the files section\r\n if (!allowFileResources) {\r\n delete handledResources.files;\r\n }\r\n }\r\n\r\n // Filter from unnecessary properties\r\n for (const propName in handledResources) {\r\n if (!allowedProps.includes(propName)) {\r\n delete handledResources[propName];\r\n } else if (!correctResources) {\r\n correctResources = true;\r\n }\r\n }\r\n\r\n // Check if at least one of allowed properties is present\r\n if (!correctResources) {\r\n return log(3, `[cli] No resources found.`);\r\n }\r\n\r\n // Handle files section\r\n if (handledResources.files) {\r\n handledResources.files = handledResources.files.map((item) => item.trim());\r\n if (!handledResources.files || handledResources.files.length <= 0) {\r\n delete handledResources.files;\r\n }\r\n }\r\n\r\n // Return resources\r\n return handledResources;\r\n};\r\n\r\n/**\r\n * Checks if provided data is or can be a correct JSON.\r\n *\r\n * @param {any} data - Data to be checked.\r\n * @param {boolean} toString - If true, return stringified representation.\r\n */\r\nexport function isCorrectJSON(data, toString) {\r\n try {\r\n // Get the string representation if not already before parsing\r\n const parsedData = JSON.parse(\r\n typeof data !== 'string' ? JSON.stringify(data) : data\r\n );\r\n\r\n // Return a stringified representation of a JSON if required\r\n if (typeof parsedData !== 'string' && toString) {\r\n return JSON.stringify(parsedData);\r\n }\r\n\r\n // Return a JSON\r\n return parsedData;\r\n } catch (error) {\r\n return false;\r\n }\r\n}\r\n\r\n/**\r\n * Checks if item is an object.\r\n *\r\n * @param {any} item - Item to be checked.\r\n */\r\nexport const isObject = (item) =>\r\n typeof item === 'object' && !Array.isArray(item) && item !== null;\r\n\r\n/**\r\n * Checks if string contains private range urls.\r\n *\r\n * @export utils\r\n * @param item {string} item to be checked\r\n */\r\nexport const isPrivateRangeUrlFound = (item) => {\r\n return [\r\n 'localhost',\r\n '(10).(.*).(.*).(.*)',\r\n '(127).(.*).(.*).(.*)',\r\n '(172).(1[6-9]|2[0-9]|3[0-1]).(.*).(.*)',\r\n '(192).(168).(.*).(.*)'\r\n ].some((ipRegEx) =>\r\n item.match(`xlink:href=\"(?:(http://|https://))?${ipRegEx}`)\r\n );\r\n};\r\n\r\n/**\r\n * Creates and returns a deep copy of the given object.\r\n *\r\n * @param {object} object - Object to copy.\r\n * @return {object} - Deep copy of the object.\r\n */\r\nexport const deepCopy = (obj) => {\r\n if (obj === null || typeof obj !== 'object') {\r\n return obj;\r\n }\r\n\r\n const copy = Array.isArray(obj) ? [] : {};\r\n\r\n for (const key in obj) {\r\n if (Object.prototype.hasOwnProperty.call(obj, key)) {\r\n copy[key] = deepCopy(obj[key]);\r\n }\r\n }\r\n\r\n return copy;\r\n};\r\n\r\n/**\r\n * Stringifies object with options. Possible to preserve functions with\r\n * allowFunctions flag.\r\n *\r\n * @param {object} options - Options to stringify.\r\n * @param {boolean} allowFunctions - Flag for keeping functions.\r\n */\r\nexport const optionsStringify = (options, allowFunctions) => {\r\n const replacerCallback = (name, value) => {\r\n if (typeof value === 'string') {\r\n value = value.trim();\r\n\r\n // If allowFunctions is set to true, preserve functions\r\n if (\r\n (value.startsWith('function(') || value.startsWith('function (')) &&\r\n value.endsWith('}')\r\n ) {\r\n value = allowFunctions\r\n ? `EXP_FUN${(value + '').replaceAll(/\\n|\\t|\\r/g, ' ')}EXP_FUN`\r\n : undefined;\r\n }\r\n }\r\n\r\n return typeof value === 'function'\r\n ? `EXP_FUN${(value + '').replaceAll(/\\n|\\t|\\r/g, ' ')}EXP_FUN`\r\n : value;\r\n };\r\n\r\n // Stringify options and if required, replace special functions marks\r\n return JSON.stringify(options, replacerCallback).replaceAll(\r\n /\"EXP_FUN|EXP_FUN\"/g,\r\n ''\r\n );\r\n};\r\n\r\n/**\r\n * Prints the export server logo.\r\n *\r\n * @param {boolean} noLogo - Whether to display logo or text.\r\n */\r\nexport const printLogo = (noLogo) => {\r\n // Get package version either from env or from package.json\r\n const packageVersion =\r\n process.env.npm_package_version ||\r\n JSON.parse(readFileSync(new URL('../package.json', import.meta.url)))\r\n .version;\r\n\r\n // Print text only\r\n if (noLogo) {\r\n console.log(`Starting highcharts export server v${packageVersion}...`);\r\n return;\r\n }\r\n\r\n // Print the logo\r\n console.log(\r\n readFileSync(__dirname + '/msg/startup.msg').toString().bold.yellow,\r\n `v${packageVersion}`\r\n );\r\n};\r\n\r\n/**\r\n * Prints the CLI usage. If required, it can list properties recursively\r\n */\r\nexport function printUsage() {\r\n const pad = 48;\r\n const readme = 'https://github.com/highcharts/node-export-server#readme';\r\n\r\n // Display readme information\r\n console.log(\r\n 'Usage of CLI arguments:'.bold,\r\n '\\n------',\r\n `\\nFor more detailed information visit readme at: ${readme.bold.yellow}.`\r\n );\r\n\r\n const cycleCategories = (categories) => {\r\n for (const [name, option] of Object.entries(categories)) {\r\n // If category has more levels, go further\r\n if (!Object.prototype.hasOwnProperty.call(option, 'value')) {\r\n cycleCategories(option);\r\n } else {\r\n let descName = ` --${option.cliName || name} ${\r\n ('<' + option.type + '>').green\r\n } `;\r\n if (descName.length < pad) {\r\n for (let i = descName.length; i < pad; i++) {\r\n descName += '.';\r\n }\r\n }\r\n\r\n // Display correctly aligned messages\r\n console.log(\r\n descName,\r\n option.description,\r\n `[Default: ${option.value.toString().bold}]`.blue\r\n );\r\n }\r\n }\r\n };\r\n\r\n // Cycle through options of each categories and display the usage info\r\n Object.keys(defaultConfig).forEach((category) => {\r\n // Only puppeteer and highcharts categories cannot be configured through CLI\r\n if (!['puppeteer', 'highcharts'].includes(category)) {\r\n console.log(`\\n${category.toUpperCase()}`.red);\r\n cycleCategories(defaultConfig[category]);\r\n }\r\n });\r\n console.log('\\n');\r\n}\r\n\r\n/**\r\n * Rounds number to passed precision.\r\n *\r\n * @param {number} value - Number to round.\r\n * @param {number} precision - A precision of rounding.\r\n */\r\nexport const roundNumber = (value, precision = 1) => {\r\n const multiplier = Math.pow(10, precision || 0);\r\n return Math.round(+value * multiplier) / multiplier;\r\n};\r\n\r\n/**\r\n * Casts the item to boolean.\r\n *\r\n * @param {any} item - Item to be cast.\r\n */\r\nexport const toBoolean = (item) =>\r\n ['false', 'undefined', 'null', 'NaN', '0', ''].includes(item)\r\n ? false\r\n : !!item;\r\n\r\n/**\r\n * If necessary, places a custom code inside a function.\r\n *\r\n * @param {any} customCode - The customCode.\r\n */\r\nexport const wrapAround = (customCode, allowFileResources) => {\r\n if (customCode && typeof customCode === 'string') {\r\n customCode = customCode.trim();\r\n\r\n if (customCode.endsWith('.js')) {\r\n return allowFileResources\r\n ? wrapAround(readFileSync(customCode, 'utf8'))\r\n : false;\r\n } else if (\r\n customCode.startsWith('function()') ||\r\n customCode.startsWith('function ()') ||\r\n customCode.startsWith('()=>') ||\r\n customCode.startsWith('() =>')\r\n ) {\r\n return `(${customCode})()`;\r\n }\r\n return customCode.replace(/;$/, '');\r\n }\r\n};\r\n\r\n/**\r\n * Utility to measure time.\r\n */\r\nexport const measureTime = () => {\r\n const start = process.hrtime.bigint();\r\n return () => Number(process.hrtime.bigint() - start) / 1000000;\r\n};\r\n\r\nexport default {\r\n __dirname,\r\n clearText,\r\n expBackoff,\r\n fixType,\r\n handleResources,\r\n isCorrectJSON,\r\n isObject,\r\n isPrivateRangeUrlFound,\r\n optionsStringify,\r\n printLogo,\r\n printUsage,\r\n roundNumber,\r\n toBoolean,\r\n wrapAround,\r\n measureTime\r\n};\r\n","/*******************************************************************************\r\n\r\nHighcharts Export Server\r\n\r\nCopyright (c) 2016-2023, Highsoft\r\n\r\nLicenced under the MIT licence.\r\n\r\nAdditionally a valid Highcharts license is required for use.\r\n\r\nSee LICENSE file in root for details.\r\n\r\n*******************************************************************************/\r\n\r\nimport rateLimit from 'express-rate-limit';\r\n\r\nimport { clearText } from '../utils.js';\r\nimport { log } from '../logger.js';\r\n\r\n/**\r\n * Enables rate limiting for a given app.\r\n *\r\n * @param {object} app - The express app.\r\n * @param {object} limitConfig - The options for the rate limiting.\r\n */\r\nexport default (app, limitConfig) => {\r\n const msg =\r\n 'Too many requests, you have been rate limited. Please try again later.';\r\n\r\n // Options for the rate limiter\r\n const rateOptions = {\r\n max: limitConfig.maxRequests || 30,\r\n window: limitConfig.window || 1,\r\n delay: limitConfig.delay || 0,\r\n trustProxy: limitConfig.trustProxy || false,\r\n skipKey: limitConfig.skipKey || false,\r\n skipToken: limitConfig.skipToken || false\r\n };\r\n\r\n // Set if behind a proxy\r\n if (rateOptions.trustProxy) {\r\n app.enable('trust proxy');\r\n }\r\n\r\n // Create a limiter\r\n const limiter = rateLimit({\r\n windowMs: rateOptions.window * 60 * 1000,\r\n // Limit each IP to 100 requests per windowMs\r\n max: rateOptions.max,\r\n // Disable delaying, full speed until the max limit is reached\r\n delayMs: rateOptions.delay,\r\n handler: (request, response) => {\r\n response.format({\r\n json: () => {\r\n response.status(429).send({ message: msg });\r\n },\r\n default: () => {\r\n response.status(429).send(msg);\r\n }\r\n });\r\n },\r\n skip: (request) => {\r\n // Allow bypassing the limiter if a valid key/token has been sent\r\n if (\r\n rateOptions.skipKey !== false &&\r\n rateOptions.skipToken !== false &&\r\n request.query.key === rateOptions.skipKey &&\r\n request.query.access_token === rateOptions.skipToken\r\n ) {\r\n log(4, '[rate-limiting] Skipping rate limiter.');\r\n return true;\r\n }\r\n return false;\r\n }\r\n });\r\n\r\n // Use a limiter as a middleware\r\n app.use(limiter);\r\n\r\n log(\r\n 3,\r\n clearText(\r\n `[rate-limiting] Enabled rate limiting: ${rateOptions.max} requests\r\n per ${rateOptions.window} minute per IP, trusting proxy:\r\n ${rateOptions.trustProxy}.`\r\n )\r\n );\r\n};\r\n","/**\r\n * This module exports two functions: fetch (for GET requests) and post (for POST requests).\r\n */\r\n\r\nimport http from 'http';\r\nimport https from 'https';\r\n\r\n/**\r\n * Determines the protocol of the given URL (either `http` or `https`).\r\n *\r\n * @function\r\n * @param {string} url - The URL whose protocol needs to be determined.\r\n * @returns {Object} Returns the `https` module if the URL starts with 'https',\r\n * otherwise returns the `http` module.\r\n * @private\r\n *\r\n * @example\r\n *\r\n * const protocol = getProtocol('https://example.com');\r\n * console.log(protocol); // Outputs the 'https' module\r\n */\r\nconst getProtocol = (url) => {\r\n return url.startsWith('https') ? https : http;\r\n};\r\n\r\n/**\r\n * Sends a GET request to the specified URL with optional request options.\r\n *\r\n * @function\r\n * @async\r\n * @param {string} url - The URL to fetch.\r\n * @param {Object} [requestOptions={}] - Optional request options and headers.\r\n * @returns {Promise} Returns a promise that resolves with the response object.\r\n * The response object contains a `.text` property with the raw response data.\r\n * @throws {Error} Throws an error if the request fails or if no data is fetched from the URL.\r\n *\r\n * @example\r\n *\r\n * async function getData() {\r\n * try {\r\n * const response = await fetch('https://api.example.com/data');\r\n * console.log(response.text);\r\n * } catch (error) {\r\n * console.error('Error fetching data:', error);\r\n * }\r\n * }\r\n *\r\n * getData();\r\n */\r\nasync function fetch(url, requestOptions = {}) {\r\n return new Promise((resolve, reject) => {\r\n const protocol = getProtocol(url);\r\n\r\n protocol\r\n .get(url, requestOptions, (res) => {\r\n let data = '';\r\n\r\n // A chunk of data has been received.\r\n res.on('data', (chunk) => {\r\n data += chunk;\r\n });\r\n\r\n // The whole response has been received.\r\n res.on('end', () => {\r\n if (!data) {\r\n reject('Nothing was fetched from the URL.');\r\n }\r\n\r\n res.text = data;\r\n resolve(res);\r\n });\r\n })\r\n .on('error', (error) => {\r\n reject(error);\r\n });\r\n });\r\n}\r\n\r\n/**\r\n * Sends a POST request to the specified URL with the given body and request options.\r\n *\r\n * @function\r\n * @async\r\n * @param {string} url - The URL to which the request should be sent.\r\n * @param {Object} [body={}] - The data to be sent as the request body, in JSON format.\r\n * @param {Object} [requestOptions={}] - Optional request options and headers.\r\n * @returns {Promise} - Returns a promise that resolves with the parsed JSON response.\r\n * @throws {Error} Throws an error if the request fails or if the response cannot be parsed.\r\n *\r\n * @example\r\n *\r\n * async function sendData() {\r\n * const dataToSend = {\r\n * key1: 'value1',\r\n * key2: 'value2',\r\n * };\r\n * try {\r\n * const response = await post('https://api.example.com/data', dataToSend);\r\n * console.log(response);\r\n * } catch (error) {\r\n * console.error('Error sending data:', error);\r\n * }\r\n * }\r\n *\r\n * sendData();\r\n */\r\nasync function post(url, body = {}, requestOptions = {}) {\r\n return new Promise((resolve, reject) => {\r\n const protocol = getProtocol(url);\r\n const data = JSON.stringify(body);\r\n\r\n // Set default headers and merge with requestOptions\r\n const options = Object.assign(\r\n {\r\n method: 'POST',\r\n headers: {\r\n 'Content-Type': 'application/json',\r\n 'Content-Length': data.length\r\n }\r\n },\r\n requestOptions\r\n );\r\n\r\n const req = protocol\r\n .request(url, options, (res) => {\r\n let responseData = '';\r\n\r\n // A chunk of data has been received.\r\n res.on('data', (chunk) => {\r\n responseData += chunk;\r\n });\r\n\r\n // The whole response has been received.\r\n res.on('end', () => {\r\n try {\r\n res.text = responseData;\r\n resolve(res);\r\n } catch (error) {\r\n reject(error);\r\n }\r\n });\r\n })\r\n .on('error', (error) => {\r\n reject(error);\r\n });\r\n\r\n // Write the request body and end the request.\r\n req.write(data);\r\n req.end();\r\n });\r\n}\r\n\r\nexport default fetch;\r\nexport { fetch, post };\r\n","/*******************************************************************************\r\n\r\nHighcharts Export Server\r\n\r\nCopyright (c) 2016-2023, Highsoft\r\n\r\nLicenced under the MIT licence.\r\n\r\nAdditionally a valid Highcharts license is required for use.\r\n\r\nSee LICENSE file in root for details.\r\n\r\n*******************************************************************************/\r\n\r\n// The cache manager manages the Highcharts library and its dependencies.\r\n// The cache itself is stored in .cache, and is checked by the config system\r\n// before starting the service\r\n\r\nimport { existsSync, mkdirSync, readFileSync, writeFileSync } from 'fs';\r\nimport { join } from 'path';\r\n\r\nimport dotenv from 'dotenv';\r\nimport HttpsProxyAgent from 'https-proxy-agent';\r\nimport { fetch } from './fetch.js';\r\n\r\nimport { log } from './logger.js';\r\nimport { __dirname } from '../lib/utils.js';\r\n\r\ndotenv.config();\r\n\r\nconst cachePath = join(__dirname, '.cache');\r\n\r\nconst cache = {\r\n cdnURL: 'https://code.highcharts.com/',\r\n activeManifest: {},\r\n sources: '',\r\n hcVersion: ''\r\n};\r\n\r\n// TODO: The config should be accesssible globally so we don't have to do this sort of thing..\r\nlet appliedConfig = false;\r\n\r\n/**\r\n * Extracts the Highcharts version from the cache\r\n */\r\nconst extractVersion = () =>\r\n (cache.hcVersion = cache.sources\r\n .substr(0, cache.sources.indexOf('*/'))\r\n .replace('/*', '')\r\n .replace('*/', '')\r\n .replace(/\\n/g, '')\r\n .trim());\r\n\r\n/**\r\n * Saves the Highcharts part of a config to a manifest file in the cache\r\n *\r\n * @param {object} config - Highcharts related configuration object.\r\n * @param {object} fetchedModules - An object that contains mapped names of\r\n * fetched Highcharts modules to use.\r\n */\r\nconst saveConfigToManifest = async (config, fetchedModules) => {\r\n const newManifest = {\r\n version: config.version,\r\n modules: fetchedModules || {}\r\n };\r\n\r\n // Update cache object with the current modules\r\n cache.activeManifest = newManifest;\r\n\r\n log(4, '[cache] writing new manifest');\r\n\r\n try {\r\n writeFileSync(\r\n join(cachePath, 'manifest.json'),\r\n JSON.stringify(newManifest),\r\n 'utf8'\r\n );\r\n } catch (error) {\r\n log(1, `[cache] Error writing cache manifest: ${error}.`);\r\n }\r\n};\r\n\r\n/**\r\n * Fetches a single script.\r\n *\r\n * @param {string} script - A path to script to get.\r\n * @param {object} proxyAgent - The proxy agent to use for a request.\r\n */\r\nconst fetchScript = async (script, proxyAgent) => {\r\n try {\r\n // Get rid of the .js from the custom strings\r\n if (script.endsWith('.js')) {\r\n script = script.substring(0, script.length - 3);\r\n }\r\n\r\n log(4, `[cache] Fetching script - ${script}.js`);\r\n\r\n // If exists, add proxy agent to request options\r\n const requestOptions = proxyAgent\r\n ? {\r\n agent: proxyAgent,\r\n timeout: +process.env['PROXY_SERVER_TIMEOUT'] || 5000\r\n }\r\n : {};\r\n\r\n // Fetch the script\r\n const response = await fetch(`${script}.js`, requestOptions);\r\n\r\n // If OK, return its text representation\r\n if (response.statusCode === 200) {\r\n return response.text;\r\n }\r\n\r\n throw `${response.statusCode}`;\r\n } catch (error) {\r\n log(1, `[cache] Error fetching script ${script}.js: ${error}.`);\r\n throw error;\r\n }\r\n};\r\n\r\n/**\r\n * Updates the Highcharts cache.\r\n *\r\n * @param {object} config - Highcharts related configuration object.\r\n * @param {string} sourcePath - A path to the file where save updated sources.\r\n * @return {object} An object that contains mapped names of fetched Highcharts\r\n * modules to use.\r\n */\r\nconst updateCache = async (config, sourcePath) => {\r\n const { coreScripts, modules, indicators, scripts: customScripts } = config;\r\n const hcVersion =\r\n config.version === 'latest' || !config.version ? '' : `${config.version}/`;\r\n\r\n log(3, '[cache] Updating cache to Highcharts ', hcVersion);\r\n\r\n // Gather all scripts to fetch\r\n const allScripts = [\r\n ...coreScripts.map((c) => `${hcVersion}${c}`),\r\n ...modules.map((m) =>\r\n m === 'map' ? `maps/${hcVersion}modules/${m}` : `${hcVersion}modules/${m}`\r\n ),\r\n ...indicators.map((i) => `stock/${hcVersion}indicators/${i}`)\r\n ];\r\n\r\n // Configure proxy if exists\r\n let proxyAgent;\r\n const proxyHost = process.env['PROXY_SERVER_HOST'];\r\n const proxyPort = process.env['PROXY_SERVER_PORT'];\r\n\r\n if (proxyHost && proxyPort) {\r\n proxyAgent = new HttpsProxyAgent({\r\n host: proxyHost,\r\n port: +proxyPort\r\n });\r\n }\r\n\r\n const fetchedModules = {};\r\n try {\r\n cache.sources = // TODO: convert to for loop\r\n (\r\n await Promise.all([\r\n ...allScripts.map(async (script) => {\r\n const text = await fetchScript(\r\n `${config.cdnURL || cache.cdnURL}${script}`,\r\n proxyAgent\r\n );\r\n\r\n // If fetched correctly, set it\r\n if (typeof text === 'string') {\r\n fetchedModules[\r\n script.replace(\r\n /(.*)\\/|(.*)modules\\/|stock\\/(.*)indicators\\/|maps\\/(.*)modules\\//gi,\r\n ''\r\n )\r\n ] = 1;\r\n }\r\n\r\n return text;\r\n }),\r\n ...customScripts.map((script) => fetchScript(script, proxyAgent))\r\n ])\r\n ).join(';\\n');\r\n extractVersion();\r\n\r\n // Save the fetched modules into caches' source JSON\r\n writeFileSync(sourcePath, cache.sources);\r\n return fetchedModules;\r\n } catch (error) {\r\n log(1, '[cache] Unable to update local Highcharts cache.');\r\n }\r\n};\r\n\r\nexport const updateVersion = async (newVersion) =>\r\n appliedConfig\r\n ? await checkCache(\r\n Object.assign(appliedConfig, {\r\n version: newVersion\r\n })\r\n )\r\n : false;\r\n\r\n/**\r\n * Fetches any missing Highcharts and dependencies\r\n *\r\n * @param {object} config - Highcharts related configuration object.\r\n */\r\nexport const checkCache = async (config) => {\r\n let fetchedModules;\r\n // Prepare paths to manifest and sources from the .cache folder\r\n const manifestPath = join(cachePath, 'manifest.json');\r\n const sourcePath = join(cachePath, 'sources.js');\r\n\r\n // TODO: deal with trying to switch to the running version\r\n // const activeVersion = appliedConfig ? appliedConfig.version : false;\r\n\r\n appliedConfig = config;\r\n\r\n // Create the .cache destination if it doesn't exist already\r\n !existsSync(cachePath) && mkdirSync(cachePath);\r\n\r\n // Fetch all the scripts either if manifest.json does not exist\r\n // or if the forceFetch option is enabled\r\n if (!existsSync(manifestPath) || config.forceFetch) {\r\n log(3, '[cache] Fetching and caching Highcharts dependencies.');\r\n fetchedModules = await updateCache(config, sourcePath);\r\n } else {\r\n let requestUpdate = false;\r\n\r\n // Read the manifest JSON\r\n const manifest = JSON.parse(readFileSync(manifestPath));\r\n\r\n // Check if the modules is an array, if so, we rewrite it to a map to make\r\n // it easier to resolve modules.\r\n if (manifest.modules && Array.isArray(manifest.modules)) {\r\n const moduleMap = {};\r\n manifest.modules.forEach((m) => (moduleMap[m] = 1));\r\n manifest.modules = moduleMap;\r\n }\r\n\r\n const { modules, coreScripts, indicators } = config;\r\n const numberOfModules =\r\n modules.length + coreScripts.length + indicators.length;\r\n\r\n // Compare the loaded config with the contents in .cache.\r\n // If there are changes, fetch requested modules and products,\r\n // and bake them into a giant blob. Save the blob.\r\n if (manifest.version !== config.version) {\r\n log(3, '[cache] Highcharts version mismatch in cache, need to re-fetch.');\r\n requestUpdate = true;\r\n } else if (Object.keys(manifest.modules || {}).length !== numberOfModules) {\r\n log(\r\n 3,\r\n '[cache] Cache and requested modules does not match, need to re-fetch.'\r\n );\r\n requestUpdate = true;\r\n } else {\r\n // Check each module, if anything is missing refetch everything\r\n requestUpdate = (config.modules || []).some((moduleName) => {\r\n if (!manifest.modules[moduleName]) {\r\n log(\r\n 3,\r\n `[cache] The ${moduleName} missing in cache, need to re-fetch.`\r\n );\r\n return true;\r\n }\r\n });\r\n }\r\n\r\n if (requestUpdate) {\r\n fetchedModules = await updateCache(config, sourcePath);\r\n } else {\r\n log(3, '[cache] Dependency cache is up to date, proceeding.');\r\n\r\n // Load the sources\r\n cache.sources = readFileSync(sourcePath, 'utf8');\r\n\r\n // Get current modules map\r\n fetchedModules = manifest.modules;\r\n extractVersion();\r\n }\r\n }\r\n\r\n // Finally, save the new manifest, which is basically our current config\r\n // in a slightly different format\r\n await saveConfigToManifest(config, fetchedModules);\r\n};\r\n\r\nexport default {\r\n checkCache,\r\n updateVersion,\r\n getCache: () => cache,\r\n highcharts: () => cache.sources,\r\n version: () => cache.hcVersion\r\n};\r\n","/*******************************************************************************\r\n\r\nHighcharts Export Server\r\n\r\nCopyright (c) 2016-2023, Highsoft\r\n\r\nLicenced under the MIT licence.\r\n\r\nAdditionally a valid Highcharts license is required for use.\r\n\r\nSee LICENSE file in root for details.\r\n\r\n*******************************************************************************/\r\n\r\nimport puppeteer from 'puppeteer';\r\nimport fs from 'fs';\r\nimport * as url from 'url';\r\nimport { log } from './logger.js';\r\nimport path from 'node:path';\r\n\r\n// Workaround for https://bugs.chromium.org/p/chromium/issues/detail?id=1463328\r\n// Not ideal - leaves trash in the FS\r\nimport { randomBytes } from 'node:crypto';\r\n\r\nconst RANDOM_PID = randomBytes(64).toString('base64url');\r\nconst PUPPETEER_DIR = path.join('tmp', `puppeteer-${RANDOM_PID}`);\r\nconst DATA_DIR = path.join(PUPPETEER_DIR, 'profile');\r\n\r\n// The minimal args to speed up the browser\r\nconst minimalArgs = [\r\n `--user-data-dir=${DATA_DIR}`,\r\n '--autoplay-policy=user-gesture-required',\r\n '--disable-background-networking',\r\n '--disable-background-timer-throttling',\r\n '--disable-backgrounding-occluded-windows',\r\n '--disable-breakpad',\r\n '--disable-client-side-phishing-detection',\r\n '--disable-component-update',\r\n '--disable-default-apps',\r\n '--disable-dev-shm-usage',\r\n '--disable-domain-reliability',\r\n '--disable-extensions',\r\n '--disable-features=AudioServiceOutOfProcess',\r\n '--disable-hang-monitor',\r\n '--disable-ipc-flooding-protection',\r\n '--disable-notifications',\r\n '--disable-offer-store-unmasked-wallet-cards',\r\n '--disable-popup-blocking',\r\n '--disable-print-preview',\r\n '--disable-prompt-on-repost',\r\n '--disable-renderer-backgrounding',\r\n '--disable-session-crashed-bubble',\r\n '--disable-setuid-sandbox',\r\n '--disable-speech-api',\r\n '--disable-sync',\r\n '--hide-crash-restore-bubble',\r\n '--hide-scrollbars',\r\n '--ignore-gpu-blacklist',\r\n '--metrics-recording-only',\r\n '--mute-audio',\r\n '--no-default-browser-check',\r\n '--no-first-run',\r\n '--no-pings',\r\n '--no-sandbox',\r\n '--no-zygote',\r\n '--password-store=basic',\r\n '--use-mock-keychain'\r\n];\r\n\r\nconst __dirname = url.fileURLToPath(new URL('.', import.meta.url));\r\n\r\nconst template = fs.readFileSync(\r\n __dirname + '/../templates/template.html',\r\n 'utf8'\r\n);\r\n\r\nlet browser;\r\n\r\nconst setPageContent = async (page) => {\r\n await page.setContent(template);\r\n await page.addScriptTag({ path: __dirname + '/../.cache/sources.js' });\r\n // eslint-disable-next-line no-undef\r\n await page.evaluate(() => window.setupHighcharts());\r\n\r\n page.on('pageerror', async (err) => {\r\n // TODO: Consider adding a switch here that turns on log(0) logging\r\n // on page errors.\r\n log(1, '[page error]', err);\r\n await page.$eval(\r\n '#container',\r\n (element, errorMessage) => {\r\n // eslint-disable-next-line no-undef\r\n if (window._displayErrors) {\r\n element.innerHTML = errorMessage;\r\n }\r\n },\r\n `

Chart input data error

${err.toString()}`\r\n );\r\n });\r\n};\r\n\r\nexport const newPage = async () => {\r\n if (!browser) return false;\r\n\r\n const page = await browser.newPage();\r\n\r\n // Set the content\r\n await setPageContent(page);\r\n return page;\r\n};\r\n\r\nexport const clearPage = async (page) => {\r\n try {\r\n // Navigate to about:blank\r\n await page.goto('about:blank');\r\n\r\n // Set the content and and scripts again\r\n await setPageContent(page);\r\n } catch (error) {\r\n log(3, '[browser] Could not clear page');\r\n }\r\n};\r\n\r\nexport const create = async (puppeteerArgs) => {\r\n const allArgs = [...minimalArgs, ...(puppeteerArgs || [])];\r\n\r\n // Create a browser\r\n if (!browser) {\r\n let tryCount = 0;\r\n\r\n const open = async () => {\r\n try {\r\n log(\r\n 3,\r\n '[browser] attempting to get a browser instance (try',\r\n tryCount + ')'\r\n );\r\n\r\n browser = await puppeteer.launch({\r\n headless: 'new',\r\n args: allArgs,\r\n userDataDir: './tmp/'\r\n });\r\n } catch (e) {\r\n log(0, '[browser]', e);\r\n if (++tryCount < 25) {\r\n log(3, '[browser] failed:', e);\r\n await new Promise((response) => setTimeout(response, 4000));\r\n await open();\r\n } else {\r\n log(0, 'Max retries reached');\r\n }\r\n }\r\n };\r\n\r\n try {\r\n await open();\r\n } catch (e) {\r\n log(0, '[browser] Unable to open browser');\r\n return false;\r\n }\r\n\r\n if (!browser) {\r\n log(0, '[browser] Unable to open browser');\r\n return false;\r\n }\r\n }\r\n\r\n // Return a browser promise\r\n return browser;\r\n};\r\n\r\nexport const get = async () => {\r\n if (!browser) {\r\n throw 'No valid browser has been created';\r\n }\r\n\r\n return browser;\r\n};\r\n\r\nexport const close = async () => {\r\n // Close the browser when connnected\r\n if (browser.connected) {\r\n await browser.close();\r\n }\r\n};\r\n\r\nexport default {\r\n newPage,\r\n clearPage,\r\n get,\r\n close\r\n};\r\n","/*******************************************************************************\r\n\r\nHighcharts Export Server\r\n\r\nCopyright (c) 2016-2023, Highsoft\r\n\r\nLicenced under the MIT licence.\r\n\r\nAdditionally a valid Highcharts license is required for use.\r\n\r\nSee LICENSE file in root for details.\r\n\r\n*******************************************************************************/\r\n\r\n// TODO: remove this temp benchmark stuff. I had this idea of doing a general benchmarking\r\n// system, but it adds so much bloat in the code that it shouldn't be there.\r\n\r\nimport benchmark from './benchmark.js';\r\nimport cache from './cache.js';\r\nimport { log } from './logger.js';\r\nimport svgTemplate from './../templates/svg_export/svg_export.js';\r\n\r\nimport { readFileSync } from 'fs';\r\nimport path from 'path';\r\nimport * as url from 'url';\r\n\r\nconst __basedir = url.fileURLToPath(new URL('.', import.meta.url));\r\n\r\n// const jsonTemplate = require('./../templates/json_export/json_export.js');\r\n\r\n/**\r\n * Gets the clip region for the chart DOM node.\r\n *\r\n * @param {object} page - A page of a browser instance.\r\n * @return {object} - A clipped region.\r\n */\r\nconst getClipRegion = (page) =>\r\n page.$eval('#chart-container', (element) => {\r\n const { x, y, width, height } = element.getBoundingClientRect();\r\n return {\r\n x,\r\n y,\r\n width,\r\n height: Math.trunc(height > 1 ? height : 500)\r\n };\r\n });\r\n\r\n/**\r\n * Rasterizes the page to an image (PNG or JPEG)\r\n *\r\n * @param {object} page - A page of a browser instance.\r\n * @param {string} type - The type of a result image.\r\n * @param {string} encoding - The type of encoding used.\r\n * @param {string} clip - The clip region.\r\n * @param {number} rasterizationTimeout - The rasterization timeout in milliseconds.\r\n * @returns {string} - A string representation of a screenshot.\r\n */\r\nconst createImage = async (page, type, encoding, clip, rasterizationTimeout) =>\r\n await Promise.race([\r\n page.screenshot({\r\n type,\r\n encoding,\r\n clip,\r\n\r\n // #447, #463 - always render on a transparent page if\r\n // the expected type format is PNG\r\n omitBackground: type == 'png'\r\n }),\r\n new Promise((resolve, reject) =>\r\n setTimeout(\r\n () => reject(new Error('Rasterization timeout')),\r\n rasterizationTimeout || 1500\r\n )\r\n )\r\n ]);\r\n\r\n/**\r\n * Turns page into a PDF.\r\n *\r\n * @param {object} page - A page of a browser instance.\r\n * @param {number} height - The height of a chart.\r\n * @param {number} width - The width of a chart.\r\n * @param {string} encoding - The type of encoding used.\r\n * @return {object} - A buffer with PDF representation.\r\n */\r\nconst createPDF = async (page, height, width, encoding) =>\r\n await page.pdf({\r\n // This will remove an extra empty page in PDF exports\r\n height: height + 1,\r\n width,\r\n encoding\r\n });\r\n\r\n/**\r\n * Exports as a SVG.\r\n *\r\n * @param {object} page - A page of a browser instance.\r\n * @return {object} - The outerHTML element with the SVG representation.\r\n */\r\nconst createSVG = async (page) =>\r\n await page.$eval(\r\n '#container svg:first-of-type',\r\n (element) => element.outerHTML\r\n );\r\n\r\n/** Load config into a page and render a chart */\r\nconst setAsConfig = async (page, chart, options) =>\r\n await page.evaluate(\r\n // eslint-disable-next-line no-undef\r\n (chart, options) => window.triggerExport(chart, options),\r\n chart,\r\n options\r\n );\r\n\r\n/** Load SVG into a page */\r\n// const setAsSVG = async (page, svgStr) => true;\r\n\r\n/**\r\n * Does an export for a given browser.\r\n *\r\n * @param {object} browser - A browser instance.\r\n * @param {object} chart - Chart's options.\r\n * @param {object} options - All options object.\r\n * @return {object} - The data returned from one of the methods for exporting\r\n * a specific type of an image.\r\n */\r\nexport default async (page, chart, options) => {\r\n /**\r\n * Keeps track of all resources added on the page with addXXXTag. etc\r\n * It's VITAL that all added resources ends up here so we can clear things\r\n * out when doing a new export in the same page!\r\n */\r\n const injectedResources = [];\r\n\r\n /** Clear out all state set on the page with addScriptTag/addStyleTag. */\r\n const clearInjected = async (page) => {\r\n for (const res of injectedResources) {\r\n await res.dispose();\r\n }\r\n\r\n // Reset all CSS and script tags\r\n await page.evaluate(() => {\r\n // eslint-disable-next-line no-undef\r\n const [, ...scriptsToRemove] = document.getElementsByTagName('script');\r\n // eslint-disable-next-line no-undef\r\n const [, ...stylesToRemove] = document.getElementsByTagName('style');\r\n // eslint-disable-next-line no-undef\r\n const [...linksToRemove] = document.getElementsByTagName('link');\r\n\r\n // Remove tags\r\n for (const element of [\r\n ...scriptsToRemove,\r\n ...stylesToRemove,\r\n ...linksToRemove\r\n ]) {\r\n element.remove();\r\n }\r\n });\r\n };\r\n\r\n try {\r\n const exportBench = benchmark('Puppeteer');\r\n\r\n log(4, '[export] Determining export path.');\r\n\r\n const exportOptions = options.export;\r\n\r\n // Force a rAF\r\n // See https://github.com/puppeteer/puppeteer/issues/7507\r\n // eslint-disable-next-line no-undef\r\n await page.evaluate(() => requestAnimationFrame(() => {}));\r\n\r\n // Decide whether display error or debbuger wrapper around it\r\n const displayErrors =\r\n exportOptions?.options?.chart?.displayErrors &&\r\n cache.getCache().activeManifest.modules.debugger;\r\n\r\n // eslint-disable-next-line no-undef\r\n await page.evaluate((d) => (window._displayErrors = d), displayErrors);\r\n\r\n const svgBench = benchmark('SVG handling');\r\n\r\n let isSVG;\r\n\r\n if (\r\n chart.indexOf &&\r\n (chart.indexOf('= 0 || chart.indexOf('= 0)\r\n ) {\r\n // SVG INPUT HANDLING\r\n\r\n log(4, '[export] Treating as SVG.');\r\n\r\n // If input is also svg, just return it\r\n if (exportOptions.type === 'svg') {\r\n return chart;\r\n }\r\n\r\n isSVG = true;\r\n const setPageBench = benchmark('Setting content');\r\n await page.setContent(svgTemplate(chart));\r\n setPageBench();\r\n } else {\r\n // JSON Config handling\r\n\r\n log(4, '[export] Treating as config.');\r\n\r\n // Need to perform straight inject\r\n if (exportOptions.strInj) {\r\n // Injection based configuration export\r\n const setPageBench = benchmark('Setting page content (inject)');\r\n\r\n await setAsConfig(\r\n page,\r\n {\r\n chart: {\r\n height: exportOptions.height,\r\n width: exportOptions.width\r\n }\r\n },\r\n options\r\n );\r\n\r\n setPageBench();\r\n } else {\r\n // Basic configuration export\r\n\r\n chart.chart.height = exportOptions.height;\r\n chart.chart.width = exportOptions.width;\r\n\r\n const setContentBench = benchmark('Setting page content (config)');\r\n await setAsConfig(page, chart, options);\r\n setContentBench();\r\n }\r\n }\r\n\r\n svgBench();\r\n const resBench = benchmark('Applying resources');\r\n\r\n // Use resources\r\n const resources = options.customCode.resources;\r\n if (resources) {\r\n // Load custom JS code\r\n if (resources.js) {\r\n injectedResources.push(\r\n await page.addScriptTag({\r\n content: resources.js\r\n })\r\n );\r\n }\r\n\r\n // Load scripts from all custom files\r\n if (resources.files) {\r\n for (const file of resources.files) {\r\n try {\r\n const isLocal = !file.startsWith('http') ? true : false;\r\n\r\n // Add each custom script from resources' files\r\n injectedResources.push(\r\n await page.addScriptTag(\r\n isLocal\r\n ? {\r\n content: readFileSync(file, 'utf8')\r\n }\r\n : {\r\n url: file\r\n }\r\n )\r\n );\r\n } catch (notice) {\r\n log(4, '[export] JS file not found.');\r\n }\r\n }\r\n }\r\n\r\n const cssBench = benchmark('Loading css');\r\n\r\n // Load CSS\r\n if (resources.css) {\r\n let cssImports = resources.css.match(/@import\\s*([^;]*);/g);\r\n if (cssImports) {\r\n // Handle css section\r\n for (let cssImportPath of cssImports) {\r\n if (cssImportPath) {\r\n cssImportPath = cssImportPath\r\n .replace('url(', '')\r\n .replace('@import', '')\r\n .replace(/\"/g, '')\r\n .replace(/'/g, '')\r\n .replace(/;/, '')\r\n .replace(/\\)/g, '')\r\n .trim();\r\n\r\n // Add each custom css from resources\r\n if (cssImportPath.startsWith('http')) {\r\n injectedResources.push(\r\n await page.addStyleTag({\r\n url: cssImportPath\r\n })\r\n );\r\n } else if (options.customCode.allowFileResources) {\r\n injectedResources.push(\r\n await page.addStyleTag({\r\n path: path.join(__basedir, cssImportPath)\r\n })\r\n );\r\n }\r\n }\r\n }\r\n }\r\n\r\n // The rest of the CSS section will be content by now\r\n injectedResources.push(\r\n await page.addStyleTag({\r\n content: resources.css.replace(/@import\\s*([^;]*);/g, '') || ' '\r\n })\r\n );\r\n }\r\n\r\n cssBench();\r\n }\r\n\r\n resBench();\r\n\r\n // Get the real chart size\r\n const size = isSVG\r\n ? await page.$eval(\r\n '#chart-container svg:first-of-type',\r\n async (element, scale) => {\r\n return {\r\n chartHeight: element.height.baseVal.value * scale,\r\n chartWidth: element.width.baseVal.value * scale\r\n };\r\n },\r\n parseFloat(exportOptions.scale)\r\n )\r\n : await page.evaluate(async () => {\r\n // eslint-disable-next-line no-undef\r\n const { chartHeight, chartWidth } = window.Highcharts.charts[0];\r\n return {\r\n chartHeight,\r\n chartWidth\r\n };\r\n });\r\n\r\n const vpBench = benchmark('Setting viewport');\r\n\r\n // Set final height and width for viewport\r\n const viewportHeight = Math.ceil(size?.chartHeight || exportOptions.height);\r\n const viewportWidth = Math.ceil(size?.chartWidth || exportOptions.width);\r\n\r\n // Set the viewport for the first time\r\n // NOTE: the call to setViewport is expensive - can we get away with only\r\n // calling it once, e.g. moving this one into the isSVG condition below?\r\n await page.setViewport({\r\n height: viewportHeight,\r\n width: viewportWidth,\r\n deviceScaleFactor: isSVG ? 1 : parseFloat(exportOptions.scale)\r\n });\r\n\r\n // Prepare a zoom callback for the next evaluate call\r\n const zoomCallback = isSVG\r\n ? // In case of SVG the zoom must be set directly for body\r\n (scale) => {\r\n // Set the zoom as scale\r\n // eslint-disable-next-line no-undef\r\n document.body.style.zoom = scale;\r\n\r\n // Set the margin to 0px\r\n // eslint-disable-next-line no-undef\r\n document.body.style.margin = '0px';\r\n }\r\n : // No need for such scale manipulation in case of other types of exports\r\n () => {\r\n // Reset the zoom for other exports than to SVGs\r\n // eslint-disable-next-line no-undef\r\n document.body.style.zoom = 1;\r\n };\r\n\r\n // Set the zoom accordingly\r\n await page.evaluate(zoomCallback, parseFloat(exportOptions.scale));\r\n\r\n // Get the clip region for the page\r\n const { height, width, x, y } = await getClipRegion(page);\r\n\r\n if (!isSVG) {\r\n // Set the final viewport now that we have the real height\r\n await page.setViewport({\r\n width: Math.round(width),\r\n height: Math.round(height),\r\n deviceScaleFactor: parseFloat(exportOptions.scale)\r\n });\r\n }\r\n\r\n vpBench();\r\n\r\n let data;\r\n\r\n const expBenchmark = benchmark('Rasterizing chart');\r\n\r\n // RASTERIZATION\r\n if (exportOptions.type === 'svg') {\r\n // SVG\r\n data = await createSVG(page);\r\n } else if (exportOptions.type === 'png' || exportOptions.type === 'jpeg') {\r\n // PNG or JPEG\r\n data = await createImage(\r\n page,\r\n exportOptions.type,\r\n 'base64',\r\n {\r\n width: viewportWidth,\r\n height: viewportHeight,\r\n x,\r\n y\r\n },\r\n options.pool.rasterizationTimeout\r\n );\r\n } else if (exportOptions.type === 'pdf') {\r\n // PDF\r\n data = await createPDF(page, viewportHeight, viewportWidth, 'base64');\r\n } else {\r\n throw `Unsupported output format ${exportOptions.type}`;\r\n }\r\n\r\n // Destroy old charts after the export is done\r\n await page.evaluate(() => {\r\n // eslint-disable-next-line no-undef\r\n const oldCharts = Highcharts.charts;\r\n\r\n // Check in any already existing charts\r\n if (oldCharts.length) {\r\n // Destroy old charts\r\n for (const oldChart of oldCharts) {\r\n oldChart && oldChart.destroy();\r\n // eslint-disable-next-line no-undef\r\n Highcharts.charts.shift();\r\n }\r\n }\r\n });\r\n\r\n expBenchmark();\r\n exportBench();\r\n\r\n await clearInjected(page);\r\n\r\n return data;\r\n } catch (error) {\r\n await clearInjected(page);\r\n log(1, `[export] Error encountered during export: ${error}`);\r\n\r\n return error;\r\n }\r\n};\r\n","/*******************************************************************************\r\n\r\nHighcharts Export Server\r\n\r\nCopyright (c) 2016-2022, Highsoft\r\n\r\nLicenced under the MIT licence.\r\n\r\nAdditionally a valid Highcharts license is required for use.\r\n\r\nSee LICENSE file in root for details.\r\n\r\n*******************************************************************************/\r\n\r\nimport { log } from './logger.js';\r\nconst timers = {};\r\n\r\n// TODO: Read from config\r\nlet enabled = false;\r\n\r\nexport default (id) => {\r\n if (!enabled) {\r\n return () => {};\r\n }\r\n\r\n timers[id] = new Date();\r\n return () => {\r\n log(\r\n 3,\r\n `[benchmark] - ${id}: ${new Date().getTime() - timers[id].getTime()}ms`\r\n );\r\n };\r\n};\r\n","/*******************************************************************************\r\n\r\nHighcharts Export Server\r\n\r\nCopyright (c) 2016-2023, Highsoft\r\n\r\nLicenced under the MIT licence.\r\n\r\nAdditionally a valid Highcharts license is required for use.\r\n\r\nSee LICENSE file in root for details.\r\n\r\n*******************************************************************************/\r\n\r\nimport cssTemplate from './css.js';\r\n\r\nexport default (chart) => `\r\n\r\n\r\n \r\n \r\n Highcarts Export\r\n \r\n \r\n \r\n
\r\n ${chart}\r\n
\r\n \r\n\r\n\r\n`;\r\n","/*******************************************************************************\r\n\r\nHighcharts Export Server\r\n\r\nCopyright (c) 2016-2023, Highsoft\r\n\r\nLicenced under the MIT licence.\r\n\r\nAdditionally a valid Highcharts license is required for use.\r\n\r\nSee LICENSE file in root for details.\r\n\r\n*******************************************************************************/\r\n\r\nimport { v4 as uuid } from 'uuid';\r\nimport { Pool } from 'tarn';\r\nimport {\r\n close,\r\n create as createBrowser,\r\n newPage as browserNewPage,\r\n clearPage\r\n} from './browser.js';\r\nimport { log } from './logger.js';\r\n\r\nimport puppeteerExport from './export.js';\r\n\r\nlet performedExports = 0;\r\nlet exportAttempts = 0;\r\nlet timeSpent = 0;\r\nlet droppedExports = 0;\r\nlet spentAverage = 0;\r\nlet poolConfig = {};\r\n\r\n// The pool instance\r\nlet pool = false;\r\n\r\n// Custom puppeteer arguments\r\nlet puppeteerArgs;\r\n\r\nconst factory = {\r\n /**\r\n * Creates a new worker.\r\n *\r\n * @return {object} - An object with the id of a resource, the work count and\r\n * a reference to the browser page.\r\n */\r\n create: async () => {\r\n const id = uuid();\r\n let page = false;\r\n\r\n const s = new Date().getTime();\r\n\r\n try {\r\n page = await browserNewPage();\r\n\r\n if (!page || page.isClosed()) {\r\n throw '[pool] Invalid page';\r\n }\r\n\r\n log(\r\n 3,\r\n `[pool] Successfully created a worker ${id} - took ${\r\n new Date().getTime() - s\r\n } ms.`\r\n );\r\n } catch (error) {\r\n log(\r\n 1,\r\n `[pool] Error creating a new page in pool entry creation! ${error}`\r\n );\r\n\r\n throw 'Error creating page';\r\n }\r\n\r\n return {\r\n id,\r\n page,\r\n // Try to distribute the initial work count\r\n workCount: Math.round(Math.random() * (poolConfig.workLimit / 2))\r\n };\r\n },\r\n\r\n /**\r\n * Validates a worker.\r\n *\r\n * @param {object} workerHandle - A browser's instance.\r\n *\r\n * @return {boolean} - Bool that indicates if a resource is valid or not.\r\n */\r\n validate: async (workerHandle) => {\r\n if (\r\n poolConfig.workLimit &&\r\n ++workerHandle.workCount > poolConfig.workLimit\r\n ) {\r\n log(\r\n 3,\r\n `[pool] Worker failed validation:`,\r\n `exceeded work limit (limit is ${poolConfig.workLimit})`\r\n );\r\n return false;\r\n }\r\n\r\n // Clear page\r\n await clearPage(workerHandle.page);\r\n return true;\r\n },\r\n\r\n /**\r\n * Destroys a worker.\r\n *\r\n * @param {object} workerHandle - A browser's instance.\r\n */\r\n destroy: (workerHandle) => {\r\n log(3, `[pool] Destroying pool entry ${workerHandle.id}.`);\r\n\r\n if (workerHandle.page) {\r\n // We don't really need to wait around for this.\r\n workerHandle.page.close();\r\n }\r\n },\r\n\r\n // Logger function\r\n log: (message, logLevel) => console.log(`${logLevel}: ${message}`)\r\n};\r\n\r\n/**\r\n * Inits the pool of resources.\r\n *\r\n * @param {object} config - Pool configuration along with custom puppeteer\r\n * arguments for the puppeteer.launch function.\r\n */\r\nexport const init = async (config) => {\r\n // The newest puppeteer arguments for the browser creation\r\n puppeteerArgs = config.puppeteerArgs;\r\n\r\n // Wait until we've sucessfully created a browser instance.\r\n try {\r\n await createBrowser(puppeteerArgs);\r\n } catch (e) {\r\n log(0, '[pool|browser]', e);\r\n }\r\n\r\n // For the module scope usage\r\n poolConfig = config && config.pool ? { ...config.pool } : {};\r\n\r\n log(\r\n 3,\r\n '[pool] Initializing pool:',\r\n `min ${poolConfig.minWorkers}, max ${poolConfig.maxWorkers}.`\r\n );\r\n\r\n if (pool) {\r\n return log(\r\n 4,\r\n '[pool] Already initialized, please kill it before creating a new one.'\r\n );\r\n }\r\n\r\n // Attach process' exit listeners\r\n if (poolConfig.listenToProcessExits) {\r\n attachProcessExitListeners();\r\n }\r\n\r\n try {\r\n // Create a pool along with a minimal number of resources\r\n pool = new Pool({\r\n // Get the create/validate/destroy/log functions\r\n ...factory,\r\n min: poolConfig.minWorkers,\r\n max: poolConfig.maxWorkers,\r\n acquireTimeoutMillis: poolConfig.acquireTimeout,\r\n createTimeoutMillis: poolConfig.createTimeout,\r\n destroyTimeoutMillis: poolConfig.destroyTimeout,\r\n idleTimeoutMillis: poolConfig.idleTimeout,\r\n createRetryIntervalMillis: poolConfig.createRetryInterval,\r\n reapIntervalMillis: poolConfig.reaperInterval,\r\n propagateCreateError: false\r\n });\r\n\r\n // Set events\r\n pool.on('createFail', (eventId, err) => {\r\n log(\r\n 1,\r\n `[pool] Error when creating worker of an event id ${eventId}:`,\r\n err\r\n );\r\n });\r\n\r\n pool.on('acquireFail', (eventId, err) => {\r\n log(\r\n 1,\r\n `[pool] Error when acquiring worker of an event id ${eventId}:`,\r\n err\r\n );\r\n });\r\n\r\n pool.on('destroyFail', (eventId, resource, err) => {\r\n log(\r\n 1,\r\n `[pool] Error when destroying worker of an id ${resource.id}, event id ${eventId}:`,\r\n err\r\n );\r\n });\r\n\r\n pool.on('release', (resource) => {\r\n log(4, `[pool] Releasing a worker of an id ${resource.id}`);\r\n });\r\n\r\n pool.on('destroySuccess', (eventId, resource) => {\r\n log(4, `[pool] Destroyed a worker of an id ${resource.id}`);\r\n });\r\n\r\n const initialResources = [];\r\n // Create an initial number of resources\r\n for (let i = 0; i < poolConfig.minWorkers; i++) {\r\n try {\r\n const resource = await pool.acquire().promise;\r\n initialResources.push(resource);\r\n } catch (error) {\r\n log(1, `[pool] Couldn't create an initial resource ${error}`);\r\n }\r\n }\r\n\r\n // Release the initial number of resources back to the pool\r\n initialResources.forEach((resource) => {\r\n pool.release(resource);\r\n });\r\n\r\n log(\r\n 3,\r\n `[pool] The pool is ready with ${poolConfig.minWorkers} initial resources waiting.`\r\n );\r\n } catch (error) {\r\n log(1, `[pool] Couldn't create the worker pool ${error}`);\r\n throw error;\r\n }\r\n};\r\n\r\n/**\r\n * Attaches process' exit listeners.\r\n */\r\nexport function attachProcessExitListeners() {\r\n log(4, '[pool] Attaching exit listeners to the process.');\r\n\r\n // Kill all pool resources on exit\r\n process.on('exit', async () => {\r\n await killPool();\r\n });\r\n\r\n // Handler for the SIGINT\r\n process.on('SIGINT', (name, code) => {\r\n log(4, `The ${name} event with code: ${code}.`);\r\n process.exit(1);\r\n });\r\n\r\n // Handler for the SIGTERM\r\n process.on('SIGTERM', (name, code) => {\r\n log(4, `The ${name} event with code: ${code}.`);\r\n process.exit(1);\r\n });\r\n\r\n // Handler for the uncaughtException\r\n process.on('uncaughtException', async (error, name) => {\r\n log(4, `The ${name} error, message: ${error.message}.`);\r\n });\r\n}\r\n\r\n/**\r\n * Kills the pool and flush the browser instance.\r\n */\r\nexport async function killPool() {\r\n log(3, '[pool] Killing all workers.');\r\n\r\n // Return true when the pool is already destroyed\r\n if (pool.destroyed) {\r\n // Close the browser instance if still connected\r\n await close();\r\n return true;\r\n }\r\n\r\n // If still alive, destroy the pool of pages before closing a browser\r\n await pool.destroy();\r\n\r\n // Close the browser instance\r\n await close();\r\n return true;\r\n}\r\n\r\n/**\r\n * Posts work to the pool.\r\n *\r\n * @param {object} chart - Chart's options.\r\n * @param {object} options - All options object.\r\n */\r\nexport const postWork = async (chart, options) => {\r\n let workerHandle;\r\n\r\n // Handle fail conditions\r\n const fail = (msg) => {\r\n ++droppedExports;\r\n\r\n if (workerHandle) {\r\n pool.release(workerHandle);\r\n }\r\n\r\n throw 'In pool.postWork: ' + msg;\r\n };\r\n\r\n log(4, '[pool] Work received, starting to process.');\r\n\r\n if (poolConfig.benchmarking) {\r\n getPoolInfo();\r\n }\r\n\r\n ++exportAttempts;\r\n\r\n if (!pool) {\r\n log(1, '[pool] Work received, but pool has not been started.');\r\n return fail('Pool is not inited but work was posted to it!');\r\n }\r\n\r\n // Acquire the worker along with the id of resource and work count\r\n try {\r\n log(4, '[pool] Acquiring worker');\r\n workerHandle = await pool.acquire().promise;\r\n } catch (error) {\r\n return fail(`[pool] Error when acquiring available entry: ${error}`);\r\n }\r\n\r\n log(4, '[pool] Acquired worker handle');\r\n\r\n if (!workerHandle.page) {\r\n return fail('Resolved worker page is invalid: pool setup is wonky');\r\n }\r\n\r\n try {\r\n // Save the start time\r\n let workStart = new Date().getTime();\r\n\r\n log(4, `[pool] Starting work on pool entry ${workerHandle.id}.`);\r\n\r\n // Perform an export on a puppeteer level\r\n const result = await puppeteerExport(workerHandle.page, chart, options);\r\n\r\n // Check if it's an error\r\n if (result instanceof Error) {\r\n // TODO: If the export failed because puppeteer timed out, we need to force kill the worker so we get a new page. That needs to be handled better than this hack.\r\n if (result.message === 'Rasterization timeout') {\r\n workerHandle.page.close();\r\n workerHandle.page = await browserNewPage();\r\n }\r\n\r\n return fail(result);\r\n }\r\n\r\n // Release the resource back to the pool\r\n pool.release(workerHandle);\r\n\r\n // Used for statistics in averageTime and processedWorkCount, which\r\n // in turn is used by the /health route.\r\n const workEnd = new Date().getTime();\r\n const exportTime = workEnd - workStart;\r\n timeSpent += exportTime;\r\n spentAverage = timeSpent / ++performedExports;\r\n\r\n log(4, `[pool] Work completed in ${exportTime} ms.`);\r\n\r\n // Otherwise return the result\r\n return {\r\n data: result,\r\n options\r\n };\r\n } catch (error) {\r\n fail(`Error trying to perform puppeteer export: ${error}.`);\r\n }\r\n};\r\n\r\n/**\r\n * Gets the pool.\r\n */\r\nexport function getPool() {\r\n return pool;\r\n}\r\n\r\nexport const getPoolInfoJSON = () => ({\r\n min: pool.min,\r\n max: pool.max,\r\n size: pool.size,\r\n available: pool.available,\r\n borrowed: pool.borrowed,\r\n pending: pool.pending,\r\n spareResourceCapacity: pool.spareResourceCapacity\r\n});\r\n\r\n/**\r\n * Gets the pool's information.\r\n */\r\nexport function getPoolInfo() {\r\n const {\r\n min,\r\n max,\r\n size,\r\n available,\r\n borrowed,\r\n pending,\r\n spareResourceCapacity\r\n } = pool;\r\n\r\n log(4, `[pool] The minimum number of resources allowed by pool: ${min}.`);\r\n log(4, `[pool] The maximum number of resources allowed by pool: ${max}.`);\r\n log(\r\n 4,\r\n `[pool] The number of all resources in pool (free or in use): ${size}.`\r\n );\r\n log(\r\n 4,\r\n `[pool] The number of resources that are currently available: ${available}.`\r\n );\r\n log(\r\n 4,\r\n `[pool] The number of resources that are currently acquired: ${borrowed}.`\r\n );\r\n log(\r\n 4,\r\n `[pool] The number of callers waiting to acquire a resource: ${pending}.`\r\n );\r\n log(\r\n 4,\r\n `[pool] The number of how many more resources can the pool manage/create: ${spareResourceCapacity}.`\r\n );\r\n}\r\n\r\nexport default {\r\n init,\r\n killPool,\r\n postWork,\r\n getPool,\r\n getPoolInfo,\r\n getPoolInfoJSON,\r\n workAttempts: () => exportAttempts,\r\n droppedWork: () => droppedExports,\r\n averageTime: () => spentAverage,\r\n processedWorkCount: () => performedExports\r\n};\r\n","/*******************************************************************************\r\n\r\nHighcharts Export Server\r\n\r\nCopyright (c) 2016-2023, Highsoft\r\n\r\nLicenced under the MIT licence.\r\n\r\nAdditionally a valid Highcharts license is required for use.\r\n\r\nSee LICENSE file in root for details.\r\n\r\n*******************************************************************************/\r\n\r\nimport cache from '../../cache.js';\r\nimport pool from '../../pool.js';\r\n\r\nconst packageVersion = process.env.npm_package_version;\r\nconst serverStartTime = new Date();\r\n\r\n/**\r\n * Adds the /health route which outputs basic stats for the server\r\n */\r\nexport default (app) =>\r\n !app\r\n ? false\r\n : app.get('/health', (request, response) => {\r\n response.send({\r\n status: 'OK',\r\n bootTime: serverStartTime,\r\n uptime:\r\n Math.floor(\r\n (new Date().getTime() - serverStartTime.getTime()) / 1000 / 60\r\n ) + ' minutes',\r\n version: packageVersion,\r\n highchartsVersion: cache.version(),\r\n averageProcessingTime: pool.averageTime(),\r\n performedExports: pool.processedWorkCount(),\r\n failedExports: pool.droppedWork(),\r\n exportAttempts: pool.workAttempts(),\r\n sucessRatio: (pool.processedWorkCount() / pool.workAttempts()) * 100,\r\n // eslint-disable-next-line import/no-named-as-default-member\r\n pool: pool.getPoolInfoJSON()\r\n });\r\n });\r\n","/*******************************************************************************\r\n\r\nHighcharts Export Server\r\n\r\nCopyright (c) 2016-2023, Highsoft\r\n\r\nLicenced under the MIT licence.\r\n\r\nAdditionally a valid Highcharts license is required for use.\r\n\r\nSee LICENSE file in root for details.\r\n\r\n*******************************************************************************/\r\n\r\nimport { existsSync, readFileSync, promises as fsPromises } from 'fs';\r\n\r\nimport prompts from 'prompts';\r\n\r\nimport { log } from './logger.js';\r\nimport { deepCopy, isObject, printUsage, toBoolean } from './utils.js';\r\nimport {\r\n absoluteProps,\r\n defaultConfig,\r\n nestedArgs,\r\n promptsConfig\r\n} from './schemas/config.js';\r\n\r\nlet generalOptions = {};\r\n\r\n/**\r\n * Getter for the general options.\r\n *\r\n * @return {object} - General options object.\r\n */\r\nexport const getOptions = () => generalOptions;\r\n\r\n/**\r\n * Initializes and sets the general options for the server instace.\r\n *\r\n * @param {object} userOptions - Additional user options (e.g. from the node\r\n * module usage).\r\n * @param {string[]} args - CLI arguments.\r\n * @return {object} - General options object.\r\n */\r\nexport const setOptions = (userOptions, args) => {\r\n // Only for the CLI usage\r\n if (args?.length) {\r\n // Get the additional options from the custom JSON file\r\n generalOptions = loadConfigFile(args);\r\n }\r\n\r\n // Update the default config with a correct option values\r\n updateDefaultConfig(defaultConfig, generalOptions);\r\n\r\n // Set values for server's options and returns them\r\n generalOptions = initOptions(defaultConfig);\r\n\r\n // Apply user options if there are any\r\n if (userOptions) {\r\n // Merge user options\r\n generalOptions = mergeConfigOptions(\r\n generalOptions,\r\n userOptions,\r\n absoluteProps\r\n );\r\n }\r\n\r\n // Only for the CLI usage\r\n if (args?.length) {\r\n // Pair provided arguments\r\n generalOptions = pairArgumentValue(generalOptions, args, defaultConfig);\r\n }\r\n\r\n // Return final general options\r\n return generalOptions;\r\n};\r\n\r\n/**\r\n * Displays a prompt for the manual configuration.\r\n *\r\n * @param {string} configFileName - The name of a configuration file.\r\n */\r\nexport const manualConfig = async (configFileName) => {\r\n // Prepare a config object\r\n let configFile = {};\r\n\r\n // Check if provided config file exists\r\n if (existsSync(configFileName)) {\r\n configFile = JSON.parse(readFileSync(configFileName, 'utf8'));\r\n }\r\n\r\n // Question about a configuration category\r\n const onSubmit = async (p, categories) => {\r\n let questionsCounter = 0;\r\n let allQuestions = [];\r\n\r\n // Create a corresponding property in the manualConfig object\r\n for (const section of categories) {\r\n // Mark each option with a section\r\n promptsConfig[section] = promptsConfig[section].map((option) => ({\r\n ...option,\r\n section\r\n }));\r\n\r\n // Collect the questions\r\n allQuestions = [...allQuestions, ...promptsConfig[section]];\r\n }\r\n\r\n await prompts(allQuestions, {\r\n onSubmit: async (prompt, answer) => {\r\n // Get the default modules\r\n if (prompt.name === 'modules') {\r\n answer = answer.length\r\n ? answer.map((module) => prompt.choices[module])\r\n : prompt.choices;\r\n\r\n configFile[prompt.section][prompt.name] = answer;\r\n } else {\r\n configFile[prompt.section] = recursiveProps(\r\n Object.assign({}, configFile[prompt.section] || {}),\r\n prompt.name.split('.'),\r\n answer\r\n );\r\n }\r\n\r\n if (++questionsCounter === allQuestions.length) {\r\n try {\r\n await fsPromises.writeFile(\r\n configFileName,\r\n JSON.stringify(configFile, null, 2),\r\n 'utf8'\r\n );\r\n } catch (error) {\r\n log(1, `[config] Error while creating config.json: ${error}`);\r\n }\r\n return true;\r\n }\r\n }\r\n });\r\n\r\n return true;\r\n };\r\n\r\n // Find the categories\r\n const choices = Object.keys(promptsConfig).map((choice) => ({\r\n title: `${choice} options`,\r\n value: choice\r\n }));\r\n\r\n // Category prompt\r\n return prompts(\r\n {\r\n type: 'multiselect',\r\n name: 'category',\r\n message: 'Which category do you want to configure?',\r\n hint: 'Space: Select specific, A: Select all, Enter: Confirm.',\r\n instructions: '',\r\n choices\r\n },\r\n { onSubmit }\r\n );\r\n};\r\n\r\n/**\r\n * Maps the old options to the new config structure.\r\n *\r\n * @param {object} oldOptions - Options to be mapped.\r\n */\r\nexport const mapToNewConfig = (oldOptions) => {\r\n const newOptions = {};\r\n // Cycle through old-structured options\r\n for (const [key, value] of Object.entries(oldOptions)) {\r\n const propertiesChain = nestedArgs[key] ? nestedArgs[key].split('.') : [];\r\n\r\n // Populate object in correct properties levels\r\n propertiesChain.reduce(\r\n (obj, prop, index) =>\r\n (obj[prop] =\r\n propertiesChain.length - 1 === index ? value : obj[prop] || {}),\r\n newOptions\r\n );\r\n }\r\n return newOptions;\r\n};\r\n\r\n/**\r\n * Merges the new options to the options object. It omits undefined values.\r\n *\r\n * @param {object} options - Old options.\r\n * @param {object} newOptions - New options.\r\n * @param {string[]} absoluteProps - Array of object names that should be force\r\n * merged.\r\n */\r\nexport const mergeConfigOptions = (options, newOptions, absoluteProps = []) => {\r\n const mergedOptions = deepCopy(options);\r\n\r\n for (const [key, value] of Object.entries(newOptions)) {\r\n mergedOptions[key] =\r\n isObject(value) &&\r\n !absoluteProps.includes(key) &&\r\n mergedOptions[key] !== undefined\r\n ? mergeConfigOptions(mergedOptions[key], value, absoluteProps)\r\n : value !== undefined\r\n ? value\r\n : mergedOptions[key];\r\n }\r\n\r\n return mergedOptions;\r\n};\r\n\r\n/**\r\n * Initializes options for the `startExport` method by merging user options\r\n * with the general options.\r\n *\r\n * @param {any} exportOptions - User options for exporting.\r\n * @param {any} generalOptions - General options are used for the export server.\r\n * @return {object} - User options merged with default options.\r\n */\r\nexport const initExportSettings = (exportOptions, generalOptions = {}) => {\r\n let options = {};\r\n\r\n if (exportOptions.svg) {\r\n options = deepCopy(generalOptions);\r\n options.export.type = exportOptions.type || exportOptions.export.type;\r\n options.export.scale = exportOptions.scale || exportOptions.export.scale;\r\n options.export.outfile =\r\n exportOptions.outfile || exportOptions.export.outfile;\r\n options.payload = {\r\n svg: exportOptions.svg\r\n };\r\n } else {\r\n options = mergeConfigOptions(\r\n generalOptions,\r\n exportOptions,\r\n // Omit going down recursively with the belows\r\n absoluteProps\r\n );\r\n }\r\n\r\n options.export.outfile =\r\n options.export?.outfile || `chart.${options.export?.type || 'png'}`;\r\n return options;\r\n};\r\n\r\n/**\r\n * Loads the configuration from a custom JSON file.\r\n *\r\n * @param {string[]} args - CLI arguments.\r\n * @return {object} - Options object from the JSON file.\r\n */\r\nfunction loadConfigFile(args) {\r\n // Check if the --loadConfig option was used\r\n const configIndex = args.findIndex(\r\n (arg) => arg.replace(/-/g, '') === 'loadConfig'\r\n );\r\n\r\n // Check if the --loadConfig has a value\r\n if (configIndex > -1 && args[configIndex + 1]) {\r\n const fileName = args[configIndex + 1];\r\n try {\r\n // Check if an additional config file is a correct JSON file\r\n if (fileName && fileName.endsWith('.json')) {\r\n // Load an optional custom JSON config file\r\n return JSON.parse(readFileSync(fileName));\r\n }\r\n } catch (error) {\r\n log(1, `[config] Unable to load config from the ${fileName}: ${error}`);\r\n }\r\n }\r\n\r\n // No additional options to return\r\n return {};\r\n}\r\n\r\n/**\r\n * Setting correct values of the options from the default config.\r\n *\r\n * @param {object} configObj - The config object based on which the initial\r\n * configuration be made.\r\n * @param {object} customObj - The custom object which can contain additional\r\n * option values to set.\r\n * @param {string} propChain - Required for creating a string chain of\r\n * properties for nested arguments.\r\n */\r\nfunction updateDefaultConfig(configObj, customObj = {}, propChain = '') {\r\n Object.keys(configObj).forEach((key) => {\r\n if (!['puppeteer', 'highcharts'].includes(key)) {\r\n const entry = configObj[key];\r\n const customValue = customObj && customObj[key];\r\n let numEnvVal;\r\n\r\n if (typeof entry.value === 'undefined') {\r\n updateDefaultConfig(entry, customValue, `${propChain}.${key}`);\r\n } else {\r\n // If a value from a custom JSON exists, it take precedence\r\n if (customValue !== undefined) {\r\n entry.value = customValue;\r\n }\r\n\r\n // If a value from an env variable exists, it take precedence\r\n if (entry.envLink) {\r\n // Load the env var\r\n if (entry.type === 'boolean') {\r\n entry.value = toBoolean(\r\n [process.env[entry.envLink], entry.value].find(\r\n (el) => el || el === 'false'\r\n )\r\n );\r\n } else if (entry.type === 'number') {\r\n numEnvVal = +process.env[entry.envLink];\r\n entry.value = numEnvVal >= 0 ? numEnvVal : entry.value;\r\n } else if (\r\n entry.type.indexOf(']') >= 0 &&\r\n process.env[entry.envLink]\r\n ) {\r\n entry.value = process.env[entry.envLink].split(',');\r\n } else {\r\n entry.value = process.env[entry.envLink] || entry.value;\r\n }\r\n }\r\n }\r\n }\r\n });\r\n}\r\n\r\n/**\r\n * Inits options recursively.\r\n *\r\n * @param {any} items - Items to update options from.\r\n * @return {object} - Updated options object.\r\n */\r\nfunction initOptions(items) {\r\n let options = {};\r\n for (const [name, item] of Object.entries(items)) {\r\n options[name] = Object.prototype.hasOwnProperty.call(item, 'value')\r\n ? item.value\r\n : initOptions(item);\r\n }\r\n return options;\r\n}\r\n\r\n/**\r\n * Pairs argument with a corresponding value.\r\n *\r\n * @param {object} options - All server options.\r\n * @param {string[]} args - Array of arguments from a user.\r\n * @param {object} defaultConfig - The default config object.\r\n */\r\nfunction pairArgumentValue(options, args, defaultConfig) {\r\n for (let i = 0; i < args.length; i++) {\r\n let option = args[i].replace(/-/g, '');\r\n\r\n // Find the right place for property's value\r\n const propertiesChain = nestedArgs[option]\r\n ? nestedArgs[option].split('.')\r\n : [];\r\n\r\n propertiesChain.reduce((obj, prop, index) => {\r\n if (propertiesChain.length - 1 === index) {\r\n // Finds an option and set a corresponding value\r\n if (typeof obj[prop] !== 'undefined') {\r\n if (args[++i]) {\r\n obj[prop] = args[i] || obj[prop];\r\n } else {\r\n console.log(`Missing argument value for ${option}!`.red, '\\n');\r\n options = printUsage(defaultConfig);\r\n }\r\n }\r\n }\r\n return obj[prop];\r\n }, options);\r\n }\r\n\r\n return options;\r\n}\r\n\r\n/**\r\n * Recursively sets a property in a correct indentation level based on the\r\n * array of nested properties names.\r\n *\r\n * @param {object} objectToUpdate - Object where a property must be set on a\r\n * correct level.\r\n * @param {string[]}nestedNames - Array of nasted names that indicates\r\n * indentation level.\r\n * @param {any} value - A value to assign to the property.\r\n * @return {object} - Updated options object.\r\n */\r\nfunction recursiveProps(objectToUpdate, nestedNames, value) {\r\n while (nestedNames.length > 1) {\r\n const propName = nestedNames.shift();\r\n\r\n // Create a property in object if it doesn't exist\r\n if (!Object.prototype.hasOwnProperty.call(objectToUpdate, propName)) {\r\n objectToUpdate[propName] = {};\r\n }\r\n\r\n // Call function again if there still names to go\r\n objectToUpdate[propName] = recursiveProps(\r\n Object.assign({}, objectToUpdate[propName]),\r\n nestedNames,\r\n value\r\n );\r\n\r\n return objectToUpdate;\r\n }\r\n\r\n // Assign the final value\r\n objectToUpdate[nestedNames[0]] = value;\r\n return objectToUpdate;\r\n}\r\n\r\nexport default {\r\n getOptions,\r\n setOptions,\r\n manualConfig,\r\n mapToNewConfig,\r\n mergeConfigOptions,\r\n initExportSettings\r\n};\r\n","/*******************************************************************************\r\n\r\nHighcharts Export Server\r\n\r\nCopyright (c) 2016-2023, Highsoft\r\n\r\nLicenced under the MIT licence.\r\n\r\nAdditionally a valid Highcharts license is required for use.\r\n\r\nSee LICENSE file in root for details.\r\n\r\n*******************************************************************************/\r\n\r\nimport { readFile, readFileSync, writeFileSync } from 'fs';\r\n\r\nimport { log } from './logger.js';\r\nimport { killPool, postWork } from './pool.js';\r\nimport {\r\n clearText,\r\n fixType,\r\n handleResources,\r\n isCorrectJSON,\r\n optionsStringify,\r\n roundNumber,\r\n toBoolean,\r\n wrapAround\r\n} from './utils.js';\r\nimport { initExportSettings, getOptions } from './config.js';\r\n\r\nlet allowCodeExecution = false;\r\n\r\nexport const startExport = async (settings, endCallback) => {\r\n // Starting exporting process message\r\n log(4, '[chart] Starting exporting process.');\r\n\r\n // Initialize options\r\n const options = initExportSettings(settings, getOptions());\r\n\r\n // Get the export options\r\n const exportOptions = options.export;\r\n\r\n // If SVG is an input (argument can be sent only by the request)\r\n if (options.payload?.svg && options.payload.svg !== '') {\r\n return exportAsString(options.payload.svg.trim(), options, endCallback);\r\n }\r\n\r\n // Export using options from the file\r\n if (exportOptions.infile && exportOptions.infile.length) {\r\n log(4, '[chart] Attempting to export from an input file.');\r\n\r\n // Try to read the file\r\n return readFile(exportOptions.infile, 'utf8', (error, infile) => {\r\n if (error) {\r\n return log(1, `[chart] Error loading input file: ${error}.`);\r\n }\r\n\r\n // Get the string representation\r\n options.export.instr = infile;\r\n return exportAsString(options.export.instr.trim(), options, endCallback);\r\n });\r\n }\r\n\r\n // Export with options from the raw representation\r\n if (\r\n (exportOptions.instr && exportOptions.instr !== '') ||\r\n (exportOptions.options && exportOptions.options !== '')\r\n ) {\r\n log(4, '[chart] Attempting to export from a raw input.');\r\n\r\n // Perform a direct inject when forced\r\n if (toBoolean(options.customCode?.allowCodeExecution)) {\r\n return doStraightInject(options, endCallback);\r\n }\r\n\r\n // Either try to parse to JSON first or do the direct export\r\n return typeof exportOptions.instr === 'string'\r\n ? exportAsString(exportOptions.instr.trim(), options, endCallback)\r\n : doExport(\r\n options,\r\n exportOptions.instr || exportOptions.options,\r\n endCallback\r\n );\r\n }\r\n\r\n // No input specified, pass an error message to the callback\r\n log(\r\n 1,\r\n clearText(\r\n `[chart] No input specified.\r\n ${JSON.stringify(exportOptions, undefined, ' ')}.`\r\n )\r\n );\r\n\r\n return (\r\n endCallback &&\r\n endCallback(false, {\r\n error: true,\r\n message: 'No input specified.'\r\n })\r\n );\r\n};\r\n\r\nexport const batchExport = (options) => {\r\n const batchFunctions = [];\r\n\r\n // Split and pair the --batch arguments\r\n for (let pair of options.export.batch.split(';')) {\r\n pair = pair.split('=');\r\n if (pair.length === 2) {\r\n batchFunctions.push(\r\n new Promise((resolve, reject) => {\r\n startExport(\r\n {\r\n ...options,\r\n export: {\r\n ...options.export,\r\n infile: pair[0],\r\n outfile: pair[1]\r\n }\r\n },\r\n (info, error) => {\r\n // Throw an error\r\n if (error) {\r\n return reject(error);\r\n }\r\n\r\n // Save the base64 from a buffer to a correct image file\r\n writeFileSync(\r\n info.options.export.outfile,\r\n Buffer.from(info.data, 'base64')\r\n );\r\n\r\n resolve();\r\n }\r\n );\r\n })\r\n );\r\n }\r\n }\r\n\r\n // Kill the pool after all exports are done\r\n Promise.all(batchFunctions)\r\n .then(() => {\r\n killPool();\r\n })\r\n .catch((error) => {\r\n log(1, `[chart] Error encountered during batch export: ${error}`);\r\n killPool();\r\n });\r\n};\r\n\r\nexport const singleExport = (options) => {\r\n // Use instr or its alias, options\r\n options.export.instr = options.export.instr || options.export.options;\r\n\r\n // Perform an export\r\n startExport(options, (info, error) => {\r\n // Exit process when error\r\n if (error) {\r\n log(1, `[cli] ${error.message}`);\r\n process.exit(1);\r\n }\r\n\r\n const { outfile, type } = info.options.export;\r\n\r\n // Save the base64 from a buffer to a correct image file\r\n writeFileSync(\r\n outfile || `chart.${type}`,\r\n type !== 'svg' ? Buffer.from(info.data, 'base64') : info.data\r\n );\r\n\r\n // Kill the pool\r\n killPool();\r\n });\r\n};\r\n\r\n/**\r\n * Function for choosing chart size and scale based on options prioritization.\r\n *\r\n * @param {object} options - All options object.\r\n * @return {object} - An object with updated size and scale for a chart.\r\n */\r\nexport const findChartSize = (options) => {\r\n const { chart, exporting } =\r\n options.export?.options || isCorrectJSON(options.export?.instr);\r\n\r\n // See if globalOptions holds chart or exporting size\r\n const globalOptions = isCorrectJSON(options.export?.globalOptions);\r\n\r\n // Secure scale value\r\n let scale =\r\n options.export?.scale ||\r\n exporting?.scale ||\r\n globalOptions?.exporting?.scale ||\r\n options.export?.defaultScale ||\r\n 1;\r\n\r\n // the scale cannot be lower than 0.1 and cannot be higher than 5.0\r\n scale = Math.max(0.1, Math.min(scale, 5.0));\r\n\r\n // we want to round the numbers like 0.23234 -> 0.23\r\n scale = roundNumber(scale, 2);\r\n\r\n // Find chart size and scale\r\n return {\r\n height:\r\n options.export?.height ||\r\n exporting?.sourceHeight ||\r\n chart?.height ||\r\n globalOptions?.exporting?.sourceHeight ||\r\n globalOptions?.chart?.height ||\r\n options.export?.defaultHeight ||\r\n 400,\r\n width:\r\n options.export?.width ||\r\n exporting?.sourceWidth ||\r\n chart?.width ||\r\n globalOptions?.exporting?.sourceWidth ||\r\n globalOptions?.chart?.width ||\r\n options.export?.defaultWidth ||\r\n 600,\r\n scale\r\n };\r\n};\r\n\r\n/**\r\n * Function for final options preparation before export.\r\n *\r\n * @param {object} options - All options object.\r\n * @param {object} chartJson - Chart JSON.\r\n * @param {function} endCallback - The end callback.\r\n * @param {string} svg - The SVG representation.\r\n */\r\nconst doExport = (options, chartJson, endCallback, svg) => {\r\n let { export: exportOptions, customCode: customCodeOptions } = options;\r\n\r\n const allowCodeExecutionScoped =\r\n typeof customCodeOptions.allowCodeExecution === 'boolean'\r\n ? customCodeOptions.allowCodeExecution\r\n : allowCodeExecution;\r\n\r\n if (!customCodeOptions) {\r\n customCodeOptions = options.customCode = {};\r\n } else if (allowCodeExecutionScoped) {\r\n if (typeof options.customCode.resources === 'string') {\r\n // Process resources\r\n options.customCode.resources = handleResources(\r\n options.customCode.resources,\r\n toBoolean(options.customCode.allowFileResources)\r\n );\r\n } else if (!options.customCode.resources) {\r\n try {\r\n const resources = readFileSync('resources.json', 'utf8');\r\n options.customCode.resources = handleResources(\r\n resources,\r\n toBoolean(options.customCode.allowFileResources)\r\n );\r\n } catch (err) {\r\n log(3, `[chart] The default resources.json file not found.`);\r\n }\r\n }\r\n }\r\n\r\n // If the allowCodeExecution flag isn't set, we should refuse the usage\r\n // of callback, resources, and custom code. Additionally, the worker will\r\n // refuse to run arbitrary JavaScript. Prioritized should be the scoped\r\n // option, then we should take a look at the overall pool option.\r\n if (!allowCodeExecutionScoped && customCodeOptions) {\r\n if (\r\n customCodeOptions.callback ||\r\n customCodeOptions.resources ||\r\n customCodeOptions.customCode\r\n ) {\r\n // Send back a friendly message saying that the exporter does not support\r\n // these settings.\r\n return (\r\n endCallback &&\r\n endCallback(false, {\r\n error: true,\r\n message: clearText(\r\n `The callback, resources and customCode have been disabled for this\r\n server.`\r\n )\r\n })\r\n );\r\n }\r\n\r\n // Reset all additional custom code\r\n customCodeOptions.callback = false;\r\n customCodeOptions.resources = false;\r\n customCodeOptions.customCode = false;\r\n }\r\n\r\n // Clean properties to keep it lean and mean\r\n if (chartJson) {\r\n chartJson.chart = chartJson.chart || {};\r\n chartJson.exporting = chartJson.exporting || {};\r\n chartJson.exporting.enabled = false;\r\n }\r\n\r\n exportOptions.constr = exportOptions.constr || 'chart';\r\n exportOptions.type = fixType(exportOptions.type, exportOptions.outfile);\r\n if (exportOptions.type === 'svg') {\r\n exportOptions.width = false;\r\n }\r\n\r\n // Prepare global and theme options\r\n ['globalOptions', 'themeOptions'].forEach((optionsName) => {\r\n try {\r\n if (exportOptions && exportOptions[optionsName]) {\r\n if (\r\n typeof exportOptions[optionsName] === 'string' &&\r\n exportOptions[optionsName].endsWith('.json')\r\n ) {\r\n exportOptions[optionsName] = isCorrectJSON(\r\n readFileSync(exportOptions[optionsName], 'utf8'),\r\n true\r\n );\r\n } else {\r\n exportOptions[optionsName] = isCorrectJSON(\r\n exportOptions[optionsName],\r\n true\r\n );\r\n }\r\n }\r\n } catch (error) {\r\n exportOptions[optionsName] = {};\r\n log(1, `[chart] The ${optionsName} not found.`);\r\n }\r\n });\r\n\r\n // Prepare customCode\r\n if (customCodeOptions.allowCodeExecution) {\r\n customCodeOptions.customCode = wrapAround(\r\n customCodeOptions.customCode,\r\n customCodeOptions.allowFileResources\r\n );\r\n }\r\n\r\n // Get the callback\r\n if (\r\n customCodeOptions &&\r\n customCodeOptions.callback &&\r\n customCodeOptions.callback?.indexOf('{') < 0\r\n ) {\r\n // The allowFileResources is always set to false for HTTP requests to avoid\r\n // injecting arbitrary files from the fs\r\n if (customCodeOptions.allowFileResources) {\r\n try {\r\n customCodeOptions.callback = readFileSync(\r\n customCodeOptions.callback,\r\n 'utf8'\r\n );\r\n } catch (error) {\r\n log(2, `[chart] Error loading callback: ${error}.`);\r\n customCodeOptions.callback = false;\r\n }\r\n } else {\r\n customCodeOptions.callback = false;\r\n }\r\n }\r\n\r\n // Size search\r\n options.export = {\r\n ...options.export,\r\n ...findChartSize(options)\r\n };\r\n\r\n // Post the work to the pool\r\n postWork(exportOptions.strInj || chartJson || svg, options)\r\n .then((result) => endCallback(result))\r\n .catch((error) => {\r\n log(0, '[chart] When posting work:', error);\r\n return endCallback(false, error);\r\n });\r\n};\r\n\r\n/**\r\n * Function for straight injecting the code.\r\n * Dangerous and must be used deliberately by someone who sets up a server\r\n * (see --allowCodeExecution).\r\n *\r\n * @param {object} options - All options object.\r\n * @param {function} endCallback - The function to call when exporting is done.\r\n */\r\nconst doStraightInject = (options, endCallback) => {\r\n try {\r\n let strInj;\r\n let instr = options.export.instr || options.export.options;\r\n\r\n if (typeof instr !== 'string') {\r\n // Try to stringify options\r\n strInj = instr = optionsStringify(\r\n instr,\r\n options.customCode?.allowCodeExecution\r\n );\r\n }\r\n strInj = instr.replaceAll(/\\t|\\n|\\r/g, '').trim();\r\n\r\n // Get rid of the ;\r\n if (strInj[strInj.length - 1] === ';') {\r\n strInj = strInj.substring(0, strInj.length - 1);\r\n }\r\n\r\n // Save as stright inject string\r\n options.export.strInj = strInj;\r\n return doExport(options, false, endCallback);\r\n } catch (error) {\r\n const message = clearText(\r\n `Malformed input detected for ${options.export?.requestId || '?'}:\r\n Please make sure that your JSON/JavaScript options\r\n are sent using the \"options\" attribute, and that if you're using\r\n SVG, it is unescaped.`\r\n );\r\n\r\n log(1, message);\r\n return (\r\n endCallback &&\r\n endCallback(\r\n false,\r\n JSON.stringify({\r\n error: true,\r\n message\r\n })\r\n )\r\n );\r\n }\r\n};\r\n\r\n/**\r\n * Prepares an input before exporting.\r\n *\r\n * @param {string} stringToExport - String representation of SVG/export options.\r\n * @param {object} options - All options object.\r\n * @param {function} endCallback - The function to call when exporting is done.\r\n */\r\nconst exportAsString = (stringToExport, options, endCallback) => {\r\n const { allowCodeExecution } = options.customCode;\r\n\r\n // Check if it is SVG\r\n if (\r\n stringToExport.indexOf('= 0 ||\r\n stringToExport.indexOf('= 0\r\n ) {\r\n log(4, '[chart] Parsing input as SVG.');\r\n return doExport(options, false, endCallback, stringToExport);\r\n }\r\n\r\n try {\r\n // Try to parse to JSON and call the doExport function\r\n const chartJSON = JSON.parse(stringToExport.replaceAll(/\\t|\\n|\\r/g, ' '));\r\n\r\n // If a correct JSON, do the export\r\n return doExport(options, chartJSON, endCallback);\r\n } catch (error) {\r\n // Not a valid JSON\r\n if (toBoolean(allowCodeExecution)) {\r\n return doStraightInject(options, endCallback);\r\n } else {\r\n // Do not allow straight injection without the allowCodeExecution flag\r\n return (\r\n endCallback &&\r\n endCallback(false, {\r\n error: true,\r\n message: clearText(\r\n `Only JSON configurations and SVG is allowed for this server. If\r\n this is your server, JavaScript exporting can be enabled by starting\r\n the server with the --allowCodeExecution flag.`\r\n )\r\n })\r\n );\r\n }\r\n }\r\n};\r\n\r\nexport const getAllowCodeExecution = () => allowCodeExecution;\r\n\r\nexport const setAllowCodeExecution = (value) => {\r\n allowCodeExecution = toBoolean(value);\r\n};\r\n\r\n/**\r\n * Starts an exporting process\r\n *\r\n * @param {object} settings - Settings for export.\r\n * @param {function} endCallback - The function to call when exporting is done.\r\n */\r\nexport default {\r\n batchExport,\r\n singleExport,\r\n getAllowCodeExecution,\r\n setAllowCodeExecution,\r\n startExport,\r\n findChartSize\r\n};\r\n","/*******************************************************************************\r\n\r\nHighcharts Export Server\r\n\r\nCopyright (c) 2016-2023, Highsoft\r\n\r\nLicenced under the MIT licence.\r\n\r\nAdditionally a valid Highcharts license is required for use.\r\n\r\nSee LICENSE file in root for details.\r\n\r\n*******************************************************************************/\r\n\r\nimport { v4 as uuid } from 'uuid';\r\n\r\nimport { getAllowCodeExecution, startExport } from '../../chart.js';\r\nimport { getOptions, mergeConfigOptions } from '../../config.js';\r\nimport { log } from '../../logger.js';\r\nimport {\r\n clearText,\r\n fixType,\r\n isCorrectJSON,\r\n isPrivateRangeUrlFound,\r\n optionsStringify,\r\n measureTime\r\n} from '../../utils.js';\r\n\r\n// Reversed MIME types\r\nconst reversedMime = {\r\n png: 'image/png',\r\n jpeg: 'image/jpeg',\r\n gif: 'image/gif',\r\n pdf: 'application/pdf',\r\n svg: 'image/svg+xml'\r\n};\r\n\r\n// The requests counter\r\nlet requestsCounter = 0;\r\n\r\nconst benchmark = false;\r\n\r\n// The array of callbacks to call before a request\r\nconst beforeRequest = [];\r\n\r\n// The array of callbacks to call after a request\r\nconst afterRequest = [];\r\n\r\n/**\r\n * Calls callbacks.\r\n *\r\n * @param {Array} callbacks - An array of callbacks.\r\n * @param {object} request - The request.\r\n * @param {object} response - The response.\r\n * @param {object} data - The data to send to callbacks.\r\n * @return {object} - The result from a callback.\r\n */\r\nconst doCallbacks = (callbacks, request, response, data) => {\r\n let result = true;\r\n const { id, uniqueId, type, body } = data;\r\n\r\n callbacks.some((callback) => {\r\n if (callback) {\r\n let callResponse = callback(request, response, id, uniqueId, type, body);\r\n\r\n if (callResponse !== undefined && callResponse !== true) {\r\n result = callResponse;\r\n }\r\n\r\n return true;\r\n }\r\n });\r\n\r\n return result;\r\n};\r\n\r\n/**\r\n * Handles an export.\r\n *\r\n * @param {object} request - The request.\r\n * @param {object} response - The response.\r\n */\r\nconst exportHandler = (request, response) => {\r\n // Start counting time\r\n const stopCounter = measureTime();\r\n\r\n // Get the current server's general options\r\n const defaultOptions = getOptions();\r\n\r\n // Init default options\r\n if (benchmark) {\r\n console.log('Init default options:', stopCounter(), 'ms.');\r\n }\r\n\r\n const body = request.body;\r\n const id = ++requestsCounter;\r\n const uniqueId = uuid().replace(/-/g, '');\r\n let type = fixType(body.type);\r\n\r\n // Fix type\r\n if (benchmark) {\r\n console.log('Fix type:', stopCounter(), 'ms.');\r\n }\r\n\r\n // Throw 'Bad Request' if there's no body\r\n if (!body) {\r\n return response.status(400).send(\r\n clearText(\r\n `Body is required. Sending a body? Make sure your Content-type header\r\n is correct. Accepted is application/json and multipart/form-data.`\r\n )\r\n );\r\n }\r\n\r\n // All of the below can be used\r\n let instr = isCorrectJSON(body.infile || body.options || body.data);\r\n\r\n // Is correct JSON\r\n if (benchmark) {\r\n console.log('Is correct JSON:', stopCounter(), 'ms.');\r\n }\r\n\r\n // Throw 'Bad Request' if there's no JSON or SVG to export\r\n if (!instr && !body.svg) {\r\n log(\r\n 2,\r\n clearText(\r\n `Request ${uniqueId} from ${\r\n request.headers['x-forwarded-for'] || request.connection.remoteAddress\r\n } was incorrect. Check your payload.`\r\n )\r\n );\r\n\r\n return response.status(400).send(\r\n clearText(\r\n `No correct chart data found. Please make sure you are using\r\n application/json or multipart/form-data headers, and that the chart\r\n data is in the 'infile', 'options' or 'data' attribute if sending\r\n JSON or in the 'svg' if sending SVG.`\r\n )\r\n );\r\n }\r\n\r\n let callResponse = false;\r\n\r\n // Call the before request functions\r\n callResponse = doCallbacks(beforeRequest, request, response, {\r\n id,\r\n uniqueId,\r\n type,\r\n body\r\n });\r\n\r\n // Do callbacks\r\n if (benchmark) {\r\n console.log('Do callbacks:', stopCounter(), 'ms.');\r\n }\r\n\r\n // Block the request if one of a callbacks failed\r\n if (callResponse !== true) {\r\n return response.send(callResponse);\r\n }\r\n\r\n let connectionAborted = false;\r\n\r\n // In case the connection is closed, force to abort further actions\r\n request.socket.on('close', () => {\r\n connectionAborted = true;\r\n });\r\n\r\n log(4, `[export] Got an incoming HTTP request ${uniqueId}.`);\r\n\r\n body.constr = (typeof body.constr === 'string' && body.constr) || 'chart';\r\n\r\n // Gather and organize options from the payload\r\n const requestOptions = {\r\n export: {\r\n instr,\r\n type,\r\n constr: body.constr[0].toLowerCase() + body.constr.substr(1),\r\n height: body.height,\r\n width: body.width,\r\n scale: body.scale || defaultOptions.export.scale,\r\n globalOptions: isCorrectJSON(body.globalOptions, true),\r\n themeOptions: isCorrectJSON(body.themeOptions, true)\r\n },\r\n customCode: {\r\n allowCodeExecution: getAllowCodeExecution(),\r\n allowFileResources: false,\r\n resources: isCorrectJSON(body.resources, true),\r\n callback: body.callback,\r\n customCode: body.customCode\r\n }\r\n };\r\n\r\n // Organize options\r\n if (benchmark) {\r\n console.log('Organize options:', stopCounter(), 'ms.');\r\n }\r\n\r\n if (instr) {\r\n // Stringify JSON with options\r\n requestOptions.export.instr = optionsStringify(\r\n instr,\r\n requestOptions.customCode.allowCodeExecution\r\n );\r\n\r\n // Stringify JSON with options\r\n if (benchmark) {\r\n console.log('Stringify JSON with options:', stopCounter(), 'ms.');\r\n }\r\n }\r\n\r\n // Merge the request options into default ones\r\n const options = mergeConfigOptions(defaultOptions, requestOptions);\r\n\r\n // Merge config options\r\n if (benchmark) {\r\n console.log('Merge config options:', stopCounter(), 'ms.');\r\n }\r\n\r\n // Save the JSON if exists\r\n options.export.options = instr;\r\n\r\n // Lastly, add the server specific arguments into options as payload\r\n options.payload = {\r\n svg: body.svg || false,\r\n b64: body.b64 || false,\r\n dataOptions: isCorrectJSON(body.dataOptions, true),\r\n noDownload: body.noDownload || false,\r\n requestId: uniqueId\r\n };\r\n\r\n // Setting payload\r\n if (benchmark) {\r\n console.log('Setting payload:', stopCounter(), 'ms.');\r\n }\r\n\r\n // Test xlink:href elements from payload's SVG\r\n if (body.svg && isPrivateRangeUrlFound(options.payload.svg)) {\r\n return response\r\n .status(400)\r\n .send(\r\n 'SVG potentially contain at least one forbidden URL in xlink:href element.'\r\n );\r\n }\r\n\r\n // Check URL range\r\n if (benchmark) {\r\n console.log('Check URL range:', stopCounter(), 'ms.');\r\n }\r\n\r\n // Start the export process\r\n startExport(options, (info, error) => {\r\n // Remove the close event from the socket\r\n request.socket.removeAllListeners('close');\r\n\r\n // After Puppeteer exporting\r\n if (benchmark) {\r\n console.log('After Puppeteer exporting:', stopCounter(), 'ms.', '\\n');\r\n }\r\n\r\n // If the connection was closed, do nothing\r\n if (connectionAborted) {\r\n return log(\r\n 3,\r\n clearText(\r\n `[export] The client closed the connection before the chart was done\r\n processing.`\r\n )\r\n );\r\n }\r\n\r\n // If error, return it\r\n if (error) {\r\n log(\r\n 1,\r\n clearText(\r\n `[export] Work: ${uniqueId} could not be completed, sending:\r\n ${error}`\r\n )\r\n );\r\n return response.status(400).send(error.message);\r\n }\r\n\r\n // If data is missing, return the error\r\n if (!info || !info.data) {\r\n log(\r\n 1,\r\n clearText(\r\n `[export] Unexpected return from chart generation, please check your\r\n data Request: ${uniqueId} is ${info.data}.`\r\n )\r\n );\r\n return response\r\n .status(400)\r\n .send(\r\n 'Unexpected return from chart generation, please check your data.'\r\n );\r\n }\r\n\r\n // Get the type from options\r\n type = info.options.export.type;\r\n\r\n // The after request callbacks\r\n doCallbacks(afterRequest, request, response, { id, body: info.data });\r\n\r\n if (info.data) {\r\n // If only base64 is required, return it\r\n if (body.b64) {\r\n // Check if it is already base64 or a raw SVG\r\n if (type === 'pdf') {\r\n return response.send(\r\n Buffer.from(info.data, 'utf8').toString('base64')\r\n );\r\n }\r\n return response.send(info.data);\r\n }\r\n\r\n // Set correct content type\r\n response.header('Content-Type', reversedMime[type] || 'image/png');\r\n\r\n // Decide whether to download or not chart file\r\n if (!body.noDownload) {\r\n response.attachment(\r\n `${request.params.filename || request.body.filename || 'chart'}.${\r\n type || 'png'\r\n }`\r\n );\r\n }\r\n\r\n // If SVG, return plain content\r\n return type === 'svg'\r\n ? response.send(info.data)\r\n : response.send(Buffer.from(info.data, 'base64'));\r\n }\r\n });\r\n};\r\n\r\nexport default (app) => {\r\n app.post('/', exportHandler);\r\n app.post('/:filename', exportHandler);\r\n};\r\n","/*******************************************************************************\r\n\r\nHighcharts Export Server\r\n\r\nCopyright (c) 2016-2023, Highsoft\r\n\r\nLicenced under the MIT licence.\r\n\r\nAdditionally a valid Highcharts license is required for use.\r\n\r\nSee LICENSE file in root for details.\r\n\r\n*******************************************************************************/\r\n\r\nimport { promises as fsPromises } from 'fs';\r\nimport { posix } from 'path';\r\n\r\nimport bodyParser from 'body-parser';\r\nimport cors from 'cors';\r\nimport express from 'express';\r\nimport multer from 'multer';\r\nimport http from 'http';\r\nimport https from 'https';\r\n\r\nimport { log } from '../logger.js';\r\nimport rateLimit from './rate_limit.js';\r\nimport { __dirname } from '../utils.js';\r\n\r\nimport healthRoute from './routes/health.js';\r\nimport exportRoutes from './routes/export.js';\r\nimport vSwitchRoute from './routes/change_hc_version.js';\r\nimport uiRoute from './routes/ui.js';\r\n\r\n// Create express app\r\nconst app = express();\r\n\r\n// Disable the X-Powered-By header\r\napp.disable('x-powered-by');\r\n\r\n// Enable CORS support\r\napp.use(cors());\r\n\r\n// Enable parsing of form data (files) with Multer package\r\nconst storage = multer.memoryStorage();\r\nconst upload = multer({\r\n storage,\r\n limits: {\r\n fieldsSize: '50MB'\r\n }\r\n});\r\n\r\napp.use(upload.any());\r\n\r\n// Enable body parser\r\napp.use(bodyParser.json({ limit: '50mb' }));\r\napp.use(bodyParser.urlencoded({ extended: true, limit: '50mb' }));\r\napp.use(bodyParser.urlencoded({ extended: false, limit: '50mb' }));\r\n\r\n/**\r\n * Error handler function.\r\n *\r\n * @param {object} error - An error object.\r\n * @return {string} - An error message.\r\n */\r\nconst errorHandler = (error) => log(1, `[server] Socket error: ${error}`);\r\n\r\n/**\r\n * Attaches error handlers for a server.\r\n *\r\n * @param {object} server - The http/https server.\r\n */\r\nconst attachErrorHandlers = (server) => {\r\n server.on('clientError', errorHandler);\r\n server.on('error', errorHandler);\r\n server.on('connection', (socket) =>\r\n socket.on('error', (error) => errorHandler(error, socket))\r\n );\r\n};\r\n\r\nexport const startServer = async (serverConfig) => {\r\n // Stop if not enabled\r\n if (!serverConfig.enable) {\r\n return false;\r\n }\r\n\r\n // // Get the pool\r\n // const pool = getPool();\r\n\r\n // // Try to create browser instance before starting the server\r\n // const resource = await pool.acquire();\r\n\r\n // // If not found, throw an error\r\n // if (!resource.browser) {\r\n // log(1, `[server] Could not acquire browser instance.`);\r\n // process.exit(1);\r\n // }\r\n\r\n // // Release the resource\r\n // pool.release(resource);\r\n\r\n // Listen HTTP server\r\n if (!serverConfig.ssl.enable && !serverConfig.ssl.force) {\r\n // Main server instance (HTTP)\r\n const httpServer = http.createServer(app);\r\n // Attach error handlers and listen to the server\r\n attachErrorHandlers(httpServer);\r\n // Listen\r\n httpServer.listen(serverConfig.port, serverConfig.host);\r\n\r\n log(\r\n 3,\r\n `[server] Started HTTP server on ${serverConfig.host}:${serverConfig.port}.`\r\n );\r\n }\r\n\r\n // Listen HTTPS server\r\n if (serverConfig.ssl.enable) {\r\n // Set up an SSL server also\r\n let key, cert;\r\n\r\n try {\r\n // Get the SSL key\r\n key = await fsPromises.readFile(\r\n posix.join(serverConfig.ssl.certPath, 'server.key'),\r\n 'utf8'\r\n );\r\n\r\n // Get the SSL certificate\r\n cert = await fsPromises.readFile(\r\n posix.join(serverConfig.ssl.certPath, 'server.crt'),\r\n 'utf8'\r\n );\r\n } catch (error) {\r\n log(\r\n 1,\r\n `[server] Unable to load key/certificate from ${serverConfig.ssl.certPath}.`\r\n );\r\n }\r\n\r\n if (key && cert) {\r\n // Main server instance (HTTPS)\r\n const httpsServer = https.createServer(app);\r\n // Attach error handlers and listen to the server\r\n attachErrorHandlers(httpsServer);\r\n // Listen\r\n httpsServer.listen(serverConfig.ssl.port, serverConfig.host);\r\n\r\n log(\r\n 3,\r\n `[server] Started HTTPS server on ${serverConfig.host}:${serverConfig.ssl.port}.`\r\n );\r\n }\r\n }\r\n\r\n // Enable the rate limiter if config says so\r\n if (\r\n serverConfig.rateLimiting &&\r\n serverConfig.rateLimiting.enable &&\r\n ![0, NaN].includes(serverConfig.rateLimiting.maxRequests)\r\n ) {\r\n rateLimit(app, serverConfig.rateLimiting);\r\n }\r\n\r\n // Set up static folder's route\r\n app.use(express.static(posix.join(__dirname, 'public')));\r\n\r\n // Set up routes\r\n healthRoute(app);\r\n exportRoutes(app);\r\n uiRoute(app);\r\n vSwitchRoute(app);\r\n};\r\n\r\n/**\r\n * Returns the express instance.\r\n */\r\nexport const getExpress = () => {\r\n return express;\r\n};\r\n\r\n/**\r\n * Returns the app instance.\r\n */\r\nexport const getApp = () => {\r\n return app;\r\n};\r\n\r\n/**\r\n * Adds a middleware to the server.\r\n *\r\n * @param {object} path - An endpoint path to add middlewares to.\r\n * @param {Array} middlewares - An unlimited number of middlewares to use\r\n * against the specific endpoint.\r\n */\r\nexport const use = (path, ...middlewares) => {\r\n app.use(path, ...middlewares);\r\n};\r\n\r\n/**\r\n * Adds a get route to the server.\r\n *\r\n * @param {object} path - An endpoint path to add middlewares to.\r\n * @param {Array} middlewares - An unlimited number of middlewares to use\r\n * against the specific endpoint for GET method.\r\n */\r\nexport const get = (path, ...middlewares) => {\r\n app.get(path, ...middlewares);\r\n};\r\n\r\n/**\r\n * Adds a post route to the server.\r\n *\r\n * @param {object} path - An endpoint path to add middlewares to.\r\n * @param {Array} middlewares - An unlimited number of middlewares to use\r\n * against the specific endpoint for POST method.\r\n */\r\nexport const post = (path, ...middlewares) => {\r\n app.post(path, ...middlewares);\r\n};\r\n\r\n/**\r\n * Forcefully enables rate limiting.\r\n *\r\n * @param {object} limitConfig - The options object for the rate limiter\r\n * configuration.\r\n */\r\nexport const enableRateLimiting = (limitConfig) => {\r\n return rateLimit(app, limitConfig);\r\n};\r\n\r\nexport default {\r\n startServer,\r\n getExpress,\r\n getApp,\r\n use,\r\n get,\r\n post,\r\n enableRateLimiting\r\n};\r\n","/*******************************************************************************\r\n\r\nHighcharts Export Server\r\n\r\nCopyright (c) 2016-2023, Highsoft\r\n\r\nLicenced under the MIT licence.\r\n\r\nAdditionally a valid Highcharts license is required for use.\r\n\r\nSee LICENSE file in root for details.\r\n\r\n*******************************************************************************/\r\n\r\nimport { join } from 'path';\r\n\r\nimport { __dirname } from '../../utils.js';\r\n/**\r\n * Adds the / route for a UI when enabled for the export server\r\n */\r\nexport default (app) =>\r\n !app\r\n ? false\r\n : app.get('/', (request, response) => {\r\n response.sendFile(join(__dirname, 'public', 'index.html'));\r\n });\r\n","/*******************************************************************************\r\n\r\nHighcharts Export Server\r\n\r\nCopyright (c) 2016-2023, Highsoft\r\n\r\nLicenced under the MIT licence.\r\n\r\nAdditionally a valid Highcharts license is required for use.\r\n\r\nSee LICENSE file in root for details.\r\n\r\n*******************************************************************************/\r\n\r\nimport cache from '../../cache.js';\r\n\r\n/**\r\n * Adds a route that can be used to change the HC version on the server\r\n * TODO: Add auth token and connect to API\r\n */\r\nexport default (app) =>\r\n !app\r\n ? false\r\n : app.post('/change_hc_version/:newVersion', async (request, response) => {\r\n const ctoken = process.env.HIGHCHARTS_ADMIN_TOKEN;\r\n\r\n if (!ctoken || !ctoken.length) {\r\n return response.send({\r\n error: true,\r\n message:\r\n 'Server not configured to do run-time version changes: HIGHCHARTS_ADMIN_TOKEN not set'\r\n });\r\n }\r\n\r\n const token = request.get('hc-auth');\r\n\r\n if (!token || token !== ctoken) {\r\n return response.send({\r\n error: true,\r\n message: 'Invalid or missing token: set token in the hc-auth header'\r\n });\r\n }\r\n\r\n const newVersion = request.params.newVersion;\r\n\r\n if (newVersion) {\r\n try {\r\n // eslint-disable-next-line import/no-named-as-default-member\r\n await cache.updateVersion(newVersion);\r\n } catch (e) {\r\n response.send({\r\n error: true,\r\n message: e\r\n });\r\n }\r\n\r\n response.send({\r\n version: cache.version()\r\n });\r\n } else {\r\n response.send({\r\n error: true,\r\n message: 'No new version supplied'\r\n });\r\n }\r\n });\r\n","/*******************************************************************************\r\n\r\nHighcharts Export Server\r\n\r\nCopyright (c) 2016-2023, Highsoft\r\n\r\nLicenced under the MIT licence.\r\n\r\nAdditionally a valid Highcharts license is required for use.\r\n\r\nSee LICENSE file in root for details.\r\n\r\n*******************************************************************************/\r\n\r\n// Add the main directory in the global object\r\nimport 'colors';\r\n\r\nimport server, { startServer } from './server/server.js';\r\nimport {\r\n setAllowCodeExecution,\r\n batchExport,\r\n singleExport,\r\n startExport\r\n} from './chart.js';\r\nimport { mapToNewConfig, setOptions } from './config.js';\r\nimport { log, setLogLevel, enableFileLogging } from './logger.js';\r\nimport { killPool, init } from './pool.js';\r\nimport { checkCache } from './cache.js';\r\n\r\nexport default {\r\n log,\r\n mapToNewConfig,\r\n setOptions,\r\n singleExport,\r\n startExport,\r\n batchExport,\r\n server,\r\n startServer,\r\n killPool,\r\n initPool: async (options = {}) => {\r\n // Set the allowCodeExecution per export module scope\r\n setAllowCodeExecution(\r\n options.customCode && options.customCode.allowCodeExecution\r\n );\r\n\r\n // Set the log level\r\n setLogLevel(options.logging && parseInt(options.logging.level));\r\n\r\n // Set the log file path and name\r\n if (options.logging && options.logging.dest) {\r\n enableFileLogging(\r\n options.logging.dest,\r\n options.logging.file || 'highcharts-export-server.log'\r\n );\r\n }\r\n\r\n // Check if cache needs to be updated\r\n await checkCache(options.highcharts || { version: 'latest' });\r\n\r\n // Init the pool\r\n await init({\r\n pool: options.pool || {\r\n minWorkers: 1,\r\n maxWorkers: 1\r\n },\r\n puppeteerArgs: options.puppeteer?.args || []\r\n });\r\n\r\n // Return updated options\r\n return options;\r\n }\r\n};\r\n"],"names":["dotenv","config","defaultConfig","puppeteer","args","value","type","description","highcharts","version","envLink","cdnURL","coreScripts","modules","indicators","scripts","forceFetch","export","infile","instr","options","outfile","constr","defaultHeight","defaultWidth","defaultScale","height","width","scale","globalOptions","themeOptions","batch","customCode","allowCodeExecution","allowFileResources","callback","resources","loadConfig","createConfig","server","enable","cliName","host","port","ssl","force","certPath","rateLimiting","maxRequests","window","delay","trustProxy","skipKey","skipToken","pool","minWorkers","maxWorkers","workLimit","acquireTimeout","createTimeout","destroyTimeout","idleTimeout","rasterizationTimeout","createRetryInterval","reaperInterval","benchmarking","listenToProcessExits","logging","level","file","dest","ui","route","other","noLogo","payload","join","absoluteProps","nestedArgs","createNestedArgs","obj","propChain","Object","keys","forEach","k","includes","entry","substring","toConsole","toFile","pathCreated","levelsDesc","title","color","listeners","key","option","entries","log","newLevel","texts","length","prefix","Date","toString","split","trim","fn","existsSync","mkdirSync","appendFile","concat","error","console","apply","undefined","__dirname","fileURLToPath","URL","url","clearText","text","rule","replacer","replaceAll","fixType","formats","outType","pop","find","t","handleResources","allowedProps","handledResources","correctResources","endsWith","isCorrectJSON","readFileSync","notice","files","propName","map","item","data","parsedData","JSON","parse","stringify","deepCopy","copy","Array","isArray","prototype","hasOwnProperty","call","optionsStringify","allowFunctions","name","startsWith","printUsage","bold","yellow","cycleCategories","categories","descName","green","i","blue","category","toUpperCase","red","toBoolean","wrapAround","replace","rateLimit","app","limitConfig","msg","rateOptions","max","limiter","windowMs","delayMs","handler","request","response","format","json","status","send","message","default","skip","query","access_token","use","async","fetch","requestOptions","Promise","resolve","reject","protocol","https","http","getProtocol","get","res","on","chunk","cachePath","cache","activeManifest","sources","hcVersion","appliedConfig","extractVersion","substr","indexOf","fetchScript","script","proxyAgent","agent","timeout","process","env","statusCode","updateCache","sourcePath","customScripts","allScripts","c","m","proxyHost","proxyPort","HttpsProxyAgent","fetchedModules","all","writeFileSync","checkCache","manifestPath","requestUpdate","manifest","moduleMap","numberOfModules","some","moduleName","newManifest","saveConfigToManifest","cache$1","newVersion","assign","RANDOM_PID","randomBytes","PUPPETEER_DIR","path","minimalArgs","template","fs","browser","setPageContent","page","setContent","addScriptTag","evaluate","setupHighcharts","err","$eval","element","errorMessage","_displayErrors","innerHTML","newPage","close","connected","__basedir","setAsConfig","chart","triggerExport","puppeteerExport","injectedResources","clearInjected","dispose","scriptsToRemove","document","getElementsByTagName","stylesToRemove","linksToRemove","remove","exportBench","exportOptions","requestAnimationFrame","displayErrors","debugger","d","svgBench","isSVG","setPageBench","svgTemplate","strInj","setContentBench","resBench","js","push","content","isLocal","cssBench","css","cssImports","match","cssImportPath","addStyleTag","size","chartHeight","baseVal","chartWidth","parseFloat","Highcharts","charts","vpBench","viewportHeight","Math","ceil","viewportWidth","setViewport","deviceScaleFactor","zoomCallback","body","style","zoom","margin","x","y","getBoundingClientRect","trunc","getClipRegion","round","expBenchmark","outerHTML","createSVG","encoding","clip","race","screenshot","omitBackground","setTimeout","Error","createImage","pdf","createPDF","oldCharts","oldChart","destroy","shift","puppeteerArgs","performedExports","exportAttempts","timeSpent","droppedExports","spentAverage","poolConfig","factory","create","id","uuid","s","getTime","browserNewPage","isClosed","workCount","random","validate","workerHandle","goto","clearPage","logLevel","init","allArgs","tryCount","open","launch","headless","userDataDir","e","createBrowser","killPool","code","exit","Pool","min","acquireTimeoutMillis","createTimeoutMillis","destroyTimeoutMillis","idleTimeoutMillis","createRetryIntervalMillis","reapIntervalMillis","propagateCreateError","eventId","resource","initialResources","acquire","promise","release","destroyed","postWork","fail","getPoolInfo","workStart","result","exportTime","available","borrowed","pending","spareResourceCapacity","pool$1","packageVersion","npm_package_version","serverStartTime","generalOptions","getOptions","mergeConfigOptions","newOptions","mergedOptions","updateDefaultConfig","configObj","customObj","customValue","numEnvVal","el","initOptions","items","startExport","settings","endCallback","svg","initExportSettings","exportAsString","readFile","doStraightInject","doExport","findChartSize","exporting","precision","multiplier","pow","roundNumber","sourceHeight","sourceWidth","chartJson","customCodeOptions","allowCodeExecutionScoped","enabled","optionsName","then","catch","requestId","stringToExport","chartJSON","reversedMime","png","jpeg","gif","requestsCounter","beforeRequest","afterRequest","doCallbacks","callbacks","uniqueId","callResponse","exportHandler","start","hrtime","bigint","measureTime","defaultOptions","headers","connection","remoteAddress","connectionAborted","socket","toLowerCase","b64","dataOptions","noDownload","ipRegEx","info","removeAllListeners","Buffer","from","header","attachment","params","filename","express","disable","cors","storage","multer","memoryStorage","upload","limits","fieldsSize","any","bodyParser","limit","urlencoded","extended","errorHandler","attachErrorHandlers","startServer","serverConfig","httpServer","createServer","listen","cert","fsPromises","posix","httpsServer","NaN","static","bootTime","uptime","floor","highchartsVersion","averageProcessingTime","failedExports","sucessRatio","healthRoute","post","exportRoutes","sendFile","uiRoute","ctoken","HIGHCHARTS_ADMIN_TOKEN","token","vSwitchRoute","getExpress","getApp","middlewares","enableRateLimiting","index","mapToNewConfig","oldOptions","propertiesChain","reduce","prop","setOptions","userOptions","configIndex","findIndex","arg","fileName","loadConfigFile","pairArgumentValue","singleExport","batchExport","batchFunctions","pair","initPool","parseInt","logDest","logFile","enableFileLogging"],"mappings":"snBAiBAA,EAAOC,SAIA,MAAMC,EAAgB,CAC3BC,UAAW,CACTC,KAAM,CACJC,MAAO,GACPC,KAAM,WACNC,YAAa,6CAGjBC,WAAY,CACVC,QAAS,CACPJ,MAAO,SACPK,QAAS,qBACTJ,KAAM,SACNC,YAAa,8BAEfI,OAAQ,CACNN,MAAO,+BACPK,QAAS,iBACTJ,KAAM,SACNC,YAAa,6CAEfK,YAAa,CACXF,QAAS,0BACTL,MAAO,CAAC,aAAc,kBAAmB,iBACzCC,KAAM,WACNC,YAAa,qCAEfM,QAAS,CACPH,QAAS,qBACTL,MAAO,CACL,QACA,MACA,QACA,YACA,cACA,uBACA,gBACA,uBACA,eACA,QACA,OACA,mBACA,eACA,cACA,UACA,UACA,WACA,UACA,YACA,cACA,YACA,sBACA,SACA,SACA,WACA,YACA,eACA,SACA,eACA,YACA,kBACA,SACA,cACA,mBACA,eACA,cACA,eACA,cACA,cACA,WACA,eACA,WACA,SACA,OACA,WACA,YACA,SACA,qBACA,aACA,WACA,WACA,WACA,WACA,eACA,UACA,kBACA,oBACA,cAEFC,KAAM,WACNC,YAAa,gCAEfO,WAAY,CACVJ,QAAS,wBACTL,MAAO,CAAC,kBACRC,KAAM,WACNC,YAAa,mCAEfQ,QAAS,CACPV,MAAO,CACL,wEACA,kGAEFC,KAAM,WACNC,YACE,qEAEJS,WAAY,CACVN,QAAS,yBACTL,OAAO,EACPC,KAAM,UACNC,YACE,oEAGNU,OAAQ,CACNC,OAAQ,CACNb,OAAO,EACPC,KAAM,SACNC,YACE,8FAEJY,MAAO,CACLd,OAAO,EACPC,KAAM,SACNC,YACE,iFAEJa,QAAS,CACPf,OAAO,EACPC,KAAM,SACNC,YAAa,oCAEfc,QAAS,CACPhB,OAAO,EACPC,KAAM,SACNC,YACE,2FAEJD,KAAM,CACJI,QAAS,sBACTL,MAAO,MACPC,KAAM,SACNC,YACE,sEAEJe,OAAQ,CACNZ,QAAS,wBACTL,MAAO,QACPC,KAAM,SACNC,YACE,6EAEJgB,cAAe,CACbb,QAAS,wBACTL,MAAO,IACPC,KAAM,SACNC,YACE,gFAEJiB,aAA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"} \ No newline at end of file diff --git a/lib/browser.js b/lib/browser.js index a3deb4d6..7b06d357 100644 --- a/lib/browser.js +++ b/lib/browser.js @@ -104,18 +104,31 @@ export const newPage = async () => { const page = await browser.newPage(); + // Disable cache + await page.setCacheEnabled(false); + // Set the content await setPageContent(page); return page; }; -export const clearPage = async (page) => { +export const clearPage = async (page, hardReset = false) => { try { - // Navigate to about:blank - await page.goto('about:blank'); - - // Set the content and and scripts again - await setPageContent(page); + if (hardReset) { + // Navigate to about:blank + await page.goto('about:blank'); + + // Set the content and and scripts again + await setPageContent(page); + } else { + // Clear body content + await page.$eval( + 'body', + (body) => + (body.innerHTML = + '
') + ); + } } catch (error) { log(3, '[browser] Could not clear page'); } diff --git a/lib/export.js b/lib/export.js index b34c44ba..c9d02198 100644 --- a/lib/export.js +++ b/lib/export.js @@ -52,7 +52,8 @@ const getClipRegion = (page) => * @param {string} type - The type of a result image. * @param {string} encoding - The type of encoding used. * @param {string} clip - The clip region. - * @param {number} rasterizationTimeout - The rasterization timeout in milliseconds. + * @param {number} rasterizationTimeout - The rasterization timeout + * in milliseconds. * @returns {string} - A string representation of a screenshot. */ const createImage = async (page, type, encoding, clip, rasterizationTimeout) => @@ -413,7 +414,7 @@ export default async (page, chart, options) => { x, y }, - options.pool.rasterizationTimeout + exportOptions.rasterizationTimeout ); } else if (exportOptions.type === 'pdf') { // PDF diff --git a/lib/pool.js b/lib/pool.js index db1332ba..9c970a2e 100644 --- a/lib/pool.js +++ b/lib/pool.js @@ -101,7 +101,7 @@ const factory = { } // Clear page - await clearPage(workerHandle.page); + await clearPage(workerHandle.page, false); return true; }, @@ -202,7 +202,9 @@ export const init = async (config) => { ); }); - pool.on('release', (resource) => { + pool.on('release', async (resource) => { + // Clear page + await clearPage(resource.page, false); log(4, `[pool] Releasing a worker of an id ${resource.id}`); }); diff --git a/lib/schemas/config.js b/lib/schemas/config.js index a47809a9..c4c4d7e1 100644 --- a/lib/schemas/config.js +++ b/lib/schemas/config.js @@ -227,6 +227,12 @@ export const defaultConfig = { type: 'string', description: 'Starts a batch job. A string that contains input/output pairs: "in=out;in=out;..".' + }, + rasterizationTimeout: { + envLink: 'EXPORT_RASTERIZATION_TIMEOUT', + value: 1500, + type: 'number', + description: 'The number of milliseconds to wait for rendering a webpage.' } }, customCode: { @@ -420,12 +426,6 @@ export const defaultConfig = { description: 'The number of milliseconds after an idle resource is destroyed.' }, - rasterizationTimeout: { - envLink: 'HIGHCHARTS_POOL_RASTERIZATION_TIMEOUT', - value: 1500, - type: 'number', - description: 'The number of milliseconds to wait for rendering a webpage.' - }, createRetryInterval: { envLink: 'HIGHCHARTS_POOL_CREATE_RETRY_INTERVAL', value: 200, @@ -593,6 +593,12 @@ export const promptsConfig = { initial: defaultConfig.export.defaultScale.value, min: 0.1, max: 5 + }, + { + type: 'number', + name: 'rasterizationTimeout', + message: 'The number of milliseconds to wait for rendering a webpage', + initial: defaultConfig.export.rasterizationTimeout.value } ], customCode: [ @@ -742,12 +748,6 @@ export const promptsConfig = { message: 'The number of milliseconds after an idle resource is destroyed', initial: defaultConfig.pool.idleTimeout.value }, - { - type: 'number', - name: 'rasterizationTimeout', - message: 'The number of milliseconds to wait for rendering a webpage', - initial: defaultConfig.pool.rasterizationTimeout.value - }, { type: 'number', name: 'createRetryInterval', diff --git a/package-lock.json b/package-lock.json index 83cac869..078f965e 100644 --- a/package-lock.json +++ b/package-lock.json @@ -1,12 +1,12 @@ { "name": "highcharts-export-server", - "version": "3.0.5", + "version": "3.1.0", "lockfileVersion": 2, "requires": true, "packages": { "": { "name": "highcharts-export-server", - "version": "3.0.5", + "version": "3.1.0", "hasInstallScript": true, "license": "MIT", "dependencies": { diff --git a/package.json b/package.json index d69a7107..e05009c3 100644 --- a/package.json +++ b/package.json @@ -3,7 +3,7 @@ "author": "Highsoft AS (http://www.highcharts.com/about)", "license": "MIT", "type": "module", - "version": "3.0.5", + "version": "3.1.0", "main": "dist/index.esm.js", "exports": { ".": { diff --git a/templates/template.html b/templates/template.html index 1e2ea54c..d4a55789 100644 --- a/templates/template.html +++ b/templates/template.html @@ -1,16 +1,21 @@ - - + + - + Highcharts Export