diff --git a/CHANGELOG.md b/CHANGELOG.md
index 60a9ee69..518738da 100644
--- a/CHANGELOG.md
+++ b/CHANGELOG.md
@@ -6,8 +6,10 @@ _Enhancements:_
- Improved overall error handling by adding a main try-catch block to correctly capture and log errors occurring throughout the code.
- Introduced two new types of custom errors: `ExportError` for functionality-related errors and `HttpError` for server-related errors.
- Introduced a new error logging mechanism with stack tracing using new function called `logWithStack`.
+- Added unit tests for certain parts of the code.
- Changed the `customCode` section of options to `customLogic` in order to avoid confusion with the existing `customCode` property within.
- Changed the names of environment variables for a better representation of their roles (refer to all envs in the README's `Environment Variables` section).
+- Added a moving average indicator for the exporting success rate ratio.
- Added new environment variables (`NODE_ENV`, `HIGHCHARTS_ADMIN_TOKEN`, and `SERVER_BENCHMARKING`) to the `.env.sample` file, along with their descriptions in the README.
- Added the `HIGHCHARTS_CACHE_PATH` option available through `.env` to set a custom directory for the fetched files.
- Added several new functions to the `highcharts-export-server` module, including `logWithStack`, `setLogLevel`, `enableFileLogging`, `manualConfig`, `printLogo`, and `printUsage`.
diff --git a/dist/index.cjs b/dist/index.cjs
index 06100145..b24cbbfc 100644
--- a/dist/index.cjs
+++ b/dist/index.cjs
@@ -1,2 +1,2 @@
-"use strict";require("colors");var e=require("dotenv"),t=require("fs"),r=require("path"),i=require("https-proxy-agent"),o=require("http"),n=require("https"),a=require("url"),s=require("zod"),l=require("prompts"),c=require("tarn"),p=require("uuid"),u=require("node:path"),h=require("puppeteer"),d=require("node:crypto"),g=require("cors"),m=require("express"),f=require("multer"),y=require("express-rate-limit"),v="undefined"!=typeof document?document.currentScript:null;function b(e){var t=Object.create(null);return e&&Object.keys(e).forEach((function(r){if("default"!==r){var i=Object.getOwnPropertyDescriptor(e,r);Object.defineProperty(t,r,i.get?i:{enumerable:!0,get:function(){return e[r]}})}})),t.default=e,Object.freeze(t)}var w=b(a);async function E(e,t={}){return new Promise(((r,i)=>{const a=(e=>e.startsWith("https")?n:o)(e);a.get(e,t,(e=>{let t="";e.on("data",(e=>{t+=e})),e.on("end",(()=>{t||i("Nothing was fetched from the URL."),e.text=t,r(e)}))})).on("error",(e=>{i(e)}))}))}const T={puppeteer:{args:{value:[],type:"string[]",description:"Arguments array to send to Puppeteer."}},highcharts:{version:{value:"latest",envLink:"HIGHCHARTS_VERSION",type:"string",description:"The Highcharts version to be used."},cdnURL:{value:"https://code.highcharts.com/",envLink:"HIGHCHARTS_CDN_URL",type:"string",description:"The CDN URL for Highcharts scripts to be used."},coreScripts:{envLink:"HIGHCHARTS_CORE_SCRIPTS",value:["highcharts","highcharts-more","highcharts-3d"],type:"string[]",description:"The core Highcharts scripts to fetch."},modules:{envLink:"HIGHCHARTS_MODULES",value:["stock","map","gantt","exporting","export-data","parallel-coordinates","accessibility","annotations-advanced","boost-canvas","boost","data","data-tools","draggable-points","static-scale","broken-axis","heatmap","tilemap","tiledwebmap","timeline","treemap","treegraph","item-series","drilldown","histogram-bellcurve","bullet","funnel","funnel3d","geoheatmap","pyramid3d","networkgraph","overlapping-datalabels","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","flowmap"],type:"string[]",description:"The modules of Highcharts to fetch."},indicators:{envLink:"HIGHCHARTS_INDICATORS",value:["indicators-all"],type:"string[]",description:"The indicators of Highcharts 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 optional scripts or dependencies to fetch."},forceFetch:{envLink:"HIGHCHARTS_FORCE_FETCH",value:!1,type:"boolean",description:"The flag to determine whether to refetch all scripts after each server rerun."},cachePath:{envLink:"HIGHCHARTS_CACHE_PATH",value:".cache",type:"string",description:"The path to the cache directory. It is used to store the Highcharts scripts and custom scripts."}},export:{infile:{value:!1,type:"string",description:"The input file should include a name and a type (json or svg). It must be correctly formatted as a JSON or SVG file."},instr:{value:!1,type:"string",description:"Input, provided in the form of a stringified JSON or SVG file, will override the --infile option."},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). This will ignore the --type flag."},type:{envLink:"EXPORT_TYPE",value:"png",type:"string",description:"The file export format. It can be jpeg, png, pdf, or svg."},constr:{envLink:"EXPORT_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 no value is set."},defaultWidth:{envLink:"EXPORT_DEFAULT_WIDTH",value:600,type:"number",description:"The default width of the exported chart. Used when no value is set."},defaultScale:{envLink:"EXPORT_DEFAULT_SCALE",value:1,type:"number",description:"The default scale of the exported chart. Used when no value is set."},height:{type:"number",value:!1,description:"The height of the exported chart, overriding the option in the chart settings."},width:{type:"number",value:!1,description:"The width of the exported chart, overriding the option in the chart settings."},scale:{value:!1,type:"number",description:"The scale of the exported chart, overriding the option in the chart settings. Ranges between 0.1 and 5.0."},globalOptions:{value:!1,type:"string",description:"Either a stringified JSON or a filename containing options to be passed into the Highcharts.setOptions."},themeOptions:{value:!1,type:"string",description:"Either a stringified JSON or a filename containing theme options to be passed into the Highcharts.setOptions."},batch:{value:!1,type:"string",description:'Initiates a batch job with a string containing input/output pairs: "in=out;in=out;...".'},rasterizationTimeout:{envLink:"EXPORT_RASTERIZATION_TIMEOUT",value:1500,type:"number",description:"The duration in milliseconds to wait for rendering a webpage."}},customLogic:{allowCodeExecution:{envLink:"CUSTOM_LOGIC_ALLOW_CODE_EXECUTION",value:!1,type:"boolean",description:"Controls whether the execution of arbitrary code is allowed during the exporting process."},allowFileResources:{envLink:"CUSTOM_LOGIC_ALLOW_FILE_RESOURCES",value:!1,type:"boolean",description:"Controls the ability to inject resources from the filesystem. This setting has no effect when running as a server."},customCode:{value:!1,type:"string",description:"Custom code to execute before chart initialization. It can be a function, code wrapped within a function, or a filename with the .js extension."},callback:{value:!1,type:"string",description:"JavaScript code to run during construction. It can be a function or a filename with the .js extension."},resources:{value:!1,type:"string",description:"Additional resource in the form of a stringified JSON, which may contain files, js, and css sections."},loadConfig:{value:!1,type:"string",legacyName:"fromFile",description:"A file containing a pre-defined configuration to use."},createConfig:{value:!1,type:"string",description:"Enables setting options through a prompt and saving them in a provided config file."}},server:{enable:{envLink:"SERVER_ENABLE",value:!1,type:"boolean",cliName:"enableServer",description:"When set to true, the server starts on the local IP address 0.0.0.0."},host:{envLink:"SERVER_HOST",value:"0.0.0.0",type:"string",description:"The hostname of the server. Additionally, it starts a server on the provided hostname."},port:{envLink:"SERVER_PORT",value:7801,type:"number",description:"The server port when enabled."},benchmarking:{envLink:"SERVER_BENCHMARKING",value:!1,type:"boolean",cliName:"serverBenchmarking",description:"Indicates whether to display the duration, in milliseconds, of specific actions that occur on the server while serving a request."},ssl:{enable:{envLink:"SERVER_SSL_ENABLE",value:!1,type:"boolean",cliName:"enableSsl",description:"Enables or disables the SSL protocol."},force:{envLink:"SERVER_SSL_FORCE",value:!1,type:"boolean",cliName:"sslForced",legacyName:"sslOnly",description:"When set to true, the server is forced to serve only over HTTPS."},port:{envLink:"SERVER_SSL_PORT",value:443,type:"number",cliName:"sslPort",description:"The port on which to run the SSL server."},certPath:{envLink:"SERVER_SSL_CERT_PATH",value:"",type:"string",legacyName:"sslPath",description:"The path to the SSL certificate/key file."}},rateLimiting:{enable:{envLink:"SERVER_RATE_LIMITING_ENABLE",value:!1,type:"boolean",cliName:"enableRateLimiting",description:"Enables rate limiting for the server."},maxRequests:{envLink:"SERVER_RATE_LIMITING_MAX_REQUESTS",value:10,type:"number",legacyName:"rateLimit",description:"The maximum number of requests allowed in one minute."},window:{envLink:"SERVER_RATE_LIMITING_WINDOW",value:1,type:"number",description:"The time window, in minutes, for the rate limiting."},delay:{envLink:"SERVER_RATE_LIMITING_DELAY",value:0,type:"number",description:"The delay duration for each successive request before reaching the maximum limit."},trustProxy:{envLink:"SERVER_RATE_LIMITING_TRUST_PROXY",value:!1,type:"boolean",description:"Set this to true if the server is behind a load balancer."},skipKey:{envLink:"SERVER_RATE_LIMITING_SKIP_KEY",value:"",type:"string",description:"Allows bypassing the rate limiter and should be provided with the skipToken argument."},skipToken:{envLink:"SERVER_RATE_LIMITING_SKIP_TOKEN",value:"",type:"string",description:"Allows bypassing the rate limiter and should be provided with the skipKey argument."}}},pool:{minWorkers:{envLink:"POOL_MIN_WORKERS",value:4,type:"number",description:"The number of minimum and initial pool workers to spawn."},maxWorkers:{envLink:"POOL_MAX_WORKERS",value:8,type:"number",legacyName:"workers",description:"The number of maximum pool workers to spawn."},workLimit:{envLink:"POOL_WORK_LIMIT",value:40,type:"number",description:"The number of work pieces that can be performed before restarting the worker process."},acquireTimeout:{envLink:"POOL_ACQUIRE_TIMEOUT",value:5e3,type:"number",description:"The duration, in milliseconds, to wait for acquiring a resource."},createTimeout:{envLink:"POOL_CREATE_TIMEOUT",value:5e3,type:"number",description:"The duration, in milliseconds, to wait for creating a resource."},destroyTimeout:{envLink:"POOL_DESTROY_TIMEOUT",value:5e3,type:"number",description:"The duration, in milliseconds, to wait for destroying a resource."},idleTimeout:{envLink:"POOL_IDLE_TIMEOUT",value:3e4,type:"number",description:"The duration, in milliseconds, after which an idle resource is destroyed."},createRetryInterval:{envLink:"POOL_CREATE_RETRY_INTERVAL",value:200,type:"number",description:"The duration, in milliseconds, to wait before retrying the create process in case of a failure."},reaperInterval:{envLink:"POOL_REAPER_INTERVAL",value:1e3,type:"number",description:"The duration, in milliseconds, after which the check for idle resources to destroy is triggered."},benchmarking:{envLink:"POOL_BENCHMARKING",value:!1,type:"boolean",cliName:"poolBenchmarking",description:"Indicate whether to show statistics for the pool of resources or not."},listenToProcessExits:{envLink:"POOL_LISTEN_TO_PROCESS_EXITS",value:!0,type:"boolean",description:"Decides whether or not to attach process.exit handlers."}},logging:{level:{envLink:"LOGGING_LEVEL",value:4,type:"number",cliName:"logLevel",description:"The logging level to be used."},file:{envLink:"LOGGING_FILE",value:"highcharts-export-server.log",type:"string",cliName:"logFile",description:"The name of a log file. The logDest option also needs to be set to enable file logging."},dest:{envLink:"LOGGING_DEST",value:"log/",type:"string",cliName:"logDest",description:"The path to store log files. This also enables file logging."}},ui:{enable:{envLink:"UI_ENABLE",value:!1,type:"boolean",cliName:"enableUi",description:"Enables or disables the user interface (UI) for the export server."},route:{envLink:"UI_ROUTE",value:"/",type:"string",cliName:"uiRoute",description:"The endpoint route to which the user interface (UI) should be attached."}},other:{noLogo:{envLink:"OTHER_NO_LOGO",value:!1,type:"boolean",description:"Skip printing the logo on a startup. Will be replaced by a simple text."}}},S={puppeteer:[{type:"list",name:"args",message:"Puppeteer arguments",initial:T.puppeteer.args.value.join(","),separator:","}],highcharts:[{type:"text",name:"version",message:"Highcharts version",initial:T.highcharts.version.value},{type:"text",name:"cdnURL",message:"The URL of CDN",initial:T.highcharts.cdnURL.value},{type:"multiselect",name:"modules",message:"Available modules",instructions:"Space: Select specific, A: Select all, Enter: Confirm.",choices:T.highcharts.modules.value},{type:"list",name:"scripts",message:"Custom scripts",initial:T.highcharts.scripts.value.join(","),separator:","},{type:"toggle",name:"forceFetch",message:"Force re-fetch the scripts",initial:T.highcharts.forceFetch.value},{type:"text",name:"cachePath",message:"The path to the cache directory",initial:T.highcharts.cachePath.value}],export:[{type:"select",name:"type",message:"The default export file type",hint:`Default: ${T.export.type.value}`,initial:0,choices:["png","jpeg","pdf","svg"]},{type:"select",name:"constr",message:"The default constructor for Highcharts",hint:`Default: ${T.export.constr.value}`,initial:0,choices:["chart","stockChart","mapChart","ganttChart"]},{type:"number",name:"defaultHeight",message:"The default fallback height of the exported chart",initial:T.export.defaultHeight.value},{type:"number",name:"defaultWidth",message:"The default fallback width of the exported chart",initial:T.export.defaultWidth.value},{type:"number",name:"defaultScale",message:"The default fallback scale of the exported chart",initial:T.export.defaultScale.value,min:.1,max:5},{type:"number",name:"rasterizationTimeout",message:"The rendering webpage timeout in milliseconds",initial:T.export.rasterizationTimeout.value}],customLogic:[{type:"toggle",name:"allowCodeExecution",message:"Enable execution of custom code",initial:T.customLogic.allowCodeExecution.value},{type:"toggle",name:"allowFileResources",message:"Enable file resources",initial:T.customLogic.allowFileResources.value}],server:[{type:"toggle",name:"enable",message:"Starts the server on 0.0.0.0",initial:T.server.enable.value},{type:"text",name:"host",message:"Server hostname",initial:T.server.host.value},{type:"number",name:"port",message:"Server port",initial:T.server.port.value},{type:"toggle",name:"benchmarking",message:"Enable server benchmarking",initial:T.server.benchmarking.value},{type:"toggle",name:"ssl.enable",message:"Enable SSL protocol",initial:T.server.ssl.enable.value},{type:"toggle",name:"ssl.force",message:"Force serving only over HTTPS",initial:T.server.ssl.force.value},{type:"number",name:"ssl.port",message:"SSL server port",initial:T.server.ssl.port.value},{type:"text",name:"ssl.certPath",message:"The path to find the SSL certificate/key",initial:T.server.ssl.certPath.value},{type:"toggle",name:"rateLimiting.enable",message:"Enable rate limiting",initial:T.server.rateLimiting.enable.value},{type:"number",name:"rateLimiting.maxRequests",message:"The maximum requests allowed per minute",initial:T.server.rateLimiting.maxRequests.value},{type:"number",name:"rateLimiting.window",message:"The rate-limiting time window in minutes",initial:T.server.rateLimiting.window.value},{type:"number",name:"rateLimiting.delay",message:"The delay for each successive request before reaching the maximum",initial:T.server.rateLimiting.delay.value},{type:"toggle",name:"rateLimiting.trustProxy",message:"Set to true if behind a load balancer",initial:T.server.rateLimiting.trustProxy.value},{type:"text",name:"rateLimiting.skipKey",message:"Allows bypassing the rate limiter when provided with the skipToken argument",initial:T.server.rateLimiting.skipKey.value},{type:"text",name:"rateLimiting.skipToken",message:"Allows bypassing the rate limiter when provided with the skipKey argument",initial:T.server.rateLimiting.skipToken.value}],pool:[{type:"number",name:"minWorkers",message:"The initial number of workers to spawn",initial:T.pool.minWorkers.value},{type:"number",name:"maxWorkers",message:"The maximum number of workers to spawn",initial:T.pool.maxWorkers.value},{type:"number",name:"workLimit",message:"The pieces of work that can be performed before restarting a Puppeteer process",initial:T.pool.workLimit.value},{type:"number",name:"acquireTimeout",message:"The number of milliseconds to wait for acquiring a resource",initial:T.pool.acquireTimeout.value},{type:"number",name:"createTimeout",message:"The number of milliseconds to wait for creating a resource",initial:T.pool.createTimeout.value},{type:"number",name:"destroyTimeout",message:"The number of milliseconds to wait for destroying a resource",initial:T.pool.destroyTimeout.value},{type:"number",name:"idleTimeout",message:"The number of milliseconds after an idle resource is destroyed",initial:T.pool.idleTimeout.value},{type:"number",name:"createRetryInterval",message:"The retry interval in milliseconds after a create process fails",initial:T.pool.createRetryInterval.value},{type:"number",name:"reaperInterval",message:"The reaper interval in milliseconds after triggering the check for idle resources to destroy",initial:T.pool.reaperInterval.value},{type:"toggle",name:"benchmarking",message:"Enable benchmarking for a resource pool",initial:T.pool.benchmarking.value},{type:"toggle",name:"listenToProcessExits",message:"Set to false to skip attaching process.exit handlers",initial:T.pool.listenToProcessExits.value}],logging:[{type:"number",name:"level",message:"The log level (0: silent, 1: error, 2: warning, 3: notice, 4: verbose, 5: benchmark)",initial:T.logging.level.value,round:0,min:0,max:5},{type:"text",name:"file",message:"A log file name. Set with the --logDest to enable file logging",initial:T.logging.file.value},{type:"text",name:"dest",message:"The path to log files. Enables file logging",initial:T.logging.dest.value}],ui:[{type:"toggle",name:"enable",message:"Enable UI for the export server",initial:T.ui.enable.value},{type:"text",name:"route",message:"A route to attach the UI",initial:T.ui.route.value}],other:[{type:"toggle",name:"noLogo",message:"Skip printing the logo on startup. Replaced by simple text",initial:T.other.noLogo.value}]},R=["options","globalOptions","themeOptions","resources","payload"],x={},L=(e,t="")=>{Object.keys(e).forEach((r=>{if(!["puppeteer","highcharts"].includes(r)){const i=e[r];void 0===i.value?L(i,`${t}.${r}`):(x[i.cliName||r]=`${t}.${r}`.substring(1),void 0!==i.legacyName&&(x[i.legacyName]=`${t}.${r}`.substring(1)))}}))};L(T);const k=["red","yellow","blue","gray","green"];let O={toConsole:!0,toFile:!1,pathCreated:!1,levelsDesc:[{title:"error",color:k[0]},{title:"warning",color:k[1]},{title:"notice",color:k[2]},{title:"verbose",color:k[3]},{title:"benchmark",color:k[4]}],listeners:[]};for(const[e,t]of Object.entries(T.logging))O[e]=t.value;const _=(e,r)=>{O.toFile&&(O.pathCreated||(!t.existsSync(O.dest)&&t.mkdirSync(O.dest),O.pathCreated=!0),t.appendFile(`${O.dest}${O.file}`,[r].concat(e).join(" ")+"\n",(e=>{e&&(console.log(`[logger] Unable to write to log file: ${e}`),O.toFile=!1)})))},I=(...e)=>{const[t,...r]=e,{level:i,levelsDesc:o}=O;if(5!==t&&(0===t||t>i||i>o.length))return;const n=`${(new Date).toString().split("(")[0].trim()} [${o[t-1].title}] -`;O.listeners.forEach((e=>{e(n,r.join(" "))})),O.toConsole&&console.log.apply(void 0,[n.toString()[O.levelsDesc[t-1].color]].concat(r)),_(r,n)},C=(e,t,r)=>{const i=r||t.message,{level:o,levelsDesc:n}=O;if(0===e||e>o||o>n.length)return;const a=`${(new Date).toString().split("(")[0].trim()} [${n[e-1].title}] -`,s=t.message!==t.stackMessage||void 0===t.stackMessage?t.stack:t.stack.split("\n").slice(1).join("\n"),l=[i,"\n",s];O.toConsole&&console.log.apply(void 0,[a.toString()[O.levelsDesc[e-1].color]].concat([i[k[e-1]],"\n",s])),O.listeners.forEach((e=>{e(a,l.join(" "))})),_(l,a)},A=e=>{e>=0&&e<=O.levelsDesc.length&&(O.level=e)},N=(e,t)=>{if(O={...O,dest:e||O.dest,file:t||O.file,toFile:!0},0===O.dest.length)return I(1,"[logger] File logging initialization: no path supplied.");O.dest.endsWith("/")||(O.dest+="/")},P=a.fileURLToPath(new URL("../.","undefined"==typeof document?require("url").pathToFileURL(__filename).href:v&&v.src||new URL("index.cjs",document.baseURI).href)),H=(e,t)=>{const r=["png","jpeg","pdf","svg"];if(t){const i=t.split(".").pop();"jpg"===i?e="jpeg":r.includes(i)&&e!==i&&(e=i)}return{"image/png":"png","image/jpeg":"jpeg","application/pdf":"pdf","image/svg+xml":"svg"}[e]||r.find((t=>t===e))||"png"},$=(e=!1,r)=>{const i=["js","css","files"];let o=e,n=!1;if(r&&e.endsWith(".json"))try{o=j(t.readFileSync(e,"utf8"))}catch(e){return C(2,e,"[cli] No resources found.")}else o=j(e),o&&!r&&delete o.files;for(const e in o)i.includes(e)?n||(n=!0):delete o[e];return n?(o.files&&(o.files=o.files.map((e=>e.trim())),(!o.files||o.files.length<=0)&&delete o.files),o):I(3,"[cli] No resources found.")};function j(e,t){try{const r=JSON.parse("string"!=typeof e?JSON.stringify(e):e);return"string"!=typeof r&&t?JSON.stringify(r):r}catch{return!1}}const U=e=>{if(null===e||"object"!=typeof e)return e;const t=Array.isArray(e)?[]:{};for(const r in e)Object.prototype.hasOwnProperty.call(e,r)&&(t[r]=U(e[r]));return t},F=(e,t)=>JSON.stringify(e,((e,r)=>("string"==typeof r&&((r=r.trim()).startsWith("function(")||r.startsWith("function ("))&&r.endsWith("}")&&(r=t?`EXP_FUN${(r+"").replaceAll(/\n|\t|\r/g," ")}EXP_FUN`:void 0),"function"==typeof r?`EXP_FUN${(r+"").replaceAll(/\n|\t|\r/g," ")}EXP_FUN`:r))).replaceAll(/"EXP_FUN|EXP_FUN"/g,"");function M(){console.log("\nUsage of CLI arguments:".bold,"\n------",`\nFor more detailed information, visit the readme at: ${"https://github.com/highcharts/node-export-server#readme".bold.yellow}.`);const e=t=>{for(const[r,i]of Object.entries(t))if(Object.prototype.hasOwnProperty.call(i,"value")){let e=` --${i.cliName||r} ${("<"+i.type+">").green} `;if(e.length<48)for(let t=e.length;t<48;t++)e+=".";console.log(e,i.description,`[Default: ${i.value.toString().bold}]`.blue)}else e(i)};Object.keys(T).forEach((t=>{["puppeteer","highcharts"].includes(t)||(console.log(`\n${t.toUpperCase()}`.red),e(T[t]))})),console.log("\n")}const G=e=>!["false","undefined","null","NaN","0",""].includes(e)&&!!e,q=(e,r)=>{if(e&&"string"==typeof e)return(e=e.trim()).endsWith(".js")?!!r&&q(t.readFileSync(e,"utf8")):e.startsWith("function()")||e.startsWith("function ()")||e.startsWith("()=>")||e.startsWith("() =>")?`(${e})()`:e.replace(/;$/,"")},D=()=>{const e=process.hrtime.bigint();return()=>Number(process.hrtime.bigint()-e)/1e6};e.config();const W=()=>s.z.enum(["true","false"]).transform((e=>"true"===e)).optional(),V=()=>s.z.string().transform((e=>e.split(",").map((e=>e.trim())))).optional(),z=s.z.object({HIGHCHARTS_VERSION:s.z.string().refine((e=>/^(latest|\d+(\.\d+){0,2})$/.test(e)),{message:"HIGHCHARTS_VERSION must be 'latest', a major version, or in the form XX.YY.ZZ"}).optional(),HIGHCHARTS_CDN_URL:s.z.string().trim().refine((e=>e.startsWith("https://")||e.startsWith("http://")),{message:"Invalid value for HIGHCHARTS_CDN_URL. It should start with http:// or https://."}).optional(),HIGHCHARTS_CORE_SCRIPTS:V(),HIGHCHARTS_MODULES:V(),HIGHCHARTS_INDICATORS:V(),HIGHCHARTS_FORCE_FETCH:W(),HIGHCHARTS_CACHE_PATH:s.z.string().optional(),HIGHCHARTS_ADMIN_TOKEN:s.z.string().optional(),EXPORT_TYPE:s.z.enum(["jpeg","png","pdf","svg"]).optional(),EXPORT_CONSTR:s.z.string().refine((e=>["chart","stockChart","mapChart","ganttChart"].includes(e||"")),{message:"Invalid value for EXPORT_CONSTR. "}).optional(),EXPORT_DEFAULT_HEIGHT:s.z.coerce.number().positive().optional(),EXPORT_DEFAULT_WIDTH:s.z.coerce.number().positive().optional(),EXPORT_DEFAULT_SCALE:s.z.coerce.number().positive().optional(),EXPORT_RASTERIZATION_TIMEOUT:s.z.coerce.number().positive().optional(),CUSTOM_LOGIC_ALLOW_CODE_EXECUTION:W(),CUSTOM_LOGIC_ALLOW_FILEL_RESOURCES:W(),SERVER_ENABLE:W(),SERVER_HOST:s.z.string().optional(),SERVER_PORT:s.z.coerce.number().optional(),SERVER_BENCHMARKING:W(),SERVER_SSL_ENABLE:W(),SERVER_SSL_FORCE:W(),SERVER_SSL_PORT:s.z.coerce.number().optional(),SERVER_SSL_CERT_PATH:s.z.string().optional(),SERVER_RATE_LIMITING_ENABLE:W(),SERVER_RATE_LIMITING_MAX_REQUESTS:s.z.coerce.number().optional(),SERVER_RATE_LIMITING_WINDOW:s.z.coerce.number().optional(),SERVER_RATE_LIMITING_DELAY:s.z.coerce.number().optional(),SERVER_RATE_LIMITING_TRUST_PROXY:W(),SERVER_RATE_LIMITING_SKIP_KEY:s.z.string().optional(),SERVER_RATE_LIMITING_SKIP_TOKEN:s.z.string().optional(),POOL_MIN_WORKERS:s.z.coerce.number().optional(),POOL_MAX_WORKERS:s.z.coerce.number().optional(),POOL_WORK_LIMIT:s.z.coerce.number().optional(),POOL_ACQUIRE_TIMEOUT:s.z.coerce.number().optional(),POOL_CREATE_TIMEOUT:s.z.coerce.number().optional(),POOL_DESTROY_TIMEOUT:s.z.coerce.number().optional(),POOL_IDLE_TIMEOUT:s.z.coerce.number().optional(),POOL_CREATE_RETRY_INTERVAL:s.z.coerce.number().optional(),POOL_REAPER_INTERVAL:s.z.coerce.number().optional(),POOL_BENCHMARKING:W(),POOL_LISTEN_TO_PROCESS_EXITS:W(),LOGGING_LEVEL:s.z.coerce.number().optional().refine((e=>(e||4)>=0&&(e||4)<=4),{message:"Invalid value for LOGGING_LEVEL. We only accept 0, 1, 2, 3, 4 as logging levels."}),LOGGING_FILE:s.z.string().optional(),LOGGING_DEST:s.z.string().optional(),UI_ENABLE:W(),UI_ROUTE:s.z.string().optional(),OTHER_NO_LOGO:W(),NODE_ENV:s.z.enum(["development","production","test"]).optional().default("production"),PROXY_SERVER_TIMEOUT:s.z.coerce.number().positive().optional().default(5e3),PROXY_SERVER_HOST:s.z.string().optional().default("localhost"),PROXY_SERVER_PORT:s.z.coerce.number().positive().optional().default(8080)}).parse(process.env);class X extends Error{constructor(e){super(),this.message=e,this.stackMessage=e}setError(e){return this.error=e,e.name&&(this.name=e.name),e.statusCode&&(this.statusCode=e.statusCode),e.stack&&(this.stackMessage=e.message,this.stack=e.stack),this}}const K={cdnURL:"https://code.highcharts.com/",activeManifest:{},sources:"",hcVersion:""};let J=!1;const B=e=>e.sources.substring(0,e.sources.indexOf("*/")).replace("/*","").replace("*/","").replace(/\n/g,"").trim(),Y=async(e,t,r,i=!1)=>{e.endsWith(".js")&&(e=e.substring(0,e.length-3)),I(4,`[cache] Fetching script - ${e}.js`);const o=t?{agent:t,timeout:z.PROXY_SERVER_TIMEOUT}:{},n=await E(`${e}.js`,o);if(200===n.statusCode&&"string"==typeof n.text){if(r){r[e.replace(/(.*)\/|(.*)modules\/|stock\/(.*)indicators\/|maps\/(.*)modules\//gi,"")]=1}return n.text}if(i)throw new X(`Could not fetch the ${e}.js. The script might not exist in the requested version (status code: ${n.statusCode}).`).setError(n);return I(2,`[cache] Could not fetch the ${e}.js. The script might not exist in the requested version.`),""},Q=async(e,r)=>{const{coreScripts:o,modules:n,indicators:a,scripts:s}=e,l="latest"!==e.version&&e.version?`${e.version}/`:"";let c;I(3,`[cache] Updating cache version to Highcharts: ${l||"latest"}.`);const p=process.env.PROXY_SERVER_HOST,u=process.env.PROXY_SERVER_PORT;if(p&&u)try{c=new i.HttpsProxyAgent({host:p,port:+u})}catch(e){throw new X("[cache] Could not create a Proxy Agent.").setError(e)}const h={};try{return K.sources=await(async(e,t,r,i,o,n)=>{const a=[...e.map((e=>Y(`${i}${e}`,o,n,!0))),...t.map((e=>Y(`${i}${e}`,o,n))),...r.map((e=>Y(`${e}`,o)))];return(await Promise.all(a)).join(";\n")})([...o.map((e=>`${l}${e}`))],[...n.map((e=>"map"===e?`maps/${l}modules/${e}`:`${l}modules/${e}`)),...a.map((e=>`stock/${l}indicators/${e}`))],s,e.cdnURL||K.cdnURL,c,h),K.hcVersion=B(K),t.writeFileSync(r,K.sources),h}catch(e){throw new X("[cache] Unable to update the local Highcharts cache.").setError(e)}},Z=async e=>{const i=r.join(P,e.cachePath);let o;const n=r.join(i,"manifest.json"),a=r.join(i,"sources.js");if(J=e,!t.existsSync(i)&&t.mkdirSync(i),!t.existsSync(n)||e.forceFetch)I(3,"[cache] Fetching and caching Highcharts dependencies."),o=await Q(e,a);else{let r=!1;const i=JSON.parse(t.readFileSync(n));if(i.modules&&Array.isArray(i.modules)){const e={};i.modules.forEach((t=>e[t]=1)),i.modules=e}const{modules:s,coreScripts:l,indicators:c}=e,p=s.length+l.length+c.length;i.version!==e.version?(I(2,"[cache] A Highcharts version mismatch in the cache, need to re-fetch."),r=!0):Object.keys(i.modules||{}).length!==p?(I(2,"[cache] The cache and the requested modules do not match, need to re-fetch."),r=!0):r=(e.modules||[]).some((e=>{if(!i.modules[e])return I(2,`[cache] The ${e} is missing in the cache, need to re-fetch.`),!0})),r?o=await Q(e,a):(I(3,"[cache] Dependency cache is up to date, proceeding."),K.sources=t.readFileSync(a,"utf8"),o=i.modules,K.hcVersion=B(K))}await(async(e,i)=>{const o={version:e.version,modules:i||{}};K.activeManifest=o,I(3,"[cache] Writing a new manifest.");try{t.writeFileSync(r.join(P,e.cachePath,"manifest.json"),JSON.stringify(o),"utf8")}catch(e){throw new X("[cache] Error writing the cache manifest.").setError(e)}})(e,o)},ee=()=>r.join(P,J.cachePath);var te=async e=>!!J&&await Z(Object.assign(J,{version:e})),re=()=>K,ie=()=>K.hcVersion;let oe={};const ne=()=>oe,ae=(e,t,r=[])=>{const i=U(e);for(const[e,n]of Object.entries(t))i[e]="object"!=typeof(o=n)||Array.isArray(o)||null===o||r.includes(e)||void 0===i[e]?void 0!==n?n:i[e]:ae(i[e],n,r);var o;return i};function se(e,t={},r=""){Object.keys(e).forEach((i=>{const o=e[i],n=t&&t[i];void 0===o.value?se(o,n,`${r}.${i}`):(void 0!==n&&(o.value=n),o.envLink in z&&(o.value=z[o.envLink]))}))}function le(e){let t={};for(const[r,i]of Object.entries(e))t[r]=Object.prototype.hasOwnProperty.call(i,"value")?i.value:le(i);return t}function ce(e,t,r){for(;t.length>1;){const i=t.shift();return Object.prototype.hasOwnProperty.call(e,i)||(e[i]={}),e[i]=ce(Object.assign({},e[i]),t,r),e}return e[t[0]]=r,e}const pe=d.randomBytes(64).toString("base64url"),ue=u.join("tmp",`puppeteer-${pe}`),he=[`--user-data-dir=${u.join(ue,"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"],de=w.fileURLToPath(new URL(".","undefined"==typeof document?require("url").pathToFileURL(__filename).href:v&&v.src||new URL("index.cjs",document.baseURI).href)),ge=t.readFileSync(de+"/../templates/template.html","utf8");let me;const fe=async e=>{await e.setContent(ge),await e.addScriptTag({path:`${ee()}/sources.js`}),await e.evaluate((()=>window.setupHighcharts())),e.on("pageerror",(async t=>{await e.$eval("#container",((e,t)=>{window._displayErrors&&(e.innerHTML=t)}),`
Chart input data error ${t.toString()}`)}))},ye=async(e,t=!1)=>{try{t?(await e.goto("about:blank"),await fe(e)):await e.evaluate((()=>{document.body.innerHTML=''}))}catch(e){C(2,e,"[browser] Could not clear the content of the page.")}},ve=async()=>{if(!me)return!1;const e=await me.newPage();return await e.setCacheEnabled(!1),await fe(e),e},be=async()=>(me?.isConnected()&&(await me.close(),I(4,"[browser] Closed the browser.")),!0);const we=w.fileURLToPath(new URL(".","undefined"==typeof document?require("url").pathToFileURL(__filename).href:v&&v.src||new URL("index.cjs",document.baseURI).href)),Ee=(e,t,r)=>e.evaluate(((e,t)=>window.triggerExport(e,t)),t,r);var Te=async(e,i,o)=>{const n=[],a=async e=>{for(const e of n)await e.dispose();await e.evaluate((()=>{const[,...e]=document.getElementsByTagName("script"),[,...t]=document.getElementsByTagName("style"),[...r]=document.getElementsByTagName("link");for(const i of[...e,...t,...r])i.remove()}))};try{I(4,"[export] Determining export path.");const s=o.export;await e.evaluate((()=>requestAnimationFrame((()=>{}))));const l=s?.options?.chart?.displayErrors&&re().activeManifest.modules.debugger;let c;if(await e.evaluate((e=>window._displayErrors=e),l),i.indexOf&&(i.indexOf("=0||i.indexOf("=0)){if(I(4,"[export] Treating as SVG."),"svg"===s.type)return i;c=!0,await e.setContent((e=>`\n\n\n \n \n Highcarts Export \n \n \n \n \n ${e}\n
\n \n\n\n`)(i))}else I(4,"[export] Treating as config."),s.strInj?await Ee(e,{chart:{height:s.height,width:s.width}},o):(i.chart.height=s.height,i.chart.width=s.width,await Ee(e,i,o));const p=o.customLogic.resources;if(p){if(p.js&&n.push(await e.addScriptTag({content:p.js})),p.files)for(const r of p.files)try{const i=!r.startsWith("http");n.push(await e.addScriptTag(i?{content:t.readFileSync(r,"utf8")}:{url:r}))}catch(e){C(2,e,`[export] The JS file ${r} cannot be loaded.`)}if(p.css){let t=p.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")?n.push(await e.addStyleTag({url:i})):o.customLogic.allowFileResources&&n.push(await e.addStyleTag({path:r.join(we,i)})));n.push(await e.addStyleTag({content:p.css.replace(/@import\s*([^;]*);/g,"")||" "}))}}const u=c?await e.$eval("#chart-container svg:first-of-type",((e,t)=>({chartHeight:e.height.baseVal.value*t,chartWidth:e.width.baseVal.value*t})),parseFloat(s.scale)):await e.evaluate((()=>{const{chartHeight:e,chartWidth:t}=window.Highcharts.charts[0];return{chartHeight:e,chartWidth:t}})),h=Math.ceil(u?.chartHeight||s.height),d=Math.ceil(u?.chartWidth||s.width);await e.setViewport({height:h,width:d,deviceScaleFactor:c?1:parseFloat(s.scale)});const g=c?e=>{document.body.style.zoom=e,document.body.style.margin="0px"}:()=>{document.body.style.zoom=1};await e.evaluate(g,parseFloat(s.scale));const{height:m,width:f,x:y,y:v}=await(e=>e.$eval("#chart-container",(e=>{const{x:t,y:r,width:i,height:o}=e.getBoundingClientRect();return{x:t,y:r,width:i,height:Math.trunc(o>1?o:500)}})))(e);let b;if(c||await e.setViewport({width:Math.round(f),height:Math.round(m),deviceScaleFactor:parseFloat(s.scale)}),"svg"===s.type)b=await(e=>e.$eval("#container svg:first-of-type",(e=>e.outerHTML)))(e);else if(["png","jpeg"].includes(s.type))b=await((e,t,r,i,o)=>Promise.race([e.screenshot({type:t,encoding:r,clip:i,omitBackground:"png"==t}),new Promise(((e,t)=>setTimeout((()=>t(new X("Rasterization timeout"))),o||1500)))]))(e,s.type,"base64",{width:d,height:h,x:y,y:v},s.rasterizationTimeout);else{if("pdf"!==s.type)throw new X(`[export] Unsupported output format ${s.type}.`);b=await((e,t,r,i)=>e.pdf({height:t+1,width:r,encoding:i}))(e,h,d,"base64")}return await e.evaluate((()=>{if("undefined"!=typeof Highcharts){const e=Highcharts.charts;if(Array.isArray(e)&&e.length)for(const t of e)t&&t.destroy(),Highcharts.charts.shift()}})),await a(e),b}catch(t){return await a(e),t}};let Se,Re=0,xe=0,Le=0,ke=0,Oe=0,_e={},Ie=!1;const Ce={create:async()=>{let e=!1;const t=p.v4(),r=(new Date).getTime();try{if(e=await ve(),!e||e.isClosed())throw new X("The page is invalid or closed.");I(3,`[pool] Successfully created a worker ${t} - took ${(new Date).getTime()-r} ms.`)}catch(e){throw new X("Error encountered when creating a new page.").setError(e)}return{id:t,page:e,workCount:Math.round(Math.random()*(_e.workLimit/2))}},validate:async e=>_e.workLimit&&++e.workCount>_e.workLimit?(I(3,`[pool] Worker failed validation: exceeded work limit (limit is ${_e.workLimit}).`),!1):(await ye(e.page,!0),!0),destroy:e=>{I(3,`[pool] Destroying pool entry ${e.id}.`),e.page&&e.page.close()}},Ae=async e=>{if(_e=e&&e.pool?{...e.pool}:{},_e.listenToProcessExits&&(I(3,"[pool] Attaching exit listeners to the process."),process.on("exit",(async e=>{I(4,`Process exited with code ${e}.`),await Ne()})),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)=>{C(1,e,`The ${t} error.`),await Ne(),process.exit(1)}))),Se=e.puppeteerArgs,await(async e=>{const t=[...he,...e||[]];if(!me){let e=0;const r=async()=>{try{I(3,`[browser] Attempting to get a browser instance (try ${++e}).`),me=await h.launch({headless:"new",args:t,userDataDir:"./tmp/"})}catch(t){if(C(1,t,"[browser] Failed to launch a browser instance."),!(e<25))throw t;I(3,`[browser] Retry to open a browser (${e} out of 25).`),await new Promise((e=>setTimeout(e,4e3))),await r()}};try{await r()}catch(e){throw new X("[browser] Maximum retries to open a browser instance reached.").setError(e)}if(!me)throw new X("[browser] Cannot find a browser to open.")}return me})(Se),I(3,`[pool] Initializing pool with workers: min ${_e.minWorkers}, max ${_e.maxWorkers}.`),Ie)return I(4,"[pool] Already initialized, please kill it before creating a new one.");parseInt(_e.minWorkers)>parseInt(_e.maxWorkers)&&(_e.minWorkers=_e.maxWorkers);try{Ie=new c.Pool({...Ce,min:parseInt(_e.minWorkers),max:parseInt(_e.maxWorkers),acquireTimeoutMillis:_e.acquireTimeout,createTimeoutMillis:_e.createTimeout,destroyTimeoutMillis:_e.destroyTimeout,idleTimeoutMillis:_e.idleTimeout,createRetryIntervalMillis:_e.createRetryInterval,reapIntervalMillis:_e.reaperInterval,propagateCreateError:!1}),Ie.on("release",(async e=>{await ye(e.page,!1),I(4,`[pool] Releasing a worker with ID ${e.id}.`)})),Ie.on("destroySuccess",((e,t)=>{I(4,`[pool] Destroyed a worker with ID ${t.id}.`)}));const e=[];for(let t=0;t<_e.minWorkers;t++)try{const t=await Ie.acquire().promise;e.push(t)}catch(e){C(2,e,"[pool] Could not create an initial resource.")}e.forEach((e=>{Ie.release(e)})),I(3,"[pool] The pool is ready"+(e.length?` with ${e.length} initial resources waiting.`:"."))}catch(e){throw await be(),new X("[pool] Could not create the pool of workers.").setError(e)}};async function Ne(){return I(3,"[pool] Killing all pool workers and browser, if any exist."),Ie?.destroyed||Ie&&(await Ie.destroy(),I(4,"[browser] Destroyed the pool of resources.")),be()}const Pe=async(e,t)=>{let r;try{if(I(4,"[pool] Work received, starting to process."),++xe,_e.benchmarking&&He(),!Ie)throw new X("Work received, but pool has not been started.");try{I(4,"[pool] Acquiring a worker handle.");const e=D();r=await Ie.acquire().promise,t.server.benchmarking&&I(5,t.payload?.requestId?`[benchmark] Request with ID ${t.payload?.requestId} -`:"[benchmark]",`Acquired a worker handle: ${e()}ms.`)}catch(e){throw new X("Error encountered when acquiring an available entry.").setError(e)}if(I(4,"[pool] Acquired a worker handle."),!r.page)throw new X("Resolved worker page is invalid: the pool setup is wonky.");let i=(new Date).getTime();I(4,`[pool] Starting work on pool entry with ID ${r.id}.`);const o=D(),n=await Te(r.page,e,t);if(n instanceof Error)throw"Rasterization timeout"===n.message&&(r.page.close(),r.page=await ve()),new X("Error encountered during export.").setError(n);t.server.benchmarking&&I(5,t.payload?.requestId?`[benchmark] Request with ID ${t.payload?.requestId} -`:"[benchmark]",`Exported a chart sucessfully: ${o()}ms.`),Ie.release(r);const a=(new Date).getTime()-i;return Le+=a,Oe=Le/++Re,I(4,`[pool] Work completed in ${a} ms.`),{result:n,options:t}}catch(e){throw++ke,r&&Ie.release(r),new X(`[pool] In pool.postWork: ${e.message}`).setError(e)}};function He(){const{min:e,max:t}=Ie;I(5,`[pool] The minimum number of resources allowed by pool: ${e}.`),I(5,`[pool] The maximum number of resources allowed by pool: ${t}.`),I(5,`[pool] The number of resources that are currently available: ${Ie.numFree()}.`),I(5,`[pool] The number of resources that are currently acquired: ${Ie.numUsed()}.`),I(5,`[pool] The number of callers waiting to acquire a resource: ${Ie.numPendingAcquires()}.`)}var $e=()=>({min:Ie.min,max:Ie.max,available:Ie.numFree(),inUse:Ie.numUsed(),pendingAcquire:Ie.numPendingAcquires()}),je=()=>xe,Ue=()=>ke,Fe=()=>Oe,Me=()=>Re;let Ge=!1;const qe=async(e,r)=>{I(4,"[chart] Starting the exporting process.");const i=((e,t={})=>{let r={};return e.svg?(r=U(t),r.export.type=e.type||e.export.type,r.export.scale=e.scale||e.export.scale,r.export.outfile=e.outfile||e.export.outfile,r.payload={svg:e.svg}):r=ae(t,e,R),r.export.outfile=r.export?.outfile||`chart.${r.export?.type||"png"}`,r})(e,ne()),o=i.export;if(i.payload?.svg&&""!==i.payload.svg)try{return I(4,"[chart] Attempting to export from a SVG input."),ze(i.payload.svg.trim(),i,r)}catch(e){return r(new X("[chart] Error loading SVG input.").setError(e))}if(o.infile&&o.infile.length)try{return I(4,"[chart] Attempting to export from an input file."),i.export.instr=t.readFileSync(o.infile,"utf8"),ze(i.export.instr.trim(),i,r)}catch(e){return r(new X("[chart] Error loading input file.").setError(e))}if(o.instr&&""!==o.instr||o.options&&""!==o.options)try{return I(4,"[chart] Attempting to export from a raw input."),G(i.customLogic?.allowCodeExecution)?Ve(i,r):"string"==typeof o.instr?ze(o.instr.trim(),i,r):We(i,o.instr||o.options,r)}catch(e){return r(new X("[chart] Error loading raw input.").setError(e))}return r(new X("[chart] No valid input specified. Check if at least one of the following parameters is correctly set: 'infile', 'instr', 'options', or 'svg'."))},De=e=>{const{chart:t,exporting:r}=e.export?.options||j(e.export?.instr),i=j(e.export?.globalOptions);let o=e.export?.scale||r?.scale||i?.exporting?.scale||e.export?.defaultScale||1;o=Math.max(.1,Math.min(o,5)),o=((e,t=1)=>{const r=Math.pow(10,t||0);return Math.round(+e*r)/r})(o,2);const n={height:e.export?.height||r?.sourceHeight||t?.height||i?.exporting?.sourceHeight||i?.chart?.height||e.export?.defaultHeight||400,width:e.export?.width||r?.sourceWidth||t?.width||i?.exporting?.sourceWidth||i?.chart?.width||e.export?.defaultWidth||600,scale:o};for(let[e,t]of Object.entries(n))n[e]="string"==typeof t?+t.replace(/px|%/gi,""):t;return n},We=async(e,r,i,o)=>{let{export:n,customLogic:a}=e;const s="boolean"==typeof a.allowCodeExecution?a.allowCodeExecution:Ge;if(a){if(s)if("string"==typeof e.customLogic.resources)e.customLogic.resources=$(e.customLogic.resources,G(e.customLogic.allowFileResources));else if(!e.customLogic.resources)try{const r=t.readFileSync("resources.json","utf8");e.customLogic.resources=$(r,G(e.customLogic.allowFileResources))}catch(e){C(2,e,"[chart] Unable to load the default resources.json file.")}}else a=e.customLogic={};if(!s&&a){if(a.callback||a.resources||a.customCode)return i(new X("[chart] The 'callback', 'resources' and 'customCode' options have been disabled for this server."));a.callback=!1,a.resources=!1,a.customCode=!1}if(r&&(r.chart=r.chart||{},r.exporting=r.exporting||{},r.exporting.enabled=!1),n.constr=n.constr||"chart",n.type=H(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(t.readFileSync(n[e],"utf8"),!0):n[e]=j(n[e],!0))}catch(t){n[e]={},C(2,t,`[chart] The '${e}' cannot be loaded.`)}})),a.allowCodeExecution)try{a.customCode=q(a.customCode,a.allowFileResources)}catch(e){C(2,e,"[chart] The 'customCode' cannot be loaded.")}if(a&&a.callback&&a.callback?.indexOf("{")<0)if(a.allowFileResources)try{a.callback=t.readFileSync(a.callback,"utf8")}catch(e){a.callback=!1,C(2,e,"[chart] The 'callback' cannot be loaded.")}else a.callback=!1;e.export={...e.export,...De(e)};try{return i(!1,await Pe(n.strInj||r||o,e))}catch(e){return i(e)}},Ve=(e,t)=>{try{let r,i=e.export.instr||e.export.options;return"string"!=typeof i&&(r=i=F(i,e.customLogic?.allowCodeExecution)),r=i.replaceAll(/\t|\n|\r/g,"").trim(),";"===r[r.length-1]&&(r=r.substring(0,r.length-1)),e.export.strInj=r,We(e,!1,t)}catch(r){return t(new X(`[chart] Malformed input detected for ${e.export?.requestId||"?"}. Please make sure that your JSON/JavaScript options are sent using the "options" attribute, and that if you're using SVG, it is unescaped.`).setError(r))}},ze=(e,t,r)=>{const{allowCodeExecution:i}=t.customLogic;if(e.indexOf("=0||e.indexOf("=0)return I(4,"[chart] Parsing input as SVG."),We(t,!1,r,e);try{const i=JSON.parse(e.replaceAll(/\t|\n|\r/g," "));return We(t,i,r)}catch(e){return G(i)?Ve(t,r):r(new X("[chart] Only JSON configurations and SVG are allowed for this server. If this is your server, JavaScript custom code can be enabled by starting the server with the --allowCodeExecution flag.").setError(e))}},Xe=(e,t,r,i)=>{C(1,e),"development"!==z.NODE_ENV&&delete e.stack,i(e)},Ke=(e,t,r,i)=>{const{statusCode:o,status:n,message:a,stack:s}=e,l=o||n||500;r.status(l).json({statusCode:l,message:a,stack:s})};var Je=(e,t)=>{const r="Too many requests, you have been rate limited. Please try again later.",i={max:t.maxRequests||30,window:t.window||1,delay:t.delay||0,trustProxy:t.trustProxy||!1,skipKey:t.skipKey||!1,skipToken:t.skipToken||!1};i.trustProxy&&e.enable("trust proxy");const o=y({windowMs:60*i.window*1e3,max:i.max,delayMs:i.delay,handler:(e,t)=>{t.format({json:()=>{t.status(429).send({message:r})},default:()=>{t.status(429).send(r)}})},skip:e=>!1!==i.skipKey&&!1!==i.skipToken&&e.query.key===i.skipKey&&e.query.access_token===i.skipToken&&(I(4,"[rate limiting] Skipping rate limiter."),!0)});e.use(o),I(3,`[rate limiting] Enabled rate limiting with ${i.max} requests per ${i.window} minute for each IP, trusting proxy: ${i.trustProxy}.`)};class Be extends X{constructor(e,t){super(e),this.status=this.statusCode=t}setStatus(e){return this.status=e,this}}const Ye={png:"image/png",jpeg:"image/jpeg",gif:"image/gif",pdf:"application/pdf",svg:"image/svg+xml"};let Qe=0;const Ze=[],et=[],tt=(e,t,r,i)=>{let o=!0;const{id:n,uniqueId:a,type:s,body:l}=i;return e.some((e=>{if(e){let i=e(t,r,n,a,s,l);return void 0!==i&&!0!==i&&(o=i),!0}})),o},rt=async(e,t,r)=>{try{const r=D(),o=p.v4().replace(/-/g,""),n=ne(),a=e.body,s=++Qe;let l=H(a.type);if(!a||"object"==typeof(i=a)&&!Array.isArray(i)&&null!==i&&0===Object.keys(i).length)throw new Be("The request body is required. Please ensure that your Content-Type header is correct (accepted types are application/json and multipart/form-data).",400);let c=j(a.infile||a.options||a.data);if(!c&&!a.svg)throw I(2,`The request with ID ${o} from ${e.headers["x-forwarded-for"]||e.connection.remoteAddress} was incorrect. Payload received: ${JSON.stringify(a)}.`),new Be("No correct chart data found. Ensure that you are using either application/json or multipart/form-data headers. If sending JSON, make sure the chart data is in the 'infile', 'options', or 'data' attribute. If sending SVG, ensure it is in the 'svg' attribute.",400);let u=!1;if(u=tt(Ze,e,t,{id:s,uniqueId:o,type:l,body:a}),!0!==u)return t.send(u);let h=!1;e.socket.on("close",(()=>{h=!0})),I(4,`[export] Got an incoming HTTP request with ID ${o}.`),a.constr="string"==typeof a.constr&&a.constr||"chart";const d={export:{instr:c,type:l,constr:a.constr[0].toLowerCase()+a.constr.substr(1),height:a.height,width:a.width,scale:a.scale||n.export.scale,globalOptions:j(a.globalOptions,!0),themeOptions:j(a.themeOptions,!0)},customLogic:{allowCodeExecution:Ge,allowFileResources:!1,resources:j(a.resources,!0),callback:a.callback,customCode:a.customCode}};c&&(d.export.instr=F(c,d.customLogic.allowCodeExecution));const g=ae(n,d);if(g.export.options=c,g.payload={svg:a.svg||!1,b64:a.b64||!1,noDownload:a.noDownload||!1,requestId:o},a.svg&&(e=>[/xlink:href="(?:http:\/\/|https:\/\/)?localhost\b/,/xlink:href="(?:http:\/\/|https:\/\/)?10\.\d{1,3}\.\d{1,3}\.\d{1,3}\b/,/xlink:href="(?:http:\/\/|https:\/\/)?127\.\d{1,3}\.\d{1,3}\.\d{1,3}\b/,/xlink:href="(?:http:\/\/|https:\/\/)?172\.(1[6-9]|2[0-9]|3[0-1])\.\d{1,3}\.\d{1,3}\b/,/xlink:href="(?:http:\/\/|https:\/\/)?192\.168\.\d{1,3}\.\d{1,3}\b/].some((t=>t.test(e))))(g.payload.svg))throw new Be("SVG potentially contain at least one forbidden URL in xlink:href element. Please review the SVG content and ensure that all referenced URLs comply with security policies.",400);await qe(g,((i,c)=>{if(e.socket.removeAllListeners("close"),n.server.benchmarking&&I(5,`[benchmark] Request with ID ${o} - After the whole exporting process: ${r()}ms.`),h)return I(3,"[export] The client closed the connection before the chart finished processing.");if(i)throw i;if(!c||!c.result)throw new Be(`Unexpected return from chart generation. Please check your request data. For the request with ID ${o}, the result is ${c.result}.`,400);return l=c.options.export.type,tt(et,e,t,{id:s,body:c.result}),c.result?a.b64?"pdf"===l||"svg"==l?t.send(Buffer.from(c.result,"utf8").toString("base64")):t.send(c.result):(t.header("Content-Type",Ye[l]||"image/png"),a.noDownload||t.attachment(`${e.params.filename||e.body.filename||"chart"}.${l||"png"}`),"svg"===l?t.send(c.result):t.send(Buffer.from(c.result,"base64"))):void 0}))}catch(e){r(e)}var i};const it=JSON.parse(t.readFileSync(r.join(P,"package.json"))),ot=new Date;const nt=m();nt.disable("x-powered-by"),nt.use(g());const at=f.memoryStorage(),st=f({storage:at,limits:{fieldSize:52428800}});nt.use(m.json({limit:52428800})),nt.use(m.urlencoded({extended:!0,limit:52428800})),nt.use(st.none());const lt=e=>{e.on("clientError",(e=>{C(1,e,`[server] Client error: ${e.message}`)})),e.on("error",(e=>{C(1,e,`[server] Server error: ${e.message}`)})),e.on("connection",(e=>{e.on("error",(e=>{C(1,e,`[server] Socket error: ${e.message}`)}))}))},ct=async e=>{try{if(!e.enable)return!1;if(!e.ssl.force){const t=o.createServer(nt);lt(t),t.listen(e.port,e.host),I(3,`[server] Started HTTP server on ${e.host}:${e.port}.`)}if(e.ssl.enable){let i,o;try{i=await t.promises.readFile(r.posix.join(e.ssl.certPath,"server.key"),"utf8"),o=await t.promises.readFile(r.posix.join(e.ssl.certPath,"server.crt"),"utf8")}catch(t){I(2,`[server] Unable to load key/certificate from the '${e.ssl.certPath}' path. Could not run secured layer server.`)}if(i&&o){const t=n.createServer({key:i,cert:o},nt);lt(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)&&Je(nt,e.rateLimiting),nt.use(m.static(r.posix.join(P,"public"))),(e=>{!!e&&e.get("/health",((e,t)=>{t.send({status:"OK",bootTime:ot,uptime:Math.floor(((new Date).getTime()-ot.getTime())/1e3/60)+" minutes",version:it.version,highchartsVersion:ie(),averageProcessingTime:Fe(),performedExports:Me(),failedExports:Ue(),exportAttempts:je(),sucessRatio:Me()/je()*100,pool:$e()})}))})(nt),(e=>{e.post("/",rt),e.post("/:filename",rt)})(nt),(e=>{!!e&&e.get("/",((e,t)=>{t.sendFile(r.join(P,"public","index.html"))}))})(nt),(e=>{!!e&&e.post("/version/change/:newVersion",(async(e,t,r)=>{try{const r=z.HIGHCHARTS_ADMIN_TOKEN;if(!r||!r.length)throw new Be("The server is not configured to perform run-time version changes: HIGHCHARTS_ADMIN_TOKEN is not set.",401);const i=e.get("hc-auth");if(!i||i!==r)throw new Be("Invalid or missing token: Set the token in the hc-auth header.",401);const o=e.params.newVersion;if(!o)throw new Be("No new version supplied.",400);try{await te(o)}catch(e){throw new Be(`Version change: ${e.message}`,e.statusCode).setError(e)}t.status(200).send({statusCode:200,version:ie(),message:`Successfully updated Highcharts to version: ${o}.`})}catch(e){r(e)}}))})(nt),(e=>{e.use(Xe),e.use(Ke)})(nt)}catch(e){throw new X("[server] Could not configure and start the server.").setError(e)}};var pt={startServer:ct,enableRateLimiting:e=>Je(nt,e),getExpress:()=>m,getApp:()=>nt,use:(e,...t)=>{nt.use(e,...t)},get:(e,...t)=>{nt.get(e,...t)},post:(e,...t)=>{nt.post(e,...t)}};e.config();var ut={server:pt,startServer:ct,setOptions:(e,r)=>(r?.length&&(oe=function(e){const r=e.findIndex((e=>"loadConfig"===e.replace(/-/g,"")));if(r>-1&&e[r+1]){const i=e[r+1];try{if(i&&i.endsWith(".json"))return JSON.parse(t.readFileSync(i))}catch(e){C(2,e,`[config] Unable to load the configuration from the ${i} file.`)}}return{}}(r)),se(T,oe),oe=le(T),e&&(oe=ae(oe,e,R)),r?.length&&(oe=function(e,t,r){let i=!1;for(let o=0;o(a.length-1===r&&(s=e[t].type),e[t])),r),a.reduce(((e,r,l)=>(a.length-1===l&&void 0!==e[r]&&(t[++o]?"boolean"===s?e[r]=G(t[o]):"number"===s?e[r]=+t[o]:s.indexOf("]")>=0?e[r]=t[o].split(","):e[r]=t[o]:(I(2,`[config] Missing value for the '${n}' argument. Using the default value.`),i=!0)),e[r])),e)}i&&M();return e}(oe,r,T)),oe),initExport:async e=>{var t;return t=e.customLogic&&e.customLogic.allowCodeExecution,Ge=G(t),(e=>{A(e&&parseInt(e.level)),e&&e.dest&&N(e.dest,e.file||"highcharts-export-server.log")})(e.logging),await Z(e.highcharts||{version:"latest"}),await Ae({pool:e.pool||{minWorkers:1,maxWorkers:1},puppeteerArgs:e.puppeteer?.args||[]}),e},singleExport:async e=>{e.export.instr=e.export.instr||e.export.options,await qe(e,(async(e,r)=>{if(e)throw e;const{outfile:i,type:o}=r.options.export;t.writeFileSync(i||`chart.${o}`,"svg"!==o?Buffer.from(r.result,"base64"):r.result),await Ne()}))},batchExport:async e=>{const r=[];for(let i of e.export.batch.split(";"))i=i.split("="),2===i.length&&r.push(qe({...e,export:{...e.export,infile:i[0],outfile:i[1]}},((e,r)=>{if(e)throw e;t.writeFileSync(r.options.export.outfile,Buffer.from(r.result,"base64"))})));try{await Promise.all(r),await Ne()}catch(e){throw new X("[chart] Error encountered during batch export.").setError(e)}},startExport:qe,killPool:Ne,log:I,logWithStack:C,setLogLevel:A,enableFileLogging:N,mapToNewConfig:e=>{const t={};for(const[r,i]of Object.entries(e)){const e=x[r]?x[r].split("."):[];e.reduce(((t,r,o)=>t[r]=e.length-1===o?i:t[r]||{}),t)}return t},manualConfig:async e=>{let r={};t.existsSync(e)&&(r=JSON.parse(t.readFileSync(e,"utf8")));const i=Object.keys(S).map((e=>({title:`${e} options`,value:e})));return l({type:"multiselect",name:"category",message:"Which category do you want to configure?",hint:"Space: Select specific, A: Select all, Enter: Confirm.",instructions:"",choices:i},{onSubmit:async(i,o)=>{let n=0,a=[];for(const e of o)S[e]=S[e].map((t=>({...t,section:e}))),a=[...a,...S[e]];return await l(a,{onSubmit:async(i,o)=>{if("modules"===i.name?(o=o.length?o.map((e=>i.choices[e])):i.choices,r[i.section][i.name]=o):r[i.section]=ce(Object.assign({},r[i.section]||{}),i.name.split("."),i.choices?i.choices[o]:o),++n===a.length){try{await t.promises.writeFile(e,JSON.stringify(r,null,2),"utf8")}catch(t){C(1,t,`[config] An error occurred while creating the ${e} file.`)}return!0}}}),!0}})},printLogo:e=>{const i=JSON.parse(t.readFileSync(r.join(P,"package.json"))).version;e?console.log(`Starting Highcharts Export Server v${i}...`):console.log(t.readFileSync(P+"/msg/startup.msg").toString().bold.yellow,`v${i}`)},printUsage:M};module.exports=ut;
-//# sourceMappingURL=data:application/json;charset=utf-8;base64,eyJ2ZXJzaW9uIjozLCJmaWxlIjoiaW5kZXguY2pzIiwic291cmNlcyI6WyIuLi9saWIvZmV0Y2guanMiLCIuLi9saWIvc2NoZW1hcy9jb25maWcuanMiLCIuLi9saWIvbG9nZ2VyLmpzIiwiLi4vbGliL3V0aWxzLmpzIiwiLi4vbGliL2VudnMuanMiLCIuLi9saWIvZXJyb3JzL0V4cG9ydEVycm9yLmpzIiwiLi4vbGliL2NhY2hlLmpzIiwiLi4vbGliL2NvbmZpZy5qcyIsIi4uL2xpYi9icm93c2VyLmpzIiwiLi4vbGliL2V4cG9ydC5qcyIsIi4uL3RlbXBsYXRlcy9zdmdfZXhwb3J0L3N2Z19leHBvcnQuanMiLCIuLi9saWIvcG9vbC5qcyIsIi4uL2xpYi9jaGFydC5qcyIsIi4uL2xpYi9zZXJ2ZXIvZXJyb3IuanMiLCIuLi9saWIvc2VydmVyL3JhdGVfbGltaXQuanMiLCIuLi9saWIvZXJyb3JzL0h0dHBFcnJvci5qcyIsIi4uL2xpYi9zZXJ2ZXIvcm91dGVzL2V4cG9ydC5qcyIsIi4uL2xpYi9zZXJ2ZXIvcm91dGVzL2hlYWx0aC5qcyIsIi4uL2xpYi9zZXJ2ZXIvc2VydmVyLmpzIiwiLi4vbGliL3NlcnZlci9yb3V0ZXMvdWkuanMiLCIuLi9saWIvc2VydmVyL3JvdXRlcy9jaGFuZ2VfaGNfdmVyc2lvbi5qcyIsIi4uL2xpYi9pbmRleC5qcyJdLCJzb3VyY2VzQ29udGVudCI6WyIvKipcclxuICogVGhpcyBtb2R1bGUgZXhwb3J0cyB0d28gZnVuY3Rpb25zOiBmZXRjaCAoZm9yIEdFVCByZXF1ZXN0cykgYW5kIHBvc3QgKGZvciBQT1NUIHJlcXVlc3RzKS5cclxuICovXHJcblxyXG5pbXBvcnQgaHR0cCBmcm9tICdodHRwJztcclxuaW1wb3J0IGh0dHBzIGZyb20gJ2h0dHBzJztcclxuXHJcbi8qKlxyXG4gKiBSZXR1cm5zIHRoZSBIVFRQIG9yIEhUVFBTIHByb3RvY29sIG1vZHVsZSBiYXNlZCBvbiB0aGUgcHJvdmlkZWQgVVJMLlxyXG4gKlxyXG4gKiBAcGFyYW0ge3N0cmluZ30gdXJsIC0gVGhlIFVSTCB0byBkZXRlcm1pbmUgdGhlIHByb3RvY29sLlxyXG4gKlxyXG4gKiBAcmV0dXJucyB7T2JqZWN0fSBUaGUgSFRUUCBvciBIVFRQUyBwcm90b2NvbCBtb2R1bGUgKGh0dHAgb3IgaHR0cHMpLlxyXG4gKi9cclxuY29uc3QgZ2V0UHJvdG9jb2wgPSAodXJsKSA9PiAodXJsLnN0YXJ0c1dpdGgoJ2h0dHBzJykgPyBodHRwcyA6IGh0dHApO1xyXG5cclxuLyoqXHJcbiAqIEZldGNoZXMgZGF0YSBmcm9tIHRoZSBzcGVjaWZpZWQgVVJMIHVzaW5nIGVpdGhlciBIVFRQIG9yIEhUVFBTIHByb3RvY29sLlxyXG4gKlxyXG4gKiBAcGFyYW0ge3N0cmluZ30gdXJsIC0gVGhlIFVSTCB0byBmZXRjaCBkYXRhIGZyb20uXHJcbiAqIEBwYXJhbSB7T2JqZWN0fSByZXF1ZXN0T3B0aW9ucyAtIE9wdGlvbnMgZm9yIHRoZSBIVFRQIHJlcXVlc3QgKG9wdGlvbmFsKS5cclxuICpcclxuICogQHJldHVybnMge1Byb21pc2U8T2JqZWN0Pn0gUHJvbWlzZSByZXNvbHZpbmcgdG8gdGhlIEhUVFAgcmVzcG9uc2Ugb2JqZWN0XHJcbiAqIHdpdGggYWRkZWQgJ3RleHQnIHByb3BlcnR5IG9yIHJlamVjdGluZyB3aXRoIGFuIGVycm9yLlxyXG4gKi9cclxuYXN5bmMgZnVuY3Rpb24gZmV0Y2godXJsLCByZXF1ZXN0T3B0aW9ucyA9IHt9KSB7XHJcbiAgcmV0dXJuIG5ldyBQcm9taXNlKChyZXNvbHZlLCByZWplY3QpID0+IHtcclxuICAgIGNvbnN0IHByb3RvY29sID0gZ2V0UHJvdG9jb2wodXJsKTtcclxuXHJcbiAgICBwcm90b2NvbFxyXG4gICAgICAuZ2V0KHVybCwgcmVxdWVzdE9wdGlvbnMsIChyZXMpID0+IHtcclxuICAgICAgICBsZXQgZGF0YSA9ICcnO1xyXG5cclxuICAgICAgICAvLyBBIGNodW5rIG9mIGRhdGEgaGFzIGJlZW4gcmVjZWl2ZWQuXHJcbiAgICAgICAgcmVzLm9uKCdkYXRhJywgKGNodW5rKSA9PiB7XHJcbiAgICAgICAgICBkYXRhICs9IGNodW5rO1xyXG4gICAgICAgIH0pO1xyXG5cclxuICAgICAgICAvLyBUaGUgd2hvbGUgcmVzcG9uc2UgaGFzIGJlZW4gcmVjZWl2ZWQuXHJcbiAgICAgICAgcmVzLm9uKCdlbmQnLCAoKSA9PiB7XHJcbiAgICAgICAgICBpZiAoIWRhdGEpIHtcclxuICAgICAgICAgICAgcmVqZWN0KCdOb3RoaW5nIHdhcyBmZXRjaGVkIGZyb20gdGhlIFVSTC4nKTtcclxuICAgICAgICAgIH1cclxuXHJcbiAgICAgICAgICByZXMudGV4dCA9IGRhdGE7XHJcbiAgICAgICAgICByZXNvbHZlKHJlcyk7XHJcbiAgICAgICAgfSk7XHJcbiAgICAgIH0pXHJcbiAgICAgIC5vbignZXJyb3InLCAoZXJyb3IpID0+IHtcclxuICAgICAgICByZWplY3QoZXJyb3IpO1xyXG4gICAgICB9KTtcclxuICB9KTtcclxufVxyXG5cclxuLyoqXHJcbiAqIFNlbmRzIGEgUE9TVCByZXF1ZXN0IHRvIHRoZSBzcGVjaWZpZWQgVVJMIHdpdGggdGhlIHByb3ZpZGVkIEpTT04gYm9keSB1c2luZ1xyXG4gKiBlaXRoZXIgSFRUUCBvciBIVFRQUyBwcm90b2NvbC5cclxuICpcclxuICogQHBhcmFtIHtzdHJpbmd9IHVybCAtIFRoZSBVUkwgdG8gc2VuZCB0aGUgUE9TVCByZXF1ZXN0IHRvLlxyXG4gKiBAcGFyYW0ge09iamVjdH0gYm9keSAtIFRoZSBKU09OIGJvZHkgdG8gaW5jbHVkZSBpbiB0aGUgUE9TVCByZXF1ZXN0XHJcbiAqIChvcHRpb25hbCwgZGVmYXVsdCBpcyBhbiBlbXB0eSBvYmplY3QpLlxyXG4gKiBAcGFyYW0ge09iamVjdH0gcmVxdWVzdE9wdGlvbnMgLSBPcHRpb25zIGZvciB0aGUgSFRUUCByZXF1ZXN0IChvcHRpb25hbCkuXHJcbiAqXHJcbiAqIEByZXR1cm5zIHtQcm9taXNlPE9iamVjdD59IFByb21pc2UgcmVzb2x2aW5nIHRvIHRoZSBIVFRQIHJlc3BvbnNlIG9iamVjdCB3aXRoXHJcbiAqIGFkZGVkICd0ZXh0JyBwcm9wZXJ0eSBvciByZWplY3Rpbmcgd2l0aCBhbiBlcnJvci5cclxuICovXHJcbmFzeW5jIGZ1bmN0aW9uIHBvc3QodXJsLCBib2R5ID0ge30sIHJlcXVlc3RPcHRpb25zID0ge30pIHtcclxuICByZXR1cm4gbmV3IFByb21pc2UoKHJlc29sdmUsIHJlamVjdCkgPT4ge1xyXG4gICAgY29uc3QgcHJvdG9jb2wgPSBnZXRQcm90b2NvbCh1cmwpO1xyXG4gICAgY29uc3QgZGF0YSA9IEpTT04uc3RyaW5naWZ5KGJvZHkpO1xyXG5cclxuICAgIC8vIFNldCBkZWZhdWx0IGhlYWRlcnMgYW5kIG1lcmdlIHdpdGggcmVxdWVzdE9wdGlvbnNcclxuICAgIGNvbnN0IG9wdGlvbnMgPSBPYmplY3QuYXNzaWduKFxyXG4gICAgICB7XHJcbiAgICAgICAgbWV0aG9kOiAnUE9TVCcsXHJcbiAgICAgICAgaGVhZGVyczoge1xyXG4gICAgICAgICAgJ0NvbnRlbnQtVHlwZSc6ICdhcHBsaWNhdGlvbi9qc29uJyxcclxuICAgICAgICAgICdDb250ZW50LUxlbmd0aCc6IGRhdGEubGVuZ3RoXHJcbiAgICAgICAgfVxyXG4gICAgICB9LFxyXG4gICAgICByZXF1ZXN0T3B0aW9uc1xyXG4gICAgKTtcclxuXHJcbiAgICBjb25zdCByZXEgPSBwcm90b2NvbFxyXG4gICAgICAucmVxdWVzdCh1cmwsIG9wdGlvbnMsIChyZXMpID0+IHtcclxuICAgICAgICBsZXQgcmVzcG9uc2VEYXRhID0gJyc7XHJcblxyXG4gICAgICAgIC8vIEEgY2h1bmsgb2YgZGF0YSBoYXMgYmVlbiByZWNlaXZlZC5cclxuICAgICAgICByZXMub24oJ2RhdGEnLCAoY2h1bmspID0+IHtcclxuICAgICAgICAgIHJlc3BvbnNlRGF0YSArPSBjaHVuaztcclxuICAgICAgICB9KTtcclxuXHJcbiAgICAgICAgLy8gVGhlIHdob2xlIHJlc3BvbnNlIGhhcyBiZWVuIHJlY2VpdmVkLlxyXG4gICAgICAgIHJlcy5vbignZW5kJywgKCkgPT4ge1xyXG4gICAgICAgICAgdHJ5IHtcclxuICAgICAgICAgICAgcmVzLnRleHQgPSByZXNwb25zZURhdGE7XHJcbiAgICAgICAgICAgIHJlc29sdmUocmVzKTtcclxuICAgICAgICAgIH0gY2F0Y2ggKGVycm9yKSB7XHJcbiAgICAgICAgICAgIHJlamVjdChlcnJvcik7XHJcbiAgICAgICAgICB9XHJcbiAgICAgICAgfSk7XHJcbiAgICAgIH0pXHJcbiAgICAgIC5vbignZXJyb3InLCAoZXJyb3IpID0+IHtcclxuICAgICAgICByZWplY3QoZXJyb3IpO1xyXG4gICAgICB9KTtcclxuXHJcbiAgICAvLyBXcml0ZSB0aGUgcmVxdWVzdCBib2R5IGFuZCBlbmQgdGhlIHJlcXVlc3QuXHJcbiAgICByZXEud3JpdGUoZGF0YSk7XHJcbiAgICByZXEuZW5kKCk7XHJcbiAgfSk7XHJcbn1cclxuXHJcbmV4cG9ydCBkZWZhdWx0IGZldGNoO1xyXG5leHBvcnQgeyBmZXRjaCwgcG9zdCB9O1xyXG4iLCIvKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKlxyXG5cclxuSGlnaGNoYXJ0cyBFeHBvcnQgU2VydmVyXHJcblxyXG5Db3B5cmlnaHQgKGMpIDIwMTYtMjAyNCwgSGlnaHNvZnRcclxuXHJcbkxpY2VuY2VkIHVuZGVyIHRoZSBNSVQgbGljZW5jZS5cclxuXHJcbkFkZGl0aW9uYWxseSBhIHZhbGlkIEhpZ2hjaGFydHMgbGljZW5zZSBpcyByZXF1aXJlZCBmb3IgdXNlLlxyXG5cclxuU2VlIExJQ0VOU0UgZmlsZSBpbiByb290IGZvciBkZXRhaWxzLlxyXG5cclxuKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKi9cclxuXHJcbi8vIFRoaXMgaXMgdGhlIGNvbmZpZ3VyYXRpb24gb2JqZWN0IHdpdGggYWxsIG9wdGlvbnMgYW5kIHRoZWlyIGRlZmF1bHQgdmFsdWVzLFxyXG4vLyBhbHNvIGZyb20gdGhlIC5lbnYgZmlsZSBpZiBvbmUgZXhpc3RzXHJcbmV4cG9ydCBjb25zdCBkZWZhdWx0Q29uZmlnID0ge1xyXG4gIHB1cHBldGVlcjoge1xyXG4gICAgYXJnczoge1xyXG4gICAgICB2YWx1ZTogW10sXHJcbiAgICAgIHR5cGU6ICdzdHJpbmdbXScsXHJcbiAgICAgIGRlc2NyaXB0aW9uOiAnQXJndW1lbnRzIGFycmF5IHRvIHNlbmQgdG8gUHVwcGV0ZWVyLidcclxuICAgIH1cclxuICB9LFxyXG4gIGhpZ2hjaGFydHM6IHtcclxuICAgIHZlcnNpb246IHtcclxuICAgICAgdmFsdWU6ICdsYXRlc3QnLFxyXG4gICAgICBlbnZMaW5rOiAnSElHSENIQVJUU19WRVJTSU9OJyxcclxuICAgICAgdHlwZTogJ3N0cmluZycsXHJcbiAgICAgIGRlc2NyaXB0aW9uOiAnVGhlIEhpZ2hjaGFydHMgdmVyc2lvbiB0byBiZSB1c2VkLidcclxuICAgIH0sXHJcbiAgICBjZG5VUkw6IHtcclxuICAgICAgdmFsdWU6ICdodHRwczovL2NvZGUuaGlnaGNoYXJ0cy5jb20vJyxcclxuICAgICAgZW52TGluazogJ0hJR0hDSEFSVFNfQ0ROX1VSTCcsXHJcbiAgICAgIHR5cGU6ICdzdHJpbmcnLFxyXG4gICAgICBkZXNjcmlwdGlvbjogJ1RoZSBDRE4gVVJMIGZvciBIaWdoY2hhcnRzIHNjcmlwdHMgdG8gYmUgdXNlZC4nXHJcbiAgICB9LFxyXG4gICAgY29yZVNjcmlwdHM6IHtcclxuICAgICAgZW52TGluazogJ0hJR0hDSEFSVFNfQ09SRV9TQ1JJUFRTJyxcclxuICAgICAgdmFsdWU6IFsnaGlnaGNoYXJ0cycsICdoaWdoY2hhcnRzLW1vcmUnLCAnaGlnaGNoYXJ0cy0zZCddLFxyXG4gICAgICB0eXBlOiAnc3RyaW5nW10nLFxyXG4gICAgICBkZXNjcmlwdGlvbjogJ1RoZSBjb3JlIEhpZ2hjaGFydHMgc2NyaXB0cyB0byBmZXRjaC4nXHJcbiAgICB9LFxyXG4gICAgbW9kdWxlczoge1xyXG4gICAgICBlbnZMaW5rOiAnSElHSENIQVJUU19NT0RVTEVTJyxcclxuICAgICAgdmFsdWU6IFtcclxuICAgICAgICAnc3RvY2snLFxyXG4gICAgICAgICdtYXAnLFxyXG4gICAgICAgICdnYW50dCcsXHJcbiAgICAgICAgJ2V4cG9ydGluZycsXHJcbiAgICAgICAgJ2V4cG9ydC1kYXRhJyxcclxuICAgICAgICAncGFyYWxsZWwtY29vcmRpbmF0ZXMnLFxyXG4gICAgICAgICdhY2Nlc3NpYmlsaXR5JyxcclxuICAgICAgICAnYW5ub3RhdGlvbnMtYWR2YW5jZWQnLFxyXG4gICAgICAgICdib29zdC1jYW52YXMnLFxyXG4gICAgICAgICdib29zdCcsXHJcbiAgICAgICAgJ2RhdGEnLFxyXG4gICAgICAgICdkYXRhLXRvb2xzJyxcclxuICAgICAgICAnZHJhZ2dhYmxlLXBvaW50cycsXHJcbiAgICAgICAgJ3N0YXRpYy1zY2FsZScsXHJcbiAgICAgICAgJ2Jyb2tlbi1heGlzJyxcclxuICAgICAgICAnaGVhdG1hcCcsXHJcbiAgICAgICAgJ3RpbGVtYXAnLFxyXG4gICAgICAgICd0aWxlZHdlYm1hcCcsXHJcbiAgICAgICAgJ3RpbWVsaW5lJyxcclxuICAgICAgICAndHJlZW1hcCcsXHJcbiAgICAgICAgJ3RyZWVncmFwaCcsXHJcbiAgICAgICAgJ2l0ZW0tc2VyaWVzJyxcclxuICAgICAgICAnZHJpbGxkb3duJyxcclxuICAgICAgICAnaGlzdG9ncmFtLWJlbGxjdXJ2ZScsXHJcbiAgICAgICAgJ2J1bGxldCcsXHJcbiAgICAgICAgJ2Z1bm5lbCcsXHJcbiAgICAgICAgJ2Z1bm5lbDNkJyxcclxuICAgICAgICAnZ2VvaGVhdG1hcCcsXHJcbiAgICAgICAgJ3B5cmFtaWQzZCcsXHJcbiAgICAgICAgJ25ldHdvcmtncmFwaCcsXHJcbiAgICAgICAgJ292ZXJsYXBwaW5nLWRhdGFsYWJlbHMnLFxyXG4gICAgICAgICdwYXJldG8nLFxyXG4gICAgICAgICdwYXR0ZXJuLWZpbGwnLFxyXG4gICAgICAgICdwaWN0b3JpYWwnLFxyXG4gICAgICAgICdwcmljZS1pbmRpY2F0b3InLFxyXG4gICAgICAgICdzYW5rZXknLFxyXG4gICAgICAgICdhcmMtZGlhZ3JhbScsXHJcbiAgICAgICAgJ2RlcGVuZGVuY3ktd2hlZWwnLFxyXG4gICAgICAgICdzZXJpZXMtbGFiZWwnLFxyXG4gICAgICAgICdzb2xpZC1nYXVnZScsXHJcbiAgICAgICAgJ3NvbmlmaWNhdGlvbicsXHJcbiAgICAgICAgJ3N0b2NrLXRvb2xzJyxcclxuICAgICAgICAnc3RyZWFtZ3JhcGgnLFxyXG4gICAgICAgICdzdW5idXJzdCcsXHJcbiAgICAgICAgJ3ZhcmlhYmxlLXBpZScsXHJcbiAgICAgICAgJ3Zhcml3aWRlJyxcclxuICAgICAgICAndmVjdG9yJyxcclxuICAgICAgICAndmVubicsXHJcbiAgICAgICAgJ3dpbmRiYXJiJyxcclxuICAgICAgICAnd29yZGNsb3VkJyxcclxuICAgICAgICAneHJhbmdlJyxcclxuICAgICAgICAnbm8tZGF0YS10by1kaXNwbGF5JyxcclxuICAgICAgICAnZHJhZy1wYW5lcycsXHJcbiAgICAgICAgJ2RlYnVnZ2VyJyxcclxuICAgICAgICAnZHVtYmJlbGwnLFxyXG4gICAgICAgICdsb2xsaXBvcCcsXHJcbiAgICAgICAgJ2N5bGluZGVyJyxcclxuICAgICAgICAnb3JnYW5pemF0aW9uJyxcclxuICAgICAgICAnZG90cGxvdCcsXHJcbiAgICAgICAgJ21hcmtlci1jbHVzdGVycycsXHJcbiAgICAgICAgJ2hvbGxvd2NhbmRsZXN0aWNrJyxcclxuICAgICAgICAnaGVpa2luYXNoaScsXHJcbiAgICAgICAgJ2Zsb3dtYXAnXHJcbiAgICAgIF0sXHJcbiAgICAgIHR5cGU6ICdzdHJpbmdbXScsXHJcbiAgICAgIGRlc2NyaXB0aW9uOiAnVGhlIG1vZHVsZXMgb2YgSGlnaGNoYXJ0cyB0byBmZXRjaC4nXHJcbiAgICB9LFxyXG4gICAgaW5kaWNhdG9yczoge1xyXG4gICAgICBlbnZMaW5rOiAnSElHSENIQVJUU19JTkRJQ0FUT1JTJyxcclxuICAgICAgdmFsdWU6IFsnaW5kaWNhdG9ycy1hbGwnXSxcclxuICAgICAgdHlwZTogJ3N0cmluZ1tdJyxcclxuICAgICAgZGVzY3JpcHRpb246ICdUaGUgaW5kaWNhdG9ycyBvZiBIaWdoY2hhcnRzIHRvIGZldGNoLidcclxuICAgIH0sXHJcbiAgICBzY3JpcHRzOiB7XHJcbiAgICAgIHZhbHVlOiBbXHJcbiAgICAgICAgJ2h0dHBzOi8vY2RuanMuY2xvdWRmbGFyZS5jb20vYWpheC9saWJzL21vbWVudC5qcy8yLjI5LjQvbW9tZW50Lm1pbi5qcycsXHJcbiAgICAgICAgJ2h0dHBzOi8vY2RuanMuY2xvdWRmbGFyZS5jb20vYWpheC9saWJzL21vbWVudC10aW1lem9uZS8wLjUuMzQvbW9tZW50LXRpbWV6b25lLXdpdGgtZGF0YS5taW4uanMnXHJcbiAgICAgIF0sXHJcbiAgICAgIHR5cGU6ICdzdHJpbmdbXScsXHJcbiAgICAgIGRlc2NyaXB0aW9uOiAnQWRkaXRpb25hbCBvcHRpb25hbCBzY3JpcHRzIG9yIGRlcGVuZGVuY2llcyB0byBmZXRjaC4nXHJcbiAgICB9LFxyXG4gICAgZm9yY2VGZXRjaDoge1xyXG4gICAgICBlbnZMaW5rOiAnSElHSENIQVJUU19GT1JDRV9GRVRDSCcsXHJcbiAgICAgIHZhbHVlOiBmYWxzZSxcclxuICAgICAgdHlwZTogJ2Jvb2xlYW4nLFxyXG4gICAgICBkZXNjcmlwdGlvbjpcclxuICAgICAgICAnVGhlIGZsYWcgdG8gZGV0ZXJtaW5lIHdoZXRoZXIgdG8gcmVmZXRjaCBhbGwgc2NyaXB0cyBhZnRlciBlYWNoIHNlcnZlciByZXJ1bi4nXHJcbiAgICB9LFxyXG4gICAgY2FjaGVQYXRoOiB7XHJcbiAgICAgIGVudkxpbms6ICdISUdIQ0hBUlRTX0NBQ0hFX1BBVEgnLFxyXG4gICAgICB2YWx1ZTogJy5jYWNoZScsXHJcbiAgICAgIHR5cGU6ICdzdHJpbmcnLFxyXG4gICAgICBkZXNjcmlwdGlvbjpcclxuICAgICAgICAnVGhlIHBhdGggdG8gdGhlIGNhY2hlIGRpcmVjdG9yeS4gSXQgaXMgdXNlZCB0byBzdG9yZSB0aGUgSGlnaGNoYXJ0cyBzY3JpcHRzIGFuZCBjdXN0b20gc2NyaXB0cy4nXHJcbiAgICB9XHJcbiAgfSxcclxuICBleHBvcnQ6IHtcclxuICAgIGluZmlsZToge1xyXG4gICAgICB2YWx1ZTogZmFsc2UsXHJcbiAgICAgIHR5cGU6ICdzdHJpbmcnLFxyXG4gICAgICBkZXNjcmlwdGlvbjpcclxuICAgICAgICAnVGhlIGlucHV0IGZpbGUgc2hvdWxkIGluY2x1ZGUgYSBuYW1lIGFuZCBhIHR5cGUgKGpzb24gb3Igc3ZnKS4gSXQgbXVzdCBiZSBjb3JyZWN0bHkgZm9ybWF0dGVkIGFzIGEgSlNPTiBvciBTVkcgZmlsZS4nXHJcbiAgICB9LFxyXG4gICAgaW5zdHI6IHtcclxuICAgICAgdmFsdWU6IGZhbHNlLFxyXG4gICAgICB0eXBlOiAnc3RyaW5nJyxcclxuICAgICAgZGVzY3JpcHRpb246XHJcbiAgICAgICAgJ0lucHV0LCBwcm92aWRlZCBpbiB0aGUgZm9ybSBvZiBhIHN0cmluZ2lmaWVkIEpTT04gb3IgU1ZHIGZpbGUsIHdpbGwgb3ZlcnJpZGUgdGhlIC0taW5maWxlIG9wdGlvbi4nXHJcbiAgICB9LFxyXG4gICAgb3B0aW9uczoge1xyXG4gICAgICB2YWx1ZTogZmFsc2UsXHJcbiAgICAgIHR5cGU6ICdzdHJpbmcnLFxyXG4gICAgICBkZXNjcmlwdGlvbjogJ0FuIGFsaWFzIGZvciB0aGUgLS1pbnN0ciBvcHRpb24uJ1xyXG4gICAgfSxcclxuICAgIG91dGZpbGU6IHtcclxuICAgICAgdmFsdWU6IGZhbHNlLFxyXG4gICAgICB0eXBlOiAnc3RyaW5nJyxcclxuICAgICAgZGVzY3JpcHRpb246XHJcbiAgICAgICAgJ1RoZSBvdXRwdXQgZmlsZW5hbWUgYWxvbmcgd2l0aCBhIHR5cGUgKGpwZWcsIHBuZywgcGRmLCBvciBzdmcpLiBUaGlzIHdpbGwgaWdub3JlIHRoZSAtLXR5cGUgZmxhZy4nXHJcbiAgICB9LFxyXG4gICAgdHlwZToge1xyXG4gICAgICBlbnZMaW5rOiAnRVhQT1JUX1RZUEUnLFxyXG4gICAgICB2YWx1ZTogJ3BuZycsXHJcbiAgICAgIHR5cGU6ICdzdHJpbmcnLFxyXG4gICAgICBkZXNjcmlwdGlvbjogJ1RoZSBmaWxlIGV4cG9ydCBmb3JtYXQuIEl0IGNhbiBiZSBqcGVnLCBwbmcsIHBkZiwgb3Igc3ZnLidcclxuICAgIH0sXHJcbiAgICBjb25zdHI6IHtcclxuICAgICAgZW52TGluazogJ0VYUE9SVF9DT05TVFInLFxyXG4gICAgICB2YWx1ZTogJ2NoYXJ0JyxcclxuICAgICAgdHlwZTogJ3N0cmluZycsXHJcbiAgICAgIGRlc2NyaXB0aW9uOlxyXG4gICAgICAgICdUaGUgY29uc3RydWN0b3IgdG8gdXNlLiBDYW4gYmUgY2hhcnQsIHN0b2NrQ2hhcnQsIG1hcENoYXJ0LCBvciBnYW50dENoYXJ0LidcclxuICAgIH0sXHJcbiAgICBkZWZhdWx0SGVpZ2h0OiB7XHJcbiAgICAgIGVudkxpbms6ICdFWFBPUlRfREVGQVVMVF9IRUlHSFQnLFxyXG4gICAgICB2YWx1ZTogNDAwLFxyXG4gICAgICB0eXBlOiAnbnVtYmVyJyxcclxuICAgICAgZGVzY3JpcHRpb246XHJcbiAgICAgICAgJ3RoZSBkZWZhdWx0IGhlaWdodCBvZiB0aGUgZXhwb3J0ZWQgY2hhcnQuIFVzZWQgd2hlbiBubyB2YWx1ZSBpcyBzZXQuJ1xyXG4gICAgfSxcclxuICAgIGRlZmF1bHRXaWR0aDoge1xyXG4gICAgICBlbnZMaW5rOiAnRVhQT1JUX0RFRkFVTFRfV0lEVEgnLFxyXG4gICAgICB2YWx1ZTogNjAwLFxyXG4gICAgICB0eXBlOiAnbnVtYmVyJyxcclxuICAgICAgZGVzY3JpcHRpb246XHJcbiAgICAgICAgJ1RoZSBkZWZhdWx0IHdpZHRoIG9mIHRoZSBleHBvcnRlZCBjaGFydC4gVXNlZCB3aGVuIG5vIHZhbHVlIGlzIHNldC4nXHJcbiAgICB9LFxyXG4gICAgZGVmYXVsdFNjYWxlOiB7XHJcbiAgICAgIGVudkxpbms6ICdFWFBPUlRfREVGQVVMVF9TQ0FMRScsXHJcbiAgICAgIHZhbHVlOiAxLFxyXG4gICAgICB0eXBlOiAnbnVtYmVyJyxcclxuICAgICAgZGVzY3JpcHRpb246XHJcbiAgICAgICAgJ1RoZSBkZWZhdWx0IHNjYWxlIG9mIHRoZSBleHBvcnRlZCBjaGFydC4gVXNlZCB3aGVuIG5vIHZhbHVlIGlzIHNldC4nXHJcbiAgICB9LFxyXG4gICAgaGVpZ2h0OiB7XHJcbiAgICAgIHR5cGU6ICdudW1iZXInLFxyXG4gICAgICB2YWx1ZTogZmFsc2UsXHJcbiAgICAgIGRlc2NyaXB0aW9uOlxyXG4gICAgICAgICdUaGUgaGVpZ2h0IG9mIHRoZSBleHBvcnRlZCBjaGFydCwgb3ZlcnJpZGluZyB0aGUgb3B0aW9uIGluIHRoZSBjaGFydCBzZXR0aW5ncy4nXHJcbiAgICB9LFxyXG4gICAgd2lkdGg6IHtcclxuICAgICAgdHlwZTogJ251bWJlcicsXHJcbiAgICAgIHZhbHVlOiBmYWxzZSxcclxuICAgICAgZGVzY3JpcHRpb246XHJcbiAgICAgICAgJ1RoZSB3aWR0aCBvZiB0aGUgZXhwb3J0ZWQgY2hhcnQsIG92ZXJyaWRpbmcgdGhlIG9wdGlvbiBpbiB0aGUgY2hhcnQgc2V0dGluZ3MuJ1xyXG4gICAgfSxcclxuICAgIHNjYWxlOiB7XHJcbiAgICAgIHZhbHVlOiBmYWxzZSxcclxuICAgICAgdHlwZTogJ251bWJlcicsXHJcbiAgICAgIGRlc2NyaXB0aW9uOlxyXG4gICAgICAgICdUaGUgc2NhbGUgb2YgdGhlIGV4cG9ydGVkIGNoYXJ0LCBvdmVycmlkaW5nIHRoZSBvcHRpb24gaW4gdGhlIGNoYXJ0IHNldHRpbmdzLiBSYW5nZXMgYmV0d2VlbiAwLjEgYW5kIDUuMC4nXHJcbiAgICB9LFxyXG4gICAgZ2xvYmFsT3B0aW9uczoge1xyXG4gICAgICB2YWx1ZTogZmFsc2UsXHJcbiAgICAgIHR5cGU6ICdzdHJpbmcnLFxyXG4gICAgICBkZXNjcmlwdGlvbjpcclxuICAgICAgICAnRWl0aGVyIGEgc3RyaW5naWZpZWQgSlNPTiBvciBhIGZpbGVuYW1lIGNvbnRhaW5pbmcgb3B0aW9ucyB0byBiZSBwYXNzZWQgaW50byB0aGUgSGlnaGNoYXJ0cy5zZXRPcHRpb25zLidcclxuICAgIH0sXHJcbiAgICB0aGVtZU9wdGlvbnM6IHtcclxuICAgICAgdmFsdWU6IGZhbHNlLFxyXG4gICAgICB0eXBlOiAnc3RyaW5nJyxcclxuICAgICAgZGVzY3JpcHRpb246XHJcbiAgICAgICAgJ0VpdGhlciBhIHN0cmluZ2lmaWVkIEpTT04gb3IgYSBmaWxlbmFtZSBjb250YWluaW5nIHRoZW1lIG9wdGlvbnMgdG8gYmUgcGFzc2VkIGludG8gdGhlIEhpZ2hjaGFydHMuc2V0T3B0aW9ucy4nXHJcbiAgICB9LFxyXG4gICAgYmF0Y2g6IHtcclxuICAgICAgdmFsdWU6IGZhbHNlLFxyXG4gICAgICB0eXBlOiAnc3RyaW5nJyxcclxuICAgICAgZGVzY3JpcHRpb246XHJcbiAgICAgICAgJ0luaXRpYXRlcyBhIGJhdGNoIGpvYiB3aXRoIGEgc3RyaW5nIGNvbnRhaW5pbmcgaW5wdXQvb3V0cHV0IHBhaXJzOiBcImluPW91dDtpbj1vdXQ7Li4uXCIuJ1xyXG4gICAgfSxcclxuICAgIHJhc3Rlcml6YXRpb25UaW1lb3V0OiB7XHJcbiAgICAgIGVudkxpbms6ICdFWFBPUlRfUkFTVEVSSVpBVElPTl9USU1FT1VUJyxcclxuICAgICAgdmFsdWU6IDE1MDAsXHJcbiAgICAgIHR5cGU6ICdudW1iZXInLFxyXG4gICAgICBkZXNjcmlwdGlvbjpcclxuICAgICAgICAnVGhlIGR1cmF0aW9uIGluIG1pbGxpc2Vjb25kcyB0byB3YWl0IGZvciByZW5kZXJpbmcgYSB3ZWJwYWdlLidcclxuICAgIH1cclxuICB9LFxyXG4gIGN1c3RvbUxvZ2ljOiB7XHJcbiAgICBhbGxvd0NvZGVFeGVjdXRpb246IHtcclxuICAgICAgZW52TGluazogJ0NVU1RPTV9MT0dJQ19BTExPV19DT0RFX0VYRUNVVElPTicsXHJcbiAgICAgIHZhbHVlOiBmYWxzZSxcclxuICAgICAgdHlwZTogJ2Jvb2xlYW4nLFxyXG4gICAgICBkZXNjcmlwdGlvbjpcclxuICAgICAgICAnQ29udHJvbHMgd2hldGhlciB0aGUgZXhlY3V0aW9uIG9mIGFyYml0cmFyeSBjb2RlIGlzIGFsbG93ZWQgZHVyaW5nIHRoZSBleHBvcnRpbmcgcHJvY2Vzcy4nXHJcbiAgICB9LFxyXG4gICAgYWxsb3dGaWxlUmVzb3VyY2VzOiB7XHJcbiAgICAgIGVudkxpbms6ICdDVVNUT01fTE9HSUNfQUxMT1dfRklMRV9SRVNPVVJDRVMnLFxyXG4gICAgICB2YWx1ZTogZmFsc2UsXHJcbiAgICAgIHR5cGU6ICdib29sZWFuJyxcclxuICAgICAgZGVzY3JpcHRpb246XHJcbiAgICAgICAgJ0NvbnRyb2xzIHRoZSBhYmlsaXR5IHRvIGluamVjdCByZXNvdXJjZXMgZnJvbSB0aGUgZmlsZXN5c3RlbS4gVGhpcyBzZXR0aW5nIGhhcyBubyBlZmZlY3Qgd2hlbiBydW5uaW5nIGFzIGEgc2VydmVyLidcclxuICAgIH0sXHJcbiAgICBjdXN0b21Db2RlOiB7XHJcbiAgICAgIHZhbHVlOiBmYWxzZSxcclxuICAgICAgdHlwZTogJ3N0cmluZycsXHJcbiAgICAgIGRlc2NyaXB0aW9uOlxyXG4gICAgICAgICdDdXN0b20gY29kZSB0byBleGVjdXRlIGJlZm9yZSBjaGFydCBpbml0aWFsaXphdGlvbi4gSXQgY2FuIGJlIGEgZnVuY3Rpb24sIGNvZGUgd3JhcHBlZCB3aXRoaW4gYSBmdW5jdGlvbiwgb3IgYSBmaWxlbmFtZSB3aXRoIHRoZSAuanMgZXh0ZW5zaW9uLidcclxuICAgIH0sXHJcbiAgICBjYWxsYmFjazoge1xyXG4gICAgICB2YWx1ZTogZmFsc2UsXHJcbiAgICAgIHR5cGU6ICdzdHJpbmcnLFxyXG4gICAgICBkZXNjcmlwdGlvbjpcclxuICAgICAgICAnSmF2YVNjcmlwdCBjb2RlIHRvIHJ1biBkdXJpbmcgY29uc3RydWN0aW9uLiBJdCBjYW4gYmUgYSBmdW5jdGlvbiBvciBhIGZpbGVuYW1lIHdpdGggdGhlIC5qcyBleHRlbnNpb24uJ1xyXG4gICAgfSxcclxuICAgIHJlc291cmNlczoge1xyXG4gICAgICB2YWx1ZTogZmFsc2UsXHJcbiAgICAgIHR5cGU6ICdzdHJpbmcnLFxyXG4gICAgICBkZXNjcmlwdGlvbjpcclxuICAgICAgICAnQWRkaXRpb25hbCByZXNvdXJjZSBpbiB0aGUgZm9ybSBvZiBhIHN0cmluZ2lmaWVkIEpTT04sIHdoaWNoIG1heSBjb250YWluIGZpbGVzLCBqcywgYW5kIGNzcyBzZWN0aW9ucy4nXHJcbiAgICB9LFxyXG4gICAgbG9hZENvbmZpZzoge1xyXG4gICAgICB2YWx1ZTogZmFsc2UsXHJcbiAgICAgIHR5cGU6ICdzdHJpbmcnLFxyXG4gICAgICBsZWdhY3lOYW1lOiAnZnJvbUZpbGUnLFxyXG4gICAgICBkZXNjcmlwdGlvbjogJ0EgZmlsZSBjb250YWluaW5nIGEgcHJlLWRlZmluZWQgY29uZmlndXJhdGlvbiB0byB1c2UuJ1xyXG4gICAgfSxcclxuICAgIGNyZWF0ZUNvbmZpZzoge1xyXG4gICAgICB2YWx1ZTogZmFsc2UsXHJcbiAgICAgIHR5cGU6ICdzdHJpbmcnLFxyXG4gICAgICBkZXNjcmlwdGlvbjpcclxuICAgICAgICAnRW5hYmxlcyBzZXR0aW5nIG9wdGlvbnMgdGhyb3VnaCBhIHByb21wdCBhbmQgc2F2aW5nIHRoZW0gaW4gYSBwcm92aWRlZCBjb25maWcgZmlsZS4nXHJcbiAgICB9XHJcbiAgfSxcclxuICBzZXJ2ZXI6IHtcclxuICAgIGVuYWJsZToge1xyXG4gICAgICBlbnZMaW5rOiAnU0VSVkVSX0VOQUJMRScsXHJcbiAgICAgIHZhbHVlOiBmYWxzZSxcclxuICAgICAgdHlwZTogJ2Jvb2xlYW4nLFxyXG4gICAgICBjbGlOYW1lOiAnZW5hYmxlU2VydmVyJyxcclxuICAgICAgZGVzY3JpcHRpb246XHJcbiAgICAgICAgJ1doZW4gc2V0IHRvIHRydWUsIHRoZSBzZXJ2ZXIgc3RhcnRzIG9uIHRoZSBsb2NhbCBJUCBhZGRyZXNzIDAuMC4wLjAuJ1xyXG4gICAgfSxcclxuICAgIGhvc3Q6IHtcclxuICAgICAgZW52TGluazogJ1NFUlZFUl9IT1NUJyxcclxuICAgICAgdmFsdWU6ICcwLjAuMC4wJyxcclxuICAgICAgdHlwZTogJ3N0cmluZycsXHJcbiAgICAgIGRlc2NyaXB0aW9uOlxyXG4gICAgICAgICdUaGUgaG9zdG5hbWUgb2YgdGhlIHNlcnZlci4gQWRkaXRpb25hbGx5LCBpdCBzdGFydHMgYSBzZXJ2ZXIgb24gdGhlIHByb3ZpZGVkIGhvc3RuYW1lLidcclxuICAgIH0sXHJcbiAgICBwb3J0OiB7XHJcbiAgICAgIGVudkxpbms6ICdTRVJWRVJfUE9SVCcsXHJcbiAgICAgIHZhbHVlOiA3ODAxLFxyXG4gICAgICB0eXBlOiAnbnVtYmVyJyxcclxuICAgICAgZGVzY3JpcHRpb246ICdUaGUgc2VydmVyIHBvcnQgd2hlbiBlbmFibGVkLidcclxuICAgIH0sXHJcbiAgICBiZW5jaG1hcmtpbmc6IHtcclxuICAgICAgZW52TGluazogJ1NFUlZFUl9CRU5DSE1BUktJTkcnLFxyXG4gICAgICB2YWx1ZTogZmFsc2UsXHJcbiAgICAgIHR5cGU6ICdib29sZWFuJyxcclxuICAgICAgY2xpTmFtZTogJ3NlcnZlckJlbmNobWFya2luZycsXHJcbiAgICAgIGRlc2NyaXB0aW9uOlxyXG4gICAgICAgICdJbmRpY2F0ZXMgd2hldGhlciB0byBkaXNwbGF5IHRoZSBkdXJhdGlvbiwgaW4gbWlsbGlzZWNvbmRzLCBvZiBzcGVjaWZpYyBhY3Rpb25zIHRoYXQgb2NjdXIgb24gdGhlIHNlcnZlciB3aGlsZSBzZXJ2aW5nIGEgcmVxdWVzdC4nXHJcbiAgICB9LFxyXG4gICAgc3NsOiB7XHJcbiAgICAgIGVuYWJsZToge1xyXG4gICAgICAgIGVudkxpbms6ICdTRVJWRVJfU1NMX0VOQUJMRScsXHJcbiAgICAgICAgdmFsdWU6IGZhbHNlLFxyXG4gICAgICAgIHR5cGU6ICdib29sZWFuJyxcclxuICAgICAgICBjbGlOYW1lOiAnZW5hYmxlU3NsJyxcclxuICAgICAgICBkZXNjcmlwdGlvbjogJ0VuYWJsZXMgb3IgZGlzYWJsZXMgdGhlIFNTTCBwcm90b2NvbC4nXHJcbiAgICAgIH0sXHJcbiAgICAgIGZvcmNlOiB7XHJcbiAgICAgICAgZW52TGluazogJ1NFUlZFUl9TU0xfRk9SQ0UnLFxyXG4gICAgICAgIHZhbHVlOiBmYWxzZSxcclxuICAgICAgICB0eXBlOiAnYm9vbGVhbicsXHJcbiAgICAgICAgY2xpTmFtZTogJ3NzbEZvcmNlZCcsXHJcbiAgICAgICAgbGVnYWN5TmFtZTogJ3NzbE9ubHknLFxyXG4gICAgICAgIGRlc2NyaXB0aW9uOlxyXG4gICAgICAgICAgJ1doZW4gc2V0IHRvIHRydWUsIHRoZSBzZXJ2ZXIgaXMgZm9yY2VkIHRvIHNlcnZlIG9ubHkgb3ZlciBIVFRQUy4nXHJcbiAgICAgIH0sXHJcbiAgICAgIHBvcnQ6IHtcclxuICAgICAgICBlbnZMaW5rOiAnU0VSVkVSX1NTTF9QT1JUJyxcclxuICAgICAgICB2YWx1ZTogNDQzLFxyXG4gICAgICAgIHR5cGU6ICdudW1iZXInLFxyXG4gICAgICAgIGNsaU5hbWU6ICdzc2xQb3J0JyxcclxuICAgICAgICBkZXNjcmlwdGlvbjogJ1RoZSBwb3J0IG9uIHdoaWNoIHRvIHJ1biB0aGUgU1NMIHNlcnZlci4nXHJcbiAgICAgIH0sXHJcbiAgICAgIGNlcnRQYXRoOiB7XHJcbiAgICAgICAgZW52TGluazogJ1NFUlZFUl9TU0xfQ0VSVF9QQVRIJyxcclxuICAgICAgICB2YWx1ZTogJycsXHJcbiAgICAgICAgdHlwZTogJ3N0cmluZycsXHJcbiAgICAgICAgbGVnYWN5TmFtZTogJ3NzbFBhdGgnLFxyXG4gICAgICAgIGRlc2NyaXB0aW9uOiAnVGhlIHBhdGggdG8gdGhlIFNTTCBjZXJ0aWZpY2F0ZS9rZXkgZmlsZS4nXHJcbiAgICAgIH1cclxuICAgIH0sXHJcbiAgICByYXRlTGltaXRpbmc6IHtcclxuICAgICAgZW5hYmxlOiB7XHJcbiAgICAgICAgZW52TGluazogJ1NFUlZFUl9SQVRFX0xJTUlUSU5HX0VOQUJMRScsXHJcbiAgICAgICAgdmFsdWU6IGZhbHNlLFxyXG4gICAgICAgIHR5cGU6ICdib29sZWFuJyxcclxuICAgICAgICBjbGlOYW1lOiAnZW5hYmxlUmF0ZUxpbWl0aW5nJyxcclxuICAgICAgICBkZXNjcmlwdGlvbjogJ0VuYWJsZXMgcmF0ZSBsaW1pdGluZyBmb3IgdGhlIHNlcnZlci4nXHJcbiAgICAgIH0sXHJcbiAgICAgIG1heFJlcXVlc3RzOiB7XHJcbiAgICAgICAgZW52TGluazogJ1NFUlZFUl9SQVRFX0xJTUlUSU5HX01BWF9SRVFVRVNUUycsXHJcbiAgICAgICAgdmFsdWU6IDEwLFxyXG4gICAgICAgIHR5cGU6ICdudW1iZXInLFxyXG4gICAgICAgIGxlZ2FjeU5hbWU6ICdyYXRlTGltaXQnLFxyXG4gICAgICAgIGRlc2NyaXB0aW9uOiAnVGhlIG1heGltdW0gbnVtYmVyIG9mIHJlcXVlc3RzIGFsbG93ZWQgaW4gb25lIG1pbnV0ZS4nXHJcbiAgICAgIH0sXHJcbiAgICAgIHdpbmRvdzoge1xyXG4gICAgICAgIGVudkxpbms6ICdTRVJWRVJfUkFURV9MSU1JVElOR19XSU5ET1cnLFxyXG4gICAgICAgIHZhbHVlOiAxLFxyXG4gICAgICAgIHR5cGU6ICdudW1iZXInLFxyXG4gICAgICAgIGRlc2NyaXB0aW9uOiAnVGhlIHRpbWUgd2luZG93LCBpbiBtaW51dGVzLCBmb3IgdGhlIHJhdGUgbGltaXRpbmcuJ1xyXG4gICAgICB9LFxyXG4gICAgICBkZWxheToge1xyXG4gICAgICAgIGVudkxpbms6ICdTRVJWRVJfUkFURV9MSU1JVElOR19ERUxBWScsXHJcbiAgICAgICAgdmFsdWU6IDAsXHJcbiAgICAgICAgdHlwZTogJ251bWJlcicsXHJcbiAgICAgICAgZGVzY3JpcHRpb246XHJcbiAgICAgICAgICAnVGhlIGRlbGF5IGR1cmF0aW9uIGZvciBlYWNoIHN1Y2Nlc3NpdmUgcmVxdWVzdCBiZWZvcmUgcmVhY2hpbmcgdGhlIG1heGltdW0gbGltaXQuJ1xyXG4gICAgICB9LFxyXG4gICAgICB0cnVzdFByb3h5OiB7XHJcbiAgICAgICAgZW52TGluazogJ1NFUlZFUl9SQVRFX0xJTUlUSU5HX1RSVVNUX1BST1hZJyxcclxuICAgICAgICB2YWx1ZTogZmFsc2UsXHJcbiAgICAgICAgdHlwZTogJ2Jvb2xlYW4nLFxyXG4gICAgICAgIGRlc2NyaXB0aW9uOiAnU2V0IHRoaXMgdG8gdHJ1ZSBpZiB0aGUgc2VydmVyIGlzIGJlaGluZCBhIGxvYWQgYmFsYW5jZXIuJ1xyXG4gICAgICB9LFxyXG4gICAgICBza2lwS2V5OiB7XHJcbiAgICAgICAgZW52TGluazogJ1NFUlZFUl9SQVRFX0xJTUlUSU5HX1NLSVBfS0VZJyxcclxuICAgICAgICB2YWx1ZTogJycsXHJcbiAgICAgICAgdHlwZTogJ3N0cmluZycsXHJcbiAgICAgICAgZGVzY3JpcHRpb246XHJcbiAgICAgICAgICAnQWxsb3dzIGJ5cGFzc2luZyB0aGUgcmF0ZSBsaW1pdGVyIGFuZCBzaG91bGQgYmUgcHJvdmlkZWQgd2l0aCB0aGUgc2tpcFRva2VuIGFyZ3VtZW50LidcclxuICAgICAgfSxcclxuICAgICAgc2tpcFRva2VuOiB7XHJcbiAgICAgICAgZW52TGluazogJ1NFUlZFUl9SQVRFX0xJTUlUSU5HX1NLSVBfVE9LRU4nLFxyXG4gICAgICAgIHZhbHVlOiAnJyxcclxuICAgICAgICB0eXBlOiAnc3RyaW5nJyxcclxuICAgICAgICBkZXNjcmlwdGlvbjpcclxuICAgICAgICAgICdBbGxvd3MgYnlwYXNzaW5nIHRoZSByYXRlIGxpbWl0ZXIgYW5kIHNob3VsZCBiZSBwcm92aWRlZCB3aXRoIHRoZSBza2lwS2V5IGFyZ3VtZW50LidcclxuICAgICAgfVxyXG4gICAgfVxyXG4gIH0sXHJcbiAgcG9vbDoge1xyXG4gICAgbWluV29ya2Vyczoge1xyXG4gICAgICBlbnZMaW5rOiAnUE9PTF9NSU5fV09SS0VSUycsXHJcbiAgICAgIHZhbHVlOiA0LFxyXG4gICAgICB0eXBlOiAnbnVtYmVyJyxcclxuICAgICAgZGVzY3JpcHRpb246ICdUaGUgbnVtYmVyIG9mIG1pbmltdW0gYW5kIGluaXRpYWwgcG9vbCB3b3JrZXJzIHRvIHNwYXduLidcclxuICAgIH0sXHJcbiAgICBtYXhXb3JrZXJzOiB7XHJcbiAgICAgIGVudkxpbms6ICdQT09MX01BWF9XT1JLRVJTJyxcclxuICAgICAgdmFsdWU6IDgsXHJcbiAgICAgIHR5cGU6ICdudW1iZXInLFxyXG4gICAgICBsZWdhY3lOYW1lOiAnd29ya2VycycsXHJcbiAgICAgIGRlc2NyaXB0aW9uOiAnVGhlIG51bWJlciBvZiBtYXhpbXVtIHBvb2wgd29ya2VycyB0byBzcGF3bi4nXHJcbiAgICB9LFxyXG4gICAgd29ya0xpbWl0OiB7XHJcbiAgICAgIGVudkxpbms6ICdQT09MX1dPUktfTElNSVQnLFxyXG4gICAgICB2YWx1ZTogNDAsXHJcbiAgICAgIHR5cGU6ICdudW1iZXInLFxyXG4gICAgICBkZXNjcmlwdGlvbjpcclxuICAgICAgICAnVGhlIG51bWJlciBvZiB3b3JrIHBpZWNlcyB0aGF0IGNhbiBiZSBwZXJmb3JtZWQgYmVmb3JlIHJlc3RhcnRpbmcgdGhlIHdvcmtlciBwcm9jZXNzLidcclxuICAgIH0sXHJcbiAgICBhY3F1aXJlVGltZW91dDoge1xyXG4gICAgICBlbnZMaW5rOiAnUE9PTF9BQ1FVSVJFX1RJTUVPVVQnLFxyXG4gICAgICB2YWx1ZTogNTAwMCxcclxuICAgICAgdHlwZTogJ251bWJlcicsXHJcbiAgICAgIGRlc2NyaXB0aW9uOlxyXG4gICAgICAgICdUaGUgZHVyYXRpb24sIGluIG1pbGxpc2Vjb25kcywgdG8gd2FpdCBmb3IgYWNxdWlyaW5nIGEgcmVzb3VyY2UuJ1xyXG4gICAgfSxcclxuICAgIGNyZWF0ZVRpbWVvdXQ6IHtcclxuICAgICAgZW52TGluazogJ1BPT0xfQ1JFQVRFX1RJTUVPVVQnLFxyXG4gICAgICB2YWx1ZTogNTAwMCxcclxuICAgICAgdHlwZTogJ251bWJlcicsXHJcbiAgICAgIGRlc2NyaXB0aW9uOlxyXG4gICAgICAgICdUaGUgZHVyYXRpb24sIGluIG1pbGxpc2Vjb25kcywgdG8gd2FpdCBmb3IgY3JlYXRpbmcgYSByZXNvdXJjZS4nXHJcbiAgICB9LFxyXG4gICAgZGVzdHJveVRpbWVvdXQ6IHtcclxuICAgICAgZW52TGluazogJ1BPT0xfREVTVFJPWV9USU1FT1VUJyxcclxuICAgICAgdmFsdWU6IDUwMDAsXHJcbiAgICAgIHR5cGU6ICdudW1iZXInLFxyXG4gICAgICBkZXNjcmlwdGlvbjpcclxuICAgICAgICAnVGhlIGR1cmF0aW9uLCBpbiBtaWxsaXNlY29uZHMsIHRvIHdhaXQgZm9yIGRlc3Ryb3lpbmcgYSByZXNvdXJjZS4nXHJcbiAgICB9LFxyXG4gICAgaWRsZVRpbWVvdXQ6IHtcclxuICAgICAgZW52TGluazogJ1BPT0xfSURMRV9USU1FT1VUJyxcclxuICAgICAgdmFsdWU6IDMwMDAwLFxyXG4gICAgICB0eXBlOiAnbnVtYmVyJyxcclxuICAgICAgZGVzY3JpcHRpb246XHJcbiAgICAgICAgJ1RoZSBkdXJhdGlvbiwgaW4gbWlsbGlzZWNvbmRzLCBhZnRlciB3aGljaCBhbiBpZGxlIHJlc291cmNlIGlzIGRlc3Ryb3llZC4nXHJcbiAgICB9LFxyXG4gICAgY3JlYXRlUmV0cnlJbnRlcnZhbDoge1xyXG4gICAgICBlbnZMaW5rOiAnUE9PTF9DUkVBVEVfUkVUUllfSU5URVJWQUwnLFxyXG4gICAgICB2YWx1ZTogMjAwLFxyXG4gICAgICB0eXBlOiAnbnVtYmVyJyxcclxuICAgICAgZGVzY3JpcHRpb246XHJcbiAgICAgICAgJ1RoZSBkdXJhdGlvbiwgaW4gbWlsbGlzZWNvbmRzLCB0byB3YWl0IGJlZm9yZSByZXRyeWluZyB0aGUgY3JlYXRlIHByb2Nlc3MgaW4gY2FzZSBvZiBhIGZhaWx1cmUuJ1xyXG4gICAgfSxcclxuICAgIHJlYXBlckludGVydmFsOiB7XHJcbiAgICAgIGVudkxpbms6ICdQT09MX1JFQVBFUl9JTlRFUlZBTCcsXHJcbiAgICAgIHZhbHVlOiAxMDAwLFxyXG4gICAgICB0eXBlOiAnbnVtYmVyJyxcclxuICAgICAgZGVzY3JpcHRpb246XHJcbiAgICAgICAgJ1RoZSBkdXJhdGlvbiwgaW4gbWlsbGlzZWNvbmRzLCBhZnRlciB3aGljaCB0aGUgY2hlY2sgZm9yIGlkbGUgcmVzb3VyY2VzIHRvIGRlc3Ryb3kgaXMgdHJpZ2dlcmVkLidcclxuICAgIH0sXHJcbiAgICBiZW5jaG1hcmtpbmc6IHtcclxuICAgICAgZW52TGluazogJ1BPT0xfQkVOQ0hNQVJLSU5HJyxcclxuICAgICAgdmFsdWU6IGZhbHNlLFxyXG4gICAgICB0eXBlOiAnYm9vbGVhbicsXHJcbiAgICAgIGNsaU5hbWU6ICdwb29sQmVuY2htYXJraW5nJyxcclxuICAgICAgZGVzY3JpcHRpb246XHJcbiAgICAgICAgJ0luZGljYXRlIHdoZXRoZXIgdG8gc2hvdyBzdGF0aXN0aWNzIGZvciB0aGUgcG9vbCBvZiByZXNvdXJjZXMgb3Igbm90LidcclxuICAgIH0sXHJcbiAgICBsaXN0ZW5Ub1Byb2Nlc3NFeGl0czoge1xyXG4gICAgICBlbnZMaW5rOiAnUE9PTF9MSVNURU5fVE9fUFJPQ0VTU19FWElUUycsXHJcbiAgICAgIHZhbHVlOiB0cnVlLFxyXG4gICAgICB0eXBlOiAnYm9vbGVhbicsXHJcbiAgICAgIGRlc2NyaXB0aW9uOiAnRGVjaWRlcyB3aGV0aGVyIG9yIG5vdCB0byBhdHRhY2ggcHJvY2Vzcy5leGl0IGhhbmRsZXJzLidcclxuICAgIH1cclxuICB9LFxyXG4gIGxvZ2dpbmc6IHtcclxuICAgIGxldmVsOiB7XHJcbiAgICAgIGVudkxpbms6ICdMT0dHSU5HX0xFVkVMJyxcclxuICAgICAgdmFsdWU6IDQsXHJcbiAgICAgIHR5cGU6ICdudW1iZXInLFxyXG4gICAgICBjbGlOYW1lOiAnbG9nTGV2ZWwnLFxyXG4gICAgICBkZXNjcmlwdGlvbjogJ1RoZSBsb2dnaW5nIGxldmVsIHRvIGJlIHVzZWQuJ1xyXG4gICAgfSxcclxuICAgIGZpbGU6IHtcclxuICAgICAgZW52TGluazogJ0xPR0dJTkdfRklMRScsXHJcbiAgICAgIHZhbHVlOiAnaGlnaGNoYXJ0cy1leHBvcnQtc2VydmVyLmxvZycsXHJcbiAgICAgIHR5cGU6ICdzdHJpbmcnLFxyXG4gICAgICBjbGlOYW1lOiAnbG9nRmlsZScsXHJcbiAgICAgIGRlc2NyaXB0aW9uOlxyXG4gICAgICAgICdUaGUgbmFtZSBvZiBhIGxvZyBmaWxlLiBUaGUgbG9nRGVzdCBvcHRpb24gYWxzbyBuZWVkcyB0byBiZSBzZXQgdG8gZW5hYmxlIGZpbGUgbG9nZ2luZy4nXHJcbiAgICB9LFxyXG4gICAgZGVzdDoge1xyXG4gICAgICBlbnZMaW5rOiAnTE9HR0lOR19ERVNUJyxcclxuICAgICAgdmFsdWU6ICdsb2cvJyxcclxuICAgICAgdHlwZTogJ3N0cmluZycsXHJcbiAgICAgIGNsaU5hbWU6ICdsb2dEZXN0JyxcclxuICAgICAgZGVzY3JpcHRpb246XHJcbiAgICAgICAgJ1RoZSBwYXRoIHRvIHN0b3JlIGxvZyBmaWxlcy4gVGhpcyBhbHNvIGVuYWJsZXMgZmlsZSBsb2dnaW5nLidcclxuICAgIH1cclxuICB9LFxyXG4gIHVpOiB7XHJcbiAgICBlbmFibGU6IHtcclxuICAgICAgZW52TGluazogJ1VJX0VOQUJMRScsXHJcbiAgICAgIHZhbHVlOiBmYWxzZSxcclxuICAgICAgdHlwZTogJ2Jvb2xlYW4nLFxyXG4gICAgICBjbGlOYW1lOiAnZW5hYmxlVWknLFxyXG4gICAgICBkZXNjcmlwdGlvbjpcclxuICAgICAgICAnRW5hYmxlcyBvciBkaXNhYmxlcyB0aGUgdXNlciBpbnRlcmZhY2UgKFVJKSBmb3IgdGhlIGV4cG9ydCBzZXJ2ZXIuJ1xyXG4gICAgfSxcclxuICAgIHJvdXRlOiB7XHJcbiAgICAgIGVudkxpbms6ICdVSV9ST1VURScsXHJcbiAgICAgIHZhbHVlOiAnLycsXHJcbiAgICAgIHR5cGU6ICdzdHJpbmcnLFxyXG4gICAgICBjbGlOYW1lOiAndWlSb3V0ZScsXHJcbiAgICAgIGRlc2NyaXB0aW9uOlxyXG4gICAgICAgICdUaGUgZW5kcG9pbnQgcm91dGUgdG8gd2hpY2ggdGhlIHVzZXIgaW50ZXJmYWNlIChVSSkgc2hvdWxkIGJlIGF0dGFjaGVkLidcclxuICAgIH1cclxuICB9LFxyXG4gIG90aGVyOiB7XHJcbiAgICBub0xvZ286IHtcclxuICAgICAgZW52TGluazogJ09USEVSX05PX0xPR08nLFxyXG4gICAgICB2YWx1ZTogZmFsc2UsXHJcbiAgICAgIHR5cGU6ICdib29sZWFuJyxcclxuICAgICAgZGVzY3JpcHRpb246XHJcbiAgICAgICAgJ1NraXAgcHJpbnRpbmcgdGhlIGxvZ28gb24gYSBzdGFydHVwLiBXaWxsIGJlIHJlcGxhY2VkIGJ5IGEgc2ltcGxlIHRleHQuJ1xyXG4gICAgfVxyXG4gIH1cclxufTtcclxuXHJcbi8vIFRoZSBjb25maWcgZGVzY3JpcHRpb25zIG9iamVjdCBmb3IgdGhlIHByb21wdHMgZnVuY3Rpb25hbGl0eS4gSXQgY29udGFpbnNcclxuLy8gaW5mb3JtYXRpb24gbGlrZTpcclxuLy8gKiBUeXBlIG9mIGEgcHJvbXB0XHJcbi8vICogTmFtZSBvZiBhbiBvcHRpb25cclxuLy8gKiBTaG9ydCBkZXNjcmlwdGlvbiBvZiBhIGNob3NlbiBvcHRpb25cclxuLy8gKiBJbml0aWFsIHZhbHVlXHJcbmV4cG9ydCBjb25zdCBwcm9tcHRzQ29uZmlnID0ge1xyXG4gIHB1cHBldGVlcjogW1xyXG4gICAge1xyXG4gICAgICB0eXBlOiAnbGlzdCcsXHJcbiAgICAgIG5hbWU6ICdhcmdzJyxcclxuICAgICAgbWVzc2FnZTogJ1B1cHBldGVlciBhcmd1bWVudHMnLFxyXG4gICAgICBpbml0aWFsOiBkZWZhdWx0Q29uZmlnLnB1cHBldGVlci5hcmdzLnZhbHVlLmpvaW4oJywnKSxcclxuICAgICAgc2VwYXJhdG9yOiAnLCdcclxuICAgIH1cclxuICBdLFxyXG4gIGhpZ2hjaGFydHM6IFtcclxuICAgIHtcclxuICAgICAgdHlwZTogJ3RleHQnLFxyXG4gICAgICBuYW1lOiAndmVyc2lvbicsXHJcbiAgICAgIG1lc3NhZ2U6ICdIaWdoY2hhcnRzIHZlcnNpb24nLFxyXG4gICAgICBpbml0aWFsOiBkZWZhdWx0Q29uZmlnLmhpZ2hjaGFydHMudmVyc2lvbi52YWx1ZVxyXG4gICAgfSxcclxuICAgIHtcclxuICAgICAgdHlwZTogJ3RleHQnLFxyXG4gICAgICBuYW1lOiAnY2RuVVJMJyxcclxuICAgICAgbWVzc2FnZTogJ1RoZSBVUkwgb2YgQ0ROJyxcclxuICAgICAgaW5pdGlhbDogZGVmYXVsdENvbmZpZy5oaWdoY2hhcnRzLmNkblVSTC52YWx1ZVxyXG4gICAgfSxcclxuICAgIHtcclxuICAgICAgdHlwZTogJ211bHRpc2VsZWN0JyxcclxuICAgICAgbmFtZTogJ21vZHVsZXMnLFxyXG4gICAgICBtZXNzYWdlOiAnQXZhaWxhYmxlIG1vZHVsZXMnLFxyXG4gICAgICBpbnN0cnVjdGlvbnM6ICdTcGFjZTogU2VsZWN0IHNwZWNpZmljLCBBOiBTZWxlY3QgYWxsLCBFbnRlcjogQ29uZmlybS4nLFxyXG4gICAgICBjaG9pY2VzOiBkZWZhdWx0Q29uZmlnLmhpZ2hjaGFydHMubW9kdWxlcy52YWx1ZVxyXG4gICAgfSxcclxuICAgIHtcclxuICAgICAgdHlwZTogJ2xpc3QnLFxyXG4gICAgICBuYW1lOiAnc2NyaXB0cycsXHJcbiAgICAgIG1lc3NhZ2U6ICdDdXN0b20gc2NyaXB0cycsXHJcbiAgICAgIGluaXRpYWw6IGRlZmF1bHRDb25maWcuaGlnaGNoYXJ0cy5zY3JpcHRzLnZhbHVlLmpvaW4oJywnKSxcclxuICAgICAgc2VwYXJhdG9yOiAnLCdcclxuICAgIH0sXHJcbiAgICB7XHJcbiAgICAgIHR5cGU6ICd0b2dnbGUnLFxyXG4gICAgICBuYW1lOiAnZm9yY2VGZXRjaCcsXHJcbiAgICAgIG1lc3NhZ2U6ICdGb3JjZSByZS1mZXRjaCB0aGUgc2NyaXB0cycsXHJcbiAgICAgIGluaXRpYWw6IGRlZmF1bHRDb25maWcuaGlnaGNoYXJ0cy5mb3JjZUZldGNoLnZhbHVlXHJcbiAgICB9LFxyXG4gICAge1xyXG4gICAgICB0eXBlOiAndGV4dCcsXHJcbiAgICAgIG5hbWU6ICdjYWNoZVBhdGgnLFxyXG4gICAgICBtZXNzYWdlOiAnVGhlIHBhdGggdG8gdGhlIGNhY2hlIGRpcmVjdG9yeScsXHJcbiAgICAgIGluaXRpYWw6IGRlZmF1bHRDb25maWcuaGlnaGNoYXJ0cy5jYWNoZVBhdGgudmFsdWVcclxuICAgIH1cclxuICBdLFxyXG4gIGV4cG9ydDogW1xyXG4gICAge1xyXG4gICAgICB0eXBlOiAnc2VsZWN0JyxcclxuICAgICAgbmFtZTogJ3R5cGUnLFxyXG4gICAgICBtZXNzYWdlOiAnVGhlIGRlZmF1bHQgZXhwb3J0IGZpbGUgdHlwZScsXHJcbiAgICAgIGhpbnQ6IGBEZWZhdWx0OiAke2RlZmF1bHRDb25maWcuZXhwb3J0LnR5cGUudmFsdWV9YCxcclxuICAgICAgaW5pdGlhbDogMCxcclxuICAgICAgY2hvaWNlczogWydwbmcnLCAnanBlZycsICdwZGYnLCAnc3ZnJ11cclxuICAgIH0sXHJcbiAgICB7XHJcbiAgICAgIHR5cGU6ICdzZWxlY3QnLFxyXG4gICAgICBuYW1lOiAnY29uc3RyJyxcclxuICAgICAgbWVzc2FnZTogJ1RoZSBkZWZhdWx0IGNvbnN0cnVjdG9yIGZvciBIaWdoY2hhcnRzJyxcclxuICAgICAgaGludDogYERlZmF1bHQ6ICR7ZGVmYXVsdENvbmZpZy5leHBvcnQuY29uc3RyLnZhbHVlfWAsXHJcbiAgICAgIGluaXRpYWw6IDAsXHJcbiAgICAgIGNob2ljZXM6IFsnY2hhcnQnLCAnc3RvY2tDaGFydCcsICdtYXBDaGFydCcsICdnYW50dENoYXJ0J11cclxuICAgIH0sXHJcbiAgICB7XHJcbiAgICAgIHR5cGU6ICdudW1iZXInLFxyXG4gICAgICBuYW1lOiAnZGVmYXVsdEhlaWdodCcsXHJcbiAgICAgIG1lc3NhZ2U6ICdUaGUgZGVmYXVsdCBmYWxsYmFjayBoZWlnaHQgb2YgdGhlIGV4cG9ydGVkIGNoYXJ0JyxcclxuICAgICAgaW5pdGlhbDogZGVmYXVsdENvbmZpZy5leHBvcnQuZGVmYXVsdEhlaWdodC52YWx1ZVxyXG4gICAgfSxcclxuICAgIHtcclxuICAgICAgdHlwZTogJ251bWJlcicsXHJcbiAgICAgIG5hbWU6ICdkZWZhdWx0V2lkdGgnLFxyXG4gICAgICBtZXNzYWdlOiAnVGhlIGRlZmF1bHQgZmFsbGJhY2sgd2lkdGggb2YgdGhlIGV4cG9ydGVkIGNoYXJ0JyxcclxuICAgICAgaW5pdGlhbDogZGVmYXVsdENvbmZpZy5leHBvcnQuZGVmYXVsdFdpZHRoLnZhbHVlXHJcbiAgICB9LFxyXG4gICAge1xyXG4gICAgICB0eXBlOiAnbnVtYmVyJyxcclxuICAgICAgbmFtZTogJ2RlZmF1bHRTY2FsZScsXHJcbiAgICAgIG1lc3NhZ2U6ICdUaGUgZGVmYXVsdCBmYWxsYmFjayBzY2FsZSBvZiB0aGUgZXhwb3J0ZWQgY2hhcnQnLFxyXG4gICAgICBpbml0aWFsOiBkZWZhdWx0Q29uZmlnLmV4cG9ydC5kZWZhdWx0U2NhbGUudmFsdWUsXHJcbiAgICAgIG1pbjogMC4xLFxyXG4gICAgICBtYXg6IDVcclxuICAgIH0sXHJcbiAgICB7XHJcbiAgICAgIHR5cGU6ICdudW1iZXInLFxyXG4gICAgICBuYW1lOiAncmFzdGVyaXphdGlvblRpbWVvdXQnLFxyXG4gICAgICBtZXNzYWdlOiAnVGhlIHJlbmRlcmluZyB3ZWJwYWdlIHRpbWVvdXQgaW4gbWlsbGlzZWNvbmRzJyxcclxuICAgICAgaW5pdGlhbDogZGVmYXVsdENvbmZpZy5leHBvcnQucmFzdGVyaXphdGlvblRpbWVvdXQudmFsdWVcclxuICAgIH1cclxuICBdLFxyXG4gIGN1c3RvbUxvZ2ljOiBbXHJcbiAgICB7XHJcbiAgICAgIHR5cGU6ICd0b2dnbGUnLFxyXG4gICAgICBuYW1lOiAnYWxsb3dDb2RlRXhlY3V0aW9uJyxcclxuICAgICAgbWVzc2FnZTogJ0VuYWJsZSBleGVjdXRpb24gb2YgY3VzdG9tIGNvZGUnLFxyXG4gICAgICBpbml0aWFsOiBkZWZhdWx0Q29uZmlnLmN1c3RvbUxvZ2ljLmFsbG93Q29kZUV4ZWN1dGlvbi52YWx1ZVxyXG4gICAgfSxcclxuICAgIHtcclxuICAgICAgdHlwZTogJ3RvZ2dsZScsXHJcbiAgICAgIG5hbWU6ICdhbGxvd0ZpbGVSZXNvdXJjZXMnLFxyXG4gICAgICBtZXNzYWdlOiAnRW5hYmxlIGZpbGUgcmVzb3VyY2VzJyxcclxuICAgICAgaW5pdGlhbDogZGVmYXVsdENvbmZpZy5jdXN0b21Mb2dpYy5hbGxvd0ZpbGVSZXNvdXJjZXMudmFsdWVcclxuICAgIH1cclxuICBdLFxyXG4gIHNlcnZlcjogW1xyXG4gICAge1xyXG4gICAgICB0eXBlOiAndG9nZ2xlJyxcclxuICAgICAgbmFtZTogJ2VuYWJsZScsXHJcbiAgICAgIG1lc3NhZ2U6ICdTdGFydHMgdGhlIHNlcnZlciBvbiAwLjAuMC4wJyxcclxuICAgICAgaW5pdGlhbDogZGVmYXVsdENvbmZpZy5zZXJ2ZXIuZW5hYmxlLnZhbHVlXHJcbiAgICB9LFxyXG4gICAge1xyXG4gICAgICB0eXBlOiAndGV4dCcsXHJcbiAgICAgIG5hbWU6ICdob3N0JyxcclxuICAgICAgbWVzc2FnZTogJ1NlcnZlciBob3N0bmFtZScsXHJcbiAgICAgIGluaXRpYWw6IGRlZmF1bHRDb25maWcuc2VydmVyLmhvc3QudmFsdWVcclxuICAgIH0sXHJcbiAgICB7XHJcbiAgICAgIHR5cGU6ICdudW1iZXInLFxyXG4gICAgICBuYW1lOiAncG9ydCcsXHJcbiAgICAgIG1lc3NhZ2U6ICdTZXJ2ZXIgcG9ydCcsXHJcbiAgICAgIGluaXRpYWw6IGRlZmF1bHRDb25maWcuc2VydmVyLnBvcnQudmFsdWVcclxuICAgIH0sXHJcbiAgICB7XHJcbiAgICAgIHR5cGU6ICd0b2dnbGUnLFxyXG4gICAgICBuYW1lOiAnYmVuY2htYXJraW5nJyxcclxuICAgICAgbWVzc2FnZTogJ0VuYWJsZSBzZXJ2ZXIgYmVuY2htYXJraW5nJyxcclxuICAgICAgaW5pdGlhbDogZGVmYXVsdENvbmZpZy5zZXJ2ZXIuYmVuY2htYXJraW5nLnZhbHVlXHJcbiAgICB9LFxyXG4gICAge1xyXG4gICAgICB0eXBlOiAndG9nZ2xlJyxcclxuICAgICAgbmFtZTogJ3NzbC5lbmFibGUnLFxyXG4gICAgICBtZXNzYWdlOiAnRW5hYmxlIFNTTCBwcm90b2NvbCcsXHJcbiAgICAgIGluaXRpYWw6IGRlZmF1bHRDb25maWcuc2VydmVyLnNzbC5lbmFibGUudmFsdWVcclxuICAgIH0sXHJcbiAgICB7XHJcbiAgICAgIHR5cGU6ICd0b2dnbGUnLFxyXG4gICAgICBuYW1lOiAnc3NsLmZvcmNlJyxcclxuICAgICAgbWVzc2FnZTogJ0ZvcmNlIHNlcnZpbmcgb25seSBvdmVyIEhUVFBTJyxcclxuICAgICAgaW5pdGlhbDogZGVmYXVsdENvbmZpZy5zZXJ2ZXIuc3NsLmZvcmNlLnZhbHVlXHJcbiAgICB9LFxyXG4gICAge1xyXG4gICAgICB0eXBlOiAnbnVtYmVyJyxcclxuICAgICAgbmFtZTogJ3NzbC5wb3J0JyxcclxuICAgICAgbWVzc2FnZTogJ1NTTCBzZXJ2ZXIgcG9ydCcsXHJcbiAgICAgIGluaXRpYWw6IGRlZmF1bHRDb25maWcuc2VydmVyLnNzbC5wb3J0LnZhbHVlXHJcbiAgICB9LFxyXG4gICAge1xyXG4gICAgICB0eXBlOiAndGV4dCcsXHJcbiAgICAgIG5hbWU6ICdzc2wuY2VydFBhdGgnLFxyXG4gICAgICBtZXNzYWdlOiAnVGhlIHBhdGggdG8gZmluZCB0aGUgU1NMIGNlcnRpZmljYXRlL2tleScsXHJcbiAgICAgIGluaXRpYWw6IGRlZmF1bHRDb25maWcuc2VydmVyLnNzbC5jZXJ0UGF0aC52YWx1ZVxyXG4gICAgfSxcclxuICAgIHtcclxuICAgICAgdHlwZTogJ3RvZ2dsZScsXHJcbiAgICAgIG5hbWU6ICdyYXRlTGltaXRpbmcuZW5hYmxlJyxcclxuICAgICAgbWVzc2FnZTogJ0VuYWJsZSByYXRlIGxpbWl0aW5nJyxcclxuICAgICAgaW5pdGlhbDogZGVmYXVsdENvbmZpZy5zZXJ2ZXIucmF0ZUxpbWl0aW5nLmVuYWJsZS52YWx1ZVxyXG4gICAgfSxcclxuICAgIHtcclxuICAgICAgdHlwZTogJ251bWJlcicsXHJcbiAgICAgIG5hbWU6ICdyYXRlTGltaXRpbmcubWF4UmVxdWVzdHMnLFxyXG4gICAgICBtZXNzYWdlOiAnVGhlIG1heGltdW0gcmVxdWVzdHMgYWxsb3dlZCBwZXIgbWludXRlJyxcclxuICAgICAgaW5pdGlhbDogZGVmYXVsdENvbmZpZy5zZXJ2ZXIucmF0ZUxpbWl0aW5nLm1heFJlcXVlc3RzLnZhbHVlXHJcbiAgICB9LFxyXG4gICAge1xyXG4gICAgICB0eXBlOiAnbnVtYmVyJyxcclxuICAgICAgbmFtZTogJ3JhdGVMaW1pdGluZy53aW5kb3cnLFxyXG4gICAgICBtZXNzYWdlOiAnVGhlIHJhdGUtbGltaXRpbmcgdGltZSB3aW5kb3cgaW4gbWludXRlcycsXHJcbiAgICAgIGluaXRpYWw6IGRlZmF1bHRDb25maWcuc2VydmVyLnJhdGVMaW1pdGluZy53aW5kb3cudmFsdWVcclxuICAgIH0sXHJcbiAgICB7XHJcbiAgICAgIHR5cGU6ICdudW1iZXInLFxyXG4gICAgICBuYW1lOiAncmF0ZUxpbWl0aW5nLmRlbGF5JyxcclxuICAgICAgbWVzc2FnZTpcclxuICAgICAgICAnVGhlIGRlbGF5IGZvciBlYWNoIHN1Y2Nlc3NpdmUgcmVxdWVzdCBiZWZvcmUgcmVhY2hpbmcgdGhlIG1heGltdW0nLFxyXG4gICAgICBpbml0aWFsOiBkZWZhdWx0Q29uZmlnLnNlcnZlci5yYXRlTGltaXRpbmcuZGVsYXkudmFsdWVcclxuICAgIH0sXHJcbiAgICB7XHJcbiAgICAgIHR5cGU6ICd0b2dnbGUnLFxyXG4gICAgICBuYW1lOiAncmF0ZUxpbWl0aW5nLnRydXN0UHJveHknLFxyXG4gICAgICBtZXNzYWdlOiAnU2V0IHRvIHRydWUgaWYgYmVoaW5kIGEgbG9hZCBiYWxhbmNlcicsXHJcbiAgICAgIGluaXRpYWw6IGRlZmF1bHRDb25maWcuc2VydmVyLnJhdGVMaW1pdGluZy50cnVzdFByb3h5LnZhbHVlXHJcbiAgICB9LFxyXG4gICAge1xyXG4gICAgICB0eXBlOiAndGV4dCcsXHJcbiAgICAgIG5hbWU6ICdyYXRlTGltaXRpbmcuc2tpcEtleScsXHJcbiAgICAgIG1lc3NhZ2U6XHJcbiAgICAgICAgJ0FsbG93cyBieXBhc3NpbmcgdGhlIHJhdGUgbGltaXRlciB3aGVuIHByb3ZpZGVkIHdpdGggdGhlIHNraXBUb2tlbiBhcmd1bWVudCcsXHJcbiAgICAgIGluaXRpYWw6IGRlZmF1bHRDb25maWcuc2VydmVyLnJhdGVMaW1pdGluZy5za2lwS2V5LnZhbHVlXHJcbiAgICB9LFxyXG4gICAge1xyXG4gICAgICB0eXBlOiAndGV4dCcsXHJcbiAgICAgIG5hbWU6ICdyYXRlTGltaXRpbmcuc2tpcFRva2VuJyxcclxuICAgICAgbWVzc2FnZTpcclxuICAgICAgICAnQWxsb3dzIGJ5cGFzc2luZyB0aGUgcmF0ZSBsaW1pdGVyIHdoZW4gcHJvdmlkZWQgd2l0aCB0aGUgc2tpcEtleSBhcmd1bWVudCcsXHJcbiAgICAgIGluaXRpYWw6IGRlZmF1bHRDb25maWcuc2VydmVyLnJhdGVMaW1pdGluZy5za2lwVG9rZW4udmFsdWVcclxuICAgIH1cclxuICBdLFxyXG4gIHBvb2w6IFtcclxuICAgIHtcclxuICAgICAgdHlwZTogJ251bWJlcicsXHJcbiAgICAgIG5hbWU6ICdtaW5Xb3JrZXJzJyxcclxuICAgICAgbWVzc2FnZTogJ1RoZSBpbml0aWFsIG51bWJlciBvZiB3b3JrZXJzIHRvIHNwYXduJyxcclxuICAgICAgaW5pdGlhbDogZGVmYXVsdENvbmZpZy5wb29sLm1pbldvcmtlcnMudmFsdWVcclxuICAgIH0sXHJcbiAgICB7XHJcbiAgICAgIHR5cGU6ICdudW1iZXInLFxyXG4gICAgICBuYW1lOiAnbWF4V29ya2VycycsXHJcbiAgICAgIG1lc3NhZ2U6ICdUaGUgbWF4aW11bSBudW1iZXIgb2Ygd29ya2VycyB0byBzcGF3bicsXHJcbiAgICAgIGluaXRpYWw6IGRlZmF1bHRDb25maWcucG9vbC5tYXhXb3JrZXJzLnZhbHVlXHJcbiAgICB9LFxyXG4gICAge1xyXG4gICAgICB0eXBlOiAnbnVtYmVyJyxcclxuICAgICAgbmFtZTogJ3dvcmtMaW1pdCcsXHJcbiAgICAgIG1lc3NhZ2U6XHJcbiAgICAgICAgJ1RoZSBwaWVjZXMgb2Ygd29yayB0aGF0IGNhbiBiZSBwZXJmb3JtZWQgYmVmb3JlIHJlc3RhcnRpbmcgYSBQdXBwZXRlZXIgcHJvY2VzcycsXHJcbiAgICAgIGluaXRpYWw6IGRlZmF1bHRDb25maWcucG9vbC53b3JrTGltaXQudmFsdWVcclxuICAgIH0sXHJcbiAgICB7XHJcbiAgICAgIHR5cGU6ICdudW1iZXInLFxyXG4gICAgICBuYW1lOiAnYWNxdWlyZVRpbWVvdXQnLFxyXG4gICAgICBtZXNzYWdlOiAnVGhlIG51bWJlciBvZiBtaWxsaXNlY29uZHMgdG8gd2FpdCBmb3IgYWNxdWlyaW5nIGEgcmVzb3VyY2UnLFxyXG4gICAgICBpbml0aWFsOiBkZWZhdWx0Q29uZmlnLnBvb2wuYWNxdWlyZVRpbWVvdXQudmFsdWVcclxuICAgIH0sXHJcbiAgICB7XHJcbiAgICAgIHR5cGU6ICdudW1iZXInLFxyXG4gICAgICBuYW1lOiAnY3JlYXRlVGltZW91dCcsXHJcbiAgICAgIG1lc3NhZ2U6ICdUaGUgbnVtYmVyIG9mIG1pbGxpc2Vjb25kcyB0byB3YWl0IGZvciBjcmVhdGluZyBhIHJlc291cmNlJyxcclxuICAgICAgaW5pdGlhbDogZGVmYXVsdENvbmZpZy5wb29sLmNyZWF0ZVRpbWVvdXQudmFsdWVcclxuICAgIH0sXHJcbiAgICB7XHJcbiAgICAgIHR5cGU6ICdudW1iZXInLFxyXG4gICAgICBuYW1lOiAnZGVzdHJveVRpbWVvdXQnLFxyXG4gICAgICBtZXNzYWdlOiAnVGhlIG51bWJlciBvZiBtaWxsaXNlY29uZHMgdG8gd2FpdCBmb3IgZGVzdHJveWluZyBhIHJlc291cmNlJyxcclxuICAgICAgaW5pdGlhbDogZGVmYXVsdENvbmZpZy5wb29sLmRlc3Ryb3lUaW1lb3V0LnZhbHVlXHJcbiAgICB9LFxyXG4gICAge1xyXG4gICAgICB0eXBlOiAnbnVtYmVyJyxcclxuICAgICAgbmFtZTogJ2lkbGVUaW1lb3V0JyxcclxuICAgICAgbWVzc2FnZTogJ1RoZSBudW1iZXIgb2YgbWlsbGlzZWNvbmRzIGFmdGVyIGFuIGlkbGUgcmVzb3VyY2UgaXMgZGVzdHJveWVkJyxcclxuICAgICAgaW5pdGlhbDogZGVmYXVsdENvbmZpZy5wb29sLmlkbGVUaW1lb3V0LnZhbHVlXHJcbiAgICB9LFxyXG4gICAge1xyXG4gICAgICB0eXBlOiAnbnVtYmVyJyxcclxuICAgICAgbmFtZTogJ2NyZWF0ZVJldHJ5SW50ZXJ2YWwnLFxyXG4gICAgICBtZXNzYWdlOlxyXG4gICAgICAgICdUaGUgcmV0cnkgaW50ZXJ2YWwgaW4gbWlsbGlzZWNvbmRzIGFmdGVyIGEgY3JlYXRlIHByb2Nlc3MgZmFpbHMnLFxyXG4gICAgICBpbml0aWFsOiBkZWZhdWx0Q29uZmlnLnBvb2wuY3JlYXRlUmV0cnlJbnRlcnZhbC52YWx1ZVxyXG4gICAgfSxcclxuICAgIHtcclxuICAgICAgdHlwZTogJ251bWJlcicsXHJcbiAgICAgIG5hbWU6ICdyZWFwZXJJbnRlcnZhbCcsXHJcbiAgICAgIG1lc3NhZ2U6XHJcbiAgICAgICAgJ1RoZSByZWFwZXIgaW50ZXJ2YWwgaW4gbWlsbGlzZWNvbmRzIGFmdGVyIHRyaWdnZXJpbmcgdGhlIGNoZWNrIGZvciBpZGxlIHJlc291cmNlcyB0byBkZXN0cm95JyxcclxuICAgICAgaW5pdGlhbDogZGVmYXVsdENvbmZpZy5wb29sLnJlYXBlckludGVydmFsLnZhbHVlXHJcbiAgICB9LFxyXG4gICAge1xyXG4gICAgICB0eXBlOiAndG9nZ2xlJyxcclxuICAgICAgbmFtZTogJ2JlbmNobWFya2luZycsXHJcbiAgICAgIG1lc3NhZ2U6ICdFbmFibGUgYmVuY2htYXJraW5nIGZvciBhIHJlc291cmNlIHBvb2wnLFxyXG4gICAgICBpbml0aWFsOiBkZWZhdWx0Q29uZmlnLnBvb2wuYmVuY2htYXJraW5nLnZhbHVlXHJcbiAgICB9LFxyXG4gICAge1xyXG4gICAgICB0eXBlOiAndG9nZ2xlJyxcclxuICAgICAgbmFtZTogJ2xpc3RlblRvUHJvY2Vzc0V4aXRzJyxcclxuICAgICAgbWVzc2FnZTogJ1NldCB0byBmYWxzZSB0byBza2lwIGF0dGFjaGluZyBwcm9jZXNzLmV4aXQgaGFuZGxlcnMnLFxyXG4gICAgICBpbml0aWFsOiBkZWZhdWx0Q29uZmlnLnBvb2wubGlzdGVuVG9Qcm9jZXNzRXhpdHMudmFsdWVcclxuICAgIH1cclxuICBdLFxyXG4gIGxvZ2dpbmc6IFtcclxuICAgIHtcclxuICAgICAgdHlwZTogJ251bWJlcicsXHJcbiAgICAgIG5hbWU6ICdsZXZlbCcsXHJcbiAgICAgIG1lc3NhZ2U6XHJcbiAgICAgICAgJ1RoZSBsb2cgbGV2ZWwgKDA6IHNpbGVudCwgMTogZXJyb3IsIDI6IHdhcm5pbmcsIDM6IG5vdGljZSwgNDogdmVyYm9zZSwgNTogYmVuY2htYXJrKScsXHJcbiAgICAgIGluaXRpYWw6IGRlZmF1bHRDb25maWcubG9nZ2luZy5sZXZlbC52YWx1ZSxcclxuICAgICAgcm91bmQ6IDAsXHJcbiAgICAgIG1pbjogMCxcclxuICAgICAgbWF4OiA1XHJcbiAgICB9LFxyXG4gICAge1xyXG4gICAgICB0eXBlOiAndGV4dCcsXHJcbiAgICAgIG5hbWU6ICdmaWxlJyxcclxuICAgICAgbWVzc2FnZTogJ0EgbG9nIGZpbGUgbmFtZS4gU2V0IHdpdGggdGhlIC0tbG9nRGVzdCB0byBlbmFibGUgZmlsZSBsb2dnaW5nJyxcclxuICAgICAgaW5pdGlhbDogZGVmYXVsdENvbmZpZy5sb2dnaW5nLmZpbGUudmFsdWVcclxuICAgIH0sXHJcbiAgICB7XHJcbiAgICAgIHR5cGU6ICd0ZXh0JyxcclxuICAgICAgbmFtZTogJ2Rlc3QnLFxyXG4gICAgICBtZXNzYWdlOiAnVGhlIHBhdGggdG8gbG9nIGZpbGVzLiBFbmFibGVzIGZpbGUgbG9nZ2luZycsXHJcbiAgICAgIGluaXRpYWw6IGRlZmF1bHRDb25maWcubG9nZ2luZy5kZXN0LnZhbHVlXHJcbiAgICB9XHJcbiAgXSxcclxuICB1aTogW1xyXG4gICAge1xyXG4gICAgICB0eXBlOiAndG9nZ2xlJyxcclxuICAgICAgbmFtZTogJ2VuYWJsZScsXHJcbiAgICAgIG1lc3NhZ2U6ICdFbmFibGUgVUkgZm9yIHRoZSBleHBvcnQgc2VydmVyJyxcclxuICAgICAgaW5pdGlhbDogZGVmYXVsdENvbmZpZy51aS5lbmFibGUudmFsdWVcclxuICAgIH0sXHJcbiAgICB7XHJcbiAgICAgIHR5cGU6ICd0ZXh0JyxcclxuICAgICAgbmFtZTogJ3JvdXRlJyxcclxuICAgICAgbWVzc2FnZTogJ0Egcm91dGUgdG8gYXR0YWNoIHRoZSBVSScsXHJcbiAgICAgIGluaXRpYWw6IGRlZmF1bHRDb25maWcudWkucm91dGUudmFsdWVcclxuICAgIH1cclxuICBdLFxyXG4gIG90aGVyOiBbXHJcbiAgICB7XHJcbiAgICAgIHR5cGU6ICd0b2dnbGUnLFxyXG4gICAgICBuYW1lOiAnbm9Mb2dvJyxcclxuICAgICAgbWVzc2FnZTogJ1NraXAgcHJpbnRpbmcgdGhlIGxvZ28gb24gc3RhcnR1cC4gUmVwbGFjZWQgYnkgc2ltcGxlIHRleHQnLFxyXG4gICAgICBpbml0aWFsOiBkZWZhdWx0Q29uZmlnLm90aGVyLm5vTG9nby52YWx1ZVxyXG4gICAgfVxyXG4gIF1cclxufTtcclxuXHJcbi8vIEFic29sdXRlIHByb3BzIHRoYXQsIGluIGNhc2Ugb2YgbWVyZ2luZyByZWN1cnNpdmVseSwgbmVlZCB0byBiZSBmb3JjZSBtZXJnZWRcclxuZXhwb3J0IGNvbnN0IGFic29sdXRlUHJvcHMgPSBbXHJcbiAgJ29wdGlvbnMnLFxyXG4gICdnbG9iYWxPcHRpb25zJyxcclxuICAndGhlbWVPcHRpb25zJyxcclxuICAncmVzb3VyY2VzJyxcclxuICAncGF5bG9hZCdcclxuXTtcclxuXHJcbi8vIEFyZ3VtZW50IG5lc3RpbmcgbGV2ZWwgb2YgYWxsIGV4cG9ydCBzZXJ2ZXIgb3B0aW9uc1xyXG5leHBvcnQgY29uc3QgbmVzdGVkQXJncyA9IHt9O1xyXG5cclxuLyoqXHJcbiAqIFJlY3Vyc2l2ZWx5IGNyZWF0ZXMgYSBjaGFpbiBvZiBuZXN0ZWQgYXJndW1lbnRzIGZyb20gYW4gb2JqZWN0LlxyXG4gKlxyXG4gKiBAcGFyYW0ge09iamVjdH0gb2JqIC0gVGhlIG9iamVjdCBjb250YWluaW5nIG5lc3RlZCBhcmd1bWVudHMuXHJcbiAqIEBwYXJhbSB7c3RyaW5nfSBwcm9wQ2hhaW4gLSBUaGUgY3VycmVudCBjaGFpbiBvZiBuZXN0ZWQgcHJvcGVydGllc1xyXG4gKiAodXNlZCBpbnRlcm5hbGx5IGR1cmluZyByZWN1cnNpb24pLlxyXG4gKi9cclxuY29uc3QgY3JlYXRlTmVzdGVkQXJncyA9IChvYmosIHByb3BDaGFpbiA9ICcnKSA9PiB7XHJcbiAgT2JqZWN0LmtleXMob2JqKS5mb3JFYWNoKChrKSA9PiB7XHJcbiAgICBpZiAoIVsncHVwcGV0ZWVyJywgJ2hpZ2hjaGFydHMnXS5pbmNsdWRlcyhrKSkge1xyXG4gICAgICBjb25zdCBlbnRyeSA9IG9ialtrXTtcclxuICAgICAgaWYgKHR5cGVvZiBlbnRyeS52YWx1ZSA9PT0gJ3VuZGVmaW5lZCcpIHtcclxuICAgICAgICAvLyBHbyBkZWVwZXIgaW4gdGhlIG5lc3RlZCBhcmd1bWVudHNcclxuICAgICAgICBjcmVhdGVOZXN0ZWRBcmdzKGVudHJ5LCBgJHtwcm9wQ2hhaW59LiR7a31gKTtcclxuICAgICAgfSBlbHNlIHtcclxuICAgICAgICAvLyBDcmVhdGUgdGhlIGNoYWluIG9mIG5lc3RlZCBhcmd1bWVudHNcclxuICAgICAgICBuZXN0ZWRBcmdzW2VudHJ5LmNsaU5hbWUgfHwga10gPSBgJHtwcm9wQ2hhaW59LiR7a31gLnN1YnN0cmluZygxKTtcclxuXHJcbiAgICAgICAgLy8gU3VwcG9ydCBmb3IgdGhlIGxlZ2FjeSwgUGhhbnRvbUpTIHByb3BlcnRpZXMgbmFtZXNcclxuICAgICAgICBpZiAoZW50cnkubGVnYWN5TmFtZSAhPT0gdW5kZWZpbmVkKSB7XHJcbiAgICAgICAgICBuZXN0ZWRBcmdzW2VudHJ5LmxlZ2FjeU5hbWVdID0gYCR7cHJvcENoYWlufS4ke2t9YC5zdWJzdHJpbmcoMSk7XHJcbiAgICAgICAgfVxyXG4gICAgICB9XHJcbiAgICB9XHJcbiAgfSk7XHJcbn07XHJcblxyXG5jcmVhdGVOZXN0ZWRBcmdzKGRlZmF1bHRDb25maWcpO1xyXG4iLCIvKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKlxyXG5cclxuSGlnaGNoYXJ0cyBFeHBvcnQgU2VydmVyXHJcblxyXG5Db3B5cmlnaHQgKGMpIDIwMTYtMjAyNCwgSGlnaHNvZnRcclxuXHJcbkxpY2VuY2VkIHVuZGVyIHRoZSBNSVQgbGljZW5jZS5cclxuXHJcbkFkZGl0aW9uYWxseSBhIHZhbGlkIEhpZ2hjaGFydHMgbGljZW5zZSBpcyByZXF1aXJlZCBmb3IgdXNlLlxyXG5cclxuU2VlIExJQ0VOU0UgZmlsZSBpbiByb290IGZvciBkZXRhaWxzLlxyXG5cclxuKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKi9cclxuXHJcbmltcG9ydCB7IGFwcGVuZEZpbGUsIGV4aXN0c1N5bmMsIG1rZGlyU3luYyB9IGZyb20gJ2ZzJztcclxuXHJcbmltcG9ydCB7IGRlZmF1bHRDb25maWcgfSBmcm9tICcuL3NjaGVtYXMvY29uZmlnLmpzJztcclxuXHJcbi8vIFRoZSBhdmFpbGFibGUgY29sb3JzXHJcbmNvbnN0IGNvbG9ycyA9IFsncmVkJywgJ3llbGxvdycsICdibHVlJywgJ2dyYXknLCAnZ3JlZW4nXTtcclxuXHJcbi8vIFRoZSBkZWZhdWx0IGxvZ2dpbmcgY29uZmlnXHJcbmxldCBsb2dnaW5nID0ge1xyXG4gIC8vIEZsYWdzIGZvciBsb2dnaW5nIHN0YXR1c1xyXG4gIHRvQ29uc29sZTogdHJ1ZSxcclxuICB0b0ZpbGU6IGZhbHNlLFxyXG4gIHBhdGhDcmVhdGVkOiBmYWxzZSxcclxuICAvLyBMb2cgbGV2ZWxzXHJcbiAgbGV2ZWxzRGVzYzogW1xyXG4gICAge1xyXG4gICAgICB0aXRsZTogJ2Vycm9yJyxcclxuICAgICAgY29sb3I6IGNvbG9yc1swXVxyXG4gICAgfSxcclxuICAgIHtcclxuICAgICAgdGl0bGU6ICd3YXJuaW5nJyxcclxuICAgICAgY29sb3I6IGNvbG9yc1sxXVxyXG4gICAgfSxcclxuICAgIHtcclxuICAgICAgdGl0bGU6ICdub3RpY2UnLFxyXG4gICAgICBjb2xvcjogY29sb3JzWzJdXHJcbiAgICB9LFxyXG4gICAge1xyXG4gICAgICB0aXRsZTogJ3ZlcmJvc2UnLFxyXG4gICAgICBjb2xvcjogY29sb3JzWzNdXHJcbiAgICB9LFxyXG4gICAge1xyXG4gICAgICB0aXRsZTogJ2JlbmNobWFyaycsXHJcbiAgICAgIGNvbG9yOiBjb2xvcnNbNF1cclxuICAgIH1cclxuICBdLFxyXG4gIC8vIExvZyBsaXN0ZW5lcnNcclxuICBsaXN0ZW5lcnM6IFtdXHJcbn07XHJcblxyXG4vLyBHYXRoZXIgaW5pdCBsb2dnaW5nIG9wdGlvbnNcclxuZm9yIChjb25zdCBba2V5LCBvcHRpb25dIG9mIE9iamVjdC5lbnRyaWVzKGRlZmF1bHRDb25maWcubG9nZ2luZykpIHtcclxuICBsb2dnaW5nW2tleV0gPSBvcHRpb24udmFsdWU7XHJcbn1cclxuXHJcbi8qKlxyXG4gKiBMb2dzIHRoZSBwcm92aWRlZCB0ZXh0cyB0byBhIGZpbGUsIGlmIGZpbGUgbG9nZ2luZyBpcyBlbmFibGVkLiBJdCBjcmVhdGVzXHJcbiAqIHRoZSBuZWNlc3NhcnkgZGlyZWN0b3J5IHN0cnVjdHVyZSBpZiBub3QgYWxyZWFkeSBjcmVhdGVkIGFuZCBhcHBlbmRzIHRoZVxyXG4gKiBjb250ZW50LCBpbmNsdWRpbmcgYW4gb3B0aW9uYWwgcHJlZml4LCB0byB0aGUgc3BlY2lmaWVkIGxvZyBmaWxlLlxyXG4gKlxyXG4gKiBAcGFyYW0ge3N0cmluZ1tdfSB0ZXh0cyAtIEFuIGFycmF5IG9mIHRleHRzIHRvIGJlIGxvZ2dlZC5cclxuICogQHBhcmFtIHtzdHJpbmd9IHByZWZpeCAtIEFuIG9wdGlvbmFsIHByZWZpeCB0byBiZSBhZGRlZCB0byBlYWNoIGxvZyBlbnRyeS5cclxuICovXHJcbmNvbnN0IGxvZ1RvRmlsZSA9ICh0ZXh0cywgcHJlZml4KSA9PiB7XHJcbiAgaWYgKGxvZ2dpbmcudG9GaWxlKSB7XHJcbiAgICBpZiAoIWxvZ2dpbmcucGF0aENyZWF0ZWQpIHtcclxuICAgICAgLy8gQ3JlYXRlIGlmIGRvZXMgbm90IGV4aXN0XHJcbiAgICAgICFleGlzdHNTeW5jKGxvZ2dpbmcuZGVzdCkgJiYgbWtkaXJTeW5jKGxvZ2dpbmcuZGVzdCk7XHJcblxyXG4gICAgICAvLyBXZSBub3cgYXNzdW1lIHRoZSBwYXRoIGlzIGF2YWlsYWJsZSwgZS5nLiBpdCdzIHRoZSByZXNwb25zaWJpbGl0eVxyXG4gICAgICAvLyBvZiB0aGUgdXNlciB0byBjcmVhdGUgdGhlIHBhdGggd2l0aCB0aGUgY29ycmVjdCBhY2Nlc3MgcmlnaHRzLlxyXG4gICAgICBsb2dnaW5nLnBhdGhDcmVhdGVkID0gdHJ1ZTtcclxuICAgIH1cclxuXHJcbiAgICAvLyBBZGQgdGhlIGNvbnRlbnQgdG8gYSBmaWxlXHJcbiAgICBhcHBlbmRGaWxlKFxyXG4gICAgICBgJHtsb2dnaW5nLmRlc3R9JHtsb2dnaW5nLmZpbGV9YCxcclxuICAgICAgW3ByZWZpeF0uY29uY2F0KHRleHRzKS5qb2luKCcgJykgKyAnXFxuJyxcclxuICAgICAgKGVycm9yKSA9PiB7XHJcbiAgICAgICAgaWYgKGVycm9yKSB7XHJcbiAgICAgICAgICBjb25zb2xlLmxvZyhgW2xvZ2dlcl0gVW5hYmxlIHRvIHdyaXRlIHRvIGxvZyBmaWxlOiAke2Vycm9yfWApO1xyXG4gICAgICAgICAgbG9nZ2luZy50b0ZpbGUgPSBmYWxzZTtcclxuICAgICAgICB9XHJcbiAgICAgIH1cclxuICAgICk7XHJcbiAgfVxyXG59O1xyXG5cclxuLyoqXHJcbiAqIExvZ3MgYSBtZXNzYWdlLiBBY2NlcHRzIGEgdmFyaWFibGUgYW1vdW50IG9mIGFyZ3VtZW50cy4gQXJndW1lbnRzIGFmdGVyXHJcbiAqIGBsZXZlbGAgd2lsbCBiZSBwYXNzZWQgZGlyZWN0bHkgdG8gY29uc29sZS5sb2csIGFuZC9vciB3aWxsIGJlIGpvaW5lZFxyXG4gKiBhbmQgYXBwZW5kZWQgdG8gdGhlIGxvZyBmaWxlLlxyXG4gKlxyXG4gKiBAcGFyYW0ge2FueX0gYXJncyAtIEFuIGFycmF5IG9mIGFyZ3VtZW50cyB3aGVyZSB0aGUgZmlyc3QgaXMgdGhlIGxvZyBsZXZlbFxyXG4gKiBhbmQgdGhlIHJlc3QgYXJlIHN0cmluZ3MgdG8gYnVpbGQgYSBtZXNzYWdlIHdpdGguXHJcbiAqL1xyXG5leHBvcnQgY29uc3QgbG9nID0gKC4uLmFyZ3MpID0+IHtcclxuICBjb25zdCBbbmV3TGV2ZWwsIC4uLnRleHRzXSA9IGFyZ3M7XHJcblxyXG4gIC8vIEN1cnJlbnQgbG9nZ2luZyBvcHRpb25zXHJcbiAgY29uc3QgeyBsZXZlbCwgbGV2ZWxzRGVzYyB9ID0gbG9nZ2luZztcclxuXHJcbiAgLy8gQ2hlY2sgaWYgbG9nIGxldmVsIGlzIHdpdGhpbiBhIGNvcnJlY3QgcmFuZ2Ugb3IgaXMgYSBiZW5jaG1hcmsgbG9nXHJcbiAgaWYgKFxyXG4gICAgbmV3TGV2ZWwgIT09IDUgJiZcclxuICAgIChuZXdMZXZlbCA9PT0gMCB8fCBuZXdMZXZlbCA+IGxldmVsIHx8IGxldmVsID4gbGV2ZWxzRGVzYy5sZW5ndGgpXHJcbiAgKSB7XHJcbiAgICByZXR1cm47XHJcbiAgfVxyXG5cclxuICAvLyBHZXQgcmlkIG9mIHRoZSBHTVQgdGV4dCBpbmZvcm1hdGlvblxyXG4gIGNvbnN0IG5ld0RhdGUgPSBuZXcgRGF0ZSgpLnRvU3RyaW5nKCkuc3BsaXQoJygnKVswXS50cmltKCk7XHJcblxyXG4gIC8vIENyZWF0ZSBhIG1lc3NhZ2UncyBwcmVmaXhcclxuICBjb25zdCBwcmVmaXggPSBgJHtuZXdEYXRlfSBbJHtsZXZlbHNEZXNjW25ld0xldmVsIC0gMV0udGl0bGV9XSAtYDtcclxuXHJcbiAgLy8gQ2FsbCBhdmFpbGFibGUgbG9nIGxpc3RlbmVyc1xyXG4gIGxvZ2dpbmcubGlzdGVuZXJzLmZvckVhY2goKGZuKSA9PiB7XHJcbiAgICBmbihwcmVmaXgsIHRleHRzLmpvaW4oJyAnKSk7XHJcbiAgfSk7XHJcblxyXG4gIC8vIExvZyB0byBjb25zb2xlXHJcbiAgaWYgKGxvZ2dpbmcudG9Db25zb2xlKSB7XHJcbiAgICBjb25zb2xlLmxvZy5hcHBseShcclxuICAgICAgdW5kZWZpbmVkLFxyXG4gICAgICBbcHJlZml4LnRvU3RyaW5nKClbbG9nZ2luZy5sZXZlbHNEZXNjW25ld0xldmVsIC0gMV0uY29sb3JdXS5jb25jYXQodGV4dHMpXHJcbiAgICApO1xyXG4gIH1cclxuXHJcbiAgLy8gTG9nIHRvIGZpbGVcclxuICBsb2dUb0ZpbGUodGV4dHMsIHByZWZpeCk7XHJcbn07XHJcblxyXG4vKipcclxuICogTG9ncyBhbiBlcnJvciBtZXNzYWdlIHdpdGggaXRzIHN0YWNrIHRyYWNlLiBPcHRpb25hbGx5LCBhIGN1c3RvbSBtZXNzYWdlXHJcbiAqIGNhbiBiZSBwcm92aWRlZC5cclxuICpcclxuICogQHBhcmFtIHtudW1iZXJ9IGxldmVsIC0gVGhlIGxvZyBsZXZlbC5cclxuICogQHBhcmFtIHtFcnJvcn0gZXJyb3IgLSBUaGUgZXJyb3Igb2JqZWN0LlxyXG4gKiBAcGFyYW0ge3N0cmluZ30gY3VzdG9tTWVzc2FnZSAtIEFuIG9wdGlvbmFsIGN1c3RvbSBtZXNzYWdlIHRvIGJlIGxvZ2dlZCBhbG9uZ1xyXG4gKiB3aXRoIHRoZSBlcnJvci5cclxuICovXHJcbmV4cG9ydCBjb25zdCBsb2dXaXRoU3RhY2sgPSAobmV3TGV2ZWwsIGVycm9yLCBjdXN0b21NZXNzYWdlKSA9PiB7XHJcbiAgLy8gR2V0IHRoZSBtYWluIG1lc3NhZ2VcclxuICBjb25zdCBtYWluTWVzc2FnZSA9IGN1c3RvbU1lc3NhZ2UgfHwgZXJyb3IubWVzc2FnZTtcclxuXHJcbiAgLy8gQ3VycmVudCBsb2dnaW5nIG9wdGlvbnNcclxuICBjb25zdCB7IGxldmVsLCBsZXZlbHNEZXNjIH0gPSBsb2dnaW5nO1xyXG5cclxuICAvLyBDaGVjayBpZiBsb2cgbGV2ZWwgaXMgd2l0aGluIGEgY29ycmVjdCByYW5nZVxyXG4gIGlmIChuZXdMZXZlbCA9PT0gMCB8fCBuZXdMZXZlbCA+IGxldmVsIHx8IGxldmVsID4gbGV2ZWxzRGVzYy5sZW5ndGgpIHtcclxuICAgIHJldHVybjtcclxuICB9XHJcblxyXG4gIC8vIEdldCByaWQgb2YgdGhlIEdNVCB0ZXh0IGluZm9ybWF0aW9uXHJcbiAgY29uc3QgbmV3RGF0ZSA9IG5ldyBEYXRlKCkudG9TdHJpbmcoKS5zcGxpdCgnKCcpWzBdLnRyaW0oKTtcclxuXHJcbiAgLy8gQ3JlYXRlIGEgbWVzc2FnZSdzIHByZWZpeFxyXG4gIGNvbnN0IHByZWZpeCA9IGAke25ld0RhdGV9IFske2xldmVsc0Rlc2NbbmV3TGV2ZWwgLSAxXS50aXRsZX1dIC1gO1xyXG5cclxuICAvLyBJZiB0aGUgY3VzdG9tTWVzc2FnZSBleGlzdHMsIHdlIHdhbnQgdG8gZGlzcGxheSB0aGUgd2hvbGUgc3RhY2sgbWVzc2FnZVxyXG4gIGNvbnN0IHN0YWNrTWVzc2FnZSA9XHJcbiAgICBlcnJvci5tZXNzYWdlICE9PSBlcnJvci5zdGFja01lc3NhZ2UgfHwgZXJyb3Iuc3RhY2tNZXNzYWdlID09PSB1bmRlZmluZWRcclxuICAgICAgPyBlcnJvci5zdGFja1xyXG4gICAgICA6IGVycm9yLnN0YWNrLnNwbGl0KCdcXG4nKS5zbGljZSgxKS5qb2luKCdcXG4nKTtcclxuXHJcbiAgLy8gQ29tYmluZSBjdXN0b20gbWVzc2FnZSBvciBlcnJvciBtZXNzYWdlIHdpdGggZXJyb3Igc3RhY2sgbWVzc2FnZVxyXG4gIGNvbnN0IHRleHRzID0gW21haW5NZXNzYWdlLCAnXFxuJywgc3RhY2tNZXNzYWdlXTtcclxuXHJcbiAgLy8gTG9nIHRvIGNvbnNvbGVcclxuICBpZiAobG9nZ2luZy50b0NvbnNvbGUpIHtcclxuICAgIGNvbnNvbGUubG9nLmFwcGx5KFxyXG4gICAgICB1bmRlZmluZWQsXHJcbiAgICAgIFtwcmVmaXgudG9TdHJpbmcoKVtsb2dnaW5nLmxldmVsc0Rlc2NbbmV3TGV2ZWwgLSAxXS5jb2xvcl1dLmNvbmNhdChbXHJcbiAgICAgICAgbWFpbk1lc3NhZ2VbY29sb3JzW25ld0xldmVsIC0gMV1dLFxyXG4gICAgICAgICdcXG4nLFxyXG4gICAgICAgIHN0YWNrTWVzc2FnZVxyXG4gICAgICBdKVxyXG4gICAgKTtcclxuICB9XHJcblxyXG4gIC8vIENhbGwgYXZhaWxhYmxlIGxvZyBsaXN0ZW5lcnNcclxuICBsb2dnaW5nLmxpc3RlbmVycy5mb3JFYWNoKChmbikgPT4ge1xyXG4gICAgZm4ocHJlZml4LCB0ZXh0cy5qb2luKCcgJykpO1xyXG4gIH0pO1xyXG5cclxuICAvLyBMb2cgdG8gZmlsZVxyXG4gIGxvZ1RvRmlsZSh0ZXh0cywgcHJlZml4KTtcclxufTtcclxuXHJcbi8qKlxyXG4gKiBTZXRzIHRoZSBsb2cgbGV2ZWwgdG8gdGhlIHNwZWNpZmllZCB2YWx1ZS4gTG9nIGxldmVscyBhcmUgKDAgPSBubyBsb2dnaW5nLFxyXG4gKiAxID0gZXJyb3IsIDIgPSB3YXJuaW5nLCAzID0gbm90aWNlLCA0ID0gdmVyYm9zZSBvciA1ID0gYmVuY2htYXJrKVxyXG4gKlxyXG4gKiBAcGFyYW0ge251bWJlcn0gbmV3TGV2ZWwgLSBUaGUgbmV3IGxvZyBsZXZlbCB0byBiZSBzZXQuXHJcbiAqL1xyXG5leHBvcnQgY29uc3Qgc2V0TG9nTGV2ZWwgPSAobmV3TGV2ZWwpID0+IHtcclxuICBpZiAobmV3TGV2ZWwgPj0gMCAmJiBuZXdMZXZlbCA8PSBsb2dnaW5nLmxldmVsc0Rlc2MubGVuZ3RoKSB7XHJcbiAgICBsb2dnaW5nLmxldmVsID0gbmV3TGV2ZWw7XHJcbiAgfVxyXG59O1xyXG5cclxuLyoqXHJcbiAqIEVuYWJsZXMgZmlsZSBsb2dnaW5nIHdpdGggdGhlIHNwZWNpZmllZCBkZXN0aW5hdGlvbiBhbmQgbG9nIGZpbGUuXHJcbiAqXHJcbiAqIEBwYXJhbSB7c3RyaW5nfSBsb2dEZXN0IC0gVGhlIGRlc3RpbmF0aW9uIHBhdGggZm9yIGxvZyBmaWxlcy5cclxuICogQHBhcmFtIHtzdHJpbmd9IGxvZ0ZpbGUgLSBUaGUgbG9nIGZpbGUgbmFtZS5cclxuICovXHJcbmV4cG9ydCBjb25zdCBlbmFibGVGaWxlTG9nZ2luZyA9IChsb2dEZXN0LCBsb2dGaWxlKSA9PiB7XHJcbiAgLy8gVXBkYXRlIGxvZ2dpbmcgb3B0aW9uc1xyXG4gIGxvZ2dpbmcgPSB7XHJcbiAgICAuLi5sb2dnaW5nLFxyXG4gICAgZGVzdDogbG9nRGVzdCB8fCBsb2dnaW5nLmRlc3QsXHJcbiAgICBmaWxlOiBsb2dGaWxlIHx8IGxvZ2dpbmcuZmlsZSxcclxuICAgIHRvRmlsZTogdHJ1ZVxyXG4gIH07XHJcblxyXG4gIGlmIChsb2dnaW5nLmRlc3QubGVuZ3RoID09PSAwKSB7XHJcbiAgICByZXR1cm4gbG9nKDEsICdbbG9nZ2VyXSBGaWxlIGxvZ2dpbmcgaW5pdGlhbGl6YXRpb246IG5vIHBhdGggc3VwcGxpZWQuJyk7XHJcbiAgfVxyXG5cclxuICBpZiAoIWxvZ2dpbmcuZGVzdC5lbmRzV2l0aCgnLycpKSB7XHJcbiAgICBsb2dnaW5nLmRlc3QgKz0gJy8nO1xyXG4gIH1cclxufTtcclxuXHJcbi8qKlxyXG4gKiBJbml0aWFsaXplcyBsb2dnaW5nIHdpdGggdGhlIHNwZWNpZmllZCBsb2dnaW5nIGNvbmZpZ3VyYXRpb24uXHJcbiAqXHJcbiAqIEBwYXJhbSB7T2JqZWN0fSBsb2dnaW5nIC0gVGhlIGxvZ2dpbmcgY29uZmlndXJhdGlvbiBvYmplY3QuXHJcbiAqL1xyXG5leHBvcnQgY29uc3QgaW5pdExvZ2dpbmcgPSAobG9nZ2luZykgPT4ge1xyXG4gIC8vIFNldCB0aGUgbG9nIGxldmVsXHJcbiAgc2V0TG9nTGV2ZWwobG9nZ2luZyAmJiBwYXJzZUludChsb2dnaW5nLmxldmVsKSk7XHJcblxyXG4gIC8vIFNldCB0aGUgbG9nIGZpbGUgcGF0aCBhbmQgbmFtZVxyXG4gIGlmIChsb2dnaW5nICYmIGxvZ2dpbmcuZGVzdCkge1xyXG4gICAgZW5hYmxlRmlsZUxvZ2dpbmcoXHJcbiAgICAgIGxvZ2dpbmcuZGVzdCxcclxuICAgICAgbG9nZ2luZy5maWxlIHx8ICdoaWdoY2hhcnRzLWV4cG9ydC1zZXJ2ZXIubG9nJ1xyXG4gICAgKTtcclxuICB9XHJcbn07XHJcblxyXG4vKipcclxuICogQWRkcyBhIGxpc3RlbmVyIGZ1bmN0aW9uIHRvIHRoZSBsb2dnaW5nIHN5c3RlbS5cclxuICpcclxuICogQHBhcmFtIHtmdW5jdGlvbn0gZm4gLSBUaGUgbGlzdGVuZXIgZnVuY3Rpb24gdG8gYmUgYWRkZWQuXHJcbiAqL1xyXG5leHBvcnQgY29uc3QgbGlzdGVuID0gKGZuKSA9PiB7XHJcbiAgbG9nZ2luZy5saXN0ZW5lcnMucHVzaChmbik7XHJcbn07XHJcblxyXG4vKipcclxuICogVG9nZ2xlcyB0aGUgc3RhbmRhcmQgb3V0cHV0IChjb25zb2xlKSBsb2dnaW5nLlxyXG4gKlxyXG4gKiBAcGFyYW0ge2Jvb2xlYW59IGVuYWJsZWQgLSBJZiB0cnVlLCBlbmFibGVzIGNvbnNvbGUgbG9nZ2luZzsgaWYgZmFsc2UsXHJcbiAqIGRpc2FibGVzIGl0LlxyXG4gKi9cclxuZXhwb3J0IGNvbnN0IHRvZ2dsZVNURE91dCA9IChlbmFibGVkKSA9PiB7XHJcbiAgbG9nZ2luZy50b0NvbnNvbGUgPSBlbmFibGVkO1xyXG59O1xyXG5cclxuZXhwb3J0IGRlZmF1bHQge1xyXG4gIGxvZyxcclxuICBsb2dXaXRoU3RhY2ssXHJcbiAgc2V0TG9nTGV2ZWwsXHJcbiAgZW5hYmxlRmlsZUxvZ2dpbmcsXHJcbiAgaW5pdExvZ2dpbmcsXHJcbiAgbGlzdGVuLFxyXG4gIHRvZ2dsZVNURE91dFxyXG59O1xyXG4iLCIvKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKlxyXG5cclxuSGlnaGNoYXJ0cyBFeHBvcnQgU2VydmVyXHJcblxyXG5Db3B5cmlnaHQgKGMpIDIwMTYtMjAyNCwgSGlnaHNvZnRcclxuXHJcbkxpY2VuY2VkIHVuZGVyIHRoZSBNSVQgbGljZW5jZS5cclxuXHJcbkFkZGl0aW9uYWxseSBhIHZhbGlkIEhpZ2hjaGFydHMgbGljZW5zZSBpcyByZXF1aXJlZCBmb3IgdXNlLlxyXG5cclxuU2VlIExJQ0VOU0UgZmlsZSBpbiByb290IGZvciBkZXRhaWxzLlxyXG5cclxuKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKi9cclxuXHJcbmltcG9ydCB7IHJlYWRGaWxlU3luYyB9IGZyb20gJ2ZzJztcclxuaW1wb3J0IHsgam9pbiB9IGZyb20gJ3BhdGgnO1xyXG5pbXBvcnQgeyBmaWxlVVJMVG9QYXRoIH0gZnJvbSAndXJsJztcclxuXHJcbmltcG9ydCB7IGRlZmF1bHRDb25maWcgfSBmcm9tICcuLi9saWIvc2NoZW1hcy9jb25maWcuanMnO1xyXG5pbXBvcnQgeyBsb2csIGxvZ1dpdGhTdGFjayB9IGZyb20gJy4vbG9nZ2VyLmpzJztcclxuXHJcbmNvbnN0IE1BWF9CQUNLT0ZGX0FUVEVNUFRTID0gNjtcclxuXHJcbmV4cG9ydCBjb25zdCBfX2Rpcm5hbWUgPSBmaWxlVVJMVG9QYXRoKG5ldyBVUkwoJy4uLy4nLCBpbXBvcnQubWV0YS51cmwpKTtcclxuXHJcbi8qKlxyXG4gKiBDbGVhcnMgYW5kIHN0YW5kYXJkaXplcyB0ZXh0IGJ5IHJlcGxhY2luZyBtdWx0aXBsZSBjb25zZWN1dGl2ZSB3aGl0ZXNwYWNlXHJcbiAqIGNoYXJhY3RlcnMgd2l0aCBhIHNpbmdsZSBzcGFjZSBhbmQgdHJpbW1pbmcgYW55IGxlYWRpbmcgb3IgdHJhaWxpbmdcclxuICogd2hpdGVzcGFjZS5cclxuICpcclxuICogQHBhcmFtIHtzdHJpbmd9IHRleHQgLSBUaGUgaW5wdXQgdGV4dCB0byBiZSBjbGVhcmVkLlxyXG4gKiBAcGFyYW0ge1JlZ0V4cH0gW3J1bGU9L1xcc1xccysvZ10gLSBUaGUgcmVndWxhciBleHByZXNzaW9uIHJ1bGUgdG8gbWF0Y2hcclxuICogbXVsdGlwbGUgY29uc2VjdXRpdmUgd2hpdGVzcGFjZSBjaGFyYWN0ZXJzLlxyXG4gKiBAcGFyYW0ge3N0cmluZ30gW3JlcGxhY2VyPScgJ10gLSBUaGUgc3RyaW5nIHVzZWQgdG8gcmVwbGFjZSBtdWx0aXBsZVxyXG4gKiBjb25zZWN1dGl2ZSB3aGl0ZXNwYWNlIGNoYXJhY3RlcnMuXHJcbiAqXHJcbiAqIEByZXR1cm5zIHtzdHJpbmd9IC0gVGhlIGNsZWFyZWQgYW5kIHN0YW5kYXJkaXplZCB0ZXh0LlxyXG4gKi9cclxuZXhwb3J0IGNvbnN0IGNsZWFyVGV4dCA9ICh0ZXh0LCBydWxlID0gL1xcc1xccysvZywgcmVwbGFjZXIgPSAnICcpID0+XHJcbiAgdGV4dC5yZXBsYWNlQWxsKHJ1bGUsIHJlcGxhY2VyKS50cmltKCk7XHJcblxyXG4vKipcclxuICogSW1wbGVtZW50cyBhbiBleHBvbmVudGlhbCBiYWNrb2ZmIHN0cmF0ZWd5IGZvciByZXRyeWluZyBhIGZ1bmN0aW9uIHVudGlsXHJcbiAqIGEgY2VydGFpbiBudW1iZXIgb2YgYXR0ZW1wdHMgYXJlIHJlYWNoZWQuXHJcbiAqXHJcbiAqIEBwYXJhbSB7RnVuY3Rpb259IGZuIC0gVGhlIGZ1bmN0aW9uIHRvIGJlIHJldHJpZWQuXHJcbiAqIEBwYXJhbSB7bnVtYmVyfSBbYXR0ZW1wdD0wXSAtIFRoZSBjdXJyZW50IGF0dGVtcHQgbnVtYmVyLlxyXG4gKiBAcGFyYW0gey4uLmFueX0gYXJncyAtIEFyZ3VtZW50cyB0byBiZSBwYXNzZWQgdG8gdGhlIGZ1bmN0aW9uLlxyXG4gKlxyXG4gKiBAcmV0dXJucyB7UHJvbWlzZX0gLSBBIHByb21pc2UgdGhhdCByZXNvbHZlcyB0byB0aGUgcmVzdWx0IG9mIHRoZSBmdW5jdGlvblxyXG4gKiBpZiBzdWNjZXNzZnVsLlxyXG4gKlxyXG4gKiBAdGhyb3dzIHtFcnJvcn0gLSBUaHJvd3MgYW4gZXJyb3IgaWYgdGhlIG1heGltdW0gbnVtYmVyIG9mIGF0dGVtcHRzXHJcbiAqIGlzIHJlYWNoZWQuXHJcbiAqL1xyXG5leHBvcnQgY29uc3QgZXhwQmFja29mZiA9IGFzeW5jIChmbiwgYXR0ZW1wdCA9IDAsIC4uLmFyZ3MpID0+IHtcclxuICB0cnkge1xyXG4gICAgLy8gVHJ5IHRvIGNhbGwgdGhlIGZ1bmN0aW9uXHJcbiAgICByZXR1cm4gYXdhaXQgZm4oLi4uYXJncyk7XHJcbiAgfSBjYXRjaCAoZXJyb3IpIHtcclxuICAgIC8vIENhbGN1bGF0ZSBkZWxheSBpbiBtc1xyXG4gICAgY29uc3QgZGVsYXlJbk1zID0gMiAqKiBhdHRlbXB0ICogMTAwMDtcclxuXHJcbiAgICAvLyBJZiB0aGUgYXR0ZW1wdCBleGNlZWRzIHRoZSBtYXhpbXVtIGF0dGVtcHRzIG9mIHJlYXBlYXQsIHRocm93IGFuIGVycm9yXHJcbiAgICBpZiAoKythdHRlbXB0ID49IE1BWF9CQUNLT0ZGX0FUVEVNUFRTKSB7XHJcbiAgICAgIHRocm93IGVycm9yO1xyXG4gICAgfVxyXG5cclxuICAgIC8vIFdhaXQgZ2l2ZW4gYW1vdW50IG9mIHRpbWVcclxuICAgIGF3YWl0IG5ldyBQcm9taXNlKChyZXNwb25zZSkgPT4gc2V0VGltZW91dChyZXNwb25zZSwgZGVsYXlJbk1zKSk7XHJcbiAgICBsb2coXHJcbiAgICAgIDMsXHJcbiAgICAgIGBbcG9vbF0gV2FpdGVkICR7ZGVsYXlJbk1zfW1zIHVudGlsIG5leHQgY2FsbCBmb3IgdGhlIHJlc291cmNlIGlkOiAke2FyZ3NbMF19LmBcclxuICAgICk7XHJcblxyXG4gICAgLy8gVHJ5IGFnYWluXHJcbiAgICByZXR1cm4gZXhwQmFja29mZihmbiwgYXR0ZW1wdCwgLi4uYXJncyk7XHJcbiAgfVxyXG59O1xyXG5cclxuLyoqXHJcbiAqIEZpeGVzIHRoZSBleHBvcnQgdHlwZSBiYXNlZCBvbiBNSU1FIHR5cGVzIGFuZCBmaWxlIGV4dGVuc2lvbnMuXHJcbiAqXHJcbiAqIEBwYXJhbSB7c3RyaW5nfSB0eXBlIC0gVGhlIG9yaWdpbmFsIGV4cG9ydCB0eXBlLlxyXG4gKiBAcGFyYW0ge3N0cmluZ30gb3V0ZmlsZSAtIFRoZSBmaWxlIHBhdGggb3IgbmFtZS5cclxuICpcclxuICogQHJldHVybnMge3N0cmluZ30gLSBUaGUgY29ycmVjdGVkIGV4cG9ydCB0eXBlLlxyXG4gKi9cclxuZXhwb3J0IGNvbnN0IGZpeFR5cGUgPSAodHlwZSwgb3V0ZmlsZSkgPT4ge1xyXG4gIC8vIE1JTUUgdHlwZXNcclxuICBjb25zdCBtaW1lVHlwZXMgPSB7XHJcbiAgICAnaW1hZ2UvcG5nJzogJ3BuZycsXHJcbiAgICAnaW1hZ2UvanBlZyc6ICdqcGVnJyxcclxuICAgICdhcHBsaWNhdGlvbi9wZGYnOiAncGRmJyxcclxuICAgICdpbWFnZS9zdmcreG1sJzogJ3N2ZydcclxuICB9O1xyXG5cclxuICAvLyBGb3JtYXRzXHJcbiAgY29uc3QgZm9ybWF0cyA9IFsncG5nJywgJ2pwZWcnLCAncGRmJywgJ3N2ZyddO1xyXG5cclxuICAvLyBDaGVjayBpZiB0eXBlIGFuZCBvdXRmaWxlJ3MgZXh0ZW5zaW9ucyBhcmUgdGhlIHNhbWVcclxuICBpZiAob3V0ZmlsZSkge1xyXG4gICAgY29uc3Qgb3V0VHlwZSA9IG91dGZpbGUuc3BsaXQoJy4nKS5wb3AoKTtcclxuXHJcbiAgICBpZiAob3V0VHlwZSA9PT0gJ2pwZycpIHtcclxuICAgICAgdHlwZSA9ICdqcGVnJztcclxuICAgIH0gZWxzZSBpZiAoZm9ybWF0cy5pbmNsdWRlcyhvdXRUeXBlKSAmJiB0eXBlICE9PSBvdXRUeXBlKSB7XHJcbiAgICAgIHR5cGUgPSBvdXRUeXBlO1xyXG4gICAgfVxyXG4gIH1cclxuXHJcbiAgLy8gUmV0dXJuIGEgY29ycmVjdCB0eXBlXHJcbiAgcmV0dXJuIG1pbWVUeXBlc1t0eXBlXSB8fCBmb3JtYXRzLmZpbmQoKHQpID0+IHQgPT09IHR5cGUpIHx8ICdwbmcnO1xyXG59O1xyXG5cclxuLyoqXHJcbiAqIEhhbmRsZXMgYW5kIHZhbGlkYXRlcyByZXNvdXJjZXMgZm9yIGV4cG9ydC5cclxuICpcclxuICogQHBhcmFtIHtPYmplY3R8c3RyaW5nfSByZXNvdXJjZXMgLSBUaGUgcmVzb3VyY2VzIHRvIGJlIGhhbmRsZWQuIENhbiBiZSBlaXRoZXJcclxuICogYSBKU09OIG9iamVjdCwgc3RyaW5naWZpZWQgSlNPTiBvciBhIHBhdGggdG8gYSBKU09OIGZpbGUuXHJcbiAqIEBwYXJhbSB7Ym9vbGVhbn0gYWxsb3dGaWxlUmVzb3VyY2VzIC0gV2hldGhlciB0byBhbGxvdyBsb2FkaW5nIHJlc291cmNlcyBmcm9tXHJcbiAqIGZpbGVzLlxyXG4gKlxyXG4gKiBAcmV0dXJucyB7T2JqZWN0fHVuZGVmaW5lZH0gLSBUaGUgaGFuZGxlZCByZXNvdXJjZXMgb3IgdW5kZWZpbmVkIGlmIG5vIHZhbGlkXHJcbiAqIHJlc291cmNlcyBhcmUgZm91bmQuXHJcbiAqL1xyXG5leHBvcnQgY29uc3QgaGFuZGxlUmVzb3VyY2VzID0gKHJlc291cmNlcyA9IGZhbHNlLCBhbGxvd0ZpbGVSZXNvdXJjZXMpID0+IHtcclxuICBjb25zdCBhbGxvd2VkUHJvcHMgPSBbJ2pzJywgJ2NzcycsICdmaWxlcyddO1xyXG5cclxuICBsZXQgaGFuZGxlZFJlc291cmNlcyA9IHJlc291cmNlcztcclxuICBsZXQgY29ycmVjdFJlc291cmNlcyA9IGZhbHNlO1xyXG5cclxuICAvLyBUcnkgdG8gbG9hZCByZXNvdXJjZXMgZnJvbSBhIGZpbGVcclxuICBpZiAoYWxsb3dGaWxlUmVzb3VyY2VzICYmIHJlc291cmNlcy5lbmRzV2l0aCgnLmpzb24nKSkge1xyXG4gICAgdHJ5IHtcclxuICAgICAgaGFuZGxlZFJlc291cmNlcyA9IGlzQ29ycmVjdEpTT04ocmVhZEZpbGVTeW5jKHJlc291cmNlcywgJ3V0ZjgnKSk7XHJcbiAgICB9IGNhdGNoIChlcnJvcikge1xyXG4gICAgICByZXR1cm4gbG9nV2l0aFN0YWNrKDIsIGVycm9yLCBgW2NsaV0gTm8gcmVzb3VyY2VzIGZvdW5kLmApO1xyXG4gICAgfVxyXG4gIH0gZWxzZSB7XHJcbiAgICAvLyBUcnkgdG8gZ2V0IEpTT05cclxuICAgIGhhbmRsZWRSZXNvdXJjZXMgPSBpc0NvcnJlY3RKU09OKHJlc291cmNlcyk7XHJcblxyXG4gICAgLy8gR2V0IHJpZCBvZiB0aGUgZmlsZXMgc2VjdGlvblxyXG4gICAgaWYgKGhhbmRsZWRSZXNvdXJjZXMgJiYgIWFsbG93RmlsZVJlc291cmNlcykge1xyXG4gICAgICBkZWxldGUgaGFuZGxlZFJlc291cmNlcy5maWxlcztcclxuICAgIH1cclxuICB9XHJcblxyXG4gIC8vIEZpbHRlciBmcm9tIHVubmVjZXNzYXJ5IHByb3BlcnRpZXNcclxuICBmb3IgKGNvbnN0IHByb3BOYW1lIGluIGhhbmRsZWRSZXNvdXJjZXMpIHtcclxuICAgIGlmICghYWxsb3dlZFByb3BzLmluY2x1ZGVzKHByb3BOYW1lKSkge1xyXG4gICAgICBkZWxldGUgaGFuZGxlZFJlc291cmNlc1twcm9wTmFtZV07XHJcbiAgICB9IGVsc2UgaWYgKCFjb3JyZWN0UmVzb3VyY2VzKSB7XHJcbiAgICAgIGNvcnJlY3RSZXNvdXJjZXMgPSB0cnVlO1xyXG4gICAgfVxyXG4gIH1cclxuXHJcbiAgLy8gQ2hlY2sgaWYgYXQgbGVhc3Qgb25lIG9mIGFsbG93ZWQgcHJvcGVydGllcyBpcyBwcmVzZW50XHJcbiAgaWYgKCFjb3JyZWN0UmVzb3VyY2VzKSB7XHJcbiAgICByZXR1cm4gbG9nKDMsIGBbY2xpXSBObyByZXNvdXJjZXMgZm91bmQuYCk7XHJcbiAgfVxyXG5cclxuICAvLyBIYW5kbGUgZmlsZXMgc2VjdGlvblxyXG4gIGlmIChoYW5kbGVkUmVzb3VyY2VzLmZpbGVzKSB7XHJcbiAgICBoYW5kbGVkUmVzb3VyY2VzLmZpbGVzID0gaGFuZGxlZFJlc291cmNlcy5maWxlcy5tYXAoKGl0ZW0pID0+IGl0ZW0udHJpbSgpKTtcclxuICAgIGlmICghaGFuZGxlZFJlc291cmNlcy5maWxlcyB8fCBoYW5kbGVkUmVzb3VyY2VzLmZpbGVzLmxlbmd0aCA8PSAwKSB7XHJcbiAgICAgIGRlbGV0ZSBoYW5kbGVkUmVzb3VyY2VzLmZpbGVzO1xyXG4gICAgfVxyXG4gIH1cclxuXHJcbiAgLy8gUmV0dXJuIHJlc291cmNlc1xyXG4gIHJldHVybiBoYW5kbGVkUmVzb3VyY2VzO1xyXG59O1xyXG5cclxuLyoqXHJcbiAqIFZhbGlkYXRlcyBhbmQgcGFyc2VzIEpTT04gZGF0YS4gQ2hlY2tzIGlmIHByb3ZpZGVkIGRhdGEgaXMgb3IgY2FuXHJcbiAqIGJlIGEgY29ycmVjdCBKU09OLiBJZiBhIHByaW1pdGl2ZSBpcyBwcm92aWRlZCwgaXQgaXMgc3RyaW5naWZpZWQgYW5kIHJldHVybmVkLlxyXG4gKlxyXG4gKiBAcGFyYW0ge09iamVjdHxzdHJpbmd9IGRhdGEgLSBUaGUgSlNPTiBkYXRhIHRvIGJlIHZhbGlkYXRlZCBhbmQgcGFyc2VkLlxyXG4gKiBAcGFyYW0ge2Jvb2xlYW59IHRvU3RyaW5nIC0gV2hldGhlciB0byByZXR1cm4gYSBzdHJpbmdpZmllZCByZXByZXNlbnRhdGlvblxyXG4gKiBvZiB0aGUgcGFyc2VkIEpTT04uXHJcbiAqXHJcbiAqIEByZXR1cm5zIHtPYmplY3R8c3RyaW5nfGJvb2xlYW59IC0gVGhlIHBhcnNlZCBKU09OIG9iamVjdCwgc3RyaW5naWZpZWQgSlNPTixcclxuICogb3IgZmFsc2UgaWYgdmFsaWRhdGlvbiBmYWlscy5cclxuICovXHJcbmV4cG9ydCBmdW5jdGlvbiBpc0NvcnJlY3RKU09OKGRhdGEsIHRvU3RyaW5nKSB7XHJcbiAgdHJ5IHtcclxuICAgIC8vIEdldCB0aGUgc3RyaW5nIHJlcHJlc2VudGF0aW9uIGlmIG5vdCBhbHJlYWR5IGJlZm9yZSBwYXJzaW5nXHJcbiAgICBjb25zdCBwYXJzZWREYXRhID0gSlNPTi5wYXJzZShcclxuICAgICAgdHlwZW9mIGRhdGEgIT09ICdzdHJpbmcnID8gSlNPTi5zdHJpbmdpZnkoZGF0YSkgOiBkYXRhXHJcbiAgICApO1xyXG5cclxuICAgIC8vIFJldHVybiBhIHN0cmluZ2lmaWVkIHJlcHJlc2VudGF0aW9uIG9mIGEgSlNPTiBpZiByZXF1aXJlZFxyXG4gICAgaWYgKHR5cGVvZiBwYXJzZWREYXRhICE9PSAnc3RyaW5nJyAmJiB0b1N0cmluZykge1xyXG4gICAgICByZXR1cm4gSlNPTi5zdHJpbmdpZnkocGFyc2VkRGF0YSk7XHJcbiAgICB9XHJcblxyXG4gICAgLy8gUmV0dXJuIGEgSlNPTlxyXG4gICAgcmV0dXJuIHBhcnNlZERhdGE7XHJcbiAgfSBjYXRjaCB7XHJcbiAgICByZXR1cm4gZmFsc2U7XHJcbiAgfVxyXG59XHJcblxyXG4vKipcclxuICogQ2hlY2tzIGlmIHRoZSBnaXZlbiBpdGVtIGlzIGFuIG9iamVjdC5cclxuICpcclxuICogQHBhcmFtIHthbnl9IGl0ZW0gLSBUaGUgaXRlbSB0byBiZSBjaGVja2VkLlxyXG4gKlxyXG4gKiBAcmV0dXJucyB7Ym9vbGVhbn0gLSBUcnVlIGlmIHRoZSBpdGVtIGlzIGFuIG9iamVjdCwgZmFsc2Ugb3RoZXJ3aXNlLlxyXG4gKi9cclxuZXhwb3J0IGNvbnN0IGlzT2JqZWN0ID0gKGl0ZW0pID0+XHJcbiAgdHlwZW9mIGl0ZW0gPT09ICdvYmplY3QnICYmICFBcnJheS5pc0FycmF5KGl0ZW0pICYmIGl0ZW0gIT09IG51bGw7XHJcblxyXG4vKipcclxuICogQ2hlY2tzIGlmIHRoZSBnaXZlbiBvYmplY3QgaXMgZW1wdHkuXHJcbiAqXHJcbiAqIEBwYXJhbSB7T2JqZWN0fSBpdGVtIC0gVGhlIG9iamVjdCB0byBiZSBjaGVja2VkLlxyXG4gKlxyXG4gKiBAcmV0dXJucyB7Ym9vbGVhbn0gLSBUcnVlIGlmIHRoZSBvYmplY3QgaXMgZW1wdHksIGZhbHNlIG90aGVyd2lzZS5cclxuICovXHJcbmV4cG9ydCBjb25zdCBpc09iamVjdEVtcHR5ID0gKGl0ZW0pID0+XHJcbiAgdHlwZW9mIGl0ZW0gPT09ICdvYmplY3QnICYmXHJcbiAgIUFycmF5LmlzQXJyYXkoaXRlbSkgJiZcclxuICBpdGVtICE9PSBudWxsICYmXHJcbiAgT2JqZWN0LmtleXMoaXRlbSkubGVuZ3RoID09PSAwO1xyXG5cclxuLyoqXHJcbiAqIENoZWNrcyBpZiBhIHByaXZhdGUgSVAgcmFuZ2UgVVJMIGlzIGZvdW5kIGluIHRoZSBnaXZlbiBzdHJpbmcuXHJcbiAqXHJcbiAqIEBwYXJhbSB7c3RyaW5nfSBpdGVtIC0gVGhlIHN0cmluZyB0byBiZSBjaGVja2VkIGZvciBhIHByaXZhdGUgSVAgcmFuZ2UgVVJMLlxyXG4gKlxyXG4gKiBAcmV0dXJucyB7Ym9vbGVhbn0gLSBUcnVlIGlmIGEgcHJpdmF0ZSBJUCByYW5nZSBVUkwgaXMgZm91bmQsIGZhbHNlXHJcbiAqIG90aGVyd2lzZS5cclxuICovXHJcbmV4cG9ydCBjb25zdCBpc1ByaXZhdGVSYW5nZVVybEZvdW5kID0gKGl0ZW0pID0+IHtcclxuICBjb25zdCByZWdleFBhdHRlcm5zID0gW1xyXG4gICAgL3hsaW5rOmhyZWY9XCIoPzpodHRwOlxcL1xcL3xodHRwczpcXC9cXC8pP2xvY2FsaG9zdFxcYi8sXHJcbiAgICAveGxpbms6aHJlZj1cIig/Omh0dHA6XFwvXFwvfGh0dHBzOlxcL1xcLyk/MTBcXC5cXGR7MSwzfVxcLlxcZHsxLDN9XFwuXFxkezEsM31cXGIvLFxyXG4gICAgL3hsaW5rOmhyZWY9XCIoPzpodHRwOlxcL1xcL3xodHRwczpcXC9cXC8pPzEyN1xcLlxcZHsxLDN9XFwuXFxkezEsM31cXC5cXGR7MSwzfVxcYi8sXHJcbiAgICAveGxpbms6aHJlZj1cIig/Omh0dHA6XFwvXFwvfGh0dHBzOlxcL1xcLyk/MTcyXFwuKDFbNi05XXwyWzAtOV18M1swLTFdKVxcLlxcZHsxLDN9XFwuXFxkezEsM31cXGIvLFxyXG4gICAgL3hsaW5rOmhyZWY9XCIoPzpodHRwOlxcL1xcL3xodHRwczpcXC9cXC8pPzE5MlxcLjE2OFxcLlxcZHsxLDN9XFwuXFxkezEsM31cXGIvXHJcbiAgXTtcclxuXHJcbiAgcmV0dXJuIHJlZ2V4UGF0dGVybnMuc29tZSgocGF0dGVybikgPT4gcGF0dGVybi50ZXN0KGl0ZW0pKTtcclxufTtcclxuXHJcbi8qKlxyXG4gKiBDcmVhdGVzIGEgZGVlcCBjb3B5IG9mIHRoZSBnaXZlbiBvYmplY3Qgb3IgYXJyYXkuXHJcbiAqXHJcbiAqIEBwYXJhbSB7T2JqZWN0fEFycmF5fSBvYmogLSBUaGUgb2JqZWN0IG9yIGFycmF5IHRvIGJlIGRlZXBseSBjb3BpZWQuXHJcbiAqXHJcbiAqIEByZXR1cm5zIHtPYmplY3R8QXJyYXl9IC0gVGhlIGRlZXAgY29weSBvZiB0aGUgcHJvdmlkZWQgb2JqZWN0IG9yIGFycmF5LlxyXG4gKi9cclxuZXhwb3J0IGNvbnN0IGRlZXBDb3B5ID0gKG9iaikgPT4ge1xyXG4gIGlmIChvYmogPT09IG51bGwgfHwgdHlwZW9mIG9iaiAhPT0gJ29iamVjdCcpIHtcclxuICAgIHJldHVybiBvYmo7XHJcbiAgfVxyXG5cclxuICBjb25zdCBjb3B5ID0gQXJyYXkuaXNBcnJheShvYmopID8gW10gOiB7fTtcclxuXHJcbiAgZm9yIChjb25zdCBrZXkgaW4gb2JqKSB7XHJcbiAgICBpZiAoT2JqZWN0LnByb3RvdHlwZS5oYXNPd25Qcm9wZXJ0eS5jYWxsKG9iaiwga2V5KSkge1xyXG4gICAgICBjb3B5W2tleV0gPSBkZWVwQ29weShvYmpba2V5XSk7XHJcbiAgICB9XHJcbiAgfVxyXG5cclxuICByZXR1cm4gY29weTtcclxufTtcclxuXHJcbi8qKlxyXG4gKiBDb252ZXJ0cyB0aGUgcHJvdmlkZWQgb3B0aW9ucyBvYmplY3QgdG8gYSBKU09OLWZvcm1hdHRlZCBzdHJpbmcgd2l0aCB0aGVcclxuICogb3B0aW9uIHRvIHByZXNlcnZlIGZ1bmN0aW9ucy5cclxuICpcclxuICogQHBhcmFtIHtPYmplY3R9IG9wdGlvbnMgLSBUaGUgb3B0aW9ucyBvYmplY3QgdG8gYmUgY29udmVydGVkIHRvIGEgc3RyaW5nLlxyXG4gKiBAcGFyYW0ge2Jvb2xlYW59IGFsbG93RnVuY3Rpb25zIC0gSWYgc2V0IHRvIHRydWUsIGZ1bmN0aW9ucyBhcmUgcHJlc2VydmVkXHJcbiAqIGluIHRoZSBvdXRwdXQuXHJcbiAqXHJcbiAqIEByZXR1cm5zIHtzdHJpbmd9IC0gVGhlIEpTT04tZm9ybWF0dGVkIHN0cmluZyByZXByZXNlbnRpbmcgdGhlIG9wdGlvbnMuXHJcbiAqL1xyXG5leHBvcnQgY29uc3Qgb3B0aW9uc1N0cmluZ2lmeSA9IChvcHRpb25zLCBhbGxvd0Z1bmN0aW9ucykgPT4ge1xyXG4gIGNvbnN0IHJlcGxhY2VyQ2FsbGJhY2sgPSAobmFtZSwgdmFsdWUpID0+IHtcclxuICAgIGlmICh0eXBlb2YgdmFsdWUgPT09ICdzdHJpbmcnKSB7XHJcbiAgICAgIHZhbHVlID0gdmFsdWUudHJpbSgpO1xyXG5cclxuICAgICAgLy8gSWYgYWxsb3dGdW5jdGlvbnMgaXMgc2V0IHRvIHRydWUsIHByZXNlcnZlIGZ1bmN0aW9uc1xyXG4gICAgICBpZiAoXHJcbiAgICAgICAgKHZhbHVlLnN0YXJ0c1dpdGgoJ2Z1bmN0aW9uKCcpIHx8IHZhbHVlLnN0YXJ0c1dpdGgoJ2Z1bmN0aW9uICgnKSkgJiZcclxuICAgICAgICB2YWx1ZS5lbmRzV2l0aCgnfScpXHJcbiAgICAgICkge1xyXG4gICAgICAgIHZhbHVlID0gYWxsb3dGdW5jdGlvbnNcclxuICAgICAgICAgID8gYEVYUF9GVU4keyh2YWx1ZSArICcnKS5yZXBsYWNlQWxsKC9cXG58XFx0fFxcci9nLCAnICcpfUVYUF9GVU5gXHJcbiAgICAgICAgICA6IHVuZGVmaW5lZDtcclxuICAgICAgfVxyXG4gICAgfVxyXG5cclxuICAgIHJldHVybiB0eXBlb2YgdmFsdWUgPT09ICdmdW5jdGlvbidcclxuICAgICAgPyBgRVhQX0ZVTiR7KHZhbHVlICsgJycpLnJlcGxhY2VBbGwoL1xcbnxcXHR8XFxyL2csICcgJyl9RVhQX0ZVTmBcclxuICAgICAgOiB2YWx1ZTtcclxuICB9O1xyXG5cclxuICAvLyBTdHJpbmdpZnkgb3B0aW9ucyBhbmQgaWYgcmVxdWlyZWQsIHJlcGxhY2Ugc3BlY2lhbCBmdW5jdGlvbnMgbWFya3NcclxuICByZXR1cm4gSlNPTi5zdHJpbmdpZnkob3B0aW9ucywgcmVwbGFjZXJDYWxsYmFjaykucmVwbGFjZUFsbChcclxuICAgIC9cIkVYUF9GVU58RVhQX0ZVTlwiL2csXHJcbiAgICAnJ1xyXG4gICk7XHJcbn07XHJcblxyXG4vKipcclxuICogUHJpbnRzIHRoZSBIaWdoY2hhcnRzIEV4cG9ydCBTZXJ2ZXIgbG9nbyBhbmQgdmVyc2lvbiBpbmZvcm1hdGlvbi5cclxuICpcclxuICogQHBhcmFtIHtib29sZWFufSBub0xvZ28gLSBJZiB0cnVlLCBvbmx5IHByaW50cyB2ZXJzaW9uIGluZm9ybWF0aW9uIHdpdGhvdXRcclxuICogdGhlIGxvZ28uXHJcbiAqL1xyXG5leHBvcnQgY29uc3QgcHJpbnRMb2dvID0gKG5vTG9nbykgPT4ge1xyXG4gIC8vIEdldCBwYWNrYWdlIHZlcnNpb24gZWl0aGVyIGZyb20gZW52IG9yIGZyb20gcGFja2FnZS5qc29uXHJcbiAgY29uc3QgcGFja2FnZVZlcnNpb24gPSBKU09OLnBhcnNlKFxyXG4gICAgcmVhZEZpbGVTeW5jKGpvaW4oX19kaXJuYW1lLCAncGFja2FnZS5qc29uJykpXHJcbiAgKS52ZXJzaW9uO1xyXG5cclxuICAvLyBQcmludCB0ZXh0IG9ubHlcclxuICBpZiAobm9Mb2dvKSB7XHJcbiAgICBjb25zb2xlLmxvZyhgU3RhcnRpbmcgSGlnaGNoYXJ0cyBFeHBvcnQgU2VydmVyIHYke3BhY2thZ2VWZXJzaW9ufS4uLmApO1xyXG4gICAgcmV0dXJuO1xyXG4gIH1cclxuXHJcbiAgLy8gUHJpbnQgdGhlIGxvZ29cclxuICBjb25zb2xlLmxvZyhcclxuICAgIHJlYWRGaWxlU3luYyhfX2Rpcm5hbWUgKyAnL21zZy9zdGFydHVwLm1zZycpLnRvU3RyaW5nKCkuYm9sZC55ZWxsb3csXHJcbiAgICBgdiR7cGFja2FnZVZlcnNpb259YFxyXG4gICk7XHJcbn07XHJcblxyXG4vKipcclxuICogUHJpbnRzIHRoZSB1c2FnZSBpbmZvcm1hdGlvbiBmb3IgQ0xJIGFyZ3VtZW50cy4gSWYgcmVxdWlyZWQsIGl0IGNhbiBsaXN0XHJcbiAqIHByb3BlcnRpZXMgcmVjdXJzaXZlbHlcclxuICovXHJcbmV4cG9ydCBmdW5jdGlvbiBwcmludFVzYWdlKCkge1xyXG4gIGNvbnN0IHBhZCA9IDQ4O1xyXG4gIGNvbnN0IHJlYWRtZSA9ICdodHRwczovL2dpdGh1Yi5jb20vaGlnaGNoYXJ0cy9ub2RlLWV4cG9ydC1zZXJ2ZXIjcmVhZG1lJztcclxuXHJcbiAgLy8gRGlzcGxheSByZWFkbWUgaW5mb3JtYXRpb25cclxuICBjb25zb2xlLmxvZyhcclxuICAgICdcXG5Vc2FnZSBvZiBDTEkgYXJndW1lbnRzOicuYm9sZCxcclxuICAgICdcXG4tLS0tLS0nLFxyXG4gICAgYFxcbkZvciBtb3JlIGRldGFpbGVkIGluZm9ybWF0aW9uLCB2aXNpdCB0aGUgcmVhZG1lIGF0OiAke3JlYWRtZS5ib2xkLnllbGxvd30uYFxyXG4gICk7XHJcblxyXG4gIGNvbnN0IGN5Y2xlQ2F0ZWdvcmllcyA9IChvcHRpb25zKSA9PiB7XHJcbiAgICBmb3IgKGNvbnN0IFtuYW1lLCBvcHRpb25dIG9mIE9iamVjdC5lbnRyaWVzKG9wdGlvbnMpKSB7XHJcbiAgICAgIC8vIElmIGNhdGVnb3J5IGhhcyBtb3JlIGxldmVscywgZ28gZnVydGhlclxyXG4gICAgICBpZiAoIU9iamVjdC5wcm90b3R5cGUuaGFzT3duUHJvcGVydHkuY2FsbChvcHRpb24sICd2YWx1ZScpKSB7XHJcbiAgICAgICAgY3ljbGVDYXRlZ29yaWVzKG9wdGlvbik7XHJcbiAgICAgIH0gZWxzZSB7XHJcbiAgICAgICAgbGV0IGRlc2NOYW1lID0gYCAgLS0ke29wdGlvbi5jbGlOYW1lIHx8IG5hbWV9ICR7XHJcbiAgICAgICAgICAoJzwnICsgb3B0aW9uLnR5cGUgKyAnPicpLmdyZWVuXHJcbiAgICAgICAgfSBgO1xyXG4gICAgICAgIGlmIChkZXNjTmFtZS5sZW5ndGggPCBwYWQpIHtcclxuICAgICAgICAgIGZvciAobGV0IGkgPSBkZXNjTmFtZS5sZW5ndGg7IGkgPCBwYWQ7IGkrKykge1xyXG4gICAgICAgICAgICBkZXNjTmFtZSArPSAnLic7XHJcbiAgICAgICAgICB9XHJcbiAgICAgICAgfVxyXG5cclxuICAgICAgICAvLyBEaXNwbGF5IGNvcnJlY3RseSBhbGlnbmVkIG1lc3NhZ2VzXHJcbiAgICAgICAgY29uc29sZS5sb2coXHJcbiAgICAgICAgICBkZXNjTmFtZSxcclxuICAgICAgICAgIG9wdGlvbi5kZXNjcmlwdGlvbixcclxuICAgICAgICAgIGBbRGVmYXVsdDogJHtvcHRpb24udmFsdWUudG9TdHJpbmcoKS5ib2xkfV1gLmJsdWVcclxuICAgICAgICApO1xyXG4gICAgICB9XHJcbiAgICB9XHJcbiAgfTtcclxuXHJcbiAgLy8gQ3ljbGUgdGhyb3VnaCBvcHRpb25zIG9mIGVhY2ggY2F0ZWdvcmllcyBhbmQgZGlzcGxheSB0aGUgdXNhZ2UgaW5mb1xyXG4gIE9iamVjdC5rZXlzKGRlZmF1bHRDb25maWcpLmZvckVhY2goKGNhdGVnb3J5KSA9PiB7XHJcbiAgICAvLyBPbmx5IHB1cHBldGVlciBhbmQgaGlnaGNoYXJ0cyBjYXRlZ29yaWVzIGNhbm5vdCBiZSBjb25maWd1cmVkIHRocm91Z2ggQ0xJXHJcbiAgICBpZiAoIVsncHVwcGV0ZWVyJywgJ2hpZ2hjaGFydHMnXS5pbmNsdWRlcyhjYXRlZ29yeSkpIHtcclxuICAgICAgY29uc29sZS5sb2coYFxcbiR7Y2F0ZWdvcnkudG9VcHBlckNhc2UoKX1gLnJlZCk7XHJcbiAgICAgIGN5Y2xlQ2F0ZWdvcmllcyhkZWZhdWx0Q29uZmlnW2NhdGVnb3J5XSk7XHJcbiAgICB9XHJcbiAgfSk7XHJcbiAgY29uc29sZS5sb2coJ1xcbicpO1xyXG59XHJcblxyXG4vKipcclxuICogUm91bmRzIGEgbnVtYmVyIHRvIHRoZSBzcGVjaWZpZWQgcHJlY2lzaW9uLlxyXG4gKlxyXG4gKiBAcGFyYW0ge251bWJlcn0gdmFsdWUgLSBUaGUgbnVtYmVyIHRvIGJlIHJvdW5kZWQuXHJcbiAqIEBwYXJhbSB7bnVtYmVyfSBwcmVjaXNpb24gLSBUaGUgbnVtYmVyIG9mIGRlY2ltYWwgcGxhY2VzIHRvIHJvdW5kIHRvLlxyXG4gKlxyXG4gKiBAcmV0dXJucyB7bnVtYmVyfSAtIFRoZSByb3VuZGVkIG51bWJlci5cclxuICovXHJcbmV4cG9ydCBjb25zdCByb3VuZE51bWJlciA9ICh2YWx1ZSwgcHJlY2lzaW9uID0gMSkgPT4ge1xyXG4gIGNvbnN0IG11bHRpcGxpZXIgPSBNYXRoLnBvdygxMCwgcHJlY2lzaW9uIHx8IDApO1xyXG4gIHJldHVybiBNYXRoLnJvdW5kKCt2YWx1ZSAqIG11bHRpcGxpZXIpIC8gbXVsdGlwbGllcjtcclxufTtcclxuXHJcbi8qKlxyXG4gKiBDb252ZXJ0cyBhIHZhbHVlIHRvIGEgYm9vbGVhbi5cclxuICpcclxuICogQHBhcmFtIHthbnl9IGl0ZW0gLSBUaGUgdmFsdWUgdG8gYmUgY29udmVydGVkIHRvIGEgYm9vbGVhbi5cclxuICpcclxuICogQHJldHVybnMge2Jvb2xlYW59IC0gVGhlIGJvb2xlYW4gcmVwcmVzZW50YXRpb24gb2YgdGhlIGlucHV0IHZhbHVlLlxyXG4gKi9cclxuZXhwb3J0IGNvbnN0IHRvQm9vbGVhbiA9IChpdGVtKSA9PlxyXG4gIFsnZmFsc2UnLCAndW5kZWZpbmVkJywgJ251bGwnLCAnTmFOJywgJzAnLCAnJ10uaW5jbHVkZXMoaXRlbSlcclxuICAgID8gZmFsc2VcclxuICAgIDogISFpdGVtO1xyXG5cclxuLyoqXHJcbiAqIFdyYXBzIGN1c3RvbSBjb2RlIHRvIGV4ZWN1dGUgaXQgc2FmZWx5LlxyXG4gKlxyXG4gKiBAcGFyYW0ge3N0cmluZ30gY3VzdG9tQ29kZSAtIFRoZSBjdXN0b20gY29kZSB0byBiZSB3cmFwcGVkLlxyXG4gKiBAcGFyYW0ge2Jvb2xlYW59IGFsbG93RmlsZVJlc291cmNlcyAtIEZsYWcgdG8gYWxsb3cgbG9hZGluZyBjb2RlIGZyb20gYSBmaWxlLlxyXG4gKlxyXG4gKiBAcmV0dXJucyB7c3RyaW5nfGJvb2xlYW59IC0gVGhlIHdyYXBwZWQgY3VzdG9tIGNvZGUgb3IgZmFsc2UgaWYgd3JhcHBpbmdcclxuICogZmFpbHMuXHJcbiAqL1xyXG5leHBvcnQgY29uc3Qgd3JhcEFyb3VuZCA9IChjdXN0b21Db2RlLCBhbGxvd0ZpbGVSZXNvdXJjZXMpID0+IHtcclxuICBpZiAoY3VzdG9tQ29kZSAmJiB0eXBlb2YgY3VzdG9tQ29kZSA9PT0gJ3N0cmluZycpIHtcclxuICAgIGN1c3RvbUNvZGUgPSBjdXN0b21Db2RlLnRyaW0oKTtcclxuXHJcbiAgICBpZiAoY3VzdG9tQ29kZS5lbmRzV2l0aCgnLmpzJykpIHtcclxuICAgICAgcmV0dXJuIGFsbG93RmlsZVJlc291cmNlc1xyXG4gICAgICAgID8gd3JhcEFyb3VuZChyZWFkRmlsZVN5bmMoY3VzdG9tQ29kZSwgJ3V0ZjgnKSlcclxuICAgICAgICA6IGZhbHNlO1xyXG4gICAgfSBlbHNlIGlmIChcclxuICAgICAgY3VzdG9tQ29kZS5zdGFydHNXaXRoKCdmdW5jdGlvbigpJykgfHxcclxuICAgICAgY3VzdG9tQ29kZS5zdGFydHNXaXRoKCdmdW5jdGlvbiAoKScpIHx8XHJcbiAgICAgIGN1c3RvbUNvZGUuc3RhcnRzV2l0aCgnKCk9PicpIHx8XHJcbiAgICAgIGN1c3RvbUNvZGUuc3RhcnRzV2l0aCgnKCkgPT4nKVxyXG4gICAgKSB7XHJcbiAgICAgIHJldHVybiBgKCR7Y3VzdG9tQ29kZX0pKClgO1xyXG4gICAgfVxyXG4gICAgcmV0dXJuIGN1c3RvbUNvZGUucmVwbGFjZSgvOyQvLCAnJyk7XHJcbiAgfVxyXG59O1xyXG5cclxuLyoqXHJcbiAqIFV0aWxpdHkgdG8gbWVhc3VyZSBlbGFwc2VkIHRpbWUgdXNpbmcgdGhlIE5vZGUuanMgcHJvY2Vzcy5ocnRpbWUoKSBtZXRob2QuXHJcbiAqXHJcbiAqIEByZXR1cm5zIHtmdW5jdGlvbigpOiBudW1iZXJ9IC0gQSBmdW5jdGlvbiB0byBjYWxjdWxhdGUgdGhlIGVsYXBzZWQgdGltZVxyXG4gKiBpbiBtaWxsaXNlY29uZHMuXHJcbiAqL1xyXG5leHBvcnQgY29uc3QgbWVhc3VyZVRpbWUgPSAoKSA9PiB7XHJcbiAgY29uc3Qgc3RhcnQgPSBwcm9jZXNzLmhydGltZS5iaWdpbnQoKTtcclxuICByZXR1cm4gKCkgPT4gTnVtYmVyKHByb2Nlc3MuaHJ0aW1lLmJpZ2ludCgpIC0gc3RhcnQpIC8gMTAwMDAwMDtcclxufTtcclxuXHJcbmV4cG9ydCBkZWZhdWx0IHtcclxuICBfX2Rpcm5hbWUsXHJcbiAgY2xlYXJUZXh0LFxyXG4gIGV4cEJhY2tvZmYsXHJcbiAgZml4VHlwZSxcclxuICBoYW5kbGVSZXNvdXJjZXMsXHJcbiAgaXNDb3JyZWN0SlNPTixcclxuICBpc09iamVjdCxcclxuICBpc09iamVjdEVtcHR5LFxyXG4gIGlzUHJpdmF0ZVJhbmdlVXJsRm91bmQsXHJcbiAgb3B0aW9uc1N0cmluZ2lmeSxcclxuICBwcmludExvZ28sXHJcbiAgcHJpbnRVc2FnZSxcclxuICByb3VuZE51bWJlcixcclxuICB0b0Jvb2xlYW4sXHJcbiAgd3JhcEFyb3VuZCxcclxuICBtZWFzdXJlVGltZVxyXG59O1xyXG4iLCIvKipcclxuICogQGZpbGVvdmVydmlld1xyXG4gKiBUaGlzIGZpbGUgaXMgcmVzcG9uc2libGUgZm9yIHBhcnNpbmcgdGhlIGVudmlyb25tZW50IHZhcmlhYmxlcyB3aXRoIHRoZSAnem9kJyBsaWJyYXJ5LlxyXG4gKiBUaGUgcGFyc2VkIGVudmlyb25tZW50IHZhcmlhYmxlcyBhcmUgdGhlbiBleHBvcnRlZCB0byBiZSB1c2VkIGluIHRoZSBhcHBsaWNhdGlvbiBhcyBcImVudnNcIi5cclxuICogV2Ugc2hvdWxkIG5vdCB1c2UgcHJvY2Vzcy5lbnYgZGlyZWN0bHkgaW4gdGhlIGFwcGxpY2F0aW9uIGFzIHRoZXNlIHdvdWxkIG5vdCBiZSBwYXJzZWQgcHJvcGVybHkuXHJcbiAqXHJcbiAqIFRoZSBlbnZpcm9ubWVudCB2YXJpYWJsZXMgYXJlIHBhcnNlZCBhbmQgdmFsaWRhdGVkIG9ubHkgb25jZSB3aGVuIHRoZSBhcHBsaWNhdGlvbiBzdGFydHMuXHJcbiAqIFdlIHNob3VsZCB3cml0ZSBhIGN1c3RvbSB2YWxpZGF0b3Igb3IgYSB0cmFuc2Zvcm1lciBmb3IgZWFjaCBvZiB0aGUgb3B0aW9ucy5cclxuICpcclxuICogRm9yIGVudnMgbm90IGRlZmluZWQgaW4gY29uZmlnLmpzIHdpdGggZGVmYXVsdHMsIHdlIGFsc28gaW5jbHVkZSBkZWZhdWx0IHZhbHVlcyBoZXJlIChQUk9YWV8uLi4pLlxyXG4gKi9cclxuXHJcbmltcG9ydCB7IHogfSBmcm9tICd6b2QnO1xyXG5pbXBvcnQgZG90ZW52IGZyb20gJ2RvdGVudic7XHJcbmRvdGVudi5jb25maWcoKTtcclxuXHJcbi8vIE9iamVjdCB3aXRoIGN1c3RvbSB2YWxpZGF0b3JzIGFuZCB0cmFuc2Zvcm1lcnMsIHRvIGF2b2lkIHJlcGV0aXRpb24gaW4gdGhlIENvbmZpZyBvYmplY3RcclxuY29uc3QgdiA9IHtcclxuICBib29sZWFuOiAoKSA9PlxyXG4gICAgelxyXG4gICAgICAuZW51bShbJ3RydWUnLCAnZmFsc2UnXSlcclxuICAgICAgLnRyYW5zZm9ybSgodmFsdWUpID0+IHZhbHVlID09PSAndHJ1ZScpXHJcbiAgICAgIC5vcHRpb25hbCgpLFxyXG4gIGFycmF5OiAoKSA9PlxyXG4gICAgelxyXG4gICAgICAuc3RyaW5nKClcclxuICAgICAgLnRyYW5zZm9ybSgodmFsKSA9PiB2YWwuc3BsaXQoJywnKS5tYXAoKHYpID0+IHYudHJpbSgpKSlcclxuICAgICAgLm9wdGlvbmFsKClcclxufTtcclxuXHJcbmV4cG9ydCBjb25zdCBDb25maWcgPSB6Lm9iamVjdCh7XHJcbiAgLy8gaGlnaGNoYXJ0c1xyXG4gIEhJR0hDSEFSVFNfVkVSU0lPTjogelxyXG4gICAgLnN0cmluZygpXHJcbiAgICAucmVmaW5lKCh2YWx1ZSkgPT4gL14obGF0ZXN0fFxcZCsoXFwuXFxkKyl7MCwyfSkkLy50ZXN0KHZhbHVlKSwge1xyXG4gICAgICBtZXNzYWdlOlxyXG4gICAgICAgIFwiSElHSENIQVJUU19WRVJTSU9OIG11c3QgYmUgJ2xhdGVzdCcsIGEgbWFqb3IgdmVyc2lvbiwgb3IgaW4gdGhlIGZvcm0gWFguWVkuWlpcIlxyXG4gICAgfSlcclxuICAgIC5vcHRpb25hbCgpLCAvLyB0b2RvOiBjcmVhdGUgYW4gYXJyYXkgb2YgYXZhaWxhYmxlIEhpZ2hjaGFydHMgdmVyc2lvbnNcclxuICBISUdIQ0hBUlRTX0NETl9VUkw6IHpcclxuICAgIC5zdHJpbmcoKVxyXG4gICAgLnRyaW0oKVxyXG4gICAgLnJlZmluZSgodmFsKSA9PiB2YWwuc3RhcnRzV2l0aCgnaHR0cHM6Ly8nKSB8fCB2YWwuc3RhcnRzV2l0aCgnaHR0cDovLycpLCB7XHJcbiAgICAgIG1lc3NhZ2U6XHJcbiAgICAgICAgJ0ludmFsaWQgdmFsdWUgZm9yIEhJR0hDSEFSVFNfQ0ROX1VSTC4gSXQgc2hvdWxkIHN0YXJ0IHdpdGggaHR0cDovLyBvciBodHRwczovLy4nXHJcbiAgICB9KVxyXG4gICAgLm9wdGlvbmFsKCksXHJcbiAgSElHSENIQVJUU19DT1JFX1NDUklQVFM6IHYuYXJyYXkoKSxcclxuICBISUdIQ0hBUlRTX01PRFVMRVM6IHYuYXJyYXkoKSxcclxuICBISUdIQ0hBUlRTX0lORElDQVRPUlM6IHYuYXJyYXkoKSxcclxuICBISUdIQ0hBUlRTX0ZPUkNFX0ZFVENIOiB2LmJvb2xlYW4oKSxcclxuICBISUdIQ0hBUlRTX0NBQ0hFX1BBVEg6IHouc3RyaW5nKCkub3B0aW9uYWwoKSxcclxuICBISUdIQ0hBUlRTX0FETUlOX1RPS0VOOiB6LnN0cmluZygpLm9wdGlvbmFsKCksXHJcblxyXG4gIC8vIGV4cG9ydFxyXG4gIEVYUE9SVF9UWVBFOiB6LmVudW0oWydqcGVnJywgJ3BuZycsICdwZGYnLCAnc3ZnJ10pLm9wdGlvbmFsKCksXHJcbiAgRVhQT1JUX0NPTlNUUjogelxyXG4gICAgLnN0cmluZygpXHJcbiAgICAucmVmaW5lKFxyXG4gICAgICAodmFsKSA9PlxyXG4gICAgICAgIFsnY2hhcnQnLCAnc3RvY2tDaGFydCcsICdtYXBDaGFydCcsICdnYW50dENoYXJ0J10uaW5jbHVkZXModmFsIHx8ICcnKSxcclxuICAgICAgeyBtZXNzYWdlOiAnSW52YWxpZCB2YWx1ZSBmb3IgRVhQT1JUX0NPTlNUUi4gJyB9XHJcbiAgICApXHJcbiAgICAub3B0aW9uYWwoKSxcclxuICBFWFBPUlRfREVGQVVMVF9IRUlHSFQ6IHouY29lcmNlLm51bWJlcigpLnBvc2l0aXZlKCkub3B0aW9uYWwoKSxcclxuICBFWFBPUlRfREVGQVVMVF9XSURUSDogei5jb2VyY2UubnVtYmVyKCkucG9zaXRpdmUoKS5vcHRpb25hbCgpLFxyXG4gIEVYUE9SVF9ERUZBVUxUX1NDQUxFOiB6LmNvZXJjZS5udW1iZXIoKS5wb3NpdGl2ZSgpLm9wdGlvbmFsKCksXHJcbiAgRVhQT1JUX1JBU1RFUklaQVRJT05fVElNRU9VVDogei5jb2VyY2UubnVtYmVyKCkucG9zaXRpdmUoKS5vcHRpb25hbCgpLFxyXG5cclxuICAvLyBjdXN0b21cclxuICBDVVNUT01fTE9HSUNfQUxMT1dfQ09ERV9FWEVDVVRJT046IHYuYm9vbGVhbigpLFxyXG4gIENVU1RPTV9MT0dJQ19BTExPV19GSUxFTF9SRVNPVVJDRVM6IHYuYm9vbGVhbigpLFxyXG5cclxuICAvLyBzZXJ2ZXItcmVsYXRlZFxyXG4gIFNFUlZFUl9FTkFCTEU6IHYuYm9vbGVhbigpLFxyXG4gIFNFUlZFUl9IT1NUOiB6LnN0cmluZygpLm9wdGlvbmFsKCksXHJcbiAgU0VSVkVSX1BPUlQ6IHouY29lcmNlLm51bWJlcigpLm9wdGlvbmFsKCksXHJcbiAgU0VSVkVSX0JFTkNITUFSS0lORzogdi5ib29sZWFuKCksXHJcbiAgU0VSVkVSX1NTTF9FTkFCTEU6IHYuYm9vbGVhbigpLFxyXG4gIFNFUlZFUl9TU0xfRk9SQ0U6IHYuYm9vbGVhbigpLFxyXG4gIFNFUlZFUl9TU0xfUE9SVDogei5jb2VyY2UubnVtYmVyKCkub3B0aW9uYWwoKSxcclxuICBTRVJWRVJfU1NMX0NFUlRfUEFUSDogei5zdHJpbmcoKS5vcHRpb25hbCgpLFxyXG4gIFNFUlZFUl9SQVRFX0xJTUlUSU5HX0VOQUJMRTogdi5ib29sZWFuKCksXHJcbiAgU0VSVkVSX1JBVEVfTElNSVRJTkdfTUFYX1JFUVVFU1RTOiB6LmNvZXJjZS5udW1iZXIoKS5vcHRpb25hbCgpLFxyXG4gIFNFUlZFUl9SQVRFX0xJTUlUSU5HX1dJTkRPVzogei5jb2VyY2UubnVtYmVyKCkub3B0aW9uYWwoKSxcclxuICBTRVJWRVJfUkFURV9MSU1JVElOR19ERUxBWTogei5jb2VyY2UubnVtYmVyKCkub3B0aW9uYWwoKSxcclxuICBTRVJWRVJfUkFURV9MSU1JVElOR19UUlVTVF9QUk9YWTogdi5ib29sZWFuKCksXHJcbiAgU0VSVkVSX1JBVEVfTElNSVRJTkdfU0tJUF9LRVk6IHouc3RyaW5nKCkub3B0aW9uYWwoKSxcclxuICBTRVJWRVJfUkFURV9MSU1JVElOR19TS0lQX1RPS0VOOiB6LnN0cmluZygpLm9wdGlvbmFsKCksXHJcblxyXG4gIC8vIHBvb2xcclxuICBQT09MX01JTl9XT1JLRVJTOiB6LmNvZXJjZS5udW1iZXIoKS5vcHRpb25hbCgpLFxyXG4gIFBPT0xfTUFYX1dPUktFUlM6IHouY29lcmNlLm51bWJlcigpLm9wdGlvbmFsKCksXHJcbiAgUE9PTF9XT1JLX0xJTUlUOiB6LmNvZXJjZS5udW1iZXIoKS5vcHRpb25hbCgpLFxyXG4gIFBPT0xfQUNRVUlSRV9USU1FT1VUOiB6LmNvZXJjZS5udW1iZXIoKS5vcHRpb25hbCgpLFxyXG4gIFBPT0xfQ1JFQVRFX1RJTUVPVVQ6IHouY29lcmNlLm51bWJlcigpLm9wdGlvbmFsKCksXHJcbiAgUE9PTF9ERVNUUk9ZX1RJTUVPVVQ6IHouY29lcmNlLm51bWJlcigpLm9wdGlvbmFsKCksXHJcbiAgUE9PTF9JRExFX1RJTUVPVVQ6IHouY29lcmNlLm51bWJlcigpLm9wdGlvbmFsKCksXHJcbiAgUE9PTF9DUkVBVEVfUkVUUllfSU5URVJWQUw6IHouY29lcmNlLm51bWJlcigpLm9wdGlvbmFsKCksXHJcbiAgUE9PTF9SRUFQRVJfSU5URVJWQUw6IHouY29lcmNlLm51bWJlcigpLm9wdGlvbmFsKCksXHJcbiAgUE9PTF9CRU5DSE1BUktJTkc6IHYuYm9vbGVhbigpLFxyXG4gIFBPT0xfTElTVEVOX1RPX1BST0NFU1NfRVhJVFM6IHYuYm9vbGVhbigpLFxyXG5cclxuICAvLyBsb2dnZXJcclxuICBMT0dHSU5HX0xFVkVMOiB6LmNvZXJjZVxyXG4gICAgLm51bWJlcigpXHJcbiAgICAub3B0aW9uYWwoKVxyXG4gICAgLnJlZmluZSgodmFsKSA9PiAodmFsIHx8IDQpID49IDAgJiYgKHZhbCB8fCA0KSA8PSA0LCB7XHJcbiAgICAgIG1lc3NhZ2U6XHJcbiAgICAgICAgJ0ludmFsaWQgdmFsdWUgZm9yIExPR0dJTkdfTEVWRUwuIFdlIG9ubHkgYWNjZXB0IDAsIDEsIDIsIDMsIDQgYXMgbG9nZ2luZyBsZXZlbHMuJ1xyXG4gICAgfSksXHJcbiAgTE9HR0lOR19GSUxFOiB6LnN0cmluZygpLm9wdGlvbmFsKCksXHJcbiAgTE9HR0lOR19ERVNUOiB6LnN0cmluZygpLm9wdGlvbmFsKCksXHJcblxyXG4gIC8vIHVpXHJcbiAgVUlfRU5BQkxFOiB2LmJvb2xlYW4oKSxcclxuICBVSV9ST1VURTogei5zdHJpbmcoKS5vcHRpb25hbCgpLFxyXG5cclxuICAvLyBvdGhlclxyXG4gIE9USEVSX05PX0xPR086IHYuYm9vbGVhbigpLFxyXG4gIE5PREVfRU5WOiB6XHJcbiAgICAuZW51bShbJ2RldmVsb3BtZW50JywgJ3Byb2R1Y3Rpb24nLCAndGVzdCddKVxyXG4gICAgLm9wdGlvbmFsKClcclxuICAgIC5kZWZhdWx0KCdwcm9kdWN0aW9uJyksXHJcblxyXG4gIC8vIHByb3h5ICghIE5PVCBJTkNMVURFRCBJTiBDT05GSUcuSlMgISlcclxuICBQUk9YWV9TRVJWRVJfVElNRU9VVDogei5jb2VyY2UubnVtYmVyKCkucG9zaXRpdmUoKS5vcHRpb25hbCgpLmRlZmF1bHQoNTAwMCksXHJcbiAgUFJPWFlfU0VSVkVSX0hPU1Q6IHouc3RyaW5nKCkub3B0aW9uYWwoKS5kZWZhdWx0KCdsb2NhbGhvc3QnKSxcclxuICBQUk9YWV9TRVJWRVJfUE9SVDogei5jb2VyY2UubnVtYmVyKCkucG9zaXRpdmUoKS5vcHRpb25hbCgpLmRlZmF1bHQoODA4MClcclxufSk7XHJcblxyXG5leHBvcnQgY29uc3QgZW52cyA9IENvbmZpZy5wYXJzZShwcm9jZXNzLmVudik7XHJcbiIsImNsYXNzIEV4cG9ydEVycm9yIGV4dGVuZHMgRXJyb3Ige1xyXG4gIGNvbnN0cnVjdG9yKG1lc3NhZ2UpIHtcclxuICAgIHN1cGVyKCk7XHJcbiAgICB0aGlzLm1lc3NhZ2UgPSBtZXNzYWdlO1xyXG4gICAgdGhpcy5zdGFja01lc3NhZ2UgPSBtZXNzYWdlO1xyXG4gIH1cclxuXHJcbiAgc2V0RXJyb3IoZXJyb3IpIHtcclxuICAgIHRoaXMuZXJyb3IgPSBlcnJvcjtcclxuICAgIGlmIChlcnJvci5uYW1lKSB7XHJcbiAgICAgIHRoaXMubmFtZSA9IGVycm9yLm5hbWU7XHJcbiAgICB9XHJcbiAgICBpZiAoZXJyb3Iuc3RhdHVzQ29kZSkge1xyXG4gICAgICB0aGlzLnN0YXR1c0NvZGUgPSBlcnJvci5zdGF0dXNDb2RlO1xyXG4gICAgfVxyXG4gICAgaWYgKGVycm9yLnN0YWNrKSB7XHJcbiAgICAgIHRoaXMuc3RhY2tNZXNzYWdlID0gZXJyb3IubWVzc2FnZTtcclxuICAgICAgdGhpcy5zdGFjayA9IGVycm9yLnN0YWNrO1xyXG4gICAgfVxyXG4gICAgcmV0dXJuIHRoaXM7XHJcbiAgfVxyXG59XHJcblxyXG5leHBvcnQgZGVmYXVsdCBFeHBvcnRFcnJvcjtcclxuIiwiLyoqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKipcclxuXHJcbkhpZ2hjaGFydHMgRXhwb3J0IFNlcnZlclxyXG5cclxuQ29weXJpZ2h0IChjKSAyMDE2LTIwMjQsIEhpZ2hzb2Z0XHJcblxyXG5MaWNlbmNlZCB1bmRlciB0aGUgTUlUIGxpY2VuY2UuXHJcblxyXG5BZGRpdGlvbmFsbHkgYSB2YWxpZCBIaWdoY2hhcnRzIGxpY2Vuc2UgaXMgcmVxdWlyZWQgZm9yIHVzZS5cclxuXHJcblNlZSBMSUNFTlNFIGZpbGUgaW4gcm9vdCBmb3IgZGV0YWlscy5cclxuXHJcbioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKiovXHJcblxyXG4vLyBUaGUgY2FjaGUgbWFuYWdlciBtYW5hZ2VzIHRoZSBIaWdoY2hhcnRzIGxpYnJhcnkgYW5kIGl0cyBkZXBlbmRlbmNpZXMuXHJcbi8vIFRoZSBjYWNoZSBpdHNlbGYgaXMgc3RvcmVkIGluIC5jYWNoZSwgYW5kIGlzIGNoZWNrZWQgYnkgdGhlIGNvbmZpZyBzeXN0ZW1cclxuLy8gYmVmb3JlIHN0YXJ0aW5nIHRoZSBzZXJ2aWNlXHJcblxyXG5pbXBvcnQgeyBleGlzdHNTeW5jLCBta2RpclN5bmMsIHJlYWRGaWxlU3luYywgd3JpdGVGaWxlU3luYyB9IGZyb20gJ2ZzJztcclxuaW1wb3J0IHsgam9pbiB9IGZyb20gJ3BhdGgnO1xyXG5cclxuaW1wb3J0IHsgSHR0cHNQcm94eUFnZW50IH0gZnJvbSAnaHR0cHMtcHJveHktYWdlbnQnO1xyXG5cclxuaW1wb3J0IHsgZmV0Y2ggfSBmcm9tICcuL2ZldGNoLmpzJztcclxuaW1wb3J0IHsgbG9nIH0gZnJvbSAnLi9sb2dnZXIuanMnO1xyXG5pbXBvcnQgeyBfX2Rpcm5hbWUgfSBmcm9tICcuL3V0aWxzLmpzJztcclxuaW1wb3J0IHsgZW52cyB9IGZyb20gJy4vZW52cy5qcyc7XHJcblxyXG5pbXBvcnQgRXhwb3J0RXJyb3IgZnJvbSAnLi9lcnJvcnMvRXhwb3J0RXJyb3IuanMnO1xyXG5cclxuY29uc3QgY2FjaGUgPSB7XHJcbiAgY2RuVVJMOiAnaHR0cHM6Ly9jb2RlLmhpZ2hjaGFydHMuY29tLycsXHJcbiAgYWN0aXZlTWFuaWZlc3Q6IHt9LFxyXG4gIHNvdXJjZXM6ICcnLFxyXG4gIGhjVmVyc2lvbjogJydcclxufTtcclxuXHJcbi8vIFRPRE86IFRoZSBjb25maWcgc2hvdWxkIGJlIGFjY2Vzc3NpYmxlIGdsb2JhbGx5IHNvIHdlIGRvbid0IGhhdmUgdG8gZG8gdGhpcyBzb3J0IG9mIHRoaW5nLi5cclxubGV0IGFwcGxpZWRDb25maWcgPSBmYWxzZTtcclxuXHJcbi8qKlxyXG4gKiBFeHRyYWN0cyBhbmQgY2FjaGVzIHRoZSBIaWdoY2hhcnRzIHZlcnNpb24gZnJvbSB0aGUgc291cmNlcyBzdHJpbmcuXHJcbiAqXHJcbiAqIEByZXR1cm5zIHtzdHJpbmd9IFRoZSBleHRyYWN0ZWQgSGlnaGNoYXJ0cyB2ZXJzaW9uLlxyXG4gKi9cclxuZXhwb3J0IGNvbnN0IGV4dHJhY3RWZXJzaW9uID0gKGNhY2hlKSA9PiB7XHJcbiAgcmV0dXJuIGNhY2hlLnNvdXJjZXNcclxuICAgIC5zdWJzdHJpbmcoMCwgY2FjaGUuc291cmNlcy5pbmRleE9mKCcqLycpKVxyXG4gICAgLnJlcGxhY2UoJy8qJywgJycpXHJcbiAgICAucmVwbGFjZSgnKi8nLCAnJylcclxuICAgIC5yZXBsYWNlKC9cXG4vZywgJycpXHJcbiAgICAudHJpbSgpO1xyXG59O1xyXG5cclxuLyoqXHJcbiAqIEV4dHJhY3RzIHRoZSBIaWdoY2hhcnRzIG1vZHVsZSBuYW1lIGJhc2VkIG9uIHRoZSBzY3JpcHRQYXRoLlxyXG4gKi9cclxuZXhwb3J0IGNvbnN0IGV4dHJhY3RNb2R1bGVOYW1lID0gKHNjcmlwdFBhdGgpID0+IHtcclxuICByZXR1cm4gc2NyaXB0UGF0aC5yZXBsYWNlKFxyXG4gICAgLyguKilcXC98KC4qKW1vZHVsZXNcXC98c3RvY2tcXC8oLiopaW5kaWNhdG9yc1xcL3xtYXBzXFwvKC4qKW1vZHVsZXNcXC8vZ2ksXHJcbiAgICAnJ1xyXG4gICk7XHJcbn07XHJcblxyXG4vKipcclxuICogU2F2ZXMgdGhlIHByb3ZpZGVkIGNvbmZpZ3VyYXRpb24gYW5kIGZldGNoZWQgbW9kdWxlcyB0byB0aGUgY2FjaGUgbWFuaWZlc3RcclxuICogZmlsZS5cclxuICpcclxuICogQHBhcmFtIHtvYmplY3R9IGNvbmZpZyAtIEhpZ2hjaGFydHMtcmVsYXRlZCBjb25maWd1cmF0aW9uIG9iamVjdC5cclxuICogQHBhcmFtIHtvYmplY3R9IGZldGNoZWRNb2R1bGVzIC0gQW4gb2JqZWN0IHRoYXQgY29udGFpbnMgbWFwcGVkIG5hbWVzIG9mXHJcbiAqIGZldGNoZWQgSGlnaGNoYXJ0cyBtb2R1bGVzIHRvIHVzZS5cclxuICpcclxuICogQHRocm93cyB7RXhwb3J0RXJyb3J9IFRocm93cyBhbiBFeHBvcnRFcnJvciBpZiBhbiBlcnJvciBvY2N1cnMgd2hpbGUgd3JpdGluZ1xyXG4gKiB0aGUgY2FjaGUgbWFuaWZlc3QuXHJcbiAqL1xyXG5leHBvcnQgY29uc3Qgc2F2ZUNvbmZpZ1RvTWFuaWZlc3QgPSBhc3luYyAoY29uZmlnLCBmZXRjaGVkTW9kdWxlcykgPT4ge1xyXG4gIGNvbnN0IG5ld01hbmlmZXN0ID0ge1xyXG4gICAgdmVyc2lvbjogY29uZmlnLnZlcnNpb24sXHJcbiAgICBtb2R1bGVzOiBmZXRjaGVkTW9kdWxlcyB8fCB7fVxyXG4gIH07XHJcblxyXG4gIC8vIFVwZGF0ZSBjYWNoZSBvYmplY3Qgd2l0aCB0aGUgY3VycmVudCBtb2R1bGVzXHJcbiAgY2FjaGUuYWN0aXZlTWFuaWZlc3QgPSBuZXdNYW5pZmVzdDtcclxuXHJcbiAgbG9nKDMsICdbY2FjaGVdIFdyaXRpbmcgYSBuZXcgbWFuaWZlc3QuJyk7XHJcbiAgdHJ5IHtcclxuICAgIHdyaXRlRmlsZVN5bmMoXHJcbiAgICAgIGpvaW4oX19kaXJuYW1lLCBjb25maWcuY2FjaGVQYXRoLCAnbWFuaWZlc3QuanNvbicpLFxyXG4gICAgICBKU09OLnN0cmluZ2lmeShuZXdNYW5pZmVzdCksXHJcbiAgICAgICd1dGY4J1xyXG4gICAgKTtcclxuICB9IGNhdGNoIChlcnJvcikge1xyXG4gICAgdGhyb3cgbmV3IEV4cG9ydEVycm9yKCdbY2FjaGVdIEVycm9yIHdyaXRpbmcgdGhlIGNhY2hlIG1hbmlmZXN0LicpLnNldEVycm9yKFxyXG4gICAgICBlcnJvclxyXG4gICAgKTtcclxuICB9XHJcbn07XHJcblxyXG4vKipcclxuICogRmV0Y2hlcyBhIHNpbmdsZSBzY3JpcHQgYW5kIHVwZGF0ZXMgdGhlIGZldGNoZWRNb2R1bGVzIGFjY29yZGluZ2x5LlxyXG4gKlxyXG4gKiBAcGFyYW0ge3N0cmluZ30gc2NyaXB0IC0gQSBwYXRoIHRvIHNjcmlwdCB0byBnZXQuXHJcbiAqIEBwYXJhbSB7T2JqZWN0fSBwcm94eUFnZW50IC0gVGhlIHByb3h5IGFnZW50IHRvIHVzZSBmb3IgYSByZXF1ZXN0LlxyXG4gKiBAcGFyYW0ge09iamVjdH0gZmV0Y2hlZE1vZHVsZXMgLSBBbiBvYmplY3Qgd2hpY2ggdHJhY2tzIHdoaWNoIEhpZ2hjaGFydHMgbW9kdWxlcyBoYXZlIGJlZW4gZmV0Y2hlZC5cclxuICogQHBhcmFtIHtib29sZWFufSBzaG91bGRUaHJvd0Vycm9yIC0gQSBmbGFnIHRvIGluZGljYXRlIGlmIHRoZSBlcnJvciBzaG91bGQgYmUgdGhyb3duLiBUaGlzIHNob3VsZCBiZSB1c2VkIG9ubHkgZm9yIHRoZSBjb3JlIHNjcmlwdHMuXHJcbiAqXHJcbiAqIEByZXR1cm5zIHtQcm9taXNlPHN0cmluZz59IEEgUHJvbWlzZSByZXNvbHZpbmcgdG8gdGhlIHRleHQgcmVwcmVzZW50YXRpb25cclxuICogb2YgdGhlIGZldGNoZWQgc2NyaXB0LlxyXG4gKlxyXG4gKiBAdGhyb3dzIHtFeHBvcnRFcnJvcn0gVGhyb3dzIGFuIEV4cG9ydEVycm9yIGlmIHRoZXJlIGlzIGEgcHJvYmxlbSB3aXRoXHJcbiAqIGZldGNoaW5nIHRoZSBzY3JpcHQuXHJcbiAqL1xyXG5leHBvcnQgY29uc3QgZmV0Y2hBbmRQcm9jZXNzU2NyaXB0ID0gYXN5bmMgKFxyXG4gIHNjcmlwdCxcclxuICBwcm94eUFnZW50LFxyXG4gIGZldGNoZWRNb2R1bGVzLFxyXG4gIHNob3VsZFRocm93RXJyb3IgPSBmYWxzZVxyXG4pID0+IHtcclxuICAvLyBHZXQgcmlkIG9mIHRoZSAuanMgZnJvbSB0aGUgY3VzdG9tIHN0cmluZ3NcclxuICBpZiAoc2NyaXB0LmVuZHNXaXRoKCcuanMnKSkge1xyXG4gICAgc2NyaXB0ID0gc2NyaXB0LnN1YnN0cmluZygwLCBzY3JpcHQubGVuZ3RoIC0gMyk7XHJcbiAgfVxyXG5cclxuICBsb2coNCwgYFtjYWNoZV0gRmV0Y2hpbmcgc2NyaXB0IC0gJHtzY3JpcHR9LmpzYCk7XHJcblxyXG4gIC8vIElmIGV4aXN0cywgYWRkIHByb3h5IGFnZW50IHRvIHJlcXVlc3Qgb3B0aW9uc1xyXG4gIGNvbnN0IHJlcXVlc3RPcHRpb25zID0gcHJveHlBZ2VudFxyXG4gICAgPyB7XHJcbiAgICAgICAgYWdlbnQ6IHByb3h5QWdlbnQsXHJcbiAgICAgICAgdGltZW91dDogZW52cy5QUk9YWV9TRVJWRVJfVElNRU9VVFxyXG4gICAgICB9XHJcbiAgICA6IHt9O1xyXG5cclxuICAvLyBGZXRjaCB0aGUgc2NyaXB0XHJcbiAgY29uc3QgcmVzcG9uc2UgPSBhd2FpdCBmZXRjaChgJHtzY3JpcHR9LmpzYCwgcmVxdWVzdE9wdGlvbnMpO1xyXG5cclxuICAvLyBJZiBPSywgcmV0dXJuIGl0cyB0ZXh0IHJlcHJlc2VudGF0aW9uXHJcbiAgaWYgKHJlc3BvbnNlLnN0YXR1c0NvZGUgPT09IDIwMCAmJiB0eXBlb2YgcmVzcG9uc2UudGV4dCA9PSAnc3RyaW5nJykge1xyXG4gICAgaWYgKGZldGNoZWRNb2R1bGVzKSB7XHJcbiAgICAgIGNvbnN0IG1vZHVsZU5hbWUgPSBleHRyYWN0TW9kdWxlTmFtZShzY3JpcHQpO1xyXG4gICAgICBmZXRjaGVkTW9kdWxlc1ttb2R1bGVOYW1lXSA9IDE7XHJcbiAgICB9XHJcblxyXG4gICAgcmV0dXJuIHJlc3BvbnNlLnRleHQ7XHJcbiAgfVxyXG5cclxuICBpZiAoc2hvdWxkVGhyb3dFcnJvcikge1xyXG4gICAgdGhyb3cgbmV3IEV4cG9ydEVycm9yKFxyXG4gICAgICBgQ291bGQgbm90IGZldGNoIHRoZSAke3NjcmlwdH0uanMuIFRoZSBzY3JpcHQgbWlnaHQgbm90IGV4aXN0IGluIHRoZSByZXF1ZXN0ZWQgdmVyc2lvbiAoc3RhdHVzIGNvZGU6ICR7cmVzcG9uc2Uuc3RhdHVzQ29kZX0pLmBcclxuICAgICkuc2V0RXJyb3IocmVzcG9uc2UpO1xyXG4gIH0gZWxzZSB7XHJcbiAgICBsb2coXHJcbiAgICAgIDIsXHJcbiAgICAgIGBbY2FjaGVdIENvdWxkIG5vdCBmZXRjaCB0aGUgJHtzY3JpcHR9LmpzLiBUaGUgc2NyaXB0IG1pZ2h0IG5vdCBleGlzdCBpbiB0aGUgcmVxdWVzdGVkIHZlcnNpb24uYFxyXG4gICAgKTtcclxuICB9XHJcblxyXG4gIHJldHVybiAnJztcclxufTtcclxuXHJcbi8qKlxyXG4gKiBGZXRjaGVzIEhpZ2hjaGFydHMgc2NyaXB0cyBhbmQgY3VzdG9tU2NyaXB0cyBmcm9tIHRoZSBnaXZlbiBDRE5zLlxyXG4gKlxyXG4gKiBAcGFyYW0ge3N0cmluZ30gc2NyaXB0cyAtIEFycmF5IG9mIEhpZ2hjaGFydHMgbW9kdWxlcyB0byBmZXRjaC5cclxuICogQHBhcmFtIHtzdHJpbmd9IGN1c3RvbVNjcmlwdHMgLSBBcnJheSBvZiBjdXN0b20gc2NyaXB0IHBhdGhzIHRvIGZldGNoIChmdWxsIFVSTHMpLlxyXG4gKiBAcGFyYW0ge29iamVjdH0gcHJveHlBZ2VudCAtIFRoZSBwcm94eSBhZ2VudCB0byB1c2UgZm9yIGEgcmVxdWVzdC5cclxuICogQHBhcmFtIHtvYmplY3R9IGZldGNoZWRNb2R1bGVzIC0gQW4gb2JqZWN0IHdoaWNoIHRyYWNrcyB3aGljaCBIaWdoY2hhcnRzIG1vZHVsZXMgaGF2ZSBiZWVuIGZldGNoZWQuXHJcbiAqIEByZXR1cm5zIHtQcm9taXNlPHN0cmluZz59IFRoZSBmZXRjaGVkIHNjcmlwdHMgY29udGVudCBqb2luZWQuXHJcbiAqL1xyXG5leHBvcnQgY29uc3QgZmV0Y2hTY3JpcHRzID0gYXN5bmMgKFxyXG4gIGNvcmVTY3JpcHRzLFxyXG4gIG1vZHVsZVNjcmlwdHMsXHJcbiAgY3VzdG9tU2NyaXB0cyxcclxuICBjZG5VUkwsXHJcbiAgcHJveHlBZ2VudCxcclxuICBmZXRjaGVkTW9kdWxlc1xyXG4pID0+IHtcclxuICBjb25zdCBhbGxGZXRjaFByb21pc2VzID0gW1xyXG4gICAgLi4uY29yZVNjcmlwdHMubWFwKChzY3JpcHQpID0+XHJcbiAgICAgIGZldGNoQW5kUHJvY2Vzc1NjcmlwdChcclxuICAgICAgICBgJHtjZG5VUkx9JHtzY3JpcHR9YCxcclxuICAgICAgICBwcm94eUFnZW50LFxyXG4gICAgICAgIGZldGNoZWRNb2R1bGVzLFxyXG4gICAgICAgIHRydWVcclxuICAgICAgKVxyXG4gICAgKSxcclxuICAgIC4uLm1vZHVsZVNjcmlwdHMubWFwKChzY3JpcHQpID0+XHJcbiAgICAgIGZldGNoQW5kUHJvY2Vzc1NjcmlwdChgJHtjZG5VUkx9JHtzY3JpcHR9YCwgcHJveHlBZ2VudCwgZmV0Y2hlZE1vZHVsZXMpXHJcbiAgICApLFxyXG4gICAgLi4uY3VzdG9tU2NyaXB0cy5tYXAoKHNjcmlwdCkgPT5cclxuICAgICAgZmV0Y2hBbmRQcm9jZXNzU2NyaXB0KGAke3NjcmlwdH1gLCBwcm94eUFnZW50KVxyXG4gICAgKVxyXG4gIF07XHJcblxyXG4gIGNvbnN0IGZldGNoZWRTY3JpcHRzID0gYXdhaXQgUHJvbWlzZS5hbGwoYWxsRmV0Y2hQcm9taXNlcyk7XHJcbiAgcmV0dXJuIGZldGNoZWRTY3JpcHRzLmpvaW4oJztcXG4nKTtcclxufTtcclxuXHJcbi8qKlxyXG4gKiBVcGRhdGVzIHRoZSBsb2NhbCBjYWNoZSB3aXRoIEhpZ2hjaGFydHMgc2NyaXB0cyBhbmQgdGhlaXIgdmVyc2lvbnMuXHJcbiAqXHJcbiAqIEBwYXJhbSB7T2JqZWN0fSBjb25maWcgLSBUaGUgY29uZmlndXJhdGlvbiBvYmplY3QgY29udGFpbmluZyBpbmZvcm1hdGlvblxyXG4gKiBhYm91dCBzY3JpcHRzIGFuZCBtb2R1bGVzLlxyXG4gKiBAcGFyYW0ge3N0cmluZ30gc291cmNlUGF0aCAtIFRoZSBwYXRoIHRvIHRoZSBzb3VyY2UgZmlsZSBpbiB0aGUgY2FjaGUuXHJcbiAqXHJcbiAqIEByZXR1cm5zIHtQcm9taXNlPG9iamVjdD59IEEgUHJvbWlzZSByZXNvbHZpbmcgdG8gYW4gb2JqZWN0IHJlcHJlc2VudGluZ1xyXG4gKiB0aGUgZmV0Y2hlZCBtb2R1bGVzLlxyXG4gKlxyXG4gKiBAdGhyb3dzIHtFeHBvcnRFcnJvcn0gVGhyb3dzIGFuIEV4cG9ydEVycm9yIGlmIHRoZXJlIGlzIGFuIGlzc3VlIHVwZGF0aW5nXHJcbiAqIHRoZSBsb2NhbCBIaWdoY2hhcnRzIGNhY2hlLlxyXG4gKi9cclxuZXhwb3J0IGNvbnN0IHVwZGF0ZUNhY2hlID0gYXN5bmMgKGNvbmZpZywgc291cmNlUGF0aCkgPT4ge1xyXG4gIGNvbnN0IHsgY29yZVNjcmlwdHMsIG1vZHVsZXMsIGluZGljYXRvcnMsIHNjcmlwdHM6IGN1c3RvbVNjcmlwdHMgfSA9IGNvbmZpZztcclxuICBjb25zdCBoY1ZlcnNpb24gPVxyXG4gICAgY29uZmlnLnZlcnNpb24gPT09ICdsYXRlc3QnIHx8ICFjb25maWcudmVyc2lvbiA/ICcnIDogYCR7Y29uZmlnLnZlcnNpb259L2A7XHJcblxyXG4gIGxvZyhcclxuICAgIDMsXHJcbiAgICBgW2NhY2hlXSBVcGRhdGluZyBjYWNoZSB2ZXJzaW9uIHRvIEhpZ2hjaGFydHM6ICR7aGNWZXJzaW9uIHx8ICdsYXRlc3QnfS5gXHJcbiAgKTtcclxuXHJcbiAgLy8gQ29uZmlndXJlIHByb3h5IGlmIGV4aXN0c1xyXG4gIGxldCBwcm94eUFnZW50O1xyXG4gIGNvbnN0IHByb3h5SG9zdCA9IHByb2Nlc3MuZW52WydQUk9YWV9TRVJWRVJfSE9TVCddO1xyXG4gIGNvbnN0IHByb3h5UG9ydCA9IHByb2Nlc3MuZW52WydQUk9YWV9TRVJWRVJfUE9SVCddO1xyXG5cclxuICAvLyBUcnkgdG8gY3JlYXRlIGEgUHJveHkgQWdlbnRcclxuICBpZiAocHJveHlIb3N0ICYmIHByb3h5UG9ydCkge1xyXG4gICAgdHJ5IHtcclxuICAgICAgcHJveHlBZ2VudCA9IG5ldyBIdHRwc1Byb3h5QWdlbnQoe1xyXG4gICAgICAgIGhvc3Q6IHByb3h5SG9zdCxcclxuICAgICAgICBwb3J0OiArcHJveHlQb3J0XHJcbiAgICAgIH0pO1xyXG4gICAgfSBjYXRjaCAoZXJyb3IpIHtcclxuICAgICAgdGhyb3cgbmV3IEV4cG9ydEVycm9yKCdbY2FjaGVdIENvdWxkIG5vdCBjcmVhdGUgYSBQcm94eSBBZ2VudC4nKS5zZXRFcnJvcihcclxuICAgICAgICBlcnJvclxyXG4gICAgICApO1xyXG4gICAgfVxyXG4gIH1cclxuXHJcbiAgY29uc3QgZmV0Y2hlZE1vZHVsZXMgPSB7fTtcclxuICB0cnkge1xyXG4gICAgY2FjaGUuc291cmNlcyA9IGF3YWl0IGZldGNoU2NyaXB0cyhcclxuICAgICAgWy4uLmNvcmVTY3JpcHRzLm1hcCgoYykgPT4gYCR7aGNWZXJzaW9ufSR7Y31gKV0sXHJcbiAgICAgIFtcclxuICAgICAgICAuLi5tb2R1bGVzLm1hcCgobSkgPT5cclxuICAgICAgICAgIG0gPT09ICdtYXAnXHJcbiAgICAgICAgICAgID8gYG1hcHMvJHtoY1ZlcnNpb259bW9kdWxlcy8ke219YFxyXG4gICAgICAgICAgICA6IGAke2hjVmVyc2lvbn1tb2R1bGVzLyR7bX1gXHJcbiAgICAgICAgKSxcclxuICAgICAgICAuLi5pbmRpY2F0b3JzLm1hcCgoaSkgPT4gYHN0b2NrLyR7aGNWZXJzaW9ufWluZGljYXRvcnMvJHtpfWApXHJcbiAgICAgIF0sXHJcbiAgICAgIGN1c3RvbVNjcmlwdHMsXHJcbiAgICAgIGNvbmZpZy5jZG5VUkwgfHwgY2FjaGUuY2RuVVJMLFxyXG4gICAgICBwcm94eUFnZW50LFxyXG4gICAgICBmZXRjaGVkTW9kdWxlc1xyXG4gICAgKTtcclxuXHJcbiAgICBjYWNoZS5oY1ZlcnNpb24gPSBleHRyYWN0VmVyc2lvbihjYWNoZSk7XHJcblxyXG4gICAgLy8gU2F2ZSB0aGUgZmV0Y2hlZCBtb2R1bGVzIGludG8gY2FjaGVzJyBzb3VyY2UgSlNPTlxyXG4gICAgd3JpdGVGaWxlU3luYyhzb3VyY2VQYXRoLCBjYWNoZS5zb3VyY2VzKTtcclxuICAgIHJldHVybiBmZXRjaGVkTW9kdWxlcztcclxuICB9IGNhdGNoIChlcnJvcikge1xyXG4gICAgdGhyb3cgbmV3IEV4cG9ydEVycm9yKFxyXG4gICAgICAnW2NhY2hlXSBVbmFibGUgdG8gdXBkYXRlIHRoZSBsb2NhbCBIaWdoY2hhcnRzIGNhY2hlLidcclxuICAgICkuc2V0RXJyb3IoZXJyb3IpO1xyXG4gIH1cclxufTtcclxuXHJcbi8qKlxyXG4gKiBVcGRhdGVzIHRoZSBIaWdoY2hhcnRzIHZlcnNpb24gaW4gdGhlIGFwcGxpZWQgY29uZmlndXJhdGlvbiBhbmQgY2hlY2tzXHJcbiAqIHRoZSBjYWNoZSBmb3IgdGhlIG5ldyB2ZXJzaW9uLlxyXG4gKlxyXG4gKiBAcGFyYW0ge3N0cmluZ30gbmV3VmVyc2lvbiAtIFRoZSBuZXcgSGlnaGNoYXJ0cyB2ZXJzaW9uIHRvIGJlIGFwcGxpZWQuXHJcbiAqXHJcbiAqIEByZXR1cm5zIHtQcm9taXNlPChvYmplY3R8Ym9vbGVhbik+fSBBIFByb21pc2UgcmVzb2x2aW5nIHRvIHRoZSB1cGRhdGVkXHJcbiAqIGNvbmZpZ3VyYXRpb24gd2l0aCB0aGUgbmV3IHZlcnNpb24sIG9yIGZhbHNlIGlmIG5vIGFwcGxpZWQgY29uZmlndXJhdGlvblxyXG4gKiBleGlzdHMuXHJcbiAqL1xyXG5leHBvcnQgY29uc3QgdXBkYXRlVmVyc2lvbiA9IGFzeW5jIChuZXdWZXJzaW9uKSA9PlxyXG4gIGFwcGxpZWRDb25maWdcclxuICAgID8gYXdhaXQgY2hlY2tBbmRVcGRhdGVDYWNoZShcclxuICAgICAgICBPYmplY3QuYXNzaWduKGFwcGxpZWRDb25maWcsIHtcclxuICAgICAgICAgIHZlcnNpb246IG5ld1ZlcnNpb25cclxuICAgICAgICB9KVxyXG4gICAgICApXHJcbiAgICA6IGZhbHNlO1xyXG5cclxuLyoqXHJcbiAqIENoZWNrcyB0aGUgY2FjaGUgZm9yIEhpZ2hjaGFydHMgZGVwZW5kZW5jaWVzLCB1cGRhdGVzIHRoZSBjYWNoZSBpZiBuZWVkZWQsXHJcbiAqIGFuZCBsb2FkcyB0aGUgc291cmNlcy5cclxuICpcclxuICogQHBhcmFtIHtPYmplY3R9IGNvbmZpZyAtIEhpZ2hjaGFydHMtcmVsYXRlZCBjb25maWd1cmF0aW9uIG9iamVjdCBjb250YWluaW5nIGluZm9ybWF0aW9uXHJcbiAqIGFib3V0IHNjcmlwdHMgYW5kIG1vZHVsZXMuXHJcbiAqXHJcbiAqIEByZXR1cm5zIHtQcm9taXNlPHZvaWQ+fSBBIFByb21pc2UgdGhhdCByZXNvbHZlcyBvbmNlIHRoZSBjYWNoZSBpcyBjaGVja2VkXHJcbiAqIGFuZCB1cGRhdGVkLlxyXG4gKlxyXG4gKiBAdGhyb3dzIHtFeHBvcnRFcnJvcn0gVGhyb3dzIGFuIEV4cG9ydEVycm9yIGlmIHRoZXJlIGlzIGFuIGlzc3VlIHVwZGF0aW5nXHJcbiAqIG9yIHJlYWRpbmcgdGhlIGNhY2hlLlxyXG4gKi9cclxuZXhwb3J0IGNvbnN0IGNoZWNrQW5kVXBkYXRlQ2FjaGUgPSBhc3luYyAoY29uZmlnKSA9PiB7XHJcbiAgY29uc3QgY2FjaGVQYXRoID0gam9pbihfX2Rpcm5hbWUsIGNvbmZpZy5jYWNoZVBhdGgpO1xyXG5cclxuICBsZXQgZmV0Y2hlZE1vZHVsZXM7XHJcbiAgLy8gUHJlcGFyZSBwYXRocyB0byBtYW5pZmVzdCBhbmQgc291cmNlcyBmcm9tIHRoZSAuY2FjaGUgZm9sZGVyXHJcbiAgY29uc3QgbWFuaWZlc3RQYXRoID0gam9pbihjYWNoZVBhdGgsICdtYW5pZmVzdC5qc29uJyk7XHJcbiAgY29uc3Qgc291cmNlUGF0aCA9IGpvaW4oY2FjaGVQYXRoLCAnc291cmNlcy5qcycpO1xyXG5cclxuICAvLyBUT0RPOiBkZWFsIHdpdGggdHJ5aW5nIHRvIHN3aXRjaCB0byB0aGUgcnVubmluZyB2ZXJzaW9uXHJcbiAgLy8gY29uc3QgYWN0aXZlVmVyc2lvbiA9IGFwcGxpZWRDb25maWcgPyBhcHBsaWVkQ29uZmlnLnZlcnNpb24gOiBmYWxzZTtcclxuXHJcbiAgYXBwbGllZENvbmZpZyA9IGNvbmZpZztcclxuXHJcbiAgLy8gQ3JlYXRlIHRoZSBjYWNoZSBkZXN0aW5hdGlvbiBpZiBpdCBkb2Vzbid0IGV4aXN0IGFscmVhZHlcclxuICAhZXhpc3RzU3luYyhjYWNoZVBhdGgpICYmIG1rZGlyU3luYyhjYWNoZVBhdGgpO1xyXG5cclxuICAvLyBGZXRjaCBhbGwgdGhlIHNjcmlwdHMgZWl0aGVyIGlmIG1hbmlmZXN0Lmpzb24gZG9lcyBub3QgZXhpc3RcclxuICAvLyBvciBpZiB0aGUgZm9yY2VGZXRjaCBvcHRpb24gaXMgZW5hYmxlZFxyXG4gIGlmICghZXhpc3RzU3luYyhtYW5pZmVzdFBhdGgpIHx8IGNvbmZpZy5mb3JjZUZldGNoKSB7XHJcbiAgICBsb2coMywgJ1tjYWNoZV0gRmV0Y2hpbmcgYW5kIGNhY2hpbmcgSGlnaGNoYXJ0cyBkZXBlbmRlbmNpZXMuJyk7XHJcbiAgICBmZXRjaGVkTW9kdWxlcyA9IGF3YWl0IHVwZGF0ZUNhY2hlKGNvbmZpZywgc291cmNlUGF0aCk7XHJcbiAgfSBlbHNlIHtcclxuICAgIGxldCByZXF1ZXN0VXBkYXRlID0gZmFsc2U7XHJcblxyXG4gICAgLy8gUmVhZCB0aGUgbWFuaWZlc3QgSlNPTlxyXG4gICAgY29uc3QgbWFuaWZlc3QgPSBKU09OLnBhcnNlKHJlYWRGaWxlU3luYyhtYW5pZmVzdFBhdGgpKTtcclxuXHJcbiAgICAvLyBDaGVjayBpZiB0aGUgbW9kdWxlcyBpcyBhbiBhcnJheSwgaWYgc28sIHdlIHJld3JpdGUgaXQgdG8gYSBtYXAgdG8gbWFrZVxyXG4gICAgLy8gaXQgZWFzaWVyIHRvIHJlc29sdmUgbW9kdWxlcy5cclxuICAgIGlmIChtYW5pZmVzdC5tb2R1bGVzICYmIEFycmF5LmlzQXJyYXkobWFuaWZlc3QubW9kdWxlcykpIHtcclxuICAgICAgY29uc3QgbW9kdWxlTWFwID0ge307XHJcbiAgICAgIG1hbmlmZXN0Lm1vZHVsZXMuZm9yRWFjaCgobSkgPT4gKG1vZHVsZU1hcFttXSA9IDEpKTtcclxuICAgICAgbWFuaWZlc3QubW9kdWxlcyA9IG1vZHVsZU1hcDtcclxuICAgIH1cclxuXHJcbiAgICBjb25zdCB7IG1vZHVsZXMsIGNvcmVTY3JpcHRzLCBpbmRpY2F0b3JzIH0gPSBjb25maWc7XHJcbiAgICBjb25zdCBudW1iZXJPZk1vZHVsZXMgPVxyXG4gICAgICBtb2R1bGVzLmxlbmd0aCArIGNvcmVTY3JpcHRzLmxlbmd0aCArIGluZGljYXRvcnMubGVuZ3RoO1xyXG5cclxuICAgIC8vIENvbXBhcmUgdGhlIGxvYWRlZCBjb25maWcgd2l0aCB0aGUgY29udGVudHMgaW4gY2FjaGUuXHJcbiAgICAvLyBJZiB0aGVyZSBhcmUgY2hhbmdlcywgZmV0Y2ggcmVxdWVzdGVkIG1vZHVsZXMgYW5kIHByb2R1Y3RzLFxyXG4gICAgLy8gYW5kIGJha2UgdGhlbSBpbnRvIGEgZ2lhbnQgYmxvYi4gU2F2ZSB0aGUgYmxvYi5cclxuICAgIGlmIChtYW5pZmVzdC52ZXJzaW9uICE9PSBjb25maWcudmVyc2lvbikge1xyXG4gICAgICBsb2coXHJcbiAgICAgICAgMixcclxuICAgICAgICAnW2NhY2hlXSBBIEhpZ2hjaGFydHMgdmVyc2lvbiBtaXNtYXRjaCBpbiB0aGUgY2FjaGUsIG5lZWQgdG8gcmUtZmV0Y2guJ1xyXG4gICAgICApO1xyXG4gICAgICByZXF1ZXN0VXBkYXRlID0gdHJ1ZTtcclxuICAgIH0gZWxzZSBpZiAoT2JqZWN0LmtleXMobWFuaWZlc3QubW9kdWxlcyB8fCB7fSkubGVuZ3RoICE9PSBudW1iZXJPZk1vZHVsZXMpIHtcclxuICAgICAgbG9nKFxyXG4gICAgICAgIDIsXHJcbiAgICAgICAgJ1tjYWNoZV0gVGhlIGNhY2hlIGFuZCB0aGUgcmVxdWVzdGVkIG1vZHVsZXMgZG8gbm90IG1hdGNoLCBuZWVkIHRvIHJlLWZldGNoLidcclxuICAgICAgKTtcclxuICAgICAgcmVxdWVzdFVwZGF0ZSA9IHRydWU7XHJcbiAgICB9IGVsc2Uge1xyXG4gICAgICAvLyBDaGVjayBlYWNoIG1vZHVsZSwgaWYgYW55dGhpbmcgaXMgbWlzc2luZyByZWZldGNoIGV2ZXJ5dGhpbmdcclxuICAgICAgcmVxdWVzdFVwZGF0ZSA9IChjb25maWcubW9kdWxlcyB8fCBbXSkuc29tZSgobW9kdWxlTmFtZSkgPT4ge1xyXG4gICAgICAgIGlmICghbWFuaWZlc3QubW9kdWxlc1ttb2R1bGVOYW1lXSkge1xyXG4gICAgICAgICAgbG9nKFxyXG4gICAgICAgICAgICAyLFxyXG4gICAgICAgICAgICBgW2NhY2hlXSBUaGUgJHttb2R1bGVOYW1lfSBpcyBtaXNzaW5nIGluIHRoZSBjYWNoZSwgbmVlZCB0byByZS1mZXRjaC5gXHJcbiAgICAgICAgICApO1xyXG4gICAgICAgICAgcmV0dXJuIHRydWU7XHJcbiAgICAgICAgfVxyXG4gICAgICB9KTtcclxuICAgIH1cclxuXHJcbiAgICBpZiAocmVxdWVzdFVwZGF0ZSkge1xyXG4gICAgICBmZXRjaGVkTW9kdWxlcyA9IGF3YWl0IHVwZGF0ZUNhY2hlKGNvbmZpZywgc291cmNlUGF0aCk7XHJcbiAgICB9IGVsc2Uge1xyXG4gICAgICBsb2coMywgJ1tjYWNoZV0gRGVwZW5kZW5jeSBjYWNoZSBpcyB1cCB0byBkYXRlLCBwcm9jZWVkaW5nLicpO1xyXG5cclxuICAgICAgLy8gTG9hZCB0aGUgc291cmNlc1xyXG4gICAgICBjYWNoZS5zb3VyY2VzID0gcmVhZEZpbGVTeW5jKHNvdXJjZVBhdGgsICd1dGY4Jyk7XHJcblxyXG4gICAgICAvLyBHZXQgY3VycmVudCBtb2R1bGVzIG1hcFxyXG4gICAgICBmZXRjaGVkTW9kdWxlcyA9IG1hbmlmZXN0Lm1vZHVsZXM7XHJcblxyXG4gICAgICBjYWNoZS5oY1ZlcnNpb24gPSBleHRyYWN0VmVyc2lvbihjYWNoZSk7XHJcbiAgICB9XHJcbiAgfVxyXG5cclxuICAvLyBGaW5hbGx5LCBzYXZlIHRoZSBuZXcgbWFuaWZlc3QsIHdoaWNoIGlzIGJhc2ljYWxseSBvdXIgY3VycmVudCBjb25maWdcclxuICAvLyBpbiBhIHNsaWdodGx5IGRpZmZlcmVudCBmb3JtYXRcclxuICBhd2FpdCBzYXZlQ29uZmlnVG9NYW5pZmVzdChjb25maWcsIGZldGNoZWRNb2R1bGVzKTtcclxufTtcclxuXHJcbmV4cG9ydCBjb25zdCBnZXRDYWNoZVBhdGggPSAoKSA9PiB7XHJcbiAgcmV0dXJuIGpvaW4oX19kaXJuYW1lLCBhcHBsaWVkQ29uZmlnLmNhY2hlUGF0aCk7XHJcbn07XHJcblxyXG5leHBvcnQgZGVmYXVsdCB7XHJcbiAgY2hlY2tBbmRVcGRhdGVDYWNoZSxcclxuICBnZXRDYWNoZVBhdGgsXHJcbiAgdXBkYXRlVmVyc2lvbixcclxuICBnZXRDYWNoZTogKCkgPT4gY2FjaGUsXHJcbiAgaGlnaGNoYXJ0czogKCkgPT4gY2FjaGUuc291cmNlcyxcclxuICB2ZXJzaW9uOiAoKSA9PiBjYWNoZS5oY1ZlcnNpb25cclxufTtcclxuIiwiLyoqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKipcclxuXHJcbkhpZ2hjaGFydHMgRXhwb3J0IFNlcnZlclxyXG5cclxuQ29weXJpZ2h0IChjKSAyMDE2LTIwMjQsIEhpZ2hzb2Z0XHJcblxyXG5MaWNlbmNlZCB1bmRlciB0aGUgTUlUIGxpY2VuY2UuXHJcblxyXG5BZGRpdGlvbmFsbHkgYSB2YWxpZCBIaWdoY2hhcnRzIGxpY2Vuc2UgaXMgcmVxdWlyZWQgZm9yIHVzZS5cclxuXHJcblNlZSBMSUNFTlNFIGZpbGUgaW4gcm9vdCBmb3IgZGV0YWlscy5cclxuXHJcbioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKiovXHJcblxyXG5pbXBvcnQgeyBleGlzdHNTeW5jLCByZWFkRmlsZVN5bmMsIHByb21pc2VzIGFzIGZzUHJvbWlzZXMgfSBmcm9tICdmcyc7XHJcblxyXG5pbXBvcnQgcHJvbXB0cyBmcm9tICdwcm9tcHRzJztcclxuXHJcbmltcG9ydCB7XHJcbiAgYWJzb2x1dGVQcm9wcyxcclxuICBkZWZhdWx0Q29uZmlnLFxyXG4gIG5lc3RlZEFyZ3MsXHJcbiAgcHJvbXB0c0NvbmZpZ1xyXG59IGZyb20gJy4vc2NoZW1hcy9jb25maWcuanMnO1xyXG5pbXBvcnQgeyBsb2csIGxvZ1dpdGhTdGFjayB9IGZyb20gJy4vbG9nZ2VyLmpzJztcclxuaW1wb3J0IHsgZGVlcENvcHksIGlzT2JqZWN0LCBwcmludFVzYWdlLCB0b0Jvb2xlYW4gfSBmcm9tICcuL3V0aWxzLmpzJztcclxuaW1wb3J0IHsgZW52cyB9IGZyb20gJy4vZW52cy5qcyc7XHJcblxyXG5sZXQgZ2VuZXJhbE9wdGlvbnMgPSB7fTtcclxuXHJcbi8qKlxyXG4gKiBSZXRyaWV2ZXMgYW5kIHJldHVybnMgdGhlIGdlbmVyYWwgb3B0aW9ucyBmb3IgdGhlIGV4cG9ydCBwcm9jZXNzLlxyXG4gKlxyXG4gKiBAcmV0dXJucyB7T2JqZWN0fSBUaGUgZ2VuZXJhbCBvcHRpb25zIG9iamVjdC5cclxuICovXHJcbmV4cG9ydCBjb25zdCBnZXRPcHRpb25zID0gKCkgPT4gZ2VuZXJhbE9wdGlvbnM7XHJcblxyXG4vKipcclxuICogSW5pdGlhbGl6ZXMgYW5kIHNldHMgdGhlIGdlbmVyYWwgb3B0aW9ucyBmb3IgdGhlIHNlcnZlciBpbnN0YWNlLCBrZWVwaW5nXHJcbiAqIHRoZSBwcmluY2lwbGUgb2YgdGhlIG9wdGlvbnMgbG9hZCBwcmlvcml0eS4gSXQgYWNjZXB0cyBvcHRpb25hbCB1c2VyT3B0aW9uc1xyXG4gKiBhbmQgYXJncyBmcm9tIHRoZSBDTEkuXHJcbiAqXHJcbiAqIEBwYXJhbSB7T2JqZWN0fSB1c2VyT3B0aW9ucyAtIFVzZXItcHJvdmlkZWQgb3B0aW9ucyBmb3IgY3VzdG9taXphdGlvbi5cclxuICogQHBhcmFtIHtBcnJheX0gYXJncyAtIENvbW1hbmQtbGluZSBhcmd1bWVudHMgZm9yIGFkZGl0aW9uYWwgY29uZmlndXJhdGlvblxyXG4gKiAoQ0xJIHVzYWdlKS5cclxuICpcclxuICogQHJldHVybnMge09iamVjdH0gVGhlIHVwZGF0ZWQgZ2VuZXJhbCBvcHRpb25zIG9iamVjdC5cclxuICovXHJcbmV4cG9ydCBjb25zdCBzZXRPcHRpb25zID0gKHVzZXJPcHRpb25zLCBhcmdzKSA9PiB7XHJcbiAgLy8gT25seSBmb3IgdGhlIENMSSB1c2FnZVxyXG4gIGlmIChhcmdzPy5sZW5ndGgpIHtcclxuICAgIC8vIEdldCB0aGUgYWRkaXRpb25hbCBvcHRpb25zIGZyb20gdGhlIGN1c3RvbSBKU09OIGZpbGVcclxuICAgIGdlbmVyYWxPcHRpb25zID0gbG9hZENvbmZpZ0ZpbGUoYXJncyk7XHJcbiAgfVxyXG5cclxuICAvLyBVcGRhdGUgdGhlIGRlZmF1bHQgY29uZmlnIHdpdGggYSBjb3JyZWN0IG9wdGlvbiB2YWx1ZXNcclxuICB1cGRhdGVEZWZhdWx0Q29uZmlnKGRlZmF1bHRDb25maWcsIGdlbmVyYWxPcHRpb25zKTtcclxuXHJcbiAgLy8gU2V0IHZhbHVlcyBmb3Igc2VydmVyJ3Mgb3B0aW9ucyBhbmQgcmV0dXJucyB0aGVtXHJcbiAgZ2VuZXJhbE9wdGlvbnMgPSBpbml0T3B0aW9ucyhkZWZhdWx0Q29uZmlnKTtcclxuXHJcbiAgLy8gQXBwbHkgdXNlciBvcHRpb25zIGlmIHRoZXJlIGFyZSBhbnlcclxuICBpZiAodXNlck9wdGlvbnMpIHtcclxuICAgIC8vIE1lcmdlIHVzZXIgb3B0aW9uc1xyXG4gICAgZ2VuZXJhbE9wdGlvbnMgPSBtZXJnZUNvbmZpZ09wdGlvbnMoXHJcbiAgICAgIGdlbmVyYWxPcHRpb25zLFxyXG4gICAgICB1c2VyT3B0aW9ucyxcclxuICAgICAgYWJzb2x1dGVQcm9wc1xyXG4gICAgKTtcclxuICB9XHJcblxyXG4gIC8vIE9ubHkgZm9yIHRoZSBDTEkgdXNhZ2VcclxuICBpZiAoYXJncz8ubGVuZ3RoKSB7XHJcbiAgICAvLyBQYWlyIHByb3ZpZGVkIGFyZ3VtZW50c1xyXG4gICAgZ2VuZXJhbE9wdGlvbnMgPSBwYWlyQXJndW1lbnRWYWx1ZShnZW5lcmFsT3B0aW9ucywgYXJncywgZGVmYXVsdENvbmZpZyk7XHJcbiAgfVxyXG5cclxuICAvLyBSZXR1cm4gZmluYWwgZ2VuZXJhbCBvcHRpb25zXHJcbiAgcmV0dXJuIGdlbmVyYWxPcHRpb25zO1xyXG59O1xyXG5cclxuLyoqXHJcbiAqIEFsbG93cyBtYW51YWwgY29uZmlndXJhdGlvbiBiYXNlZCBvbiBzcGVjaWZpZWQgcHJvbXB0cyBhbmQgc2F2ZXNcclxuICogdGhlIGNvbmZpZ3VyYXRpb24gdG8gYSBmaWxlLlxyXG4gKlxyXG4gKiBAcGFyYW0ge3N0cmluZ30gY29uZmlnRmlsZU5hbWUgLSBUaGUgbmFtZSBvZiB0aGUgY29uZmlndXJhdGlvbiBmaWxlLlxyXG4gKlxyXG4gKiBAcmV0dXJucyB7UHJvbWlzZTxib29sZWFuPn0gQSBQcm9taXNlIHRoYXQgcmVzb2x2ZXMgdG8gdHJ1ZSBvbmNlIHRoZSBtYW51YWxcclxuICogY29uZmlndXJhdGlvbiBpcyBjb21wbGV0ZWQgYW5kIHNhdmVkLlxyXG4gKi9cclxuZXhwb3J0IGNvbnN0IG1hbnVhbENvbmZpZyA9IGFzeW5jIChjb25maWdGaWxlTmFtZSkgPT4ge1xyXG4gIC8vIFByZXBhcmUgYSBjb25maWcgb2JqZWN0XHJcbiAgbGV0IGNvbmZpZ0ZpbGUgPSB7fTtcclxuXHJcbiAgLy8gQ2hlY2sgaWYgcHJvdmlkZWQgY29uZmlnIGZpbGUgZXhpc3RzXHJcbiAgaWYgKGV4aXN0c1N5bmMoY29uZmlnRmlsZU5hbWUpKSB7XHJcbiAgICBjb25maWdGaWxlID0gSlNPTi5wYXJzZShyZWFkRmlsZVN5bmMoY29uZmlnRmlsZU5hbWUsICd1dGY4JykpO1xyXG4gIH1cclxuXHJcbiAgLy8gUXVlc3Rpb24gYWJvdXQgYSBjb25maWd1cmF0aW9uIGNhdGVnb3J5XHJcbiAgY29uc3Qgb25TdWJtaXQgPSBhc3luYyAocCwgY2F0ZWdvcmllcykgPT4ge1xyXG4gICAgbGV0IHF1ZXN0aW9uc0NvdW50ZXIgPSAwO1xyXG4gICAgbGV0IGFsbFF1ZXN0aW9ucyA9IFtdO1xyXG5cclxuICAgIC8vIENyZWF0ZSBhIGNvcnJlc3BvbmRpbmcgcHJvcGVydHkgaW4gdGhlIG1hbnVhbENvbmZpZyBvYmplY3RcclxuICAgIGZvciAoY29uc3Qgc2VjdGlvbiBvZiBjYXRlZ29yaWVzKSB7XHJcbiAgICAgIC8vIE1hcmsgZWFjaCBvcHRpb24gd2l0aCBhIHNlY3Rpb25cclxuICAgICAgcHJvbXB0c0NvbmZpZ1tzZWN0aW9uXSA9IHByb21wdHNDb25maWdbc2VjdGlvbl0ubWFwKChvcHRpb24pID0+ICh7XHJcbiAgICAgICAgLi4ub3B0aW9uLFxyXG4gICAgICAgIHNlY3Rpb25cclxuICAgICAgfSkpO1xyXG5cclxuICAgICAgLy8gQ29sbGVjdCB0aGUgcXVlc3Rpb25zXHJcbiAgICAgIGFsbFF1ZXN0aW9ucyA9IFsuLi5hbGxRdWVzdGlvbnMsIC4uLnByb21wdHNDb25maWdbc2VjdGlvbl1dO1xyXG4gICAgfVxyXG5cclxuICAgIGF3YWl0IHByb21wdHMoYWxsUXVlc3Rpb25zLCB7XHJcbiAgICAgIG9uU3VibWl0OiBhc3luYyAocHJvbXB0LCBhbnN3ZXIpID0+IHtcclxuICAgICAgICAvLyBHZXQgdGhlIGRlZmF1bHQgbW9kdWxlc1xyXG4gICAgICAgIGlmIChwcm9tcHQubmFtZSA9PT0gJ21vZHVsZXMnKSB7XHJcbiAgICAgICAgICBhbnN3ZXIgPSBhbnN3ZXIubGVuZ3RoXHJcbiAgICAgICAgICAgID8gYW5zd2VyLm1hcCgobW9kdWxlKSA9PiBwcm9tcHQuY2hvaWNlc1ttb2R1bGVdKVxyXG4gICAgICAgICAgICA6IHByb21wdC5jaG9pY2VzO1xyXG5cclxuICAgICAgICAgIGNvbmZpZ0ZpbGVbcHJvbXB0LnNlY3Rpb25dW3Byb21wdC5uYW1lXSA9IGFuc3dlcjtcclxuICAgICAgICB9IGVsc2Uge1xyXG4gICAgICAgICAgY29uZmlnRmlsZVtwcm9tcHQuc2VjdGlvbl0gPSByZWN1cnNpdmVQcm9wcyhcclxuICAgICAgICAgICAgT2JqZWN0LmFzc2lnbih7fSwgY29uZmlnRmlsZVtwcm9tcHQuc2VjdGlvbl0gfHwge30pLFxyXG4gICAgICAgICAgICBwcm9tcHQubmFtZS5zcGxpdCgnLicpLFxyXG4gICAgICAgICAgICBwcm9tcHQuY2hvaWNlcyA/IHByb21wdC5jaG9pY2VzW2Fuc3dlcl0gOiBhbnN3ZXJcclxuICAgICAgICAgICk7XHJcbiAgICAgICAgfVxyXG5cclxuICAgICAgICBpZiAoKytxdWVzdGlvbnNDb3VudGVyID09PSBhbGxRdWVzdGlvbnMubGVuZ3RoKSB7XHJcbiAgICAgICAgICB0cnkge1xyXG4gICAgICAgICAgICBhd2FpdCBmc1Byb21pc2VzLndyaXRlRmlsZShcclxuICAgICAgICAgICAgICBjb25maWdGaWxlTmFtZSxcclxuICAgICAgICAgICAgICBKU09OLnN0cmluZ2lmeShjb25maWdGaWxlLCBudWxsLCAyKSxcclxuICAgICAgICAgICAgICAndXRmOCdcclxuICAgICAgICAgICAgKTtcclxuICAgICAgICAgIH0gY2F0Y2ggKGVycm9yKSB7XHJcbiAgICAgICAgICAgIGxvZ1dpdGhTdGFjayhcclxuICAgICAgICAgICAgICAxLFxyXG4gICAgICAgICAgICAgIGVycm9yLFxyXG4gICAgICAgICAgICAgIGBbY29uZmlnXSBBbiBlcnJvciBvY2N1cnJlZCB3aGlsZSBjcmVhdGluZyB0aGUgJHtjb25maWdGaWxlTmFtZX0gZmlsZS5gXHJcbiAgICAgICAgICAgICk7XHJcbiAgICAgICAgICB9XHJcbiAgICAgICAgICByZXR1cm4gdHJ1ZTtcclxuICAgICAgICB9XHJcbiAgICAgIH1cclxuICAgIH0pO1xyXG5cclxuICAgIHJldHVybiB0cnVlO1xyXG4gIH07XHJcblxyXG4gIC8vIEZpbmQgdGhlIGNhdGVnb3JpZXNcclxuICBjb25zdCBjaG9pY2VzID0gT2JqZWN0LmtleXMocHJvbXB0c0NvbmZpZykubWFwKChjaG9pY2UpID0+ICh7XHJcbiAgICB0aXRsZTogYCR7Y2hvaWNlfSBvcHRpb25zYCxcclxuICAgIHZhbHVlOiBjaG9pY2VcclxuICB9KSk7XHJcblxyXG4gIC8vIENhdGVnb3J5IHByb21wdFxyXG4gIHJldHVybiBwcm9tcHRzKFxyXG4gICAge1xyXG4gICAgICB0eXBlOiAnbXVsdGlzZWxlY3QnLFxyXG4gICAgICBuYW1lOiAnY2F0ZWdvcnknLFxyXG4gICAgICBtZXNzYWdlOiAnV2hpY2ggY2F0ZWdvcnkgZG8geW91IHdhbnQgdG8gY29uZmlndXJlPycsXHJcbiAgICAgIGhpbnQ6ICdTcGFjZTogU2VsZWN0IHNwZWNpZmljLCBBOiBTZWxlY3QgYWxsLCBFbnRlcjogQ29uZmlybS4nLFxyXG4gICAgICBpbnN0cnVjdGlvbnM6ICcnLFxyXG4gICAgICBjaG9pY2VzXHJcbiAgICB9LFxyXG4gICAgeyBvblN1Ym1pdCB9XHJcbiAgKTtcclxufTtcclxuXHJcbi8qKlxyXG4gKiBNYXBzIG9sZC1zdHJ1Y3R1cmVkIChQaGFudG9tSlMpIG9wdGlvbnMgdG8gYSBuZXcgY29uZmlndXJhdGlvbiBmb3JtYXRcclxuICogKFB1cHBldGVlcikuXHJcbiAqXHJcbiAqIEBwYXJhbSB7T2JqZWN0fSBvbGRPcHRpb25zIC0gT2xkLXN0cnVjdHVyZWQgb3B0aW9ucyB0byBiZSBtYXBwZWQuXHJcbiAqXHJcbiAqIEByZXR1cm5zIHtPYmplY3R9IE5ldyBvcHRpb25zIHN0cnVjdHVyZWQgYmFzZWQgb24gdGhlIGRlZmluZWQgbmVzdGVkQXJnc1xyXG4gKiBtYXBwaW5nLlxyXG4gKi9cclxuZXhwb3J0IGNvbnN0IG1hcFRvTmV3Q29uZmlnID0gKG9sZE9wdGlvbnMpID0+IHtcclxuICBjb25zdCBuZXdPcHRpb25zID0ge307XHJcbiAgLy8gQ3ljbGUgdGhyb3VnaCBvbGQtc3RydWN0dXJlZCBvcHRpb25zXHJcbiAgZm9yIChjb25zdCBba2V5LCB2YWx1ZV0gb2YgT2JqZWN0LmVudHJpZXMob2xkT3B0aW9ucykpIHtcclxuICAgIGNvbnN0IHByb3BlcnRpZXNDaGFpbiA9IG5lc3RlZEFyZ3Nba2V5XSA/IG5lc3RlZEFyZ3Nba2V5XS5zcGxpdCgnLicpIDogW107XHJcblxyXG4gICAgLy8gUG9wdWxhdGUgb2JqZWN0IGluIGNvcnJlY3QgcHJvcGVydGllcyBsZXZlbHNcclxuICAgIHByb3BlcnRpZXNDaGFpbi5yZWR1Y2UoXHJcbiAgICAgIChvYmosIHByb3AsIGluZGV4KSA9PlxyXG4gICAgICAgIChvYmpbcHJvcF0gPVxyXG4gICAgICAgICAgcHJvcGVydGllc0NoYWluLmxlbmd0aCAtIDEgPT09IGluZGV4ID8gdmFsdWUgOiBvYmpbcHJvcF0gfHwge30pLFxyXG4gICAgICBuZXdPcHRpb25zXHJcbiAgICApO1xyXG4gIH1cclxuICByZXR1cm4gbmV3T3B0aW9ucztcclxufTtcclxuXHJcbi8qKlxyXG4gKiBNZXJnZXMgdHdvIHNldHMgb2YgY29uZmlndXJhdGlvbiBvcHRpb25zLCBjb25zaWRlcmluZyBhYnNvbHV0ZSBwcm9wZXJ0aWVzLlxyXG4gKlxyXG4gKiBAcGFyYW0ge09iamVjdH0gb3B0aW9ucyAtIE9yaWdpbmFsIGNvbmZpZ3VyYXRpb24gb3B0aW9ucy5cclxuICogQHBhcmFtIHtPYmplY3R9IG5ld09wdGlvbnMgLSBOZXcgY29uZmlndXJhdGlvbiBvcHRpb25zIHRvIGJlIG1lcmdlZC5cclxuICogQHBhcmFtIHtBcnJheX0gYWJzb2x1dGVQcm9wcyAtIExpc3Qgb2YgcHJvcGVydGllcyB0aGF0IHNob3VsZFxyXG4gKiBub3QgYmUgcmVjdXJzaXZlbHkgbWVyZ2VkLlxyXG4gKlxyXG4gKiBAcmV0dXJucyB7T2JqZWN0fSBNZXJnZWQgY29uZmlndXJhdGlvbiBvcHRpb25zLlxyXG4gKi9cclxuZXhwb3J0IGNvbnN0IG1lcmdlQ29uZmlnT3B0aW9ucyA9IChvcHRpb25zLCBuZXdPcHRpb25zLCBhYnNvbHV0ZVByb3BzID0gW10pID0+IHtcclxuICBjb25zdCBtZXJnZWRPcHRpb25zID0gZGVlcENvcHkob3B0aW9ucyk7XHJcblxyXG4gIGZvciAoY29uc3QgW2tleSwgdmFsdWVdIG9mIE9iamVjdC5lbnRyaWVzKG5ld09wdGlvbnMpKSB7XHJcbiAgICBtZXJnZWRPcHRpb25zW2tleV0gPVxyXG4gICAgICBpc09iamVjdCh2YWx1ZSkgJiZcclxuICAgICAgIWFic29sdXRlUHJvcHMuaW5jbHVkZXMoa2V5KSAmJlxyXG4gICAgICBtZXJnZWRPcHRpb25zW2tleV0gIT09IHVuZGVmaW5lZFxyXG4gICAgICAgID8gbWVyZ2VDb25maWdPcHRpb25zKG1lcmdlZE9wdGlvbnNba2V5XSwgdmFsdWUsIGFic29sdXRlUHJvcHMpXHJcbiAgICAgICAgOiB2YWx1ZSAhPT0gdW5kZWZpbmVkXHJcbiAgICAgICAgICA/IHZhbHVlXHJcbiAgICAgICAgICA6IG1lcmdlZE9wdGlvbnNba2V5XTtcclxuICB9XHJcblxyXG4gIHJldHVybiBtZXJnZWRPcHRpb25zO1xyXG59O1xyXG5cclxuLyoqXHJcbiAqIEluaXRpYWxpemVzIGV4cG9ydCBzZXR0aW5ncyBiYXNlZCBvbiBwcm92aWRlZCBleHBvcnRPcHRpb25zXHJcbiAqIGFuZCBnZW5lcmFsT3B0aW9ucy5cclxuICpcclxuICogQHBhcmFtIHtPYmplY3R9IGV4cG9ydE9wdGlvbnMgLSBPcHRpb25zIHNwZWNpZmljIHRvIHRoZSBleHBvcnQgcHJvY2Vzcy5cclxuICogQHBhcmFtIHtPYmplY3R9IGdlbmVyYWxPcHRpb25zIC0gR2VuZXJhbCBjb25maWd1cmF0aW9uIG9wdGlvbnMuXHJcbiAqXHJcbiAqIEByZXR1cm5zIHtPYmplY3R9IEluaXRpYWxpemVkIGV4cG9ydCBzZXR0aW5ncy5cclxuICovXHJcbmV4cG9ydCBjb25zdCBpbml0RXhwb3J0U2V0dGluZ3MgPSAoZXhwb3J0T3B0aW9ucywgZ2VuZXJhbE9wdGlvbnMgPSB7fSkgPT4ge1xyXG4gIGxldCBvcHRpb25zID0ge307XHJcblxyXG4gIGlmIChleHBvcnRPcHRpb25zLnN2Zykge1xyXG4gICAgb3B0aW9ucyA9IGRlZXBDb3B5KGdlbmVyYWxPcHRpb25zKTtcclxuICAgIG9wdGlvbnMuZXhwb3J0LnR5cGUgPSBleHBvcnRPcHRpb25zLnR5cGUgfHwgZXhwb3J0T3B0aW9ucy5leHBvcnQudHlwZTtcclxuICAgIG9wdGlvbnMuZXhwb3J0LnNjYWxlID0gZXhwb3J0T3B0aW9ucy5zY2FsZSB8fCBleHBvcnRPcHRpb25zLmV4cG9ydC5zY2FsZTtcclxuICAgIG9wdGlvbnMuZXhwb3J0Lm91dGZpbGUgPVxyXG4gICAgICBleHBvcnRPcHRpb25zLm91dGZpbGUgfHwgZXhwb3J0T3B0aW9ucy5leHBvcnQub3V0ZmlsZTtcclxuICAgIG9wdGlvbnMucGF5bG9hZCA9IHtcclxuICAgICAgc3ZnOiBleHBvcnRPcHRpb25zLnN2Z1xyXG4gICAgfTtcclxuICB9IGVsc2Uge1xyXG4gICAgb3B0aW9ucyA9IG1lcmdlQ29uZmlnT3B0aW9ucyhcclxuICAgICAgZ2VuZXJhbE9wdGlvbnMsXHJcbiAgICAgIGV4cG9ydE9wdGlvbnMsXHJcbiAgICAgIC8vIE9taXQgZ29pbmcgZG93biByZWN1cnNpdmVseSB3aXRoIHRoZSBiZWxvd3NcclxuICAgICAgYWJzb2x1dGVQcm9wc1xyXG4gICAgKTtcclxuICB9XHJcblxyXG4gIG9wdGlvbnMuZXhwb3J0Lm91dGZpbGUgPVxyXG4gICAgb3B0aW9ucy5leHBvcnQ/Lm91dGZpbGUgfHwgYGNoYXJ0LiR7b3B0aW9ucy5leHBvcnQ/LnR5cGUgfHwgJ3BuZyd9YDtcclxuICByZXR1cm4gb3B0aW9ucztcclxufTtcclxuXHJcbi8qKlxyXG4gKiBMb2FkcyBhZGRpdGlvbmFsIGNvbmZpZ3VyYXRpb24gZnJvbSBhIHNwZWNpZmllZCBmaWxlIHVzaW5nXHJcbiAqIHRoZSAtLWxvYWRDb25maWcgb3B0aW9uLlxyXG4gKlxyXG4gKiBAcGFyYW0ge0FycmF5fSBhcmdzIC0gQ29tbWFuZC1saW5lIGFyZ3VtZW50cyB0byBjaGVjayBmb3JcclxuICogdGhlIC0tbG9hZENvbmZpZyBvcHRpb24uXHJcbiAqXHJcbiAqIEByZXR1cm5zIHtPYmplY3R9IEFkZGl0aW9uYWwgY29uZmlndXJhdGlvbiBsb2FkZWQgZnJvbSB0aGUgc3BlY2lmaWVkIGZpbGUsXHJcbiAqIG9yIGFuIGVtcHR5IG9iamVjdCBpZiBub3QgZm91bmQgb3IgaW52YWxpZC5cclxuICovXHJcbmZ1bmN0aW9uIGxvYWRDb25maWdGaWxlKGFyZ3MpIHtcclxuICAvLyBDaGVjayBpZiB0aGUgLS1sb2FkQ29uZmlnIG9wdGlvbiB3YXMgdXNlZFxyXG4gIGNvbnN0IGNvbmZpZ0luZGV4ID0gYXJncy5maW5kSW5kZXgoXHJcbiAgICAoYXJnKSA9PiBhcmcucmVwbGFjZSgvLS9nLCAnJykgPT09ICdsb2FkQ29uZmlnJ1xyXG4gICk7XHJcblxyXG4gIC8vIENoZWNrIGlmIHRoZSAtLWxvYWRDb25maWcgaGFzIGEgdmFsdWVcclxuICBpZiAoY29uZmlnSW5kZXggPiAtMSAmJiBhcmdzW2NvbmZpZ0luZGV4ICsgMV0pIHtcclxuICAgIGNvbnN0IGZpbGVOYW1lID0gYXJnc1tjb25maWdJbmRleCArIDFdO1xyXG4gICAgdHJ5IHtcclxuICAgICAgLy8gQ2hlY2sgaWYgYW4gYWRkaXRpb25hbCBjb25maWcgZmlsZSBpcyBhIGNvcnJlY3QgSlNPTiBmaWxlXHJcbiAgICAgIGlmIChmaWxlTmFtZSAmJiBmaWxlTmFtZS5lbmRzV2l0aCgnLmpzb24nKSkge1xyXG4gICAgICAgIC8vIExvYWQgYW4gb3B0aW9uYWwgY3VzdG9tIEpTT04gY29uZmlnIGZpbGVcclxuICAgICAgICByZXR1cm4gSlNPTi5wYXJzZShyZWFkRmlsZVN5bmMoZmlsZU5hbWUpKTtcclxuICAgICAgfVxyXG4gICAgfSBjYXRjaCAoZXJyb3IpIHtcclxuICAgICAgbG9nV2l0aFN0YWNrKFxyXG4gICAgICAgIDIsXHJcbiAgICAgICAgZXJyb3IsXHJcbiAgICAgICAgYFtjb25maWddIFVuYWJsZSB0byBsb2FkIHRoZSBjb25maWd1cmF0aW9uIGZyb20gdGhlICR7ZmlsZU5hbWV9IGZpbGUuYFxyXG4gICAgICApO1xyXG4gICAgfVxyXG4gIH1cclxuXHJcbiAgLy8gTm8gYWRkaXRpb25hbCBvcHRpb25zIHRvIHJldHVyblxyXG4gIHJldHVybiB7fTtcclxufVxyXG5cclxuLyoqXHJcbiAqIFVwZGF0ZXMgdGhlIGRlZmF1bHQgY29uZmlndXJhdGlvbiBvYmplY3Qgd2l0aCB2YWx1ZXMgZnJvbSBhIGN1c3RvbSBvYmplY3RcclxuICogYW5kIGVudmlyb25tZW50IHZhcmlhYmxlcy5cclxuICpcclxuICogQHBhcmFtIHtPYmplY3R9IGNvbmZpZ09iaiAtIFRoZSBkZWZhdWx0IGNvbmZpZ3VyYXRpb24gb2JqZWN0LlxyXG4gKiBAcGFyYW0ge09iamVjdH0gY3VzdG9tT2JqIC0gQ3VzdG9tIGNvbmZpZ3VyYXRpb24gb2JqZWN0IHRvIG92ZXJyaWRlIGRlZmF1bHRzLlxyXG4gKiBAcGFyYW0ge3N0cmluZ30gcHJvcENoYWluIC0gUHJvcGVydHkgY2hhaW4gZm9yIHRyYWNraW5nIG5lc3RlZCBwcm9wZXJ0aWVzXHJcbiAqIGR1cmluZyByZWN1cnNpb24uXHJcbiAqL1xyXG5mdW5jdGlvbiB1cGRhdGVEZWZhdWx0Q29uZmlnKGNvbmZpZ09iaiwgY3VzdG9tT2JqID0ge30sIHByb3BDaGFpbiA9ICcnKSB7XHJcbiAgT2JqZWN0LmtleXMoY29uZmlnT2JqKS5mb3JFYWNoKChrZXkpID0+IHtcclxuICAgIGNvbnN0IGVudHJ5ID0gY29uZmlnT2JqW2tleV07XHJcbiAgICBjb25zdCBjdXN0b21WYWx1ZSA9IGN1c3RvbU9iaiAmJiBjdXN0b21PYmpba2V5XTtcclxuXHJcbiAgICBpZiAodHlwZW9mIGVudHJ5LnZhbHVlID09PSAndW5kZWZpbmVkJykge1xyXG4gICAgICB1cGRhdGVEZWZhdWx0Q29uZmlnKGVudHJ5LCBjdXN0b21WYWx1ZSwgYCR7cHJvcENoYWlufS4ke2tleX1gKTtcclxuICAgIH0gZWxzZSB7XHJcbiAgICAgIC8vIElmIGEgdmFsdWUgZnJvbSBhIGN1c3RvbSBKU09OIGV4aXN0cywgaXQgdGFrZSBwcmVjZWRlbmNlXHJcbiAgICAgIGlmIChjdXN0b21WYWx1ZSAhPT0gdW5kZWZpbmVkKSB7XHJcbiAgICAgICAgZW50cnkudmFsdWUgPSBjdXN0b21WYWx1ZTtcclxuICAgICAgfVxyXG5cclxuICAgICAgLy8gSWYgYSB2YWx1ZSBmcm9tIGFuIGVudiB2YXJpYWJsZSBleGlzdHMsIGl0IHRha2UgcHJlY2VkZW5jZVxyXG4gICAgICBpZiAoZW50cnkuZW52TGluayBpbiBlbnZzKSB7XHJcbiAgICAgICAgZW50cnkudmFsdWUgPSBlbnZzW2VudHJ5LmVudkxpbmtdO1xyXG4gICAgICB9XHJcbiAgICB9XHJcbiAgfSk7XHJcbn1cclxuXHJcbi8qKlxyXG4gKiBJbml0aWFsaXplcyBvcHRpb25zIG9iamVjdCBiYXNlZCBvbiBwcm92aWRlZCBpdGVtcywgc2V0dGluZyB2YWx1ZXMgZnJvbVxyXG4gKiBuZXN0ZWQgcHJvcGVydGllcyByZWN1cnNpdmVseS5cclxuICpcclxuICogQHBhcmFtIHtPYmplY3R9IGl0ZW1zIC0gQ29uZmlndXJhdGlvbiBpdGVtcyB0byBiZSB1c2VkIGZvciBpbml0aWFsaXppbmdcclxuICogb3B0aW9ucy5cclxuICpcclxuICogQHJldHVybnMge09iamVjdH0gSW5pdGlhbGl6ZWQgb3B0aW9ucyBvYmplY3QuXHJcbiAqL1xyXG5mdW5jdGlvbiBpbml0T3B0aW9ucyhpdGVtcykge1xyXG4gIGxldCBvcHRpb25zID0ge307XHJcbiAgZm9yIChjb25zdCBbbmFtZSwgaXRlbV0gb2YgT2JqZWN0LmVudHJpZXMoaXRlbXMpKSB7XHJcbiAgICBvcHRpb25zW25hbWVdID0gT2JqZWN0LnByb3RvdHlwZS5oYXNPd25Qcm9wZXJ0eS5jYWxsKGl0ZW0sICd2YWx1ZScpXHJcbiAgICAgID8gaXRlbS52YWx1ZVxyXG4gICAgICA6IGluaXRPcHRpb25zKGl0ZW0pO1xyXG4gIH1cclxuICByZXR1cm4gb3B0aW9ucztcclxufVxyXG5cclxuLyoqXHJcbiAqIFBhaXJzIGFyZ3VtZW50IHZhbHVlcyB3aXRoIGNvcnJlc3BvbmRpbmcgb3B0aW9ucyBpbiB0aGUgY29uZmlndXJhdGlvbixcclxuICogdXBkYXRpbmcgdGhlIG9wdGlvbnMgb2JqZWN0LlxyXG4gKlxyXG4gKiBAcGFyYW0ge09iamVjdH0gb3B0aW9ucyAtIENvbmZpZ3VyYXRpb24gb3B0aW9ucyBvYmplY3QgdG8gYmUgdXBkYXRlZC5cclxuICogQHBhcmFtIHtBcnJheX0gYXJncyAtIENvbW1hbmQtbGluZSBhcmd1bWVudHMgY29udGFpbmluZyB2YWx1ZXMgZm9yIHNwZWNpZmljXHJcbiAqIG9wdGlvbnMuXHJcbiAqIEBwYXJhbSB7T2JqZWN0fSBkZWZhdWx0Q29uZmlnIC0gRGVmYXVsdCBjb25maWd1cmF0aW9uIG9iamVjdCBmb3IgcmVmZXJlbmNlLlxyXG4gKlxyXG4gKiBAcmV0dXJucyB7T2JqZWN0fSBVcGRhdGVkIG9wdGlvbnMgb2JqZWN0LlxyXG4gKi9cclxuZnVuY3Rpb24gcGFpckFyZ3VtZW50VmFsdWUob3B0aW9ucywgYXJncywgZGVmYXVsdENvbmZpZykge1xyXG4gIGxldCBzaG93VXNhZ2UgPSBmYWxzZTtcclxuICBmb3IgKGxldCBpID0gMDsgaSA8IGFyZ3MubGVuZ3RoOyBpKyspIHtcclxuICAgIGNvbnN0IG9wdGlvbiA9IGFyZ3NbaV0ucmVwbGFjZSgvLS9nLCAnJyk7XHJcblxyXG4gICAgLy8gRmluZCB0aGUgcmlnaHQgcGxhY2UgZm9yIHByb3BlcnR5J3MgdmFsdWVcclxuICAgIGNvbnN0IHByb3BlcnRpZXNDaGFpbiA9IG5lc3RlZEFyZ3Nbb3B0aW9uXVxyXG4gICAgICA/IG5lc3RlZEFyZ3Nbb3B0aW9uXS5zcGxpdCgnLicpXHJcbiAgICAgIDogW107XHJcblxyXG4gICAgLy8gR2V0IHRoZSBjb3JyZWN0IHR5cGUgZm9yIENMSSBhcmdzIHdoaWNoIGFyZSBwYXNzZWQgYXMgc3RyaW5nc1xyXG4gICAgbGV0IGFyZ3VtZW50VHlwZTtcclxuICAgIHByb3BlcnRpZXNDaGFpbi5yZWR1Y2UoKG9iaiwgcHJvcCwgaW5kZXgpID0+IHtcclxuICAgICAgaWYgKHByb3BlcnRpZXNDaGFpbi5sZW5ndGggLSAxID09PSBpbmRleCkge1xyXG4gICAgICAgIGFyZ3VtZW50VHlwZSA9IG9ialtwcm9wXS50eXBlO1xyXG4gICAgICB9XHJcbiAgICAgIHJldHVybiBvYmpbcHJvcF07XHJcbiAgICB9LCBkZWZhdWx0Q29uZmlnKTtcclxuXHJcbiAgICBwcm9wZXJ0aWVzQ2hhaW4ucmVkdWNlKChvYmosIHByb3AsIGluZGV4KSA9PiB7XHJcbiAgICAgIGlmIChwcm9wZXJ0aWVzQ2hhaW4ubGVuZ3RoIC0gMSA9PT0gaW5kZXgpIHtcclxuICAgICAgICAvLyBGaW5kcyBhbiBvcHRpb24gYW5kIHNldCBhIGNvcnJlc3BvbmRpbmcgdmFsdWVcclxuICAgICAgICBpZiAodHlwZW9mIG9ialtwcm9wXSAhPT0gJ3VuZGVmaW5lZCcpIHtcclxuICAgICAgICAgIGlmIChhcmdzWysraV0pIHtcclxuICAgICAgICAgICAgaWYgKGFyZ3VtZW50VHlwZSA9PT0gJ2Jvb2xlYW4nKSB7XHJcbiAgICAgICAgICAgICAgb2JqW3Byb3BdID0gdG9Cb29sZWFuKGFyZ3NbaV0pO1xyXG4gICAgICAgICAgICB9IGVsc2UgaWYgKGFyZ3VtZW50VHlwZSA9PT0gJ251bWJlcicpIHtcclxuICAgICAgICAgICAgICBvYmpbcHJvcF0gPSArYXJnc1tpXTtcclxuICAgICAgICAgICAgfSBlbHNlIGlmIChhcmd1bWVudFR5cGUuaW5kZXhPZignXScpID49IDApIHtcclxuICAgICAgICAgICAgICBvYmpbcHJvcF0gPSBhcmdzW2ldLnNwbGl0KCcsJyk7XHJcbiAgICAgICAgICAgIH0gZWxzZSB7XHJcbiAgICAgICAgICAgICAgb2JqW3Byb3BdID0gYXJnc1tpXTtcclxuICAgICAgICAgICAgfVxyXG4gICAgICAgICAgfSBlbHNlIHtcclxuICAgICAgICAgICAgbG9nKFxyXG4gICAgICAgICAgICAgIDIsXHJcbiAgICAgICAgICAgICAgYFtjb25maWddIE1pc3NpbmcgdmFsdWUgZm9yIHRoZSAnJHtvcHRpb259JyBhcmd1bWVudC4gVXNpbmcgdGhlIGRlZmF1bHQgdmFsdWUuYFxyXG4gICAgICAgICAgICApO1xyXG4gICAgICAgICAgICBzaG93VXNhZ2UgPSB0cnVlO1xyXG4gICAgICAgICAgfVxyXG4gICAgICAgIH1cclxuICAgICAgfVxyXG4gICAgICByZXR1cm4gb2JqW3Byb3BdO1xyXG4gICAgfSwgb3B0aW9ucyk7XHJcbiAgfVxyXG5cclxuICAvLyBEaXNwbGF5IHRoZSB1c2FnZSBmb3IgdGhlIHJlZmVyZW5jZSBpZiBuZWVkZWRcclxuICBpZiAoc2hvd1VzYWdlKSB7XHJcbiAgICBwcmludFVzYWdlKGRlZmF1bHRDb25maWcpO1xyXG4gIH1cclxuXHJcbiAgcmV0dXJuIG9wdGlvbnM7XHJcbn1cclxuXHJcbi8qKlxyXG4gKiBSZWN1cnNpdmVseSB1cGRhdGVzIHByb3BlcnRpZXMgaW4gYW4gb2JqZWN0IGJhc2VkIG9uIG5lc3RlZCBuYW1lcyBhbmQgYXNzaWduc1xyXG4gKiB0aGUgZmluYWwgdmFsdWUuXHJcbiAqXHJcbiAqIEBwYXJhbSB7T2JqZWN0fSBvYmplY3RUb1VwZGF0ZSAtIFRoZSBvYmplY3QgdG8gYmUgdXBkYXRlZC5cclxuICogQHBhcmFtIHtBcnJheX0gbmVzdGVkTmFtZXMgLSBBcnJheSBvZiBuZXN0ZWQgcHJvcGVydHkgbmFtZXMuXHJcbiAqIEBwYXJhbSB7YW55fSB2YWx1ZSAtIFRoZSBmaW5hbCB2YWx1ZSB0byBiZSBhc3NpZ25lZC5cclxuICpcclxuICogQHJldHVybnMge09iamVjdH0gVXBkYXRlZCBvYmplY3Qgd2l0aCBhc3NpZ25lZCB2YWx1ZXMuXHJcbiAqL1xyXG5mdW5jdGlvbiByZWN1cnNpdmVQcm9wcyhvYmplY3RUb1VwZGF0ZSwgbmVzdGVkTmFtZXMsIHZhbHVlKSB7XHJcbiAgd2hpbGUgKG5lc3RlZE5hbWVzLmxlbmd0aCA+IDEpIHtcclxuICAgIGNvbnN0IHByb3BOYW1lID0gbmVzdGVkTmFtZXMuc2hpZnQoKTtcclxuXHJcbiAgICAvLyBDcmVhdGUgYSBwcm9wZXJ0eSBpbiBvYmplY3QgaWYgaXQgZG9lc24ndCBleGlzdFxyXG4gICAgaWYgKCFPYmplY3QucHJvdG90eXBlLmhhc093blByb3BlcnR5LmNhbGwob2JqZWN0VG9VcGRhdGUsIHByb3BOYW1lKSkge1xyXG4gICAgICBvYmplY3RUb1VwZGF0ZVtwcm9wTmFtZV0gPSB7fTtcclxuICAgIH1cclxuXHJcbiAgICAvLyBDYWxsIGZ1bmN0aW9uIGFnYWluIGlmIHRoZXJlIHN0aWxsIG5hbWVzIHRvIGdvXHJcbiAgICBvYmplY3RUb1VwZGF0ZVtwcm9wTmFtZV0gPSByZWN1cnNpdmVQcm9wcyhcclxuICAgICAgT2JqZWN0LmFzc2lnbih7fSwgb2JqZWN0VG9VcGRhdGVbcHJvcE5hbWVdKSxcclxuICAgICAgbmVzdGVkTmFtZXMsXHJcbiAgICAgIHZhbHVlXHJcbiAgICApO1xyXG5cclxuICAgIHJldHVybiBvYmplY3RUb1VwZGF0ZTtcclxuICB9XHJcblxyXG4gIC8vIEFzc2lnbiB0aGUgZmluYWwgdmFsdWVcclxuICBvYmplY3RUb1VwZGF0ZVtuZXN0ZWROYW1lc1swXV0gPSB2YWx1ZTtcclxuICByZXR1cm4gb2JqZWN0VG9VcGRhdGU7XHJcbn1cclxuXHJcbmV4cG9ydCBkZWZhdWx0IHtcclxuICBnZXRPcHRpb25zLFxyXG4gIHNldE9wdGlvbnMsXHJcbiAgbWFudWFsQ29uZmlnLFxyXG4gIG1hcFRvTmV3Q29uZmlnLFxyXG4gIG1lcmdlQ29uZmlnT3B0aW9ucyxcclxuICBpbml0RXhwb3J0U2V0dGluZ3NcclxufTtcclxuIiwiLyoqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKipcclxuXHJcbkhpZ2hjaGFydHMgRXhwb3J0IFNlcnZlclxyXG5cclxuQ29weXJpZ2h0IChjKSAyMDE2LTIwMjQsIEhpZ2hzb2Z0XHJcblxyXG5MaWNlbmNlZCB1bmRlciB0aGUgTUlUIGxpY2VuY2UuXHJcblxyXG5BZGRpdGlvbmFsbHkgYSB2YWxpZCBIaWdoY2hhcnRzIGxpY2Vuc2UgaXMgcmVxdWlyZWQgZm9yIHVzZS5cclxuXHJcblNlZSBMSUNFTlNFIGZpbGUgaW4gcm9vdCBmb3IgZGV0YWlscy5cclxuXHJcbioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKiovXHJcblxyXG5pbXBvcnQgZnMgZnJvbSAnZnMnO1xyXG5pbXBvcnQgKiBhcyB1cmwgZnJvbSAndXJsJztcclxuaW1wb3J0IHBhdGggZnJvbSAnbm9kZTpwYXRoJztcclxuXHJcbmltcG9ydCBwdXBwZXRlZXIgZnJvbSAncHVwcGV0ZWVyJztcclxuXHJcbi8vIFdvcmthcm91bmQgZm9yIGh0dHBzOi8vYnVncy5jaHJvbWl1bS5vcmcvcC9jaHJvbWl1bS9pc3N1ZXMvZGV0YWlsP2lkPTE0NjMzMjhcclxuLy8gTm90IGlkZWFsIC0gbGVhdmVzIHRyYXNoIGluIHRoZSBGU1xyXG5pbXBvcnQgeyByYW5kb21CeXRlcyB9IGZyb20gJ25vZGU6Y3J5cHRvJztcclxuXHJcbmltcG9ydCB7IGdldENhY2hlUGF0aCB9IGZyb20gJy4vY2FjaGUuanMnO1xyXG5pbXBvcnQgeyBsb2csIGxvZ1dpdGhTdGFjayB9IGZyb20gJy4vbG9nZ2VyLmpzJztcclxuXHJcbmltcG9ydCBFeHBvcnRFcnJvciBmcm9tICcuL2Vycm9ycy9FeHBvcnRFcnJvci5qcyc7XHJcblxyXG5jb25zdCBSQU5ET01fUElEID0gcmFuZG9tQnl0ZXMoNjQpLnRvU3RyaW5nKCdiYXNlNjR1cmwnKTtcclxuY29uc3QgUFVQUEVURUVSX0RJUiA9IHBhdGguam9pbigndG1wJywgYHB1cHBldGVlci0ke1JBTkRPTV9QSUR9YCk7XHJcbmNvbnN0IERBVEFfRElSID0gcGF0aC5qb2luKFBVUFBFVEVFUl9ESVIsICdwcm9maWxlJyk7XHJcblxyXG4vLyBUaGUgbWluaW1hbCBhcmdzIHRvIHNwZWVkIHVwIHRoZSBicm93c2VyXHJcbmNvbnN0IG1pbmltYWxBcmdzID0gW1xyXG4gIGAtLXVzZXItZGF0YS1kaXI9JHtEQVRBX0RJUn1gLFxyXG4gICctLWF1dG9wbGF5LXBvbGljeT11c2VyLWdlc3R1cmUtcmVxdWlyZWQnLFxyXG4gICctLWRpc2FibGUtYmFja2dyb3VuZC1uZXR3b3JraW5nJyxcclxuICAnLS1kaXNhYmxlLWJhY2tncm91bmQtdGltZXItdGhyb3R0bGluZycsXHJcbiAgJy0tZGlzYWJsZS1iYWNrZ3JvdW5kaW5nLW9jY2x1ZGVkLXdpbmRvd3MnLFxyXG4gICctLWRpc2FibGUtYnJlYWtwYWQnLFxyXG4gICctLWRpc2FibGUtY2xpZW50LXNpZGUtcGhpc2hpbmctZGV0ZWN0aW9uJyxcclxuICAnLS1kaXNhYmxlLWNvbXBvbmVudC11cGRhdGUnLFxyXG4gICctLWRpc2FibGUtZGVmYXVsdC1hcHBzJyxcclxuICAnLS1kaXNhYmxlLWRldi1zaG0tdXNhZ2UnLFxyXG4gICctLWRpc2FibGUtZG9tYWluLXJlbGlhYmlsaXR5JyxcclxuICAnLS1kaXNhYmxlLWV4dGVuc2lvbnMnLFxyXG4gICctLWRpc2FibGUtZmVhdHVyZXM9QXVkaW9TZXJ2aWNlT3V0T2ZQcm9jZXNzJyxcclxuICAnLS1kaXNhYmxlLWhhbmctbW9uaXRvcicsXHJcbiAgJy0tZGlzYWJsZS1pcGMtZmxvb2RpbmctcHJvdGVjdGlvbicsXHJcbiAgJy0tZGlzYWJsZS1ub3RpZmljYXRpb25zJyxcclxuICAnLS1kaXNhYmxlLW9mZmVyLXN0b3JlLXVubWFza2VkLXdhbGxldC1jYXJkcycsXHJcbiAgJy0tZGlzYWJsZS1wb3B1cC1ibG9ja2luZycsXHJcbiAgJy0tZGlzYWJsZS1wcmludC1wcmV2aWV3JyxcclxuICAnLS1kaXNhYmxlLXByb21wdC1vbi1yZXBvc3QnLFxyXG4gICctLWRpc2FibGUtcmVuZGVyZXItYmFja2dyb3VuZGluZycsXHJcbiAgJy0tZGlzYWJsZS1zZXNzaW9uLWNyYXNoZWQtYnViYmxlJyxcclxuICAnLS1kaXNhYmxlLXNldHVpZC1zYW5kYm94JyxcclxuICAnLS1kaXNhYmxlLXNwZWVjaC1hcGknLFxyXG4gICctLWRpc2FibGUtc3luYycsXHJcbiAgJy0taGlkZS1jcmFzaC1yZXN0b3JlLWJ1YmJsZScsXHJcbiAgJy0taGlkZS1zY3JvbGxiYXJzJyxcclxuICAnLS1pZ25vcmUtZ3B1LWJsYWNrbGlzdCcsXHJcbiAgJy0tbWV0cmljcy1yZWNvcmRpbmctb25seScsXHJcbiAgJy0tbXV0ZS1hdWRpbycsXHJcbiAgJy0tbm8tZGVmYXVsdC1icm93c2VyLWNoZWNrJyxcclxuICAnLS1uby1maXJzdC1ydW4nLFxyXG4gICctLW5vLXBpbmdzJyxcclxuICAnLS1uby1zYW5kYm94JyxcclxuICAnLS1uby16eWdvdGUnLFxyXG4gICctLXBhc3N3b3JkLXN0b3JlPWJhc2ljJyxcclxuICAnLS11c2UtbW9jay1rZXljaGFpbidcclxuXTtcclxuXHJcbmNvbnN0IF9fZGlybmFtZSA9IHVybC5maWxlVVJMVG9QYXRoKG5ldyBVUkwoJy4nLCBpbXBvcnQubWV0YS51cmwpKTtcclxuXHJcbmNvbnN0IHRlbXBsYXRlID0gZnMucmVhZEZpbGVTeW5jKFxyXG4gIF9fZGlybmFtZSArICcvLi4vdGVtcGxhdGVzL3RlbXBsYXRlLmh0bWwnLFxyXG4gICd1dGY4J1xyXG4pO1xyXG5cclxubGV0IGJyb3dzZXI7XHJcblxyXG4vKipcclxuICogU2V0cyB0aGUgY29udGVudCBmb3IgYSBQdXBwZXRlZXIgUGFnZSB1c2luZyBhIHByZWRlZmluZWQgdGVtcGxhdGVcclxuICogYW5kIGFkZGl0aW9uYWwgc2NyaXB0cy4gQWxzbywgc2V0cyB0aGUgcGFnZWVycm9yIGluIG9yZGVyIHRvIGNhdGNoXHJcbiAqIGFuZCBkaXNwbGF5IGVycm9ycyBmcm9tIHRoZSB3aW5kb3cgY29udGV4dC5cclxuICpcclxuICogQHBhcmFtIHtPYmplY3R9IHBhZ2UgLSBUaGUgUHVwcGV0ZWVyIFBhZ2Ugb2JqZWN0IGZvciB3aGljaCB0aGUgY29udGVudFxyXG4gKiBpcyBiZWluZyBzZXQuXHJcbiAqL1xyXG5jb25zdCBzZXRQYWdlQ29udGVudCA9IGFzeW5jIChwYWdlKSA9PiB7XHJcbiAgYXdhaXQgcGFnZS5zZXRDb250ZW50KHRlbXBsYXRlKTtcclxuICBhd2FpdCBwYWdlLmFkZFNjcmlwdFRhZyh7IHBhdGg6IGAke2dldENhY2hlUGF0aCgpfS9zb3VyY2VzLmpzYCB9KTtcclxuICAvLyBlc2xpbnQtZGlzYWJsZS1uZXh0LWxpbmUgbm8tdW5kZWZcclxuICBhd2FpdCBwYWdlLmV2YWx1YXRlKCgpID0+IHdpbmRvdy5zZXR1cEhpZ2hjaGFydHMoKSk7XHJcblxyXG4gIHBhZ2Uub24oJ3BhZ2VlcnJvcicsIGFzeW5jIChlcnJvcikgPT4ge1xyXG4gICAgLy8gVE9ETzogQ29uc2lkZXIgYWRkaW5nIGEgc3dpdGNoIGhlcmUgdGhhdCB0dXJucyBvbiBsb2coMCkgbG9nZ2luZ1xyXG4gICAgLy8gb24gcGFnZSBlcnJvcnMuXHJcbiAgICBhd2FpdCBwYWdlLiRldmFsKFxyXG4gICAgICAnI2NvbnRhaW5lcicsXHJcbiAgICAgIChlbGVtZW50LCBlcnJvck1lc3NhZ2UpID0+IHtcclxuICAgICAgICAvLyBlc2xpbnQtZGlzYWJsZS1uZXh0LWxpbmUgbm8tdW5kZWZcclxuICAgICAgICBpZiAod2luZG93Ll9kaXNwbGF5RXJyb3JzKSB7XHJcbiAgICAgICAgICBlbGVtZW50LmlubmVySFRNTCA9IGVycm9yTWVzc2FnZTtcclxuICAgICAgICB9XHJcbiAgICAgIH0sXHJcbiAgICAgIGA8aDE+Q2hhcnQgaW5wdXQgZGF0YSBlcnJvcjwvaDE+JHtlcnJvci50b1N0cmluZygpfWBcclxuICAgICk7XHJcbiAgfSk7XHJcbn07XHJcblxyXG4vKipcclxuICogQ2xlYXJzIHRoZSBjb250ZW50IG9mIGEgUHVwcGV0ZWVyIFBhZ2UgYmFzZWQgb24gdGhlIHNwZWNpZmllZCBtb2RlLlxyXG4gKlxyXG4gKiBAcGFyYW0ge09iamVjdH0gcGFnZSAtIFRoZSBQdXBwZXRlZXIgUGFnZSBvYmplY3QgdG8gYmUgY2xlYXJlZC5cclxuICogQHBhcmFtIHtib29sZWFufSBoYXJkUmVzZXQgLSBBIGZsYWcgaW5kaWNhdGluZyB0aGUgdHlwZSBvZiBjbGVhcmluZ1xyXG4gKiB0byBiZSBwZXJmb3JtZWQuIElmIHRydWUsIG5hdmlnYXRlcyB0byAnYWJvdXQ6YmxhbmsnIGFuZCByZXNldHMgY29udGVudFxyXG4gKiBhbmQgc2NyaXB0cy4gSWYgZmFsc2UsIGNsZWFycyB0aGUgYm9keSBjb250ZW50IGJ5IHNldHRpbmcgYSBwcmVkZWZpbmVkIEhUTUxcclxuICogc3RydWN0dXJlLlxyXG4gKlxyXG4gKiBAdGhyb3dzIHtFcnJvcn0gTG9ncyB0aHJvd24gZXJyb3IgaWYgY2xlYXJpbmcgdGhlIHBhZ2UgY29udGVudCBmYWlscy5cclxuICovXHJcbmV4cG9ydCBjb25zdCBjbGVhclBhZ2UgPSBhc3luYyAocGFnZSwgaGFyZFJlc2V0ID0gZmFsc2UpID0+IHtcclxuICB0cnkge1xyXG4gICAgaWYgKGhhcmRSZXNldCkge1xyXG4gICAgICAvLyBOYXZpZ2F0ZSB0byBhYm91dDpibGFua1xyXG4gICAgICBhd2FpdCBwYWdlLmdvdG8oJ2Fib3V0OmJsYW5rJyk7XHJcblxyXG4gICAgICAvLyBTZXQgdGhlIGNvbnRlbnQgYW5kIGFuZCBzY3JpcHRzIGFnYWluXHJcbiAgICAgIGF3YWl0IHNldFBhZ2VDb250ZW50KHBhZ2UpO1xyXG4gICAgfSBlbHNlIHtcclxuICAgICAgLy8gQ2xlYXIgYm9keSBjb250ZW50XHJcbiAgICAgIGF3YWl0IHBhZ2UuZXZhbHVhdGUoKCkgPT4ge1xyXG4gICAgICAgIGRvY3VtZW50LmJvZHkuaW5uZXJIVE1MID1cclxuICAgICAgICAgICc8ZGl2IGlkPVwiY2hhcnQtY29udGFpbmVyXCI+PGRpdiBpZD1cImNvbnRhaW5lclwiPjwvZGl2PjwvZGl2Pic7XHJcbiAgICAgIH0pO1xyXG4gICAgfVxyXG4gIH0gY2F0Y2ggKGVycm9yKSB7XHJcbiAgICBsb2dXaXRoU3RhY2soXHJcbiAgICAgIDIsXHJcbiAgICAgIGVycm9yLFxyXG4gICAgICAnW2Jyb3dzZXJdIENvdWxkIG5vdCBjbGVhciB0aGUgY29udGVudCBvZiB0aGUgcGFnZS4nXHJcbiAgICApO1xyXG4gIH1cclxufTtcclxuXHJcbi8qKlxyXG4gKiBDcmVhdGVzIGEgbmV3IFB1cHBldGVlciBQYWdlIHdpdGhpbiBhbiBleGlzdGluZyBicm93c2VyIGluc3RhbmNlLlxyXG4gKlxyXG4gKiBJZiB0aGUgYnJvd3NlciBpbnN0YW5jZSBpcyBub3QgYXZhaWxhYmxlLCByZXR1cm5zIGZhbHNlLlxyXG4gKlxyXG4gKiBUaGUgZnVuY3Rpb24gY3JlYXRlcyBhIG5ldyBwYWdlLCBkaXNhYmxlcyBjYWNoaW5nLCBzZXRzIGNvbnRlbnQgdXNpbmdcclxuICogc2V0UGFnZUNvbnRlbnQoKSwgYW5kIHJldHVybnMgdGhlIGNyZWF0ZWQgUHVwcGV0ZWVyIFBhZ2UuXHJcbiAqXHJcbiAqIEByZXR1cm5zIHsoYm9vbGVhbnxvYmplY3QpfSBSZXR1cm5zIGZhbHNlIGlmIHRoZSBicm93c2VyIGluc3RhbmNlIGlzIG5vdFxyXG4gKiBhdmFpbGFibGUsIG9yIGEgUHVwcGV0ZWVyIFBhZ2Ugb2JqZWN0IHJlcHJlc2VudGluZyB0aGUgbmV3bHkgY3JlYXRlZCBwYWdlLlxyXG4gKi9cclxuZXhwb3J0IGNvbnN0IG5ld1BhZ2UgPSBhc3luYyAoKSA9PiB7XHJcbiAgaWYgKCFicm93c2VyKSB7XHJcbiAgICByZXR1cm4gZmFsc2U7XHJcbiAgfVxyXG5cclxuICBjb25zdCBwYWdlID0gYXdhaXQgYnJvd3Nlci5uZXdQYWdlKCk7XHJcblxyXG4gIC8vIERpc2FibGUgY2FjaGVcclxuICBhd2FpdCBwYWdlLnNldENhY2hlRW5hYmxlZChmYWxzZSk7XHJcblxyXG4gIC8vIFNldCB0aGUgY29udGVudFxyXG4gIGF3YWl0IHNldFBhZ2VDb250ZW50KHBhZ2UpO1xyXG4gIHJldHVybiBwYWdlO1xyXG59O1xyXG5cclxuLyoqXHJcbiAqIENyZWF0ZXMgYSBQdXBwZXRlZXIgYnJvd3NlciBpbnN0YW5jZSB3aXRoIHRoZSBzcGVjaWZpZWQgYXJndW1lbnRzLlxyXG4gKlxyXG4gKiBAcGFyYW0ge0FycmF5fSBwdXBwZXRlZXJBcmdzIC0gQWRkaXRpb25hbCBhcmd1bWVudHMgZm9yIFB1cHBldGVlciBsYXVuY2guXHJcbiAqXHJcbiAqIEByZXR1cm5zIHtQcm9taXNlPG9iamVjdD59IEEgUHJvbWlzZSByZXNvbHZpbmcgdG8gdGhlIFB1cHBldGVlciBicm93c2VyXHJcbiAqIGluc3RhbmNlLlxyXG4gKlxyXG4gKiBAdGhyb3dzIHtFeHBvcnRFcnJvcn0gVGhyb3dzIGFuIEV4cG9ydEVycm9yIGlmIG1heCByZXRyaWVzIHRvIG9wZW4gYSBicm93c2VyXHJcbiAqIGluc3RhbmNlIGFyZSByZWFjaGVkLCBvciBpZiBubyBicm93c2VyIGluc3RhbmNlIGlzIGZvdW5kIGFmdGVyIHJldHJpZXMuXHJcbiAqL1xyXG5leHBvcnQgY29uc3QgY3JlYXRlID0gYXN5bmMgKHB1cHBldGVlckFyZ3MpID0+IHtcclxuICBjb25zdCBhbGxBcmdzID0gWy4uLm1pbmltYWxBcmdzLCAuLi4ocHVwcGV0ZWVyQXJncyB8fCBbXSldO1xyXG5cclxuICAvLyBDcmVhdGUgYSBicm93c2VyXHJcbiAgaWYgKCFicm93c2VyKSB7XHJcbiAgICBsZXQgdHJ5Q291bnQgPSAwO1xyXG5cclxuICAgIGNvbnN0IG9wZW4gPSBhc3luYyAoKSA9PiB7XHJcbiAgICAgIHRyeSB7XHJcbiAgICAgICAgbG9nKFxyXG4gICAgICAgICAgMyxcclxuICAgICAgICAgIGBbYnJvd3Nlcl0gQXR0ZW1wdGluZyB0byBnZXQgYSBicm93c2VyIGluc3RhbmNlICh0cnkgJHsrK3RyeUNvdW50fSkuYFxyXG4gICAgICAgICk7XHJcbiAgICAgICAgYnJvd3NlciA9IGF3YWl0IHB1cHBldGVlci5sYXVuY2goe1xyXG4gICAgICAgICAgaGVhZGxlc3M6ICduZXcnLFxyXG4gICAgICAgICAgYXJnczogYWxsQXJncyxcclxuICAgICAgICAgIHVzZXJEYXRhRGlyOiAnLi90bXAvJ1xyXG4gICAgICAgIH0pO1xyXG4gICAgICB9IGNhdGNoIChlcnJvcikge1xyXG4gICAgICAgIGxvZ1dpdGhTdGFjayhcclxuICAgICAgICAgIDEsXHJcbiAgICAgICAgICBlcnJvcixcclxuICAgICAgICAgICdbYnJvd3Nlcl0gRmFpbGVkIHRvIGxhdW5jaCBhIGJyb3dzZXIgaW5zdGFuY2UuJ1xyXG4gICAgICAgICk7XHJcblxyXG4gICAgICAgIC8vIFJldHJ5IHRvIGxhdW5jaCBicm93c2VyIHVudGlsIHJlYWNoaW5nIG1heCBhdHRlbXB0c1xyXG4gICAgICAgIGlmICh0cnlDb3VudCA8IDI1KSB7XHJcbiAgICAgICAgICBsb2coMywgYFticm93c2VyXSBSZXRyeSB0byBvcGVuIGEgYnJvd3NlciAoJHt0cnlDb3VudH0gb3V0IG9mIDI1KS5gKTtcclxuICAgICAgICAgIGF3YWl0IG5ldyBQcm9taXNlKChyZXNwb25zZSkgPT4gc2V0VGltZW91dChyZXNwb25zZSwgNDAwMCkpO1xyXG4gICAgICAgICAgYXdhaXQgb3BlbigpO1xyXG4gICAgICAgIH0gZWxzZSB7XHJcbiAgICAgICAgICB0aHJvdyBlcnJvcjtcclxuICAgICAgICB9XHJcbiAgICAgIH1cclxuICAgIH07XHJcblxyXG4gICAgdHJ5IHtcclxuICAgICAgYXdhaXQgb3BlbigpO1xyXG4gICAgfSBjYXRjaCAoZXJyb3IpIHtcclxuICAgICAgdGhyb3cgbmV3IEV4cG9ydEVycm9yKFxyXG4gICAgICAgICdbYnJvd3Nlcl0gTWF4aW11bSByZXRyaWVzIHRvIG9wZW4gYSBicm93c2VyIGluc3RhbmNlIHJlYWNoZWQuJ1xyXG4gICAgICApLnNldEVycm9yKGVycm9yKTtcclxuICAgIH1cclxuXHJcbiAgICBpZiAoIWJyb3dzZXIpIHtcclxuICAgICAgdGhyb3cgbmV3IEV4cG9ydEVycm9yKCdbYnJvd3Nlcl0gQ2Fubm90IGZpbmQgYSBicm93c2VyIHRvIG9wZW4uJyk7XHJcbiAgICB9XHJcbiAgfVxyXG5cclxuICAvLyBSZXR1cm4gYSBicm93c2VyIHByb21pc2VcclxuICByZXR1cm4gYnJvd3NlcjtcclxufTtcclxuXHJcbi8qKlxyXG4gKiBSZXRyaWV2ZXMgdGhlIGV4aXN0aW5nIFB1cHBldGVlciBicm93c2VyIGluc3RhbmNlLlxyXG4gKlxyXG4gKiBAcmV0dXJucyB7UHJvbWlzZTxvYmplY3Q+fSBBIFByb21pc2UgcmVzb2x2aW5nIHRvIHRoZSBQdXBwZXRlZXIgYnJvd3NlclxyXG4gKiBpbnN0YW5jZS5cclxuICpcclxuICogQHRocm93cyB7RXhwb3J0RXJyb3J9IFRocm93cyBhbiBFeHBvcnRFcnJvciBpZiBubyB2YWxpZCBicm93c2VyIGhhcyBiZWVuXHJcbiAqIGNyZWF0ZWQuXHJcbiAqL1xyXG5leHBvcnQgY29uc3QgZ2V0ID0gYXN5bmMgKCkgPT4ge1xyXG4gIGlmICghYnJvd3Nlcikge1xyXG4gICAgdGhyb3cgbmV3IEV4cG9ydEVycm9yKCdbYnJvd3Nlcl0gTm8gdmFsaWQgYnJvd3NlciBoYXMgYmVlbiBjcmVhdGVkLicpO1xyXG4gIH1cclxuXHJcbiAgcmV0dXJuIGJyb3dzZXI7XHJcbn07XHJcblxyXG4vKipcclxuICogQ2xvc2VzIHRoZSBQdXBwZXRlZXIgYnJvd3NlciBpbnN0YW5jZSBpZiBpdCBpcyBjb25uZWN0ZWQuXHJcbiAqXHJcbiAqIEByZXR1cm5zIHtQcm9taXNlPGJvb2xlYW4+fSBBIFByb21pc2UgcmVzb2x2aW5nIHRvIHRydWUgYWZ0ZXIgdGhlIGJyb3dzZXJcclxuICogaXMgY2xvc2VkLlxyXG4gKi9cclxuZXhwb3J0IGNvbnN0IGNsb3NlID0gYXN5bmMgKCkgPT4ge1xyXG4gIC8vIENsb3NlIHRoZSBicm93c2VyIHdoZW4gY29ubm5lY3RlZFxyXG4gIGlmIChicm93c2VyPy5pc0Nvbm5lY3RlZCgpKSB7XHJcbiAgICBhd2FpdCBicm93c2VyLmNsb3NlKCk7XHJcbiAgICBsb2coNCwgJ1ticm93c2VyXSBDbG9zZWQgdGhlIGJyb3dzZXIuJyk7XHJcbiAgfVxyXG4gIHJldHVybiB0cnVlO1xyXG59O1xyXG5cclxuZXhwb3J0IGRlZmF1bHQge1xyXG4gIG5ld1BhZ2UsXHJcbiAgY2xlYXJQYWdlLFxyXG4gIGdldCxcclxuICBjbG9zZVxyXG59O1xyXG4iLCIvKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKlxyXG5cclxuSGlnaGNoYXJ0cyBFeHBvcnQgU2VydmVyXHJcblxyXG5Db3B5cmlnaHQgKGMpIDIwMTYtMjAyNCwgSGlnaHNvZnRcclxuXHJcbkxpY2VuY2VkIHVuZGVyIHRoZSBNSVQgbGljZW5jZS5cclxuXHJcbkFkZGl0aW9uYWxseSBhIHZhbGlkIEhpZ2hjaGFydHMgbGljZW5zZSBpcyByZXF1aXJlZCBmb3IgdXNlLlxyXG5cclxuU2VlIExJQ0VOU0UgZmlsZSBpbiByb290IGZvciBkZXRhaWxzLlxyXG5cclxuKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKi9cclxuXHJcbmltcG9ydCB7IHJlYWRGaWxlU3luYyB9IGZyb20gJ2ZzJztcclxuaW1wb3J0IHBhdGggZnJvbSAncGF0aCc7XHJcbmltcG9ydCAqIGFzIHVybCBmcm9tICd1cmwnO1xyXG5cclxuaW1wb3J0IGNhY2hlIGZyb20gJy4vY2FjaGUuanMnO1xyXG5pbXBvcnQgeyBsb2csIGxvZ1dpdGhTdGFjayB9IGZyb20gJy4vbG9nZ2VyLmpzJztcclxuaW1wb3J0IHN2Z1RlbXBsYXRlIGZyb20gJy4vLi4vdGVtcGxhdGVzL3N2Z19leHBvcnQvc3ZnX2V4cG9ydC5qcyc7XHJcblxyXG5pbXBvcnQgRXhwb3J0RXJyb3IgZnJvbSAnLi9lcnJvcnMvRXhwb3J0RXJyb3IuanMnO1xyXG5cclxuY29uc3QgX19iYXNlZGlyID0gdXJsLmZpbGVVUkxUb1BhdGgobmV3IFVSTCgnLicsIGltcG9ydC5tZXRhLnVybCkpO1xyXG5cclxuLyoqXHJcbiAqIFJldHJpZXZlcyB0aGUgY2xpcHBpbmcgcmVnaW9uIGNvb3JkaW5hdGVzIG9mIHRoZSBzcGVjaWZpZWQgcGFnZSBlbGVtZW50IHdpdGhcclxuICogdGhlIGlkICdjaGFydC1jb250YWluZXInLlxyXG4gKlxyXG4gKiBAcGFyYW0ge09iamVjdH0gcGFnZSAtIFB1cHBldGVlciBwYWdlIG9iamVjdC5cclxuICpcclxuICogQHJldHVybnMge1Byb21pc2U8T2JqZWN0Pn0gUHJvbWlzZSByZXNvbHZpbmcgdG8gYW4gb2JqZWN0IGNvbnRhaW5pbmdcclxuICogeCwgeSwgd2lkdGgsIGFuZCBoZWlnaHQgcHJvcGVydGllcy5cclxuICovXHJcbmNvbnN0IGdldENsaXBSZWdpb24gPSAocGFnZSkgPT5cclxuICBwYWdlLiRldmFsKCcjY2hhcnQtY29udGFpbmVyJywgKGVsZW1lbnQpID0+IHtcclxuICAgIGNvbnN0IHsgeCwgeSwgd2lkdGgsIGhlaWdodCB9ID0gZWxlbWVudC5nZXRCb3VuZGluZ0NsaWVudFJlY3QoKTtcclxuICAgIHJldHVybiB7XHJcbiAgICAgIHgsXHJcbiAgICAgIHksXHJcbiAgICAgIHdpZHRoLFxyXG4gICAgICBoZWlnaHQ6IE1hdGgudHJ1bmMoaGVpZ2h0ID4gMSA/IGhlaWdodCA6IDUwMClcclxuICAgIH07XHJcbiAgfSk7XHJcblxyXG4vKipcclxuICogQ3JlYXRlcyBhbiBpbWFnZSB1c2luZyBQdXBwZXRlZXIncyBwYWdlIHNjcmVlbnNob3QgZnVuY3Rpb25hbGl0eSB3aXRoXHJcbiAqIHNwZWNpZmllZCBvcHRpb25zLlxyXG4gKlxyXG4gKiBAcGFyYW0ge09iamVjdH0gcGFnZSAtIFB1cHBldGVlciBwYWdlIG9iamVjdC5cclxuICogQHBhcmFtIHtzdHJpbmd9IHR5cGUgLSBJbWFnZSB0eXBlLlxyXG4gKiBAcGFyYW0ge3N0cmluZ30gZW5jb2RpbmcgLSBJbWFnZSBlbmNvZGluZy5cclxuICogQHBhcmFtIHtPYmplY3R9IGNsaXAgLSBDbGlwcGluZyByZWdpb24gY29vcmRpbmF0ZXMuXHJcbiAqIEBwYXJhbSB7bnVtYmVyfSByYXN0ZXJpemF0aW9uVGltZW91dCAtIFRpbWVvdXQgZm9yIHJhc3Rlcml6YXRpb25cclxuICogaW4gbWlsbGlzZWNvbmRzLlxyXG4gKlxyXG4gKiBAcmV0dXJucyB7UHJvbWlzZTxCdWZmZXI+fSBQcm9taXNlIHJlc29sdmluZyB0byB0aGUgaW1hZ2UgYnVmZmVyIG9yIHJlamVjdGluZ1xyXG4gKiB3aXRoIGFuIEV4cG9ydEVycm9yIGZvciB0aW1lb3V0LlxyXG4gKi9cclxuY29uc3QgY3JlYXRlSW1hZ2UgPSAocGFnZSwgdHlwZSwgZW5jb2RpbmcsIGNsaXAsIHJhc3Rlcml6YXRpb25UaW1lb3V0KSA9PlxyXG4gIFByb21pc2UucmFjZShbXHJcbiAgICBwYWdlLnNjcmVlbnNob3Qoe1xyXG4gICAgICB0eXBlLFxyXG4gICAgICBlbmNvZGluZyxcclxuICAgICAgY2xpcCxcclxuXHJcbiAgICAgIC8vICM0NDcsICM0NjMgLSBhbHdheXMgcmVuZGVyIG9uIGEgdHJhbnNwYXJlbnQgcGFnZSBpZiB0aGUgZXhwZWN0ZWQgdHlwZVxyXG4gICAgICAvLyBmb3JtYXQgaXMgUE5HXHJcbiAgICAgIG9taXRCYWNrZ3JvdW5kOiB0eXBlID09ICdwbmcnXHJcbiAgICB9KSxcclxuICAgIG5ldyBQcm9taXNlKChfcmVzb2x2ZSwgcmVqZWN0KSA9PlxyXG4gICAgICBzZXRUaW1lb3V0KFxyXG4gICAgICAgICgpID0+IHJlamVjdChuZXcgRXhwb3J0RXJyb3IoJ1Jhc3Rlcml6YXRpb24gdGltZW91dCcpKSxcclxuICAgICAgICByYXN0ZXJpemF0aW9uVGltZW91dCB8fCAxNTAwXHJcbiAgICAgIClcclxuICAgIClcclxuICBdKTtcclxuXHJcbi8qKlxyXG4gKiBDcmVhdGVzIGEgUERGIHVzaW5nIFB1cHBldGVlcidzIHBhZ2UgcGRmIGZ1bmN0aW9uYWxpdHkgd2l0aCBzcGVjaWZpZWRcclxuICogb3B0aW9ucy5cclxuICpcclxuICogQHBhcmFtIHtPYmplY3R9IHBhZ2UgLSBQdXBwZXRlZXIgcGFnZSBvYmplY3QuXHJcbiAqIEBwYXJhbSB7bnVtYmVyfSBoZWlnaHQgLSBQREYgaGVpZ2h0LlxyXG4gKiBAcGFyYW0ge251bWJlcn0gd2lkdGggLSBQREYgd2lkdGguXHJcbiAqIEBwYXJhbSB7c3RyaW5nfSBlbmNvZGluZyAtIFBERiBlbmNvZGluZy5cclxuICpcclxuICogQHJldHVybnMge1Byb21pc2U8QnVmZmVyPn0gUHJvbWlzZSByZXNvbHZpbmcgdG8gdGhlIFBERiBidWZmZXIuXHJcbiAqL1xyXG5jb25zdCBjcmVhdGVQREYgPSAocGFnZSwgaGVpZ2h0LCB3aWR0aCwgZW5jb2RpbmcpID0+XHJcbiAgcGFnZS5wZGYoe1xyXG4gICAgLy8gVGhpcyB3aWxsIHJlbW92ZSBhbiBleHRyYSBlbXB0eSBwYWdlIGluIFBERiBleHBvcnRzXHJcbiAgICBoZWlnaHQ6IGhlaWdodCArIDEsXHJcbiAgICB3aWR0aCxcclxuICAgIGVuY29kaW5nXHJcbiAgfSk7XHJcblxyXG4vKipcclxuICogQ3JlYXRlcyBhbiBTVkcgc3RyaW5nIGJ5IGV2YWx1YXRpbmcgdGhlIG91dGVySFRNTCBvZiB0aGUgZmlyc3QgJ3N2ZycgZWxlbWVudFxyXG4gKiBpbnNpZGUgYW4gZWxlbWVudCB3aXRoIHRoZSBpZCAnY29udGFpbmVyJy5cclxuICpcclxuICogQHBhcmFtIHtPYmplY3R9IHBhZ2UgLSBQdXBwZXRlZXIgcGFnZSBvYmplY3QuXHJcbiAqXHJcbiAqIEByZXR1cm5zIHtQcm9taXNlPHN0cmluZz59IFByb21pc2UgcmVzb2x2aW5nIHRvIHRoZSBTVkcgc3RyaW5nLlxyXG4gKi9cclxuY29uc3QgY3JlYXRlU1ZHID0gKHBhZ2UpID0+XHJcbiAgcGFnZS4kZXZhbCgnI2NvbnRhaW5lciBzdmc6Zmlyc3Qtb2YtdHlwZScsIChlbGVtZW50KSA9PiBlbGVtZW50Lm91dGVySFRNTCk7XHJcblxyXG4vKipcclxuICogU2V0cyB0aGUgc3BlY2lmaWVkIGNoYXJ0IGFuZCBvcHRpb25zIGFzIGNvbmZpZ3VyYXRpb24gaW50byB0aGUgdHJpZ2dlckV4cG9ydFxyXG4gKiBmdW5jdGlvbiB3aXRoaW4gdGhlIHdpbmRvdyBjb250ZXh0IHVzaW5nIHBhZ2UuZXZhbHVhdGUuXHJcbiAqXHJcbiAqIEBwYXJhbSB7T2JqZWN0fSBwYWdlIC0gUHVwcGV0ZWVyIHBhZ2Ugb2JqZWN0LlxyXG4gKiBAcGFyYW0ge2FueX0gY2hhcnQgLSBUaGUgY2hhcnQgb2JqZWN0IHRvIGJlIGNvbmZpZ3VyZWQuXHJcbiAqIEBwYXJhbSB7T2JqZWN0fSBvcHRpb25zIC0gQ29uZmlndXJhdGlvbiBvcHRpb25zIGZvciB0aGUgY2hhcnQuXHJcbiAqXHJcbiAqIEByZXR1cm5zIHtQcm9taXNlPHZvaWQ+fSBQcm9taXNlIHJlc29sdmluZyBhZnRlciB0aGUgY29uZmlndXJhdGlvbiBpcyBzZXQuXHJcbiAqL1xyXG5jb25zdCBzZXRBc0NvbmZpZyA9IChwYWdlLCBjaGFydCwgb3B0aW9ucykgPT5cclxuICBwYWdlLmV2YWx1YXRlKFxyXG4gICAgLy8gZXNsaW50LWRpc2FibGUtbmV4dC1saW5lIG5vLXVuZGVmXHJcbiAgICAoY2hhcnQsIG9wdGlvbnMpID0+IHdpbmRvdy50cmlnZ2VyRXhwb3J0KGNoYXJ0LCBvcHRpb25zKSxcclxuICAgIGNoYXJ0LFxyXG4gICAgb3B0aW9uc1xyXG4gICk7XHJcblxyXG4vKipcclxuICogRXhwb3J0cyB0byBhIGNoYXJ0IGZyb20gYSBwYWdlIHVzaW5nIFB1cHBldGVlci5cclxuICpcclxuICogQHBhcmFtIHtPYmplY3R9IHBhZ2UgLSBQdXBwZXRlZXIgcGFnZSBvYmplY3QuXHJcbiAqIEBwYXJhbSB7YW55fSBjaGFydCAtIFRoZSBjaGFydCBvYmplY3Qgb3IgU1ZHIGNvbmZpZ3VyYXRpb24gdG8gYmUgZXhwb3J0ZWQuXHJcbiAqIEBwYXJhbSB7T2JqZWN0fSBvcHRpb25zIC0gRXhwb3J0IG9wdGlvbnMgYW5kIGNvbmZpZ3VyYXRpb24uXHJcbiAqXHJcbiAqIEByZXR1cm5zIHtQcm9taXNlPHN0cmluZyB8IEJ1ZmZlciB8IEV4cG9ydEVycm9yPn0gUHJvbWlzZSByZXNvbHZpbmcgdG9cclxuICogdGhlIGV4cG9ydGVkIGRhdGEgb3IgcmVqZWN0aW5nIHdpdGggYW4gRXhwb3J0RXJyb3IuXHJcbiAqL1xyXG5leHBvcnQgZGVmYXVsdCBhc3luYyAocGFnZSwgY2hhcnQsIG9wdGlvbnMpID0+IHtcclxuICAvKipcclxuICAgKiBLZWVwcyB0cmFjayBvZiBhbGwgcmVzb3VyY2VzIGFkZGVkIG9uIHRoZSBwYWdlIHdpdGggYWRkWFhYVGFnLiBldGNcclxuICAgKiBJdCdzIFZJVEFMIHRoYXQgYWxsIGFkZGVkIHJlc291cmNlcyBlbmRzIHVwIGhlcmUgc28gd2UgY2FuIGNsZWFyIHRoaW5nc1xyXG4gICAqIG91dCB3aGVuIGRvaW5nIGEgbmV3IGV4cG9ydCBpbiB0aGUgc2FtZSBwYWdlIVxyXG4gICAqL1xyXG4gIGNvbnN0IGluamVjdGVkUmVzb3VyY2VzID0gW107XHJcblxyXG4gIC8qKiBDbGVhciBvdXQgYWxsIHN0YXRlIHNldCBvbiB0aGUgcGFnZSB3aXRoIGFkZFNjcmlwdFRhZy9hZGRTdHlsZVRhZy4gKi9cclxuICBjb25zdCBjbGVhckluamVjdGVkID0gYXN5bmMgKHBhZ2UpID0+IHtcclxuICAgIGZvciAoY29uc3QgcmVzIG9mIGluamVjdGVkUmVzb3VyY2VzKSB7XHJcbiAgICAgIGF3YWl0IHJlcy5kaXNwb3NlKCk7XHJcbiAgICB9XHJcblxyXG4gICAgLy8gUmVzZXQgYWxsIENTUyBhbmQgc2NyaXB0IHRhZ3NcclxuICAgIGF3YWl0IHBhZ2UuZXZhbHVhdGUoKCkgPT4ge1xyXG4gICAgICAvLyBlc2xpbnQtZGlzYWJsZS1uZXh0LWxpbmUgbm8tdW5kZWZcclxuICAgICAgY29uc3QgWywgLi4uc2NyaXB0c1RvUmVtb3ZlXSA9IGRvY3VtZW50LmdldEVsZW1lbnRzQnlUYWdOYW1lKCdzY3JpcHQnKTtcclxuICAgICAgLy8gZXNsaW50LWRpc2FibGUtbmV4dC1saW5lIG5vLXVuZGVmXHJcbiAgICAgIGNvbnN0IFssIC4uLnN0eWxlc1RvUmVtb3ZlXSA9IGRvY3VtZW50LmdldEVsZW1lbnRzQnlUYWdOYW1lKCdzdHlsZScpO1xyXG4gICAgICAvLyBlc2xpbnQtZGlzYWJsZS1uZXh0LWxpbmUgbm8tdW5kZWZcclxuICAgICAgY29uc3QgWy4uLmxpbmtzVG9SZW1vdmVdID0gZG9jdW1lbnQuZ2V0RWxlbWVudHNCeVRhZ05hbWUoJ2xpbmsnKTtcclxuXHJcbiAgICAgIC8vIFJlbW92ZSB0YWdzXHJcbiAgICAgIGZvciAoY29uc3QgZWxlbWVudCBvZiBbXHJcbiAgICAgICAgLi4uc2NyaXB0c1RvUmVtb3ZlLFxyXG4gICAgICAgIC4uLnN0eWxlc1RvUmVtb3ZlLFxyXG4gICAgICAgIC4uLmxpbmtzVG9SZW1vdmVcclxuICAgICAgXSkge1xyXG4gICAgICAgIGVsZW1lbnQucmVtb3ZlKCk7XHJcbiAgICAgIH1cclxuICAgIH0pO1xyXG4gIH07XHJcblxyXG4gIHRyeSB7XHJcbiAgICBsb2coNCwgJ1tleHBvcnRdIERldGVybWluaW5nIGV4cG9ydCBwYXRoLicpO1xyXG5cclxuICAgIGNvbnN0IGV4cG9ydE9wdGlvbnMgPSBvcHRpb25zLmV4cG9ydDtcclxuXHJcbiAgICAvLyBGb3JjZSBhIHJBRlxyXG4gICAgLy8gU2VlIGh0dHBzOi8vZ2l0aHViLmNvbS9wdXBwZXRlZXIvcHVwcGV0ZWVyL2lzc3Vlcy83NTA3XHJcbiAgICAvLyBlc2xpbnQtZGlzYWJsZS1uZXh0LWxpbmUgbm8tdW5kZWZcclxuICAgIGF3YWl0IHBhZ2UuZXZhbHVhdGUoKCkgPT4gcmVxdWVzdEFuaW1hdGlvbkZyYW1lKCgpID0+IHt9KSk7XHJcblxyXG4gICAgLy8gRGVjaWRlIHdoZXRoZXIgZGlzcGxheSBlcnJvciBvciBkZWJidWdlciB3cmFwcGVyIGFyb3VuZCBpdFxyXG4gICAgY29uc3QgZGlzcGxheUVycm9ycyA9XHJcbiAgICAgIGV4cG9ydE9wdGlvbnM/Lm9wdGlvbnM/LmNoYXJ0Py5kaXNwbGF5RXJyb3JzICYmXHJcbiAgICAgIGNhY2hlLmdldENhY2hlKCkuYWN0aXZlTWFuaWZlc3QubW9kdWxlcy5kZWJ1Z2dlcjtcclxuXHJcbiAgICAvLyBlc2xpbnQtZGlzYWJsZS1uZXh0LWxpbmUgbm8tdW5kZWZcclxuICAgIGF3YWl0IHBhZ2UuZXZhbHVhdGUoKGQpID0+ICh3aW5kb3cuX2Rpc3BsYXlFcnJvcnMgPSBkKSwgZGlzcGxheUVycm9ycyk7XHJcblxyXG4gICAgbGV0IGlzU1ZHO1xyXG4gICAgaWYgKFxyXG4gICAgICBjaGFydC5pbmRleE9mICYmXHJcbiAgICAgIChjaGFydC5pbmRleE9mKCc8c3ZnJykgPj0gMCB8fCBjaGFydC5pbmRleE9mKCc8P3htbCcpID49IDApXHJcbiAgICApIHtcclxuICAgICAgLy8gU1ZHIGlucHV0IGhhbmRsaW5nXHJcbiAgICAgIGxvZyg0LCAnW2V4cG9ydF0gVHJlYXRpbmcgYXMgU1ZHLicpO1xyXG5cclxuICAgICAgLy8gSWYgaW5wdXQgaXMgYWxzbyBTVkcsIGp1c3QgcmV0dXJuIGl0XHJcbiAgICAgIGlmIChleHBvcnRPcHRpb25zLnR5cGUgPT09ICdzdmcnKSB7XHJcbiAgICAgICAgcmV0dXJuIGNoYXJ0O1xyXG4gICAgICB9XHJcblxyXG4gICAgICBpc1NWRyA9IHRydWU7XHJcbiAgICAgIGF3YWl0IHBhZ2Uuc2V0Q29udGVudChzdmdUZW1wbGF0ZShjaGFydCkpO1xyXG4gICAgfSBlbHNlIHtcclxuICAgICAgLy8gSlNPTiBjb25maWcgaGFuZGxpbmdcclxuICAgICAgbG9nKDQsICdbZXhwb3J0XSBUcmVhdGluZyBhcyBjb25maWcuJyk7XHJcblxyXG4gICAgICAvLyBOZWVkIHRvIHBlcmZvcm0gc3RyYWlnaHQgaW5qZWN0XHJcbiAgICAgIGlmIChleHBvcnRPcHRpb25zLnN0ckluaikge1xyXG4gICAgICAgIC8vIEluamVjdGlvbiBiYXNlZCBjb25maWd1cmF0aW9uIGV4cG9ydFxyXG4gICAgICAgIGF3YWl0IHNldEFzQ29uZmlnKFxyXG4gICAgICAgICAgcGFnZSxcclxuICAgICAgICAgIHtcclxuICAgICAgICAgICAgY2hhcnQ6IHtcclxuICAgICAgICAgICAgICBoZWlnaHQ6IGV4cG9ydE9wdGlvbnMuaGVpZ2h0LFxyXG4gICAgICAgICAgICAgIHdpZHRoOiBleHBvcnRPcHRpb25zLndpZHRoXHJcbiAgICAgICAgICAgIH1cclxuICAgICAgICAgIH0sXHJcbiAgICAgICAgICBvcHRpb25zXHJcbiAgICAgICAgKTtcclxuICAgICAgfSBlbHNlIHtcclxuICAgICAgICAvLyBCYXNpYyBjb25maWd1cmF0aW9uIGV4cG9ydFxyXG4gICAgICAgIGNoYXJ0LmNoYXJ0LmhlaWdodCA9IGV4cG9ydE9wdGlvbnMuaGVpZ2h0O1xyXG4gICAgICAgIGNoYXJ0LmNoYXJ0LndpZHRoID0gZXhwb3J0T3B0aW9ucy53aWR0aDtcclxuXHJcbiAgICAgICAgYXdhaXQgc2V0QXNDb25maWcocGFnZSwgY2hhcnQsIG9wdGlvbnMpO1xyXG4gICAgICB9XHJcbiAgICB9XHJcblxyXG4gICAgLy8gVXNlIHJlc291cmNlc1xyXG4gICAgY29uc3QgcmVzb3VyY2VzID0gb3B0aW9ucy5jdXN0b21Mb2dpYy5yZXNvdXJjZXM7XHJcbiAgICBpZiAocmVzb3VyY2VzKSB7XHJcbiAgICAgIC8vIExvYWQgY3VzdG9tIEpTIGNvZGVcclxuICAgICAgaWYgKHJlc291cmNlcy5qcykge1xyXG4gICAgICAgIGluamVjdGVkUmVzb3VyY2VzLnB1c2goXHJcbiAgICAgICAgICBhd2FpdCBwYWdlLmFkZFNjcmlwdFRhZyh7XHJcbiAgICAgICAgICAgIGNvbnRlbnQ6IHJlc291cmNlcy5qc1xyXG4gICAgICAgICAgfSlcclxuICAgICAgICApO1xyXG4gICAgICB9XHJcblxyXG4gICAgICAvLyBMb2FkIHNjcmlwdHMgZnJvbSBhbGwgY3VzdG9tIGZpbGVzXHJcbiAgICAgIGlmIChyZXNvdXJjZXMuZmlsZXMpIHtcclxuICAgICAgICBmb3IgKGNvbnN0IGZpbGUgb2YgcmVzb3VyY2VzLmZpbGVzKSB7XHJcbiAgICAgICAgICB0cnkge1xyXG4gICAgICAgICAgICBjb25zdCBpc0xvY2FsID0gIWZpbGUuc3RhcnRzV2l0aCgnaHR0cCcpID8gdHJ1ZSA6IGZhbHNlO1xyXG5cclxuICAgICAgICAgICAgLy8gQWRkIGVhY2ggY3VzdG9tIHNjcmlwdCBmcm9tIHJlc291cmNlcycgZmlsZXNcclxuICAgICAgICAgICAgaW5qZWN0ZWRSZXNvdXJjZXMucHVzaChcclxuICAgICAgICAgICAgICBhd2FpdCBwYWdlLmFkZFNjcmlwdFRhZyhcclxuICAgICAgICAgICAgICAgIGlzTG9jYWxcclxuICAgICAgICAgICAgICAgICAgPyB7XHJcbiAgICAgICAgICAgICAgICAgICAgICBjb250ZW50OiByZWFkRmlsZVN5bmMoZmlsZSwgJ3V0ZjgnKVxyXG4gICAgICAgICAgICAgICAgICAgIH1cclxuICAgICAgICAgICAgICAgICAgOiB7XHJcbiAgICAgICAgICAgICAgICAgICAgICB1cmw6IGZpbGVcclxuICAgICAgICAgICAgICAgICAgICB9XHJcbiAgICAgICAgICAgICAgKVxyXG4gICAgICAgICAgICApO1xyXG4gICAgICAgICAgfSBjYXRjaCAoZXJyb3IpIHtcclxuICAgICAgICAgICAgbG9nV2l0aFN0YWNrKFxyXG4gICAgICAgICAgICAgIDIsXHJcbiAgICAgICAgICAgICAgZXJyb3IsXHJcbiAgICAgICAgICAgICAgYFtleHBvcnRdIFRoZSBKUyBmaWxlICR7ZmlsZX0gY2Fubm90IGJlIGxvYWRlZC5gXHJcbiAgICAgICAgICAgICk7XHJcbiAgICAgICAgICB9XHJcbiAgICAgICAgfVxyXG4gICAgICB9XHJcblxyXG4gICAgICAvLyBMb2FkIENTU1xyXG4gICAgICBpZiAocmVzb3VyY2VzLmNzcykge1xyXG4gICAgICAgIGxldCBjc3NJbXBvcnRzID0gcmVzb3VyY2VzLmNzcy5tYXRjaCgvQGltcG9ydFxccyooW147XSopOy9nKTtcclxuICAgICAgICBpZiAoY3NzSW1wb3J0cykge1xyXG4gICAgICAgICAgLy8gSGFuZGxlIGNzcyBzZWN0aW9uXHJcbiAgICAgICAgICBmb3IgKGxldCBjc3NJbXBvcnRQYXRoIG9mIGNzc0ltcG9ydHMpIHtcclxuICAgICAgICAgICAgaWYgKGNzc0ltcG9ydFBhdGgpIHtcclxuICAgICAgICAgICAgICBjc3NJbXBvcnRQYXRoID0gY3NzSW1wb3J0UGF0aFxyXG4gICAgICAgICAgICAgICAgLnJlcGxhY2UoJ3VybCgnLCAnJylcclxuICAgICAgICAgICAgICAgIC5yZXBsYWNlKCdAaW1wb3J0JywgJycpXHJcbiAgICAgICAgICAgICAgICAucmVwbGFjZSgvXCIvZywgJycpXHJcbiAgICAgICAgICAgICAgICAucmVwbGFjZSgvJy9nLCAnJylcclxuICAgICAgICAgICAgICAgIC5yZXBsYWNlKC87LywgJycpXHJcbiAgICAgICAgICAgICAgICAucmVwbGFjZSgvXFwpL2csICcnKVxyXG4gICAgICAgICAgICAgICAgLnRyaW0oKTtcclxuXHJcbiAgICAgICAgICAgICAgLy8gQWRkIGVhY2ggY3VzdG9tIGNzcyBmcm9tIHJlc291cmNlc1xyXG4gICAgICAgICAgICAgIGlmIChjc3NJbXBvcnRQYXRoLnN0YXJ0c1dpdGgoJ2h0dHAnKSkge1xyXG4gICAgICAgICAgICAgICAgaW5qZWN0ZWRSZXNvdXJjZXMucHVzaChcclxuICAgICAgICAgICAgICAgICAgYXdhaXQgcGFnZS5hZGRTdHlsZVRhZyh7XHJcbiAgICAgICAgICAgICAgICAgICAgdXJsOiBjc3NJbXBvcnRQYXRoXHJcbiAgICAgICAgICAgICAgICAgIH0pXHJcbiAgICAgICAgICAgICAgICApO1xyXG4gICAgICAgICAgICAgIH0gZWxzZSBpZiAob3B0aW9ucy5jdXN0b21Mb2dpYy5hbGxvd0ZpbGVSZXNvdXJjZXMpIHtcclxuICAgICAgICAgICAgICAgIGluamVjdGVkUmVzb3VyY2VzLnB1c2goXHJcbiAgICAgICAgICAgICAgICAgIGF3YWl0IHBhZ2UuYWRkU3R5bGVUYWcoe1xyXG4gICAgICAgICAgICAgICAgICAgIHBhdGg6IHBhdGguam9pbihfX2Jhc2VkaXIsIGNzc0ltcG9ydFBhdGgpXHJcbiAgICAgICAgICAgICAgICAgIH0pXHJcbiAgICAgICAgICAgICAgICApO1xyXG4gICAgICAgICAgICAgIH1cclxuICAgICAgICAgICAgfVxyXG4gICAgICAgICAgfVxyXG4gICAgICAgIH1cclxuXHJcbiAgICAgICAgLy8gVGhlIHJlc3Qgb2YgdGhlIENTUyBzZWN0aW9uIHdpbGwgYmUgY29udGVudCBieSBub3dcclxuICAgICAgICBpbmplY3RlZFJlc291cmNlcy5wdXNoKFxyXG4gICAgICAgICAgYXdhaXQgcGFnZS5hZGRTdHlsZVRhZyh7XHJcbiAgICAgICAgICAgIGNvbnRlbnQ6IHJlc291cmNlcy5jc3MucmVwbGFjZSgvQGltcG9ydFxccyooW147XSopOy9nLCAnJykgfHwgJyAnXHJcbiAgICAgICAgICB9KVxyXG4gICAgICAgICk7XHJcbiAgICAgIH1cclxuICAgIH1cclxuXHJcbiAgICAvLyBHZXQgdGhlIHJlYWwgY2hhcnQgc2l6ZVxyXG4gICAgY29uc3Qgc2l6ZSA9IGlzU1ZHXHJcbiAgICAgID8gYXdhaXQgcGFnZS4kZXZhbChcclxuICAgICAgICAgICcjY2hhcnQtY29udGFpbmVyIHN2ZzpmaXJzdC1vZi10eXBlJyxcclxuICAgICAgICAgIChlbGVtZW50LCBzY2FsZSkgPT4gKHtcclxuICAgICAgICAgICAgY2hhcnRIZWlnaHQ6IGVsZW1lbnQuaGVpZ2h0LmJhc2VWYWwudmFsdWUgKiBzY2FsZSxcclxuICAgICAgICAgICAgY2hhcnRXaWR0aDogZWxlbWVudC53aWR0aC5iYXNlVmFsLnZhbHVlICogc2NhbGVcclxuICAgICAgICAgIH0pLFxyXG4gICAgICAgICAgcGFyc2VGbG9hdChleHBvcnRPcHRpb25zLnNjYWxlKVxyXG4gICAgICAgIClcclxuICAgICAgOiBhd2FpdCBwYWdlLmV2YWx1YXRlKCgpID0+IHtcclxuICAgICAgICAgIC8vIGVzbGludC1kaXNhYmxlLW5leHQtbGluZSBuby11bmRlZlxyXG4gICAgICAgICAgY29uc3QgeyBjaGFydEhlaWdodCwgY2hhcnRXaWR0aCB9ID0gd2luZG93LkhpZ2hjaGFydHMuY2hhcnRzWzBdO1xyXG4gICAgICAgICAgcmV0dXJuIHtcclxuICAgICAgICAgICAgY2hhcnRIZWlnaHQsXHJcbiAgICAgICAgICAgIGNoYXJ0V2lkdGhcclxuICAgICAgICAgIH07XHJcbiAgICAgICAgfSk7XHJcblxyXG4gICAgLy8gU2V0IGZpbmFsIGhlaWdodCBhbmQgd2lkdGggZm9yIHZpZXdwb3J0XHJcbiAgICBjb25zdCB2aWV3cG9ydEhlaWdodCA9IE1hdGguY2VpbChzaXplPy5jaGFydEhlaWdodCB8fCBleHBvcnRPcHRpb25zLmhlaWdodCk7XHJcbiAgICBjb25zdCB2aWV3cG9ydFdpZHRoID0gTWF0aC5jZWlsKHNpemU/LmNoYXJ0V2lkdGggfHwgZXhwb3J0T3B0aW9ucy53aWR0aCk7XHJcblxyXG4gICAgLy8gU2V0IHRoZSB2aWV3cG9ydCBmb3IgdGhlIGZpcnN0IHRpbWVcclxuICAgIC8vIE5PVEU6IHRoZSBjYWxsIHRvIHNldFZpZXdwb3J0IGlzIGV4cGVuc2l2ZSAtIGNhbiB3ZSBnZXQgYXdheSB3aXRoIG9ubHlcclxuICAgIC8vIGNhbGxpbmcgaXQgb25jZSwgZS5nLiBtb3ZpbmcgdGhpcyBvbmUgaW50byB0aGUgaXNTVkcgY29uZGl0aW9uIGJlbG93P1xyXG4gICAgYXdhaXQgcGFnZS5zZXRWaWV3cG9ydCh7XHJcbiAgICAgIGhlaWdodDogdmlld3BvcnRIZWlnaHQsXHJcbiAgICAgIHdpZHRoOiB2aWV3cG9ydFdpZHRoLFxyXG4gICAgICBkZXZpY2VTY2FsZUZhY3RvcjogaXNTVkcgPyAxIDogcGFyc2VGbG9hdChleHBvcnRPcHRpb25zLnNjYWxlKVxyXG4gICAgfSk7XHJcblxyXG4gICAgLy8gUHJlcGFyZSBhIHpvb20gY2FsbGJhY2sgZm9yIHRoZSBuZXh0IGV2YWx1YXRlIGNhbGxcclxuICAgIGNvbnN0IHpvb21DYWxsYmFjayA9IGlzU1ZHXHJcbiAgICAgID8gLy8gSW4gY2FzZSBvZiBTVkcgdGhlIHpvb20gbXVzdCBiZSBzZXQgZGlyZWN0bHkgZm9yIGJvZHlcclxuICAgICAgICAoc2NhbGUpID0+IHtcclxuICAgICAgICAgIC8vIFNldCB0aGUgem9vbSBhcyBzY2FsZVxyXG4gICAgICAgICAgLy8gZXNsaW50LWRpc2FibGUtbmV4dC1saW5lIG5vLXVuZGVmXHJcbiAgICAgICAgICBkb2N1bWVudC5ib2R5LnN0eWxlLnpvb20gPSBzY2FsZTtcclxuXHJcbiAgICAgICAgICAvLyBTZXQgdGhlIG1hcmdpbiB0byAwcHhcclxuICAgICAgICAgIC8vIGVzbGludC1kaXNhYmxlLW5leHQtbGluZSBuby11bmRlZlxyXG4gICAgICAgICAgZG9jdW1lbnQuYm9keS5zdHlsZS5tYXJnaW4gPSAnMHB4JztcclxuICAgICAgICB9XHJcbiAgICAgIDogLy8gTm8gbmVlZCBmb3Igc3VjaCBzY2FsZSBtYW5pcHVsYXRpb24gaW4gY2FzZSBvZiBvdGhlciB0eXBlcyBvZiBleHBvcnRzXHJcbiAgICAgICAgKCkgPT4ge1xyXG4gICAgICAgICAgLy8gUmVzZXQgdGhlIHpvb20gZm9yIG90aGVyIGV4cG9ydHMgdGhhbiB0byBTVkdzXHJcbiAgICAgICAgICAvLyBlc2xpbnQtZGlzYWJsZS1uZXh0LWxpbmUgbm8tdW5kZWZcclxuICAgICAgICAgIGRvY3VtZW50LmJvZHkuc3R5bGUuem9vbSA9IDE7XHJcbiAgICAgICAgfTtcclxuXHJcbiAgICAvLyBTZXQgdGhlIHpvb20gYWNjb3JkaW5nbHlcclxuICAgIGF3YWl0IHBhZ2UuZXZhbHVhdGUoem9vbUNhbGxiYWNrLCBwYXJzZUZsb2F0KGV4cG9ydE9wdGlvbnMuc2NhbGUpKTtcclxuXHJcbiAgICAvLyBHZXQgdGhlIGNsaXAgcmVnaW9uIGZvciB0aGUgcGFnZVxyXG4gICAgY29uc3QgeyBoZWlnaHQsIHdpZHRoLCB4LCB5IH0gPSBhd2FpdCBnZXRDbGlwUmVnaW9uKHBhZ2UpO1xyXG5cclxuICAgIGlmICghaXNTVkcpIHtcclxuICAgICAgLy8gU2V0IHRoZSBmaW5hbCB2aWV3cG9ydCBub3cgdGhhdCB3ZSBoYXZlIHRoZSByZWFsIGhlaWdodFxyXG4gICAgICBhd2FpdCBwYWdlLnNldFZpZXdwb3J0KHtcclxuICAgICAgICB3aWR0aDogTWF0aC5yb3VuZCh3aWR0aCksXHJcbiAgICAgICAgaGVpZ2h0OiBNYXRoLnJvdW5kKGhlaWdodCksXHJcbiAgICAgICAgZGV2aWNlU2NhbGVGYWN0b3I6IHBhcnNlRmxvYXQoZXhwb3J0T3B0aW9ucy5zY2FsZSlcclxuICAgICAgfSk7XHJcbiAgICB9XHJcblxyXG4gICAgbGV0IGRhdGE7XHJcbiAgICAvLyBSQVNURVJJWkFUSU9OXHJcbiAgICBpZiAoZXhwb3J0T3B0aW9ucy50eXBlID09PSAnc3ZnJykge1xyXG4gICAgICAvLyBTVkdcclxuICAgICAgZGF0YSA9IGF3YWl0IGNyZWF0ZVNWRyhwYWdlKTtcclxuICAgIH0gZWxzZSBpZiAoWydwbmcnLCAnanBlZyddLmluY2x1ZGVzKGV4cG9ydE9wdGlvbnMudHlwZSkpIHtcclxuICAgICAgLy8gUE5HIG9yIEpQRUdcclxuICAgICAgZGF0YSA9IGF3YWl0IGNyZWF0ZUltYWdlKFxyXG4gICAgICAgIHBhZ2UsXHJcbiAgICAgICAgZXhwb3J0T3B0aW9ucy50eXBlLFxyXG4gICAgICAgICdiYXNlNjQnLFxyXG4gICAgICAgIHtcclxuICAgICAgICAgIHdpZHRoOiB2aWV3cG9ydFdpZHRoLFxyXG4gICAgICAgICAgaGVpZ2h0OiB2aWV3cG9ydEhlaWdodCxcclxuICAgICAgICAgIHgsXHJcbiAgICAgICAgICB5XHJcbiAgICAgICAgfSxcclxuICAgICAgICBleHBvcnRPcHRpb25zLnJhc3Rlcml6YXRpb25UaW1lb3V0XHJcbiAgICAgICk7XHJcbiAgICB9IGVsc2UgaWYgKGV4cG9ydE9wdGlvbnMudHlwZSA9PT0gJ3BkZicpIHtcclxuICAgICAgLy8gUERGXHJcbiAgICAgIGRhdGEgPSBhd2FpdCBjcmVhdGVQREYocGFnZSwgdmlld3BvcnRIZWlnaHQsIHZpZXdwb3J0V2lkdGgsICdiYXNlNjQnKTtcclxuICAgIH0gZWxzZSB7XHJcbiAgICAgIHRocm93IG5ldyBFeHBvcnRFcnJvcihcclxuICAgICAgICBgW2V4cG9ydF0gVW5zdXBwb3J0ZWQgb3V0cHV0IGZvcm1hdCAke2V4cG9ydE9wdGlvbnMudHlwZX0uYFxyXG4gICAgICApO1xyXG4gICAgfVxyXG5cclxuICAgIC8vIERlc3Ryb3kgb2xkIGNoYXJ0cyBhZnRlciB0aGUgZXhwb3J0IGlzIGRvbmVcclxuICAgIGF3YWl0IHBhZ2UuZXZhbHVhdGUoKCkgPT4ge1xyXG4gICAgICAvLyBXZSBhcmUgbm90IGd1YXJhbnRlZWQgdGhhdCBIaWdoY2hhcnRzIGlzIGxvYWRlZCwgZSxnLCB3aGVuIGRvaW5nIFNWR1xyXG4gICAgICAvLyBleHBvcnRzXHJcbiAgICAgIGlmICh0eXBlb2YgSGlnaGNoYXJ0cyAhPT0gJ3VuZGVmaW5lZCcpIHtcclxuICAgICAgICAvLyBlc2xpbnQtZGlzYWJsZS1uZXh0LWxpbmUgbm8tdW5kZWZcclxuICAgICAgICBjb25zdCBvbGRDaGFydHMgPSBIaWdoY2hhcnRzLmNoYXJ0cztcclxuXHJcbiAgICAgICAgLy8gQ2hlY2sgaW4gYW55IGFscmVhZHkgZXhpc3RpbmcgY2hhcnRzXHJcbiAgICAgICAgaWYgKEFycmF5LmlzQXJyYXkob2xkQ2hhcnRzKSAmJiBvbGRDaGFydHMubGVuZ3RoKSB7XHJcbiAgICAgICAgICAvLyBEZXN0cm95IG9sZCBjaGFydHNcclxuICAgICAgICAgIGZvciAoY29uc3Qgb2xkQ2hhcnQgb2Ygb2xkQ2hhcnRzKSB7XHJcbiAgICAgICAgICAgIG9sZENoYXJ0ICYmIG9sZENoYXJ0LmRlc3Ryb3koKTtcclxuICAgICAgICAgICAgLy8gZXNsaW50LWRpc2FibGUtbmV4dC1saW5lIG5vLXVuZGVmXHJcbiAgICAgICAgICAgIEhpZ2hjaGFydHMuY2hhcnRzLnNoaWZ0KCk7XHJcbiAgICAgICAgICB9XHJcbiAgICAgICAgfVxyXG4gICAgICB9XHJcbiAgICB9KTtcclxuXHJcbiAgICBhd2FpdCBjbGVhckluamVjdGVkKHBhZ2UpO1xyXG4gICAgcmV0dXJuIGRhdGE7XHJcbiAgfSBjYXRjaCAoZXJyb3IpIHtcclxuICAgIGF3YWl0IGNsZWFySW5qZWN0ZWQocGFnZSk7XHJcbiAgICByZXR1cm4gZXJyb3I7XHJcbiAgfVxyXG59O1xyXG4iLCIvKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKlxyXG5cclxuSGlnaGNoYXJ0cyBFeHBvcnQgU2VydmVyXHJcblxyXG5Db3B5cmlnaHQgKGMpIDIwMTYtMjAyNCwgSGlnaHNvZnRcclxuXHJcbkxpY2VuY2VkIHVuZGVyIHRoZSBNSVQgbGljZW5jZS5cclxuXHJcbkFkZGl0aW9uYWxseSBhIHZhbGlkIEhpZ2hjaGFydHMgbGljZW5zZSBpcyByZXF1aXJlZCBmb3IgdXNlLlxyXG5cclxuU2VlIExJQ0VOU0UgZmlsZSBpbiByb290IGZvciBkZXRhaWxzLlxyXG5cclxuKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKi9cclxuXHJcbmltcG9ydCBjc3NUZW1wbGF0ZSBmcm9tICcuL2Nzcy5qcyc7XHJcblxyXG5leHBvcnQgZGVmYXVsdCAoY2hhcnQpID0+IGBcclxuPCFET0NUWVBFIGh0bWw+XHJcbjxodG1sIGxhbmc9J2VuLVVTJz5cclxuICA8aGVhZD5cclxuICAgIDxtZXRhIGh0dHAtZXF1aXY9XCJDb250ZW50LVR5cGVcIiBjb250ZW50PVwidGV4dC9odG1sOyBjaGFyc2V0PXV0Zi04XCI+XHJcbiAgICA8dGl0bGU+SGlnaGNhcnRzIEV4cG9ydDwvdGl0bGU+XHJcbiAgPC9oZWFkPlxyXG4gIDxzdHlsZT5cclxuICAgICR7Y3NzVGVtcGxhdGUoKX1cclxuICA8L3N0eWxlPlxyXG4gIDxib2R5PlxyXG4gICAgPGRpdiBpZD1cImNoYXJ0LWNvbnRhaW5lclwiPlxyXG4gICAgICAke2NoYXJ0fVxyXG4gICAgPC9kaXY+XHJcbiAgPC9ib2R5PlxyXG48L2h0bWw+XHJcblxyXG5gO1xyXG4iLCIvKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKlxyXG5cclxuSGlnaGNoYXJ0cyBFeHBvcnQgU2VydmVyXHJcblxyXG5Db3B5cmlnaHQgKGMpIDIwMTYtMjAyNCwgSGlnaHNvZnRcclxuXHJcbkxpY2VuY2VkIHVuZGVyIHRoZSBNSVQgbGljZW5jZS5cclxuXHJcbkFkZGl0aW9uYWxseSBhIHZhbGlkIEhpZ2hjaGFydHMgbGljZW5zZSBpcyByZXF1aXJlZCBmb3IgdXNlLlxyXG5cclxuU2VlIExJQ0VOU0UgZmlsZSBpbiByb290IGZvciBkZXRhaWxzLlxyXG5cclxuKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKi9cclxuXHJcbmltcG9ydCB7IFBvb2wgfSBmcm9tICd0YXJuJztcclxuaW1wb3J0IHsgdjQgYXMgdXVpZCB9IGZyb20gJ3V1aWQnO1xyXG5cclxuaW1wb3J0IHtcclxuICBjbG9zZSBhcyBicm93c2VyQ2xvc2UsXHJcbiAgY3JlYXRlIGFzIGNyZWF0ZUJyb3dzZXIsXHJcbiAgbmV3UGFnZSBhcyBicm93c2VyTmV3UGFnZSxcclxuICBjbGVhclBhZ2VcclxufSBmcm9tICcuL2Jyb3dzZXIuanMnO1xyXG5pbXBvcnQgcHVwcGV0ZWVyRXhwb3J0IGZyb20gJy4vZXhwb3J0LmpzJztcclxuaW1wb3J0IHsgbG9nLCBsb2dXaXRoU3RhY2sgfSBmcm9tICcuL2xvZ2dlci5qcyc7XHJcbmltcG9ydCB7IG1lYXN1cmVUaW1lIH0gZnJvbSAnLi91dGlscy5qcyc7XHJcblxyXG5pbXBvcnQgRXhwb3J0RXJyb3IgZnJvbSAnLi9lcnJvcnMvRXhwb3J0RXJyb3IuanMnO1xyXG5cclxubGV0IHBlcmZvcm1lZEV4cG9ydHMgPSAwO1xyXG5sZXQgZXhwb3J0QXR0ZW1wdHMgPSAwO1xyXG5sZXQgdGltZVNwZW50ID0gMDtcclxubGV0IGRyb3BwZWRFeHBvcnRzID0gMDtcclxubGV0IHNwZW50QXZlcmFnZSA9IDA7XHJcbmxldCBwb29sQ29uZmlnID0ge307XHJcblxyXG4vLyBUaGUgcG9vbCBpbnN0YW5jZVxyXG5sZXQgcG9vbCA9IGZhbHNlO1xyXG5cclxuLy8gQ3VzdG9tIHB1cHBldGVlciBhcmd1bWVudHNcclxubGV0IHB1cHBldGVlckFyZ3M7XHJcblxyXG5jb25zdCBmYWN0b3J5ID0ge1xyXG4gIC8qKlxyXG4gICAqIENyZWF0ZXMgYSBuZXcgd29ya2VyIHBhZ2UgZm9yIHRoZSBleHBvcnQgcG9vbC5cclxuICAgKlxyXG4gICAqIEByZXR1cm5zIHtPYmplY3R9IC0gQW4gb2JqZWN0IGNvbnRhaW5pbmcgdGhlIHdvcmtlciBJRCwgYSByZWZlcmVuY2UgdG8gdGhlXHJcbiAgICogYnJvd3NlciBwYWdlLCBhbmQgaW5pdGlhbCB3b3JrIGNvdW50LlxyXG4gICAqXHJcbiAgICogQHRocm93cyB7RXhwb3J0RXJyb3J9IC0gSWYgdGhlcmUncyBhbiBlcnJvciBkdXJpbmcgdGhlIGNyZWF0aW9uIG9mIHRoZSBuZXdcclxuICAgKiBwYWdlLlxyXG4gICAqL1xyXG4gIGNyZWF0ZTogYXN5bmMgKCkgPT4ge1xyXG4gICAgbGV0IHBhZ2UgPSBmYWxzZTtcclxuXHJcbiAgICBjb25zdCBpZCA9IHV1aWQoKTtcclxuICAgIGNvbnN0IHN0YXJ0RGF0ZSA9IG5ldyBEYXRlKCkuZ2V0VGltZSgpO1xyXG5cclxuICAgIHRyeSB7XHJcbiAgICAgIHBhZ2UgPSBhd2FpdCBicm93c2VyTmV3UGFnZSgpO1xyXG5cclxuICAgICAgaWYgKCFwYWdlIHx8IHBhZ2UuaXNDbG9zZWQoKSkge1xyXG4gICAgICAgIHRocm93IG5ldyBFeHBvcnRFcnJvcignVGhlIHBhZ2UgaXMgaW52YWxpZCBvciBjbG9zZWQuJyk7XHJcbiAgICAgIH1cclxuXHJcbiAgICAgIGxvZyhcclxuICAgICAgICAzLFxyXG4gICAgICAgIGBbcG9vbF0gU3VjY2Vzc2Z1bGx5IGNyZWF0ZWQgYSB3b3JrZXIgJHtpZH0gLSB0b29rICR7XHJcbiAgICAgICAgICBuZXcgRGF0ZSgpLmdldFRpbWUoKSAtIHN0YXJ0RGF0ZVxyXG4gICAgICAgIH0gbXMuYFxyXG4gICAgICApO1xyXG4gICAgfSBjYXRjaCAoZXJyb3IpIHtcclxuICAgICAgdGhyb3cgbmV3IEV4cG9ydEVycm9yKFxyXG4gICAgICAgICdFcnJvciBlbmNvdW50ZXJlZCB3aGVuIGNyZWF0aW5nIGEgbmV3IHBhZ2UuJ1xyXG4gICAgICApLnNldEVycm9yKGVycm9yKTtcclxuICAgIH1cclxuXHJcbiAgICByZXR1cm4ge1xyXG4gICAgICBpZCxcclxuICAgICAgcGFnZSxcclxuICAgICAgLy8gVHJ5IHRvIGRpc3RyaWJ1dGUgdGhlIGluaXRpYWwgd29yayBjb3VudFxyXG4gICAgICB3b3JrQ291bnQ6IE1hdGgucm91bmQoTWF0aC5yYW5kb20oKSAqIChwb29sQ29uZmlnLndvcmtMaW1pdCAvIDIpKVxyXG4gICAgfTtcclxuICB9LFxyXG5cclxuICAvKipcclxuICAgKiBWYWxpZGF0ZXMgYSB3b3JrZXIgcGFnZSBpbiB0aGUgZXhwb3J0IHBvb2wsIGNoZWNraW5nIGlmIGl0IGhhcyBleGNlZWRlZFxyXG4gICAqIHRoZSB3b3JrIGxpbWl0LlxyXG4gICAqXHJcbiAgICogQHBhcmFtIHtPYmplY3R9IHdvcmtlckhhbmRsZSAtIFRoZSBoYW5kbGUgdG8gdGhlIHdvcmtlciwgY29udGFpbmluZyB0aGVcclxuICAgKiB3b3JrZXIncyBJRCwgYSByZWZlcmVuY2UgdG8gdGhlIGJyb3dzZXIgcGFnZSwgYW5kIHdvcmsgY291bnQuXHJcbiAgICpcclxuICAgKiBAcmV0dXJucyB7Ym9vbGVhbn0gLSBSZXR1cm5zIHRydWUgaWYgdGhlIHdvcmtlciBpcyB2YWxpZCBhbmQgd2l0aGluXHJcbiAgICogdGhlIHdvcmsgbGltaXQ7IG90aGVyd2lzZSwgcmV0dXJucyBmYWxzZS5cclxuICAgKi9cclxuICB2YWxpZGF0ZTogYXN5bmMgKHdvcmtlckhhbmRsZSkgPT4ge1xyXG4gICAgaWYgKFxyXG4gICAgICBwb29sQ29uZmlnLndvcmtMaW1pdCAmJlxyXG4gICAgICArK3dvcmtlckhhbmRsZS53b3JrQ291bnQgPiBwb29sQ29uZmlnLndvcmtMaW1pdFxyXG4gICAgKSB7XHJcbiAgICAgIGxvZyhcclxuICAgICAgICAzLFxyXG4gICAgICAgIGBbcG9vbF0gV29ya2VyIGZhaWxlZCB2YWxpZGF0aW9uOiBleGNlZWRlZCB3b3JrIGxpbWl0IChsaW1pdCBpcyAke3Bvb2xDb25maWcud29ya0xpbWl0fSkuYFxyXG4gICAgICApO1xyXG4gICAgICByZXR1cm4gZmFsc2U7XHJcbiAgICB9XHJcblxyXG4gICAgLy8gQ2xlYXIgcGFnZVxyXG4gICAgYXdhaXQgY2xlYXJQYWdlKHdvcmtlckhhbmRsZS5wYWdlLCB0cnVlKTtcclxuICAgIHJldHVybiB0cnVlO1xyXG4gIH0sXHJcblxyXG4gIC8qKlxyXG4gICAqIERlc3Ryb3lzIGEgd29ya2VyIGVudHJ5IGluIHRoZSBleHBvcnQgcG9vbCwgY2xvc2luZyBpdHMgYXNzb2NpYXRlZCBwYWdlLlxyXG4gICAqXHJcbiAgICogQHBhcmFtIHtPYmplY3R9IHdvcmtlckhhbmRsZSAtIFRoZSBoYW5kbGUgdG8gdGhlIHdvcmtlciwgY29udGFpbmluZ1xyXG4gICAqIHRoZSB3b3JrZXIncyBJRCBhbmQgYSByZWZlcmVuY2UgdG8gdGhlIGJyb3dzZXIgcGFnZS5cclxuICAgKi9cclxuICBkZXN0cm95OiAod29ya2VySGFuZGxlKSA9PiB7XHJcbiAgICBsb2coMywgYFtwb29sXSBEZXN0cm95aW5nIHBvb2wgZW50cnkgJHt3b3JrZXJIYW5kbGUuaWR9LmApO1xyXG5cclxuICAgIGlmICh3b3JrZXJIYW5kbGUucGFnZSkge1xyXG4gICAgICAvLyBXZSBkb24ndCByZWFsbHkgbmVlZCB0byB3YWl0IGFyb3VuZCBmb3IgdGhpcy5cclxuICAgICAgd29ya2VySGFuZGxlLnBhZ2UuY2xvc2UoKTtcclxuICAgIH1cclxuICB9XHJcbn07XHJcblxyXG4vKipcclxuICogSW5pdGlhbGl6ZXMgdGhlIGV4cG9ydCBwb29sIHdpdGggdGhlIHByb3ZpZGVkIGNvbmZpZ3VyYXRpb24sIGNyZWF0aW5nXHJcbiAqIGEgYnJvd3NlciBpbnN0YW5jZSBhbmQgc2V0dGluZyB1cCB3b3JrZXIgcmVzb3VyY2VzLlxyXG4gKlxyXG4gKiBAcGFyYW0ge09iamVjdH0gY29uZmlnIC0gQ29uZmlndXJhdGlvbiBvcHRpb25zIGZvciB0aGUgZXhwb3J0IHBvb2wgYWxvbmdcclxuICogd2l0aCBjdXN0b20gcHVwcGV0ZWVyIGFyZ3VtZW50cyBmb3IgdGhlIHB1cHBldGVlci5sYXVuY2ggZnVuY3Rpb24uXHJcbiAqL1xyXG5leHBvcnQgY29uc3QgaW5pdFBvb2wgPSBhc3luYyAoY29uZmlnKSA9PiB7XHJcbiAgLy8gRm9yIHRoZSBtb2R1bGUgc2NvcGUgdXNhZ2VcclxuICBwb29sQ29uZmlnID0gY29uZmlnICYmIGNvbmZpZy5wb29sID8geyAuLi5jb25maWcucG9vbCB9IDoge307XHJcblxyXG4gIC8vIEF0dGFjaCBwcm9jZXNzJyBleGl0IGxpc3RlbmVyc1xyXG4gIGlmIChwb29sQ29uZmlnLmxpc3RlblRvUHJvY2Vzc0V4aXRzKSB7XHJcbiAgICBhdHRhY2hQcm9jZXNzRXhpdExpc3RlbmVycygpO1xyXG4gIH1cclxuXHJcbiAgLy8gVGhlIG5ld2VzdCBwdXBwZXRlZXIgYXJndW1lbnRzIGZvciB0aGUgYnJvd3NlciBjcmVhdGlvblxyXG4gIHB1cHBldGVlckFyZ3MgPSBjb25maWcucHVwcGV0ZWVyQXJncztcclxuXHJcbiAgLy8gQ3JlYXRlIGEgYnJvd3NlciBpbnN0YW5jZVxyXG4gIGF3YWl0IGNyZWF0ZUJyb3dzZXIocHVwcGV0ZWVyQXJncyk7XHJcblxyXG4gIGxvZyhcclxuICAgIDMsXHJcbiAgICBgW3Bvb2xdIEluaXRpYWxpemluZyBwb29sIHdpdGggd29ya2VyczogbWluICR7cG9vbENvbmZpZy5taW5Xb3JrZXJzfSwgbWF4ICR7cG9vbENvbmZpZy5tYXhXb3JrZXJzfS5gXHJcbiAgKTtcclxuXHJcbiAgaWYgKHBvb2wpIHtcclxuICAgIHJldHVybiBsb2coXHJcbiAgICAgIDQsXHJcbiAgICAgICdbcG9vbF0gQWxyZWFkeSBpbml0aWFsaXplZCwgcGxlYXNlIGtpbGwgaXQgYmVmb3JlIGNyZWF0aW5nIGEgbmV3IG9uZS4nXHJcbiAgICApO1xyXG4gIH1cclxuXHJcbiAgaWYgKHBhcnNlSW50KHBvb2xDb25maWcubWluV29ya2VycykgPiBwYXJzZUludChwb29sQ29uZmlnLm1heFdvcmtlcnMpKSB7XHJcbiAgICBwb29sQ29uZmlnLm1pbldvcmtlcnMgPSBwb29sQ29uZmlnLm1heFdvcmtlcnM7XHJcbiAgfVxyXG5cclxuICB0cnkge1xyXG4gICAgLy8gQ3JlYXRlIGEgcG9vbCBhbG9uZyB3aXRoIGEgbWluaW1hbCBudW1iZXIgb2YgcmVzb3VyY2VzXHJcbiAgICBwb29sID0gbmV3IFBvb2woe1xyXG4gICAgICAvLyBHZXQgdGhlIGNyZWF0ZS92YWxpZGF0ZS9kZXN0cm95L2xvZyBmdW5jdGlvbnNcclxuICAgICAgLi4uZmFjdG9yeSxcclxuICAgICAgbWluOiBwYXJzZUludChwb29sQ29uZmlnLm1pbldvcmtlcnMpLFxyXG4gICAgICBtYXg6IHBhcnNlSW50KHBvb2xDb25maWcubWF4V29ya2VycyksXHJcbiAgICAgIGFjcXVpcmVUaW1lb3V0TWlsbGlzOiBwb29sQ29uZmlnLmFjcXVpcmVUaW1lb3V0LFxyXG4gICAgICBjcmVhdGVUaW1lb3V0TWlsbGlzOiBwb29sQ29uZmlnLmNyZWF0ZVRpbWVvdXQsXHJcbiAgICAgIGRlc3Ryb3lUaW1lb3V0TWlsbGlzOiBwb29sQ29uZmlnLmRlc3Ryb3lUaW1lb3V0LFxyXG4gICAgICBpZGxlVGltZW91dE1pbGxpczogcG9vbENvbmZpZy5pZGxlVGltZW91dCxcclxuICAgICAgY3JlYXRlUmV0cnlJbnRlcnZhbE1pbGxpczogcG9vbENvbmZpZy5jcmVhdGVSZXRyeUludGVydmFsLFxyXG4gICAgICByZWFwSW50ZXJ2YWxNaWxsaXM6IHBvb2xDb25maWcucmVhcGVySW50ZXJ2YWwsXHJcbiAgICAgIHByb3BhZ2F0ZUNyZWF0ZUVycm9yOiBmYWxzZVxyXG4gICAgfSk7XHJcblxyXG4gICAgLy8gU2V0IGV2ZW50c1xyXG4gICAgcG9vbC5vbigncmVsZWFzZScsIGFzeW5jIChyZXNvdXJjZSkgPT4ge1xyXG4gICAgICAvLyBDbGVhciBwYWdlXHJcbiAgICAgIGF3YWl0IGNsZWFyUGFnZShyZXNvdXJjZS5wYWdlLCBmYWxzZSk7XHJcbiAgICAgIGxvZyg0LCBgW3Bvb2xdIFJlbGVhc2luZyBhIHdvcmtlciB3aXRoIElEICR7cmVzb3VyY2UuaWR9LmApO1xyXG4gICAgfSk7XHJcblxyXG4gICAgcG9vbC5vbignZGVzdHJveVN1Y2Nlc3MnLCAoZXZlbnRJZCwgcmVzb3VyY2UpID0+IHtcclxuICAgICAgbG9nKDQsIGBbcG9vbF0gRGVzdHJveWVkIGEgd29ya2VyIHdpdGggSUQgJHtyZXNvdXJjZS5pZH0uYCk7XHJcbiAgICB9KTtcclxuXHJcbiAgICBjb25zdCBpbml0aWFsUmVzb3VyY2VzID0gW107XHJcbiAgICAvLyBDcmVhdGUgYW4gaW5pdGlhbCBudW1iZXIgb2YgcmVzb3VyY2VzXHJcbiAgICBmb3IgKGxldCBpID0gMDsgaSA8IHBvb2xDb25maWcubWluV29ya2VyczsgaSsrKSB7XHJcbiAgICAgIHRyeSB7XHJcbiAgICAgICAgY29uc3QgcmVzb3VyY2UgPSBhd2FpdCBwb29sLmFjcXVpcmUoKS5wcm9taXNlO1xyXG4gICAgICAgIGluaXRpYWxSZXNvdXJjZXMucHVzaChyZXNvdXJjZSk7XHJcbiAgICAgIH0gY2F0Y2ggKGVycm9yKSB7XHJcbiAgICAgICAgbG9nV2l0aFN0YWNrKDIsIGVycm9yLCAnW3Bvb2xdIENvdWxkIG5vdCBjcmVhdGUgYW4gaW5pdGlhbCByZXNvdXJjZS4nKTtcclxuICAgICAgfVxyXG4gICAgfVxyXG5cclxuICAgIC8vIFJlbGVhc2UgdGhlIGluaXRpYWwgbnVtYmVyIG9mIHJlc291cmNlcyBiYWNrIHRvIHRoZSBwb29sXHJcbiAgICBpbml0aWFsUmVzb3VyY2VzLmZvckVhY2goKHJlc291cmNlKSA9PiB7XHJcbiAgICAgIHBvb2wucmVsZWFzZShyZXNvdXJjZSk7XHJcbiAgICB9KTtcclxuXHJcbiAgICBsb2coXHJcbiAgICAgIDMsXHJcbiAgICAgIGBbcG9vbF0gVGhlIHBvb2wgaXMgcmVhZHkke2luaXRpYWxSZXNvdXJjZXMubGVuZ3RoID8gYCB3aXRoICR7aW5pdGlhbFJlc291cmNlcy5sZW5ndGh9IGluaXRpYWwgcmVzb3VyY2VzIHdhaXRpbmcuYCA6ICcuJ31gXHJcbiAgICApO1xyXG4gIH0gY2F0Y2ggKGVycm9yKSB7XHJcbiAgICAvLyBDbG9zZSBicm93c2VyIGlmIGZvciBzb21lIHJlYXNvbiBjYW5ub3QgZXN0YWJsaXNoIHRoZSBwb29sXHJcbiAgICBhd2FpdCBicm93c2VyQ2xvc2UoKTtcclxuICAgIHRocm93IG5ldyBFeHBvcnRFcnJvcihcclxuICAgICAgJ1twb29sXSBDb3VsZCBub3QgY3JlYXRlIHRoZSBwb29sIG9mIHdvcmtlcnMuJ1xyXG4gICAgKS5zZXRFcnJvcihlcnJvcik7XHJcbiAgfVxyXG59O1xyXG5cclxuLyoqXHJcbiAqIEF0dGFjaGVzIGV4aXQgbGlzdGVuZXJzIHRvIHRoZSBwcm9jZXNzLCBlbnN1cmluZyBwcm9wZXIgY2xlYW51cCBvZiByZXNvdXJjZXNcclxuICogYW5kIHRlcm1pbmF0aW9uIG9uIGV4aXQgc2lnbmFscy4gSGFuZGxlcyAnZXhpdCcsICdTSUdJTlQnLCAnU0lHVEVSTScsIGFuZFxyXG4gKiAndW5jYXVnaHRFeGNlcHRpb24nIGV2ZW50cy5cclxuICovXHJcbmV4cG9ydCBmdW5jdGlvbiBhdHRhY2hQcm9jZXNzRXhpdExpc3RlbmVycygpIHtcclxuICBsb2coMywgJ1twb29sXSBBdHRhY2hpbmcgZXhpdCBsaXN0ZW5lcnMgdG8gdGhlIHByb2Nlc3MuJyk7XHJcblxyXG4gIC8vIEtpbGwgYWxsIHBvb2wgcmVzb3VyY2VzIG9uIGV4aXRcclxuICBwcm9jZXNzLm9uKCdleGl0JywgYXN5bmMgKGNvZGUpID0+IHtcclxuICAgIGxvZyg0LCBgUHJvY2VzcyBleGl0ZWQgd2l0aCBjb2RlICR7Y29kZX0uYCk7XHJcbiAgICBhd2FpdCBraWxsUG9vbCgpO1xyXG4gIH0pO1xyXG5cclxuICAvLyBIYW5kbGVyIGZvciB0aGUgU0lHSU5UXHJcbiAgcHJvY2Vzcy5vbignU0lHSU5UJywgKG5hbWUsIGNvZGUpID0+IHtcclxuICAgIGxvZyg0LCBgVGhlICR7bmFtZX0gZXZlbnQgd2l0aCBjb2RlOiAke2NvZGV9LmApO1xyXG4gICAgcHJvY2Vzcy5leGl0KDEpO1xyXG4gIH0pO1xyXG5cclxuICAvLyBIYW5kbGVyIGZvciB0aGUgU0lHVEVSTVxyXG4gIHByb2Nlc3Mub24oJ1NJR1RFUk0nLCAobmFtZSwgY29kZSkgPT4ge1xyXG4gICAgbG9nKDQsIGBUaGUgJHtuYW1lfSBldmVudCB3aXRoIGNvZGU6ICR7Y29kZX0uYCk7XHJcbiAgICBwcm9jZXNzLmV4aXQoMSk7XHJcbiAgfSk7XHJcblxyXG4gIC8vIEhhbmRsZXIgZm9yIHRoZSB1bmNhdWdodEV4Y2VwdGlvblxyXG4gIHByb2Nlc3Mub24oJ3VuY2F1Z2h0RXhjZXB0aW9uJywgYXN5bmMgKGVycm9yLCBuYW1lKSA9PiB7XHJcbiAgICBsb2dXaXRoU3RhY2soMSwgZXJyb3IsIGBUaGUgJHtuYW1lfSBlcnJvci5gKTtcclxuICAgIGF3YWl0IGtpbGxQb29sKCk7XHJcbiAgICBwcm9jZXNzLmV4aXQoMSk7XHJcbiAgfSk7XHJcbn1cclxuXHJcbi8qKlxyXG4gKiBLaWxscyBhbGwgd29ya2VycyBpbiB0aGUgcG9vbCwgZGVzdHJveXMgdGhlIHBvb2wsIGFuZCBjbG9zZXMgdGhlIGJyb3dzZXJcclxuICogaW5zdGFuY2UuXHJcbiAqXHJcbiAqIEByZXR1cm5zIHtQcm9taXNlPHZvaWQ+fSBBIHByb21pc2UgdGhhdCByZXNvbHZlcyBhZnRlciB0aGUgd29ya2VycyBhcmVcclxuICoga2lsbGVkLCB0aGUgcG9vbCBpcyBkZXN0cm95ZWQsIGFuZCB0aGUgYnJvd3NlciBpcyBjbG9zZWQuXHJcbiAqL1xyXG5leHBvcnQgYXN5bmMgZnVuY3Rpb24ga2lsbFBvb2woKSB7XHJcbiAgbG9nKDMsICdbcG9vbF0gS2lsbGluZyBhbGwgcG9vbCB3b3JrZXJzIGFuZCBicm93c2VyLCBpZiBhbnkgZXhpc3QuJyk7XHJcblxyXG4gIC8vIFJldHVybiB0cnVlIHdoZW4gdGhlIHBvb2wgaXMgYWxyZWFkeSBkZXN0cm95ZWRcclxuICBpZiAocG9vbD8uZGVzdHJveWVkKSB7XHJcbiAgICAvLyBDbG9zZSB0aGUgYnJvd3NlciBpbnN0YW5jZSBpZiBzdGlsbCBjb25uZWN0ZWRcclxuICAgIHJldHVybiBicm93c2VyQ2xvc2UoKTtcclxuICB9XHJcblxyXG4gIC8vIElmIHN0aWxsIGFsaXZlLCBkZXN0cm95IHRoZSBwb29sIG9mIHBhZ2VzIGJlZm9yZSBjbG9zaW5nIGEgYnJvd3NlclxyXG4gIGlmIChwb29sKSB7XHJcbiAgICBhd2FpdCBwb29sLmRlc3Ryb3koKTtcclxuICAgIGxvZyg0LCAnW2Jyb3dzZXJdIERlc3Ryb3llZCB0aGUgcG9vbCBvZiByZXNvdXJjZXMuJyk7XHJcbiAgfVxyXG5cclxuICAvLyBDbG9zZSB0aGUgYnJvd3NlciBpbnN0YW5jZVxyXG4gIHJldHVybiBicm93c2VyQ2xvc2UoKTtcclxufVxyXG5cclxuLyoqXHJcbiAqIFByb2Nlc3NlcyB0aGUgZXhwb3J0IHdvcmsgdXNpbmcgYSB3b3JrZXIgZnJvbSB0aGUgcG9vbC4gQWNxdWlyZXMgYSB3b3JrZXJcclxuICogaGFuZGxlIGZyb20gdGhlIHBvb2wsIHBlcmZvcm1zIHRoZSBleHBvcnQgdXNpbmcgcHVwcGV0ZWVyLCBhbmQgcmVsZWFzZXNcclxuICogdGhlIHdvcmtlciBoYW5kbGUgYmFjayB0byB0aGUgcG9vbC5cclxuICpcclxuICogQHBhcmFtIHtzdHJpbmd9IGNoYXJ0IC0gVGhlIGNoYXJ0IGRhdGEgb3IgY29uZmlndXJhdGlvbiB0byBiZSBleHBvcnRlZC5cclxuICogQHBhcmFtIHtPYmplY3R9IG9wdGlvbnMgLSBFeHBvcnQgb3B0aW9ucyBhbmQgY29uZmlndXJhdGlvbi5cclxuICpcclxuICogQHJldHVybnMge1Byb21pc2U8T2JqZWN0Pn0gQSBwcm9taXNlIHRoYXQgcmVzb2x2ZXMgd2l0aCB0aGUgZXhwb3J0IHJlc3VsdGFuZFxyXG4gKiBvcHRpb25zLlxyXG4gKlxyXG4gKiBAdGhyb3dzIHtFeHBvcnRFcnJvcn0gSWYgYW4gZXJyb3Igb2NjdXJzIGR1cmluZyB0aGUgZXhwb3J0IHByb2Nlc3MuXHJcbiAqL1xyXG5leHBvcnQgY29uc3QgcG9zdFdvcmsgPSBhc3luYyAoY2hhcnQsIG9wdGlvbnMpID0+IHtcclxuICBsZXQgd29ya2VySGFuZGxlO1xyXG5cclxuICB0cnkge1xyXG4gICAgbG9nKDQsICdbcG9vbF0gV29yayByZWNlaXZlZCwgc3RhcnRpbmcgdG8gcHJvY2Vzcy4nKTtcclxuXHJcbiAgICArK2V4cG9ydEF0dGVtcHRzO1xyXG4gICAgaWYgKHBvb2xDb25maWcuYmVuY2htYXJraW5nKSB7XHJcbiAgICAgIGdldFBvb2xJbmZvKCk7XHJcbiAgICB9XHJcblxyXG4gICAgaWYgKCFwb29sKSB7XHJcbiAgICAgIHRocm93IG5ldyBFeHBvcnRFcnJvcignV29yayByZWNlaXZlZCwgYnV0IHBvb2wgaGFzIG5vdCBiZWVuIHN0YXJ0ZWQuJyk7XHJcbiAgICB9XHJcblxyXG4gICAgLy8gQWNxdWlyZSB0aGUgd29ya2VyIGFsb25nIHdpdGggdGhlIGlkIG9mIHJlc291cmNlIGFuZCB3b3JrIGNvdW50XHJcbiAgICB0cnkge1xyXG4gICAgICBsb2coNCwgJ1twb29sXSBBY3F1aXJpbmcgYSB3b3JrZXIgaGFuZGxlLicpO1xyXG4gICAgICBjb25zdCBhY3F1aXJlQ291bnRlciA9IG1lYXN1cmVUaW1lKCk7XHJcbiAgICAgIHdvcmtlckhhbmRsZSA9IGF3YWl0IHBvb2wuYWNxdWlyZSgpLnByb21pc2U7XHJcblxyXG4gICAgICAvLyBDaGVjayB0aGUgcGFnZSBhY3F1aXJlIHRpbWVcclxuICAgICAgaWYgKG9wdGlvbnMuc2VydmVyLmJlbmNobWFya2luZykge1xyXG4gICAgICAgIGxvZyhcclxuICAgICAgICAgIDUsXHJcbiAgICAgICAgICBvcHRpb25zLnBheWxvYWQ/LnJlcXVlc3RJZFxyXG4gICAgICAgICAgICA/IGBbYmVuY2htYXJrXSBSZXF1ZXN0IHdpdGggSUQgJHtvcHRpb25zLnBheWxvYWQ/LnJlcXVlc3RJZH0gLWBcclxuICAgICAgICAgICAgOiAnW2JlbmNobWFya10nLFxyXG4gICAgICAgICAgYEFjcXVpcmVkIGEgd29ya2VyIGhhbmRsZTogJHthY3F1aXJlQ291bnRlcigpfW1zLmBcclxuICAgICAgICApO1xyXG4gICAgICB9XHJcbiAgICB9IGNhdGNoIChlcnJvcikge1xyXG4gICAgICB0aHJvdyBuZXcgRXhwb3J0RXJyb3IoXHJcbiAgICAgICAgJ0Vycm9yIGVuY291bnRlcmVkIHdoZW4gYWNxdWlyaW5nIGFuIGF2YWlsYWJsZSBlbnRyeS4nXHJcbiAgICAgICkuc2V0RXJyb3IoZXJyb3IpO1xyXG4gICAgfVxyXG4gICAgbG9nKDQsICdbcG9vbF0gQWNxdWlyZWQgYSB3b3JrZXIgaGFuZGxlLicpO1xyXG5cclxuICAgIGlmICghd29ya2VySGFuZGxlLnBhZ2UpIHtcclxuICAgICAgdGhyb3cgbmV3IEV4cG9ydEVycm9yKFxyXG4gICAgICAgICdSZXNvbHZlZCB3b3JrZXIgcGFnZSBpcyBpbnZhbGlkOiB0aGUgcG9vbCBzZXR1cCBpcyB3b25reS4nXHJcbiAgICAgICk7XHJcbiAgICB9XHJcblxyXG4gICAgLy8gU2F2ZSB0aGUgc3RhcnQgdGltZVxyXG4gICAgbGV0IHdvcmtTdGFydCA9IG5ldyBEYXRlKCkuZ2V0VGltZSgpO1xyXG5cclxuICAgIGxvZyg0LCBgW3Bvb2xdIFN0YXJ0aW5nIHdvcmsgb24gcG9vbCBlbnRyeSB3aXRoIElEICR7d29ya2VySGFuZGxlLmlkfS5gKTtcclxuXHJcbiAgICAvLyBQZXJmb3JtIGFuIGV4cG9ydCBvbiBhIHB1cHBldGVlciBsZXZlbFxyXG4gICAgY29uc3QgZXhwb3J0Q291bnRlciA9IG1lYXN1cmVUaW1lKCk7XHJcbiAgICBjb25zdCByZXN1bHQgPSBhd2FpdCBwdXBwZXRlZXJFeHBvcnQod29ya2VySGFuZGxlLnBhZ2UsIGNoYXJ0LCBvcHRpb25zKTtcclxuXHJcbiAgICAvLyBDaGVjayBpZiBpdCdzIGFuIGVycm9yXHJcbiAgICBpZiAocmVzdWx0IGluc3RhbmNlb2YgRXJyb3IpIHtcclxuICAgICAgLy8gVE9ETzogSWYgdGhlIGV4cG9ydCBmYWlsZWQgYmVjYXVzZSBwdXBwZXRlZXIgdGltZWQgb3V0LCB3ZSBuZWVkIHRvIGZvcmNlIGtpbGwgdGhlIHdvcmtlciBzbyB3ZSBnZXQgYSBuZXcgcGFnZS4gVGhhdCBuZWVkcyB0byBiZSBoYW5kbGVkIGJldHRlciB0aGFuIHRoaXMgaGFjay5cclxuICAgICAgaWYgKHJlc3VsdC5tZXNzYWdlID09PSAnUmFzdGVyaXphdGlvbiB0aW1lb3V0Jykge1xyXG4gICAgICAgIHdvcmtlckhhbmRsZS5wYWdlLmNsb3NlKCk7XHJcbiAgICAgICAgd29ya2VySGFuZGxlLnBhZ2UgPSBhd2FpdCBicm93c2VyTmV3UGFnZSgpO1xyXG4gICAgICB9XHJcblxyXG4gICAgICB0aHJvdyBuZXcgRXhwb3J0RXJyb3IoJ0Vycm9yIGVuY291bnRlcmVkIGR1cmluZyBleHBvcnQuJykuc2V0RXJyb3IoXHJcbiAgICAgICAgcmVzdWx0XHJcbiAgICAgICk7XHJcbiAgICB9XHJcblxyXG4gICAgLy8gQ2hlY2sgdGhlIFB1cHBldGVlciBleHBvcnQgdGltZVxyXG4gICAgaWYgKG9wdGlvbnMuc2VydmVyLmJlbmNobWFya2luZykge1xyXG4gICAgICBsb2coXHJcbiAgICAgICAgNSxcclxuICAgICAgICBvcHRpb25zLnBheWxvYWQ/LnJlcXVlc3RJZFxyXG4gICAgICAgICAgPyBgW2JlbmNobWFya10gUmVxdWVzdCB3aXRoIElEICR7b3B0aW9ucy5wYXlsb2FkPy5yZXF1ZXN0SWR9IC1gXHJcbiAgICAgICAgICA6ICdbYmVuY2htYXJrXScsXHJcbiAgICAgICAgYEV4cG9ydGVkIGEgY2hhcnQgc3VjZXNzZnVsbHk6ICR7ZXhwb3J0Q291bnRlcigpfW1zLmBcclxuICAgICAgKTtcclxuICAgIH1cclxuXHJcbiAgICAvLyBSZWxlYXNlIHRoZSByZXNvdXJjZSBiYWNrIHRvIHRoZSBwb29sXHJcbiAgICBwb29sLnJlbGVhc2Uod29ya2VySGFuZGxlKTtcclxuXHJcbiAgICAvLyBVc2VkIGZvciBzdGF0aXN0aWNzIGluIGF2ZXJhZ2VUaW1lIGFuZCBwcm9jZXNzZWRXb3JrQ291bnQsIHdoaWNoXHJcbiAgICAvLyBpbiB0dXJuIGlzIHVzZWQgYnkgdGhlIC9oZWFsdGggcm91dGUuXHJcbiAgICBjb25zdCB3b3JrRW5kID0gbmV3IERhdGUoKS5nZXRUaW1lKCk7XHJcbiAgICBjb25zdCBleHBvcnRUaW1lID0gd29ya0VuZCAtIHdvcmtTdGFydDtcclxuICAgIHRpbWVTcGVudCArPSBleHBvcnRUaW1lO1xyXG4gICAgc3BlbnRBdmVyYWdlID0gdGltZVNwZW50IC8gKytwZXJmb3JtZWRFeHBvcnRzO1xyXG5cclxuICAgIGxvZyg0LCBgW3Bvb2xdIFdvcmsgY29tcGxldGVkIGluICR7ZXhwb3J0VGltZX0gbXMuYCk7XHJcblxyXG4gICAgLy8gT3RoZXJ3aXNlIHJldHVybiB0aGUgcmVzdWx0XHJcbiAgICByZXR1cm4ge1xyXG4gICAgICByZXN1bHQsXHJcbiAgICAgIG9wdGlvbnNcclxuICAgIH07XHJcbiAgfSBjYXRjaCAoZXJyb3IpIHtcclxuICAgICsrZHJvcHBlZEV4cG9ydHM7XHJcblxyXG4gICAgaWYgKHdvcmtlckhhbmRsZSkge1xyXG4gICAgICBwb29sLnJlbGVhc2Uod29ya2VySGFuZGxlKTtcclxuICAgIH1cclxuXHJcbiAgICB0aHJvdyBuZXcgRXhwb3J0RXJyb3IoYFtwb29sXSBJbiBwb29sLnBvc3RXb3JrOiAke2Vycm9yLm1lc3NhZ2V9YCkuc2V0RXJyb3IoXHJcbiAgICAgIGVycm9yXHJcbiAgICApO1xyXG4gIH1cclxufTtcclxuXHJcbi8qKlxyXG4gKiBSZXRyaWV2ZXMgdGhlIGN1cnJlbnQgcG9vbCBpbnN0YW5jZS5cclxuICpcclxuICogQHJldHVybnMge09iamVjdHxudWxsfSBUaGUgY3VycmVudCBwb29sIGluc3RhbmNlIGlmIGluaXRpYWxpemVkLCBvciBudWxsXHJcbiAqIGlmIHRoZSBwb29sIGhhcyBub3QgYmVlbiBjcmVhdGVkLlxyXG4gKi9cclxuZXhwb3J0IGZ1bmN0aW9uIGdldFBvb2woKSB7XHJcbiAgcmV0dXJuIHBvb2w7XHJcbn1cclxuXHJcbi8qKlxyXG4gKiBSZXRyaWV2ZXMgcG9vbCBpbmZvcm1hdGlvbiBpbiBKU09OIGZvcm1hdCwgaW5jbHVkaW5nIG1pbmltdW0gYW5kIG1heGltdW1cclxuICogd29ya2VycywgYXZhaWxhYmxlIHdvcmtlcnMsIHdvcmtlcnMgaW4gdXNlLCBhbmQgcGVuZGluZyBhY3F1aXJlIHJlcXVlc3RzLlxyXG4gKlxyXG4gKiBAcmV0dXJucyB7T2JqZWN0fSBQb29sIGluZm9ybWF0aW9uIGluIEpTT04gZm9ybWF0LlxyXG4gKi9cclxuZXhwb3J0IGNvbnN0IGdldFBvb2xJbmZvSlNPTiA9ICgpID0+ICh7XHJcbiAgbWluOiBwb29sLm1pbixcclxuICBtYXg6IHBvb2wubWF4LFxyXG4gIGF2YWlsYWJsZTogcG9vbC5udW1GcmVlKCksXHJcbiAgaW5Vc2U6IHBvb2wubnVtVXNlZCgpLFxyXG4gIHBlbmRpbmdBY3F1aXJlOiBwb29sLm51bVBlbmRpbmdBY3F1aXJlcygpXHJcbn0pO1xyXG5cclxuLyoqXHJcbiAqIExvZ3MgaW5mb3JtYXRpb24gYWJvdXQgdGhlIGN1cnJlbnQgc3RhdGUgb2YgdGhlIHBvb2wsIGluY2x1ZGluZyB0aGUgbWluaW11bVxyXG4gKiBhbmQgbWF4aW11bSB3b3JrZXJzLCBhdmFpbGFibGUgd29ya2Vycywgd29ya2VycyBpbiB1c2UsIGFuZCBwZW5kaW5nIGFjcXVpcmVcclxuICogcmVxdWVzdHMuXHJcbiAqL1xyXG5leHBvcnQgZnVuY3Rpb24gZ2V0UG9vbEluZm8oKSB7XHJcbiAgY29uc3QgeyBtaW4sIG1heCB9ID0gcG9vbDtcclxuXHJcbiAgbG9nKDUsIGBbcG9vbF0gVGhlIG1pbmltdW0gbnVtYmVyIG9mIHJlc291cmNlcyBhbGxvd2VkIGJ5IHBvb2w6ICR7bWlufS5gKTtcclxuICBsb2coNSwgYFtwb29sXSBUaGUgbWF4aW11bSBudW1iZXIgb2YgcmVzb3VyY2VzIGFsbG93ZWQgYnkgcG9vbDogJHttYXh9LmApO1xyXG4gIGxvZyhcclxuICAgIDUsXHJcbiAgICBgW3Bvb2xdIFRoZSBudW1iZXIgb2YgcmVzb3VyY2VzIHRoYXQgYXJlIGN1cnJlbnRseSBhdmFpbGFibGU6ICR7cG9vbC5udW1GcmVlKCl9LmBcclxuICApO1xyXG4gIGxvZyhcclxuICAgIDUsXHJcbiAgICBgW3Bvb2xdIFRoZSBudW1iZXIgb2YgcmVzb3VyY2VzIHRoYXQgYXJlIGN1cnJlbnRseSBhY3F1aXJlZDogJHtwb29sLm51bVVzZWQoKX0uYFxyXG4gICk7XHJcbiAgbG9nKFxyXG4gICAgNSxcclxuICAgIGBbcG9vbF0gVGhlIG51bWJlciBvZiBjYWxsZXJzIHdhaXRpbmcgdG8gYWNxdWlyZSBhIHJlc291cmNlOiAke3Bvb2wubnVtUGVuZGluZ0FjcXVpcmVzKCl9LmBcclxuICApO1xyXG59XHJcblxyXG5leHBvcnQgZGVmYXVsdCB7XHJcbiAgaW5pdFBvb2wsXHJcbiAga2lsbFBvb2wsXHJcbiAgcG9zdFdvcmssXHJcbiAgZ2V0UG9vbCxcclxuICBnZXRQb29sSW5mbyxcclxuICBnZXRQb29sSW5mb0pTT04sXHJcbiAgd29ya0F0dGVtcHRzOiAoKSA9PiBleHBvcnRBdHRlbXB0cyxcclxuICBkcm9wcGVkV29yazogKCkgPT4gZHJvcHBlZEV4cG9ydHMsXHJcbiAgYXZlcmFnZVRpbWU6ICgpID0+IHNwZW50QXZlcmFnZSxcclxuICBwcm9jZXNzZWRXb3JrQ291bnQ6ICgpID0+IHBlcmZvcm1lZEV4cG9ydHNcclxufTtcclxuIiwiLyoqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKipcclxuXHJcbkhpZ2hjaGFydHMgRXhwb3J0IFNlcnZlclxyXG5cclxuQ29weXJpZ2h0IChjKSAyMDE2LTIwMjQsIEhpZ2hzb2Z0XHJcblxyXG5MaWNlbmNlZCB1bmRlciB0aGUgTUlUIGxpY2VuY2UuXHJcblxyXG5BZGRpdGlvbmFsbHkgYSB2YWxpZCBIaWdoY2hhcnRzIGxpY2Vuc2UgaXMgcmVxdWlyZWQgZm9yIHVzZS5cclxuXHJcblNlZSBMSUNFTlNFIGZpbGUgaW4gcm9vdCBmb3IgZGV0YWlscy5cclxuXHJcbioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKiovXHJcblxyXG5pbXBvcnQgeyByZWFkRmlsZVN5bmMsIHdyaXRlRmlsZVN5bmMgfSBmcm9tICdmcyc7XHJcblxyXG5pbXBvcnQgeyBnZXRPcHRpb25zLCBpbml0RXhwb3J0U2V0dGluZ3MgfSBmcm9tICcuL2NvbmZpZy5qcyc7XHJcbmltcG9ydCB7IGxvZywgbG9nV2l0aFN0YWNrIH0gZnJvbSAnLi9sb2dnZXIuanMnO1xyXG5pbXBvcnQgeyBraWxsUG9vbCwgcG9zdFdvcmsgfSBmcm9tICcuL3Bvb2wuanMnO1xyXG5pbXBvcnQge1xyXG4gIGZpeFR5cGUsXHJcbiAgaGFuZGxlUmVzb3VyY2VzLFxyXG4gIGlzQ29ycmVjdEpTT04sXHJcbiAgb3B0aW9uc1N0cmluZ2lmeSxcclxuICByb3VuZE51bWJlcixcclxuICB0b0Jvb2xlYW4sXHJcbiAgd3JhcEFyb3VuZFxyXG59IGZyb20gJy4vdXRpbHMuanMnO1xyXG5cclxuaW1wb3J0IEV4cG9ydEVycm9yIGZyb20gJy4vZXJyb3JzL0V4cG9ydEVycm9yLmpzJztcclxuXHJcbmxldCBhbGxvd0NvZGVFeGVjdXRpb24gPSBmYWxzZTtcclxuXHJcbi8qKlxyXG4gKiBTdGFydHMgYW4gZXhwb3J0IHByb2Nlc3MuIFRoZSBgc2V0dGluZ3NgIGNvbnRhaW5zIGZpbmFsIG9wdGlvbnMgZ2F0aGVyZWRcclxuICogZnJvbSBhbGwgcG9zc2libGUgc291cmNlcyAoY29uZmlnLCBlbnYsIGNsaSwganNvbikuIFRoZSBgZW5kQ2FsbGJhY2tgIGlzXHJcbiAqIGNhbGxlZCB3aGVuIHRoZSBleHBvcnQgaXMgY29tcGxldGVkLCB3aXRoIGFuIGVycm9yIG9iamVjdCBhcyB0aGUgZmlyc3RcclxuICogYXJndW1lbnQgYW5kIHRoZSBzZWNvbmQgY29udGFpbmluZyB0aGUgYmFzZTY0IHJlc3ByZXNlbnRhdGlvbiBvZiBhIGNoYXJ0LlxyXG4gKlxyXG4gKiBAcGFyYW0ge09iamVjdH0gc2V0dGluZ3MgLSBUaGUgc2V0dGluZ3Mgb2JqZWN0IGNvbnRhaW5pbmcgZXhwb3J0XHJcbiAqIGNvbmZpZ3VyYXRpb24uXHJcbiAqIEBwYXJhbSB7ZnVuY3Rpb259IGVuZENhbGxiYWNrIC0gVGhlIGNhbGxiYWNrIGZ1bmN0aW9uIHRvIGJlIGludm9rZWQgdXBvblxyXG4gKiBmaW5hbGl6aW5nIHdvcmsgb3IgdXBvbiBlcnJvciBvY2N1cmFuY2Ugb2YgdGhlIGV4cG9ydGluZyBwcm9jZXNzLlxyXG4gKlxyXG4gKiBAcmV0dXJucyB7dm9pZH0gVGhpcyBmdW5jdGlvbiBkb2VzIG5vdCByZXR1cm4gYSB2YWx1ZSBkaXJlY3RseTsgaW5zdGVhZCxcclxuICogaXQgY29tbXVuaWNhdGVzIHJlc3VsdHMgdmlhIHRoZSBlbmRDYWxsYmFjay5cclxuICovXHJcbmV4cG9ydCBjb25zdCBzdGFydEV4cG9ydCA9IGFzeW5jIChzZXR0aW5ncywgZW5kQ2FsbGJhY2spID0+IHtcclxuICAvLyBTdGFydGluZyBleHBvcnRpbmcgcHJvY2VzcyBtZXNzYWdlXHJcbiAgbG9nKDQsICdbY2hhcnRdIFN0YXJ0aW5nIHRoZSBleHBvcnRpbmcgcHJvY2Vzcy4nKTtcclxuXHJcbiAgLy8gSW5pdGlhbGl6ZSBvcHRpb25zXHJcbiAgY29uc3Qgb3B0aW9ucyA9IGluaXRFeHBvcnRTZXR0aW5ncyhzZXR0aW5ncywgZ2V0T3B0aW9ucygpKTtcclxuXHJcbiAgLy8gR2V0IHRoZSBleHBvcnQgb3B0aW9uc1xyXG4gIGNvbnN0IGV4cG9ydE9wdGlvbnMgPSBvcHRpb25zLmV4cG9ydDtcclxuXHJcbiAgLy8gSWYgU1ZHIGlzIGFuIGlucHV0IChhcmd1bWVudCBjYW4gYmUgc2VudCBvbmx5IGJ5IHRoZSByZXF1ZXN0KVxyXG4gIGlmIChvcHRpb25zLnBheWxvYWQ/LnN2ZyAmJiBvcHRpb25zLnBheWxvYWQuc3ZnICE9PSAnJykge1xyXG4gICAgdHJ5IHtcclxuICAgICAgbG9nKDQsICdbY2hhcnRdIEF0dGVtcHRpbmcgdG8gZXhwb3J0IGZyb20gYSBTVkcgaW5wdXQuJyk7XHJcbiAgICAgIHJldHVybiBleHBvcnRBc1N0cmluZyhvcHRpb25zLnBheWxvYWQuc3ZnLnRyaW0oKSwgb3B0aW9ucywgZW5kQ2FsbGJhY2spO1xyXG4gICAgfSBjYXRjaCAoZXJyb3IpIHtcclxuICAgICAgcmV0dXJuIGVuZENhbGxiYWNrKFxyXG4gICAgICAgIG5ldyBFeHBvcnRFcnJvcignW2NoYXJ0XSBFcnJvciBsb2FkaW5nIFNWRyBpbnB1dC4nKS5zZXRFcnJvcihlcnJvcilcclxuICAgICAgKTtcclxuICAgIH1cclxuICB9XHJcblxyXG4gIC8vIEV4cG9ydCB1c2luZyBvcHRpb25zIGZyb20gdGhlIGZpbGVcclxuICBpZiAoZXhwb3J0T3B0aW9ucy5pbmZpbGUgJiYgZXhwb3J0T3B0aW9ucy5pbmZpbGUubGVuZ3RoKSB7XHJcbiAgICAvLyBUcnkgdG8gcmVhZCB0aGUgZmlsZSB0byBnZXQgdGhlIHN0cmluZyByZXByZXNlbnRhdGlvblxyXG4gICAgdHJ5IHtcclxuICAgICAgbG9nKDQsICdbY2hhcnRdIEF0dGVtcHRpbmcgdG8gZXhwb3J0IGZyb20gYW4gaW5wdXQgZmlsZS4nKTtcclxuICAgICAgb3B0aW9ucy5leHBvcnQuaW5zdHIgPSByZWFkRmlsZVN5bmMoZXhwb3J0T3B0aW9ucy5pbmZpbGUsICd1dGY4Jyk7XHJcbiAgICAgIHJldHVybiBleHBvcnRBc1N0cmluZyhvcHRpb25zLmV4cG9ydC5pbnN0ci50cmltKCksIG9wdGlvbnMsIGVuZENhbGxiYWNrKTtcclxuICAgIH0gY2F0Y2ggKGVycm9yKSB7XHJcbiAgICAgIHJldHVybiBlbmRDYWxsYmFjayhcclxuICAgICAgICBuZXcgRXhwb3J0RXJyb3IoJ1tjaGFydF0gRXJyb3IgbG9hZGluZyBpbnB1dCBmaWxlLicpLnNldEVycm9yKGVycm9yKVxyXG4gICAgICApO1xyXG4gICAgfVxyXG4gIH1cclxuXHJcbiAgLy8gRXhwb3J0IHdpdGggb3B0aW9ucyBmcm9tIHRoZSByYXcgcmVwcmVzZW50YXRpb25cclxuICBpZiAoXHJcbiAgICAoZXhwb3J0T3B0aW9ucy5pbnN0ciAmJiBleHBvcnRPcHRpb25zLmluc3RyICE9PSAnJykgfHxcclxuICAgIChleHBvcnRPcHRpb25zLm9wdGlvbnMgJiYgZXhwb3J0T3B0aW9ucy5vcHRpb25zICE9PSAnJylcclxuICApIHtcclxuICAgIHRyeSB7XHJcbiAgICAgIGxvZyg0LCAnW2NoYXJ0XSBBdHRlbXB0aW5nIHRvIGV4cG9ydCBmcm9tIGEgcmF3IGlucHV0LicpO1xyXG5cclxuICAgICAgLy8gUGVyZm9ybSBhIGRpcmVjdCBpbmplY3Qgd2hlbiBmb3JjZWRcclxuICAgICAgaWYgKHRvQm9vbGVhbihvcHRpb25zLmN1c3RvbUxvZ2ljPy5hbGxvd0NvZGVFeGVjdXRpb24pKSB7XHJcbiAgICAgICAgcmV0dXJuIGRvU3RyYWlnaHRJbmplY3Qob3B0aW9ucywgZW5kQ2FsbGJhY2spO1xyXG4gICAgICB9XHJcblxyXG4gICAgICAvLyBFaXRoZXIgdHJ5IHRvIHBhcnNlIHRvIEpTT04gZmlyc3Qgb3IgZG8gdGhlIGRpcmVjdCBleHBvcnRcclxuICAgICAgcmV0dXJuIHR5cGVvZiBleHBvcnRPcHRpb25zLmluc3RyID09PSAnc3RyaW5nJ1xyXG4gICAgICAgID8gZXhwb3J0QXNTdHJpbmcoZXhwb3J0T3B0aW9ucy5pbnN0ci50cmltKCksIG9wdGlvbnMsIGVuZENhbGxiYWNrKVxyXG4gICAgICAgIDogZG9FeHBvcnQoXHJcbiAgICAgICAgICAgIG9wdGlvbnMsXHJcbiAgICAgICAgICAgIGV4cG9ydE9wdGlvbnMuaW5zdHIgfHwgZXhwb3J0T3B0aW9ucy5vcHRpb25zLFxyXG4gICAgICAgICAgICBlbmRDYWxsYmFja1xyXG4gICAgICAgICAgKTtcclxuICAgIH0gY2F0Y2ggKGVycm9yKSB7XHJcbiAgICAgIHJldHVybiBlbmRDYWxsYmFjayhcclxuICAgICAgICBuZXcgRXhwb3J0RXJyb3IoJ1tjaGFydF0gRXJyb3IgbG9hZGluZyByYXcgaW5wdXQuJykuc2V0RXJyb3IoZXJyb3IpXHJcbiAgICAgICk7XHJcbiAgICB9XHJcbiAgfVxyXG5cclxuICAvLyBObyBpbnB1dCBzcGVjaWZpZWQsIHBhc3MgYW4gZXJyb3IgbWVzc2FnZSB0byB0aGUgY2FsbGJhY2tcclxuICByZXR1cm4gZW5kQ2FsbGJhY2soXHJcbiAgICBuZXcgRXhwb3J0RXJyb3IoXHJcbiAgICAgIGBbY2hhcnRdIE5vIHZhbGlkIGlucHV0IHNwZWNpZmllZC4gQ2hlY2sgaWYgYXQgbGVhc3Qgb25lIG9mIHRoZSBmb2xsb3dpbmcgcGFyYW1ldGVycyBpcyBjb3JyZWN0bHkgc2V0OiAnaW5maWxlJywgJ2luc3RyJywgJ29wdGlvbnMnLCBvciAnc3ZnJy5gXHJcbiAgICApXHJcbiAgKTtcclxufTtcclxuXHJcbi8qKlxyXG4gKiBTdGFydHMgYSBiYXRjaCBleHBvcnQgcHJvY2VzcyBmb3IgbXVsdGlwbGUgY2hhcnRzIGJhc2VkIG9uIHRoZSBpbmZvcm1hdGlvblxyXG4gKiBpbiB0aGUgYmF0Y2ggb3B0aW9uLiBUaGUgYmF0Y2ggaXMgYSBzdHJpbmcgaW4gdGhlIGZvbGxvd2luZyBmb3JtYXQ6XHJcbiAqIFwiaW5maWxlMS5qc29uPW91dGZpbGUxLnBuZztpbmZpbGUyLmpzb249b3V0ZmlsZTIucG5nOy4uLlwiXHJcbiAqXHJcbiAqIEBwYXJhbSB7T2JqZWN0fSBvcHRpb25zIC0gVGhlIG9wdGlvbnMgb2JqZWN0IGNvbnRhaW5pbmcgY29uZmlndXJhdGlvbiBmb3JcclxuICogYSBiYXRjaCBleHBvcnQuXHJcbiAqXHJcbiAqIEByZXR1cm5zIHtQcm9taXNlPHZvaWQ+fSBBIFByb21pc2UgdGhhdCByZXNvbHZlcyBvbmNlIHRoZSBiYXRjaCBleHBvcnRcclxuICogcHJvY2VzcyBpcyBjb21wbGV0ZWQuXHJcbiAqXHJcbiAqIEB0aHJvd3Mge0V4cG9ydEVycm9yfSBUaHJvd3MgYW4gRXhwb3J0RXJyb3IgaWYgYW4gZXJyb3Igb2NjdXJzIGR1cmluZ1xyXG4gKiBhbnkgb2YgdGhlIGJhdGNoIGV4cG9ydCBwcm9jZXNzLlxyXG4gKi9cclxuZXhwb3J0IGNvbnN0IGJhdGNoRXhwb3J0ID0gYXN5bmMgKG9wdGlvbnMpID0+IHtcclxuICBjb25zdCBiYXRjaEZ1bmN0aW9ucyA9IFtdO1xyXG5cclxuICAvLyBTcGxpdCBhbmQgcGFpciB0aGUgLS1iYXRjaCBhcmd1bWVudHNcclxuICBmb3IgKGxldCBwYWlyIG9mIG9wdGlvbnMuZXhwb3J0LmJhdGNoLnNwbGl0KCc7JykpIHtcclxuICAgIHBhaXIgPSBwYWlyLnNwbGl0KCc9Jyk7XHJcbiAgICBpZiAocGFpci5sZW5ndGggPT09IDIpIHtcclxuICAgICAgYmF0Y2hGdW5jdGlvbnMucHVzaChcclxuICAgICAgICBzdGFydEV4cG9ydChcclxuICAgICAgICAgIHtcclxuICAgICAgICAgICAgLi4ub3B0aW9ucyxcclxuICAgICAgICAgICAgZXhwb3J0OiB7XHJcbiAgICAgICAgICAgICAgLi4ub3B0aW9ucy5leHBvcnQsXHJcbiAgICAgICAgICAgICAgaW5maWxlOiBwYWlyWzBdLFxyXG4gICAgICAgICAgICAgIG91dGZpbGU6IHBhaXJbMV1cclxuICAgICAgICAgICAgfVxyXG4gICAgICAgICAgfSxcclxuICAgICAgICAgIChlcnJvciwgaW5mbykgPT4ge1xyXG4gICAgICAgICAgICAvLyBUaHJvdyBhbiBlcnJvclxyXG4gICAgICAgICAgICBpZiAoZXJyb3IpIHtcclxuICAgICAgICAgICAgICB0aHJvdyBlcnJvcjtcclxuICAgICAgICAgICAgfVxyXG5cclxuICAgICAgICAgICAgLy8gU2F2ZSB0aGUgYmFzZTY0IGZyb20gYSBidWZmZXIgdG8gYSBjb3JyZWN0IGltYWdlIGZpbGVcclxuICAgICAgICAgICAgd3JpdGVGaWxlU3luYyhcclxuICAgICAgICAgICAgICBpbmZvLm9wdGlvbnMuZXhwb3J0Lm91dGZpbGUsXHJcbiAgICAgICAgICAgICAgQnVmZmVyLmZyb20oaW5mby5yZXN1bHQsICdiYXNlNjQnKVxyXG4gICAgICAgICAgICApO1xyXG4gICAgICAgICAgfVxyXG4gICAgICAgIClcclxuICAgICAgKTtcclxuICAgIH1cclxuICB9XHJcblxyXG4gIHRyeSB7XHJcbiAgICAvLyBBd2FpdCBhbGwgZXhwb3J0cyBhcmUgZG9uZVxyXG4gICAgYXdhaXQgUHJvbWlzZS5hbGwoYmF0Y2hGdW5jdGlvbnMpO1xyXG5cclxuICAgIC8vIEtpbGwgcG9vbCBhbmQgY2xvc2UgYnJvd3NlciBhZnRlciBmaW5pc2hpbmcgYmF0Y2ggZXhwb3J0XHJcbiAgICBhd2FpdCBraWxsUG9vbCgpO1xyXG4gIH0gY2F0Y2ggKGVycm9yKSB7XHJcbiAgICB0aHJvdyBuZXcgRXhwb3J0RXJyb3IoXHJcbiAgICAgICdbY2hhcnRdIEVycm9yIGVuY291bnRlcmVkIGR1cmluZyBiYXRjaCBleHBvcnQuJ1xyXG4gICAgKS5zZXRFcnJvcihlcnJvcik7XHJcbiAgfVxyXG59O1xyXG5cclxuLyoqXHJcbiAqIFN0YXJ0cyBhIHNpbmdsZSBleHBvcnQgcHJvY2VzcyBiYXNlZCBvbiB0aGUgc3BlY2lmaWVkIG9wdGlvbnMuXHJcbiAqXHJcbiAqIEBwYXJhbSB7T2JqZWN0fSBvcHRpb25zIC0gVGhlIG9wdGlvbnMgb2JqZWN0IGNvbnRhaW5pbmcgY29uZmlndXJhdGlvbiBmb3JcclxuICogYSBzaW5nbGUgZXhwb3J0LlxyXG4gKlxyXG4gKiBAcmV0dXJucyB7UHJvbWlzZTx2b2lkPn0gQSBQcm9taXNlIHRoYXQgcmVzb2x2ZXMgb25jZSB0aGUgc2luZ2xlIGV4cG9ydFxyXG4gKiBwcm9jZXNzIGlzIGNvbXBsZXRlZC5cclxuICpcclxuICogQHRocm93cyB7RXhwb3J0RXJyb3J9IFRocm93cyBhbiBFeHBvcnRFcnJvciBpZiBhbiBlcnJvciBvY2N1cnMgZHVyaW5nXHJcbiAqIHRoZSBzaW5nbGUgZXhwb3J0IHByb2Nlc3MuXHJcbiAqL1xyXG5leHBvcnQgY29uc3Qgc2luZ2xlRXhwb3J0ID0gYXN5bmMgKG9wdGlvbnMpID0+IHtcclxuICAvLyBVc2UgaW5zdHIgb3IgaXRzIGFsaWFzLCBvcHRpb25zXHJcbiAgb3B0aW9ucy5leHBvcnQuaW5zdHIgPSBvcHRpb25zLmV4cG9ydC5pbnN0ciB8fCBvcHRpb25zLmV4cG9ydC5vcHRpb25zO1xyXG5cclxuICAvLyBQZXJmb3JtIGFuIGV4cG9ydFxyXG4gIGF3YWl0IHN0YXJ0RXhwb3J0KG9wdGlvbnMsIGFzeW5jIChlcnJvciwgaW5mbykgPT4ge1xyXG4gICAgLy8gRXhpdCBwcm9jZXNzIHdoZW4gZXJyb3JcclxuICAgIGlmIChlcnJvcikge1xyXG4gICAgICB0aHJvdyBlcnJvcjtcclxuICAgIH1cclxuXHJcbiAgICBjb25zdCB7IG91dGZpbGUsIHR5cGUgfSA9IGluZm8ub3B0aW9ucy5leHBvcnQ7XHJcblxyXG4gICAgLy8gU2F2ZSB0aGUgYmFzZTY0IGZyb20gYSBidWZmZXIgdG8gYSBjb3JyZWN0IGltYWdlIGZpbGVcclxuICAgIHdyaXRlRmlsZVN5bmMoXHJcbiAgICAgIG91dGZpbGUgfHwgYGNoYXJ0LiR7dHlwZX1gLFxyXG4gICAgICB0eXBlICE9PSAnc3ZnJyA/IEJ1ZmZlci5mcm9tKGluZm8ucmVzdWx0LCAnYmFzZTY0JykgOiBpbmZvLnJlc3VsdFxyXG4gICAgKTtcclxuXHJcbiAgICAvLyBLaWxsIHRoZSBwb29sXHJcbiAgICBhd2FpdCBraWxsUG9vbCgpO1xyXG4gIH0pO1xyXG59O1xyXG5cclxuLyoqXHJcbiAqIERldGVybWluZXMgdGhlIHNpemUgYW5kIHNjYWxlIGZvciBjaGFydCBleHBvcnQgYmFzZWQgb24gdGhlIHByb3ZpZGVkIG9wdGlvbnMuXHJcbiAqXHJcbiAqIEBwYXJhbSB7T2JqZWN0fSBvcHRpb25zIC0gVGhlIG9wdGlvbnMgb2JqZWN0IGNvbnRhaW5pbmcgY29uZmlndXJhdGlvbiBmb3JcclxuICogY2hhcnQgZXhwb3J0LlxyXG4gKlxyXG4gKiBAcmV0dXJucyB7T2JqZWN0fSBBbiBvYmplY3QgY29udGFpbmluZyB0aGUgY2FsY3VsYXRlZCBoZWlnaHQsIHdpZHRoLFxyXG4gKiBhbmQgc2NhbGUgZm9yIHRoZSBjaGFydCBleHBvcnQuXHJcbiAqL1xyXG5leHBvcnQgY29uc3QgZmluZENoYXJ0U2l6ZSA9IChvcHRpb25zKSA9PiB7XHJcbiAgY29uc3QgeyBjaGFydCwgZXhwb3J0aW5nIH0gPVxyXG4gICAgb3B0aW9ucy5leHBvcnQ/Lm9wdGlvbnMgfHwgaXNDb3JyZWN0SlNPTihvcHRpb25zLmV4cG9ydD8uaW5zdHIpO1xyXG5cclxuICAvLyBTZWUgaWYgZ2xvYmFsT3B0aW9ucyBob2xkcyBjaGFydCBvciBleHBvcnRpbmcgc2l6ZVxyXG4gIGNvbnN0IGdsb2JhbE9wdGlvbnMgPSBpc0NvcnJlY3RKU09OKG9wdGlvbnMuZXhwb3J0Py5nbG9iYWxPcHRpb25zKTtcclxuXHJcbiAgLy8gU2VjdXJlIHNjYWxlIHZhbHVlXHJcbiAgbGV0IHNjYWxlID1cclxuICAgIG9wdGlvbnMuZXhwb3J0Py5zY2FsZSB8fFxyXG4gICAgZXhwb3J0aW5nPy5zY2FsZSB8fFxyXG4gICAgZ2xvYmFsT3B0aW9ucz8uZXhwb3J0aW5nPy5zY2FsZSB8fFxyXG4gICAgb3B0aW9ucy5leHBvcnQ/LmRlZmF1bHRTY2FsZSB8fFxyXG4gICAgMTtcclxuXHJcbiAgLy8gdGhlIHNjYWxlIGNhbm5vdCBiZSBsb3dlciB0aGFuIDAuMSBhbmQgY2Fubm90IGJlIGhpZ2hlciB0aGFuIDUuMFxyXG4gIHNjYWxlID0gTWF0aC5tYXgoMC4xLCBNYXRoLm1pbihzY2FsZSwgNS4wKSk7XHJcblxyXG4gIC8vIHdlIHdhbnQgdG8gcm91bmQgdGhlIG51bWJlcnMgbGlrZSAwLjIzMjM0IC0+IDAuMjNcclxuICBzY2FsZSA9IHJvdW5kTnVtYmVyKHNjYWxlLCAyKTtcclxuXHJcbiAgLy8gRmluZCBjaGFydCBzaXplIGFuZCBzY2FsZVxyXG4gIGNvbnN0IHNpemUgPSB7XHJcbiAgICBoZWlnaHQ6XHJcbiAgICAgIG9wdGlvbnMuZXhwb3J0Py5oZWlnaHQgfHxcclxuICAgICAgZXhwb3J0aW5nPy5zb3VyY2VIZWlnaHQgfHxcclxuICAgICAgY2hhcnQ/LmhlaWdodCB8fFxyXG4gICAgICBnbG9iYWxPcHRpb25zPy5leHBvcnRpbmc/LnNvdXJjZUhlaWdodCB8fFxyXG4gICAgICBnbG9iYWxPcHRpb25zPy5jaGFydD8uaGVpZ2h0IHx8XHJcbiAgICAgIG9wdGlvbnMuZXhwb3J0Py5kZWZhdWx0SGVpZ2h0IHx8XHJcbiAgICAgIDQwMCxcclxuICAgIHdpZHRoOlxyXG4gICAgICBvcHRpb25zLmV4cG9ydD8ud2lkdGggfHxcclxuICAgICAgZXhwb3J0aW5nPy5zb3VyY2VXaWR0aCB8fFxyXG4gICAgICBjaGFydD8ud2lkdGggfHxcclxuICAgICAgZ2xvYmFsT3B0aW9ucz8uZXhwb3J0aW5nPy5zb3VyY2VXaWR0aCB8fFxyXG4gICAgICBnbG9iYWxPcHRpb25zPy5jaGFydD8ud2lkdGggfHxcclxuICAgICAgb3B0aW9ucy5leHBvcnQ/LmRlZmF1bHRXaWR0aCB8fFxyXG4gICAgICA2MDAsXHJcbiAgICBzY2FsZVxyXG4gIH07XHJcblxyXG4gIC8vIEdldCByaWQgb2YgcG90ZW50aWFsIHB4IGFuZCAlXHJcbiAgZm9yIChsZXQgW3BhcmFtLCB2YWx1ZV0gb2YgT2JqZWN0LmVudHJpZXMoc2l6ZSkpIHtcclxuICAgIHNpemVbcGFyYW1dID1cclxuICAgICAgdHlwZW9mIHZhbHVlID09PSAnc3RyaW5nJyA/ICt2YWx1ZS5yZXBsYWNlKC9weHwlL2dpLCAnJykgOiB2YWx1ZTtcclxuICB9XHJcbiAgcmV0dXJuIHNpemU7XHJcbn07XHJcblxyXG4vKipcclxuICogRnVuY3Rpb24gZm9yIGZpbmFsaXppbmcgb3B0aW9ucyBiZWZvcmUgZXhwb3J0LlxyXG4gKlxyXG4gKiBAcGFyYW0ge09iamVjdH0gb3B0aW9ucyAtIFRoZSBvcHRpb25zIG9iamVjdCBjb250YWluaW5nIGNvbmZpZ3VyYXRpb24gZm9yXHJcbiAqIHRoZSBleHBvcnQgcHJvY2Vzcy5cclxuICogQHBhcmFtIHtPYmplY3R9IGNoYXJ0SnNvbiAtIFRoZSBKU09OIHJlcHJlc2VudGF0aW9uIG9mIHRoZSBjaGFydC5cclxuICogQHBhcmFtIHtGdW5jdGlvbn0gZW5kQ2FsbGJhY2sgLSBUaGUgY2FsbGJhY2sgZnVuY3Rpb24gdG8gYmUgY2FsbGVkIHVwb25cclxuICogY29tcGxldGlvbiBvciBlcnJvci5cclxuICogQHBhcmFtIHtzdHJpbmd9IHN2ZyAtIFRoZSBTVkcgcmVwcmVzZW50YXRpb24gb2YgdGhlIGNoYXJ0LlxyXG4gKlxyXG4gKiBAcmV0dXJucyB7UHJvbWlzZTx2b2lkPn0gQSBQcm9taXNlIHRoYXQgcmVzb2x2ZXMgb25jZSB0aGUgZXhwb3J0IHByb2Nlc3NcclxuICogaXMgY29tcGxldGVkLlxyXG4gKi9cclxuY29uc3QgZG9FeHBvcnQgPSBhc3luYyAob3B0aW9ucywgY2hhcnRKc29uLCBlbmRDYWxsYmFjaywgc3ZnKSA9PiB7XHJcbiAgbGV0IHsgZXhwb3J0OiBleHBvcnRPcHRpb25zLCBjdXN0b21Mb2dpYzogY3VzdG9tTG9naWNPcHRpb25zIH0gPSBvcHRpb25zO1xyXG5cclxuICBjb25zdCBhbGxvd0NvZGVFeGVjdXRpb25TY29wZWQgPVxyXG4gICAgdHlwZW9mIGN1c3RvbUxvZ2ljT3B0aW9ucy5hbGxvd0NvZGVFeGVjdXRpb24gPT09ICdib29sZWFuJ1xyXG4gICAgICA/IGN1c3RvbUxvZ2ljT3B0aW9ucy5hbGxvd0NvZGVFeGVjdXRpb25cclxuICAgICAgOiBhbGxvd0NvZGVFeGVjdXRpb247XHJcblxyXG4gIGlmICghY3VzdG9tTG9naWNPcHRpb25zKSB7XHJcbiAgICBjdXN0b21Mb2dpY09wdGlvbnMgPSBvcHRpb25zLmN1c3RvbUxvZ2ljID0ge307XHJcbiAgfSBlbHNlIGlmIChhbGxvd0NvZGVFeGVjdXRpb25TY29wZWQpIHtcclxuICAgIGlmICh0eXBlb2Ygb3B0aW9ucy5jdXN0b21Mb2dpYy5yZXNvdXJjZXMgPT09ICdzdHJpbmcnKSB7XHJcbiAgICAgIC8vIFByb2Nlc3MgcmVzb3VyY2VzXHJcbiAgICAgIG9wdGlvbnMuY3VzdG9tTG9naWMucmVzb3VyY2VzID0gaGFuZGxlUmVzb3VyY2VzKFxyXG4gICAgICAgIG9wdGlvbnMuY3VzdG9tTG9naWMucmVzb3VyY2VzLFxyXG4gICAgICAgIHRvQm9vbGVhbihvcHRpb25zLmN1c3RvbUxvZ2ljLmFsbG93RmlsZVJlc291cmNlcylcclxuICAgICAgKTtcclxuICAgIH0gZWxzZSBpZiAoIW9wdGlvbnMuY3VzdG9tTG9naWMucmVzb3VyY2VzKSB7XHJcbiAgICAgIHRyeSB7XHJcbiAgICAgICAgY29uc3QgcmVzb3VyY2VzID0gcmVhZEZpbGVTeW5jKCdyZXNvdXJjZXMuanNvbicsICd1dGY4Jyk7XHJcbiAgICAgICAgb3B0aW9ucy5jdXN0b21Mb2dpYy5yZXNvdXJjZXMgPSBoYW5kbGVSZXNvdXJjZXMoXHJcbiAgICAgICAgICByZXNvdXJjZXMsXHJcbiAgICAgICAgICB0b0Jvb2xlYW4ob3B0aW9ucy5jdXN0b21Mb2dpYy5hbGxvd0ZpbGVSZXNvdXJjZXMpXHJcbiAgICAgICAgKTtcclxuICAgICAgfSBjYXRjaCAoZXJyb3IpIHtcclxuICAgICAgICBsb2dXaXRoU3RhY2soXHJcbiAgICAgICAgICAyLFxyXG4gICAgICAgICAgZXJyb3IsXHJcbiAgICAgICAgICBgW2NoYXJ0XSBVbmFibGUgdG8gbG9hZCB0aGUgZGVmYXVsdCByZXNvdXJjZXMuanNvbiBmaWxlLmBcclxuICAgICAgICApO1xyXG4gICAgICB9XHJcbiAgICB9XHJcbiAgfVxyXG5cclxuICAvLyBJZiB0aGUgYWxsb3dDb2RlRXhlY3V0aW9uIGZsYWcgaXNuJ3Qgc2V0LCB3ZSBzaG91bGQgcmVmdXNlIHRoZSB1c2FnZVxyXG4gIC8vIG9mIGNhbGxiYWNrLCByZXNvdXJjZXMsIGFuZCBjdXN0b20gY29kZS4gQWRkaXRpb25hbGx5LCB0aGUgd29ya2VyIHdpbGxcclxuICAvLyByZWZ1c2UgdG8gcnVuIGFyYml0cmFyeSBKYXZhU2NyaXB0LiBQcmlvcml0aXplZCBzaG91bGQgYmUgdGhlIHNjb3BlZFxyXG4gIC8vIG9wdGlvbiwgdGhlbiB3ZSBzaG91bGQgdGFrZSBhIGxvb2sgYXQgdGhlIG92ZXJhbGwgcG9vbCBvcHRpb24uXHJcbiAgaWYgKCFhbGxvd0NvZGVFeGVjdXRpb25TY29wZWQgJiYgY3VzdG9tTG9naWNPcHRpb25zKSB7XHJcbiAgICBpZiAoXHJcbiAgICAgIGN1c3RvbUxvZ2ljT3B0aW9ucy5jYWxsYmFjayB8fFxyXG4gICAgICBjdXN0b21Mb2dpY09wdGlvbnMucmVzb3VyY2VzIHx8XHJcbiAgICAgIGN1c3RvbUxvZ2ljT3B0aW9ucy5jdXN0b21Db2RlXHJcbiAgICApIHtcclxuICAgICAgLy8gU2VuZCBiYWNrIGEgZnJpZW5kbHkgbWVzc2FnZSBzYXlpbmcgdGhhdCB0aGUgZXhwb3J0ZXIgZG9lcyBub3Qgc3VwcG9ydFxyXG4gICAgICAvLyB0aGVzZSBzZXR0aW5ncy5cclxuICAgICAgcmV0dXJuIGVuZENhbGxiYWNrKFxyXG4gICAgICAgIG5ldyBFeHBvcnRFcnJvcihcclxuICAgICAgICAgIGBbY2hhcnRdIFRoZSAnY2FsbGJhY2snLCAncmVzb3VyY2VzJyBhbmQgJ2N1c3RvbUNvZGUnIG9wdGlvbnMgaGF2ZSBiZWVuIGRpc2FibGVkIGZvciB0aGlzIHNlcnZlci5gXHJcbiAgICAgICAgKVxyXG4gICAgICApO1xyXG4gICAgfVxyXG5cclxuICAgIC8vIFJlc2V0IGFsbCBhZGRpdGlvbmFsIGN1c3RvbSBjb2RlXHJcbiAgICBjdXN0b21Mb2dpY09wdGlvbnMuY2FsbGJhY2sgPSBmYWxzZTtcclxuICAgIGN1c3RvbUxvZ2ljT3B0aW9ucy5yZXNvdXJjZXMgPSBmYWxzZTtcclxuICAgIGN1c3RvbUxvZ2ljT3B0aW9ucy5jdXN0b21Db2RlID0gZmFsc2U7XHJcbiAgfVxyXG5cclxuICAvLyBDbGVhbiBwcm9wZXJ0aWVzIHRvIGtlZXAgaXQgbGVhbiBhbmQgbWVhblxyXG4gIGlmIChjaGFydEpzb24pIHtcclxuICAgIGNoYXJ0SnNvbi5jaGFydCA9IGNoYXJ0SnNvbi5jaGFydCB8fCB7fTtcclxuICAgIGNoYXJ0SnNvbi5leHBvcnRpbmcgPSBjaGFydEpzb24uZXhwb3J0aW5nIHx8IHt9O1xyXG4gICAgY2hhcnRKc29uLmV4cG9ydGluZy5lbmFibGVkID0gZmFsc2U7XHJcbiAgfVxyXG5cclxuICBleHBvcnRPcHRpb25zLmNvbnN0ciA9IGV4cG9ydE9wdGlvbnMuY29uc3RyIHx8ICdjaGFydCc7XHJcbiAgZXhwb3J0T3B0aW9ucy50eXBlID0gZml4VHlwZShleHBvcnRPcHRpb25zLnR5cGUsIGV4cG9ydE9wdGlvbnMub3V0ZmlsZSk7XHJcbiAgaWYgKGV4cG9ydE9wdGlvbnMudHlwZSA9PT0gJ3N2ZycpIHtcclxuICAgIGV4cG9ydE9wdGlvbnMud2lkdGggPSBmYWxzZTtcclxuICB9XHJcblxyXG4gIC8vIFByZXBhcmUgZ2xvYmFsIGFuZCB0aGVtZSBvcHRpb25zXHJcbiAgWydnbG9iYWxPcHRpb25zJywgJ3RoZW1lT3B0aW9ucyddLmZvckVhY2goKG9wdGlvbnNOYW1lKSA9PiB7XHJcbiAgICB0cnkge1xyXG4gICAgICBpZiAoZXhwb3J0T3B0aW9ucyAmJiBleHBvcnRPcHRpb25zW29wdGlvbnNOYW1lXSkge1xyXG4gICAgICAgIGlmIChcclxuICAgICAgICAgIHR5cGVvZiBleHBvcnRPcHRpb25zW29wdGlvbnNOYW1lXSA9PT0gJ3N0cmluZycgJiZcclxuICAgICAgICAgIGV4cG9ydE9wdGlvbnNbb3B0aW9uc05hbWVdLmVuZHNXaXRoKCcuanNvbicpXHJcbiAgICAgICAgKSB7XHJcbiAgICAgICAgICBleHBvcnRPcHRpb25zW29wdGlvbnNOYW1lXSA9IGlzQ29ycmVjdEpTT04oXHJcbiAgICAgICAgICAgIHJlYWRGaWxlU3luYyhleHBvcnRPcHRpb25zW29wdGlvbnNOYW1lXSwgJ3V0ZjgnKSxcclxuICAgICAgICAgICAgdHJ1ZVxyXG4gICAgICAgICAgKTtcclxuICAgICAgICB9IGVsc2Uge1xyXG4gICAgICAgICAgZXhwb3J0T3B0aW9uc1tvcHRpb25zTmFtZV0gPSBpc0NvcnJlY3RKU09OKFxyXG4gICAgICAgICAgICBleHBvcnRPcHRpb25zW29wdGlvbnNOYW1lXSxcclxuICAgICAgICAgICAgdHJ1ZVxyXG4gICAgICAgICAgKTtcclxuICAgICAgICB9XHJcbiAgICAgIH1cclxuICAgIH0gY2F0Y2ggKGVycm9yKSB7XHJcbiAgICAgIGV4cG9ydE9wdGlvbnNbb3B0aW9uc05hbWVdID0ge307XHJcbiAgICAgIGxvZ1dpdGhTdGFjaygyLCBlcnJvciwgYFtjaGFydF0gVGhlICcke29wdGlvbnNOYW1lfScgY2Fubm90IGJlIGxvYWRlZC5gKTtcclxuICAgIH1cclxuICB9KTtcclxuXHJcbiAgLy8gUHJlcGFyZSB0aGUgY3VzdG9tQ29kZVxyXG4gIGlmIChjdXN0b21Mb2dpY09wdGlvbnMuYWxsb3dDb2RlRXhlY3V0aW9uKSB7XHJcbiAgICB0cnkge1xyXG4gICAgICBjdXN0b21Mb2dpY09wdGlvbnMuY3VzdG9tQ29kZSA9IHdyYXBBcm91bmQoXHJcbiAgICAgICAgY3VzdG9tTG9naWNPcHRpb25zLmN1c3RvbUNvZGUsXHJcbiAgICAgICAgY3VzdG9tTG9naWNPcHRpb25zLmFsbG93RmlsZVJlc291cmNlc1xyXG4gICAgICApO1xyXG4gICAgfSBjYXRjaCAoZXJyb3IpIHtcclxuICAgICAgbG9nV2l0aFN0YWNrKDIsIGVycm9yLCBgW2NoYXJ0XSBUaGUgJ2N1c3RvbUNvZGUnIGNhbm5vdCBiZSBsb2FkZWQuYCk7XHJcbiAgICB9XHJcbiAgfVxyXG5cclxuICAvLyBHZXQgdGhlIGNhbGxiYWNrXHJcbiAgaWYgKFxyXG4gICAgY3VzdG9tTG9naWNPcHRpb25zICYmXHJcbiAgICBjdXN0b21Mb2dpY09wdGlvbnMuY2FsbGJhY2sgJiZcclxuICAgIGN1c3RvbUxvZ2ljT3B0aW9ucy5jYWxsYmFjaz8uaW5kZXhPZigneycpIDwgMFxyXG4gICkge1xyXG4gICAgLy8gVGhlIGFsbG93RmlsZVJlc291cmNlcyBpcyBhbHdheXMgc2V0IHRvIGZhbHNlIGZvciBIVFRQIHJlcXVlc3RzIHRvIGF2b2lkXHJcbiAgICAvLyBpbmplY3RpbmcgYXJiaXRyYXJ5IGZpbGVzIGZyb20gdGhlIGZzXHJcbiAgICBpZiAoY3VzdG9tTG9naWNPcHRpb25zLmFsbG93RmlsZVJlc291cmNlcykge1xyXG4gICAgICB0cnkge1xyXG4gICAgICAgIGN1c3RvbUxvZ2ljT3B0aW9ucy5jYWxsYmFjayA9IHJlYWRGaWxlU3luYyhcclxuICAgICAgICAgIGN1c3RvbUxvZ2ljT3B0aW9ucy5jYWxsYmFjayxcclxuICAgICAgICAgICd1dGY4J1xyXG4gICAgICAgICk7XHJcbiAgICAgIH0gY2F0Y2ggKGVycm9yKSB7XHJcbiAgICAgICAgY3VzdG9tTG9naWNPcHRpb25zLmNhbGxiYWNrID0gZmFsc2U7XHJcbiAgICAgICAgbG9nV2l0aFN0YWNrKDIsIGVycm9yLCBgW2NoYXJ0XSBUaGUgJ2NhbGxiYWNrJyBjYW5ub3QgYmUgbG9hZGVkLmApO1xyXG4gICAgICB9XHJcbiAgICB9IGVsc2Uge1xyXG4gICAgICBjdXN0b21Mb2dpY09wdGlvbnMuY2FsbGJhY2sgPSBmYWxzZTtcclxuICAgIH1cclxuICB9XHJcblxyXG4gIC8vIFNpemUgc2VhcmNoXHJcbiAgb3B0aW9ucy5leHBvcnQgPSB7XHJcbiAgICAuLi5vcHRpb25zLmV4cG9ydCxcclxuICAgIC4uLmZpbmRDaGFydFNpemUob3B0aW9ucylcclxuICB9O1xyXG5cclxuICAvLyBQb3N0IHRoZSB3b3JrIHRvIHRoZSBwb29sXHJcbiAgdHJ5IHtcclxuICAgIGNvbnN0IHJlc3VsdCA9IGF3YWl0IHBvc3RXb3JrKFxyXG4gICAgICBleHBvcnRPcHRpb25zLnN0ckluaiB8fCBjaGFydEpzb24gfHwgc3ZnLFxyXG4gICAgICBvcHRpb25zXHJcbiAgICApO1xyXG4gICAgcmV0dXJuIGVuZENhbGxiYWNrKGZhbHNlLCByZXN1bHQpO1xyXG4gIH0gY2F0Y2ggKGVycm9yKSB7XHJcbiAgICByZXR1cm4gZW5kQ2FsbGJhY2soZXJyb3IpO1xyXG4gIH1cclxufTtcclxuXHJcbi8qKlxyXG4gKiBQZXJmb3JtcyBhIGRpcmVjdCBpbmplY3Qgb2Ygb3B0aW9ucyBiZWZvcmUgZXhwb3J0LiBUaGUgZnVuY3Rpb24gYXR0ZW1wdHNcclxuICogdG8gc3RyaW5naWZ5IHRoZSBwcm92aWRlZCBvcHRpb25zIGFuZCByZW1vdmVzIHVubmVjZXNzYXJ5IGNoYXJhY3RlcnMsXHJcbiAqIGVuc3VyaW5nIGEgY2xlYW4gYW5kIGZvcm1hdHRlZCBpbnB1dC4gVGhlIHJlc3VsdGluZyBzdHJpbmcgaXMgc2F2ZWQgYXNcclxuICogYSBcInN0cmlnaHQgaW5qZWN0XCIgc3RyaW5nIGluIHRoZSBleHBvcnQgb3B0aW9ucy4gSXQgdGhlbiBpbnZva2VzIHRoZVxyXG4gKiBkb0V4cG9ydCBmdW5jdGlvbiB3aXRoIHRoZSB1cGRhdGVkIG9wdGlvbnMuXHJcbiAqXHJcbiAqIElNUE9SVEFOVDogRGFuZ2Vyb3VzIGFuZCBtdXN0IGJlIHVzZWQgZGVsaWJlcmF0ZWx5IGJ5IHNvbWVvbmUgd2hvIHNldHMgdXBcclxuICogYSBzZXJ2ZXIgKHNlZSB0aGUgIC0tYWxsb3dDb2RlRXhlY3V0aW9uIG9wdGlvbikuXHJcbiAqXHJcbiAqIEBwYXJhbSB7T2JqZWN0fSBvcHRpb25zIC0gVGhlIGV4cG9ydCBvcHRpb25zIGNvbnRhaW5pbmcgdGhlIGlucHV0XHJcbiAqIHRvIGJlIGluamVjdGVkLlxyXG4gKiBAcGFyYW0ge2Z1bmN0aW9ufSBlbmRDYWxsYmFjayAtIFRoZSBjYWxsYmFjayBmdW5jdGlvbiB0byBiZSBpbnZva2VkXHJcbiAqIGF0IHRoZSBlbmQgb2YgdGhlIHByb2Nlc3MuXHJcbiAqXHJcbiAqIEByZXR1cm5zIHtQcm9taXNlfSBBIFByb21pc2UgdGhhdCByZXNvbHZlcyB3aXRoIHRoZSByZXN1bHQgb2YgdGhlIGV4cG9ydFxyXG4gKiBvcGVyYXRpb24gb3IgcmVqZWN0cyB3aXRoIGFuIGVycm9yIGlmIGFueSBpc3N1ZXMgb2NjdXIgZHVyaW5nIHRoZSBwcm9jZXNzLlxyXG4gKi9cclxuY29uc3QgZG9TdHJhaWdodEluamVjdCA9IChvcHRpb25zLCBlbmRDYWxsYmFjaykgPT4ge1xyXG4gIHRyeSB7XHJcbiAgICBsZXQgc3RySW5qO1xyXG4gICAgbGV0IGluc3RyID0gb3B0aW9ucy5leHBvcnQuaW5zdHIgfHwgb3B0aW9ucy5leHBvcnQub3B0aW9ucztcclxuXHJcbiAgICBpZiAodHlwZW9mIGluc3RyICE9PSAnc3RyaW5nJykge1xyXG4gICAgICAvLyBUcnkgdG8gc3RyaW5naWZ5IG9wdGlvbnNcclxuICAgICAgc3RySW5qID0gaW5zdHIgPSBvcHRpb25zU3RyaW5naWZ5KFxyXG4gICAgICAgIGluc3RyLFxyXG4gICAgICAgIG9wdGlvbnMuY3VzdG9tTG9naWM/LmFsbG93Q29kZUV4ZWN1dGlvblxyXG4gICAgICApO1xyXG4gICAgfVxyXG4gICAgc3RySW5qID0gaW5zdHIucmVwbGFjZUFsbCgvXFx0fFxcbnxcXHIvZywgJycpLnRyaW0oKTtcclxuXHJcbiAgICAvLyBHZXQgcmlkIG9mIHRoZSA7XHJcbiAgICBpZiAoc3RySW5qW3N0ckluai5sZW5ndGggLSAxXSA9PT0gJzsnKSB7XHJcbiAgICAgIHN0ckluaiA9IHN0ckluai5zdWJzdHJpbmcoMCwgc3RySW5qLmxlbmd0aCAtIDEpO1xyXG4gICAgfVxyXG5cclxuICAgIC8vIFNhdmUgYXMgc3RyaWdodCBpbmplY3Qgc3RyaW5nXHJcbiAgICBvcHRpb25zLmV4cG9ydC5zdHJJbmogPSBzdHJJbmo7XHJcbiAgICByZXR1cm4gZG9FeHBvcnQob3B0aW9ucywgZmFsc2UsIGVuZENhbGxiYWNrKTtcclxuICB9IGNhdGNoIChlcnJvcikge1xyXG4gICAgcmV0dXJuIGVuZENhbGxiYWNrKFxyXG4gICAgICBuZXcgRXhwb3J0RXJyb3IoXHJcbiAgICAgICAgYFtjaGFydF0gTWFsZm9ybWVkIGlucHV0IGRldGVjdGVkIGZvciAke29wdGlvbnMuZXhwb3J0Py5yZXF1ZXN0SWQgfHwgJz8nfS4gUGxlYXNlIG1ha2Ugc3VyZSB0aGF0IHlvdXIgSlNPTi9KYXZhU2NyaXB0IG9wdGlvbnMgYXJlIHNlbnQgdXNpbmcgdGhlIFwib3B0aW9uc1wiIGF0dHJpYnV0ZSwgYW5kIHRoYXQgaWYgeW91J3JlIHVzaW5nIFNWRywgaXQgaXMgdW5lc2NhcGVkLmBcclxuICAgICAgKS5zZXRFcnJvcihlcnJvcilcclxuICAgICk7XHJcbiAgfVxyXG59O1xyXG5cclxuLyoqXHJcbiAqIEV4cG9ydHMgYSBzdHJpbmcgYmFzZWQgb24gdGhlIHByb3ZpZGVkIG9wdGlvbnMgYW5kIGludm9rZXMgYW4gZW5kIGNhbGxiYWNrLlxyXG4gKlxyXG4gKiBAcGFyYW0ge3N0cmluZ30gc3RyaW5nVG9FeHBvcnQgLSBUaGUgc3RyaW5nIGNvbnRlbnQgdG8gYmUgZXhwb3J0ZWQuXHJcbiAqIEBwYXJhbSB7T2JqZWN0fSBvcHRpb25zIC0gRXhwb3J0IG9wdGlvbnMsIGluY2x1ZGluZyBjdXN0b21Mb2dpYyB3aXRoXHJcbiAqIGFsbG93Q29kZUV4ZWN1dGlvbiBmbGFnLlxyXG4gKiBAcGFyYW0ge0Z1bmN0aW9ufSBlbmRDYWxsYmFjayAtIENhbGxiYWNrIGZ1bmN0aW9uIHRvIGJlIGludm9rZWQgYXQgdGhlIGVuZFxyXG4gKiBvZiB0aGUgZXhwb3J0IHByb2Nlc3MuXHJcbiAqXHJcbiAqIEByZXR1cm5zIHthbnl9IFJlc3VsdCBvZiB0aGUgZXhwb3J0IHByb2Nlc3Mgb3IgYW4gZXJyb3IgaWYgZW5jb3VudGVyZWQuXHJcbiAqL1xyXG5jb25zdCBleHBvcnRBc1N0cmluZyA9IChzdHJpbmdUb0V4cG9ydCwgb3B0aW9ucywgZW5kQ2FsbGJhY2spID0+IHtcclxuICBjb25zdCB7IGFsbG93Q29kZUV4ZWN1dGlvbiB9ID0gb3B0aW9ucy5jdXN0b21Mb2dpYztcclxuXHJcbiAgLy8gQ2hlY2sgaWYgaXQgaXMgU1ZHXHJcbiAgaWYgKFxyXG4gICAgc3RyaW5nVG9FeHBvcnQuaW5kZXhPZignPHN2ZycpID49IDAgfHxcclxuICAgIHN0cmluZ1RvRXhwb3J0LmluZGV4T2YoJzw/eG1sJykgPj0gMFxyXG4gICkge1xyXG4gICAgbG9nKDQsICdbY2hhcnRdIFBhcnNpbmcgaW5wdXQgYXMgU1ZHLicpO1xyXG4gICAgcmV0dXJuIGRvRXhwb3J0KG9wdGlvbnMsIGZhbHNlLCBlbmRDYWxsYmFjaywgc3RyaW5nVG9FeHBvcnQpO1xyXG4gIH1cclxuXHJcbiAgdHJ5IHtcclxuICAgIC8vIFRyeSB0byBwYXJzZSB0byBKU09OIGFuZCBjYWxsIHRoZSBkb0V4cG9ydCBmdW5jdGlvblxyXG4gICAgY29uc3QgY2hhcnRKU09OID0gSlNPTi5wYXJzZShzdHJpbmdUb0V4cG9ydC5yZXBsYWNlQWxsKC9cXHR8XFxufFxcci9nLCAnICcpKTtcclxuXHJcbiAgICAvLyBJZiBhIGNvcnJlY3QgSlNPTiwgZG8gdGhlIGV4cG9ydFxyXG4gICAgcmV0dXJuIGRvRXhwb3J0KG9wdGlvbnMsIGNoYXJ0SlNPTiwgZW5kQ2FsbGJhY2spO1xyXG4gIH0gY2F0Y2ggKGVycm9yKSB7XHJcbiAgICAvLyBOb3QgYSB2YWxpZCBKU09OXHJcbiAgICBpZiAodG9Cb29sZWFuKGFsbG93Q29kZUV4ZWN1dGlvbikpIHtcclxuICAgICAgcmV0dXJuIGRvU3RyYWlnaHRJbmplY3Qob3B0aW9ucywgZW5kQ2FsbGJhY2spO1xyXG4gICAgfSBlbHNlIHtcclxuICAgICAgLy8gRG8gbm90IGFsbG93IHN0cmFpZ2h0IGluamVjdGlvbiB3aXRob3V0IHRoZSBhbGxvd0NvZGVFeGVjdXRpb24gZmxhZ1xyXG4gICAgICByZXR1cm4gZW5kQ2FsbGJhY2soXHJcbiAgICAgICAgbmV3IEV4cG9ydEVycm9yKFxyXG4gICAgICAgICAgJ1tjaGFydF0gT25seSBKU09OIGNvbmZpZ3VyYXRpb25zIGFuZCBTVkcgYXJlIGFsbG93ZWQgZm9yIHRoaXMgc2VydmVyLiBJZiB0aGlzIGlzIHlvdXIgc2VydmVyLCBKYXZhU2NyaXB0IGN1c3RvbSBjb2RlIGNhbiBiZSBlbmFibGVkIGJ5IHN0YXJ0aW5nIHRoZSBzZXJ2ZXIgd2l0aCB0aGUgLS1hbGxvd0NvZGVFeGVjdXRpb24gZmxhZy4nXHJcbiAgICAgICAgKS5zZXRFcnJvcihlcnJvcilcclxuICAgICAgKTtcclxuICAgIH1cclxuICB9XHJcbn07XHJcblxyXG4vKipcclxuICogUmV0cmlldmVzIGFuZCByZXR1cm5zIHRoZSBjdXJyZW50IHN0YXR1cyBvZiBjb2RlIGV4ZWN1dGlvbiBwZXJtaXNzaW9uLlxyXG4gKlxyXG4gKiBAcmV0dXJucyB7YW55fSBUaGUgdmFsdWUgb2YgYWxsb3dDb2RlRXhlY3V0aW9uLlxyXG4gKi9cclxuZXhwb3J0IGNvbnN0IGdldEFsbG93Q29kZUV4ZWN1dGlvbiA9ICgpID0+IGFsbG93Q29kZUV4ZWN1dGlvbjtcclxuXHJcbi8qKlxyXG4gKiBTZXRzIHRoZSBjb2RlIGV4ZWN1dGlvbiBwZXJtaXNzaW9uIGJhc2VkIG9uIHRoZSBwcm92aWRlZCBib29sZWFuIHZhbHVlLlxyXG4gKlxyXG4gKiBAcGFyYW0ge2FueX0gdmFsdWUgLSBUaGUgdmFsdWUgdG8gYmUgY29udmVydGVkIGFuZCBhc3NpZ25lZFxyXG4gKiB0byBhbGxvd0NvZGVFeGVjdXRpb24uXHJcbiAqL1xyXG5leHBvcnQgY29uc3Qgc2V0QWxsb3dDb2RlRXhlY3V0aW9uID0gKHZhbHVlKSA9PiB7XHJcbiAgYWxsb3dDb2RlRXhlY3V0aW9uID0gdG9Cb29sZWFuKHZhbHVlKTtcclxufTtcclxuXHJcbmV4cG9ydCBkZWZhdWx0IHtcclxuICBiYXRjaEV4cG9ydCxcclxuICBzaW5nbGVFeHBvcnQsXHJcbiAgZ2V0QWxsb3dDb2RlRXhlY3V0aW9uLFxyXG4gIHNldEFsbG93Q29kZUV4ZWN1dGlvbixcclxuICBzdGFydEV4cG9ydCxcclxuICBmaW5kQ2hhcnRTaXplXHJcbn07XHJcbiIsImltcG9ydCB7IGxvZ1dpdGhTdGFjayB9IGZyb20gJy4uL2xvZ2dlci5qcyc7XHJcbmltcG9ydCB7IGVudnMgfSBmcm9tICcuLi9lbnZzLmpzJztcclxuXHJcbi8qKlxyXG4gKiBNaWRkbGV3YXJlIGZvciBsb2dnaW5nIGVycm9ycyB3aXRoIHN0YWNrIHRyYWNlIGFuZCBoYW5kbGluZyBlcnJvciByZXNwb25zZS5cclxuICpcclxuICogQHBhcmFtIHtFcnJvcn0gZXJyb3IgLSBUaGUgZXJyb3Igb2JqZWN0LlxyXG4gKiBAcGFyYW0ge0V4cHJlc3MuUmVxdWVzdH0gcmVxIC0gVGhlIEV4cHJlc3MgcmVxdWVzdCBvYmplY3QuXHJcbiAqIEBwYXJhbSB7RXhwcmVzcy5SZXNwb25zZX0gcmVzIC0gVGhlIEV4cHJlc3MgcmVzcG9uc2Ugb2JqZWN0LlxyXG4gKiBAcGFyYW0ge0Z1bmN0aW9ufSBuZXh0IC0gVGhlIG5leHQgbWlkZGxld2FyZSBmdW5jdGlvbi5cclxuICovXHJcbmNvbnN0IGxvZ0Vycm9yTWlkZGxld2FyZSA9IChlcnJvciwgcmVxLCByZXMsIG5leHQpID0+IHtcclxuICAvLyBEaXNwbGF5IHRoZSBlcnJvciB3aXRoIHN0YWNrIGluIGEgY29ycmVjdCBmb3JtYXRcclxuICBsb2dXaXRoU3RhY2soMSwgZXJyb3IpO1xyXG5cclxuICAvLyBEZWxldGUgdGhlIHN0YWNrIGZvciB0aGUgZW52aXJvbm1lbnQgb3RoZXIgdGhhbiB0aGUgZGV2ZWxvcG1lbnRcclxuICBpZiAoZW52cy5OT0RFX0VOViAhPT0gJ2RldmVsb3BtZW50Jykge1xyXG4gICAgZGVsZXRlIGVycm9yLnN0YWNrO1xyXG4gIH1cclxuXHJcbiAgLy8gQ2FsbCB0aGUgcmV0dXJuRXJyb3JNaWRkbGV3YXJlXHJcbiAgbmV4dChlcnJvcik7XHJcbn07XHJcblxyXG4vKipcclxuICogTWlkZGxld2FyZSBmb3IgcmV0dXJuaW5nIGVycm9yIHJlc3BvbnNlLlxyXG4gKlxyXG4gKiBAcGFyYW0ge0Vycm9yfSBlcnJvciAtIFRoZSBlcnJvciBvYmplY3QuXHJcbiAqIEBwYXJhbSB7RXhwcmVzcy5SZXF1ZXN0fSByZXEgLSBUaGUgRXhwcmVzcyByZXF1ZXN0IG9iamVjdC5cclxuICogQHBhcmFtIHtFeHByZXNzLlJlc3BvbnNlfSByZXMgLSBUaGUgRXhwcmVzcyByZXNwb25zZSBvYmplY3QuXHJcbiAqIEBwYXJhbSB7RnVuY3Rpb259IG5leHQgLSBUaGUgbmV4dCBtaWRkbGV3YXJlIGZ1bmN0aW9uLlxyXG4gKi9cclxuY29uc3QgcmV0dXJuRXJyb3JNaWRkbGV3YXJlID0gKGVycm9yLCByZXEsIHJlcywgbmV4dCkgPT4ge1xyXG4gIC8vIEdhdGhlciBhbGwgcmVxdWllZCBpbmZvcm1hdGlvbiBmb3IgdGhlIHJlc3BvbnNlXHJcbiAgY29uc3QgeyBzdGF0dXNDb2RlOiBzdENvZGUsIHN0YXR1cywgbWVzc2FnZSwgc3RhY2sgfSA9IGVycm9yO1xyXG4gIGNvbnN0IHN0YXR1c0NvZGUgPSBzdENvZGUgfHwgc3RhdHVzIHx8IDUwMDtcclxuXHJcbiAgLy8gU2V0IGFuZCByZXR1cm4gcmVzcG9uc2VcclxuICByZXMuc3RhdHVzKHN0YXR1c0NvZGUpLmpzb24oeyBzdGF0dXNDb2RlLCBtZXNzYWdlLCBzdGFjayB9KTtcclxufTtcclxuXHJcbmV4cG9ydCBkZWZhdWx0IChhcHApID0+IHtcclxuICAvLyBBZGQgbG9nIGVycm9yIG1pZGRsZXdhcmVcclxuICBhcHAudXNlKGxvZ0Vycm9yTWlkZGxld2FyZSk7XHJcblxyXG4gIC8vIEFkZCBzZXQgc3RhdHVzIGFuZCByZXR1cm4gZXJyb3IgbWlkZGxld2FyZVxyXG4gIGFwcC51c2UocmV0dXJuRXJyb3JNaWRkbGV3YXJlKTtcclxufTtcclxuIiwiLyoqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKipcclxuXHJcbkhpZ2hjaGFydHMgRXhwb3J0IFNlcnZlclxyXG5cclxuQ29weXJpZ2h0IChjKSAyMDE2LTIwMjQsIEhpZ2hzb2Z0XHJcblxyXG5MaWNlbmNlZCB1bmRlciB0aGUgTUlUIGxpY2VuY2UuXHJcblxyXG5BZGRpdGlvbmFsbHkgYSB2YWxpZCBIaWdoY2hhcnRzIGxpY2Vuc2UgaXMgcmVxdWlyZWQgZm9yIHVzZS5cclxuXHJcblNlZSBMSUNFTlNFIGZpbGUgaW4gcm9vdCBmb3IgZGV0YWlscy5cclxuXHJcbioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKiovXHJcblxyXG5pbXBvcnQgcmF0ZUxpbWl0IGZyb20gJ2V4cHJlc3MtcmF0ZS1saW1pdCc7XHJcblxyXG5pbXBvcnQgeyBsb2cgfSBmcm9tICcuLi9sb2dnZXIuanMnO1xyXG5cclxuLyoqXHJcbiAqIE1pZGRsZXdhcmUgZm9yIGVuYWJsaW5nIHJhdGUgbGltaXRpbmcgb24gdGhlIHNwZWNpZmllZCBFeHByZXNzIGFwcC5cclxuICpcclxuICogQHBhcmFtIHtFeHByZXNzfSBhcHAgLSBUaGUgRXhwcmVzcyBhcHAgaW5zdGFuY2UuXHJcbiAqIEBwYXJhbSB7T2JqZWN0fSBsaW1pdENvbmZpZyAtIENvbmZpZ3VyYXRpb24gb3B0aW9ucyBmb3IgcmF0ZSBsaW1pdGluZy5cclxuICovXHJcbmV4cG9ydCBkZWZhdWx0IChhcHAsIGxpbWl0Q29uZmlnKSA9PiB7XHJcbiAgY29uc3QgbXNnID1cclxuICAgICdUb28gbWFueSByZXF1ZXN0cywgeW91IGhhdmUgYmVlbiByYXRlIGxpbWl0ZWQuIFBsZWFzZSB0cnkgYWdhaW4gbGF0ZXIuJztcclxuXHJcbiAgLy8gT3B0aW9ucyBmb3IgdGhlIHJhdGUgbGltaXRlclxyXG4gIGNvbnN0IHJhdGVPcHRpb25zID0ge1xyXG4gICAgbWF4OiBsaW1pdENvbmZpZy5tYXhSZXF1ZXN0cyB8fCAzMCxcclxuICAgIHdpbmRvdzogbGltaXRDb25maWcud2luZG93IHx8IDEsXHJcbiAgICBkZWxheTogbGltaXRDb25maWcuZGVsYXkgfHwgMCxcclxuICAgIHRydXN0UHJveHk6IGxpbWl0Q29uZmlnLnRydXN0UHJveHkgfHwgZmFsc2UsXHJcbiAgICBza2lwS2V5OiBsaW1pdENvbmZpZy5za2lwS2V5IHx8IGZhbHNlLFxyXG4gICAgc2tpcFRva2VuOiBsaW1pdENvbmZpZy5za2lwVG9rZW4gfHwgZmFsc2VcclxuICB9O1xyXG5cclxuICAvLyBTZXQgaWYgYmVoaW5kIGEgcHJveHlcclxuICBpZiAocmF0ZU9wdGlvbnMudHJ1c3RQcm94eSkge1xyXG4gICAgYXBwLmVuYWJsZSgndHJ1c3QgcHJveHknKTtcclxuICB9XHJcblxyXG4gIC8vIENyZWF0ZSBhIGxpbWl0ZXJcclxuICBjb25zdCBsaW1pdGVyID0gcmF0ZUxpbWl0KHtcclxuICAgIHdpbmRvd01zOiByYXRlT3B0aW9ucy53aW5kb3cgKiA2MCAqIDEwMDAsXHJcbiAgICAvLyBMaW1pdCBlYWNoIElQIHRvIDEwMCByZXF1ZXN0cyBwZXIgd2luZG93TXNcclxuICAgIG1heDogcmF0ZU9wdGlvbnMubWF4LFxyXG4gICAgLy8gRGlzYWJsZSBkZWxheWluZywgZnVsbCBzcGVlZCB1bnRpbCB0aGUgbWF4IGxpbWl0IGlzIHJlYWNoZWRcclxuICAgIGRlbGF5TXM6IHJhdGVPcHRpb25zLmRlbGF5LFxyXG4gICAgaGFuZGxlcjogKHJlcXVlc3QsIHJlc3BvbnNlKSA9PiB7XHJcbiAgICAgIHJlc3BvbnNlLmZvcm1hdCh7XHJcbiAgICAgICAganNvbjogKCkgPT4ge1xyXG4gICAgICAgICAgcmVzcG9uc2Uuc3RhdHVzKDQyOSkuc2VuZCh7IG1lc3NhZ2U6IG1zZyB9KTtcclxuICAgICAgICB9LFxyXG4gICAgICAgIGRlZmF1bHQ6ICgpID0+IHtcclxuICAgICAgICAgIHJlc3BvbnNlLnN0YXR1cyg0MjkpLnNlbmQobXNnKTtcclxuICAgICAgICB9XHJcbiAgICAgIH0pO1xyXG4gICAgfSxcclxuICAgIHNraXA6IChyZXF1ZXN0KSA9PiB7XHJcbiAgICAgIC8vIEFsbG93IGJ5cGFzc2luZyB0aGUgbGltaXRlciBpZiBhIHZhbGlkIGtleS90b2tlbiBoYXMgYmVlbiBzZW50XHJcbiAgICAgIGlmIChcclxuICAgICAgICByYXRlT3B0aW9ucy5za2lwS2V5ICE9PSBmYWxzZSAmJlxyXG4gICAgICAgIHJhdGVPcHRpb25zLnNraXBUb2tlbiAhPT0gZmFsc2UgJiZcclxuICAgICAgICByZXF1ZXN0LnF1ZXJ5LmtleSA9PT0gcmF0ZU9wdGlvbnMuc2tpcEtleSAmJlxyXG4gICAgICAgIHJlcXVlc3QucXVlcnkuYWNjZXNzX3Rva2VuID09PSByYXRlT3B0aW9ucy5za2lwVG9rZW5cclxuICAgICAgKSB7XHJcbiAgICAgICAgbG9nKDQsICdbcmF0ZSBsaW1pdGluZ10gU2tpcHBpbmcgcmF0ZSBsaW1pdGVyLicpO1xyXG4gICAgICAgIHJldHVybiB0cnVlO1xyXG4gICAgICB9XHJcbiAgICAgIHJldHVybiBmYWxzZTtcclxuICAgIH1cclxuICB9KTtcclxuXHJcbiAgLy8gVXNlIGEgbGltaXRlciBhcyBhIG1pZGRsZXdhcmVcclxuICBhcHAudXNlKGxpbWl0ZXIpO1xyXG5cclxuICBsb2coXHJcbiAgICAzLFxyXG4gICAgYFtyYXRlIGxpbWl0aW5nXSBFbmFibGVkIHJhdGUgbGltaXRpbmcgd2l0aCAke3JhdGVPcHRpb25zLm1heH0gcmVxdWVzdHMgcGVyICR7cmF0ZU9wdGlvbnMud2luZG93fSBtaW51dGUgZm9yIGVhY2ggSVAsIHRydXN0aW5nIHByb3h5OiAke3JhdGVPcHRpb25zLnRydXN0UHJveHl9LmBcclxuICApO1xyXG59O1xyXG4iLCJpbXBvcnQgRXhwb3J0RXJyb3IgZnJvbSAnLi9FeHBvcnRFcnJvci5qcyc7XHJcblxyXG5jbGFzcyBIdHRwRXJyb3IgZXh0ZW5kcyBFeHBvcnRFcnJvciB7XHJcbiAgY29uc3RydWN0b3IobWVzc2FnZSwgc3RhdHVzKSB7XHJcbiAgICBzdXBlcihtZXNzYWdlKTtcclxuICAgIHRoaXMuc3RhdHVzID0gdGhpcy5zdGF0dXNDb2RlID0gc3RhdHVzO1xyXG4gIH1cclxuXHJcbiAgc2V0U3RhdHVzKHN0YXR1cykge1xyXG4gICAgdGhpcy5zdGF0dXMgPSBzdGF0dXM7XHJcbiAgICByZXR1cm4gdGhpcztcclxuICB9XHJcbn1cclxuXHJcbmV4cG9ydCBkZWZhdWx0IEh0dHBFcnJvcjtcclxuIiwiLyoqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKipcclxuXHJcbkhpZ2hjaGFydHMgRXhwb3J0IFNlcnZlclxyXG5cclxuQ29weXJpZ2h0IChjKSAyMDE2LTIwMjQsIEhpZ2hzb2Z0XHJcblxyXG5MaWNlbmNlZCB1bmRlciB0aGUgTUlUIGxpY2VuY2UuXHJcblxyXG5BZGRpdGlvbmFsbHkgYSB2YWxpZCBIaWdoY2hhcnRzIGxpY2Vuc2UgaXMgcmVxdWlyZWQgZm9yIHVzZS5cclxuXHJcblNlZSBMSUNFTlNFIGZpbGUgaW4gcm9vdCBmb3IgZGV0YWlscy5cclxuXHJcbioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKiovXHJcblxyXG5pbXBvcnQgeyB2NCBhcyB1dWlkIH0gZnJvbSAndXVpZCc7XHJcblxyXG5pbXBvcnQgeyBnZXRBbGxvd0NvZGVFeGVjdXRpb24sIHN0YXJ0RXhwb3J0IH0gZnJvbSAnLi4vLi4vY2hhcnQuanMnO1xyXG5pbXBvcnQgeyBnZXRPcHRpb25zLCBtZXJnZUNvbmZpZ09wdGlvbnMgfSBmcm9tICcuLi8uLi9jb25maWcuanMnO1xyXG5pbXBvcnQgeyBsb2cgfSBmcm9tICcuLi8uLi9sb2dnZXIuanMnO1xyXG5pbXBvcnQge1xyXG4gIGZpeFR5cGUsXHJcbiAgaXNDb3JyZWN0SlNPTixcclxuICBpc09iamVjdEVtcHR5LFxyXG4gIGlzUHJpdmF0ZVJhbmdlVXJsRm91bmQsXHJcbiAgb3B0aW9uc1N0cmluZ2lmeSxcclxuICBtZWFzdXJlVGltZVxyXG59IGZyb20gJy4uLy4uL3V0aWxzLmpzJztcclxuXHJcbmltcG9ydCBIdHRwRXJyb3IgZnJvbSAnLi4vLi4vZXJyb3JzL0h0dHBFcnJvci5qcyc7XHJcblxyXG4vLyBSZXZlcnNlZCBNSU1FIHR5cGVzXHJcbmNvbnN0IHJldmVyc2VkTWltZSA9IHtcclxuICBwbmc6ICdpbWFnZS9wbmcnLFxyXG4gIGpwZWc6ICdpbWFnZS9qcGVnJyxcclxuICBnaWY6ICdpbWFnZS9naWYnLFxyXG4gIHBkZjogJ2FwcGxpY2F0aW9uL3BkZicsXHJcbiAgc3ZnOiAnaW1hZ2Uvc3ZnK3htbCdcclxufTtcclxuXHJcbi8vIFRoZSByZXF1ZXN0cyBjb3VudGVyXHJcbmxldCByZXF1ZXN0c0NvdW50ZXIgPSAwO1xyXG5cclxuLy8gVGhlIGFycmF5IG9mIGNhbGxiYWNrcyB0byBjYWxsIGJlZm9yZSBhIHJlcXVlc3RcclxuY29uc3QgYmVmb3JlUmVxdWVzdCA9IFtdO1xyXG5cclxuLy8gVGhlIGFycmF5IG9mIGNhbGxiYWNrcyB0byBjYWxsIGFmdGVyIGEgcmVxdWVzdFxyXG5jb25zdCBhZnRlclJlcXVlc3QgPSBbXTtcclxuXHJcbi8qKlxyXG4gKiBJbnZva2VzIGFuIGFycmF5IG9mIGNhbGxiYWNrIGZ1bmN0aW9ucyB3aXRoIHNwZWNpZmllZCBwYXJhbWV0ZXJzLCBhbGxvd2luZ1xyXG4gKiBjdXN0b21pemF0aW9uIG9mIHJlcXVlc3QgaGFuZGxpbmcuXHJcbiAqXHJcbiAqIEBwYXJhbSB7RnVuY3Rpb25bXX0gY2FsbGJhY2tzIC0gQW4gYXJyYXkgb2YgY2FsbGJhY2sgZnVuY3Rpb25zXHJcbiAqIHRvIGJlIGV4ZWN1dGVkLlxyXG4gKiBAcGFyYW0ge0V4cHJlc3MuUmVxdWVzdH0gcmVxdWVzdCAtIFRoZSBFeHByZXNzIHJlcXVlc3Qgb2JqZWN0LlxyXG4gKiBAcGFyYW0ge0V4cHJlc3MuUmVzcG9uc2V9IHJlc3BvbnNlIC0gVGhlIEV4cHJlc3MgcmVzcG9uc2Ugb2JqZWN0LlxyXG4gKiBAcGFyYW0ge09iamVjdH0gZGF0YSAtIEFuIG9iamVjdCBjb250YWluaW5nIHBhcmFtZXRlcnMgbGlrZSBpZCwgdW5pcXVlSWQsXHJcbiAqIHR5cGUsIGFuZCBib2R5LlxyXG4gKlxyXG4gKiBAcmV0dXJucyB7Ym9vbGVhbn0gLSBSZXR1cm5zIGEgYm9vbGVhbiBpbmRpY2F0aW5nIHRoZSBvdmVyYWxsIHJlc3VsdFxyXG4gKiBvZiB0aGUgY2FsbGJhY2sgaW52b2NhdGlvbnMuXHJcbiAqL1xyXG5jb25zdCBkb0NhbGxiYWNrcyA9IChjYWxsYmFja3MsIHJlcXVlc3QsIHJlc3BvbnNlLCBkYXRhKSA9PiB7XHJcbiAgbGV0IHJlc3VsdCA9IHRydWU7XHJcbiAgY29uc3QgeyBpZCwgdW5pcXVlSWQsIHR5cGUsIGJvZHkgfSA9IGRhdGE7XHJcblxyXG4gIGNhbGxiYWNrcy5zb21lKChjYWxsYmFjaykgPT4ge1xyXG4gICAgaWYgKGNhbGxiYWNrKSB7XHJcbiAgICAgIGxldCBjYWxsUmVzcG9uc2UgPSBjYWxsYmFjayhyZXF1ZXN0LCByZXNwb25zZSwgaWQsIHVuaXF1ZUlkLCB0eXBlLCBib2R5KTtcclxuXHJcbiAgICAgIGlmIChjYWxsUmVzcG9uc2UgIT09IHVuZGVmaW5lZCAmJiBjYWxsUmVzcG9uc2UgIT09IHRydWUpIHtcclxuICAgICAgICByZXN1bHQgPSBjYWxsUmVzcG9uc2U7XHJcbiAgICAgIH1cclxuXHJcbiAgICAgIHJldHVybiB0cnVlO1xyXG4gICAgfVxyXG4gIH0pO1xyXG5cclxuICByZXR1cm4gcmVzdWx0O1xyXG59O1xyXG5cclxuLyoqXHJcbiAqIEhhbmRsZXMgdGhlIGV4cG9ydCByZXF1ZXN0cyBmcm9tIHRoZSBjbGllbnQuXHJcbiAqXHJcbiAqIEBwYXJhbSB7RXhwcmVzcy5SZXF1ZXN0fSByZXF1ZXN0IC0gVGhlIEV4cHJlc3MgcmVxdWVzdCBvYmplY3QuXHJcbiAqIEBwYXJhbSB7RXhwcmVzcy5SZXNwb25zZX0gcmVzcG9uc2UgLSBUaGUgRXhwcmVzcyByZXNwb25zZSBvYmplY3QuXHJcbiAqIEBwYXJhbSB7RnVuY3Rpb259IG5leHQgLSBUaGUgbmV4dCBtaWRkbGV3YXJlIGZ1bmN0aW9uLlxyXG4gKlxyXG4gKiBAcmV0dXJucyB7UHJvbWlzZTx2b2lkPn0gLSBBIHByb21pc2UgdGhhdCByZXNvbHZlcyBvbmNlIHRoZSBleHBvcnQgcHJvY2Vzc1xyXG4gKiBpcyBjb21wbGV0ZS5cclxuICovXHJcbmNvbnN0IGV4cG9ydEhhbmRsZXIgPSBhc3luYyAocmVxdWVzdCwgcmVzcG9uc2UsIG5leHQpID0+IHtcclxuICB0cnkge1xyXG4gICAgLy8gU3RhcnQgY291bnRpbmcgdGltZVxyXG4gICAgY29uc3Qgc3RvcENvdW50ZXIgPSBtZWFzdXJlVGltZSgpO1xyXG5cclxuICAgIC8vIENyZWF0ZSBhIHVuaXF1ZSBJRCBmb3IgYSByZXF1ZXN0XHJcbiAgICBjb25zdCB1bmlxdWVJZCA9IHV1aWQoKS5yZXBsYWNlKC8tL2csICcnKTtcclxuXHJcbiAgICAvLyBHZXQgdGhlIGN1cnJlbnQgc2VydmVyJ3MgZ2VuZXJhbCBvcHRpb25zXHJcbiAgICBjb25zdCBkZWZhdWx0T3B0aW9ucyA9IGdldE9wdGlvbnMoKTtcclxuXHJcbiAgICBjb25zdCBib2R5ID0gcmVxdWVzdC5ib2R5O1xyXG4gICAgY29uc3QgaWQgPSArK3JlcXVlc3RzQ291bnRlcjtcclxuXHJcbiAgICBsZXQgdHlwZSA9IGZpeFR5cGUoYm9keS50eXBlKTtcclxuXHJcbiAgICAvLyBUaHJvdyAnQmFkIFJlcXVlc3QnIGlmIHRoZXJlJ3Mgbm8gYm9keVxyXG4gICAgaWYgKCFib2R5IHx8IGlzT2JqZWN0RW1wdHkoYm9keSkpIHtcclxuICAgICAgdGhyb3cgbmV3IEh0dHBFcnJvcihcclxuICAgICAgICAnVGhlIHJlcXVlc3QgYm9keSBpcyByZXF1aXJlZC4gUGxlYXNlIGVuc3VyZSB0aGF0IHlvdXIgQ29udGVudC1UeXBlIGhlYWRlciBpcyBjb3JyZWN0IChhY2NlcHRlZCB0eXBlcyBhcmUgYXBwbGljYXRpb24vanNvbiBhbmQgbXVsdGlwYXJ0L2Zvcm0tZGF0YSkuJyxcclxuICAgICAgICA0MDBcclxuICAgICAgKTtcclxuICAgIH1cclxuXHJcbiAgICAvLyBBbGwgb2YgdGhlIGJlbG93IGNhbiBiZSB1c2VkXHJcbiAgICBsZXQgaW5zdHIgPSBpc0NvcnJlY3RKU09OKGJvZHkuaW5maWxlIHx8IGJvZHkub3B0aW9ucyB8fCBib2R5LmRhdGEpO1xyXG5cclxuICAgIC8vIFRocm93ICdCYWQgUmVxdWVzdCcgaWYgdGhlcmUncyBubyBKU09OIG9yIFNWRyB0byBleHBvcnRcclxuICAgIGlmICghaW5zdHIgJiYgIWJvZHkuc3ZnKSB7XHJcbiAgICAgIGxvZyhcclxuICAgICAgICAyLFxyXG4gICAgICAgIGBUaGUgcmVxdWVzdCB3aXRoIElEICR7dW5pcXVlSWR9IGZyb20gJHtcclxuICAgICAgICAgIHJlcXVlc3QuaGVhZGVyc1sneC1mb3J3YXJkZWQtZm9yJ10gfHwgcmVxdWVzdC5jb25uZWN0aW9uLnJlbW90ZUFkZHJlc3NcclxuICAgICAgICB9IHdhcyBpbmNvcnJlY3QuIFBheWxvYWQgcmVjZWl2ZWQ6ICR7SlNPTi5zdHJpbmdpZnkoYm9keSl9LmBcclxuICAgICAgKTtcclxuXHJcbiAgICAgIHRocm93IG5ldyBIdHRwRXJyb3IoXHJcbiAgICAgICAgXCJObyBjb3JyZWN0IGNoYXJ0IGRhdGEgZm91bmQuIEVuc3VyZSB0aGF0IHlvdSBhcmUgdXNpbmcgZWl0aGVyIGFwcGxpY2F0aW9uL2pzb24gb3IgbXVsdGlwYXJ0L2Zvcm0tZGF0YSBoZWFkZXJzLiBJZiBzZW5kaW5nIEpTT04sIG1ha2Ugc3VyZSB0aGUgY2hhcnQgZGF0YSBpcyBpbiB0aGUgJ2luZmlsZScsICdvcHRpb25zJywgb3IgJ2RhdGEnIGF0dHJpYnV0ZS4gSWYgc2VuZGluZyBTVkcsIGVuc3VyZSBpdCBpcyBpbiB0aGUgJ3N2ZycgYXR0cmlidXRlLlwiLFxyXG4gICAgICAgIDQwMFxyXG4gICAgICApO1xyXG4gICAgfVxyXG5cclxuICAgIGxldCBjYWxsUmVzcG9uc2UgPSBmYWxzZTtcclxuXHJcbiAgICAvLyBDYWxsIHRoZSBiZWZvcmUgcmVxdWVzdCBmdW5jdGlvbnNcclxuICAgIGNhbGxSZXNwb25zZSA9IGRvQ2FsbGJhY2tzKGJlZm9yZVJlcXVlc3QsIHJlcXVlc3QsIHJlc3BvbnNlLCB7XHJcbiAgICAgIGlkLFxyXG4gICAgICB1bmlxdWVJZCxcclxuICAgICAgdHlwZSxcclxuICAgICAgYm9keVxyXG4gICAgfSk7XHJcblxyXG4gICAgLy8gQmxvY2sgdGhlIHJlcXVlc3QgaWYgb25lIG9mIGEgY2FsbGJhY2tzIGZhaWxlZFxyXG4gICAgaWYgKGNhbGxSZXNwb25zZSAhPT0gdHJ1ZSkge1xyXG4gICAgICByZXR1cm4gcmVzcG9uc2Uuc2VuZChjYWxsUmVzcG9uc2UpO1xyXG4gICAgfVxyXG5cclxuICAgIGxldCBjb25uZWN0aW9uQWJvcnRlZCA9IGZhbHNlO1xyXG5cclxuICAgIC8vIEluIGNhc2UgdGhlIGNvbm5lY3Rpb24gaXMgY2xvc2VkLCBmb3JjZSB0byBhYm9ydCBmdXJ0aGVyIGFjdGlvbnNcclxuICAgIHJlcXVlc3Quc29ja2V0Lm9uKCdjbG9zZScsICgpID0+IHtcclxuICAgICAgY29ubmVjdGlvbkFib3J0ZWQgPSB0cnVlO1xyXG4gICAgfSk7XHJcblxyXG4gICAgbG9nKDQsIGBbZXhwb3J0XSBHb3QgYW4gaW5jb21pbmcgSFRUUCByZXF1ZXN0IHdpdGggSUQgJHt1bmlxdWVJZH0uYCk7XHJcblxyXG4gICAgYm9keS5jb25zdHIgPSAodHlwZW9mIGJvZHkuY29uc3RyID09PSAnc3RyaW5nJyAmJiBib2R5LmNvbnN0cikgfHwgJ2NoYXJ0JztcclxuXHJcbiAgICAvLyBHYXRoZXIgYW5kIG9yZ2FuaXplIG9wdGlvbnMgZnJvbSB0aGUgcGF5bG9hZFxyXG4gICAgY29uc3QgcmVxdWVzdE9wdGlvbnMgPSB7XHJcbiAgICAgIGV4cG9ydDoge1xyXG4gICAgICAgIGluc3RyLFxyXG4gICAgICAgIHR5cGUsXHJcbiAgICAgICAgY29uc3RyOiBib2R5LmNvbnN0clswXS50b0xvd2VyQ2FzZSgpICsgYm9keS5jb25zdHIuc3Vic3RyKDEpLFxyXG4gICAgICAgIGhlaWdodDogYm9keS5oZWlnaHQsXHJcbiAgICAgICAgd2lkdGg6IGJvZHkud2lkdGgsXHJcbiAgICAgICAgc2NhbGU6IGJvZHkuc2NhbGUgfHwgZGVmYXVsdE9wdGlvbnMuZXhwb3J0LnNjYWxlLFxyXG4gICAgICAgIGdsb2JhbE9wdGlvbnM6IGlzQ29ycmVjdEpTT04oYm9keS5nbG9iYWxPcHRpb25zLCB0cnVlKSxcclxuICAgICAgICB0aGVtZU9wdGlvbnM6IGlzQ29ycmVjdEpTT04oYm9keS50aGVtZU9wdGlvbnMsIHRydWUpXHJcbiAgICAgIH0sXHJcbiAgICAgIGN1c3RvbUxvZ2ljOiB7XHJcbiAgICAgICAgYWxsb3dDb2RlRXhlY3V0aW9uOiBnZXRBbGxvd0NvZGVFeGVjdXRpb24oKSxcclxuICAgICAgICBhbGxvd0ZpbGVSZXNvdXJjZXM6IGZhbHNlLFxyXG4gICAgICAgIHJlc291cmNlczogaXNDb3JyZWN0SlNPTihib2R5LnJlc291cmNlcywgdHJ1ZSksXHJcbiAgICAgICAgY2FsbGJhY2s6IGJvZHkuY2FsbGJhY2ssXHJcbiAgICAgICAgY3VzdG9tQ29kZTogYm9keS5jdXN0b21Db2RlXHJcbiAgICAgIH1cclxuICAgIH07XHJcblxyXG4gICAgaWYgKGluc3RyKSB7XHJcbiAgICAgIC8vIFN0cmluZ2lmeSBKU09OIHdpdGggb3B0aW9uc1xyXG4gICAgICByZXF1ZXN0T3B0aW9ucy5leHBvcnQuaW5zdHIgPSBvcHRpb25zU3RyaW5naWZ5KFxyXG4gICAgICAgIGluc3RyLFxyXG4gICAgICAgIHJlcXVlc3RPcHRpb25zLmN1c3RvbUxvZ2ljLmFsbG93Q29kZUV4ZWN1dGlvblxyXG4gICAgICApO1xyXG4gICAgfVxyXG5cclxuICAgIC8vIE1lcmdlIHRoZSByZXF1ZXN0IG9wdGlvbnMgaW50byBkZWZhdWx0IG9uZXNcclxuICAgIGNvbnN0IG9wdGlvbnMgPSBtZXJnZUNvbmZpZ09wdGlvbnMoZGVmYXVsdE9wdGlvbnMsIHJlcXVlc3RPcHRpb25zKTtcclxuXHJcbiAgICAvLyBTYXZlIHRoZSBKU09OIGlmIGV4aXN0c1xyXG4gICAgb3B0aW9ucy5leHBvcnQub3B0aW9ucyA9IGluc3RyO1xyXG5cclxuICAgIC8vIExhc3RseSwgYWRkIHRoZSBzZXJ2ZXIgc3BlY2lmaWMgYXJndW1lbnRzIGludG8gb3B0aW9ucyBhcyBwYXlsb2FkXHJcbiAgICBvcHRpb25zLnBheWxvYWQgPSB7XHJcbiAgICAgIHN2ZzogYm9keS5zdmcgfHwgZmFsc2UsXHJcbiAgICAgIGI2NDogYm9keS5iNjQgfHwgZmFsc2UsXHJcbiAgICAgIG5vRG93bmxvYWQ6IGJvZHkubm9Eb3dubG9hZCB8fCBmYWxzZSxcclxuICAgICAgcmVxdWVzdElkOiB1bmlxdWVJZFxyXG4gICAgfTtcclxuXHJcbiAgICAvLyBUZXN0IHhsaW5rOmhyZWYgZWxlbWVudHMgZnJvbSBwYXlsb2FkJ3MgU1ZHXHJcbiAgICBpZiAoYm9keS5zdmcgJiYgaXNQcml2YXRlUmFuZ2VVcmxGb3VuZChvcHRpb25zLnBheWxvYWQuc3ZnKSkge1xyXG4gICAgICB0aHJvdyBuZXcgSHR0cEVycm9yKFxyXG4gICAgICAgICdTVkcgcG90ZW50aWFsbHkgY29udGFpbiBhdCBsZWFzdCBvbmUgZm9yYmlkZGVuIFVSTCBpbiB4bGluazpocmVmIGVsZW1lbnQuIFBsZWFzZSByZXZpZXcgdGhlIFNWRyBjb250ZW50IGFuZCBlbnN1cmUgdGhhdCBhbGwgcmVmZXJlbmNlZCBVUkxzIGNvbXBseSB3aXRoIHNlY3VyaXR5IHBvbGljaWVzLicsXHJcbiAgICAgICAgNDAwXHJcbiAgICAgICk7XHJcbiAgICB9XHJcblxyXG4gICAgLy8gU3RhcnQgdGhlIGV4cG9ydCBwcm9jZXNzXHJcbiAgICBhd2FpdCBzdGFydEV4cG9ydChvcHRpb25zLCAoZXJyb3IsIGluZm8pID0+IHtcclxuICAgICAgLy8gUmVtb3ZlIHRoZSBjbG9zZSBldmVudCBmcm9tIHRoZSBzb2NrZXRcclxuICAgICAgcmVxdWVzdC5zb2NrZXQucmVtb3ZlQWxsTGlzdGVuZXJzKCdjbG9zZScpO1xyXG5cclxuICAgICAgLy8gQWZ0ZXIgdGhlIHdob2xlIGV4cG9ydGluZyBwcm9jZXNzXHJcbiAgICAgIGlmIChkZWZhdWx0T3B0aW9ucy5zZXJ2ZXIuYmVuY2htYXJraW5nKSB7XHJcbiAgICAgICAgbG9nKFxyXG4gICAgICAgICAgNSxcclxuICAgICAgICAgIGBbYmVuY2htYXJrXSBSZXF1ZXN0IHdpdGggSUQgJHt1bmlxdWVJZH0gLSBBZnRlciB0aGUgd2hvbGUgZXhwb3J0aW5nIHByb2Nlc3M6ICR7c3RvcENvdW50ZXIoKX1tcy5gXHJcbiAgICAgICAgKTtcclxuICAgICAgfVxyXG5cclxuICAgICAgLy8gSWYgdGhlIGNvbm5lY3Rpb24gd2FzIGNsb3NlZCwgZG8gbm90aGluZ1xyXG4gICAgICBpZiAoY29ubmVjdGlvbkFib3J0ZWQpIHtcclxuICAgICAgICByZXR1cm4gbG9nKFxyXG4gICAgICAgICAgMyxcclxuICAgICAgICAgIGBbZXhwb3J0XSBUaGUgY2xpZW50IGNsb3NlZCB0aGUgY29ubmVjdGlvbiBiZWZvcmUgdGhlIGNoYXJ0IGZpbmlzaGVkIHByb2Nlc3NpbmcuYFxyXG4gICAgICAgICk7XHJcbiAgICAgIH1cclxuXHJcbiAgICAgIC8vIElmIGVycm9yLCBsb2cgaXQgYW5kIHNlbmQgaXQgdG8gdGhlIGVycm9yIG1pZGRsZXdhcmVcclxuICAgICAgaWYgKGVycm9yKSB7XHJcbiAgICAgICAgdGhyb3cgZXJyb3I7XHJcbiAgICAgIH1cclxuXHJcbiAgICAgIC8vIElmIGRhdGEgaXMgbWlzc2luZywgbG9nIHRoZSBtZXNzYWdlIGFuZCBzZW5kIGl0IHRvIHRoZSBlcnJvciBtaWRkbGV3YXJlXHJcbiAgICAgIGlmICghaW5mbyB8fCAhaW5mby5yZXN1bHQpIHtcclxuICAgICAgICB0aHJvdyBuZXcgSHR0cEVycm9yKFxyXG4gICAgICAgICAgYFVuZXhwZWN0ZWQgcmV0dXJuIGZyb20gY2hhcnQgZ2VuZXJhdGlvbi4gUGxlYXNlIGNoZWNrIHlvdXIgcmVxdWVzdCBkYXRhLiBGb3IgdGhlIHJlcXVlc3Qgd2l0aCBJRCAke3VuaXF1ZUlkfSwgdGhlIHJlc3VsdCBpcyAke2luZm8ucmVzdWx0fS5gLFxyXG4gICAgICAgICAgNDAwXHJcbiAgICAgICAgKTtcclxuICAgICAgfVxyXG5cclxuICAgICAgLy8gR2V0IHRoZSB0eXBlIGZyb20gb3B0aW9uc1xyXG4gICAgICB0eXBlID0gaW5mby5vcHRpb25zLmV4cG9ydC50eXBlO1xyXG5cclxuICAgICAgLy8gVGhlIGFmdGVyIHJlcXVlc3QgY2FsbGJhY2tzXHJcbiAgICAgIGRvQ2FsbGJhY2tzKGFmdGVyUmVxdWVzdCwgcmVxdWVzdCwgcmVzcG9uc2UsIHsgaWQsIGJvZHk6IGluZm8ucmVzdWx0IH0pO1xyXG5cclxuICAgICAgaWYgKGluZm8ucmVzdWx0KSB7XHJcbiAgICAgICAgLy8gSWYgb25seSBiYXNlNjQgaXMgcmVxdWlyZWQsIHJldHVybiBpdFxyXG4gICAgICAgIGlmIChib2R5LmI2NCkge1xyXG4gICAgICAgICAgLy8gU1ZHIEV4Y2VwdGlvbiBmb3IgdGhlIEhpZ2hjaGFydHMgMTEuMy4wIHZlcnNpb25cclxuICAgICAgICAgIGlmICh0eXBlID09PSAncGRmJyB8fCB0eXBlID09ICdzdmcnKSB7XHJcbiAgICAgICAgICAgIHJldHVybiByZXNwb25zZS5zZW5kKFxyXG4gICAgICAgICAgICAgIEJ1ZmZlci5mcm9tKGluZm8ucmVzdWx0LCAndXRmOCcpLnRvU3RyaW5nKCdiYXNlNjQnKVxyXG4gICAgICAgICAgICApO1xyXG4gICAgICAgICAgfVxyXG5cclxuICAgICAgICAgIHJldHVybiByZXNwb25zZS5zZW5kKGluZm8ucmVzdWx0KTtcclxuICAgICAgICB9XHJcblxyXG4gICAgICAgIC8vIFNldCBjb3JyZWN0IGNvbnRlbnQgdHlwZVxyXG4gICAgICAgIHJlc3BvbnNlLmhlYWRlcignQ29udGVudC1UeXBlJywgcmV2ZXJzZWRNaW1lW3R5cGVdIHx8ICdpbWFnZS9wbmcnKTtcclxuXHJcbiAgICAgICAgLy8gRGVjaWRlIHdoZXRoZXIgdG8gZG93bmxvYWQgb3Igbm90IGNoYXJ0IGZpbGVcclxuICAgICAgICBpZiAoIWJvZHkubm9Eb3dubG9hZCkge1xyXG4gICAgICAgICAgcmVzcG9uc2UuYXR0YWNobWVudChcclxuICAgICAgICAgICAgYCR7cmVxdWVzdC5wYXJhbXMuZmlsZW5hbWUgfHwgcmVxdWVzdC5ib2R5LmZpbGVuYW1lIHx8ICdjaGFydCd9LiR7XHJcbiAgICAgICAgICAgICAgdHlwZSB8fCAncG5nJ1xyXG4gICAgICAgICAgICB9YFxyXG4gICAgICAgICAgKTtcclxuICAgICAgICB9XHJcblxyXG4gICAgICAgIC8vIElmIFNWRywgcmV0dXJuIHBsYWluIGNvbnRlbnRcclxuICAgICAgICByZXR1cm4gdHlwZSA9PT0gJ3N2ZydcclxuICAgICAgICAgID8gcmVzcG9uc2Uuc2VuZChpbmZvLnJlc3VsdClcclxuICAgICAgICAgIDogcmVzcG9uc2Uuc2VuZChCdWZmZXIuZnJvbShpbmZvLnJlc3VsdCwgJ2Jhc2U2NCcpKTtcclxuICAgICAgfVxyXG4gICAgfSk7XHJcbiAgfSBjYXRjaCAoZXJyb3IpIHtcclxuICAgIG5leHQoZXJyb3IpO1xyXG4gIH1cclxufTtcclxuXHJcbmV4cG9ydCBkZWZhdWx0IChhcHApID0+IHtcclxuICAvKipcclxuICAgKiBBZGRzIHRoZSBQT1NUIC8gYSByb3V0ZSBmb3IgaGFuZGxpbmcgUE9TVCByZXF1ZXN0cyBhdCB0aGUgcm9vdCBlbmRwb2ludC5cclxuICAgKi9cclxuICBhcHAucG9zdCgnLycsIGV4cG9ydEhhbmRsZXIpO1xyXG5cclxuICAvKipcclxuICAgKiBBZGRzIHRoZSBQT1NUIC86ZmlsZW5hbWUgYSByb3V0ZSBmb3IgaGFuZGxpbmcgUE9TVCByZXF1ZXN0cyB3aXRoXHJcbiAgICogYSBzcGVjaWZpZWQgZmlsZW5hbWUgcGFyYW1ldGVyLlxyXG4gICAqL1xyXG4gIGFwcC5wb3N0KCcvOmZpbGVuYW1lJywgZXhwb3J0SGFuZGxlcik7XHJcbn07XHJcbiIsIi8qKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqXHJcblxyXG5IaWdoY2hhcnRzIEV4cG9ydCBTZXJ2ZXJcclxuXHJcbkNvcHlyaWdodCAoYykgMjAxNi0yMDI0LCBIaWdoc29mdFxyXG5cclxuTGljZW5jZWQgdW5kZXIgdGhlIE1JVCBsaWNlbmNlLlxyXG5cclxuQWRkaXRpb25hbGx5IGEgdmFsaWQgSGlnaGNoYXJ0cyBsaWNlbnNlIGlzIHJlcXVpcmVkIGZvciB1c2UuXHJcblxyXG5TZWUgTElDRU5TRSBmaWxlIGluIHJvb3QgZm9yIGRldGFpbHMuXHJcblxyXG4qKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqL1xyXG5cclxuaW1wb3J0IHsgcmVhZEZpbGVTeW5jIH0gZnJvbSAnZnMnO1xyXG5pbXBvcnQgeyBqb2luIGFzIHBhdGhlciB9IGZyb20gJ3BhdGgnO1xyXG5cclxuaW1wb3J0IGNhY2hlIGZyb20gJy4uLy4uL2NhY2hlLmpzJztcclxuaW1wb3J0IHBvb2wgZnJvbSAnLi4vLi4vcG9vbC5qcyc7XHJcbmltcG9ydCB7IF9fZGlybmFtZSB9IGZyb20gJy4uLy4uL3V0aWxzLmpzJztcclxuXHJcbmNvbnN0IHBrZ0ZpbGUgPSBKU09OLnBhcnNlKHJlYWRGaWxlU3luYyhwYXRoZXIoX19kaXJuYW1lLCAncGFja2FnZS5qc29uJykpKTtcclxuXHJcbmNvbnN0IHNlcnZlclN0YXJ0VGltZSA9IG5ldyBEYXRlKCk7XHJcblxyXG4vKipcclxuICogQWRkcyB0aGUgR0VUIC9oZWFsdGggcm91dGUsIHdoaWNoIG91dHB1dHMgYmFzaWMgc3RhdHMgZm9yIHRoZSBzZXJ2ZXIuXHJcbiAqL1xyXG5leHBvcnQgZGVmYXVsdCAoYXBwKSA9PlxyXG4gICFhcHBcclxuICAgID8gZmFsc2VcclxuICAgIDogYXBwLmdldCgnL2hlYWx0aCcsIChyZXF1ZXN0LCByZXNwb25zZSkgPT4ge1xyXG4gICAgICAgIHJlc3BvbnNlLnNlbmQoe1xyXG4gICAgICAgICAgc3RhdHVzOiAnT0snLFxyXG4gICAgICAgICAgYm9vdFRpbWU6IHNlcnZlclN0YXJ0VGltZSxcclxuICAgICAgICAgIHVwdGltZTpcclxuICAgICAgICAgICAgTWF0aC5mbG9vcihcclxuICAgICAgICAgICAgICAobmV3IERhdGUoKS5nZXRUaW1lKCkgLSBzZXJ2ZXJTdGFydFRpbWUuZ2V0VGltZSgpKSAvIDEwMDAgLyA2MFxyXG4gICAgICAgICAgICApICsgJyBtaW51dGVzJyxcclxuICAgICAgICAgIHZlcnNpb246IHBrZ0ZpbGUudmVyc2lvbixcclxuICAgICAgICAgIGhpZ2hjaGFydHNWZXJzaW9uOiBjYWNoZS52ZXJzaW9uKCksXHJcbiAgICAgICAgICBhdmVyYWdlUHJvY2Vzc2luZ1RpbWU6IHBvb2wuYXZlcmFnZVRpbWUoKSxcclxuICAgICAgICAgIHBlcmZvcm1lZEV4cG9ydHM6IHBvb2wucHJvY2Vzc2VkV29ya0NvdW50KCksXHJcbiAgICAgICAgICBmYWlsZWRFeHBvcnRzOiBwb29sLmRyb3BwZWRXb3JrKCksXHJcbiAgICAgICAgICBleHBvcnRBdHRlbXB0czogcG9vbC53b3JrQXR0ZW1wdHMoKSxcclxuICAgICAgICAgIHN1Y2Vzc1JhdGlvOiAocG9vbC5wcm9jZXNzZWRXb3JrQ291bnQoKSAvIHBvb2wud29ya0F0dGVtcHRzKCkpICogMTAwLFxyXG4gICAgICAgICAgLy8gZXNsaW50LWRpc2FibGUtbmV4dC1saW5lIGltcG9ydC9uby1uYW1lZC1hcy1kZWZhdWx0LW1lbWJlclxyXG4gICAgICAgICAgcG9vbDogcG9vbC5nZXRQb29sSW5mb0pTT04oKVxyXG4gICAgICAgIH0pO1xyXG4gICAgICB9KTtcclxuIiwiLyoqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKipcclxuXHJcbkhpZ2hjaGFydHMgRXhwb3J0IFNlcnZlclxyXG5cclxuQ29weXJpZ2h0IChjKSAyMDE2LTIwMjQsIEhpZ2hzb2Z0XHJcblxyXG5MaWNlbmNlZCB1bmRlciB0aGUgTUlUIGxpY2VuY2UuXHJcblxyXG5BZGRpdGlvbmFsbHkgYSB2YWxpZCBIaWdoY2hhcnRzIGxpY2Vuc2UgaXMgcmVxdWlyZWQgZm9yIHVzZS5cclxuXHJcblNlZSBMSUNFTlNFIGZpbGUgaW4gcm9vdCBmb3IgZGV0YWlscy5cclxuXHJcbioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKiovXHJcblxyXG5pbXBvcnQgeyBwcm9taXNlcyBhcyBmc1Byb21pc2VzIH0gZnJvbSAnZnMnO1xyXG5pbXBvcnQgeyBwb3NpeCB9IGZyb20gJ3BhdGgnO1xyXG5cclxuaW1wb3J0IGNvcnMgZnJvbSAnY29ycyc7XHJcbmltcG9ydCBleHByZXNzIGZyb20gJ2V4cHJlc3MnO1xyXG5pbXBvcnQgaHR0cCBmcm9tICdodHRwJztcclxuaW1wb3J0IGh0dHBzIGZyb20gJ2h0dHBzJztcclxuaW1wb3J0IG11bHRlciBmcm9tICdtdWx0ZXInO1xyXG5cclxuaW1wb3J0IGVycm9ySGFuZGxlciBmcm9tICcuL2Vycm9yLmpzJztcclxuaW1wb3J0IHsgbG9nLCBsb2dXaXRoU3RhY2sgfSBmcm9tICcuLi9sb2dnZXIuanMnO1xyXG5pbXBvcnQgcmF0ZUxpbWl0IGZyb20gJy4vcmF0ZV9saW1pdC5qcyc7XHJcbmltcG9ydCB7IF9fZGlybmFtZSB9IGZyb20gJy4uL3V0aWxzLmpzJztcclxuXHJcbmltcG9ydCB2U3dpdGNoUm91dGUgZnJvbSAnLi9yb3V0ZXMvY2hhbmdlX2hjX3ZlcnNpb24uanMnO1xyXG5pbXBvcnQgZXhwb3J0Um91dGVzIGZyb20gJy4vcm91dGVzL2V4cG9ydC5qcyc7XHJcbmltcG9ydCBoZWFsdGhSb3V0ZSBmcm9tICcuL3JvdXRlcy9oZWFsdGguanMnO1xyXG5pbXBvcnQgdWlSb3V0ZSBmcm9tICcuL3JvdXRlcy91aS5qcyc7XHJcblxyXG5pbXBvcnQgRXhwb3J0RXJyb3IgZnJvbSAnLi4vZXJyb3JzL0V4cG9ydEVycm9yLmpzJztcclxuXHJcbi8vIENyZWF0ZSBleHByZXNzIGFwcFxyXG5jb25zdCBhcHAgPSBleHByZXNzKCk7XHJcblxyXG4vLyBEaXNhYmxlIHRoZSBYLVBvd2VyZWQtQnkgaGVhZGVyXHJcbmFwcC5kaXNhYmxlKCd4LXBvd2VyZWQtYnknKTtcclxuXHJcbi8vIEVuYWJsZSBDT1JTIHN1cHBvcnRcclxuYXBwLnVzZShjb3JzKCkpO1xyXG5cclxuLy8gRW5hYmxlIHBhcnNpbmcgb2YgZm9ybSBkYXRhIChmaWxlcykgd2l0aCBNdWx0ZXIgcGFja2FnZVxyXG5jb25zdCBzdG9yYWdlID0gbXVsdGVyLm1lbW9yeVN0b3JhZ2UoKTtcclxuY29uc3QgdXBsb2FkID0gbXVsdGVyKHtcclxuICBzdG9yYWdlLFxyXG4gIGxpbWl0czoge1xyXG4gICAgZmllbGRTaXplOiA1MCAqIDEwMjQgKiAxMDI0XHJcbiAgfVxyXG59KTtcclxuXHJcbi8vIEVuYWJsZSBib2R5IHBhcnNlclxyXG5hcHAudXNlKGV4cHJlc3MuanNvbih7IGxpbWl0OiA1MCAqIDEwMjQgKiAxMDI0IH0pKTtcclxuYXBwLnVzZShleHByZXNzLnVybGVuY29kZWQoeyBleHRlbmRlZDogdHJ1ZSwgbGltaXQ6IDUwICogMTAyNCAqIDEwMjQgfSkpO1xyXG5cclxuLy8gVXNlIG9ubHkgbm9uLWZpbGUgbXVsdGlwYXJ0IGZvcm0gZmllbGRzXHJcbmFwcC51c2UodXBsb2FkLm5vbmUoKSk7XHJcblxyXG4vKipcclxuICogQXR0YWNoIGVycm9yIGhhbmRsZXJzIHRvIHRoZSBzZXJ2ZXIuXHJcbiAqXHJcbiAqIEBwYXJhbSB7aHR0cC5TZXJ2ZXJ9IHNlcnZlciAtIFRoZSBIVFRQL0hUVFBTIHNlcnZlciBpbnN0YW5jZS5cclxuICovXHJcbmNvbnN0IGF0dGFjaEVycm9ySGFuZGxlcnMgPSAoc2VydmVyKSA9PiB7XHJcbiAgc2VydmVyLm9uKCdjbGllbnRFcnJvcicsIChlcnJvcikgPT4ge1xyXG4gICAgbG9nV2l0aFN0YWNrKDEsIGVycm9yLCBgW3NlcnZlcl0gQ2xpZW50IGVycm9yOiAke2Vycm9yLm1lc3NhZ2V9YCk7XHJcbiAgfSk7XHJcbiAgc2VydmVyLm9uKCdlcnJvcicsIChlcnJvcikgPT4ge1xyXG4gICAgbG9nV2l0aFN0YWNrKDEsIGVycm9yLCBgW3NlcnZlcl0gU2VydmVyIGVycm9yOiAke2Vycm9yLm1lc3NhZ2V9YCk7XHJcbiAgfSk7XHJcbiAgc2VydmVyLm9uKCdjb25uZWN0aW9uJywgKHNvY2tldCkgPT4ge1xyXG4gICAgc29ja2V0Lm9uKCdlcnJvcicsIChlcnJvcikgPT4ge1xyXG4gICAgICBsb2dXaXRoU3RhY2soMSwgZXJyb3IsIGBbc2VydmVyXSBTb2NrZXQgZXJyb3I6ICR7ZXJyb3IubWVzc2FnZX1gKTtcclxuICAgIH0pO1xyXG4gIH0pO1xyXG59O1xyXG5cclxuLyoqXHJcbiAqIFN0YXJ0cyBhbiBIVFRQIHNlcnZlciBiYXNlZCBvbiB0aGUgcHJvdmlkZWQgY29uZmlndXJhdGlvbi4gVGhlIGBzZXJ2ZXJDb25maWdgXHJcbiAqIG9iamVjdCBjb250YWlucyBhbGwgc2VydmVyIHJlbGF0ZWQgcHJvcGVydGllcyAoc2VlIHRoZSBgc2VydmVyYCBzZWN0aW9uXHJcbiAqIGluIHRoZSBgbGliL3NjaGVtYXMvY29uZmlnLmpzYCBmaWxlIGZvciBhIHJlZmVyZW5jZSkuXHJcbiAqXHJcbiAqIEBwYXJhbSB7T2JqZWN0fSBzZXJ2ZXJDb25maWcgLSBUaGUgc2VydmVyIGNvbmZpZ3VyYXRpb24gb2JqZWN0LlxyXG4gKlxyXG4gKiBAdGhyb3dzIHtFeHBvcnRFcnJvcn0gLSBUaHJvd3MgYW4gZXJyb3IgaWYgdGhlIHNlcnZlciBjYW5ub3QgYmUgY29uZmlndXJlZFxyXG4gKiBhbmQgc3RhcnRlZC5cclxuICovXHJcbmV4cG9ydCBjb25zdCBzdGFydFNlcnZlciA9IGFzeW5jIChzZXJ2ZXJDb25maWcpID0+IHtcclxuICB0cnkge1xyXG4gICAgLy8gU3RvcCBpZiBub3QgZW5hYmxlZFxyXG4gICAgaWYgKCFzZXJ2ZXJDb25maWcuZW5hYmxlKSB7XHJcbiAgICAgIHJldHVybiBmYWxzZTtcclxuICAgIH1cclxuXHJcbiAgICAvLyBMaXN0ZW4gSFRUUCBzZXJ2ZXJcclxuICAgIGlmICghc2VydmVyQ29uZmlnLnNzbC5mb3JjZSkge1xyXG4gICAgICAvLyBNYWluIHNlcnZlciBpbnN0YW5jZSAoSFRUUClcclxuICAgICAgY29uc3QgaHR0cFNlcnZlciA9IGh0dHAuY3JlYXRlU2VydmVyKGFwcCk7XHJcblxyXG4gICAgICAvLyBBdHRhY2ggZXJyb3IgaGFuZGxlcnMgYW5kIGxpc3RlbiB0byB0aGUgc2VydmVyXHJcbiAgICAgIGF0dGFjaEVycm9ySGFuZGxlcnMoaHR0cFNlcnZlcik7XHJcblxyXG4gICAgICAvLyBMaXN0ZW5cclxuICAgICAgaHR0cFNlcnZlci5saXN0ZW4oc2VydmVyQ29uZmlnLnBvcnQsIHNlcnZlckNvbmZpZy5ob3N0KTtcclxuXHJcbiAgICAgIGxvZyhcclxuICAgICAgICAzLFxyXG4gICAgICAgIGBbc2VydmVyXSBTdGFydGVkIEhUVFAgc2VydmVyIG9uICR7c2VydmVyQ29uZmlnLmhvc3R9OiR7c2VydmVyQ29uZmlnLnBvcnR9LmBcclxuICAgICAgKTtcclxuICAgIH1cclxuXHJcbiAgICAvLyBMaXN0ZW4gSFRUUFMgc2VydmVyXHJcbiAgICBpZiAoc2VydmVyQ29uZmlnLnNzbC5lbmFibGUpIHtcclxuICAgICAgLy8gU2V0IHVwIGFuIFNTTCBzZXJ2ZXIgYWxzb1xyXG4gICAgICBsZXQga2V5LCBjZXJ0O1xyXG5cclxuICAgICAgdHJ5IHtcclxuICAgICAgICAvLyBHZXQgdGhlIFNTTCBrZXlcclxuICAgICAgICBrZXkgPSBhd2FpdCBmc1Byb21pc2VzLnJlYWRGaWxlKFxyXG4gICAgICAgICAgcG9zaXguam9pbihzZXJ2ZXJDb25maWcuc3NsLmNlcnRQYXRoLCAnc2VydmVyLmtleScpLFxyXG4gICAgICAgICAgJ3V0ZjgnXHJcbiAgICAgICAgKTtcclxuXHJcbiAgICAgICAgLy8gR2V0IHRoZSBTU0wgY2VydGlmaWNhdGVcclxuICAgICAgICBjZXJ0ID0gYXdhaXQgZnNQcm9taXNlcy5yZWFkRmlsZShcclxuICAgICAgICAgIHBvc2l4LmpvaW4oc2VydmVyQ29uZmlnLnNzbC5jZXJ0UGF0aCwgJ3NlcnZlci5jcnQnKSxcclxuICAgICAgICAgICd1dGY4J1xyXG4gICAgICAgICk7XHJcbiAgICAgIH0gY2F0Y2ggKGVycm9yKSB7XHJcbiAgICAgICAgbG9nKFxyXG4gICAgICAgICAgMixcclxuICAgICAgICAgIGBbc2VydmVyXSBVbmFibGUgdG8gbG9hZCBrZXkvY2VydGlmaWNhdGUgZnJvbSB0aGUgJyR7c2VydmVyQ29uZmlnLnNzbC5jZXJ0UGF0aH0nIHBhdGguIENvdWxkIG5vdCBydW4gc2VjdXJlZCBsYXllciBzZXJ2ZXIuYFxyXG4gICAgICAgICk7XHJcbiAgICAgIH1cclxuXHJcbiAgICAgIGlmIChrZXkgJiYgY2VydCkge1xyXG4gICAgICAgIC8vIE1haW4gc2VydmVyIGluc3RhbmNlIChIVFRQUylcclxuICAgICAgICBjb25zdCBodHRwc1NlcnZlciA9IGh0dHBzLmNyZWF0ZVNlcnZlcih7IGtleSwgY2VydCB9LCBhcHApO1xyXG5cclxuICAgICAgICAvLyBBdHRhY2ggZXJyb3IgaGFuZGxlcnMgYW5kIGxpc3RlbiB0byB0aGUgc2VydmVyXHJcbiAgICAgICAgYXR0YWNoRXJyb3JIYW5kbGVycyhodHRwc1NlcnZlcik7XHJcblxyXG4gICAgICAgIC8vIExpc3RlblxyXG4gICAgICAgIGh0dHBzU2VydmVyLmxpc3RlbihzZXJ2ZXJDb25maWcuc3NsLnBvcnQsIHNlcnZlckNvbmZpZy5ob3N0KTtcclxuXHJcbiAgICAgICAgbG9nKFxyXG4gICAgICAgICAgMyxcclxuICAgICAgICAgIGBbc2VydmVyXSBTdGFydGVkIEhUVFBTIHNlcnZlciBvbiAke3NlcnZlckNvbmZpZy5ob3N0fToke3NlcnZlckNvbmZpZy5zc2wucG9ydH0uYFxyXG4gICAgICAgICk7XHJcbiAgICAgIH1cclxuICAgIH1cclxuXHJcbiAgICAvLyBFbmFibGUgdGhlIHJhdGUgbGltaXRlciBpZiBjb25maWcgc2F5cyBzb1xyXG4gICAgaWYgKFxyXG4gICAgICBzZXJ2ZXJDb25maWcucmF0ZUxpbWl0aW5nICYmXHJcbiAgICAgIHNlcnZlckNvbmZpZy5yYXRlTGltaXRpbmcuZW5hYmxlICYmXHJcbiAgICAgICFbMCwgTmFOXS5pbmNsdWRlcyhzZXJ2ZXJDb25maWcucmF0ZUxpbWl0aW5nLm1heFJlcXVlc3RzKVxyXG4gICAgKSB7XHJcbiAgICAgIHJhdGVMaW1pdChhcHAsIHNlcnZlckNvbmZpZy5yYXRlTGltaXRpbmcpO1xyXG4gICAgfVxyXG5cclxuICAgIC8vIFNldCB1cCBzdGF0aWMgZm9sZGVyJ3Mgcm91dGVcclxuICAgIGFwcC51c2UoZXhwcmVzcy5zdGF0aWMocG9zaXguam9pbihfX2Rpcm5hbWUsICdwdWJsaWMnKSkpO1xyXG5cclxuICAgIC8vIFNldCB1cCByb3V0ZXNcclxuICAgIGhlYWx0aFJvdXRlKGFwcCk7XHJcbiAgICBleHBvcnRSb3V0ZXMoYXBwKTtcclxuICAgIHVpUm91dGUoYXBwKTtcclxuICAgIHZTd2l0Y2hSb3V0ZShhcHApO1xyXG5cclxuICAgIC8vIFNldCB1cCBjZW50cmFsaXplZCBlcnJvciBoYW5kbGVyXHJcbiAgICBlcnJvckhhbmRsZXIoYXBwKTtcclxuICB9IGNhdGNoIChlcnJvcikge1xyXG4gICAgdGhyb3cgbmV3IEV4cG9ydEVycm9yKFxyXG4gICAgICAnW3NlcnZlcl0gQ291bGQgbm90IGNvbmZpZ3VyZSBhbmQgc3RhcnQgdGhlIHNlcnZlci4nXHJcbiAgICApLnNldEVycm9yKGVycm9yKTtcclxuICB9XHJcbn07XHJcblxyXG4vKipcclxuICogRW5hYmxlIHJhdGUgbGltaXRpbmcgZm9yIHRoZSBzZXJ2ZXIuXHJcbiAqXHJcbiAqIEBwYXJhbSB7T2JqZWN0fSBsaW1pdENvbmZpZyAtIENvbmZpZ3VyYXRpb24gb2JqZWN0IGZvciByYXRlIGxpbWl0aW5nLlxyXG4gKi9cclxuZXhwb3J0IGNvbnN0IGVuYWJsZVJhdGVMaW1pdGluZyA9IChsaW1pdENvbmZpZykgPT4gcmF0ZUxpbWl0KGFwcCwgbGltaXRDb25maWcpO1xyXG5cclxuLyoqXHJcbiAqIEdldCB0aGUgRXhwcmVzcyBpbnN0YW5jZS5cclxuICpcclxuICogQHJldHVybnMge09iamVjdH0gLSBUaGUgRXhwcmVzcyBpbnN0YW5jZS5cclxuICovXHJcbmV4cG9ydCBjb25zdCBnZXRFeHByZXNzID0gKCkgPT4gZXhwcmVzcztcclxuXHJcbi8qKlxyXG4gKiBHZXQgdGhlIEV4cHJlc3MgYXBwIGluc3RhbmNlLlxyXG4gKlxyXG4gKiBAcmV0dXJucyB7T2JqZWN0fSAtIFRoZSBFeHByZXNzIGFwcCBpbnN0YW5jZS5cclxuICovXHJcbmV4cG9ydCBjb25zdCBnZXRBcHAgPSAoKSA9PiBhcHA7XHJcblxyXG4vKipcclxuICogQXBwbHkgbWlkZGxld2FyZShzKSB0byBhIHNwZWNpZmljIHBhdGguXHJcbiAqXHJcbiAqIEBwYXJhbSB7c3RyaW5nfSBwYXRoIC0gVGhlIHBhdGggdG8gd2hpY2ggdGhlIG1pZGRsZXdhcmUocykgc2hvdWxkIGJlIGFwcGxpZWQuXHJcbiAqIEBwYXJhbSB7Li4uRnVuY3Rpb259IG1pZGRsZXdhcmVzIC0gVGhlIG1pZGRsZXdhcmUgZnVuY3Rpb25zIHRvIGJlIGFwcGxpZWQuXHJcbiAqL1xyXG5leHBvcnQgY29uc3QgdXNlID0gKHBhdGgsIC4uLm1pZGRsZXdhcmVzKSA9PiB7XHJcbiAgYXBwLnVzZShwYXRoLCAuLi5taWRkbGV3YXJlcyk7XHJcbn07XHJcblxyXG4vKipcclxuICogU2V0IHVwIGEgcm91dGUgd2l0aCBHRVQgbWV0aG9kIGFuZCBhcHBseSBtaWRkbGV3YXJlKHMpLlxyXG4gKlxyXG4gKiBAcGFyYW0ge3N0cmluZ30gcGF0aCAtIFRoZSByb3V0ZSBwYXRoLlxyXG4gKiBAcGFyYW0gey4uLkZ1bmN0aW9ufSBtaWRkbGV3YXJlcyAtIFRoZSBtaWRkbGV3YXJlIGZ1bmN0aW9ucyB0byBiZSBhcHBsaWVkLlxyXG4gKi9cclxuZXhwb3J0IGNvbnN0IGdldCA9IChwYXRoLCAuLi5taWRkbGV3YXJlcykgPT4ge1xyXG4gIGFwcC5nZXQocGF0aCwgLi4ubWlkZGxld2FyZXMpO1xyXG59O1xyXG5cclxuLyoqXHJcbiAqIFNldCB1cCBhIHJvdXRlIHdpdGggUE9TVCBtZXRob2QgYW5kIGFwcGx5IG1pZGRsZXdhcmUocykuXHJcbiAqXHJcbiAqIEBwYXJhbSB7c3RyaW5nfSBwYXRoIC0gVGhlIHJvdXRlIHBhdGguXHJcbiAqIEBwYXJhbSB7Li4uRnVuY3Rpb259IG1pZGRsZXdhcmVzIC0gVGhlIG1pZGRsZXdhcmUgZnVuY3Rpb25zIHRvIGJlIGFwcGxpZWQuXHJcbiAqL1xyXG5leHBvcnQgY29uc3QgcG9zdCA9IChwYXRoLCAuLi5taWRkbGV3YXJlcykgPT4ge1xyXG4gIGFwcC5wb3N0KHBhdGgsIC4uLm1pZGRsZXdhcmVzKTtcclxufTtcclxuXHJcbmV4cG9ydCBkZWZhdWx0IHtcclxuICBzdGFydFNlcnZlcixcclxuICBlbmFibGVSYXRlTGltaXRpbmcsXHJcbiAgZ2V0RXhwcmVzcyxcclxuICBnZXRBcHAsXHJcbiAgdXNlLFxyXG4gIGdldCxcclxuICBwb3N0XHJcbn07XHJcbiIsIi8qKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqXHJcblxyXG5IaWdoY2hhcnRzIEV4cG9ydCBTZXJ2ZXJcclxuXHJcbkNvcHlyaWdodCAoYykgMjAxNi0yMDI0LCBIaWdoc29mdFxyXG5cclxuTGljZW5jZWQgdW5kZXIgdGhlIE1JVCBsaWNlbmNlLlxyXG5cclxuQWRkaXRpb25hbGx5IGEgdmFsaWQgSGlnaGNoYXJ0cyBsaWNlbnNlIGlzIHJlcXVpcmVkIGZvciB1c2UuXHJcblxyXG5TZWUgTElDRU5TRSBmaWxlIGluIHJvb3QgZm9yIGRldGFpbHMuXHJcblxyXG4qKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqL1xyXG5cclxuaW1wb3J0IHsgam9pbiB9IGZyb20gJ3BhdGgnO1xyXG5cclxuaW1wb3J0IHsgX19kaXJuYW1lIH0gZnJvbSAnLi4vLi4vdXRpbHMuanMnO1xyXG5cclxuLyoqXHJcbiAqIEFkZHMgdGhlIEdFVCAvIHJvdXRlIGZvciBhIFVJIHdoZW4gZW5hYmxlZCBvbiB0aGUgZXhwb3J0IHNlcnZlci5cclxuICovXHJcbmV4cG9ydCBkZWZhdWx0IChhcHApID0+XHJcbiAgIWFwcFxyXG4gICAgPyBmYWxzZVxyXG4gICAgOiBhcHAuZ2V0KCcvJywgKHJlcXVlc3QsIHJlc3BvbnNlKSA9PiB7XHJcbiAgICAgICAgcmVzcG9uc2Uuc2VuZEZpbGUoam9pbihfX2Rpcm5hbWUsICdwdWJsaWMnLCAnaW5kZXguaHRtbCcpKTtcclxuICAgICAgfSk7XHJcbiIsIi8qKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqXHJcblxyXG5IaWdoY2hhcnRzIEV4cG9ydCBTZXJ2ZXJcclxuXHJcbkNvcHlyaWdodCAoYykgMjAxNi0yMDI0LCBIaWdoc29mdFxyXG5cclxuTGljZW5jZWQgdW5kZXIgdGhlIE1JVCBsaWNlbmNlLlxyXG5cclxuQWRkaXRpb25hbGx5IGEgdmFsaWQgSGlnaGNoYXJ0cyBsaWNlbnNlIGlzIHJlcXVpcmVkIGZvciB1c2UuXHJcblxyXG5TZWUgTElDRU5TRSBmaWxlIGluIHJvb3QgZm9yIGRldGFpbHMuXHJcblxyXG4qKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqL1xyXG5cclxuaW1wb3J0IGNhY2hlIGZyb20gJy4uLy4uL2NhY2hlLmpzJztcclxuaW1wb3J0IEh0dHBFcnJvciBmcm9tICcuLi8uLi9lcnJvcnMvSHR0cEVycm9yLmpzJztcclxuaW1wb3J0IHsgZW52cyB9IGZyb20gJy4uLy4uL2VudnMuanMnO1xyXG5cclxuLyoqXHJcbiAqIEFkZHMgdGhlIFBPU1QgL2NoYW5nZV9oY192ZXJzaW9uLzpuZXdWZXJzaW9uIHJvdXRlIHRoYXQgY2FuIGJlIHV0aWxpemVkIHRvIG1vZGlmeVxyXG4gKiB0aGUgSGlnaGNoYXJ0cyB2ZXJzaW9uIG9uIHRoZSBzZXJ2ZXIuXHJcbiAqXHJcbiAqIFRPRE86IEFkZCBhdXRoIHRva2VuIGFuZCBjb25uZWN0IHRvIEFQSVxyXG4gKi9cclxuZXhwb3J0IGRlZmF1bHQgKGFwcCkgPT5cclxuICAhYXBwXHJcbiAgICA/IGZhbHNlXHJcbiAgICA6IGFwcC5wb3N0KFxyXG4gICAgICAgICcvdmVyc2lvbi9jaGFuZ2UvOm5ld1ZlcnNpb24nLFxyXG4gICAgICAgIGFzeW5jIChyZXF1ZXN0LCByZXNwb25zZSwgbmV4dCkgPT4ge1xyXG4gICAgICAgICAgdHJ5IHtcclxuICAgICAgICAgICAgY29uc3QgYWRtaW5Ub2tlbiA9IGVudnMuSElHSENIQVJUU19BRE1JTl9UT0tFTjtcclxuXHJcbiAgICAgICAgICAgIC8vIENoZWNrIHRoZSBleGlzdGVuY2Ugb2YgdGhlIHRva2VuXHJcbiAgICAgICAgICAgIGlmICghYWRtaW5Ub2tlbiB8fCAhYWRtaW5Ub2tlbi5sZW5ndGgpIHtcclxuICAgICAgICAgICAgICB0aHJvdyBuZXcgSHR0cEVycm9yKFxyXG4gICAgICAgICAgICAgICAgJ1RoZSBzZXJ2ZXIgaXMgbm90IGNvbmZpZ3VyZWQgdG8gcGVyZm9ybSBydW4tdGltZSB2ZXJzaW9uIGNoYW5nZXM6IEhJR0hDSEFSVFNfQURNSU5fVE9LRU4gaXMgbm90IHNldC4nLFxyXG4gICAgICAgICAgICAgICAgNDAxXHJcbiAgICAgICAgICAgICAgKTtcclxuICAgICAgICAgICAgfVxyXG5cclxuICAgICAgICAgICAgLy8gQ2hlY2sgaWYgdGhlIGhjLWF1dGggaGVhZGVyIGNvbnRhaW4gYSBjb3JyZWN0IHRva2VuXHJcbiAgICAgICAgICAgIGNvbnN0IHRva2VuID0gcmVxdWVzdC5nZXQoJ2hjLWF1dGgnKTtcclxuICAgICAgICAgICAgaWYgKCF0b2tlbiB8fCB0b2tlbiAhPT0gYWRtaW5Ub2tlbikge1xyXG4gICAgICAgICAgICAgIHRocm93IG5ldyBIdHRwRXJyb3IoXHJcbiAgICAgICAgICAgICAgICAnSW52YWxpZCBvciBtaXNzaW5nIHRva2VuOiBTZXQgdGhlIHRva2VuIGluIHRoZSBoYy1hdXRoIGhlYWRlci4nLFxyXG4gICAgICAgICAgICAgICAgNDAxXHJcbiAgICAgICAgICAgICAgKTtcclxuICAgICAgICAgICAgfVxyXG5cclxuICAgICAgICAgICAgLy8gQ29tcGFyZSB2ZXJzaW9uc1xyXG4gICAgICAgICAgICBjb25zdCBuZXdWZXJzaW9uID0gcmVxdWVzdC5wYXJhbXMubmV3VmVyc2lvbjtcclxuICAgICAgICAgICAgaWYgKG5ld1ZlcnNpb24pIHtcclxuICAgICAgICAgICAgICB0cnkge1xyXG4gICAgICAgICAgICAgICAgLy8gZXNsaW50LWRpc2FibGUtbmV4dC1saW5lIGltcG9ydC9uby1uYW1lZC1hcy1kZWZhdWx0LW1lbWJlclxyXG4gICAgICAgICAgICAgICAgYXdhaXQgY2FjaGUudXBkYXRlVmVyc2lvbihuZXdWZXJzaW9uKTtcclxuICAgICAgICAgICAgICB9IGNhdGNoIChlcnJvcikge1xyXG4gICAgICAgICAgICAgICAgdGhyb3cgbmV3IEh0dHBFcnJvcihcclxuICAgICAgICAgICAgICAgICAgYFZlcnNpb24gY2hhbmdlOiAke2Vycm9yLm1lc3NhZ2V9YCxcclxuICAgICAgICAgICAgICAgICAgZXJyb3Iuc3RhdHVzQ29kZVxyXG4gICAgICAgICAgICAgICAgKS5zZXRFcnJvcihlcnJvcik7XHJcbiAgICAgICAgICAgICAgfVxyXG5cclxuICAgICAgICAgICAgICAvLyBTdWNjZXNzXHJcbiAgICAgICAgICAgICAgcmVzcG9uc2Uuc3RhdHVzKDIwMCkuc2VuZCh7XHJcbiAgICAgICAgICAgICAgICBzdGF0dXNDb2RlOiAyMDAsXHJcbiAgICAgICAgICAgICAgICB2ZXJzaW9uOiBjYWNoZS52ZXJzaW9uKCksXHJcbiAgICAgICAgICAgICAgICBtZXNzYWdlOiBgU3VjY2Vzc2Z1bGx5IHVwZGF0ZWQgSGlnaGNoYXJ0cyB0byB2ZXJzaW9uOiAke25ld1ZlcnNpb259LmBcclxuICAgICAgICAgICAgICB9KTtcclxuICAgICAgICAgICAgfSBlbHNlIHtcclxuICAgICAgICAgICAgICAvLyBObyB2ZXJzaW9uIHNwZWNpZmllZFxyXG4gICAgICAgICAgICAgIHRocm93IG5ldyBIdHRwRXJyb3IoJ05vIG5ldyB2ZXJzaW9uIHN1cHBsaWVkLicsIDQwMCk7XHJcbiAgICAgICAgICAgIH1cclxuICAgICAgICAgIH0gY2F0Y2ggKGVycm9yKSB7XHJcbiAgICAgICAgICAgIG5leHQoZXJyb3IpO1xyXG4gICAgICAgICAgfVxyXG4gICAgICAgIH1cclxuICAgICAgKTtcclxuIiwiLyoqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKipcclxuXHJcbkhpZ2hjaGFydHMgRXhwb3J0IFNlcnZlclxyXG5cclxuQ29weXJpZ2h0IChjKSAyMDE2LTIwMjQsIEhpZ2hzb2Z0XHJcblxyXG5MaWNlbmNlZCB1bmRlciB0aGUgTUlUIGxpY2VuY2UuXHJcblxyXG5BZGRpdGlvbmFsbHkgYSB2YWxpZCBIaWdoY2hhcnRzIGxpY2Vuc2UgaXMgcmVxdWlyZWQgZm9yIHVzZS5cclxuXHJcblNlZSBMSUNFTlNFIGZpbGUgaW4gcm9vdCBmb3IgZGV0YWlscy5cclxuXHJcbioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKiovXHJcblxyXG5pbXBvcnQgJ2NvbG9ycyc7XHJcbmltcG9ydCBkb3RlbnYgZnJvbSAnZG90ZW52JztcclxuXHJcbmltcG9ydCB7IGNoZWNrQW5kVXBkYXRlQ2FjaGUgfSBmcm9tICcuL2NhY2hlLmpzJztcclxuaW1wb3J0IHtcclxuICBiYXRjaEV4cG9ydCxcclxuICBzZXRBbGxvd0NvZGVFeGVjdXRpb24sXHJcbiAgc2luZ2xlRXhwb3J0LFxyXG4gIHN0YXJ0RXhwb3J0XHJcbn0gZnJvbSAnLi9jaGFydC5qcyc7XHJcbmltcG9ydCB7IG1hcFRvTmV3Q29uZmlnLCBtYW51YWxDb25maWcsIHNldE9wdGlvbnMgfSBmcm9tICcuL2NvbmZpZy5qcyc7XHJcbmltcG9ydCB7XHJcbiAgaW5pdExvZ2dpbmcsXHJcbiAgbG9nLFxyXG4gIGxvZ1dpdGhTdGFjayxcclxuICBzZXRMb2dMZXZlbCxcclxuICBlbmFibGVGaWxlTG9nZ2luZ1xyXG59IGZyb20gJy4vbG9nZ2VyLmpzJztcclxuaW1wb3J0IHsgaW5pdFBvb2wsIGtpbGxQb29sIH0gZnJvbSAnLi9wb29sLmpzJztcclxuaW1wb3J0IHNlcnZlciwgeyBzdGFydFNlcnZlciB9IGZyb20gJy4vc2VydmVyL3NlcnZlci5qcyc7XHJcbmltcG9ydCB7IHByaW50TG9nbywgcHJpbnRVc2FnZSB9IGZyb20gJy4vdXRpbHMuanMnO1xyXG5cclxuLy8gTG9hZCAuZW52IGludG8gZW52aXJvbm1lbnQgdmFyaWFibGVzXHJcbmRvdGVudi5jb25maWcoKTtcclxuXHJcbi8qKlxyXG4gKiBJbml0aWFsaXplcyB0aGUgZXhwb3J0IHByb2Nlc3MuIFRhc2tzIHN1Y2ggYXMgY29uZmlndXJpbmcgbG9nZ2luZywgY2hlY2tpbmdcclxuICogY2FjaGUgYW5kIHNvdXJjZXMsIGFuZCBpbml0aWFsaXppbmcgdGhlIHBvb2wgb2YgcmVzb3VyY2VzIGhhcHBlbiBkdXJpbmdcclxuICogdGhpcyBzdGFnZS4gRnVuY3Rpb24gdGhhdCBpcyByZXF1aXJlZCB0byBiZSBjYWxsZWQgYmVmb3JlIHRyeWluZyB0byBleHBvcnQgY2hhcnRzIG9yIHNldHRpbmcgYSBzZXJ2ZXIuIFRoZSBgb3B0aW9uc2AgaXMgYW4gb2JqZWN0IHRoYXQgY29udGFpbnMgYWxsIG9wdGlvbnMuXHJcbiAqXHJcbiAqIEBwYXJhbSB7T2JqZWN0fSBvcHRpb25zIC0gQWxsIGV4cG9ydCBvcHRpb25zLlxyXG4gKlxyXG4gKiBAcmV0dXJucyB7UHJvbWlzZTxPYmplY3Q+fSBQcm9taXNlIHJlc29sdmluZyB0byB0aGUgdXBkYXRlZCBleHBvcnQgb3B0aW9ucy5cclxuICovXHJcbmNvbnN0IGluaXRFeHBvcnQgPSBhc3luYyAob3B0aW9ucykgPT4ge1xyXG4gIC8vIFNldCB0aGUgYWxsb3dDb2RlRXhlY3V0aW9uIHBlciBleHBvcnQgbW9kdWxlIHNjb3BlXHJcbiAgc2V0QWxsb3dDb2RlRXhlY3V0aW9uKFxyXG4gICAgb3B0aW9ucy5jdXN0b21Mb2dpYyAmJiBvcHRpb25zLmN1c3RvbUxvZ2ljLmFsbG93Q29kZUV4ZWN1dGlvblxyXG4gICk7XHJcblxyXG4gIC8vIEluaXQgdGhlIGxvZ2dpbmdcclxuICBpbml0TG9nZ2luZyhvcHRpb25zLmxvZ2dpbmcpO1xyXG5cclxuICAvLyBDaGVjayBpZiBjYWNoZSBuZWVkcyB0byBiZSB1cGRhdGVkXHJcbiAgYXdhaXQgY2hlY2tBbmRVcGRhdGVDYWNoZShvcHRpb25zLmhpZ2hjaGFydHMgfHwgeyB2ZXJzaW9uOiAnbGF0ZXN0JyB9KTtcclxuXHJcbiAgLy8gSW5pdCB0aGUgcG9vbFxyXG4gIGF3YWl0IGluaXRQb29sKHtcclxuICAgIHBvb2w6IG9wdGlvbnMucG9vbCB8fCB7XHJcbiAgICAgIG1pbldvcmtlcnM6IDEsXHJcbiAgICAgIG1heFdvcmtlcnM6IDFcclxuICAgIH0sXHJcbiAgICBwdXBwZXRlZXJBcmdzOiBvcHRpb25zLnB1cHBldGVlcj8uYXJncyB8fCBbXVxyXG4gIH0pO1xyXG5cclxuICAvLyBSZXR1cm4gdXBkYXRlZCBvcHRpb25zXHJcbiAgcmV0dXJuIG9wdGlvbnM7XHJcbn07XHJcblxyXG5leHBvcnQgZGVmYXVsdCB7XHJcbiAgLy8gU2VydmVyXHJcbiAgc2VydmVyLFxyXG4gIHN0YXJ0U2VydmVyLFxyXG4gIHNldE9wdGlvbnMsXHJcblxyXG4gIC8vIEV4cG9ydGluZ1xyXG4gIGluaXRFeHBvcnQsXHJcbiAgc2luZ2xlRXhwb3J0LFxyXG4gIGJhdGNoRXhwb3J0LFxyXG4gIHN0YXJ0RXhwb3J0LFxyXG4gIGtpbGxQb29sLFxyXG5cclxuICAvLyBMb2dzXHJcbiAgbG9nLFxyXG4gIGxvZ1dpdGhTdGFjayxcclxuICBzZXRMb2dMZXZlbCxcclxuICBlbmFibGVGaWxlTG9nZ2luZyxcclxuXHJcbiAgLy8gVXRpbHNcclxuICBtYXBUb05ld0NvbmZpZyxcclxuICBtYW51YWxDb25maWcsXHJcbiAgcHJpbnRMb2dvLFxyXG4gIHByaW50VXNhZ2VcclxufTtcclxuIl0sIm5hbWVzIjpbImFzeW5jIiwiZmV0Y2giLCJ1cmwiLCJyZXF1ZXN0T3B0aW9ucyIsIlByb21pc2UiLCJyZXNvbHZlIiwicmVqZWN0IiwicHJvdG9jb2wiLCJzdGFydHNXaXRoIiwiaHR0cHMiLCJodHRwIiwiZ2V0UHJvdG9jb2wiLCJnZXQiLCJyZXMiLCJkYXRhIiwib24iLCJjaHVuayIsInRleHQiLCJlcnJvciIsImRlZmF1bHRDb25maWciLCJwdXBwZXRlZXIiLCJhcmdzIiwidmFsdWUiLCJ0eXBlIiwiZGVzY3JpcHRpb24iLCJoaWdoY2hhcnRzIiwidmVyc2lvbiIsImVudkxpbmsiLCJjZG5VUkwiLCJjb3JlU2NyaXB0cyIsIm1vZHVsZXMiLCJpbmRpY2F0b3JzIiwic2NyaXB0cyIsImZvcmNlRmV0Y2giLCJjYWNoZVBhdGgiLCJleHBvcnQiLCJpbmZpbGUiLCJpbnN0ciIsIm9wdGlvbnMiLCJvdXRmaWxlIiwiY29uc3RyIiwiZGVmYXVsdEhlaWdodCIsImRlZmF1bHRXaWR0aCIsImRlZmF1bHRTY2FsZSIsImhlaWdodCIsIndpZHRoIiwic2NhbGUiLCJnbG9iYWxPcHRpb25zIiwidGhlbWVPcHRpb25zIiwiYmF0Y2giLCJyYXN0ZXJpemF0aW9uVGltZW91dCIsImN1c3RvbUxvZ2ljIiwiYWxsb3dDb2RlRXhlY3V0aW9uIiwiYWxsb3dGaWxlUmVzb3VyY2VzIiwiY3VzdG9tQ29kZSIsImNhbGxiYWNrIiwicmVzb3VyY2VzIiwibG9hZENvbmZpZyIsImxlZ2FjeU5hbWUiLCJjcmVhdGVDb25maWciLCJzZXJ2ZXIiLCJlbmFibGUiLCJjbGlOYW1lIiwiaG9zdCIsInBvcnQiLCJiZW5jaG1hcmtpbmciLCJzc2wiLCJmb3JjZSIsImNlcnRQYXRoIiwicmF0ZUxpbWl0aW5nIiwibWF4UmVxdWVzdHMiLCJ3aW5kb3ciLCJkZWxheSIsInRydXN0UHJveHkiLCJza2lwS2V5Iiwic2tpcFRva2VuIiwicG9vbCIsIm1pbldvcmtlcnMiLCJtYXhXb3JrZXJzIiwid29ya0xpbWl0IiwiYWNxdWlyZVRpbWVvdXQiLCJjcmVhdGVUaW1lb3V0IiwiZGVzdHJveVRpbWVvdXQiLCJpZGxlVGltZW91dCIsImNyZWF0ZVJldHJ5SW50ZXJ2YWwiLCJyZWFwZXJJbnRlcnZhbCIsImxpc3RlblRvUHJvY2Vzc0V4aXRzIiwibG9nZ2luZyIsImxldmVsIiwiZmlsZSIsImRlc3QiLCJ1aSIsInJvdXRlIiwib3RoZXIiLCJub0xvZ28iLCJwcm9tcHRzQ29uZmlnIiwibmFtZSIsIm1lc3NhZ2UiLCJpbml0aWFsIiwiam9pbiIsInNlcGFyYXRvciIsImluc3RydWN0aW9ucyIsImNob2ljZXMiLCJoaW50IiwibWluIiwibWF4Iiwicm91bmQiLCJhYnNvbHV0ZVByb3BzIiwibmVzdGVkQXJncyIsImNyZWF0ZU5lc3RlZEFyZ3MiLCJvYmoiLCJwcm9wQ2hhaW4iLCJPYmplY3QiLCJrZXlzIiwiZm9yRWFjaCIsImsiLCJpbmNsdWRlcyIsImVudHJ5Iiwic3Vic3RyaW5nIiwidW5kZWZpbmVkIiwiY29sb3JzIiwidG9Db25zb2xlIiwidG9GaWxlIiwicGF0aENyZWF0ZWQiLCJsZXZlbHNEZXNjIiwidGl0bGUiLCJjb2xvciIsImxpc3RlbmVycyIsImtleSIsIm9wdGlvbiIsImVudHJpZXMiLCJsb2dUb0ZpbGUiLCJ0ZXh0cyIsInByZWZpeCIsImV4aXN0c1N5bmMiLCJta2RpclN5bmMiLCJhcHBlbmRGaWxlIiwiY29uY2F0IiwiY29uc29sZSIsImxvZyIsIm5ld0xldmVsIiwibGVuZ3RoIiwiRGF0ZSIsInRvU3RyaW5nIiwic3BsaXQiLCJ0cmltIiwiZm4iLCJhcHBseSIsImxvZ1dpdGhTdGFjayIsImN1c3RvbU1lc3NhZ2UiLCJtYWluTWVzc2FnZSIsInN0YWNrTWVzc2FnZSIsInN0YWNrIiwic2xpY2UiLCJzZXRMb2dMZXZlbCIsImVuYWJsZUZpbGVMb2dnaW5nIiwibG9nRGVzdCIsImxvZ0ZpbGUiLCJlbmRzV2l0aCIsIl9fZGlybmFtZSIsImZpbGVVUkxUb1BhdGgiLCJVUkwiLCJkb2N1bWVudCIsInJlcXVpcmUiLCJwYXRoVG9GaWxlVVJMIiwiX19maWxlbmFtZSIsImhyZWYiLCJfZG9jdW1lbnRDdXJyZW50U2NyaXB0Iiwic3JjIiwiYmFzZVVSSSIsImZpeFR5cGUiLCJmb3JtYXRzIiwib3V0VHlwZSIsInBvcCIsImZpbmQiLCJ0IiwiaGFuZGxlUmVzb3VyY2VzIiwiYWxsb3dlZFByb3BzIiwiaGFuZGxlZFJlc291cmNlcyIsImNvcnJlY3RSZXNvdXJjZXMiLCJpc0NvcnJlY3RKU09OIiwicmVhZEZpbGVTeW5jIiwiZmlsZXMiLCJwcm9wTmFtZSIsIm1hcCIsIml0ZW0iLCJwYXJzZWREYXRhIiwiSlNPTiIsInBhcnNlIiwic3RyaW5naWZ5IiwiZGVlcENvcHkiLCJjb3B5IiwiQXJyYXkiLCJpc0FycmF5IiwicHJvdG90eXBlIiwiaGFzT3duUHJvcGVydHkiLCJjYWxsIiwib3B0aW9uc1N0cmluZ2lmeSIsImFsbG93RnVuY3Rpb25zIiwicmVwbGFjZUFsbCIsInByaW50VXNhZ2UiLCJib2xkIiwieWVsbG93IiwiY3ljbGVDYXRlZ29yaWVzIiwiZGVzY05hbWUiLCJncmVlbiIsImkiLCJibHVlIiwiY2F0ZWdvcnkiLCJ0b1VwcGVyQ2FzZSIsInJlZCIsInRvQm9vbGVhbiIsIndyYXBBcm91bmQiLCJyZXBsYWNlIiwibWVhc3VyZVRpbWUiLCJzdGFydCIsInByb2Nlc3MiLCJocnRpbWUiLCJiaWdpbnQiLCJOdW1iZXIiLCJkb3RlbnYiLCJjb25maWciLCJ2IiwieiIsImVudW0iLCJ0cmFuc2Zvcm0iLCJvcHRpb25hbCIsInN0cmluZyIsInZhbCIsImVudnMiLCJvYmplY3QiLCJISUdIQ0hBUlRTX1ZFUlNJT04iLCJyZWZpbmUiLCJ0ZXN0IiwiSElHSENIQVJUU19DRE5fVVJMIiwiSElHSENIQVJUU19DT1JFX1NDUklQVFMiLCJISUdIQ0hBUlRTX01PRFVMRVMiLCJISUdIQ0hBUlRTX0lORElDQVRPUlMiLCJISUdIQ0hBUlRTX0ZPUkNFX0ZFVENIIiwiSElHSENIQVJUU19DQUNIRV9QQVRIIiwiSElHSENIQVJUU19BRE1JTl9UT0tFTiIsIkVYUE9SVF9UWVBFIiwiRVhQT1JUX0NPTlNUUiIsIkVYUE9SVF9ERUZBVUxUX0hFSUdIVCIsImNvZXJjZSIsIm51bWJlciIsInBvc2l0aXZlIiwiRVhQT1JUX0RFRkFVTFRfV0lEVEgiLCJFWFBPUlRfREVGQVVMVF9TQ0FMRSIsIkVYUE9SVF9SQVNURVJJWkFUSU9OX1RJTUVPVVQiLCJDVVNUT01fTE9HSUNfQUxMT1dfQ09ERV9FWEVDVVRJT04iLCJDVVNUT01fTE9HSUNfQUxMT1dfRklMRUxfUkVTT1VSQ0VTIiwiU0VSVkVSX0VOQUJMRSIsIlNFUlZFUl9IT1NUIiwiU0VSVkVSX1BPUlQiLCJTRVJWRVJfQkVOQ0hNQVJLSU5HIiwiU0VSVkVSX1NTTF9FTkFCTEUiLCJTRVJWRVJfU1NMX0ZPUkNFIiwiU0VSVkVSX1NTTF9QT1JUIiwiU0VSVkVSX1NTTF9DRVJUX1BBVEgiLCJTRVJWRVJfUkFURV9MSU1JVElOR19FTkFCTEUiLCJTRVJWRVJfUkFURV9MSU1JVElOR19NQVhfUkVRVUVTVFMiLCJTRVJWRVJfUkFURV9MSU1JVElOR19XSU5ET1ciLCJTRVJWRVJfUkFURV9MSU1JVElOR19ERUxBWSIsIlNFUlZFUl9SQVRFX0xJTUlUSU5HX1RSVVNUX1BST1hZIiwiU0VSVkVSX1JBVEVfTElNSVRJTkdfU0tJUF9LRVkiLCJTRVJWRVJfUkFURV9MSU1JVElOR19TS0lQX1RPS0VOIiwiUE9PTF9NSU5fV09SS0VSUyIsIlBPT0xfTUFYX1dPUktFUlMiLCJQT09MX1dPUktfTElNSVQiLCJQT09MX0FDUVVJUkVfVElNRU9VVCIsIlBPT0xfQ1JFQVRFX1RJTUVPVVQiLCJQT09MX0RFU1RST1lfVElNRU9VVCIsIlBPT0xfSURMRV9USU1FT1VUIiwiUE9PTF9DUkVBVEVfUkVUUllfSU5URVJWQUwiLCJQT09MX1JFQVBFUl9JTlRFUlZBTCIsIlBPT0xfQkVOQ0hNQVJLSU5HIiwiUE9PTF9MSVNURU5fVE9fUFJPQ0VTU19FWElUUyIsIkxPR0dJTkdfTEVWRUwiLCJMT0dHSU5HX0ZJTEUiLCJMT0dHSU5HX0RFU1QiLCJVSV9FTkFCTEUiLCJVSV9ST1VURSIsIk9USEVSX05PX0xPR08iLCJOT0RFX0VOViIsImRlZmF1bHQiLCJQUk9YWV9TRVJWRVJfVElNRU9VVCIsIlBST1hZX1NFUlZFUl9IT1NUIiwiUFJPWFlfU0VSVkVSX1BPUlQiLCJlbnYiLCJFeHBvcnRFcnJvciIsIkVycm9yIiwiY29uc3RydWN0b3IiLCJzdXBlciIsInRoaXMiLCJzZXRFcnJvciIsInN0YXR1c0NvZGUiLCJjYWNoZSIsImFjdGl2ZU1hbmlmZXN0Iiwic291cmNlcyIsImhjVmVyc2lvbiIsImFwcGxpZWRDb25maWciLCJleHRyYWN0VmVyc2lvbiIsImluZGV4T2YiLCJmZXRjaEFuZFByb2Nlc3NTY3JpcHQiLCJzY3JpcHQiLCJwcm94eUFnZW50IiwiZmV0Y2hlZE1vZHVsZXMiLCJzaG91bGRUaHJvd0Vycm9yIiwiYWdlbnQiLCJ0aW1lb3V0IiwicmVzcG9uc2UiLCJ1cGRhdGVDYWNoZSIsInNvdXJjZVBhdGgiLCJjdXN0b21TY3JpcHRzIiwicHJveHlIb3N0IiwicHJveHlQb3J0IiwiSHR0cHNQcm94eUFnZW50IiwibW9kdWxlU2NyaXB0cyIsImFsbEZldGNoUHJvbWlzZXMiLCJhbGwiLCJmZXRjaFNjcmlwdHMiLCJjIiwibSIsIndyaXRlRmlsZVN5bmMiLCJjaGVja0FuZFVwZGF0ZUNhY2hlIiwibWFuaWZlc3RQYXRoIiwicmVxdWVzdFVwZGF0ZSIsIm1hbmlmZXN0IiwibW9kdWxlTWFwIiwibnVtYmVyT2ZNb2R1bGVzIiwic29tZSIsIm1vZHVsZU5hbWUiLCJuZXdNYW5pZmVzdCIsInNhdmVDb25maWdUb01hbmlmZXN0IiwiZ2V0Q2FjaGVQYXRoIiwiY2FjaGUkMSIsIm5ld1ZlcnNpb24iLCJhc3NpZ24iLCJnZW5lcmFsT3B0aW9ucyIsImdldE9wdGlvbnMiLCJtZXJnZUNvbmZpZ09wdGlvbnMiLCJuZXdPcHRpb25zIiwibWVyZ2VkT3B0aW9ucyIsInVwZGF0ZURlZmF1bHRDb25maWciLCJjb25maWdPYmoiLCJjdXN0b21PYmoiLCJjdXN0b21WYWx1ZSIsImluaXRPcHRpb25zIiwiaXRlbXMiLCJyZWN1cnNpdmVQcm9wcyIsIm9iamVjdFRvVXBkYXRlIiwibmVzdGVkTmFtZXMiLCJzaGlmdCIsIlJBTkRPTV9QSUQiLCJyYW5kb21CeXRlcyIsIlBVUFBFVEVFUl9ESVIiLCJwYXRoIiwibWluaW1hbEFyZ3MiLCJ0ZW1wbGF0ZSIsImZzIiwiYnJvd3NlciIsInNldFBhZ2VDb250ZW50IiwicGFnZSIsInNldENvbnRlbnQiLCJhZGRTY3JpcHRUYWciLCJldmFsdWF0ZSIsInNldHVwSGlnaGNoYXJ0cyIsIiRldmFsIiwiZWxlbWVudCIsImVycm9yTWVzc2FnZSIsIl9kaXNwbGF5RXJyb3JzIiwiaW5uZXJIVE1MIiwiY2xlYXJQYWdlIiwiaGFyZFJlc2V0IiwiZ290byIsImJvZHkiLCJuZXdQYWdlIiwic2V0Q2FjaGVFbmFibGVkIiwiY2xvc2UiLCJpc0Nvbm5lY3RlZCIsIl9fYmFzZWRpciIsInNldEFzQ29uZmlnIiwiY2hhcnQiLCJ0cmlnZ2VyRXhwb3J0IiwicHVwcGV0ZWVyRXhwb3J0IiwiaW5qZWN0ZWRSZXNvdXJjZXMiLCJjbGVhckluamVjdGVkIiwiZGlzcG9zZSIsInNjcmlwdHNUb1JlbW92ZSIsImdldEVsZW1lbnRzQnlUYWdOYW1lIiwic3R5bGVzVG9SZW1vdmUiLCJsaW5rc1RvUmVtb3ZlIiwicmVtb3ZlIiwiZXhwb3J0T3B0aW9ucyIsInJlcXVlc3RBbmltYXRpb25GcmFtZSIsImRpc3BsYXlFcnJvcnMiLCJkZWJ1Z2dlciIsImlzU1ZHIiwiZCIsInN2Z1RlbXBsYXRlIiwic3RySW5qIiwianMiLCJwdXNoIiwiY29udGVudCIsImlzTG9jYWwiLCJjc3MiLCJjc3NJbXBvcnRzIiwibWF0Y2giLCJjc3NJbXBvcnRQYXRoIiwiYWRkU3R5bGVUYWciLCJzaXplIiwiY2hhcnRIZWlnaHQiLCJiYXNlVmFsIiwiY2hhcnRXaWR0aCIsInBhcnNlRmxvYXQiLCJIaWdoY2hhcnRzIiwiY2hhcnRzIiwidmlld3BvcnRIZWlnaHQiLCJNYXRoIiwiY2VpbCIsInZpZXdwb3J0V2lkdGgiLCJzZXRWaWV3cG9ydCIsImRldmljZVNjYWxlRmFjdG9yIiwiem9vbUNhbGxiYWNrIiwic3R5bGUiLCJ6b29tIiwibWFyZ2luIiwieCIsInkiLCJnZXRCb3VuZGluZ0NsaWVudFJlY3QiLCJ0cnVuYyIsImdldENsaXBSZWdpb24iLCJvdXRlckhUTUwiLCJjcmVhdGVTVkciLCJlbmNvZGluZyIsImNsaXAiLCJyYWNlIiwic2NyZWVuc2hvdCIsIm9taXRCYWNrZ3JvdW5kIiwiX3Jlc29sdmUiLCJzZXRUaW1lb3V0IiwiY3JlYXRlSW1hZ2UiLCJwZGYiLCJjcmVhdGVQREYiLCJvbGRDaGFydHMiLCJvbGRDaGFydCIsImRlc3Ryb3kiLCJwdXBwZXRlZXJBcmdzIiwicGVyZm9ybWVkRXhwb3J0cyIsImV4cG9ydEF0dGVtcHRzIiwidGltZVNwZW50IiwiZHJvcHBlZEV4cG9ydHMiLCJzcGVudEF2ZXJhZ2UiLCJwb29sQ29uZmlnIiwiZmFjdG9yeSIsImNyZWF0ZSIsImlkIiwidXVpZCIsInN0YXJ0RGF0ZSIsImdldFRpbWUiLCJicm93c2VyTmV3UGFnZSIsImlzQ2xvc2VkIiwid29ya0NvdW50IiwicmFuZG9tIiwidmFsaWRhdGUiLCJ3b3JrZXJIYW5kbGUiLCJpbml0UG9vbCIsImNvZGUiLCJraWxsUG9vbCIsImV4aXQiLCJhbGxBcmdzIiwidHJ5Q291bnQiLCJvcGVuIiwibGF1bmNoIiwiaGVhZGxlc3MiLCJ1c2VyRGF0YURpciIsImNyZWF0ZUJyb3dzZXIiLCJwYXJzZUludCIsIlBvb2wiLCJhY3F1aXJlVGltZW91dE1pbGxpcyIsImNyZWF0ZVRpbWVvdXRNaWxsaXMiLCJkZXN0cm95VGltZW91dE1pbGxpcyIsImlkbGVUaW1lb3V0TWlsbGlzIiwiY3JlYXRlUmV0cnlJbnRlcnZhbE1pbGxpcyIsInJlYXBJbnRlcnZhbE1pbGxpcyIsInByb3BhZ2F0ZUNyZWF0ZUVycm9yIiwicmVzb3VyY2UiLCJldmVudElkIiwiaW5pdGlhbFJlc291cmNlcyIsImFjcXVpcmUiLCJwcm9taXNlIiwicmVsZWFzZSIsImJyb3dzZXJDbG9zZSIsImRlc3Ryb3llZCIsInBvc3RXb3JrIiwiZ2V0UG9vbEluZm8iLCJhY3F1aXJlQ291bnRlciIsInBheWxvYWQiLCJyZXF1ZXN0SWQiLCJ3b3JrU3RhcnQiLCJleHBvcnRDb3VudGVyIiwicmVzdWx0IiwiZXhwb3J0VGltZSIsIm51bUZyZWUiLCJudW1Vc2VkIiwibnVtUGVuZGluZ0FjcXVpcmVzIiwicG9vbCQxIiwiYXZhaWxhYmxlIiwiaW5Vc2UiLCJwZW5kaW5nQWNxdWlyZSIsInN0YXJ0RXhwb3J0Iiwic2V0dGluZ3MiLCJlbmRDYWxsYmFjayIsInN2ZyIsImluaXRFeHBvcnRTZXR0aW5ncyIsImV4cG9ydEFzU3RyaW5nIiwiZG9TdHJhaWdodEluamVjdCIsImRvRXhwb3J0IiwiZmluZENoYXJ0U2l6ZSIsImV4cG9ydGluZyIsInByZWNpc2lvbiIsIm11bHRpcGxpZXIiLCJwb3ciLCJyb3VuZE51bWJlciIsInNvdXJjZUhlaWdodCIsInNvdXJjZVdpZHRoIiwicGFyYW0iLCJjaGFydEpzb24iLCJjdXN0b21Mb2dpY09wdGlvbnMiLCJhbGxvd0NvZGVFeGVjdXRpb25TY29wZWQiLCJlbmFibGVkIiwib3B0aW9uc05hbWUiLCJzdHJpbmdUb0V4cG9ydCIsImNoYXJ0SlNPTiIsImxvZ0Vycm9yTWlkZGxld2FyZSIsInJlcSIsIm5leHQiLCJyZXR1cm5FcnJvck1pZGRsZXdhcmUiLCJzdENvZGUiLCJzdGF0dXMiLCJqc29uIiwicmF0ZUxpbWl0IiwiYXBwIiwibGltaXRDb25maWciLCJtc2ciLCJyYXRlT3B0aW9ucyIsImxpbWl0ZXIiLCJ3aW5kb3dNcyIsImRlbGF5TXMiLCJoYW5kbGVyIiwicmVxdWVzdCIsImZvcm1hdCIsInNlbmQiLCJza2lwIiwicXVlcnkiLCJhY2Nlc3NfdG9rZW4iLCJ1c2UiLCJIdHRwRXJyb3IiLCJzZXRTdGF0dXMiLCJyZXZlcnNlZE1pbWUiLCJwbmciLCJqcGVnIiwiZ2lmIiwicmVxdWVzdHNDb3VudGVyIiwiYmVmb3JlUmVxdWVzdCIsImFmdGVyUmVxdWVzdCIsImRvQ2FsbGJhY2tzIiwiY2FsbGJhY2tzIiwidW5pcXVlSWQiLCJjYWxsUmVzcG9uc2UiLCJleHBvcnRIYW5kbGVyIiwic3RvcENvdW50ZXIiLCJkZWZhdWx0T3B0aW9ucyIsImhlYWRlcnMiLCJjb25uZWN0aW9uIiwicmVtb3RlQWRkcmVzcyIsImNvbm5lY3Rpb25BYm9ydGVkIiwic29ja2V0IiwidG9Mb3dlckNhc2UiLCJzdWJzdHIiLCJiNjQiLCJub0Rvd25sb2FkIiwicGF0dGVybiIsImlzUHJpdmF0ZVJhbmdlVXJsRm91bmQiLCJpbmZvIiwicmVtb3ZlQWxsTGlzdGVuZXJzIiwiQnVmZmVyIiwiZnJvbSIsImhlYWRlciIsImF0dGFjaG1lbnQiLCJwYXJhbXMiLCJmaWxlbmFtZSIsInBrZ0ZpbGUiLCJwYXRoZXIiLCJzZXJ2ZXJTdGFydFRpbWUiLCJleHByZXNzIiwiZGlzYWJsZSIsImNvcnMiLCJzdG9yYWdlIiwibXVsdGVyIiwibWVtb3J5U3RvcmFnZSIsInVwbG9hZCIsImxpbWl0cyIsImZpZWxkU2l6ZSIsImxpbWl0IiwidXJsZW5jb2RlZCIsImV4dGVuZGVkIiwibm9uZSIsImF0dGFjaEVycm9ySGFuZGxlcnMiLCJzdGFydFNlcnZlciIsInNlcnZlckNvbmZpZyIsImh0dHBTZXJ2ZXIiLCJjcmVhdGVTZXJ2ZXIiLCJsaXN0ZW4iLCJjZXJ0IiwiZnNQcm9taXNlcyIsInJlYWRGaWxlIiwicG9zaXgiLCJodHRwc1NlcnZlciIsIk5hTiIsInN0YXRpYyIsImJvb3RUaW1lIiwidXB0aW1lIiwiZmxvb3IiLCJoaWdoY2hhcnRzVmVyc2lvbiIsImF2ZXJhZ2VQcm9jZXNzaW5nVGltZSIsImZhaWxlZEV4cG9ydHMiLCJzdWNlc3NSYXRpbyIsImhlYWx0aFJvdXRlIiwicG9zdCIsImV4cG9ydFJvdXRlcyIsInNlbmRGaWxlIiwidWlSb3V0ZSIsImFkbWluVG9rZW4iLCJ0b2tlbiIsInZTd2l0Y2hSb3V0ZSIsImVycm9ySGFuZGxlciIsImVuYWJsZVJhdGVMaW1pdGluZyIsImdldEV4cHJlc3MiLCJnZXRBcHAiLCJtaWRkbGV3YXJlcyIsImluZGV4Iiwic2V0T3B0aW9ucyIsInVzZXJPcHRpb25zIiwiY29uZmlnSW5kZXgiLCJmaW5kSW5kZXgiLCJhcmciLCJmaWxlTmFtZSIsImxvYWRDb25maWdGaWxlIiwic2hvd1VzYWdlIiwicHJvcGVydGllc0NoYWluIiwiYXJndW1lbnRUeXBlIiwicmVkdWNlIiwicHJvcCIsInBhaXJBcmd1bWVudFZhbHVlIiwiaW5pdEV4cG9ydCIsImluaXRMb2dnaW5nIiwic2luZ2xlRXhwb3J0IiwiYmF0Y2hFeHBvcnQiLCJiYXRjaEZ1bmN0aW9ucyIsInBhaXIiLCJtYXBUb05ld0NvbmZpZyIsIm9sZE9wdGlvbnMiLCJtYW51YWxDb25maWciLCJjb25maWdGaWxlTmFtZSIsImNvbmZpZ0ZpbGUiLCJjaG9pY2UiLCJwcm9tcHRzIiwib25TdWJtaXQiLCJwIiwiY2F0ZWdvcmllcyIsInF1ZXN0aW9uc0NvdW50ZXIiLCJhbGxRdWVzdGlvbnMiLCJzZWN0aW9uIiwicHJvbXB0IiwiYW5zd2VyIiwibW9kdWxlIiwicHJvbWlzZXMiLCJ3cml0ZUZpbGUiLCJwcmludExvZ28iLCJwYWNrYWdlVmVyc2lvbiJdLCJtYXBwaW5ncyI6Im11QkF5QkFBLGVBQWVDLEVBQU1DLEVBQUtDLEVBQWlCLElBQ3pDLE9BQU8sSUFBSUMsU0FBUSxDQUFDQyxFQUFTQyxLQUMzQixNQUFNQyxFQWJVLENBQUNMLEdBQVNBLEVBQUlNLFdBQVcsU0FBV0MsRUFBUUMsRUFhM0NDLENBQVlULEdBRTdCSyxFQUNHSyxJQUFJVixFQUFLQyxHQUFpQlUsSUFDekIsSUFBSUMsRUFBTyxHQUdYRCxFQUFJRSxHQUFHLFFBQVNDLElBQ2RGLEdBQVFFLENBQUssSUFJZkgsRUFBSUUsR0FBRyxPQUFPLEtBQ1BELEdBQ0hSLEVBQU8scUNBR1RPLEVBQUlJLEtBQU9ILEVBQ1hULEVBQVFRLEVBQUksR0FDWixJQUVIRSxHQUFHLFNBQVVHLElBQ1paLEVBQU9ZLEVBQU0sR0FDYixHQUVSLENDcENPLE1BQU1DLEVBQWdCLENBQzNCQyxVQUFXLENBQ1RDLEtBQU0sQ0FDSkMsTUFBTyxHQUNQQyxLQUFNLFdBQ05DLFlBQWEsMENBR2pCQyxXQUFZLENBQ1ZDLFFBQVMsQ0FDUEosTUFBTyxTQUNQSyxRQUFTLHFCQUNUSixLQUFNLFNBQ05DLFlBQWEsc0NBRWZJLE9BQVEsQ0FDTk4sTUFBTywrQkFDUEssUUFBUyxxQkFDVEosS0FBTSxTQUNOQyxZQUFhLGtEQUVmSyxZQUFhLENBQ1hGLFFBQVMsMEJBQ1RMLE1BQU8sQ0FBQyxhQUFjLGtCQUFtQixpQkFDekNDLEtBQU0sV0FDTkMsWUFBYSx5Q0FFZk0sUUFBUyxDQUNQSCxRQUFTLHFCQUNUTCxNQUFPLENBQ0wsUUFDQSxNQUNBLFFBQ0EsWUFDQSxjQUNBLHVCQUNBLGdCQUNBLHVCQUNBLGVBQ0EsUUFDQSxPQUNBLGFBQ0EsbUJBQ0EsZUFDQSxjQUNBLFVBQ0EsVUFDQSxjQUNBLFdBQ0EsVUFDQSxZQUNBLGNBQ0EsWUFDQSxzQkFDQSxTQUNBLFNBQ0EsV0FDQSxhQUNBLFlBQ0EsZUFDQSx5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
+"use strict";require("colors");var e=require("dotenv"),t=require("fs"),r=require("path"),i=require("https-proxy-agent"),o=require("http"),n=require("https"),s=require("url"),a=require("zod"),l=require("prompts"),c=require("tarn"),p=require("uuid"),u=require("node:path"),h=require("puppeteer"),d=require("node:crypto"),g=require("cors"),m=require("express"),f=require("multer"),y=require("express-rate-limit"),v="undefined"!=typeof document?document.currentScript:null;function b(e){var t=Object.create(null);return e&&Object.keys(e).forEach((function(r){if("default"!==r){var i=Object.getOwnPropertyDescriptor(e,r);Object.defineProperty(t,r,i.get?i:{enumerable:!0,get:function(){return e[r]}})}})),t.default=e,Object.freeze(t)}var w=b(s);async function E(e,t={}){return new Promise(((r,i)=>{const s=(e=>e.startsWith("https")?n:o)(e);s.get(e,t,(e=>{let t="";e.on("data",(e=>{t+=e})),e.on("end",(()=>{t||i("Nothing was fetched from the URL."),e.text=t,r(e)}))})).on("error",(e=>{i(e)}))}))}const T={puppeteer:{args:{value:[],type:"string[]",description:"Arguments array to send to Puppeteer."}},highcharts:{version:{value:"latest",envLink:"HIGHCHARTS_VERSION",type:"string",description:"The Highcharts version to be used."},cdnURL:{value:"https://code.highcharts.com/",envLink:"HIGHCHARTS_CDN_URL",type:"string",description:"The CDN URL for Highcharts scripts to be used."},coreScripts:{envLink:"HIGHCHARTS_CORE_SCRIPTS",value:["highcharts","highcharts-more","highcharts-3d"],type:"string[]",description:"The core Highcharts scripts to fetch."},modules:{envLink:"HIGHCHARTS_MODULES",value:["stock","map","gantt","exporting","export-data","parallel-coordinates","accessibility","annotations-advanced","boost-canvas","boost","data","data-tools","draggable-points","static-scale","broken-axis","heatmap","tilemap","tiledwebmap","timeline","treemap","treegraph","item-series","drilldown","histogram-bellcurve","bullet","funnel","funnel3d","geoheatmap","pyramid3d","networkgraph","overlapping-datalabels","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","flowmap"],type:"string[]",description:"The modules of Highcharts to fetch."},indicators:{envLink:"HIGHCHARTS_INDICATORS",value:["indicators-all"],type:"string[]",description:"The indicators of Highcharts 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 optional scripts or dependencies to fetch."},forceFetch:{envLink:"HIGHCHARTS_FORCE_FETCH",value:!1,type:"boolean",description:"The flag to determine whether to refetch all scripts after each server rerun."},cachePath:{envLink:"HIGHCHARTS_CACHE_PATH",value:".cache",type:"string",description:"The path to the cache directory. It is used to store the Highcharts scripts and custom scripts."}},export:{infile:{value:!1,type:"string",description:"The input file should include a name and a type (json or svg). It must be correctly formatted as a JSON or SVG file."},instr:{value:!1,type:"string",description:"Input, provided in the form of a stringified JSON or SVG file, will override the --infile option."},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). This will ignore the --type flag."},type:{envLink:"EXPORT_TYPE",value:"png",type:"string",description:"The file export format. It can be jpeg, png, pdf, or svg."},constr:{envLink:"EXPORT_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 no value is set."},defaultWidth:{envLink:"EXPORT_DEFAULT_WIDTH",value:600,type:"number",description:"The default width of the exported chart. Used when no value is set."},defaultScale:{envLink:"EXPORT_DEFAULT_SCALE",value:1,type:"number",description:"The default scale of the exported chart. Used when no value is set."},height:{type:"number",value:!1,description:"The height of the exported chart, overriding the option in the chart settings."},width:{type:"number",value:!1,description:"The width of the exported chart, overriding the option in the chart settings."},scale:{value:!1,type:"number",description:"The scale of the exported chart, overriding the option in the chart settings. Ranges between 0.1 and 5.0."},globalOptions:{value:!1,type:"string",description:"Either a stringified JSON or a filename containing options to be passed into the Highcharts.setOptions."},themeOptions:{value:!1,type:"string",description:"Either a stringified JSON or a filename containing theme options to be passed into the Highcharts.setOptions."},batch:{value:!1,type:"string",description:'Initiates a batch job with a string containing input/output pairs: "in=out;in=out;...".'},rasterizationTimeout:{envLink:"EXPORT_RASTERIZATION_TIMEOUT",value:1500,type:"number",description:"The duration in milliseconds to wait for rendering a webpage."}},customLogic:{allowCodeExecution:{envLink:"CUSTOM_LOGIC_ALLOW_CODE_EXECUTION",value:!1,type:"boolean",description:"Controls whether the execution of arbitrary code is allowed during the exporting process."},allowFileResources:{envLink:"CUSTOM_LOGIC_ALLOW_FILE_RESOURCES",value:!1,type:"boolean",description:"Controls the ability to inject resources from the filesystem. This setting has no effect when running as a server."},customCode:{value:!1,type:"string",description:"Custom code to execute before chart initialization. It can be a function, code wrapped within a function, or a filename with the .js extension."},callback:{value:!1,type:"string",description:"JavaScript code to run during construction. It can be a function or a filename with the .js extension."},resources:{value:!1,type:"string",description:"Additional resource in the form of a stringified JSON, which may contain files, js, and css sections."},loadConfig:{value:!1,type:"string",legacyName:"fromFile",description:"A file containing a pre-defined configuration to use."},createConfig:{value:!1,type:"string",description:"Enables setting options through a prompt and saving them in a provided config file."}},server:{enable:{envLink:"SERVER_ENABLE",value:!1,type:"boolean",cliName:"enableServer",description:"When set to true, the server starts on the local IP address 0.0.0.0."},host:{envLink:"SERVER_HOST",value:"0.0.0.0",type:"string",description:"The hostname of the server. Additionally, it starts a server on the provided hostname."},port:{envLink:"SERVER_PORT",value:7801,type:"number",description:"The server port when enabled."},benchmarking:{envLink:"SERVER_BENCHMARKING",value:!1,type:"boolean",cliName:"serverBenchmarking",description:"Indicates whether to display the duration, in milliseconds, of specific actions that occur on the server while serving a request."},ssl:{enable:{envLink:"SERVER_SSL_ENABLE",value:!1,type:"boolean",cliName:"enableSsl",description:"Enables or disables the SSL protocol."},force:{envLink:"SERVER_SSL_FORCE",value:!1,type:"boolean",cliName:"sslForced",legacyName:"sslOnly",description:"When set to true, the server is forced to serve only over HTTPS."},port:{envLink:"SERVER_SSL_PORT",value:443,type:"number",cliName:"sslPort",description:"The port on which to run the SSL server."},certPath:{envLink:"SERVER_SSL_CERT_PATH",value:"",type:"string",legacyName:"sslPath",description:"The path to the SSL certificate/key file."}},rateLimiting:{enable:{envLink:"SERVER_RATE_LIMITING_ENABLE",value:!1,type:"boolean",cliName:"enableRateLimiting",description:"Enables rate limiting for the server."},maxRequests:{envLink:"SERVER_RATE_LIMITING_MAX_REQUESTS",value:10,type:"number",legacyName:"rateLimit",description:"The maximum number of requests allowed in one minute."},window:{envLink:"SERVER_RATE_LIMITING_WINDOW",value:1,type:"number",description:"The time window, in minutes, for the rate limiting."},delay:{envLink:"SERVER_RATE_LIMITING_DELAY",value:0,type:"number",description:"The delay duration for each successive request before reaching the maximum limit."},trustProxy:{envLink:"SERVER_RATE_LIMITING_TRUST_PROXY",value:!1,type:"boolean",description:"Set this to true if the server is behind a load balancer."},skipKey:{envLink:"SERVER_RATE_LIMITING_SKIP_KEY",value:"",type:"string",description:"Allows bypassing the rate limiter and should be provided with the skipToken argument."},skipToken:{envLink:"SERVER_RATE_LIMITING_SKIP_TOKEN",value:"",type:"string",description:"Allows bypassing the rate limiter and should be provided with the skipKey argument."}}},pool:{minWorkers:{envLink:"POOL_MIN_WORKERS",value:4,type:"number",description:"The number of minimum and initial pool workers to spawn."},maxWorkers:{envLink:"POOL_MAX_WORKERS",value:8,type:"number",legacyName:"workers",description:"The number of maximum pool workers to spawn."},workLimit:{envLink:"POOL_WORK_LIMIT",value:40,type:"number",description:"The number of work pieces that can be performed before restarting the worker process."},acquireTimeout:{envLink:"POOL_ACQUIRE_TIMEOUT",value:5e3,type:"number",description:"The duration, in milliseconds, to wait for acquiring a resource."},createTimeout:{envLink:"POOL_CREATE_TIMEOUT",value:5e3,type:"number",description:"The duration, in milliseconds, to wait for creating a resource."},destroyTimeout:{envLink:"POOL_DESTROY_TIMEOUT",value:5e3,type:"number",description:"The duration, in milliseconds, to wait for destroying a resource."},idleTimeout:{envLink:"POOL_IDLE_TIMEOUT",value:3e4,type:"number",description:"The duration, in milliseconds, after which an idle resource is destroyed."},createRetryInterval:{envLink:"POOL_CREATE_RETRY_INTERVAL",value:200,type:"number",description:"The duration, in milliseconds, to wait before retrying the create process in case of a failure."},reaperInterval:{envLink:"POOL_REAPER_INTERVAL",value:1e3,type:"number",description:"The duration, in milliseconds, after which the check for idle resources to destroy is triggered."},benchmarking:{envLink:"POOL_BENCHMARKING",value:!1,type:"boolean",cliName:"poolBenchmarking",description:"Indicate whether to show statistics for the pool of resources or not."},listenToProcessExits:{envLink:"POOL_LISTEN_TO_PROCESS_EXITS",value:!0,type:"boolean",description:"Decides whether or not to attach process.exit handlers."}},logging:{level:{envLink:"LOGGING_LEVEL",value:4,type:"number",cliName:"logLevel",description:"The logging level to be used."},file:{envLink:"LOGGING_FILE",value:"highcharts-export-server.log",type:"string",cliName:"logFile",description:"The name of a log file. The logDest option also needs to be set to enable file logging."},dest:{envLink:"LOGGING_DEST",value:"log/",type:"string",cliName:"logDest",description:"The path to store log files. This also enables file logging."}},ui:{enable:{envLink:"UI_ENABLE",value:!1,type:"boolean",cliName:"enableUi",description:"Enables or disables the user interface (UI) for the export server."},route:{envLink:"UI_ROUTE",value:"/",type:"string",cliName:"uiRoute",description:"The endpoint route to which the user interface (UI) should be attached."}},other:{noLogo:{envLink:"OTHER_NO_LOGO",value:!1,type:"boolean",description:"Skip printing the logo on a startup. Will be replaced by a simple text."}}},S={puppeteer:[{type:"list",name:"args",message:"Puppeteer arguments",initial:T.puppeteer.args.value.join(","),separator:","}],highcharts:[{type:"text",name:"version",message:"Highcharts version",initial:T.highcharts.version.value},{type:"text",name:"cdnURL",message:"The URL of CDN",initial:T.highcharts.cdnURL.value},{type:"multiselect",name:"modules",message:"Available modules",instructions:"Space: Select specific, A: Select all, Enter: Confirm.",choices:T.highcharts.modules.value},{type:"list",name:"scripts",message:"Custom scripts",initial:T.highcharts.scripts.value.join(","),separator:","},{type:"toggle",name:"forceFetch",message:"Force re-fetch the scripts",initial:T.highcharts.forceFetch.value},{type:"text",name:"cachePath",message:"The path to the cache directory",initial:T.highcharts.cachePath.value}],export:[{type:"select",name:"type",message:"The default export file type",hint:`Default: ${T.export.type.value}`,initial:0,choices:["png","jpeg","pdf","svg"]},{type:"select",name:"constr",message:"The default constructor for Highcharts",hint:`Default: ${T.export.constr.value}`,initial:0,choices:["chart","stockChart","mapChart","ganttChart"]},{type:"number",name:"defaultHeight",message:"The default fallback height of the exported chart",initial:T.export.defaultHeight.value},{type:"number",name:"defaultWidth",message:"The default fallback width of the exported chart",initial:T.export.defaultWidth.value},{type:"number",name:"defaultScale",message:"The default fallback scale of the exported chart",initial:T.export.defaultScale.value,min:.1,max:5},{type:"number",name:"rasterizationTimeout",message:"The rendering webpage timeout in milliseconds",initial:T.export.rasterizationTimeout.value}],customLogic:[{type:"toggle",name:"allowCodeExecution",message:"Enable execution of custom code",initial:T.customLogic.allowCodeExecution.value},{type:"toggle",name:"allowFileResources",message:"Enable file resources",initial:T.customLogic.allowFileResources.value}],server:[{type:"toggle",name:"enable",message:"Starts the server on 0.0.0.0",initial:T.server.enable.value},{type:"text",name:"host",message:"Server hostname",initial:T.server.host.value},{type:"number",name:"port",message:"Server port",initial:T.server.port.value},{type:"toggle",name:"benchmarking",message:"Enable server benchmarking",initial:T.server.benchmarking.value},{type:"toggle",name:"ssl.enable",message:"Enable SSL protocol",initial:T.server.ssl.enable.value},{type:"toggle",name:"ssl.force",message:"Force serving only over HTTPS",initial:T.server.ssl.force.value},{type:"number",name:"ssl.port",message:"SSL server port",initial:T.server.ssl.port.value},{type:"text",name:"ssl.certPath",message:"The path to find the SSL certificate/key",initial:T.server.ssl.certPath.value},{type:"toggle",name:"rateLimiting.enable",message:"Enable rate limiting",initial:T.server.rateLimiting.enable.value},{type:"number",name:"rateLimiting.maxRequests",message:"The maximum requests allowed per minute",initial:T.server.rateLimiting.maxRequests.value},{type:"number",name:"rateLimiting.window",message:"The rate-limiting time window in minutes",initial:T.server.rateLimiting.window.value},{type:"number",name:"rateLimiting.delay",message:"The delay for each successive request before reaching the maximum",initial:T.server.rateLimiting.delay.value},{type:"toggle",name:"rateLimiting.trustProxy",message:"Set to true if behind a load balancer",initial:T.server.rateLimiting.trustProxy.value},{type:"text",name:"rateLimiting.skipKey",message:"Allows bypassing the rate limiter when provided with the skipToken argument",initial:T.server.rateLimiting.skipKey.value},{type:"text",name:"rateLimiting.skipToken",message:"Allows bypassing the rate limiter when provided with the skipKey argument",initial:T.server.rateLimiting.skipToken.value}],pool:[{type:"number",name:"minWorkers",message:"The initial number of workers to spawn",initial:T.pool.minWorkers.value},{type:"number",name:"maxWorkers",message:"The maximum number of workers to spawn",initial:T.pool.maxWorkers.value},{type:"number",name:"workLimit",message:"The pieces of work that can be performed before restarting a Puppeteer process",initial:T.pool.workLimit.value},{type:"number",name:"acquireTimeout",message:"The number of milliseconds to wait for acquiring a resource",initial:T.pool.acquireTimeout.value},{type:"number",name:"createTimeout",message:"The number of milliseconds to wait for creating a resource",initial:T.pool.createTimeout.value},{type:"number",name:"destroyTimeout",message:"The number of milliseconds to wait for destroying a resource",initial:T.pool.destroyTimeout.value},{type:"number",name:"idleTimeout",message:"The number of milliseconds after an idle resource is destroyed",initial:T.pool.idleTimeout.value},{type:"number",name:"createRetryInterval",message:"The retry interval in milliseconds after a create process fails",initial:T.pool.createRetryInterval.value},{type:"number",name:"reaperInterval",message:"The reaper interval in milliseconds after triggering the check for idle resources to destroy",initial:T.pool.reaperInterval.value},{type:"toggle",name:"benchmarking",message:"Enable benchmarking for a resource pool",initial:T.pool.benchmarking.value},{type:"toggle",name:"listenToProcessExits",message:"Set to false to skip attaching process.exit handlers",initial:T.pool.listenToProcessExits.value}],logging:[{type:"number",name:"level",message:"The log level (0: silent, 1: error, 2: warning, 3: notice, 4: verbose, 5: benchmark)",initial:T.logging.level.value,round:0,min:0,max:5},{type:"text",name:"file",message:"A log file name. Set with the --logDest to enable file logging",initial:T.logging.file.value},{type:"text",name:"dest",message:"The path to log files. Enables file logging",initial:T.logging.dest.value}],ui:[{type:"toggle",name:"enable",message:"Enable UI for the export server",initial:T.ui.enable.value},{type:"text",name:"route",message:"A route to attach the UI",initial:T.ui.route.value}],other:[{type:"toggle",name:"noLogo",message:"Skip printing the logo on startup. Replaced by simple text",initial:T.other.noLogo.value}]},x=["options","globalOptions","themeOptions","resources","payload"],R={},L=(e,t="")=>{Object.keys(e).forEach((r=>{if(!["puppeteer","highcharts"].includes(r)){const i=e[r];void 0===i.value?L(i,`${t}.${r}`):(R[i.cliName||r]=`${t}.${r}`.substring(1),void 0!==i.legacyName&&(R[i.legacyName]=`${t}.${r}`.substring(1)))}}))};L(T);const k=["red","yellow","blue","gray","green"];let O={toConsole:!0,toFile:!1,pathCreated:!1,levelsDesc:[{title:"error",color:k[0]},{title:"warning",color:k[1]},{title:"notice",color:k[2]},{title:"verbose",color:k[3]},{title:"benchmark",color:k[4]}],listeners:[]};for(const[e,t]of Object.entries(T.logging))O[e]=t.value;const _=(e,r)=>{O.toFile&&(O.pathCreated||(!t.existsSync(O.dest)&&t.mkdirSync(O.dest),O.pathCreated=!0),t.appendFile(`${O.dest}${O.file}`,[r].concat(e).join(" ")+"\n",(e=>{e&&(console.log(`[logger] Unable to write to log file: ${e}`),O.toFile=!1)})))},I=(...e)=>{const[t,...r]=e,{level:i,levelsDesc:o}=O;if(5!==t&&(0===t||t>i||i>o.length))return;const n=`${(new Date).toString().split("(")[0].trim()} [${o[t-1].title}] -`;O.listeners.forEach((e=>{e(n,r.join(" "))})),O.toConsole&&console.log.apply(void 0,[n.toString()[O.levelsDesc[t-1].color]].concat(r)),_(r,n)},A=(e,t,r)=>{const i=r||t.message,{level:o,levelsDesc:n}=O;if(0===e||e>o||o>n.length)return;const s=`${(new Date).toString().split("(")[0].trim()} [${n[e-1].title}] -`,a=t.message!==t.stackMessage||void 0===t.stackMessage?t.stack:t.stack.split("\n").slice(1).join("\n"),l=[i,"\n",a];O.toConsole&&console.log.apply(void 0,[s.toString()[O.levelsDesc[e-1].color]].concat([i[k[e-1]],"\n",a])),O.listeners.forEach((e=>{e(s,l.join(" "))})),_(l,s)},C=e=>{e>=0&&e<=O.levelsDesc.length&&(O.level=e)},N=(e,t)=>{if(O={...O,dest:e||O.dest,file:t||O.file,toFile:!0},0===O.dest.length)return I(1,"[logger] File logging initialization: no path supplied.");O.dest.endsWith("/")||(O.dest+="/")},P=s.fileURLToPath(new URL("../.","undefined"==typeof document?require("url").pathToFileURL(__filename).href:v&&v.src||new URL("index.cjs",document.baseURI).href)),$=(e,t)=>{const r=["png","jpeg","pdf","svg"];if(t){const i=t.split(".").pop();"jpg"===i?e="jpeg":r.includes(i)&&e!==i&&(e=i)}return{"image/png":"png","image/jpeg":"jpeg","application/pdf":"pdf","image/svg+xml":"svg"}[e]||r.find((t=>t===e))||"png"},H=(e=!1,r)=>{const i=["js","css","files"];let o=e,n=!1;if(r&&e.endsWith(".json"))try{o=j(t.readFileSync(e,"utf8"))}catch(e){return A(2,e,"[cli] No resources found.")}else o=j(e),o&&!r&&delete o.files;for(const e in o)i.includes(e)?n||(n=!0):delete o[e];return n?(o.files&&(o.files=o.files.map((e=>e.trim())),(!o.files||o.files.length<=0)&&delete o.files),o):I(3,"[cli] No resources found.")};function j(e,t){try{const r=JSON.parse("string"!=typeof e?JSON.stringify(e):e);return"string"!=typeof r&&t?JSON.stringify(r):r}catch{return!1}}const U=e=>{if(null===e||"object"!=typeof e)return e;const t=Array.isArray(e)?[]:{};for(const r in e)Object.prototype.hasOwnProperty.call(e,r)&&(t[r]=U(e[r]));return t},F=(e,t)=>JSON.stringify(e,((e,r)=>("string"==typeof r&&((r=r.trim()).startsWith("function(")||r.startsWith("function ("))&&r.endsWith("}")&&(r=t?`EXP_FUN${(r+"").replaceAll(/\n|\t|\r/g," ")}EXP_FUN`:void 0),"function"==typeof r?`EXP_FUN${(r+"").replaceAll(/\n|\t|\r/g," ")}EXP_FUN`:r))).replaceAll(/"EXP_FUN|EXP_FUN"/g,"");function G(){console.log("\nUsage of CLI arguments:".bold,"\n------",`\nFor more detailed information, visit the readme at: ${"https://github.com/highcharts/node-export-server#readme".bold.yellow}.`);const e=t=>{for(const[r,i]of Object.entries(t))if(Object.prototype.hasOwnProperty.call(i,"value")){let e=` --${i.cliName||r} ${("<"+i.type+">").green} `;if(e.length<48)for(let t=e.length;t<48;t++)e+=".";console.log(e,i.description,`[Default: ${i.value.toString().bold}]`.blue)}else e(i)};Object.keys(T).forEach((t=>{["puppeteer","highcharts"].includes(t)||(console.log(`\n${t.toUpperCase()}`.red),e(T[t]))})),console.log("\n")}const M=e=>!["false","undefined","null","NaN","0",""].includes(e)&&!!e,q=(e,r)=>{if(e&&"string"==typeof e)return(e=e.trim()).endsWith(".js")?!!r&&q(t.readFileSync(e,"utf8")):e.startsWith("function()")||e.startsWith("function ()")||e.startsWith("()=>")||e.startsWith("() =>")?`(${e})()`:e.replace(/;$/,"")},D=()=>{const e=process.hrtime.bigint();return()=>Number(process.hrtime.bigint()-e)/1e6};e.config();const W=()=>a.z.enum(["true","false"]).transform((e=>"true"===e)).optional(),V=()=>a.z.string().transform((e=>e.split(",").map((e=>e.trim())))).optional(),z=a.z.object({HIGHCHARTS_VERSION:a.z.string().refine((e=>/^(latest|\d+(\.\d+){0,2})$/.test(e)),{message:"HIGHCHARTS_VERSION must be 'latest', a major version, or in the form XX.YY.ZZ"}).optional(),HIGHCHARTS_CDN_URL:a.z.string().trim().refine((e=>e.startsWith("https://")||e.startsWith("http://")),{message:"Invalid value for HIGHCHARTS_CDN_URL. It should start with http:// or https://."}).optional(),HIGHCHARTS_CORE_SCRIPTS:V(),HIGHCHARTS_MODULES:V(),HIGHCHARTS_INDICATORS:V(),HIGHCHARTS_FORCE_FETCH:W(),HIGHCHARTS_CACHE_PATH:a.z.string().optional(),HIGHCHARTS_ADMIN_TOKEN:a.z.string().optional(),EXPORT_TYPE:a.z.enum(["jpeg","png","pdf","svg"]).optional(),EXPORT_CONSTR:a.z.string().refine((e=>["chart","stockChart","mapChart","ganttChart"].includes(e||"")),{message:"Invalid value for EXPORT_CONSTR. "}).optional(),EXPORT_DEFAULT_HEIGHT:a.z.coerce.number().positive().optional(),EXPORT_DEFAULT_WIDTH:a.z.coerce.number().positive().optional(),EXPORT_DEFAULT_SCALE:a.z.coerce.number().positive().optional(),EXPORT_RASTERIZATION_TIMEOUT:a.z.coerce.number().positive().optional(),CUSTOM_LOGIC_ALLOW_CODE_EXECUTION:W(),CUSTOM_LOGIC_ALLOW_FILEL_RESOURCES:W(),SERVER_ENABLE:W(),SERVER_HOST:a.z.string().optional(),SERVER_PORT:a.z.coerce.number().optional(),SERVER_BENCHMARKING:W(),SERVER_SSL_ENABLE:W(),SERVER_SSL_FORCE:W(),SERVER_SSL_PORT:a.z.coerce.number().optional(),SERVER_SSL_CERT_PATH:a.z.string().optional(),SERVER_RATE_LIMITING_ENABLE:W(),SERVER_RATE_LIMITING_MAX_REQUESTS:a.z.coerce.number().optional(),SERVER_RATE_LIMITING_WINDOW:a.z.coerce.number().optional(),SERVER_RATE_LIMITING_DELAY:a.z.coerce.number().optional(),SERVER_RATE_LIMITING_TRUST_PROXY:W(),SERVER_RATE_LIMITING_SKIP_KEY:a.z.string().optional(),SERVER_RATE_LIMITING_SKIP_TOKEN:a.z.string().optional(),POOL_MIN_WORKERS:a.z.coerce.number().optional(),POOL_MAX_WORKERS:a.z.coerce.number().optional(),POOL_WORK_LIMIT:a.z.coerce.number().optional(),POOL_ACQUIRE_TIMEOUT:a.z.coerce.number().optional(),POOL_CREATE_TIMEOUT:a.z.coerce.number().optional(),POOL_DESTROY_TIMEOUT:a.z.coerce.number().optional(),POOL_IDLE_TIMEOUT:a.z.coerce.number().optional(),POOL_CREATE_RETRY_INTERVAL:a.z.coerce.number().optional(),POOL_REAPER_INTERVAL:a.z.coerce.number().optional(),POOL_BENCHMARKING:W(),POOL_LISTEN_TO_PROCESS_EXITS:W(),LOGGING_LEVEL:a.z.coerce.number().optional().refine((e=>(e||5)>=0&&(e||5)<=5),{message:"Invalid value for LOGGING_LEVEL. We only accept 0, 1, 2, 3, 4, 5 as logging levels."}),LOGGING_FILE:a.z.string().optional(),LOGGING_DEST:a.z.string().optional(),UI_ENABLE:W(),UI_ROUTE:a.z.string().optional(),OTHER_NO_LOGO:W(),NODE_ENV:a.z.enum(["development","production","test"]).optional().default("production"),PROXY_SERVER_TIMEOUT:a.z.coerce.number().positive().optional().default(5e3),PROXY_SERVER_HOST:a.z.string().optional().default("localhost"),PROXY_SERVER_PORT:a.z.coerce.number().positive().optional().default(8080)}).parse(process.env);class X extends Error{constructor(e){super(),this.message=e,this.stackMessage=e}setError(e){return this.error=e,e.name&&(this.name=e.name),e.statusCode&&(this.statusCode=e.statusCode),e.stack&&(this.stackMessage=e.message,this.stack=e.stack),this}}const K={cdnURL:"https://code.highcharts.com/",activeManifest:{},sources:"",hcVersion:""};let J=!1;const B=e=>e.sources.substring(0,e.sources.indexOf("*/")).replace("/*","").replace("*/","").replace(/\n/g,"").trim(),Y=async(e,t,r,i=!1)=>{e.endsWith(".js")&&(e=e.substring(0,e.length-3)),I(4,`[cache] Fetching script - ${e}.js`);const o=t?{agent:t,timeout:z.PROXY_SERVER_TIMEOUT}:{},n=await E(`${e}.js`,o);if(200===n.statusCode&&"string"==typeof n.text){if(r){r[e.replace(/(.*)\/|(.*)modules\/|stock\/(.*)indicators\/|maps\/(.*)modules\//gi,"")]=1}return n.text}if(i)throw new X(`Could not fetch the ${e}.js. The script might not exist in the requested version (status code: ${n.statusCode}).`).setError(n);return I(2,`[cache] Could not fetch the ${e}.js. The script might not exist in the requested version.`),""},Q=async(e,r)=>{const{coreScripts:o,modules:n,indicators:s,scripts:a}=e,l="latest"!==e.version&&e.version?`${e.version}/`:"";let c;I(3,`[cache] Updating cache version to Highcharts: ${l||"latest"}.`);const p=process.env.PROXY_SERVER_HOST,u=process.env.PROXY_SERVER_PORT;if(p&&u)try{c=new i.HttpsProxyAgent({host:p,port:+u})}catch(e){throw new X("[cache] Could not create a Proxy Agent.").setError(e)}const h={};try{return K.sources=await(async(e,t,r,i,o,n)=>{const s=[...e.map((e=>Y(`${i}${e}`,o,n,!0))),...t.map((e=>Y(`${i}${e}`,o,n))),...r.map((e=>Y(`${e}`,o)))];return(await Promise.all(s)).join(";\n")})([...o.map((e=>`${l}${e}`))],[...n.map((e=>"map"===e?`maps/${l}modules/${e}`:`${l}modules/${e}`)),...s.map((e=>`stock/${l}indicators/${e}`))],a,e.cdnURL||K.cdnURL,c,h),K.hcVersion=B(K),t.writeFileSync(r,K.sources),h}catch(e){throw new X("[cache] Unable to update the local Highcharts cache.").setError(e)}},Z=async e=>{const i=r.join(P,e.cachePath);let o;const n=r.join(i,"manifest.json"),s=r.join(i,"sources.js");if(J=e,!t.existsSync(i)&&t.mkdirSync(i),!t.existsSync(n)||e.forceFetch)I(3,"[cache] Fetching and caching Highcharts dependencies."),o=await Q(e,s);else{let r=!1;const i=JSON.parse(t.readFileSync(n));if(i.modules&&Array.isArray(i.modules)){const e={};i.modules.forEach((t=>e[t]=1)),i.modules=e}const{modules:a,coreScripts:l,indicators:c}=e,p=a.length+l.length+c.length;i.version!==e.version?(I(2,"[cache] A Highcharts version mismatch in the cache, need to re-fetch."),r=!0):Object.keys(i.modules||{}).length!==p?(I(2,"[cache] The cache and the requested modules do not match, need to re-fetch."),r=!0):r=(e.modules||[]).some((e=>{if(!i.modules[e])return I(2,`[cache] The ${e} is missing in the cache, need to re-fetch.`),!0})),r?o=await Q(e,s):(I(3,"[cache] Dependency cache is up to date, proceeding."),K.sources=t.readFileSync(s,"utf8"),o=i.modules,K.hcVersion=B(K))}await(async(e,i)=>{const o={version:e.version,modules:i||{}};K.activeManifest=o,I(3,"[cache] Writing a new manifest.");try{t.writeFileSync(r.join(P,e.cachePath,"manifest.json"),JSON.stringify(o),"utf8")}catch(e){throw new X("[cache] Error writing the cache manifest.").setError(e)}})(e,o)},ee=()=>r.join(P,J.cachePath);var te=async e=>!!J&&await Z(Object.assign(J,{version:e})),re=()=>K,ie=()=>K.hcVersion;let oe={};const ne=()=>oe,se=(e,t,r=[])=>{const i=U(e);for(const[e,n]of Object.entries(t))i[e]="object"!=typeof(o=n)||Array.isArray(o)||null===o||r.includes(e)||void 0===i[e]?void 0!==n?n:i[e]:se(i[e],n,r);var o;return i};function ae(e,t={},r=""){Object.keys(e).forEach((i=>{const o=e[i],n=t&&t[i];void 0===o.value?ae(o,n,`${r}.${i}`):(void 0!==n&&(o.value=n),o.envLink in z&&(o.value=z[o.envLink]))}))}function le(e){let t={};for(const[r,i]of Object.entries(e))t[r]=Object.prototype.hasOwnProperty.call(i,"value")?i.value:le(i);return t}function ce(e,t,r){for(;t.length>1;){const i=t.shift();return Object.prototype.hasOwnProperty.call(e,i)||(e[i]={}),e[i]=ce(Object.assign({},e[i]),t,r),e}return e[t[0]]=r,e}const pe=d.randomBytes(64).toString("base64url"),ue=u.join("tmp",`puppeteer-${pe}`),he=[`--user-data-dir=${u.join(ue,"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"],de=w.fileURLToPath(new URL(".","undefined"==typeof document?require("url").pathToFileURL(__filename).href:v&&v.src||new URL("index.cjs",document.baseURI).href)),ge=t.readFileSync(de+"/../templates/template.html","utf8");let me;const fe=async e=>{await e.setContent(ge),await e.addScriptTag({path:`${ee()}/sources.js`}),await e.evaluate((()=>window.setupHighcharts())),e.on("pageerror",(async t=>{await e.$eval("#container",((e,t)=>{window._displayErrors&&(e.innerHTML=t)}),`Chart input data error ${t.toString()}`)}))},ye=async(e,t=!1)=>{try{t?(await e.goto("about:blank"),await fe(e)):await e.evaluate((()=>{document.body.innerHTML=''}))}catch(e){A(2,e,"[browser] Could not clear the content of the page.")}},ve=async()=>{if(!me)return!1;const e=await me.newPage();return await e.setCacheEnabled(!1),await fe(e),e},be=async()=>(me?.isConnected()&&(await me.close(),I(4,"[browser] Closed the browser.")),!0);const we=w.fileURLToPath(new URL(".","undefined"==typeof document?require("url").pathToFileURL(__filename).href:v&&v.src||new URL("index.cjs",document.baseURI).href)),Ee=(e,t,r)=>e.evaluate(((e,t)=>window.triggerExport(e,t)),t,r);var Te=async(e,i,o)=>{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"),[...r]=document.getElementsByTagName("link");for(const i of[...e,...t,...r])i.remove()}))};try{I(4,"[export] Determining export path.");const a=o.export;await e.evaluate((()=>requestAnimationFrame((()=>{}))));const l=a?.options?.chart?.displayErrors&&re().activeManifest.modules.debugger;let c;if(await e.evaluate((e=>window._displayErrors=e),l),i.indexOf&&(i.indexOf("=0||i.indexOf("=0)){if(I(4,"[export] Treating as SVG."),"svg"===a.type)return i;c=!0,await e.setContent((e=>`\n\n\n \n \n Highcarts Export \n \n \n \n \n ${e}\n
\n \n\n\n`)(i))}else I(4,"[export] Treating as config."),a.strInj?await Ee(e,{chart:{height:a.height,width:a.width}},o):(i.chart.height=a.height,i.chart.width=a.width,await Ee(e,i,o));const p=o.customLogic.resources;if(p){if(p.js&&n.push(await e.addScriptTag({content:p.js})),p.files)for(const r of p.files)try{const i=!r.startsWith("http");n.push(await e.addScriptTag(i?{content:t.readFileSync(r,"utf8")}:{url:r}))}catch(e){A(2,e,`[export] The JS file ${r} cannot be loaded.`)}if(p.css){let t=p.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")?n.push(await e.addStyleTag({url:i})):o.customLogic.allowFileResources&&n.push(await e.addStyleTag({path:r.join(we,i)})));n.push(await e.addStyleTag({content:p.css.replace(/@import\s*([^;]*);/g,"")||" "}))}}const u=c?await e.$eval("#chart-container svg:first-of-type",((e,t)=>({chartHeight:e.height.baseVal.value*t,chartWidth:e.width.baseVal.value*t})),parseFloat(a.scale)):await e.evaluate((()=>{const{chartHeight:e,chartWidth:t}=window.Highcharts.charts[0];return{chartHeight:e,chartWidth:t}})),h=Math.ceil(u?.chartHeight||a.height),d=Math.ceil(u?.chartWidth||a.width);await e.setViewport({height:h,width:d,deviceScaleFactor:c?1:parseFloat(a.scale)});const g=c?e=>{document.body.style.zoom=e,document.body.style.margin="0px"}:()=>{document.body.style.zoom=1};await e.evaluate(g,parseFloat(a.scale));const{height:m,width:f,x:y,y:v}=await(e=>e.$eval("#chart-container",(e=>{const{x:t,y:r,width:i,height:o}=e.getBoundingClientRect();return{x:t,y:r,width:i,height:Math.trunc(o>1?o:500)}})))(e);let b;if(c||await e.setViewport({width:Math.round(f),height:Math.round(m),deviceScaleFactor:parseFloat(a.scale)}),"svg"===a.type)b=await(e=>e.$eval("#container svg:first-of-type",(e=>e.outerHTML)))(e);else if(["png","jpeg"].includes(a.type))b=await((e,t,r,i,o)=>Promise.race([e.screenshot({type:t,encoding:r,clip:i,omitBackground:"png"==t}),new Promise(((e,t)=>setTimeout((()=>t(new X("Rasterization timeout"))),o||1500)))]))(e,a.type,"base64",{width:d,height:h,x:y,y:v},a.rasterizationTimeout);else{if("pdf"!==a.type)throw new X(`[export] Unsupported output format ${a.type}.`);b=await((e,t,r,i)=>e.pdf({height:t+1,width:r,encoding:i}))(e,h,d,"base64")}return await e.evaluate((()=>{if("undefined"!=typeof Highcharts){const e=Highcharts.charts;if(Array.isArray(e)&&e.length)for(const t of e)t&&t.destroy(),Highcharts.charts.shift()}})),await s(e),b}catch(t){return await s(e),t}};const Se={performedExports:0,exportAttempts:0,exportFromSvgAttempts:0,timeSpent:0,droppedExports:0,spentAverage:0};let xe,Re={},Le=!1;const ke={create:async()=>{let e=!1;const t=p.v4(),r=(new Date).getTime();try{if(e=await ve(),!e||e.isClosed())throw new X("The page is invalid or closed.");I(3,`[pool] Successfully created a worker ${t} - took ${(new Date).getTime()-r} ms.`)}catch(e){throw new X("Error encountered when creating a new page.").setError(e)}return{id:t,page:e,workCount:Math.round(Math.random()*(Re.workLimit/2))}},validate:async e=>Re.workLimit&&++e.workCount>Re.workLimit?(I(3,`[pool] Worker failed validation: exceeded work limit (limit is ${Re.workLimit}).`),!1):(await ye(e.page,!0),!0),destroy:e=>{I(3,`[pool] Destroying pool entry ${e.id}.`),e.page&&e.page.close()}},Oe=async e=>{if(Re=e&&e.pool?{...e.pool}:{},Re.listenToProcessExits&&(I(3,"[pool] Attaching exit listeners to the process."),process.on("exit",(async e=>{I(4,`Process exited with code ${e}.`),await _e()})),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)=>{A(1,e,`The ${t} error.`),await _e(),process.exit(1)}))),xe=e.puppeteerArgs,await(async e=>{const t=[...he,...e||[]];if(!me){let e=0;const r=async()=>{try{I(3,`[browser] Attempting to get a browser instance (try ${++e}).`),me=await h.launch({headless:"new",args:t,userDataDir:"./tmp/"})}catch(t){if(A(1,t,"[browser] Failed to launch a browser instance."),!(e<25))throw t;I(3,`[browser] Retry to open a browser (${e} out of 25).`),await new Promise((e=>setTimeout(e,4e3))),await r()}};try{await r()}catch(e){throw new X("[browser] Maximum retries to open a browser instance reached.").setError(e)}if(!me)throw new X("[browser] Cannot find a browser to open.")}return me})(xe),I(3,`[pool] Initializing pool with workers: min ${Re.minWorkers}, max ${Re.maxWorkers}.`),Le)return I(4,"[pool] Already initialized, please kill it before creating a new one.");parseInt(Re.minWorkers)>parseInt(Re.maxWorkers)&&(Re.minWorkers=Re.maxWorkers);try{Le=new c.Pool({...ke,min:parseInt(Re.minWorkers),max:parseInt(Re.maxWorkers),acquireTimeoutMillis:Re.acquireTimeout,createTimeoutMillis:Re.createTimeout,destroyTimeoutMillis:Re.destroyTimeout,idleTimeoutMillis:Re.idleTimeout,createRetryIntervalMillis:Re.createRetryInterval,reapIntervalMillis:Re.reaperInterval,propagateCreateError:!1}),Le.on("release",(async e=>{await ye(e.page,!1),I(4,`[pool] Releasing a worker with ID ${e.id}.`)})),Le.on("destroySuccess",((e,t)=>{I(4,`[pool] Destroyed a worker with ID ${t.id}.`)}));const e=[];for(let t=0;t{Le.release(e)})),I(3,"[pool] The pool is ready"+(e.length?` with ${e.length} initial resources waiting.`:"."))}catch(e){throw await be(),new X("[pool] Could not create the pool of workers.").setError(e)}};async function _e(){return I(3,"[pool] Killing all pool workers and browser, if any exist."),Le?.destroyed||Le&&(await Le.destroy(),I(4,"[browser] Destroyed the pool of resources.")),be()}const Ie=async(e,t)=>{let r;try{if(I(4,"[pool] Work received, starting to process."),++Se.exportAttempts,Re.benchmarking&&Ae(),!Le)throw new X("Work received, but pool has not been started.");try{I(4,"[pool] Acquiring a worker handle.");const e=D();r=await Le.acquire().promise,t.server.benchmarking&&I(5,t.payload?.requestId?`[benchmark] Request with ID ${t.payload?.requestId} -`:"[benchmark]",`Acquired a worker handle: ${e()}ms.`)}catch(e){throw new X("Error encountered when acquiring an available entry.").setError(e)}if(I(4,"[pool] Acquired a worker handle."),!r.page)throw new X("Resolved worker page is invalid: the pool setup is wonky.");let i=(new Date).getTime();I(4,`[pool] Starting work on pool entry with ID ${r.id}.`);const o=D(),n=await Te(r.page,e,t);if(n instanceof Error)throw"Rasterization timeout"===n.message&&(r.page.close(),r.page=await ve()),new X("Error encountered during export.").setError(n);t.server.benchmarking&&I(5,t.payload?.requestId?`[benchmark] Request with ID ${t.payload?.requestId} -`:"[benchmark]",`Exported a chart sucessfully: ${o()}ms.`),Le.release(r);const s=(new Date).getTime()-i;return Se.timeSpent+=s,Se.spentAverage=Se.timeSpent/++Se.performedExports,I(4,`[pool] Work completed in ${s} ms.`),{result:n,options:t}}catch(e){throw++Se.droppedExports,r&&Le.release(r),new X(`[pool] In pool.postWork: ${e.message}`).setError(e)}};function Ae(){const{min:e,max:t}=Le;I(5,`[pool] The minimum number of resources allowed by pool: ${e}.`),I(5,`[pool] The maximum number of resources allowed by pool: ${t}.`),I(5,`[pool] The number of resources that are currently available: ${Le.numFree()}.`),I(5,`[pool] The number of resources that are currently acquired: ${Le.numUsed()}.`),I(5,`[pool] The number of callers waiting to acquire a resource: ${Le.numPendingAcquires()}.`)}var Ce=()=>({min:Le.min,max:Le.max,available:Le.numFree(),inUse:Le.numUsed(),pendingAcquire:Le.numPendingAcquires()}),Ne=()=>Se;let Pe=!1;const $e=async(e,r)=>{I(4,"[chart] Starting the exporting process.");const i=((e,t={})=>{let r={};return e.svg?(r=U(t),r.export.type=e.type||e.export.type,r.export.scale=e.scale||e.export.scale,r.export.outfile=e.outfile||e.export.outfile,r.payload={svg:e.svg}):r=se(t,e,x),r.export.outfile=r.export?.outfile||`chart.${r.export?.type||"png"}`,r})(e,ne()),o=i.export;if(i.payload?.svg&&""!==i.payload.svg)try{I(4,"[chart] Attempting to export from a SVG input.");const e=Fe(i.payload.svg.trim(),i,r);return++Se.exportFromSvgAttempts,e}catch(e){return r(new X("[chart] Error loading SVG input.").setError(e))}if(o.infile&&o.infile.length)try{return I(4,"[chart] Attempting to export from an input file."),i.export.instr=t.readFileSync(o.infile,"utf8"),Fe(i.export.instr.trim(),i,r)}catch(e){return r(new X("[chart] Error loading input file.").setError(e))}if(o.instr&&""!==o.instr||o.options&&""!==o.options)try{return I(4,"[chart] Attempting to export from a raw input."),M(i.customLogic?.allowCodeExecution)?Ue(i,r):"string"==typeof o.instr?Fe(o.instr.trim(),i,r):je(i,o.instr||o.options,r)}catch(e){return r(new X("[chart] Error loading raw input.").setError(e))}return r(new X("[chart] No valid input specified. Check if at least one of the following parameters is correctly set: 'infile', 'instr', 'options', or 'svg'."))},He=e=>{const{chart:t,exporting:r}=e.export?.options||j(e.export?.instr),i=j(e.export?.globalOptions);let o=e.export?.scale||r?.scale||i?.exporting?.scale||e.export?.defaultScale||1;o=Math.max(.1,Math.min(o,5)),o=((e,t=1)=>{const r=Math.pow(10,t||0);return Math.round(+e*r)/r})(o,2);const n={height:e.export?.height||r?.sourceHeight||t?.height||i?.exporting?.sourceHeight||i?.chart?.height||e.export?.defaultHeight||400,width:e.export?.width||r?.sourceWidth||t?.width||i?.exporting?.sourceWidth||i?.chart?.width||e.export?.defaultWidth||600,scale:o};for(let[e,t]of Object.entries(n))n[e]="string"==typeof t?+t.replace(/px|%/gi,""):t;return n},je=async(e,r,i,o)=>{let{export:n,customLogic:s}=e;const a="boolean"==typeof s.allowCodeExecution?s.allowCodeExecution:Pe;if(s){if(a)if("string"==typeof e.customLogic.resources)e.customLogic.resources=H(e.customLogic.resources,M(e.customLogic.allowFileResources));else if(!e.customLogic.resources)try{const r=t.readFileSync("resources.json","utf8");e.customLogic.resources=H(r,M(e.customLogic.allowFileResources))}catch(e){A(2,e,"[chart] Unable to load the default resources.json file.")}}else s=e.customLogic={};if(!a&&s){if(s.callback||s.resources||s.customCode)return i(new X("[chart] The 'callback', 'resources' and 'customCode' options have been disabled for this server."));s.callback=!1,s.resources=!1,s.customCode=!1}if(r&&(r.chart=r.chart||{},r.exporting=r.exporting||{},r.exporting.enabled=!1),n.constr=n.constr||"chart",n.type=$(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(t.readFileSync(n[e],"utf8"),!0):n[e]=j(n[e],!0))}catch(t){n[e]={},A(2,t,`[chart] The '${e}' cannot be loaded.`)}})),s.allowCodeExecution)try{s.customCode=q(s.customCode,s.allowFileResources)}catch(e){A(2,e,"[chart] The 'customCode' cannot be loaded.")}if(s&&s.callback&&s.callback?.indexOf("{")<0)if(s.allowFileResources)try{s.callback=t.readFileSync(s.callback,"utf8")}catch(e){s.callback=!1,A(2,e,"[chart] The 'callback' cannot be loaded.")}else s.callback=!1;e.export={...e.export,...He(e)};try{return i(!1,await Ie(n.strInj||r||o,e))}catch(e){return i(e)}},Ue=(e,t)=>{try{let r,i=e.export.instr||e.export.options;return"string"!=typeof i&&(r=i=F(i,e.customLogic?.allowCodeExecution)),r=i.replaceAll(/\t|\n|\r/g,"").trim(),";"===r[r.length-1]&&(r=r.substring(0,r.length-1)),e.export.strInj=r,je(e,!1,t)}catch(r){return t(new X(`[chart] Malformed input detected for ${e.export?.requestId||"?"}. Please make sure that your JSON/JavaScript options are sent using the "options" attribute, and that if you're using SVG, it is unescaped.`).setError(r))}},Fe=(e,t,r)=>{const{allowCodeExecution:i}=t.customLogic;if(e.indexOf("=0||e.indexOf("=0)return I(4,"[chart] Parsing input as SVG."),je(t,!1,r,e);try{const i=JSON.parse(e.replaceAll(/\t|\n|\r/g," "));return je(t,i,r)}catch(e){return M(i)?Ue(t,r):r(new X("[chart] Only JSON configurations and SVG are allowed for this server. If this is your server, JavaScript custom code can be enabled by starting the server with the --allowCodeExecution flag.").setError(e))}},Ge=(e,t,r,i)=>{A(1,e),"development"!==z.NODE_ENV&&delete e.stack,i(e)},Me=(e,t,r,i)=>{const{statusCode:o,status:n,message:s,stack:a}=e,l=o||n||500;r.status(l).json({statusCode:l,message:s,stack:a})};var qe=(e,t)=>{const r="Too many requests, you have been rate limited. Please try again later.",i={max:t.maxRequests||30,window:t.window||1,delay:t.delay||0,trustProxy:t.trustProxy||!1,skipKey:t.skipKey||!1,skipToken:t.skipToken||!1};i.trustProxy&&e.enable("trust proxy");const o=y({windowMs:60*i.window*1e3,max:i.max,delayMs:i.delay,handler:(e,t)=>{t.format({json:()=>{t.status(429).send({message:r})},default:()=>{t.status(429).send(r)}})},skip:e=>!1!==i.skipKey&&!1!==i.skipToken&&e.query.key===i.skipKey&&e.query.access_token===i.skipToken&&(I(4,"[rate limiting] Skipping rate limiter."),!0)});e.use(o),I(3,`[rate limiting] Enabled rate limiting with ${i.max} requests per ${i.window} minute for each IP, trusting proxy: ${i.trustProxy}.`)};class De extends X{constructor(e,t){super(e),this.status=this.statusCode=t}setStatus(e){return this.status=e,this}}const We={png:"image/png",jpeg:"image/jpeg",gif:"image/gif",pdf:"application/pdf",svg:"image/svg+xml"};let Ve=0;const ze=[],Xe=[],Ke=(e,t,r,i)=>{let o=!0;const{id:n,uniqueId:s,type:a,body:l}=i;return e.some((e=>{if(e){let i=e(t,r,n,s,a,l);return void 0!==i&&!0!==i&&(o=i),!0}})),o},Je=async(e,t,r)=>{try{const r=D(),o=p.v4().replace(/-/g,""),n=ne(),s=e.body,a=++Ve;let l=$(s.type);if(!s||"object"==typeof(i=s)&&!Array.isArray(i)&&null!==i&&0===Object.keys(i).length)throw new De("The request body is required. Please ensure that your Content-Type header is correct (accepted types are application/json and multipart/form-data).",400);let c=j(s.infile||s.options||s.data);if(!c&&!s.svg)throw I(2,`The request with ID ${o} from ${e.headers["x-forwarded-for"]||e.connection.remoteAddress} was incorrect. Payload received: ${JSON.stringify(s)}.`),new De("No correct chart data found. Ensure that you are using either application/json or multipart/form-data headers. If sending JSON, make sure the chart data is in the 'infile', 'options', or 'data' attribute. If sending SVG, ensure it is in the 'svg' attribute.",400);let u=!1;if(u=Ke(ze,e,t,{id:a,uniqueId:o,type:l,body:s}),!0!==u)return t.send(u);let h=!1;e.socket.on("close",(()=>{h=!0})),I(4,`[export] Got an incoming HTTP request with ID ${o}.`),s.constr="string"==typeof s.constr&&s.constr||"chart";const d={export:{instr:c,type:l,constr:s.constr[0].toLowerCase()+s.constr.substr(1),height:s.height,width:s.width,scale:s.scale||n.export.scale,globalOptions:j(s.globalOptions,!0),themeOptions:j(s.themeOptions,!0)},customLogic:{allowCodeExecution:Pe,allowFileResources:!1,resources:j(s.resources,!0),callback:s.callback,customCode:s.customCode}};c&&(d.export.instr=F(c,d.customLogic.allowCodeExecution));const g=se(n,d);if(g.export.options=c,g.payload={svg:s.svg||!1,b64:s.b64||!1,noDownload:s.noDownload||!1,requestId:o},s.svg&&(e=>[/xlink:href="(?:http:\/\/|https:\/\/)?localhost\b/,/xlink:href="(?:http:\/\/|https:\/\/)?10\.\d{1,3}\.\d{1,3}\.\d{1,3}\b/,/xlink:href="(?:http:\/\/|https:\/\/)?127\.\d{1,3}\.\d{1,3}\.\d{1,3}\b/,/xlink:href="(?:http:\/\/|https:\/\/)?172\.(1[6-9]|2[0-9]|3[0-1])\.\d{1,3}\.\d{1,3}\b/,/xlink:href="(?:http:\/\/|https:\/\/)?192\.168\.\d{1,3}\.\d{1,3}\b/].some((t=>t.test(e))))(g.payload.svg))throw new De("SVG potentially contain at least one forbidden URL in xlink:href element. Please review the SVG content and ensure that all referenced URLs comply with security policies.",400);await $e(g,((i,c)=>{if(e.socket.removeAllListeners("close"),n.server.benchmarking&&I(5,`[benchmark] Request with ID ${o} - After the whole exporting process: ${r()}ms.`),h)return I(3,"[export] The client closed the connection before the chart finished processing.");if(i)throw i;if(!c||!c.result)throw new De(`Unexpected return from chart generation. Please check your request data. For the request with ID ${o}, the result is ${c.result}.`,400);return l=c.options.export.type,Ke(Xe,e,t,{id:a,body:c.result}),c.result?s.b64?"pdf"===l||"svg"==l?t.send(Buffer.from(c.result,"utf8").toString("base64")):t.send(c.result):(t.header("Content-Type",We[l]||"image/png"),s.noDownload||t.attachment(`${e.params.filename||e.body.filename||"chart"}.${l||"png"}`),"svg"===l?t.send(c.result):t.send(Buffer.from(c.result,"base64"))):void 0}))}catch(e){r(e)}var i};const Be=JSON.parse(t.readFileSync(r.join(P,"package.json"))),Ye=new Date,Qe=[];function Ze(e){if(!e)return!1;e.get("/health",((e,t)=>{const r=Ne(),i=Qe.length,o=Qe.reduce(((e,t)=>e+t),0)/Qe.length;I(4,"[health.js] GET /health [200] - returning server health."),t.send({status:"OK",bootTime:Ye,uptime:Math.floor(((new Date).getTime()-Ye.getTime())/1e3/60)+" minutes",version:Be.version,highchartsVersion:ie(),averageProcessingTime:r.spentAverage,performedExports:r.performedExports,failedExports:r.droppedExports,exportAttempts:r.exportAttempts,sucessRatio:r.performedExports/r.exportAttempts*100,pool:Ce(),period:i,movingAverage:o,message:`Last ${i} minutes had a success rate of ${o.toFixed(2)}%.`,svgExportAttempts:r.exportFromSvgAttempts,jsonExportAttempts:r.performedExports-r.exportFromSvgAttempts})}))}setInterval((function(){const e=Ne(),t=0===e.exportAttempts?1:e.performedExports/e.exportAttempts*100;Qe.push(t),Qe.length>30&&Qe.shift()}),6e4);const et=m();et.disable("x-powered-by"),et.use(g());const tt=f.memoryStorage(),rt=f({storage:tt,limits:{fieldSize:52428800}});et.use(m.json({limit:52428800})),et.use(m.urlencoded({extended:!0,limit:52428800})),et.use(rt.none());const it=e=>{e.on("clientError",(e=>{A(1,e,`[server] Client error: ${e.message}`)})),e.on("error",(e=>{A(1,e,`[server] Server error: ${e.message}`)})),e.on("connection",(e=>{e.on("error",(e=>{A(1,e,`[server] Socket error: ${e.message}`)}))}))},ot=async e=>{try{if(!e.enable)return!1;if(!e.ssl.force){const t=o.createServer(et);it(t),t.listen(e.port,e.host),I(3,`[server] Started HTTP server on ${e.host}:${e.port}.`)}if(e.ssl.enable){let i,o;try{i=await t.promises.readFile(r.posix.join(e.ssl.certPath,"server.key"),"utf8"),o=await t.promises.readFile(r.posix.join(e.ssl.certPath,"server.crt"),"utf8")}catch(t){I(2,`[server] Unable to load key/certificate from the '${e.ssl.certPath}' path. Could not run secured layer server.`)}if(i&&o){const t=n.createServer({key:i,cert:o},et);it(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)&&qe(et,e.rateLimiting),et.use(m.static(r.posix.join(P,"public"))),Ze(et),(e=>{e.post("/",Je),e.post("/:filename",Je)})(et),(e=>{!!e&&e.get("/",((e,t)=>{t.sendFile(r.join(P,"public","index.html"))}))})(et),(e=>{!!e&&e.post("/version/change/:newVersion",(async(e,t,r)=>{try{const r=z.HIGHCHARTS_ADMIN_TOKEN;if(!r||!r.length)throw new De("The server is not configured to perform run-time version changes: HIGHCHARTS_ADMIN_TOKEN is not set.",401);const i=e.get("hc-auth");if(!i||i!==r)throw new De("Invalid or missing token: Set the token in the hc-auth header.",401);const o=e.params.newVersion;if(!o)throw new De("No new version supplied.",400);try{await te(o)}catch(e){throw new De(`Version change: ${e.message}`,e.statusCode).setError(e)}t.status(200).send({statusCode:200,version:ie(),message:`Successfully updated Highcharts to version: ${o}.`})}catch(e){r(e)}}))})(et),(e=>{e.use(Ge),e.use(Me)})(et)}catch(e){throw new X("[server] Could not configure and start the server.").setError(e)}};var nt={startServer:ot,enableRateLimiting:e=>qe(et,e),getExpress:()=>m,getApp:()=>et,use:(e,...t)=>{et.use(e,...t)},get:(e,...t)=>{et.get(e,...t)},post:(e,...t)=>{et.post(e,...t)}};e.config();var st={server:nt,startServer:ot,setOptions:(e,r)=>(r?.length&&(oe=function(e){const r=e.findIndex((e=>"loadConfig"===e.replace(/-/g,"")));if(r>-1&&e[r+1]){const i=e[r+1];try{if(i&&i.endsWith(".json"))return JSON.parse(t.readFileSync(i))}catch(e){A(2,e,`[config] Unable to load the configuration from the ${i} file.`)}}return{}}(r)),ae(T,oe),oe=le(T),e&&(oe=se(oe,e,x)),r?.length&&(oe=function(e,t,r){let i=!1;for(let o=0;o(s.length-1===r&&(a=e[t].type),e[t])),r),s.reduce(((e,r,l)=>(s.length-1===l&&void 0!==e[r]&&(t[++o]?"boolean"===a?e[r]=M(t[o]):"number"===a?e[r]=+t[o]:a.indexOf("]")>=0?e[r]=t[o].split(","):e[r]=t[o]:(I(2,`[config] Missing value for the '${n}' argument. Using the default value.`),i=!0)),e[r])),e)}i&&G();return e}(oe,r,T)),oe),initExport:async e=>{var t;return t=e.customLogic&&e.customLogic.allowCodeExecution,Pe=M(t),(e=>{C(e&&parseInt(e.level)),e&&e.dest&&N(e.dest,e.file||"highcharts-export-server.log")})(e.logging),await Z(e.highcharts||{version:"latest"}),await Oe({pool:e.pool||{minWorkers:1,maxWorkers:1},puppeteerArgs:e.puppeteer?.args||[]}),e},singleExport:async e=>{e.export.instr=e.export.instr||e.export.options,await $e(e,(async(e,r)=>{if(e)throw e;const{outfile:i,type:o}=r.options.export;t.writeFileSync(i||`chart.${o}`,"svg"!==o?Buffer.from(r.result,"base64"):r.result),await _e()}))},batchExport:async e=>{const r=[];for(let i of e.export.batch.split(";"))i=i.split("="),2===i.length&&r.push($e({...e,export:{...e.export,infile:i[0],outfile:i[1]}},((e,r)=>{if(e)throw e;t.writeFileSync(r.options.export.outfile,Buffer.from(r.result,"base64"))})));try{await Promise.all(r),await _e()}catch(e){throw new X("[chart] Error encountered during batch export.").setError(e)}},startExport:$e,killPool:_e,log:I,logWithStack:A,setLogLevel:C,enableFileLogging:N,mapToNewConfig:e=>{const t={};for(const[r,i]of Object.entries(e)){const e=R[r]?R[r].split("."):[];e.reduce(((t,r,o)=>t[r]=e.length-1===o?i:t[r]||{}),t)}return t},manualConfig:async e=>{let r={};t.existsSync(e)&&(r=JSON.parse(t.readFileSync(e,"utf8")));const i=Object.keys(S).map((e=>({title:`${e} options`,value:e})));return l({type:"multiselect",name:"category",message:"Which category do you want to configure?",hint:"Space: Select specific, A: Select all, Enter: Confirm.",instructions:"",choices:i},{onSubmit:async(i,o)=>{let n=0,s=[];for(const e of o)S[e]=S[e].map((t=>({...t,section:e}))),s=[...s,...S[e]];return await l(s,{onSubmit:async(i,o)=>{if("modules"===i.name?(o=o.length?o.map((e=>i.choices[e])):i.choices,r[i.section][i.name]=o):r[i.section]=ce(Object.assign({},r[i.section]||{}),i.name.split("."),i.choices?i.choices[o]:o),++n===s.length){try{await t.promises.writeFile(e,JSON.stringify(r,null,2),"utf8")}catch(t){A(1,t,`[config] An error occurred while creating the ${e} file.`)}return!0}}}),!0}})},printLogo:e=>{const i=JSON.parse(t.readFileSync(r.join(P,"package.json"))).version;e?console.log(`Starting Highcharts Export Server v${i}...`):console.log(t.readFileSync(P+"/msg/startup.msg").toString().bold.yellow,`v${i}`)},printUsage:G};module.exports=st;
+//# sourceMappingURL=data:application/json;charset=utf-8;base64,eyJ2ZXJzaW9uIjozLCJmaWxlIjoiaW5kZXguY2pzIiwic291cmNlcyI6WyIuLi9saWIvZmV0Y2guanMiLCIuLi9saWIvc2NoZW1hcy9jb25maWcuanMiLCIuLi9saWIvbG9nZ2VyLmpzIiwiLi4vbGliL3V0aWxzLmpzIiwiLi4vbGliL2VudnMuanMiLCIuLi9saWIvZXJyb3JzL0V4cG9ydEVycm9yLmpzIiwiLi4vbGliL2NhY2hlLmpzIiwiLi4vbGliL2NvbmZpZy5qcyIsIi4uL2xpYi9icm93c2VyLmpzIiwiLi4vbGliL2V4cG9ydC5qcyIsIi4uL3RlbXBsYXRlcy9zdmdfZXhwb3J0L3N2Z19leHBvcnQuanMiLCIuLi9saWIvcG9vbC5qcyIsIi4uL2xpYi9jaGFydC5qcyIsIi4uL2xpYi9zZXJ2ZXIvZXJyb3IuanMiLCIuLi9saWIvc2VydmVyL3JhdGVfbGltaXQuanMiLCIuLi9saWIvZXJyb3JzL0h0dHBFcnJvci5qcyIsIi4uL2xpYi9zZXJ2ZXIvcm91dGVzL2V4cG9ydC5qcyIsIi4uL2xpYi9zZXJ2ZXIvcm91dGVzL2hlYWx0aC5qcyIsIi4uL2xpYi9zZXJ2ZXIvc2VydmVyLmpzIiwiLi4vbGliL3NlcnZlci9yb3V0ZXMvdWkuanMiLCIuLi9saWIvc2VydmVyL3JvdXRlcy9jaGFuZ2VfaGNfdmVyc2lvbi5qcyIsIi4uL2xpYi9pbmRleC5qcyJdLCJzb3VyY2VzQ29udGVudCI6WyIvKipcclxuICogVGhpcyBtb2R1bGUgZXhwb3J0cyB0d28gZnVuY3Rpb25zOiBmZXRjaCAoZm9yIEdFVCByZXF1ZXN0cykgYW5kIHBvc3QgKGZvciBQT1NUIHJlcXVlc3RzKS5cclxuICovXHJcblxyXG5pbXBvcnQgaHR0cCBmcm9tICdodHRwJztcclxuaW1wb3J0IGh0dHBzIGZyb20gJ2h0dHBzJztcclxuXHJcbi8qKlxyXG4gKiBSZXR1cm5zIHRoZSBIVFRQIG9yIEhUVFBTIHByb3RvY29sIG1vZHVsZSBiYXNlZCBvbiB0aGUgcHJvdmlkZWQgVVJMLlxyXG4gKlxyXG4gKiBAcGFyYW0ge3N0cmluZ30gdXJsIC0gVGhlIFVSTCB0byBkZXRlcm1pbmUgdGhlIHByb3RvY29sLlxyXG4gKlxyXG4gKiBAcmV0dXJucyB7T2JqZWN0fSBUaGUgSFRUUCBvciBIVFRQUyBwcm90b2NvbCBtb2R1bGUgKGh0dHAgb3IgaHR0cHMpLlxyXG4gKi9cclxuY29uc3QgZ2V0UHJvdG9jb2wgPSAodXJsKSA9PiAodXJsLnN0YXJ0c1dpdGgoJ2h0dHBzJykgPyBodHRwcyA6IGh0dHApO1xyXG5cclxuLyoqXHJcbiAqIEZldGNoZXMgZGF0YSBmcm9tIHRoZSBzcGVjaWZpZWQgVVJMIHVzaW5nIGVpdGhlciBIVFRQIG9yIEhUVFBTIHByb3RvY29sLlxyXG4gKlxyXG4gKiBAcGFyYW0ge3N0cmluZ30gdXJsIC0gVGhlIFVSTCB0byBmZXRjaCBkYXRhIGZyb20uXHJcbiAqIEBwYXJhbSB7T2JqZWN0fSByZXF1ZXN0T3B0aW9ucyAtIE9wdGlvbnMgZm9yIHRoZSBIVFRQIHJlcXVlc3QgKG9wdGlvbmFsKS5cclxuICpcclxuICogQHJldHVybnMge1Byb21pc2U8T2JqZWN0Pn0gUHJvbWlzZSByZXNvbHZpbmcgdG8gdGhlIEhUVFAgcmVzcG9uc2Ugb2JqZWN0XHJcbiAqIHdpdGggYWRkZWQgJ3RleHQnIHByb3BlcnR5IG9yIHJlamVjdGluZyB3aXRoIGFuIGVycm9yLlxyXG4gKi9cclxuYXN5bmMgZnVuY3Rpb24gZmV0Y2godXJsLCByZXF1ZXN0T3B0aW9ucyA9IHt9KSB7XHJcbiAgcmV0dXJuIG5ldyBQcm9taXNlKChyZXNvbHZlLCByZWplY3QpID0+IHtcclxuICAgIGNvbnN0IHByb3RvY29sID0gZ2V0UHJvdG9jb2wodXJsKTtcclxuXHJcbiAgICBwcm90b2NvbFxyXG4gICAgICAuZ2V0KHVybCwgcmVxdWVzdE9wdGlvbnMsIChyZXMpID0+IHtcclxuICAgICAgICBsZXQgZGF0YSA9ICcnO1xyXG5cclxuICAgICAgICAvLyBBIGNodW5rIG9mIGRhdGEgaGFzIGJlZW4gcmVjZWl2ZWQuXHJcbiAgICAgICAgcmVzLm9uKCdkYXRhJywgKGNodW5rKSA9PiB7XHJcbiAgICAgICAgICBkYXRhICs9IGNodW5rO1xyXG4gICAgICAgIH0pO1xyXG5cclxuICAgICAgICAvLyBUaGUgd2hvbGUgcmVzcG9uc2UgaGFzIGJlZW4gcmVjZWl2ZWQuXHJcbiAgICAgICAgcmVzLm9uKCdlbmQnLCAoKSA9PiB7XHJcbiAgICAgICAgICBpZiAoIWRhdGEpIHtcclxuICAgICAgICAgICAgcmVqZWN0KCdOb3RoaW5nIHdhcyBmZXRjaGVkIGZyb20gdGhlIFVSTC4nKTtcclxuICAgICAgICAgIH1cclxuXHJcbiAgICAgICAgICByZXMudGV4dCA9IGRhdGE7XHJcbiAgICAgICAgICByZXNvbHZlKHJlcyk7XHJcbiAgICAgICAgfSk7XHJcbiAgICAgIH0pXHJcbiAgICAgIC5vbignZXJyb3InLCAoZXJyb3IpID0+IHtcclxuICAgICAgICByZWplY3QoZXJyb3IpO1xyXG4gICAgICB9KTtcclxuICB9KTtcclxufVxyXG5cclxuLyoqXHJcbiAqIFNlbmRzIGEgUE9TVCByZXF1ZXN0IHRvIHRoZSBzcGVjaWZpZWQgVVJMIHdpdGggdGhlIHByb3ZpZGVkIEpTT04gYm9keSB1c2luZ1xyXG4gKiBlaXRoZXIgSFRUUCBvciBIVFRQUyBwcm90b2NvbC5cclxuICpcclxuICogQHBhcmFtIHtzdHJpbmd9IHVybCAtIFRoZSBVUkwgdG8gc2VuZCB0aGUgUE9TVCByZXF1ZXN0IHRvLlxyXG4gKiBAcGFyYW0ge09iamVjdH0gYm9keSAtIFRoZSBKU09OIGJvZHkgdG8gaW5jbHVkZSBpbiB0aGUgUE9TVCByZXF1ZXN0XHJcbiAqIChvcHRpb25hbCwgZGVmYXVsdCBpcyBhbiBlbXB0eSBvYmplY3QpLlxyXG4gKiBAcGFyYW0ge09iamVjdH0gcmVxdWVzdE9wdGlvbnMgLSBPcHRpb25zIGZvciB0aGUgSFRUUCByZXF1ZXN0IChvcHRpb25hbCkuXHJcbiAqXHJcbiAqIEByZXR1cm5zIHtQcm9taXNlPE9iamVjdD59IFByb21pc2UgcmVzb2x2aW5nIHRvIHRoZSBIVFRQIHJlc3BvbnNlIG9iamVjdCB3aXRoXHJcbiAqIGFkZGVkICd0ZXh0JyBwcm9wZXJ0eSBvciByZWplY3Rpbmcgd2l0aCBhbiBlcnJvci5cclxuICovXHJcbmFzeW5jIGZ1bmN0aW9uIHBvc3QodXJsLCBib2R5ID0ge30sIHJlcXVlc3RPcHRpb25zID0ge30pIHtcclxuICByZXR1cm4gbmV3IFByb21pc2UoKHJlc29sdmUsIHJlamVjdCkgPT4ge1xyXG4gICAgY29uc3QgcHJvdG9jb2wgPSBnZXRQcm90b2NvbCh1cmwpO1xyXG4gICAgY29uc3QgZGF0YSA9IEpTT04uc3RyaW5naWZ5KGJvZHkpO1xyXG5cclxuICAgIC8vIFNldCBkZWZhdWx0IGhlYWRlcnMgYW5kIG1lcmdlIHdpdGggcmVxdWVzdE9wdGlvbnNcclxuICAgIGNvbnN0IG9wdGlvbnMgPSBPYmplY3QuYXNzaWduKFxyXG4gICAgICB7XHJcbiAgICAgICAgbWV0aG9kOiAnUE9TVCcsXHJcbiAgICAgICAgaGVhZGVyczoge1xyXG4gICAgICAgICAgJ0NvbnRlbnQtVHlwZSc6ICdhcHBsaWNhdGlvbi9qc29uJyxcclxuICAgICAgICAgICdDb250ZW50LUxlbmd0aCc6IGRhdGEubGVuZ3RoXHJcbiAgICAgICAgfVxyXG4gICAgICB9LFxyXG4gICAgICByZXF1ZXN0T3B0aW9uc1xyXG4gICAgKTtcclxuXHJcbiAgICBjb25zdCByZXEgPSBwcm90b2NvbFxyXG4gICAgICAucmVxdWVzdCh1cmwsIG9wdGlvbnMsIChyZXMpID0+IHtcclxuICAgICAgICBsZXQgcmVzcG9uc2VEYXRhID0gJyc7XHJcblxyXG4gICAgICAgIC8vIEEgY2h1bmsgb2YgZGF0YSBoYXMgYmVlbiByZWNlaXZlZC5cclxuICAgICAgICByZXMub24oJ2RhdGEnLCAoY2h1bmspID0+IHtcclxuICAgICAgICAgIHJlc3BvbnNlRGF0YSArPSBjaHVuaztcclxuICAgICAgICB9KTtcclxuXHJcbiAgICAgICAgLy8gVGhlIHdob2xlIHJlc3BvbnNlIGhhcyBiZWVuIHJlY2VpdmVkLlxyXG4gICAgICAgIHJlcy5vbignZW5kJywgKCkgPT4ge1xyXG4gICAgICAgICAgdHJ5IHtcclxuICAgICAgICAgICAgcmVzLnRleHQgPSByZXNwb25zZURhdGE7XHJcbiAgICAgICAgICAgIHJlc29sdmUocmVzKTtcclxuICAgICAgICAgIH0gY2F0Y2ggKGVycm9yKSB7XHJcbiAgICAgICAgICAgIHJlamVjdChlcnJvcik7XHJcbiAgICAgICAgICB9XHJcbiAgICAgICAgfSk7XHJcbiAgICAgIH0pXHJcbiAgICAgIC5vbignZXJyb3InLCAoZXJyb3IpID0+IHtcclxuICAgICAgICByZWplY3QoZXJyb3IpO1xyXG4gICAgICB9KTtcclxuXHJcbiAgICAvLyBXcml0ZSB0aGUgcmVxdWVzdCBib2R5IGFuZCBlbmQgdGhlIHJlcXVlc3QuXHJcbiAgICByZXEud3JpdGUoZGF0YSk7XHJcbiAgICByZXEuZW5kKCk7XHJcbiAgfSk7XHJcbn1cclxuXHJcbmV4cG9ydCBkZWZhdWx0IGZldGNoO1xyXG5leHBvcnQgeyBmZXRjaCwgcG9zdCB9O1xyXG4iLCIvKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKlxyXG5cclxuSGlnaGNoYXJ0cyBFeHBvcnQgU2VydmVyXHJcblxyXG5Db3B5cmlnaHQgKGMpIDIwMTYtMjAyNCwgSGlnaHNvZnRcclxuXHJcbkxpY2VuY2VkIHVuZGVyIHRoZSBNSVQgbGljZW5jZS5cclxuXHJcbkFkZGl0aW9uYWxseSBhIHZhbGlkIEhpZ2hjaGFydHMgbGljZW5zZSBpcyByZXF1aXJlZCBmb3IgdXNlLlxyXG5cclxuU2VlIExJQ0VOU0UgZmlsZSBpbiByb290IGZvciBkZXRhaWxzLlxyXG5cclxuKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKi9cclxuXHJcbi8vIFRoaXMgaXMgdGhlIGNvbmZpZ3VyYXRpb24gb2JqZWN0IHdpdGggYWxsIG9wdGlvbnMgYW5kIHRoZWlyIGRlZmF1bHQgdmFsdWVzLFxyXG4vLyBhbHNvIGZyb20gdGhlIC5lbnYgZmlsZSBpZiBvbmUgZXhpc3RzXHJcbmV4cG9ydCBjb25zdCBkZWZhdWx0Q29uZmlnID0ge1xyXG4gIHB1cHBldGVlcjoge1xyXG4gICAgYXJnczoge1xyXG4gICAgICB2YWx1ZTogW10sXHJcbiAgICAgIHR5cGU6ICdzdHJpbmdbXScsXHJcbiAgICAgIGRlc2NyaXB0aW9uOiAnQXJndW1lbnRzIGFycmF5IHRvIHNlbmQgdG8gUHVwcGV0ZWVyLidcclxuICAgIH1cclxuICB9LFxyXG4gIGhpZ2hjaGFydHM6IHtcclxuICAgIHZlcnNpb246IHtcclxuICAgICAgdmFsdWU6ICdsYXRlc3QnLFxyXG4gICAgICBlbnZMaW5rOiAnSElHSENIQVJUU19WRVJTSU9OJyxcclxuICAgICAgdHlwZTogJ3N0cmluZycsXHJcbiAgICAgIGRlc2NyaXB0aW9uOiAnVGhlIEhpZ2hjaGFydHMgdmVyc2lvbiB0byBiZSB1c2VkLidcclxuICAgIH0sXHJcbiAgICBjZG5VUkw6IHtcclxuICAgICAgdmFsdWU6ICdodHRwczovL2NvZGUuaGlnaGNoYXJ0cy5jb20vJyxcclxuICAgICAgZW52TGluazogJ0hJR0hDSEFSVFNfQ0ROX1VSTCcsXHJcbiAgICAgIHR5cGU6ICdzdHJpbmcnLFxyXG4gICAgICBkZXNjcmlwdGlvbjogJ1RoZSBDRE4gVVJMIGZvciBIaWdoY2hhcnRzIHNjcmlwdHMgdG8gYmUgdXNlZC4nXHJcbiAgICB9LFxyXG4gICAgY29yZVNjcmlwdHM6IHtcclxuICAgICAgZW52TGluazogJ0hJR0hDSEFSVFNfQ09SRV9TQ1JJUFRTJyxcclxuICAgICAgdmFsdWU6IFsnaGlnaGNoYXJ0cycsICdoaWdoY2hhcnRzLW1vcmUnLCAnaGlnaGNoYXJ0cy0zZCddLFxyXG4gICAgICB0eXBlOiAnc3RyaW5nW10nLFxyXG4gICAgICBkZXNjcmlwdGlvbjogJ1RoZSBjb3JlIEhpZ2hjaGFydHMgc2NyaXB0cyB0byBmZXRjaC4nXHJcbiAgICB9LFxyXG4gICAgbW9kdWxlczoge1xyXG4gICAgICBlbnZMaW5rOiAnSElHSENIQVJUU19NT0RVTEVTJyxcclxuICAgICAgdmFsdWU6IFtcclxuICAgICAgICAnc3RvY2snLFxyXG4gICAgICAgICdtYXAnLFxyXG4gICAgICAgICdnYW50dCcsXHJcbiAgICAgICAgJ2V4cG9ydGluZycsXHJcbiAgICAgICAgJ2V4cG9ydC1kYXRhJyxcclxuICAgICAgICAncGFyYWxsZWwtY29vcmRpbmF0ZXMnLFxyXG4gICAgICAgICdhY2Nlc3NpYmlsaXR5JyxcclxuICAgICAgICAnYW5ub3RhdGlvbnMtYWR2YW5jZWQnLFxyXG4gICAgICAgICdib29zdC1jYW52YXMnLFxyXG4gICAgICAgICdib29zdCcsXHJcbiAgICAgICAgJ2RhdGEnLFxyXG4gICAgICAgICdkYXRhLXRvb2xzJyxcclxuICAgICAgICAnZHJhZ2dhYmxlLXBvaW50cycsXHJcbiAgICAgICAgJ3N0YXRpYy1zY2FsZScsXHJcbiAgICAgICAgJ2Jyb2tlbi1heGlzJyxcclxuICAgICAgICAnaGVhdG1hcCcsXHJcbiAgICAgICAgJ3RpbGVtYXAnLFxyXG4gICAgICAgICd0aWxlZHdlYm1hcCcsXHJcbiAgICAgICAgJ3RpbWVsaW5lJyxcclxuICAgICAgICAndHJlZW1hcCcsXHJcbiAgICAgICAgJ3RyZWVncmFwaCcsXHJcbiAgICAgICAgJ2l0ZW0tc2VyaWVzJyxcclxuICAgICAgICAnZHJpbGxkb3duJyxcclxuICAgICAgICAnaGlzdG9ncmFtLWJlbGxjdXJ2ZScsXHJcbiAgICAgICAgJ2J1bGxldCcsXHJcbiAgICAgICAgJ2Z1bm5lbCcsXHJcbiAgICAgICAgJ2Z1bm5lbDNkJyxcclxuICAgICAgICAnZ2VvaGVhdG1hcCcsXHJcbiAgICAgICAgJ3B5cmFtaWQzZCcsXHJcbiAgICAgICAgJ25ldHdvcmtncmFwaCcsXHJcbiAgICAgICAgJ292ZXJsYXBwaW5nLWRhdGFsYWJlbHMnLFxyXG4gICAgICAgICdwYXJldG8nLFxyXG4gICAgICAgICdwYXR0ZXJuLWZpbGwnLFxyXG4gICAgICAgICdwaWN0b3JpYWwnLFxyXG4gICAgICAgICdwcmljZS1pbmRpY2F0b3InLFxyXG4gICAgICAgICdzYW5rZXknLFxyXG4gICAgICAgICdhcmMtZGlhZ3JhbScsXHJcbiAgICAgICAgJ2RlcGVuZGVuY3ktd2hlZWwnLFxyXG4gICAgICAgICdzZXJpZXMtbGFiZWwnLFxyXG4gICAgICAgICdzb2xpZC1nYXVnZScsXHJcbiAgICAgICAgJ3NvbmlmaWNhdGlvbicsXHJcbiAgICAgICAgJ3N0b2NrLXRvb2xzJyxcclxuICAgICAgICAnc3RyZWFtZ3JhcGgnLFxyXG4gICAgICAgICdzdW5idXJzdCcsXHJcbiAgICAgICAgJ3ZhcmlhYmxlLXBpZScsXHJcbiAgICAgICAgJ3Zhcml3aWRlJyxcclxuICAgICAgICAndmVjdG9yJyxcclxuICAgICAgICAndmVubicsXHJcbiAgICAgICAgJ3dpbmRiYXJiJyxcclxuICAgICAgICAnd29yZGNsb3VkJyxcclxuICAgICAgICAneHJhbmdlJyxcclxuICAgICAgICAnbm8tZGF0YS10by1kaXNwbGF5JyxcclxuICAgICAgICAnZHJhZy1wYW5lcycsXHJcbiAgICAgICAgJ2RlYnVnZ2VyJyxcclxuICAgICAgICAnZHVtYmJlbGwnLFxyXG4gICAgICAgICdsb2xsaXBvcCcsXHJcbiAgICAgICAgJ2N5bGluZGVyJyxcclxuICAgICAgICAnb3JnYW5pemF0aW9uJyxcclxuICAgICAgICAnZG90cGxvdCcsXHJcbiAgICAgICAgJ21hcmtlci1jbHVzdGVycycsXHJcbiAgICAgICAgJ2hvbGxvd2NhbmRsZXN0aWNrJyxcclxuICAgICAgICAnaGVpa2luYXNoaScsXHJcbiAgICAgICAgJ2Zsb3dtYXAnXHJcbiAgICAgIF0sXHJcbiAgICAgIHR5cGU6ICdzdHJpbmdbXScsXHJcbiAgICAgIGRlc2NyaXB0aW9uOiAnVGhlIG1vZHVsZXMgb2YgSGlnaGNoYXJ0cyB0byBmZXRjaC4nXHJcbiAgICB9LFxyXG4gICAgaW5kaWNhdG9yczoge1xyXG4gICAgICBlbnZMaW5rOiAnSElHSENIQVJUU19JTkRJQ0FUT1JTJyxcclxuICAgICAgdmFsdWU6IFsnaW5kaWNhdG9ycy1hbGwnXSxcclxuICAgICAgdHlwZTogJ3N0cmluZ1tdJyxcclxuICAgICAgZGVzY3JpcHRpb246ICdUaGUgaW5kaWNhdG9ycyBvZiBIaWdoY2hhcnRzIHRvIGZldGNoLidcclxuICAgIH0sXHJcbiAgICBzY3JpcHRzOiB7XHJcbiAgICAgIHZhbHVlOiBbXHJcbiAgICAgICAgJ2h0dHBzOi8vY2RuanMuY2xvdWRmbGFyZS5jb20vYWpheC9saWJzL21vbWVudC5qcy8yLjI5LjQvbW9tZW50Lm1pbi5qcycsXHJcbiAgICAgICAgJ2h0dHBzOi8vY2RuanMuY2xvdWRmbGFyZS5jb20vYWpheC9saWJzL21vbWVudC10aW1lem9uZS8wLjUuMzQvbW9tZW50LXRpbWV6b25lLXdpdGgtZGF0YS5taW4uanMnXHJcbiAgICAgIF0sXHJcbiAgICAgIHR5cGU6ICdzdHJpbmdbXScsXHJcbiAgICAgIGRlc2NyaXB0aW9uOiAnQWRkaXRpb25hbCBvcHRpb25hbCBzY3JpcHRzIG9yIGRlcGVuZGVuY2llcyB0byBmZXRjaC4nXHJcbiAgICB9LFxyXG4gICAgZm9yY2VGZXRjaDoge1xyXG4gICAgICBlbnZMaW5rOiAnSElHSENIQVJUU19GT1JDRV9GRVRDSCcsXHJcbiAgICAgIHZhbHVlOiBmYWxzZSxcclxuICAgICAgdHlwZTogJ2Jvb2xlYW4nLFxyXG4gICAgICBkZXNjcmlwdGlvbjpcclxuICAgICAgICAnVGhlIGZsYWcgdG8gZGV0ZXJtaW5lIHdoZXRoZXIgdG8gcmVmZXRjaCBhbGwgc2NyaXB0cyBhZnRlciBlYWNoIHNlcnZlciByZXJ1bi4nXHJcbiAgICB9LFxyXG4gICAgY2FjaGVQYXRoOiB7XHJcbiAgICAgIGVudkxpbms6ICdISUdIQ0hBUlRTX0NBQ0hFX1BBVEgnLFxyXG4gICAgICB2YWx1ZTogJy5jYWNoZScsXHJcbiAgICAgIHR5cGU6ICdzdHJpbmcnLFxyXG4gICAgICBkZXNjcmlwdGlvbjpcclxuICAgICAgICAnVGhlIHBhdGggdG8gdGhlIGNhY2hlIGRpcmVjdG9yeS4gSXQgaXMgdXNlZCB0byBzdG9yZSB0aGUgSGlnaGNoYXJ0cyBzY3JpcHRzIGFuZCBjdXN0b20gc2NyaXB0cy4nXHJcbiAgICB9XHJcbiAgfSxcclxuICBleHBvcnQ6IHtcclxuICAgIGluZmlsZToge1xyXG4gICAgICB2YWx1ZTogZmFsc2UsXHJcbiAgICAgIHR5cGU6ICdzdHJpbmcnLFxyXG4gICAgICBkZXNjcmlwdGlvbjpcclxuICAgICAgICAnVGhlIGlucHV0IGZpbGUgc2hvdWxkIGluY2x1ZGUgYSBuYW1lIGFuZCBhIHR5cGUgKGpzb24gb3Igc3ZnKS4gSXQgbXVzdCBiZSBjb3JyZWN0bHkgZm9ybWF0dGVkIGFzIGEgSlNPTiBvciBTVkcgZmlsZS4nXHJcbiAgICB9LFxyXG4gICAgaW5zdHI6IHtcclxuICAgICAgdmFsdWU6IGZhbHNlLFxyXG4gICAgICB0eXBlOiAnc3RyaW5nJyxcclxuICAgICAgZGVzY3JpcHRpb246XHJcbiAgICAgICAgJ0lucHV0LCBwcm92aWRlZCBpbiB0aGUgZm9ybSBvZiBhIHN0cmluZ2lmaWVkIEpTT04gb3IgU1ZHIGZpbGUsIHdpbGwgb3ZlcnJpZGUgdGhlIC0taW5maWxlIG9wdGlvbi4nXHJcbiAgICB9LFxyXG4gICAgb3B0aW9uczoge1xyXG4gICAgICB2YWx1ZTogZmFsc2UsXHJcbiAgICAgIHR5cGU6ICdzdHJpbmcnLFxyXG4gICAgICBkZXNjcmlwdGlvbjogJ0FuIGFsaWFzIGZvciB0aGUgLS1pbnN0ciBvcHRpb24uJ1xyXG4gICAgfSxcclxuICAgIG91dGZpbGU6IHtcclxuICAgICAgdmFsdWU6IGZhbHNlLFxyXG4gICAgICB0eXBlOiAnc3RyaW5nJyxcclxuICAgICAgZGVzY3JpcHRpb246XHJcbiAgICAgICAgJ1RoZSBvdXRwdXQgZmlsZW5hbWUgYWxvbmcgd2l0aCBhIHR5cGUgKGpwZWcsIHBuZywgcGRmLCBvciBzdmcpLiBUaGlzIHdpbGwgaWdub3JlIHRoZSAtLXR5cGUgZmxhZy4nXHJcbiAgICB9LFxyXG4gICAgdHlwZToge1xyXG4gICAgICBlbnZMaW5rOiAnRVhQT1JUX1RZUEUnLFxyXG4gICAgICB2YWx1ZTogJ3BuZycsXHJcbiAgICAgIHR5cGU6ICdzdHJpbmcnLFxyXG4gICAgICBkZXNjcmlwdGlvbjogJ1RoZSBmaWxlIGV4cG9ydCBmb3JtYXQuIEl0IGNhbiBiZSBqcGVnLCBwbmcsIHBkZiwgb3Igc3ZnLidcclxuICAgIH0sXHJcbiAgICBjb25zdHI6IHtcclxuICAgICAgZW52TGluazogJ0VYUE9SVF9DT05TVFInLFxyXG4gICAgICB2YWx1ZTogJ2NoYXJ0JyxcclxuICAgICAgdHlwZTogJ3N0cmluZycsXHJcbiAgICAgIGRlc2NyaXB0aW9uOlxyXG4gICAgICAgICdUaGUgY29uc3RydWN0b3IgdG8gdXNlLiBDYW4gYmUgY2hhcnQsIHN0b2NrQ2hhcnQsIG1hcENoYXJ0LCBvciBnYW50dENoYXJ0LidcclxuICAgIH0sXHJcbiAgICBkZWZhdWx0SGVpZ2h0OiB7XHJcbiAgICAgIGVudkxpbms6ICdFWFBPUlRfREVGQVVMVF9IRUlHSFQnLFxyXG4gICAgICB2YWx1ZTogNDAwLFxyXG4gICAgICB0eXBlOiAnbnVtYmVyJyxcclxuICAgICAgZGVzY3JpcHRpb246XHJcbiAgICAgICAgJ3RoZSBkZWZhdWx0IGhlaWdodCBvZiB0aGUgZXhwb3J0ZWQgY2hhcnQuIFVzZWQgd2hlbiBubyB2YWx1ZSBpcyBzZXQuJ1xyXG4gICAgfSxcclxuICAgIGRlZmF1bHRXaWR0aDoge1xyXG4gICAgICBlbnZMaW5rOiAnRVhQT1JUX0RFRkFVTFRfV0lEVEgnLFxyXG4gICAgICB2YWx1ZTogNjAwLFxyXG4gICAgICB0eXBlOiAnbnVtYmVyJyxcclxuICAgICAgZGVzY3JpcHRpb246XHJcbiAgICAgICAgJ1RoZSBkZWZhdWx0IHdpZHRoIG9mIHRoZSBleHBvcnRlZCBjaGFydC4gVXNlZCB3aGVuIG5vIHZhbHVlIGlzIHNldC4nXHJcbiAgICB9LFxyXG4gICAgZGVmYXVsdFNjYWxlOiB7XHJcbiAgICAgIGVudkxpbms6ICdFWFBPUlRfREVGQVVMVF9TQ0FMRScsXHJcbiAgICAgIHZhbHVlOiAxLFxyXG4gICAgICB0eXBlOiAnbnVtYmVyJyxcclxuICAgICAgZGVzY3JpcHRpb246XHJcbiAgICAgICAgJ1RoZSBkZWZhdWx0IHNjYWxlIG9mIHRoZSBleHBvcnRlZCBjaGFydC4gVXNlZCB3aGVuIG5vIHZhbHVlIGlzIHNldC4nXHJcbiAgICB9LFxyXG4gICAgaGVpZ2h0OiB7XHJcbiAgICAgIHR5cGU6ICdudW1iZXInLFxyXG4gICAgICB2YWx1ZTogZmFsc2UsXHJcbiAgICAgIGRlc2NyaXB0aW9uOlxyXG4gICAgICAgICdUaGUgaGVpZ2h0IG9mIHRoZSBleHBvcnRlZCBjaGFydCwgb3ZlcnJpZGluZyB0aGUgb3B0aW9uIGluIHRoZSBjaGFydCBzZXR0aW5ncy4nXHJcbiAgICB9LFxyXG4gICAgd2lkdGg6IHtcclxuICAgICAgdHlwZTogJ251bWJlcicsXHJcbiAgICAgIHZhbHVlOiBmYWxzZSxcclxuICAgICAgZGVzY3JpcHRpb246XHJcbiAgICAgICAgJ1RoZSB3aWR0aCBvZiB0aGUgZXhwb3J0ZWQgY2hhcnQsIG92ZXJyaWRpbmcgdGhlIG9wdGlvbiBpbiB0aGUgY2hhcnQgc2V0dGluZ3MuJ1xyXG4gICAgfSxcclxuICAgIHNjYWxlOiB7XHJcbiAgICAgIHZhbHVlOiBmYWxzZSxcclxuICAgICAgdHlwZTogJ251bWJlcicsXHJcbiAgICAgIGRlc2NyaXB0aW9uOlxyXG4gICAgICAgICdUaGUgc2NhbGUgb2YgdGhlIGV4cG9ydGVkIGNoYXJ0LCBvdmVycmlkaW5nIHRoZSBvcHRpb24gaW4gdGhlIGNoYXJ0IHNldHRpbmdzLiBSYW5nZXMgYmV0d2VlbiAwLjEgYW5kIDUuMC4nXHJcbiAgICB9LFxyXG4gICAgZ2xvYmFsT3B0aW9uczoge1xyXG4gICAgICB2YWx1ZTogZmFsc2UsXHJcbiAgICAgIHR5cGU6ICdzdHJpbmcnLFxyXG4gICAgICBkZXNjcmlwdGlvbjpcclxuICAgICAgICAnRWl0aGVyIGEgc3RyaW5naWZpZWQgSlNPTiBvciBhIGZpbGVuYW1lIGNvbnRhaW5pbmcgb3B0aW9ucyB0byBiZSBwYXNzZWQgaW50byB0aGUgSGlnaGNoYXJ0cy5zZXRPcHRpb25zLidcclxuICAgIH0sXHJcbiAgICB0aGVtZU9wdGlvbnM6IHtcclxuICAgICAgdmFsdWU6IGZhbHNlLFxyXG4gICAgICB0eXBlOiAnc3RyaW5nJyxcclxuICAgICAgZGVzY3JpcHRpb246XHJcbiAgICAgICAgJ0VpdGhlciBhIHN0cmluZ2lmaWVkIEpTT04gb3IgYSBmaWxlbmFtZSBjb250YWluaW5nIHRoZW1lIG9wdGlvbnMgdG8gYmUgcGFzc2VkIGludG8gdGhlIEhpZ2hjaGFydHMuc2V0T3B0aW9ucy4nXHJcbiAgICB9LFxyXG4gICAgYmF0Y2g6IHtcclxuICAgICAgdmFsdWU6IGZhbHNlLFxyXG4gICAgICB0eXBlOiAnc3RyaW5nJyxcclxuICAgICAgZGVzY3JpcHRpb246XHJcbiAgICAgICAgJ0luaXRpYXRlcyBhIGJhdGNoIGpvYiB3aXRoIGEgc3RyaW5nIGNvbnRhaW5pbmcgaW5wdXQvb3V0cHV0IHBhaXJzOiBcImluPW91dDtpbj1vdXQ7Li4uXCIuJ1xyXG4gICAgfSxcclxuICAgIHJhc3Rlcml6YXRpb25UaW1lb3V0OiB7XHJcbiAgICAgIGVudkxpbms6ICdFWFBPUlRfUkFTVEVSSVpBVElPTl9USU1FT1VUJyxcclxuICAgICAgdmFsdWU6IDE1MDAsXHJcbiAgICAgIHR5cGU6ICdudW1iZXInLFxyXG4gICAgICBkZXNjcmlwdGlvbjpcclxuICAgICAgICAnVGhlIGR1cmF0aW9uIGluIG1pbGxpc2Vjb25kcyB0byB3YWl0IGZvciByZW5kZXJpbmcgYSB3ZWJwYWdlLidcclxuICAgIH1cclxuICB9LFxyXG4gIGN1c3RvbUxvZ2ljOiB7XHJcbiAgICBhbGxvd0NvZGVFeGVjdXRpb246IHtcclxuICAgICAgZW52TGluazogJ0NVU1RPTV9MT0dJQ19BTExPV19DT0RFX0VYRUNVVElPTicsXHJcbiAgICAgIHZhbHVlOiBmYWxzZSxcclxuICAgICAgdHlwZTogJ2Jvb2xlYW4nLFxyXG4gICAgICBkZXNjcmlwdGlvbjpcclxuICAgICAgICAnQ29udHJvbHMgd2hldGhlciB0aGUgZXhlY3V0aW9uIG9mIGFyYml0cmFyeSBjb2RlIGlzIGFsbG93ZWQgZHVyaW5nIHRoZSBleHBvcnRpbmcgcHJvY2Vzcy4nXHJcbiAgICB9LFxyXG4gICAgYWxsb3dGaWxlUmVzb3VyY2VzOiB7XHJcbiAgICAgIGVudkxpbms6ICdDVVNUT01fTE9HSUNfQUxMT1dfRklMRV9SRVNPVVJDRVMnLFxyXG4gICAgICB2YWx1ZTogZmFsc2UsXHJcbiAgICAgIHR5cGU6ICdib29sZWFuJyxcclxuICAgICAgZGVzY3JpcHRpb246XHJcbiAgICAgICAgJ0NvbnRyb2xzIHRoZSBhYmlsaXR5IHRvIGluamVjdCByZXNvdXJjZXMgZnJvbSB0aGUgZmlsZXN5c3RlbS4gVGhpcyBzZXR0aW5nIGhhcyBubyBlZmZlY3Qgd2hlbiBydW5uaW5nIGFzIGEgc2VydmVyLidcclxuICAgIH0sXHJcbiAgICBjdXN0b21Db2RlOiB7XHJcbiAgICAgIHZhbHVlOiBmYWxzZSxcclxuICAgICAgdHlwZTogJ3N0cmluZycsXHJcbiAgICAgIGRlc2NyaXB0aW9uOlxyXG4gICAgICAgICdDdXN0b20gY29kZSB0byBleGVjdXRlIGJlZm9yZSBjaGFydCBpbml0aWFsaXphdGlvbi4gSXQgY2FuIGJlIGEgZnVuY3Rpb24sIGNvZGUgd3JhcHBlZCB3aXRoaW4gYSBmdW5jdGlvbiwgb3IgYSBmaWxlbmFtZSB3aXRoIHRoZSAuanMgZXh0ZW5zaW9uLidcclxuICAgIH0sXHJcbiAgICBjYWxsYmFjazoge1xyXG4gICAgICB2YWx1ZTogZmFsc2UsXHJcbiAgICAgIHR5cGU6ICdzdHJpbmcnLFxyXG4gICAgICBkZXNjcmlwdGlvbjpcclxuICAgICAgICAnSmF2YVNjcmlwdCBjb2RlIHRvIHJ1biBkdXJpbmcgY29uc3RydWN0aW9uLiBJdCBjYW4gYmUgYSBmdW5jdGlvbiBvciBhIGZpbGVuYW1lIHdpdGggdGhlIC5qcyBleHRlbnNpb24uJ1xyXG4gICAgfSxcclxuICAgIHJlc291cmNlczoge1xyXG4gICAgICB2YWx1ZTogZmFsc2UsXHJcbiAgICAgIHR5cGU6ICdzdHJpbmcnLFxyXG4gICAgICBkZXNjcmlwdGlvbjpcclxuICAgICAgICAnQWRkaXRpb25hbCByZXNvdXJjZSBpbiB0aGUgZm9ybSBvZiBhIHN0cmluZ2lmaWVkIEpTT04sIHdoaWNoIG1heSBjb250YWluIGZpbGVzLCBqcywgYW5kIGNzcyBzZWN0aW9ucy4nXHJcbiAgICB9LFxyXG4gICAgbG9hZENvbmZpZzoge1xyXG4gICAgICB2YWx1ZTogZmFsc2UsXHJcbiAgICAgIHR5cGU6ICdzdHJpbmcnLFxyXG4gICAgICBsZWdhY3lOYW1lOiAnZnJvbUZpbGUnLFxyXG4gICAgICBkZXNjcmlwdGlvbjogJ0EgZmlsZSBjb250YWluaW5nIGEgcHJlLWRlZmluZWQgY29uZmlndXJhdGlvbiB0byB1c2UuJ1xyXG4gICAgfSxcclxuICAgIGNyZWF0ZUNvbmZpZzoge1xyXG4gICAgICB2YWx1ZTogZmFsc2UsXHJcbiAgICAgIHR5cGU6ICdzdHJpbmcnLFxyXG4gICAgICBkZXNjcmlwdGlvbjpcclxuICAgICAgICAnRW5hYmxlcyBzZXR0aW5nIG9wdGlvbnMgdGhyb3VnaCBhIHByb21wdCBhbmQgc2F2aW5nIHRoZW0gaW4gYSBwcm92aWRlZCBjb25maWcgZmlsZS4nXHJcbiAgICB9XHJcbiAgfSxcclxuICBzZXJ2ZXI6IHtcclxuICAgIGVuYWJsZToge1xyXG4gICAgICBlbnZMaW5rOiAnU0VSVkVSX0VOQUJMRScsXHJcbiAgICAgIHZhbHVlOiBmYWxzZSxcclxuICAgICAgdHlwZTogJ2Jvb2xlYW4nLFxyXG4gICAgICBjbGlOYW1lOiAnZW5hYmxlU2VydmVyJyxcclxuICAgICAgZGVzY3JpcHRpb246XHJcbiAgICAgICAgJ1doZW4gc2V0IHRvIHRydWUsIHRoZSBzZXJ2ZXIgc3RhcnRzIG9uIHRoZSBsb2NhbCBJUCBhZGRyZXNzIDAuMC4wLjAuJ1xyXG4gICAgfSxcclxuICAgIGhvc3Q6IHtcclxuICAgICAgZW52TGluazogJ1NFUlZFUl9IT1NUJyxcclxuICAgICAgdmFsdWU6ICcwLjAuMC4wJyxcclxuICAgICAgdHlwZTogJ3N0cmluZycsXHJcbiAgICAgIGRlc2NyaXB0aW9uOlxyXG4gICAgICAgICdUaGUgaG9zdG5hbWUgb2YgdGhlIHNlcnZlci4gQWRkaXRpb25hbGx5LCBpdCBzdGFydHMgYSBzZXJ2ZXIgb24gdGhlIHByb3ZpZGVkIGhvc3RuYW1lLidcclxuICAgIH0sXHJcbiAgICBwb3J0OiB7XHJcbiAgICAgIGVudkxpbms6ICdTRVJWRVJfUE9SVCcsXHJcbiAgICAgIHZhbHVlOiA3ODAxLFxyXG4gICAgICB0eXBlOiAnbnVtYmVyJyxcclxuICAgICAgZGVzY3JpcHRpb246ICdUaGUgc2VydmVyIHBvcnQgd2hlbiBlbmFibGVkLidcclxuICAgIH0sXHJcbiAgICBiZW5jaG1hcmtpbmc6IHtcclxuICAgICAgZW52TGluazogJ1NFUlZFUl9CRU5DSE1BUktJTkcnLFxyXG4gICAgICB2YWx1ZTogZmFsc2UsXHJcbiAgICAgIHR5cGU6ICdib29sZWFuJyxcclxuICAgICAgY2xpTmFtZTogJ3NlcnZlckJlbmNobWFya2luZycsXHJcbiAgICAgIGRlc2NyaXB0aW9uOlxyXG4gICAgICAgICdJbmRpY2F0ZXMgd2hldGhlciB0byBkaXNwbGF5IHRoZSBkdXJhdGlvbiwgaW4gbWlsbGlzZWNvbmRzLCBvZiBzcGVjaWZpYyBhY3Rpb25zIHRoYXQgb2NjdXIgb24gdGhlIHNlcnZlciB3aGlsZSBzZXJ2aW5nIGEgcmVxdWVzdC4nXHJcbiAgICB9LFxyXG4gICAgc3NsOiB7XHJcbiAgICAgIGVuYWJsZToge1xyXG4gICAgICAgIGVudkxpbms6ICdTRVJWRVJfU1NMX0VOQUJMRScsXHJcbiAgICAgICAgdmFsdWU6IGZhbHNlLFxyXG4gICAgICAgIHR5cGU6ICdib29sZWFuJyxcclxuICAgICAgICBjbGlOYW1lOiAnZW5hYmxlU3NsJyxcclxuICAgICAgICBkZXNjcmlwdGlvbjogJ0VuYWJsZXMgb3IgZGlzYWJsZXMgdGhlIFNTTCBwcm90b2NvbC4nXHJcbiAgICAgIH0sXHJcbiAgICAgIGZvcmNlOiB7XHJcbiAgICAgICAgZW52TGluazogJ1NFUlZFUl9TU0xfRk9SQ0UnLFxyXG4gICAgICAgIHZhbHVlOiBmYWxzZSxcclxuICAgICAgICB0eXBlOiAnYm9vbGVhbicsXHJcbiAgICAgICAgY2xpTmFtZTogJ3NzbEZvcmNlZCcsXHJcbiAgICAgICAgbGVnYWN5TmFtZTogJ3NzbE9ubHknLFxyXG4gICAgICAgIGRlc2NyaXB0aW9uOlxyXG4gICAgICAgICAgJ1doZW4gc2V0IHRvIHRydWUsIHRoZSBzZXJ2ZXIgaXMgZm9yY2VkIHRvIHNlcnZlIG9ubHkgb3ZlciBIVFRQUy4nXHJcbiAgICAgIH0sXHJcbiAgICAgIHBvcnQ6IHtcclxuICAgICAgICBlbnZMaW5rOiAnU0VSVkVSX1NTTF9QT1JUJyxcclxuICAgICAgICB2YWx1ZTogNDQzLFxyXG4gICAgICAgIHR5cGU6ICdudW1iZXInLFxyXG4gICAgICAgIGNsaU5hbWU6ICdzc2xQb3J0JyxcclxuICAgICAgICBkZXNjcmlwdGlvbjogJ1RoZSBwb3J0IG9uIHdoaWNoIHRvIHJ1biB0aGUgU1NMIHNlcnZlci4nXHJcbiAgICAgIH0sXHJcbiAgICAgIGNlcnRQYXRoOiB7XHJcbiAgICAgICAgZW52TGluazogJ1NFUlZFUl9TU0xfQ0VSVF9QQVRIJyxcclxuICAgICAgICB2YWx1ZTogJycsXHJcbiAgICAgICAgdHlwZTogJ3N0cmluZycsXHJcbiAgICAgICAgbGVnYWN5TmFtZTogJ3NzbFBhdGgnLFxyXG4gICAgICAgIGRlc2NyaXB0aW9uOiAnVGhlIHBhdGggdG8gdGhlIFNTTCBjZXJ0aWZpY2F0ZS9rZXkgZmlsZS4nXHJcbiAgICAgIH1cclxuICAgIH0sXHJcbiAgICByYXRlTGltaXRpbmc6IHtcclxuICAgICAgZW5hYmxlOiB7XHJcbiAgICAgICAgZW52TGluazogJ1NFUlZFUl9SQVRFX0xJTUlUSU5HX0VOQUJMRScsXHJcbiAgICAgICAgdmFsdWU6IGZhbHNlLFxyXG4gICAgICAgIHR5cGU6ICdib29sZWFuJyxcclxuICAgICAgICBjbGlOYW1lOiAnZW5hYmxlUmF0ZUxpbWl0aW5nJyxcclxuICAgICAgICBkZXNjcmlwdGlvbjogJ0VuYWJsZXMgcmF0ZSBsaW1pdGluZyBmb3IgdGhlIHNlcnZlci4nXHJcbiAgICAgIH0sXHJcbiAgICAgIG1heFJlcXVlc3RzOiB7XHJcbiAgICAgICAgZW52TGluazogJ1NFUlZFUl9SQVRFX0xJTUlUSU5HX01BWF9SRVFVRVNUUycsXHJcbiAgICAgICAgdmFsdWU6IDEwLFxyXG4gICAgICAgIHR5cGU6ICdudW1iZXInLFxyXG4gICAgICAgIGxlZ2FjeU5hbWU6ICdyYXRlTGltaXQnLFxyXG4gICAgICAgIGRlc2NyaXB0aW9uOiAnVGhlIG1heGltdW0gbnVtYmVyIG9mIHJlcXVlc3RzIGFsbG93ZWQgaW4gb25lIG1pbnV0ZS4nXHJcbiAgICAgIH0sXHJcbiAgICAgIHdpbmRvdzoge1xyXG4gICAgICAgIGVudkxpbms6ICdTRVJWRVJfUkFURV9MSU1JVElOR19XSU5ET1cnLFxyXG4gICAgICAgIHZhbHVlOiAxLFxyXG4gICAgICAgIHR5cGU6ICdudW1iZXInLFxyXG4gICAgICAgIGRlc2NyaXB0aW9uOiAnVGhlIHRpbWUgd2luZG93LCBpbiBtaW51dGVzLCBmb3IgdGhlIHJhdGUgbGltaXRpbmcuJ1xyXG4gICAgICB9LFxyXG4gICAgICBkZWxheToge1xyXG4gICAgICAgIGVudkxpbms6ICdTRVJWRVJfUkFURV9MSU1JVElOR19ERUxBWScsXHJcbiAgICAgICAgdmFsdWU6IDAsXHJcbiAgICAgICAgdHlwZTogJ251bWJlcicsXHJcbiAgICAgICAgZGVzY3JpcHRpb246XHJcbiAgICAgICAgICAnVGhlIGRlbGF5IGR1cmF0aW9uIGZvciBlYWNoIHN1Y2Nlc3NpdmUgcmVxdWVzdCBiZWZvcmUgcmVhY2hpbmcgdGhlIG1heGltdW0gbGltaXQuJ1xyXG4gICAgICB9LFxyXG4gICAgICB0cnVzdFByb3h5OiB7XHJcbiAgICAgICAgZW52TGluazogJ1NFUlZFUl9SQVRFX0xJTUlUSU5HX1RSVVNUX1BST1hZJyxcclxuICAgICAgICB2YWx1ZTogZmFsc2UsXHJcbiAgICAgICAgdHlwZTogJ2Jvb2xlYW4nLFxyXG4gICAgICAgIGRlc2NyaXB0aW9uOiAnU2V0IHRoaXMgdG8gdHJ1ZSBpZiB0aGUgc2VydmVyIGlzIGJlaGluZCBhIGxvYWQgYmFsYW5jZXIuJ1xyXG4gICAgICB9LFxyXG4gICAgICBza2lwS2V5OiB7XHJcbiAgICAgICAgZW52TGluazogJ1NFUlZFUl9SQVRFX0xJTUlUSU5HX1NLSVBfS0VZJyxcclxuICAgICAgICB2YWx1ZTogJycsXHJcbiAgICAgICAgdHlwZTogJ3N0cmluZycsXHJcbiAgICAgICAgZGVzY3JpcHRpb246XHJcbiAgICAgICAgICAnQWxsb3dzIGJ5cGFzc2luZyB0aGUgcmF0ZSBsaW1pdGVyIGFuZCBzaG91bGQgYmUgcHJvdmlkZWQgd2l0aCB0aGUgc2tpcFRva2VuIGFyZ3VtZW50LidcclxuICAgICAgfSxcclxuICAgICAgc2tpcFRva2VuOiB7XHJcbiAgICAgICAgZW52TGluazogJ1NFUlZFUl9SQVRFX0xJTUlUSU5HX1NLSVBfVE9LRU4nLFxyXG4gICAgICAgIHZhbHVlOiAnJyxcclxuICAgICAgICB0eXBlOiAnc3RyaW5nJyxcclxuICAgICAgICBkZXNjcmlwdGlvbjpcclxuICAgICAgICAgICdBbGxvd3MgYnlwYXNzaW5nIHRoZSByYXRlIGxpbWl0ZXIgYW5kIHNob3VsZCBiZSBwcm92aWRlZCB3aXRoIHRoZSBza2lwS2V5IGFyZ3VtZW50LidcclxuICAgICAgfVxyXG4gICAgfVxyXG4gIH0sXHJcbiAgcG9vbDoge1xyXG4gICAgbWluV29ya2Vyczoge1xyXG4gICAgICBlbnZMaW5rOiAnUE9PTF9NSU5fV09SS0VSUycsXHJcbiAgICAgIHZhbHVlOiA0LFxyXG4gICAgICB0eXBlOiAnbnVtYmVyJyxcclxuICAgICAgZGVzY3JpcHRpb246ICdUaGUgbnVtYmVyIG9mIG1pbmltdW0gYW5kIGluaXRpYWwgcG9vbCB3b3JrZXJzIHRvIHNwYXduLidcclxuICAgIH0sXHJcbiAgICBtYXhXb3JrZXJzOiB7XHJcbiAgICAgIGVudkxpbms6ICdQT09MX01BWF9XT1JLRVJTJyxcclxuICAgICAgdmFsdWU6IDgsXHJcbiAgICAgIHR5cGU6ICdudW1iZXInLFxyXG4gICAgICBsZWdhY3lOYW1lOiAnd29ya2VycycsXHJcbiAgICAgIGRlc2NyaXB0aW9uOiAnVGhlIG51bWJlciBvZiBtYXhpbXVtIHBvb2wgd29ya2VycyB0byBzcGF3bi4nXHJcbiAgICB9LFxyXG4gICAgd29ya0xpbWl0OiB7XHJcbiAgICAgIGVudkxpbms6ICdQT09MX1dPUktfTElNSVQnLFxyXG4gICAgICB2YWx1ZTogNDAsXHJcbiAgICAgIHR5cGU6ICdudW1iZXInLFxyXG4gICAgICBkZXNjcmlwdGlvbjpcclxuICAgICAgICAnVGhlIG51bWJlciBvZiB3b3JrIHBpZWNlcyB0aGF0IGNhbiBiZSBwZXJmb3JtZWQgYmVmb3JlIHJlc3RhcnRpbmcgdGhlIHdvcmtlciBwcm9jZXNzLidcclxuICAgIH0sXHJcbiAgICBhY3F1aXJlVGltZW91dDoge1xyXG4gICAgICBlbnZMaW5rOiAnUE9PTF9BQ1FVSVJFX1RJTUVPVVQnLFxyXG4gICAgICB2YWx1ZTogNTAwMCxcclxuICAgICAgdHlwZTogJ251bWJlcicsXHJcbiAgICAgIGRlc2NyaXB0aW9uOlxyXG4gICAgICAgICdUaGUgZHVyYXRpb24sIGluIG1pbGxpc2Vjb25kcywgdG8gd2FpdCBmb3IgYWNxdWlyaW5nIGEgcmVzb3VyY2UuJ1xyXG4gICAgfSxcclxuICAgIGNyZWF0ZVRpbWVvdXQ6IHtcclxuICAgICAgZW52TGluazogJ1BPT0xfQ1JFQVRFX1RJTUVPVVQnLFxyXG4gICAgICB2YWx1ZTogNTAwMCxcclxuICAgICAgdHlwZTogJ251bWJlcicsXHJcbiAgICAgIGRlc2NyaXB0aW9uOlxyXG4gICAgICAgICdUaGUgZHVyYXRpb24sIGluIG1pbGxpc2Vjb25kcywgdG8gd2FpdCBmb3IgY3JlYXRpbmcgYSByZXNvdXJjZS4nXHJcbiAgICB9LFxyXG4gICAgZGVzdHJveVRpbWVvdXQ6IHtcclxuICAgICAgZW52TGluazogJ1BPT0xfREVTVFJPWV9USU1FT1VUJyxcclxuICAgICAgdmFsdWU6IDUwMDAsXHJcbiAgICAgIHR5cGU6ICdudW1iZXInLFxyXG4gICAgICBkZXNjcmlwdGlvbjpcclxuICAgICAgICAnVGhlIGR1cmF0aW9uLCBpbiBtaWxsaXNlY29uZHMsIHRvIHdhaXQgZm9yIGRlc3Ryb3lpbmcgYSByZXNvdXJjZS4nXHJcbiAgICB9LFxyXG4gICAgaWRsZVRpbWVvdXQ6IHtcclxuICAgICAgZW52TGluazogJ1BPT0xfSURMRV9USU1FT1VUJyxcclxuICAgICAgdmFsdWU6IDMwMDAwLFxyXG4gICAgICB0eXBlOiAnbnVtYmVyJyxcclxuICAgICAgZGVzY3JpcHRpb246XHJcbiAgICAgICAgJ1RoZSBkdXJhdGlvbiwgaW4gbWlsbGlzZWNvbmRzLCBhZnRlciB3aGljaCBhbiBpZGxlIHJlc291cmNlIGlzIGRlc3Ryb3llZC4nXHJcbiAgICB9LFxyXG4gICAgY3JlYXRlUmV0cnlJbnRlcnZhbDoge1xyXG4gICAgICBlbnZMaW5rOiAnUE9PTF9DUkVBVEVfUkVUUllfSU5URVJWQUwnLFxyXG4gICAgICB2YWx1ZTogMjAwLFxyXG4gICAgICB0eXBlOiAnbnVtYmVyJyxcclxuICAgICAgZGVzY3JpcHRpb246XHJcbiAgICAgICAgJ1RoZSBkdXJhdGlvbiwgaW4gbWlsbGlzZWNvbmRzLCB0byB3YWl0IGJlZm9yZSByZXRyeWluZyB0aGUgY3JlYXRlIHByb2Nlc3MgaW4gY2FzZSBvZiBhIGZhaWx1cmUuJ1xyXG4gICAgfSxcclxuICAgIHJlYXBlckludGVydmFsOiB7XHJcbiAgICAgIGVudkxpbms6ICdQT09MX1JFQVBFUl9JTlRFUlZBTCcsXHJcbiAgICAgIHZhbHVlOiAxMDAwLFxyXG4gICAgICB0eXBlOiAnbnVtYmVyJyxcclxuICAgICAgZGVzY3JpcHRpb246XHJcbiAgICAgICAgJ1RoZSBkdXJhdGlvbiwgaW4gbWlsbGlzZWNvbmRzLCBhZnRlciB3aGljaCB0aGUgY2hlY2sgZm9yIGlkbGUgcmVzb3VyY2VzIHRvIGRlc3Ryb3kgaXMgdHJpZ2dlcmVkLidcclxuICAgIH0sXHJcbiAgICBiZW5jaG1hcmtpbmc6IHtcclxuICAgICAgZW52TGluazogJ1BPT0xfQkVOQ0hNQVJLSU5HJyxcclxuICAgICAgdmFsdWU6IGZhbHNlLFxyXG4gICAgICB0eXBlOiAnYm9vbGVhbicsXHJcbiAgICAgIGNsaU5hbWU6ICdwb29sQmVuY2htYXJraW5nJyxcclxuICAgICAgZGVzY3JpcHRpb246XHJcbiAgICAgICAgJ0luZGljYXRlIHdoZXRoZXIgdG8gc2hvdyBzdGF0aXN0aWNzIGZvciB0aGUgcG9vbCBvZiByZXNvdXJjZXMgb3Igbm90LidcclxuICAgIH0sXHJcbiAgICBsaXN0ZW5Ub1Byb2Nlc3NFeGl0czoge1xyXG4gICAgICBlbnZMaW5rOiAnUE9PTF9MSVNURU5fVE9fUFJPQ0VTU19FWElUUycsXHJcbiAgICAgIHZhbHVlOiB0cnVlLFxyXG4gICAgICB0eXBlOiAnYm9vbGVhbicsXHJcbiAgICAgIGRlc2NyaXB0aW9uOiAnRGVjaWRlcyB3aGV0aGVyIG9yIG5vdCB0byBhdHRhY2ggcHJvY2Vzcy5leGl0IGhhbmRsZXJzLidcclxuICAgIH1cclxuICB9LFxyXG4gIGxvZ2dpbmc6IHtcclxuICAgIGxldmVsOiB7XHJcbiAgICAgIGVudkxpbms6ICdMT0dHSU5HX0xFVkVMJyxcclxuICAgICAgdmFsdWU6IDQsXHJcbiAgICAgIHR5cGU6ICdudW1iZXInLFxyXG4gICAgICBjbGlOYW1lOiAnbG9nTGV2ZWwnLFxyXG4gICAgICBkZXNjcmlwdGlvbjogJ1RoZSBsb2dnaW5nIGxldmVsIHRvIGJlIHVzZWQuJ1xyXG4gICAgfSxcclxuICAgIGZpbGU6IHtcclxuICAgICAgZW52TGluazogJ0xPR0dJTkdfRklMRScsXHJcbiAgICAgIHZhbHVlOiAnaGlnaGNoYXJ0cy1leHBvcnQtc2VydmVyLmxvZycsXHJcbiAgICAgIHR5cGU6ICdzdHJpbmcnLFxyXG4gICAgICBjbGlOYW1lOiAnbG9nRmlsZScsXHJcbiAgICAgIGRlc2NyaXB0aW9uOlxyXG4gICAgICAgICdUaGUgbmFtZSBvZiBhIGxvZyBmaWxlLiBUaGUgbG9nRGVzdCBvcHRpb24gYWxzbyBuZWVkcyB0byBiZSBzZXQgdG8gZW5hYmxlIGZpbGUgbG9nZ2luZy4nXHJcbiAgICB9LFxyXG4gICAgZGVzdDoge1xyXG4gICAgICBlbnZMaW5rOiAnTE9HR0lOR19ERVNUJyxcclxuICAgICAgdmFsdWU6ICdsb2cvJyxcclxuICAgICAgdHlwZTogJ3N0cmluZycsXHJcbiAgICAgIGNsaU5hbWU6ICdsb2dEZXN0JyxcclxuICAgICAgZGVzY3JpcHRpb246XHJcbiAgICAgICAgJ1RoZSBwYXRoIHRvIHN0b3JlIGxvZyBmaWxlcy4gVGhpcyBhbHNvIGVuYWJsZXMgZmlsZSBsb2dnaW5nLidcclxuICAgIH1cclxuICB9LFxyXG4gIHVpOiB7XHJcbiAgICBlbmFibGU6IHtcclxuICAgICAgZW52TGluazogJ1VJX0VOQUJMRScsXHJcbiAgICAgIHZhbHVlOiBmYWxzZSxcclxuICAgICAgdHlwZTogJ2Jvb2xlYW4nLFxyXG4gICAgICBjbGlOYW1lOiAnZW5hYmxlVWknLFxyXG4gICAgICBkZXNjcmlwdGlvbjpcclxuICAgICAgICAnRW5hYmxlcyBvciBkaXNhYmxlcyB0aGUgdXNlciBpbnRlcmZhY2UgKFVJKSBmb3IgdGhlIGV4cG9ydCBzZXJ2ZXIuJ1xyXG4gICAgfSxcclxuICAgIHJvdXRlOiB7XHJcbiAgICAgIGVudkxpbms6ICdVSV9ST1VURScsXHJcbiAgICAgIHZhbHVlOiAnLycsXHJcbiAgICAgIHR5cGU6ICdzdHJpbmcnLFxyXG4gICAgICBjbGlOYW1lOiAndWlSb3V0ZScsXHJcbiAgICAgIGRlc2NyaXB0aW9uOlxyXG4gICAgICAgICdUaGUgZW5kcG9pbnQgcm91dGUgdG8gd2hpY2ggdGhlIHVzZXIgaW50ZXJmYWNlIChVSSkgc2hvdWxkIGJlIGF0dGFjaGVkLidcclxuICAgIH1cclxuICB9LFxyXG4gIG90aGVyOiB7XHJcbiAgICBub0xvZ286IHtcclxuICAgICAgZW52TGluazogJ09USEVSX05PX0xPR08nLFxyXG4gICAgICB2YWx1ZTogZmFsc2UsXHJcbiAgICAgIHR5cGU6ICdib29sZWFuJyxcclxuICAgICAgZGVzY3JpcHRpb246XHJcbiAgICAgICAgJ1NraXAgcHJpbnRpbmcgdGhlIGxvZ28gb24gYSBzdGFydHVwLiBXaWxsIGJlIHJlcGxhY2VkIGJ5IGEgc2ltcGxlIHRleHQuJ1xyXG4gICAgfVxyXG4gIH1cclxufTtcclxuXHJcbi8vIFRoZSBjb25maWcgZGVzY3JpcHRpb25zIG9iamVjdCBmb3IgdGhlIHByb21wdHMgZnVuY3Rpb25hbGl0eS4gSXQgY29udGFpbnNcclxuLy8gaW5mb3JtYXRpb24gbGlrZTpcclxuLy8gKiBUeXBlIG9mIGEgcHJvbXB0XHJcbi8vICogTmFtZSBvZiBhbiBvcHRpb25cclxuLy8gKiBTaG9ydCBkZXNjcmlwdGlvbiBvZiBhIGNob3NlbiBvcHRpb25cclxuLy8gKiBJbml0aWFsIHZhbHVlXHJcbmV4cG9ydCBjb25zdCBwcm9tcHRzQ29uZmlnID0ge1xyXG4gIHB1cHBldGVlcjogW1xyXG4gICAge1xyXG4gICAgICB0eXBlOiAnbGlzdCcsXHJcbiAgICAgIG5hbWU6ICdhcmdzJyxcclxuICAgICAgbWVzc2FnZTogJ1B1cHBldGVlciBhcmd1bWVudHMnLFxyXG4gICAgICBpbml0aWFsOiBkZWZhdWx0Q29uZmlnLnB1cHBldGVlci5hcmdzLnZhbHVlLmpvaW4oJywnKSxcclxuICAgICAgc2VwYXJhdG9yOiAnLCdcclxuICAgIH1cclxuICBdLFxyXG4gIGhpZ2hjaGFydHM6IFtcclxuICAgIHtcclxuICAgICAgdHlwZTogJ3RleHQnLFxyXG4gICAgICBuYW1lOiAndmVyc2lvbicsXHJcbiAgICAgIG1lc3NhZ2U6ICdIaWdoY2hhcnRzIHZlcnNpb24nLFxyXG4gICAgICBpbml0aWFsOiBkZWZhdWx0Q29uZmlnLmhpZ2hjaGFydHMudmVyc2lvbi52YWx1ZVxyXG4gICAgfSxcclxuICAgIHtcclxuICAgICAgdHlwZTogJ3RleHQnLFxyXG4gICAgICBuYW1lOiAnY2RuVVJMJyxcclxuICAgICAgbWVzc2FnZTogJ1RoZSBVUkwgb2YgQ0ROJyxcclxuICAgICAgaW5pdGlhbDogZGVmYXVsdENvbmZpZy5oaWdoY2hhcnRzLmNkblVSTC52YWx1ZVxyXG4gICAgfSxcclxuICAgIHtcclxuICAgICAgdHlwZTogJ211bHRpc2VsZWN0JyxcclxuICAgICAgbmFtZTogJ21vZHVsZXMnLFxyXG4gICAgICBtZXNzYWdlOiAnQXZhaWxhYmxlIG1vZHVsZXMnLFxyXG4gICAgICBpbnN0cnVjdGlvbnM6ICdTcGFjZTogU2VsZWN0IHNwZWNpZmljLCBBOiBTZWxlY3QgYWxsLCBFbnRlcjogQ29uZmlybS4nLFxyXG4gICAgICBjaG9pY2VzOiBkZWZhdWx0Q29uZmlnLmhpZ2hjaGFydHMubW9kdWxlcy52YWx1ZVxyXG4gICAgfSxcclxuICAgIHtcclxuICAgICAgdHlwZTogJ2xpc3QnLFxyXG4gICAgICBuYW1lOiAnc2NyaXB0cycsXHJcbiAgICAgIG1lc3NhZ2U6ICdDdXN0b20gc2NyaXB0cycsXHJcbiAgICAgIGluaXRpYWw6IGRlZmF1bHRDb25maWcuaGlnaGNoYXJ0cy5zY3JpcHRzLnZhbHVlLmpvaW4oJywnKSxcclxuICAgICAgc2VwYXJhdG9yOiAnLCdcclxuICAgIH0sXHJcbiAgICB7XHJcbiAgICAgIHR5cGU6ICd0b2dnbGUnLFxyXG4gICAgICBuYW1lOiAnZm9yY2VGZXRjaCcsXHJcbiAgICAgIG1lc3NhZ2U6ICdGb3JjZSByZS1mZXRjaCB0aGUgc2NyaXB0cycsXHJcbiAgICAgIGluaXRpYWw6IGRlZmF1bHRDb25maWcuaGlnaGNoYXJ0cy5mb3JjZUZldGNoLnZhbHVlXHJcbiAgICB9LFxyXG4gICAge1xyXG4gICAgICB0eXBlOiAndGV4dCcsXHJcbiAgICAgIG5hbWU6ICdjYWNoZVBhdGgnLFxyXG4gICAgICBtZXNzYWdlOiAnVGhlIHBhdGggdG8gdGhlIGNhY2hlIGRpcmVjdG9yeScsXHJcbiAgICAgIGluaXRpYWw6IGRlZmF1bHRDb25maWcuaGlnaGNoYXJ0cy5jYWNoZVBhdGgudmFsdWVcclxuICAgIH1cclxuICBdLFxyXG4gIGV4cG9ydDogW1xyXG4gICAge1xyXG4gICAgICB0eXBlOiAnc2VsZWN0JyxcclxuICAgICAgbmFtZTogJ3R5cGUnLFxyXG4gICAgICBtZXNzYWdlOiAnVGhlIGRlZmF1bHQgZXhwb3J0IGZpbGUgdHlwZScsXHJcbiAgICAgIGhpbnQ6IGBEZWZhdWx0OiAke2RlZmF1bHRDb25maWcuZXhwb3J0LnR5cGUudmFsdWV9YCxcclxuICAgICAgaW5pdGlhbDogMCxcclxuICAgICAgY2hvaWNlczogWydwbmcnLCAnanBlZycsICdwZGYnLCAnc3ZnJ11cclxuICAgIH0sXHJcbiAgICB7XHJcbiAgICAgIHR5cGU6ICdzZWxlY3QnLFxyXG4gICAgICBuYW1lOiAnY29uc3RyJyxcclxuICAgICAgbWVzc2FnZTogJ1RoZSBkZWZhdWx0IGNvbnN0cnVjdG9yIGZvciBIaWdoY2hhcnRzJyxcclxuICAgICAgaGludDogYERlZmF1bHQ6ICR7ZGVmYXVsdENvbmZpZy5leHBvcnQuY29uc3RyLnZhbHVlfWAsXHJcbiAgICAgIGluaXRpYWw6IDAsXHJcbiAgICAgIGNob2ljZXM6IFsnY2hhcnQnLCAnc3RvY2tDaGFydCcsICdtYXBDaGFydCcsICdnYW50dENoYXJ0J11cclxuICAgIH0sXHJcbiAgICB7XHJcbiAgICAgIHR5cGU6ICdudW1iZXInLFxyXG4gICAgICBuYW1lOiAnZGVmYXVsdEhlaWdodCcsXHJcbiAgICAgIG1lc3NhZ2U6ICdUaGUgZGVmYXVsdCBmYWxsYmFjayBoZWlnaHQgb2YgdGhlIGV4cG9ydGVkIGNoYXJ0JyxcclxuICAgICAgaW5pdGlhbDogZGVmYXVsdENvbmZpZy5leHBvcnQuZGVmYXVsdEhlaWdodC52YWx1ZVxyXG4gICAgfSxcclxuICAgIHtcclxuICAgICAgdHlwZTogJ251bWJlcicsXHJcbiAgICAgIG5hbWU6ICdkZWZhdWx0V2lkdGgnLFxyXG4gICAgICBtZXNzYWdlOiAnVGhlIGRlZmF1bHQgZmFsbGJhY2sgd2lkdGggb2YgdGhlIGV4cG9ydGVkIGNoYXJ0JyxcclxuICAgICAgaW5pdGlhbDogZGVmYXVsdENvbmZpZy5leHBvcnQuZGVmYXVsdFdpZHRoLnZhbHVlXHJcbiAgICB9LFxyXG4gICAge1xyXG4gICAgICB0eXBlOiAnbnVtYmVyJyxcclxuICAgICAgbmFtZTogJ2RlZmF1bHRTY2FsZScsXHJcbiAgICAgIG1lc3NhZ2U6ICdUaGUgZGVmYXVsdCBmYWxsYmFjayBzY2FsZSBvZiB0aGUgZXhwb3J0ZWQgY2hhcnQnLFxyXG4gICAgICBpbml0aWFsOiBkZWZhdWx0Q29uZmlnLmV4cG9ydC5kZWZhdWx0U2NhbGUudmFsdWUsXHJcbiAgICAgIG1pbjogMC4xLFxyXG4gICAgICBtYXg6IDVcclxuICAgIH0sXHJcbiAgICB7XHJcbiAgICAgIHR5cGU6ICdudW1iZXInLFxyXG4gICAgICBuYW1lOiAncmFzdGVyaXphdGlvblRpbWVvdXQnLFxyXG4gICAgICBtZXNzYWdlOiAnVGhlIHJlbmRlcmluZyB3ZWJwYWdlIHRpbWVvdXQgaW4gbWlsbGlzZWNvbmRzJyxcclxuICAgICAgaW5pdGlhbDogZGVmYXVsdENvbmZpZy5leHBvcnQucmFzdGVyaXphdGlvblRpbWVvdXQudmFsdWVcclxuICAgIH1cclxuICBdLFxyXG4gIGN1c3RvbUxvZ2ljOiBbXHJcbiAgICB7XHJcbiAgICAgIHR5cGU6ICd0b2dnbGUnLFxyXG4gICAgICBuYW1lOiAnYWxsb3dDb2RlRXhlY3V0aW9uJyxcclxuICAgICAgbWVzc2FnZTogJ0VuYWJsZSBleGVjdXRpb24gb2YgY3VzdG9tIGNvZGUnLFxyXG4gICAgICBpbml0aWFsOiBkZWZhdWx0Q29uZmlnLmN1c3RvbUxvZ2ljLmFsbG93Q29kZUV4ZWN1dGlvbi52YWx1ZVxyXG4gICAgfSxcclxuICAgIHtcclxuICAgICAgdHlwZTogJ3RvZ2dsZScsXHJcbiAgICAgIG5hbWU6ICdhbGxvd0ZpbGVSZXNvdXJjZXMnLFxyXG4gICAgICBtZXNzYWdlOiAnRW5hYmxlIGZpbGUgcmVzb3VyY2VzJyxcclxuICAgICAgaW5pdGlhbDogZGVmYXVsdENvbmZpZy5jdXN0b21Mb2dpYy5hbGxvd0ZpbGVSZXNvdXJjZXMudmFsdWVcclxuICAgIH1cclxuICBdLFxyXG4gIHNlcnZlcjogW1xyXG4gICAge1xyXG4gICAgICB0eXBlOiAndG9nZ2xlJyxcclxuICAgICAgbmFtZTogJ2VuYWJsZScsXHJcbiAgICAgIG1lc3NhZ2U6ICdTdGFydHMgdGhlIHNlcnZlciBvbiAwLjAuMC4wJyxcclxuICAgICAgaW5pdGlhbDogZGVmYXVsdENvbmZpZy5zZXJ2ZXIuZW5hYmxlLnZhbHVlXHJcbiAgICB9LFxyXG4gICAge1xyXG4gICAgICB0eXBlOiAndGV4dCcsXHJcbiAgICAgIG5hbWU6ICdob3N0JyxcclxuICAgICAgbWVzc2FnZTogJ1NlcnZlciBob3N0bmFtZScsXHJcbiAgICAgIGluaXRpYWw6IGRlZmF1bHRDb25maWcuc2VydmVyLmhvc3QudmFsdWVcclxuICAgIH0sXHJcbiAgICB7XHJcbiAgICAgIHR5cGU6ICdudW1iZXInLFxyXG4gICAgICBuYW1lOiAncG9ydCcsXHJcbiAgICAgIG1lc3NhZ2U6ICdTZXJ2ZXIgcG9ydCcsXHJcbiAgICAgIGluaXRpYWw6IGRlZmF1bHRDb25maWcuc2VydmVyLnBvcnQudmFsdWVcclxuICAgIH0sXHJcbiAgICB7XHJcbiAgICAgIHR5cGU6ICd0b2dnbGUnLFxyXG4gICAgICBuYW1lOiAnYmVuY2htYXJraW5nJyxcclxuICAgICAgbWVzc2FnZTogJ0VuYWJsZSBzZXJ2ZXIgYmVuY2htYXJraW5nJyxcclxuICAgICAgaW5pdGlhbDogZGVmYXVsdENvbmZpZy5zZXJ2ZXIuYmVuY2htYXJraW5nLnZhbHVlXHJcbiAgICB9LFxyXG4gICAge1xyXG4gICAgICB0eXBlOiAndG9nZ2xlJyxcclxuICAgICAgbmFtZTogJ3NzbC5lbmFibGUnLFxyXG4gICAgICBtZXNzYWdlOiAnRW5hYmxlIFNTTCBwcm90b2NvbCcsXHJcbiAgICAgIGluaXRpYWw6IGRlZmF1bHRDb25maWcuc2VydmVyLnNzbC5lbmFibGUudmFsdWVcclxuICAgIH0sXHJcbiAgICB7XHJcbiAgICAgIHR5cGU6ICd0b2dnbGUnLFxyXG4gICAgICBuYW1lOiAnc3NsLmZvcmNlJyxcclxuICAgICAgbWVzc2FnZTogJ0ZvcmNlIHNlcnZpbmcgb25seSBvdmVyIEhUVFBTJyxcclxuICAgICAgaW5pdGlhbDogZGVmYXVsdENvbmZpZy5zZXJ2ZXIuc3NsLmZvcmNlLnZhbHVlXHJcbiAgICB9LFxyXG4gICAge1xyXG4gICAgICB0eXBlOiAnbnVtYmVyJyxcclxuICAgICAgbmFtZTogJ3NzbC5wb3J0JyxcclxuICAgICAgbWVzc2FnZTogJ1NTTCBzZXJ2ZXIgcG9ydCcsXHJcbiAgICAgIGluaXRpYWw6IGRlZmF1bHRDb25maWcuc2VydmVyLnNzbC5wb3J0LnZhbHVlXHJcbiAgICB9LFxyXG4gICAge1xyXG4gICAgICB0eXBlOiAndGV4dCcsXHJcbiAgICAgIG5hbWU6ICdzc2wuY2VydFBhdGgnLFxyXG4gICAgICBtZXNzYWdlOiAnVGhlIHBhdGggdG8gZmluZCB0aGUgU1NMIGNlcnRpZmljYXRlL2tleScsXHJcbiAgICAgIGluaXRpYWw6IGRlZmF1bHRDb25maWcuc2VydmVyLnNzbC5jZXJ0UGF0aC52YWx1ZVxyXG4gICAgfSxcclxuICAgIHtcclxuICAgICAgdHlwZTogJ3RvZ2dsZScsXHJcbiAgICAgIG5hbWU6ICdyYXRlTGltaXRpbmcuZW5hYmxlJyxcclxuICAgICAgbWVzc2FnZTogJ0VuYWJsZSByYXRlIGxpbWl0aW5nJyxcclxuICAgICAgaW5pdGlhbDogZGVmYXVsdENvbmZpZy5zZXJ2ZXIucmF0ZUxpbWl0aW5nLmVuYWJsZS52YWx1ZVxyXG4gICAgfSxcclxuICAgIHtcclxuICAgICAgdHlwZTogJ251bWJlcicsXHJcbiAgICAgIG5hbWU6ICdyYXRlTGltaXRpbmcubWF4UmVxdWVzdHMnLFxyXG4gICAgICBtZXNzYWdlOiAnVGhlIG1heGltdW0gcmVxdWVzdHMgYWxsb3dlZCBwZXIgbWludXRlJyxcclxuICAgICAgaW5pdGlhbDogZGVmYXVsdENvbmZpZy5zZXJ2ZXIucmF0ZUxpbWl0aW5nLm1heFJlcXVlc3RzLnZhbHVlXHJcbiAgICB9LFxyXG4gICAge1xyXG4gICAgICB0eXBlOiAnbnVtYmVyJyxcclxuICAgICAgbmFtZTogJ3JhdGVMaW1pdGluZy53aW5kb3cnLFxyXG4gICAgICBtZXNzYWdlOiAnVGhlIHJhdGUtbGltaXRpbmcgdGltZSB3aW5kb3cgaW4gbWludXRlcycsXHJcbiAgICAgIGluaXRpYWw6IGRlZmF1bHRDb25maWcuc2VydmVyLnJhdGVMaW1pdGluZy53aW5kb3cudmFsdWVcclxuICAgIH0sXHJcbiAgICB7XHJcbiAgICAgIHR5cGU6ICdudW1iZXInLFxyXG4gICAgICBuYW1lOiAncmF0ZUxpbWl0aW5nLmRlbGF5JyxcclxuICAgICAgbWVzc2FnZTpcclxuICAgICAgICAnVGhlIGRlbGF5IGZvciBlYWNoIHN1Y2Nlc3NpdmUgcmVxdWVzdCBiZWZvcmUgcmVhY2hpbmcgdGhlIG1heGltdW0nLFxyXG4gICAgICBpbml0aWFsOiBkZWZhdWx0Q29uZmlnLnNlcnZlci5yYXRlTGltaXRpbmcuZGVsYXkudmFsdWVcclxuICAgIH0sXHJcbiAgICB7XHJcbiAgICAgIHR5cGU6ICd0b2dnbGUnLFxyXG4gICAgICBuYW1lOiAncmF0ZUxpbWl0aW5nLnRydXN0UHJveHknLFxyXG4gICAgICBtZXNzYWdlOiAnU2V0IHRvIHRydWUgaWYgYmVoaW5kIGEgbG9hZCBiYWxhbmNlcicsXHJcbiAgICAgIGluaXRpYWw6IGRlZmF1bHRDb25maWcuc2VydmVyLnJhdGVMaW1pdGluZy50cnVzdFByb3h5LnZhbHVlXHJcbiAgICB9LFxyXG4gICAge1xyXG4gICAgICB0eXBlOiAndGV4dCcsXHJcbiAgICAgIG5hbWU6ICdyYXRlTGltaXRpbmcuc2tpcEtleScsXHJcbiAgICAgIG1lc3NhZ2U6XHJcbiAgICAgICAgJ0FsbG93cyBieXBhc3NpbmcgdGhlIHJhdGUgbGltaXRlciB3aGVuIHByb3ZpZGVkIHdpdGggdGhlIHNraXBUb2tlbiBhcmd1bWVudCcsXHJcbiAgICAgIGluaXRpYWw6IGRlZmF1bHRDb25maWcuc2VydmVyLnJhdGVMaW1pdGluZy5za2lwS2V5LnZhbHVlXHJcbiAgICB9LFxyXG4gICAge1xyXG4gICAgICB0eXBlOiAndGV4dCcsXHJcbiAgICAgIG5hbWU6ICdyYXRlTGltaXRpbmcuc2tpcFRva2VuJyxcclxuICAgICAgbWVzc2FnZTpcclxuICAgICAgICAnQWxsb3dzIGJ5cGFzc2luZyB0aGUgcmF0ZSBsaW1pdGVyIHdoZW4gcHJvdmlkZWQgd2l0aCB0aGUgc2tpcEtleSBhcmd1bWVudCcsXHJcbiAgICAgIGluaXRpYWw6IGRlZmF1bHRDb25maWcuc2VydmVyLnJhdGVMaW1pdGluZy5za2lwVG9rZW4udmFsdWVcclxuICAgIH1cclxuICBdLFxyXG4gIHBvb2w6IFtcclxuICAgIHtcclxuICAgICAgdHlwZTogJ251bWJlcicsXHJcbiAgICAgIG5hbWU6ICdtaW5Xb3JrZXJzJyxcclxuICAgICAgbWVzc2FnZTogJ1RoZSBpbml0aWFsIG51bWJlciBvZiB3b3JrZXJzIHRvIHNwYXduJyxcclxuICAgICAgaW5pdGlhbDogZGVmYXVsdENvbmZpZy5wb29sLm1pbldvcmtlcnMudmFsdWVcclxuICAgIH0sXHJcbiAgICB7XHJcbiAgICAgIHR5cGU6ICdudW1iZXInLFxyXG4gICAgICBuYW1lOiAnbWF4V29ya2VycycsXHJcbiAgICAgIG1lc3NhZ2U6ICdUaGUgbWF4aW11bSBudW1iZXIgb2Ygd29ya2VycyB0byBzcGF3bicsXHJcbiAgICAgIGluaXRpYWw6IGRlZmF1bHRDb25maWcucG9vbC5tYXhXb3JrZXJzLnZhbHVlXHJcbiAgICB9LFxyXG4gICAge1xyXG4gICAgICB0eXBlOiAnbnVtYmVyJyxcclxuICAgICAgbmFtZTogJ3dvcmtMaW1pdCcsXHJcbiAgICAgIG1lc3NhZ2U6XHJcbiAgICAgICAgJ1RoZSBwaWVjZXMgb2Ygd29yayB0aGF0IGNhbiBiZSBwZXJmb3JtZWQgYmVmb3JlIHJlc3RhcnRpbmcgYSBQdXBwZXRlZXIgcHJvY2VzcycsXHJcbiAgICAgIGluaXRpYWw6IGRlZmF1bHRDb25maWcucG9vbC53b3JrTGltaXQudmFsdWVcclxuICAgIH0sXHJcbiAgICB7XHJcbiAgICAgIHR5cGU6ICdudW1iZXInLFxyXG4gICAgICBuYW1lOiAnYWNxdWlyZVRpbWVvdXQnLFxyXG4gICAgICBtZXNzYWdlOiAnVGhlIG51bWJlciBvZiBtaWxsaXNlY29uZHMgdG8gd2FpdCBmb3IgYWNxdWlyaW5nIGEgcmVzb3VyY2UnLFxyXG4gICAgICBpbml0aWFsOiBkZWZhdWx0Q29uZmlnLnBvb2wuYWNxdWlyZVRpbWVvdXQudmFsdWVcclxuICAgIH0sXHJcbiAgICB7XHJcbiAgICAgIHR5cGU6ICdudW1iZXInLFxyXG4gICAgICBuYW1lOiAnY3JlYXRlVGltZW91dCcsXHJcbiAgICAgIG1lc3NhZ2U6ICdUaGUgbnVtYmVyIG9mIG1pbGxpc2Vjb25kcyB0byB3YWl0IGZvciBjcmVhdGluZyBhIHJlc291cmNlJyxcclxuICAgICAgaW5pdGlhbDogZGVmYXVsdENvbmZpZy5wb29sLmNyZWF0ZVRpbWVvdXQudmFsdWVcclxuICAgIH0sXHJcbiAgICB7XHJcbiAgICAgIHR5cGU6ICdudW1iZXInLFxyXG4gICAgICBuYW1lOiAnZGVzdHJveVRpbWVvdXQnLFxyXG4gICAgICBtZXNzYWdlOiAnVGhlIG51bWJlciBvZiBtaWxsaXNlY29uZHMgdG8gd2FpdCBmb3IgZGVzdHJveWluZyBhIHJlc291cmNlJyxcclxuICAgICAgaW5pdGlhbDogZGVmYXVsdENvbmZpZy5wb29sLmRlc3Ryb3lUaW1lb3V0LnZhbHVlXHJcbiAgICB9LFxyXG4gICAge1xyXG4gICAgICB0eXBlOiAnbnVtYmVyJyxcclxuICAgICAgbmFtZTogJ2lkbGVUaW1lb3V0JyxcclxuICAgICAgbWVzc2FnZTogJ1RoZSBudW1iZXIgb2YgbWlsbGlzZWNvbmRzIGFmdGVyIGFuIGlkbGUgcmVzb3VyY2UgaXMgZGVzdHJveWVkJyxcclxuICAgICAgaW5pdGlhbDogZGVmYXVsdENvbmZpZy5wb29sLmlkbGVUaW1lb3V0LnZhbHVlXHJcbiAgICB9LFxyXG4gICAge1xyXG4gICAgICB0eXBlOiAnbnVtYmVyJyxcclxuICAgICAgbmFtZTogJ2NyZWF0ZVJldHJ5SW50ZXJ2YWwnLFxyXG4gICAgICBtZXNzYWdlOlxyXG4gICAgICAgICdUaGUgcmV0cnkgaW50ZXJ2YWwgaW4gbWlsbGlzZWNvbmRzIGFmdGVyIGEgY3JlYXRlIHByb2Nlc3MgZmFpbHMnLFxyXG4gICAgICBpbml0aWFsOiBkZWZhdWx0Q29uZmlnLnBvb2wuY3JlYXRlUmV0cnlJbnRlcnZhbC52YWx1ZVxyXG4gICAgfSxcclxuICAgIHtcclxuICAgICAgdHlwZTogJ251bWJlcicsXHJcbiAgICAgIG5hbWU6ICdyZWFwZXJJbnRlcnZhbCcsXHJcbiAgICAgIG1lc3NhZ2U6XHJcbiAgICAgICAgJ1RoZSByZWFwZXIgaW50ZXJ2YWwgaW4gbWlsbGlzZWNvbmRzIGFmdGVyIHRyaWdnZXJpbmcgdGhlIGNoZWNrIGZvciBpZGxlIHJlc291cmNlcyB0byBkZXN0cm95JyxcclxuICAgICAgaW5pdGlhbDogZGVmYXVsdENvbmZpZy5wb29sLnJlYXBlckludGVydmFsLnZhbHVlXHJcbiAgICB9LFxyXG4gICAge1xyXG4gICAgICB0eXBlOiAndG9nZ2xlJyxcclxuICAgICAgbmFtZTogJ2JlbmNobWFya2luZycsXHJcbiAgICAgIG1lc3NhZ2U6ICdFbmFibGUgYmVuY2htYXJraW5nIGZvciBhIHJlc291cmNlIHBvb2wnLFxyXG4gICAgICBpbml0aWFsOiBkZWZhdWx0Q29uZmlnLnBvb2wuYmVuY2htYXJraW5nLnZhbHVlXHJcbiAgICB9LFxyXG4gICAge1xyXG4gICAgICB0eXBlOiAndG9nZ2xlJyxcclxuICAgICAgbmFtZTogJ2xpc3RlblRvUHJvY2Vzc0V4aXRzJyxcclxuICAgICAgbWVzc2FnZTogJ1NldCB0byBmYWxzZSB0byBza2lwIGF0dGFjaGluZyBwcm9jZXNzLmV4aXQgaGFuZGxlcnMnLFxyXG4gICAgICBpbml0aWFsOiBkZWZhdWx0Q29uZmlnLnBvb2wubGlzdGVuVG9Qcm9jZXNzRXhpdHMudmFsdWVcclxuICAgIH1cclxuICBdLFxyXG4gIGxvZ2dpbmc6IFtcclxuICAgIHtcclxuICAgICAgdHlwZTogJ251bWJlcicsXHJcbiAgICAgIG5hbWU6ICdsZXZlbCcsXHJcbiAgICAgIG1lc3NhZ2U6XHJcbiAgICAgICAgJ1RoZSBsb2cgbGV2ZWwgKDA6IHNpbGVudCwgMTogZXJyb3IsIDI6IHdhcm5pbmcsIDM6IG5vdGljZSwgNDogdmVyYm9zZSwgNTogYmVuY2htYXJrKScsXHJcbiAgICAgIGluaXRpYWw6IGRlZmF1bHRDb25maWcubG9nZ2luZy5sZXZlbC52YWx1ZSxcclxuICAgICAgcm91bmQ6IDAsXHJcbiAgICAgIG1pbjogMCxcclxuICAgICAgbWF4OiA1XHJcbiAgICB9LFxyXG4gICAge1xyXG4gICAgICB0eXBlOiAndGV4dCcsXHJcbiAgICAgIG5hbWU6ICdmaWxlJyxcclxuICAgICAgbWVzc2FnZTogJ0EgbG9nIGZpbGUgbmFtZS4gU2V0IHdpdGggdGhlIC0tbG9nRGVzdCB0byBlbmFibGUgZmlsZSBsb2dnaW5nJyxcclxuICAgICAgaW5pdGlhbDogZGVmYXVsdENvbmZpZy5sb2dnaW5nLmZpbGUudmFsdWVcclxuICAgIH0sXHJcbiAgICB7XHJcbiAgICAgIHR5cGU6ICd0ZXh0JyxcclxuICAgICAgbmFtZTogJ2Rlc3QnLFxyXG4gICAgICBtZXNzYWdlOiAnVGhlIHBhdGggdG8gbG9nIGZpbGVzLiBFbmFibGVzIGZpbGUgbG9nZ2luZycsXHJcbiAgICAgIGluaXRpYWw6IGRlZmF1bHRDb25maWcubG9nZ2luZy5kZXN0LnZhbHVlXHJcbiAgICB9XHJcbiAgXSxcclxuICB1aTogW1xyXG4gICAge1xyXG4gICAgICB0eXBlOiAndG9nZ2xlJyxcclxuICAgICAgbmFtZTogJ2VuYWJsZScsXHJcbiAgICAgIG1lc3NhZ2U6ICdFbmFibGUgVUkgZm9yIHRoZSBleHBvcnQgc2VydmVyJyxcclxuICAgICAgaW5pdGlhbDogZGVmYXVsdENvbmZpZy51aS5lbmFibGUudmFsdWVcclxuICAgIH0sXHJcbiAgICB7XHJcbiAgICAgIHR5cGU6ICd0ZXh0JyxcclxuICAgICAgbmFtZTogJ3JvdXRlJyxcclxuICAgICAgbWVzc2FnZTogJ0Egcm91dGUgdG8gYXR0YWNoIHRoZSBVSScsXHJcbiAgICAgIGluaXRpYWw6IGRlZmF1bHRDb25maWcudWkucm91dGUudmFsdWVcclxuICAgIH1cclxuICBdLFxyXG4gIG90aGVyOiBbXHJcbiAgICB7XHJcbiAgICAgIHR5cGU6ICd0b2dnbGUnLFxyXG4gICAgICBuYW1lOiAnbm9Mb2dvJyxcclxuICAgICAgbWVzc2FnZTogJ1NraXAgcHJpbnRpbmcgdGhlIGxvZ28gb24gc3RhcnR1cC4gUmVwbGFjZWQgYnkgc2ltcGxlIHRleHQnLFxyXG4gICAgICBpbml0aWFsOiBkZWZhdWx0Q29uZmlnLm90aGVyLm5vTG9nby52YWx1ZVxyXG4gICAgfVxyXG4gIF1cclxufTtcclxuXHJcbi8vIEFic29sdXRlIHByb3BzIHRoYXQsIGluIGNhc2Ugb2YgbWVyZ2luZyByZWN1cnNpdmVseSwgbmVlZCB0byBiZSBmb3JjZSBtZXJnZWRcclxuZXhwb3J0IGNvbnN0IGFic29sdXRlUHJvcHMgPSBbXHJcbiAgJ29wdGlvbnMnLFxyXG4gICdnbG9iYWxPcHRpb25zJyxcclxuICAndGhlbWVPcHRpb25zJyxcclxuICAncmVzb3VyY2VzJyxcclxuICAncGF5bG9hZCdcclxuXTtcclxuXHJcbi8vIEFyZ3VtZW50IG5lc3RpbmcgbGV2ZWwgb2YgYWxsIGV4cG9ydCBzZXJ2ZXIgb3B0aW9uc1xyXG5leHBvcnQgY29uc3QgbmVzdGVkQXJncyA9IHt9O1xyXG5cclxuLyoqXHJcbiAqIFJlY3Vyc2l2ZWx5IGNyZWF0ZXMgYSBjaGFpbiBvZiBuZXN0ZWQgYXJndW1lbnRzIGZyb20gYW4gb2JqZWN0LlxyXG4gKlxyXG4gKiBAcGFyYW0ge09iamVjdH0gb2JqIC0gVGhlIG9iamVjdCBjb250YWluaW5nIG5lc3RlZCBhcmd1bWVudHMuXHJcbiAqIEBwYXJhbSB7c3RyaW5nfSBwcm9wQ2hhaW4gLSBUaGUgY3VycmVudCBjaGFpbiBvZiBuZXN0ZWQgcHJvcGVydGllc1xyXG4gKiAodXNlZCBpbnRlcm5hbGx5IGR1cmluZyByZWN1cnNpb24pLlxyXG4gKi9cclxuY29uc3QgY3JlYXRlTmVzdGVkQXJncyA9IChvYmosIHByb3BDaGFpbiA9ICcnKSA9PiB7XHJcbiAgT2JqZWN0LmtleXMob2JqKS5mb3JFYWNoKChrKSA9PiB7XHJcbiAgICBpZiAoIVsncHVwcGV0ZWVyJywgJ2hpZ2hjaGFydHMnXS5pbmNsdWRlcyhrKSkge1xyXG4gICAgICBjb25zdCBlbnRyeSA9IG9ialtrXTtcclxuICAgICAgaWYgKHR5cGVvZiBlbnRyeS52YWx1ZSA9PT0gJ3VuZGVmaW5lZCcpIHtcclxuICAgICAgICAvLyBHbyBkZWVwZXIgaW4gdGhlIG5lc3RlZCBhcmd1bWVudHNcclxuICAgICAgICBjcmVhdGVOZXN0ZWRBcmdzKGVudHJ5LCBgJHtwcm9wQ2hhaW59LiR7a31gKTtcclxuICAgICAgfSBlbHNlIHtcclxuICAgICAgICAvLyBDcmVhdGUgdGhlIGNoYWluIG9mIG5lc3RlZCBhcmd1bWVudHNcclxuICAgICAgICBuZXN0ZWRBcmdzW2VudHJ5LmNsaU5hbWUgfHwga10gPSBgJHtwcm9wQ2hhaW59LiR7a31gLnN1YnN0cmluZygxKTtcclxuXHJcbiAgICAgICAgLy8gU3VwcG9ydCBmb3IgdGhlIGxlZ2FjeSwgUGhhbnRvbUpTIHByb3BlcnRpZXMgbmFtZXNcclxuICAgICAgICBpZiAoZW50cnkubGVnYWN5TmFtZSAhPT0gdW5kZWZpbmVkKSB7XHJcbiAgICAgICAgICBuZXN0ZWRBcmdzW2VudHJ5LmxlZ2FjeU5hbWVdID0gYCR7cHJvcENoYWlufS4ke2t9YC5zdWJzdHJpbmcoMSk7XHJcbiAgICAgICAgfVxyXG4gICAgICB9XHJcbiAgICB9XHJcbiAgfSk7XHJcbn07XHJcblxyXG5jcmVhdGVOZXN0ZWRBcmdzKGRlZmF1bHRDb25maWcpO1xyXG4iLCIvKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKlxyXG5cclxuSGlnaGNoYXJ0cyBFeHBvcnQgU2VydmVyXHJcblxyXG5Db3B5cmlnaHQgKGMpIDIwMTYtMjAyNCwgSGlnaHNvZnRcclxuXHJcbkxpY2VuY2VkIHVuZGVyIHRoZSBNSVQgbGljZW5jZS5cclxuXHJcbkFkZGl0aW9uYWxseSBhIHZhbGlkIEhpZ2hjaGFydHMgbGljZW5zZSBpcyByZXF1aXJlZCBmb3IgdXNlLlxyXG5cclxuU2VlIExJQ0VOU0UgZmlsZSBpbiByb290IGZvciBkZXRhaWxzLlxyXG5cclxuKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKi9cclxuXHJcbmltcG9ydCB7IGFwcGVuZEZpbGUsIGV4aXN0c1N5bmMsIG1rZGlyU3luYyB9IGZyb20gJ2ZzJztcclxuXHJcbmltcG9ydCB7IGRlZmF1bHRDb25maWcgfSBmcm9tICcuL3NjaGVtYXMvY29uZmlnLmpzJztcclxuXHJcbi8vIFRoZSBhdmFpbGFibGUgY29sb3JzXHJcbmNvbnN0IGNvbG9ycyA9IFsncmVkJywgJ3llbGxvdycsICdibHVlJywgJ2dyYXknLCAnZ3JlZW4nXTtcclxuXHJcbi8vIFRoZSBkZWZhdWx0IGxvZ2dpbmcgY29uZmlnXHJcbmxldCBsb2dnaW5nID0ge1xyXG4gIC8vIEZsYWdzIGZvciBsb2dnaW5nIHN0YXR1c1xyXG4gIHRvQ29uc29sZTogdHJ1ZSxcclxuICB0b0ZpbGU6IGZhbHNlLFxyXG4gIHBhdGhDcmVhdGVkOiBmYWxzZSxcclxuICAvLyBMb2cgbGV2ZWxzXHJcbiAgbGV2ZWxzRGVzYzogW1xyXG4gICAge1xyXG4gICAgICB0aXRsZTogJ2Vycm9yJyxcclxuICAgICAgY29sb3I6IGNvbG9yc1swXVxyXG4gICAgfSxcclxuICAgIHtcclxuICAgICAgdGl0bGU6ICd3YXJuaW5nJyxcclxuICAgICAgY29sb3I6IGNvbG9yc1sxXVxyXG4gICAgfSxcclxuICAgIHtcclxuICAgICAgdGl0bGU6ICdub3RpY2UnLFxyXG4gICAgICBjb2xvcjogY29sb3JzWzJdXHJcbiAgICB9LFxyXG4gICAge1xyXG4gICAgICB0aXRsZTogJ3ZlcmJvc2UnLFxyXG4gICAgICBjb2xvcjogY29sb3JzWzNdXHJcbiAgICB9LFxyXG4gICAge1xyXG4gICAgICB0aXRsZTogJ2JlbmNobWFyaycsXHJcbiAgICAgIGNvbG9yOiBjb2xvcnNbNF1cclxuICAgIH1cclxuICBdLFxyXG4gIC8vIExvZyBsaXN0ZW5lcnNcclxuICBsaXN0ZW5lcnM6IFtdXHJcbn07XHJcblxyXG4vLyBHYXRoZXIgaW5pdCBsb2dnaW5nIG9wdGlvbnNcclxuZm9yIChjb25zdCBba2V5LCBvcHRpb25dIG9mIE9iamVjdC5lbnRyaWVzKGRlZmF1bHRDb25maWcubG9nZ2luZykpIHtcclxuICBsb2dnaW5nW2tleV0gPSBvcHRpb24udmFsdWU7XHJcbn1cclxuXHJcbi8qKlxyXG4gKiBMb2dzIHRoZSBwcm92aWRlZCB0ZXh0cyB0byBhIGZpbGUsIGlmIGZpbGUgbG9nZ2luZyBpcyBlbmFibGVkLiBJdCBjcmVhdGVzXHJcbiAqIHRoZSBuZWNlc3NhcnkgZGlyZWN0b3J5IHN0cnVjdHVyZSBpZiBub3QgYWxyZWFkeSBjcmVhdGVkIGFuZCBhcHBlbmRzIHRoZVxyXG4gKiBjb250ZW50LCBpbmNsdWRpbmcgYW4gb3B0aW9uYWwgcHJlZml4LCB0byB0aGUgc3BlY2lmaWVkIGxvZyBmaWxlLlxyXG4gKlxyXG4gKiBAcGFyYW0ge3N0cmluZ1tdfSB0ZXh0cyAtIEFuIGFycmF5IG9mIHRleHRzIHRvIGJlIGxvZ2dlZC5cclxuICogQHBhcmFtIHtzdHJpbmd9IHByZWZpeCAtIEFuIG9wdGlvbmFsIHByZWZpeCB0byBiZSBhZGRlZCB0byBlYWNoIGxvZyBlbnRyeS5cclxuICovXHJcbmNvbnN0IGxvZ1RvRmlsZSA9ICh0ZXh0cywgcHJlZml4KSA9PiB7XHJcbiAgaWYgKGxvZ2dpbmcudG9GaWxlKSB7XHJcbiAgICBpZiAoIWxvZ2dpbmcucGF0aENyZWF0ZWQpIHtcclxuICAgICAgLy8gQ3JlYXRlIGlmIGRvZXMgbm90IGV4aXN0XHJcbiAgICAgICFleGlzdHNTeW5jKGxvZ2dpbmcuZGVzdCkgJiYgbWtkaXJTeW5jKGxvZ2dpbmcuZGVzdCk7XHJcblxyXG4gICAgICAvLyBXZSBub3cgYXNzdW1lIHRoZSBwYXRoIGlzIGF2YWlsYWJsZSwgZS5nLiBpdCdzIHRoZSByZXNwb25zaWJpbGl0eVxyXG4gICAgICAvLyBvZiB0aGUgdXNlciB0byBjcmVhdGUgdGhlIHBhdGggd2l0aCB0aGUgY29ycmVjdCBhY2Nlc3MgcmlnaHRzLlxyXG4gICAgICBsb2dnaW5nLnBhdGhDcmVhdGVkID0gdHJ1ZTtcclxuICAgIH1cclxuXHJcbiAgICAvLyBBZGQgdGhlIGNvbnRlbnQgdG8gYSBmaWxlXHJcbiAgICBhcHBlbmRGaWxlKFxyXG4gICAgICBgJHtsb2dnaW5nLmRlc3R9JHtsb2dnaW5nLmZpbGV9YCxcclxuICAgICAgW3ByZWZpeF0uY29uY2F0KHRleHRzKS5qb2luKCcgJykgKyAnXFxuJyxcclxuICAgICAgKGVycm9yKSA9PiB7XHJcbiAgICAgICAgaWYgKGVycm9yKSB7XHJcbiAgICAgICAgICBjb25zb2xlLmxvZyhgW2xvZ2dlcl0gVW5hYmxlIHRvIHdyaXRlIHRvIGxvZyBmaWxlOiAke2Vycm9yfWApO1xyXG4gICAgICAgICAgbG9nZ2luZy50b0ZpbGUgPSBmYWxzZTtcclxuICAgICAgICB9XHJcbiAgICAgIH1cclxuICAgICk7XHJcbiAgfVxyXG59O1xyXG5cclxuLyoqXHJcbiAqIExvZ3MgYSBtZXNzYWdlLiBBY2NlcHRzIGEgdmFyaWFibGUgYW1vdW50IG9mIGFyZ3VtZW50cy4gQXJndW1lbnRzIGFmdGVyXHJcbiAqIGBsZXZlbGAgd2lsbCBiZSBwYXNzZWQgZGlyZWN0bHkgdG8gY29uc29sZS5sb2csIGFuZC9vciB3aWxsIGJlIGpvaW5lZFxyXG4gKiBhbmQgYXBwZW5kZWQgdG8gdGhlIGxvZyBmaWxlLlxyXG4gKlxyXG4gKiBAcGFyYW0ge2FueX0gYXJncyAtIEFuIGFycmF5IG9mIGFyZ3VtZW50cyB3aGVyZSB0aGUgZmlyc3QgaXMgdGhlIGxvZyBsZXZlbFxyXG4gKiBhbmQgdGhlIHJlc3QgYXJlIHN0cmluZ3MgdG8gYnVpbGQgYSBtZXNzYWdlIHdpdGguXHJcbiAqL1xyXG5leHBvcnQgY29uc3QgbG9nID0gKC4uLmFyZ3MpID0+IHtcclxuICBjb25zdCBbbmV3TGV2ZWwsIC4uLnRleHRzXSA9IGFyZ3M7XHJcblxyXG4gIC8vIEN1cnJlbnQgbG9nZ2luZyBvcHRpb25zXHJcbiAgY29uc3QgeyBsZXZlbCwgbGV2ZWxzRGVzYyB9ID0gbG9nZ2luZztcclxuXHJcbiAgLy8gQ2hlY2sgaWYgbG9nIGxldmVsIGlzIHdpdGhpbiBhIGNvcnJlY3QgcmFuZ2Ugb3IgaXMgYSBiZW5jaG1hcmsgbG9nXHJcbiAgaWYgKFxyXG4gICAgbmV3TGV2ZWwgIT09IDUgJiZcclxuICAgIChuZXdMZXZlbCA9PT0gMCB8fCBuZXdMZXZlbCA+IGxldmVsIHx8IGxldmVsID4gbGV2ZWxzRGVzYy5sZW5ndGgpXHJcbiAgKSB7XHJcbiAgICByZXR1cm47XHJcbiAgfVxyXG5cclxuICAvLyBHZXQgcmlkIG9mIHRoZSBHTVQgdGV4dCBpbmZvcm1hdGlvblxyXG4gIGNvbnN0IG5ld0RhdGUgPSBuZXcgRGF0ZSgpLnRvU3RyaW5nKCkuc3BsaXQoJygnKVswXS50cmltKCk7XHJcblxyXG4gIC8vIENyZWF0ZSBhIG1lc3NhZ2UncyBwcmVmaXhcclxuICBjb25zdCBwcmVmaXggPSBgJHtuZXdEYXRlfSBbJHtsZXZlbHNEZXNjW25ld0xldmVsIC0gMV0udGl0bGV9XSAtYDtcclxuXHJcbiAgLy8gQ2FsbCBhdmFpbGFibGUgbG9nIGxpc3RlbmVyc1xyXG4gIGxvZ2dpbmcubGlzdGVuZXJzLmZvckVhY2goKGZuKSA9PiB7XHJcbiAgICBmbihwcmVmaXgsIHRleHRzLmpvaW4oJyAnKSk7XHJcbiAgfSk7XHJcblxyXG4gIC8vIExvZyB0byBjb25zb2xlXHJcbiAgaWYgKGxvZ2dpbmcudG9Db25zb2xlKSB7XHJcbiAgICBjb25zb2xlLmxvZy5hcHBseShcclxuICAgICAgdW5kZWZpbmVkLFxyXG4gICAgICBbcHJlZml4LnRvU3RyaW5nKClbbG9nZ2luZy5sZXZlbHNEZXNjW25ld0xldmVsIC0gMV0uY29sb3JdXS5jb25jYXQodGV4dHMpXHJcbiAgICApO1xyXG4gIH1cclxuXHJcbiAgLy8gTG9nIHRvIGZpbGVcclxuICBsb2dUb0ZpbGUodGV4dHMsIHByZWZpeCk7XHJcbn07XHJcblxyXG4vKipcclxuICogTG9ncyBhbiBlcnJvciBtZXNzYWdlIHdpdGggaXRzIHN0YWNrIHRyYWNlLiBPcHRpb25hbGx5LCBhIGN1c3RvbSBtZXNzYWdlXHJcbiAqIGNhbiBiZSBwcm92aWRlZC5cclxuICpcclxuICogQHBhcmFtIHtudW1iZXJ9IGxldmVsIC0gVGhlIGxvZyBsZXZlbC5cclxuICogQHBhcmFtIHtFcnJvcn0gZXJyb3IgLSBUaGUgZXJyb3Igb2JqZWN0LlxyXG4gKiBAcGFyYW0ge3N0cmluZ30gY3VzdG9tTWVzc2FnZSAtIEFuIG9wdGlvbmFsIGN1c3RvbSBtZXNzYWdlIHRvIGJlIGxvZ2dlZCBhbG9uZ1xyXG4gKiB3aXRoIHRoZSBlcnJvci5cclxuICovXHJcbmV4cG9ydCBjb25zdCBsb2dXaXRoU3RhY2sgPSAobmV3TGV2ZWwsIGVycm9yLCBjdXN0b21NZXNzYWdlKSA9PiB7XHJcbiAgLy8gR2V0IHRoZSBtYWluIG1lc3NhZ2VcclxuICBjb25zdCBtYWluTWVzc2FnZSA9IGN1c3RvbU1lc3NhZ2UgfHwgZXJyb3IubWVzc2FnZTtcclxuXHJcbiAgLy8gQ3VycmVudCBsb2dnaW5nIG9wdGlvbnNcclxuICBjb25zdCB7IGxldmVsLCBsZXZlbHNEZXNjIH0gPSBsb2dnaW5nO1xyXG5cclxuICAvLyBDaGVjayBpZiBsb2cgbGV2ZWwgaXMgd2l0aGluIGEgY29ycmVjdCByYW5nZVxyXG4gIGlmIChuZXdMZXZlbCA9PT0gMCB8fCBuZXdMZXZlbCA+IGxldmVsIHx8IGxldmVsID4gbGV2ZWxzRGVzYy5sZW5ndGgpIHtcclxuICAgIHJldHVybjtcclxuICB9XHJcblxyXG4gIC8vIEdldCByaWQgb2YgdGhlIEdNVCB0ZXh0IGluZm9ybWF0aW9uXHJcbiAgY29uc3QgbmV3RGF0ZSA9IG5ldyBEYXRlKCkudG9TdHJpbmcoKS5zcGxpdCgnKCcpWzBdLnRyaW0oKTtcclxuXHJcbiAgLy8gQ3JlYXRlIGEgbWVzc2FnZSdzIHByZWZpeFxyXG4gIGNvbnN0IHByZWZpeCA9IGAke25ld0RhdGV9IFske2xldmVsc0Rlc2NbbmV3TGV2ZWwgLSAxXS50aXRsZX1dIC1gO1xyXG5cclxuICAvLyBJZiB0aGUgY3VzdG9tTWVzc2FnZSBleGlzdHMsIHdlIHdhbnQgdG8gZGlzcGxheSB0aGUgd2hvbGUgc3RhY2sgbWVzc2FnZVxyXG4gIGNvbnN0IHN0YWNrTWVzc2FnZSA9XHJcbiAgICBlcnJvci5tZXNzYWdlICE9PSBlcnJvci5zdGFja01lc3NhZ2UgfHwgZXJyb3Iuc3RhY2tNZXNzYWdlID09PSB1bmRlZmluZWRcclxuICAgICAgPyBlcnJvci5zdGFja1xyXG4gICAgICA6IGVycm9yLnN0YWNrLnNwbGl0KCdcXG4nKS5zbGljZSgxKS5qb2luKCdcXG4nKTtcclxuXHJcbiAgLy8gQ29tYmluZSBjdXN0b20gbWVzc2FnZSBvciBlcnJvciBtZXNzYWdlIHdpdGggZXJyb3Igc3RhY2sgbWVzc2FnZVxyXG4gIGNvbnN0IHRleHRzID0gW21haW5NZXNzYWdlLCAnXFxuJywgc3RhY2tNZXNzYWdlXTtcclxuXHJcbiAgLy8gTG9nIHRvIGNvbnNvbGVcclxuICBpZiAobG9nZ2luZy50b0NvbnNvbGUpIHtcclxuICAgIGNvbnNvbGUubG9nLmFwcGx5KFxyXG4gICAgICB1bmRlZmluZWQsXHJcbiAgICAgIFtwcmVmaXgudG9TdHJpbmcoKVtsb2dnaW5nLmxldmVsc0Rlc2NbbmV3TGV2ZWwgLSAxXS5jb2xvcl1dLmNvbmNhdChbXHJcbiAgICAgICAgbWFpbk1lc3NhZ2VbY29sb3JzW25ld0xldmVsIC0gMV1dLFxyXG4gICAgICAgICdcXG4nLFxyXG4gICAgICAgIHN0YWNrTWVzc2FnZVxyXG4gICAgICBdKVxyXG4gICAgKTtcclxuICB9XHJcblxyXG4gIC8vIENhbGwgYXZhaWxhYmxlIGxvZyBsaXN0ZW5lcnNcclxuICBsb2dnaW5nLmxpc3RlbmVycy5mb3JFYWNoKChmbikgPT4ge1xyXG4gICAgZm4ocHJlZml4LCB0ZXh0cy5qb2luKCcgJykpO1xyXG4gIH0pO1xyXG5cclxuICAvLyBMb2cgdG8gZmlsZVxyXG4gIGxvZ1RvRmlsZSh0ZXh0cywgcHJlZml4KTtcclxufTtcclxuXHJcbi8qKlxyXG4gKiBTZXRzIHRoZSBsb2cgbGV2ZWwgdG8gdGhlIHNwZWNpZmllZCB2YWx1ZS4gTG9nIGxldmVscyBhcmUgKDAgPSBubyBsb2dnaW5nLFxyXG4gKiAxID0gZXJyb3IsIDIgPSB3YXJuaW5nLCAzID0gbm90aWNlLCA0ID0gdmVyYm9zZSBvciA1ID0gYmVuY2htYXJrKVxyXG4gKlxyXG4gKiBAcGFyYW0ge251bWJlcn0gbmV3TGV2ZWwgLSBUaGUgbmV3IGxvZyBsZXZlbCB0byBiZSBzZXQuXHJcbiAqL1xyXG5leHBvcnQgY29uc3Qgc2V0TG9nTGV2ZWwgPSAobmV3TGV2ZWwpID0+IHtcclxuICBpZiAobmV3TGV2ZWwgPj0gMCAmJiBuZXdMZXZlbCA8PSBsb2dnaW5nLmxldmVsc0Rlc2MubGVuZ3RoKSB7XHJcbiAgICBsb2dnaW5nLmxldmVsID0gbmV3TGV2ZWw7XHJcbiAgfVxyXG59O1xyXG5cclxuLyoqXHJcbiAqIEVuYWJsZXMgZmlsZSBsb2dnaW5nIHdpdGggdGhlIHNwZWNpZmllZCBkZXN0aW5hdGlvbiBhbmQgbG9nIGZpbGUuXHJcbiAqXHJcbiAqIEBwYXJhbSB7c3RyaW5nfSBsb2dEZXN0IC0gVGhlIGRlc3RpbmF0aW9uIHBhdGggZm9yIGxvZyBmaWxlcy5cclxuICogQHBhcmFtIHtzdHJpbmd9IGxvZ0ZpbGUgLSBUaGUgbG9nIGZpbGUgbmFtZS5cclxuICovXHJcbmV4cG9ydCBjb25zdCBlbmFibGVGaWxlTG9nZ2luZyA9IChsb2dEZXN0LCBsb2dGaWxlKSA9PiB7XHJcbiAgLy8gVXBkYXRlIGxvZ2dpbmcgb3B0aW9uc1xyXG4gIGxvZ2dpbmcgPSB7XHJcbiAgICAuLi5sb2dnaW5nLFxyXG4gICAgZGVzdDogbG9nRGVzdCB8fCBsb2dnaW5nLmRlc3QsXHJcbiAgICBmaWxlOiBsb2dGaWxlIHx8IGxvZ2dpbmcuZmlsZSxcclxuICAgIHRvRmlsZTogdHJ1ZVxyXG4gIH07XHJcblxyXG4gIGlmIChsb2dnaW5nLmRlc3QubGVuZ3RoID09PSAwKSB7XHJcbiAgICByZXR1cm4gbG9nKDEsICdbbG9nZ2VyXSBGaWxlIGxvZ2dpbmcgaW5pdGlhbGl6YXRpb246IG5vIHBhdGggc3VwcGxpZWQuJyk7XHJcbiAgfVxyXG5cclxuICBpZiAoIWxvZ2dpbmcuZGVzdC5lbmRzV2l0aCgnLycpKSB7XHJcbiAgICBsb2dnaW5nLmRlc3QgKz0gJy8nO1xyXG4gIH1cclxufTtcclxuXHJcbi8qKlxyXG4gKiBJbml0aWFsaXplcyBsb2dnaW5nIHdpdGggdGhlIHNwZWNpZmllZCBsb2dnaW5nIGNvbmZpZ3VyYXRpb24uXHJcbiAqXHJcbiAqIEBwYXJhbSB7T2JqZWN0fSBsb2dnaW5nIC0gVGhlIGxvZ2dpbmcgY29uZmlndXJhdGlvbiBvYmplY3QuXHJcbiAqL1xyXG5leHBvcnQgY29uc3QgaW5pdExvZ2dpbmcgPSAobG9nZ2luZykgPT4ge1xyXG4gIC8vIFNldCB0aGUgbG9nIGxldmVsXHJcbiAgc2V0TG9nTGV2ZWwobG9nZ2luZyAmJiBwYXJzZUludChsb2dnaW5nLmxldmVsKSk7XHJcblxyXG4gIC8vIFNldCB0aGUgbG9nIGZpbGUgcGF0aCBhbmQgbmFtZVxyXG4gIGlmIChsb2dnaW5nICYmIGxvZ2dpbmcuZGVzdCkge1xyXG4gICAgZW5hYmxlRmlsZUxvZ2dpbmcoXHJcbiAgICAgIGxvZ2dpbmcuZGVzdCxcclxuICAgICAgbG9nZ2luZy5maWxlIHx8ICdoaWdoY2hhcnRzLWV4cG9ydC1zZXJ2ZXIubG9nJ1xyXG4gICAgKTtcclxuICB9XHJcbn07XHJcblxyXG4vKipcclxuICogQWRkcyBhIGxpc3RlbmVyIGZ1bmN0aW9uIHRvIHRoZSBsb2dnaW5nIHN5c3RlbS5cclxuICpcclxuICogQHBhcmFtIHtmdW5jdGlvbn0gZm4gLSBUaGUgbGlzdGVuZXIgZnVuY3Rpb24gdG8gYmUgYWRkZWQuXHJcbiAqL1xyXG5leHBvcnQgY29uc3QgbGlzdGVuID0gKGZuKSA9PiB7XHJcbiAgbG9nZ2luZy5saXN0ZW5lcnMucHVzaChmbik7XHJcbn07XHJcblxyXG4vKipcclxuICogVG9nZ2xlcyB0aGUgc3RhbmRhcmQgb3V0cHV0IChjb25zb2xlKSBsb2dnaW5nLlxyXG4gKlxyXG4gKiBAcGFyYW0ge2Jvb2xlYW59IGVuYWJsZWQgLSBJZiB0cnVlLCBlbmFibGVzIGNvbnNvbGUgbG9nZ2luZzsgaWYgZmFsc2UsXHJcbiAqIGRpc2FibGVzIGl0LlxyXG4gKi9cclxuZXhwb3J0IGNvbnN0IHRvZ2dsZVNURE91dCA9IChlbmFibGVkKSA9PiB7XHJcbiAgbG9nZ2luZy50b0NvbnNvbGUgPSBlbmFibGVkO1xyXG59O1xyXG5cclxuZXhwb3J0IGRlZmF1bHQge1xyXG4gIGxvZyxcclxuICBsb2dXaXRoU3RhY2ssXHJcbiAgc2V0TG9nTGV2ZWwsXHJcbiAgZW5hYmxlRmlsZUxvZ2dpbmcsXHJcbiAgaW5pdExvZ2dpbmcsXHJcbiAgbGlzdGVuLFxyXG4gIHRvZ2dsZVNURE91dFxyXG59O1xyXG4iLCIvKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKlxyXG5cclxuSGlnaGNoYXJ0cyBFeHBvcnQgU2VydmVyXHJcblxyXG5Db3B5cmlnaHQgKGMpIDIwMTYtMjAyNCwgSGlnaHNvZnRcclxuXHJcbkxpY2VuY2VkIHVuZGVyIHRoZSBNSVQgbGljZW5jZS5cclxuXHJcbkFkZGl0aW9uYWxseSBhIHZhbGlkIEhpZ2hjaGFydHMgbGljZW5zZSBpcyByZXF1aXJlZCBmb3IgdXNlLlxyXG5cclxuU2VlIExJQ0VOU0UgZmlsZSBpbiByb290IGZvciBkZXRhaWxzLlxyXG5cclxuKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKi9cclxuXHJcbmltcG9ydCB7IHJlYWRGaWxlU3luYyB9IGZyb20gJ2ZzJztcclxuaW1wb3J0IHsgam9pbiB9IGZyb20gJ3BhdGgnO1xyXG5pbXBvcnQgeyBmaWxlVVJMVG9QYXRoIH0gZnJvbSAndXJsJztcclxuXHJcbmltcG9ydCB7IGRlZmF1bHRDb25maWcgfSBmcm9tICcuLi9saWIvc2NoZW1hcy9jb25maWcuanMnO1xyXG5pbXBvcnQgeyBsb2csIGxvZ1dpdGhTdGFjayB9IGZyb20gJy4vbG9nZ2VyLmpzJztcclxuXHJcbmNvbnN0IE1BWF9CQUNLT0ZGX0FUVEVNUFRTID0gNjtcclxuXHJcbmV4cG9ydCBjb25zdCBfX2Rpcm5hbWUgPSBmaWxlVVJMVG9QYXRoKG5ldyBVUkwoJy4uLy4nLCBpbXBvcnQubWV0YS51cmwpKTtcclxuXHJcbi8qKlxyXG4gKiBDbGVhcnMgYW5kIHN0YW5kYXJkaXplcyB0ZXh0IGJ5IHJlcGxhY2luZyBtdWx0aXBsZSBjb25zZWN1dGl2ZSB3aGl0ZXNwYWNlXHJcbiAqIGNoYXJhY3RlcnMgd2l0aCBhIHNpbmdsZSBzcGFjZSBhbmQgdHJpbW1pbmcgYW55IGxlYWRpbmcgb3IgdHJhaWxpbmdcclxuICogd2hpdGVzcGFjZS5cclxuICpcclxuICogQHBhcmFtIHtzdHJpbmd9IHRleHQgLSBUaGUgaW5wdXQgdGV4dCB0byBiZSBjbGVhcmVkLlxyXG4gKiBAcGFyYW0ge1JlZ0V4cH0gW3J1bGU9L1xcc1xccysvZ10gLSBUaGUgcmVndWxhciBleHByZXNzaW9uIHJ1bGUgdG8gbWF0Y2hcclxuICogbXVsdGlwbGUgY29uc2VjdXRpdmUgd2hpdGVzcGFjZSBjaGFyYWN0ZXJzLlxyXG4gKiBAcGFyYW0ge3N0cmluZ30gW3JlcGxhY2VyPScgJ10gLSBUaGUgc3RyaW5nIHVzZWQgdG8gcmVwbGFjZSBtdWx0aXBsZVxyXG4gKiBjb25zZWN1dGl2ZSB3aGl0ZXNwYWNlIGNoYXJhY3RlcnMuXHJcbiAqXHJcbiAqIEByZXR1cm5zIHtzdHJpbmd9IC0gVGhlIGNsZWFyZWQgYW5kIHN0YW5kYXJkaXplZCB0ZXh0LlxyXG4gKi9cclxuZXhwb3J0IGNvbnN0IGNsZWFyVGV4dCA9ICh0ZXh0LCBydWxlID0gL1xcc1xccysvZywgcmVwbGFjZXIgPSAnICcpID0+XHJcbiAgdGV4dC5yZXBsYWNlQWxsKHJ1bGUsIHJlcGxhY2VyKS50cmltKCk7XHJcblxyXG4vKipcclxuICogSW1wbGVtZW50cyBhbiBleHBvbmVudGlhbCBiYWNrb2ZmIHN0cmF0ZWd5IGZvciByZXRyeWluZyBhIGZ1bmN0aW9uIHVudGlsXHJcbiAqIGEgY2VydGFpbiBudW1iZXIgb2YgYXR0ZW1wdHMgYXJlIHJlYWNoZWQuXHJcbiAqXHJcbiAqIEBwYXJhbSB7RnVuY3Rpb259IGZuIC0gVGhlIGZ1bmN0aW9uIHRvIGJlIHJldHJpZWQuXHJcbiAqIEBwYXJhbSB7bnVtYmVyfSBbYXR0ZW1wdD0wXSAtIFRoZSBjdXJyZW50IGF0dGVtcHQgbnVtYmVyLlxyXG4gKiBAcGFyYW0gey4uLmFueX0gYXJncyAtIEFyZ3VtZW50cyB0byBiZSBwYXNzZWQgdG8gdGhlIGZ1bmN0aW9uLlxyXG4gKlxyXG4gKiBAcmV0dXJucyB7UHJvbWlzZX0gLSBBIHByb21pc2UgdGhhdCByZXNvbHZlcyB0byB0aGUgcmVzdWx0IG9mIHRoZSBmdW5jdGlvblxyXG4gKiBpZiBzdWNjZXNzZnVsLlxyXG4gKlxyXG4gKiBAdGhyb3dzIHtFcnJvcn0gLSBUaHJvd3MgYW4gZXJyb3IgaWYgdGhlIG1heGltdW0gbnVtYmVyIG9mIGF0dGVtcHRzXHJcbiAqIGlzIHJlYWNoZWQuXHJcbiAqL1xyXG5leHBvcnQgY29uc3QgZXhwQmFja29mZiA9IGFzeW5jIChmbiwgYXR0ZW1wdCA9IDAsIC4uLmFyZ3MpID0+IHtcclxuICB0cnkge1xyXG4gICAgLy8gVHJ5IHRvIGNhbGwgdGhlIGZ1bmN0aW9uXHJcbiAgICByZXR1cm4gYXdhaXQgZm4oLi4uYXJncyk7XHJcbiAgfSBjYXRjaCAoZXJyb3IpIHtcclxuICAgIC8vIENhbGN1bGF0ZSBkZWxheSBpbiBtc1xyXG4gICAgY29uc3QgZGVsYXlJbk1zID0gMiAqKiBhdHRlbXB0ICogMTAwMDtcclxuXHJcbiAgICAvLyBJZiB0aGUgYXR0ZW1wdCBleGNlZWRzIHRoZSBtYXhpbXVtIGF0dGVtcHRzIG9mIHJlYXBlYXQsIHRocm93IGFuIGVycm9yXHJcbiAgICBpZiAoKythdHRlbXB0ID49IE1BWF9CQUNLT0ZGX0FUVEVNUFRTKSB7XHJcbiAgICAgIHRocm93IGVycm9yO1xyXG4gICAgfVxyXG5cclxuICAgIC8vIFdhaXQgZ2l2ZW4gYW1vdW50IG9mIHRpbWVcclxuICAgIGF3YWl0IG5ldyBQcm9taXNlKChyZXNwb25zZSkgPT4gc2V0VGltZW91dChyZXNwb25zZSwgZGVsYXlJbk1zKSk7XHJcbiAgICBsb2coXHJcbiAgICAgIDMsXHJcbiAgICAgIGBbcG9vbF0gV2FpdGVkICR7ZGVsYXlJbk1zfW1zIHVudGlsIG5leHQgY2FsbCBmb3IgdGhlIHJlc291cmNlIGlkOiAke2FyZ3NbMF19LmBcclxuICAgICk7XHJcblxyXG4gICAgLy8gVHJ5IGFnYWluXHJcbiAgICByZXR1cm4gZXhwQmFja29mZihmbiwgYXR0ZW1wdCwgLi4uYXJncyk7XHJcbiAgfVxyXG59O1xyXG5cclxuLyoqXHJcbiAqIEZpeGVzIHRoZSBleHBvcnQgdHlwZSBiYXNlZCBvbiBNSU1FIHR5cGVzIGFuZCBmaWxlIGV4dGVuc2lvbnMuXHJcbiAqXHJcbiAqIEBwYXJhbSB7c3RyaW5nfSB0eXBlIC0gVGhlIG9yaWdpbmFsIGV4cG9ydCB0eXBlLlxyXG4gKiBAcGFyYW0ge3N0cmluZ30gb3V0ZmlsZSAtIFRoZSBmaWxlIHBhdGggb3IgbmFtZS5cclxuICpcclxuICogQHJldHVybnMge3N0cmluZ30gLSBUaGUgY29ycmVjdGVkIGV4cG9ydCB0eXBlLlxyXG4gKi9cclxuZXhwb3J0IGNvbnN0IGZpeFR5cGUgPSAodHlwZSwgb3V0ZmlsZSkgPT4ge1xyXG4gIC8vIE1JTUUgdHlwZXNcclxuICBjb25zdCBtaW1lVHlwZXMgPSB7XHJcbiAgICAnaW1hZ2UvcG5nJzogJ3BuZycsXHJcbiAgICAnaW1hZ2UvanBlZyc6ICdqcGVnJyxcclxuICAgICdhcHBsaWNhdGlvbi9wZGYnOiAncGRmJyxcclxuICAgICdpbWFnZS9zdmcreG1sJzogJ3N2ZydcclxuICB9O1xyXG5cclxuICAvLyBGb3JtYXRzXHJcbiAgY29uc3QgZm9ybWF0cyA9IFsncG5nJywgJ2pwZWcnLCAncGRmJywgJ3N2ZyddO1xyXG5cclxuICAvLyBDaGVjayBpZiB0eXBlIGFuZCBvdXRmaWxlJ3MgZXh0ZW5zaW9ucyBhcmUgdGhlIHNhbWVcclxuICBpZiAob3V0ZmlsZSkge1xyXG4gICAgY29uc3Qgb3V0VHlwZSA9IG91dGZpbGUuc3BsaXQoJy4nKS5wb3AoKTtcclxuXHJcbiAgICBpZiAob3V0VHlwZSA9PT0gJ2pwZycpIHtcclxuICAgICAgdHlwZSA9ICdqcGVnJztcclxuICAgIH0gZWxzZSBpZiAoZm9ybWF0cy5pbmNsdWRlcyhvdXRUeXBlKSAmJiB0eXBlICE9PSBvdXRUeXBlKSB7XHJcbiAgICAgIHR5cGUgPSBvdXRUeXBlO1xyXG4gICAgfVxyXG4gIH1cclxuXHJcbiAgLy8gUmV0dXJuIGEgY29ycmVjdCB0eXBlXHJcbiAgcmV0dXJuIG1pbWVUeXBlc1t0eXBlXSB8fCBmb3JtYXRzLmZpbmQoKHQpID0+IHQgPT09IHR5cGUpIHx8ICdwbmcnO1xyXG59O1xyXG5cclxuLyoqXHJcbiAqIEhhbmRsZXMgYW5kIHZhbGlkYXRlcyByZXNvdXJjZXMgZm9yIGV4cG9ydC5cclxuICpcclxuICogQHBhcmFtIHtPYmplY3R8c3RyaW5nfSByZXNvdXJjZXMgLSBUaGUgcmVzb3VyY2VzIHRvIGJlIGhhbmRsZWQuIENhbiBiZSBlaXRoZXJcclxuICogYSBKU09OIG9iamVjdCwgc3RyaW5naWZpZWQgSlNPTiBvciBhIHBhdGggdG8gYSBKU09OIGZpbGUuXHJcbiAqIEBwYXJhbSB7Ym9vbGVhbn0gYWxsb3dGaWxlUmVzb3VyY2VzIC0gV2hldGhlciB0byBhbGxvdyBsb2FkaW5nIHJlc291cmNlcyBmcm9tXHJcbiAqIGZpbGVzLlxyXG4gKlxyXG4gKiBAcmV0dXJucyB7T2JqZWN0fHVuZGVmaW5lZH0gLSBUaGUgaGFuZGxlZCByZXNvdXJjZXMgb3IgdW5kZWZpbmVkIGlmIG5vIHZhbGlkXHJcbiAqIHJlc291cmNlcyBhcmUgZm91bmQuXHJcbiAqL1xyXG5leHBvcnQgY29uc3QgaGFuZGxlUmVzb3VyY2VzID0gKHJlc291cmNlcyA9IGZhbHNlLCBhbGxvd0ZpbGVSZXNvdXJjZXMpID0+IHtcclxuICBjb25zdCBhbGxvd2VkUHJvcHMgPSBbJ2pzJywgJ2NzcycsICdmaWxlcyddO1xyXG5cclxuICBsZXQgaGFuZGxlZFJlc291cmNlcyA9IHJlc291cmNlcztcclxuICBsZXQgY29ycmVjdFJlc291cmNlcyA9IGZhbHNlO1xyXG5cclxuICAvLyBUcnkgdG8gbG9hZCByZXNvdXJjZXMgZnJvbSBhIGZpbGVcclxuICBpZiAoYWxsb3dGaWxlUmVzb3VyY2VzICYmIHJlc291cmNlcy5lbmRzV2l0aCgnLmpzb24nKSkge1xyXG4gICAgdHJ5IHtcclxuICAgICAgaGFuZGxlZFJlc291cmNlcyA9IGlzQ29ycmVjdEpTT04ocmVhZEZpbGVTeW5jKHJlc291cmNlcywgJ3V0ZjgnKSk7XHJcbiAgICB9IGNhdGNoIChlcnJvcikge1xyXG4gICAgICByZXR1cm4gbG9nV2l0aFN0YWNrKDIsIGVycm9yLCBgW2NsaV0gTm8gcmVzb3VyY2VzIGZvdW5kLmApO1xyXG4gICAgfVxyXG4gIH0gZWxzZSB7XHJcbiAgICAvLyBUcnkgdG8gZ2V0IEpTT05cclxuICAgIGhhbmRsZWRSZXNvdXJjZXMgPSBpc0NvcnJlY3RKU09OKHJlc291cmNlcyk7XHJcblxyXG4gICAgLy8gR2V0IHJpZCBvZiB0aGUgZmlsZXMgc2VjdGlvblxyXG4gICAgaWYgKGhhbmRsZWRSZXNvdXJjZXMgJiYgIWFsbG93RmlsZVJlc291cmNlcykge1xyXG4gICAgICBkZWxldGUgaGFuZGxlZFJlc291cmNlcy5maWxlcztcclxuICAgIH1cclxuICB9XHJcblxyXG4gIC8vIEZpbHRlciBmcm9tIHVubmVjZXNzYXJ5IHByb3BlcnRpZXNcclxuICBmb3IgKGNvbnN0IHByb3BOYW1lIGluIGhhbmRsZWRSZXNvdXJjZXMpIHtcclxuICAgIGlmICghYWxsb3dlZFByb3BzLmluY2x1ZGVzKHByb3BOYW1lKSkge1xyXG4gICAgICBkZWxldGUgaGFuZGxlZFJlc291cmNlc1twcm9wTmFtZV07XHJcbiAgICB9IGVsc2UgaWYgKCFjb3JyZWN0UmVzb3VyY2VzKSB7XHJcbiAgICAgIGNvcnJlY3RSZXNvdXJjZXMgPSB0cnVlO1xyXG4gICAgfVxyXG4gIH1cclxuXHJcbiAgLy8gQ2hlY2sgaWYgYXQgbGVhc3Qgb25lIG9mIGFsbG93ZWQgcHJvcGVydGllcyBpcyBwcmVzZW50XHJcbiAgaWYgKCFjb3JyZWN0UmVzb3VyY2VzKSB7XHJcbiAgICByZXR1cm4gbG9nKDMsIGBbY2xpXSBObyByZXNvdXJjZXMgZm91bmQuYCk7XHJcbiAgfVxyXG5cclxuICAvLyBIYW5kbGUgZmlsZXMgc2VjdGlvblxyXG4gIGlmIChoYW5kbGVkUmVzb3VyY2VzLmZpbGVzKSB7XHJcbiAgICBoYW5kbGVkUmVzb3VyY2VzLmZpbGVzID0gaGFuZGxlZFJlc291cmNlcy5maWxlcy5tYXAoKGl0ZW0pID0+IGl0ZW0udHJpbSgpKTtcclxuICAgIGlmICghaGFuZGxlZFJlc291cmNlcy5maWxlcyB8fCBoYW5kbGVkUmVzb3VyY2VzLmZpbGVzLmxlbmd0aCA8PSAwKSB7XHJcbiAgICAgIGRlbGV0ZSBoYW5kbGVkUmVzb3VyY2VzLmZpbGVzO1xyXG4gICAgfVxyXG4gIH1cclxuXHJcbiAgLy8gUmV0dXJuIHJlc291cmNlc1xyXG4gIHJldHVybiBoYW5kbGVkUmVzb3VyY2VzO1xyXG59O1xyXG5cclxuLyoqXHJcbiAqIFZhbGlkYXRlcyBhbmQgcGFyc2VzIEpTT04gZGF0YS4gQ2hlY2tzIGlmIHByb3ZpZGVkIGRhdGEgaXMgb3IgY2FuXHJcbiAqIGJlIGEgY29ycmVjdCBKU09OLiBJZiBhIHByaW1pdGl2ZSBpcyBwcm92aWRlZCwgaXQgaXMgc3RyaW5naWZpZWQgYW5kIHJldHVybmVkLlxyXG4gKlxyXG4gKiBAcGFyYW0ge09iamVjdHxzdHJpbmd9IGRhdGEgLSBUaGUgSlNPTiBkYXRhIHRvIGJlIHZhbGlkYXRlZCBhbmQgcGFyc2VkLlxyXG4gKiBAcGFyYW0ge2Jvb2xlYW59IHRvU3RyaW5nIC0gV2hldGhlciB0byByZXR1cm4gYSBzdHJpbmdpZmllZCByZXByZXNlbnRhdGlvblxyXG4gKiBvZiB0aGUgcGFyc2VkIEpTT04uXHJcbiAqXHJcbiAqIEByZXR1cm5zIHtPYmplY3R8c3RyaW5nfGJvb2xlYW59IC0gVGhlIHBhcnNlZCBKU09OIG9iamVjdCwgc3RyaW5naWZpZWQgSlNPTixcclxuICogb3IgZmFsc2UgaWYgdmFsaWRhdGlvbiBmYWlscy5cclxuICovXHJcbmV4cG9ydCBmdW5jdGlvbiBpc0NvcnJlY3RKU09OKGRhdGEsIHRvU3RyaW5nKSB7XHJcbiAgdHJ5IHtcclxuICAgIC8vIEdldCB0aGUgc3RyaW5nIHJlcHJlc2VudGF0aW9uIGlmIG5vdCBhbHJlYWR5IGJlZm9yZSBwYXJzaW5nXHJcbiAgICBjb25zdCBwYXJzZWREYXRhID0gSlNPTi5wYXJzZShcclxuICAgICAgdHlwZW9mIGRhdGEgIT09ICdzdHJpbmcnID8gSlNPTi5zdHJpbmdpZnkoZGF0YSkgOiBkYXRhXHJcbiAgICApO1xyXG5cclxuICAgIC8vIFJldHVybiBhIHN0cmluZ2lmaWVkIHJlcHJlc2VudGF0aW9uIG9mIGEgSlNPTiBpZiByZXF1aXJlZFxyXG4gICAgaWYgKHR5cGVvZiBwYXJzZWREYXRhICE9PSAnc3RyaW5nJyAmJiB0b1N0cmluZykge1xyXG4gICAgICByZXR1cm4gSlNPTi5zdHJpbmdpZnkocGFyc2VkRGF0YSk7XHJcbiAgICB9XHJcblxyXG4gICAgLy8gUmV0dXJuIGEgSlNPTlxyXG4gICAgcmV0dXJuIHBhcnNlZERhdGE7XHJcbiAgfSBjYXRjaCB7XHJcbiAgICByZXR1cm4gZmFsc2U7XHJcbiAgfVxyXG59XHJcblxyXG4vKipcclxuICogQ2hlY2tzIGlmIHRoZSBnaXZlbiBpdGVtIGlzIGFuIG9iamVjdC5cclxuICpcclxuICogQHBhcmFtIHthbnl9IGl0ZW0gLSBUaGUgaXRlbSB0byBiZSBjaGVja2VkLlxyXG4gKlxyXG4gKiBAcmV0dXJucyB7Ym9vbGVhbn0gLSBUcnVlIGlmIHRoZSBpdGVtIGlzIGFuIG9iamVjdCwgZmFsc2Ugb3RoZXJ3aXNlLlxyXG4gKi9cclxuZXhwb3J0IGNvbnN0IGlzT2JqZWN0ID0gKGl0ZW0pID0+XHJcbiAgdHlwZW9mIGl0ZW0gPT09ICdvYmplY3QnICYmICFBcnJheS5pc0FycmF5KGl0ZW0pICYmIGl0ZW0gIT09IG51bGw7XHJcblxyXG4vKipcclxuICogQ2hlY2tzIGlmIHRoZSBnaXZlbiBvYmplY3QgaXMgZW1wdHkuXHJcbiAqXHJcbiAqIEBwYXJhbSB7T2JqZWN0fSBpdGVtIC0gVGhlIG9iamVjdCB0byBiZSBjaGVja2VkLlxyXG4gKlxyXG4gKiBAcmV0dXJucyB7Ym9vbGVhbn0gLSBUcnVlIGlmIHRoZSBvYmplY3QgaXMgZW1wdHksIGZhbHNlIG90aGVyd2lzZS5cclxuICovXHJcbmV4cG9ydCBjb25zdCBpc09iamVjdEVtcHR5ID0gKGl0ZW0pID0+XHJcbiAgdHlwZW9mIGl0ZW0gPT09ICdvYmplY3QnICYmXHJcbiAgIUFycmF5LmlzQXJyYXkoaXRlbSkgJiZcclxuICBpdGVtICE9PSBudWxsICYmXHJcbiAgT2JqZWN0LmtleXMoaXRlbSkubGVuZ3RoID09PSAwO1xyXG5cclxuLyoqXHJcbiAqIENoZWNrcyBpZiBhIHByaXZhdGUgSVAgcmFuZ2UgVVJMIGlzIGZvdW5kIGluIHRoZSBnaXZlbiBzdHJpbmcuXHJcbiAqXHJcbiAqIEBwYXJhbSB7c3RyaW5nfSBpdGVtIC0gVGhlIHN0cmluZyB0byBiZSBjaGVja2VkIGZvciBhIHByaXZhdGUgSVAgcmFuZ2UgVVJMLlxyXG4gKlxyXG4gKiBAcmV0dXJucyB7Ym9vbGVhbn0gLSBUcnVlIGlmIGEgcHJpdmF0ZSBJUCByYW5nZSBVUkwgaXMgZm91bmQsIGZhbHNlXHJcbiAqIG90aGVyd2lzZS5cclxuICovXHJcbmV4cG9ydCBjb25zdCBpc1ByaXZhdGVSYW5nZVVybEZvdW5kID0gKGl0ZW0pID0+IHtcclxuICBjb25zdCByZWdleFBhdHRlcm5zID0gW1xyXG4gICAgL3hsaW5rOmhyZWY9XCIoPzpodHRwOlxcL1xcL3xodHRwczpcXC9cXC8pP2xvY2FsaG9zdFxcYi8sXHJcbiAgICAveGxpbms6aHJlZj1cIig/Omh0dHA6XFwvXFwvfGh0dHBzOlxcL1xcLyk/MTBcXC5cXGR7MSwzfVxcLlxcZHsxLDN9XFwuXFxkezEsM31cXGIvLFxyXG4gICAgL3hsaW5rOmhyZWY9XCIoPzpodHRwOlxcL1xcL3xodHRwczpcXC9cXC8pPzEyN1xcLlxcZHsxLDN9XFwuXFxkezEsM31cXC5cXGR7MSwzfVxcYi8sXHJcbiAgICAveGxpbms6aHJlZj1cIig/Omh0dHA6XFwvXFwvfGh0dHBzOlxcL1xcLyk/MTcyXFwuKDFbNi05XXwyWzAtOV18M1swLTFdKVxcLlxcZHsxLDN9XFwuXFxkezEsM31cXGIvLFxyXG4gICAgL3hsaW5rOmhyZWY9XCIoPzpodHRwOlxcL1xcL3xodHRwczpcXC9cXC8pPzE5MlxcLjE2OFxcLlxcZHsxLDN9XFwuXFxkezEsM31cXGIvXHJcbiAgXTtcclxuXHJcbiAgcmV0dXJuIHJlZ2V4UGF0dGVybnMuc29tZSgocGF0dGVybikgPT4gcGF0dGVybi50ZXN0KGl0ZW0pKTtcclxufTtcclxuXHJcbi8qKlxyXG4gKiBDcmVhdGVzIGEgZGVlcCBjb3B5IG9mIHRoZSBnaXZlbiBvYmplY3Qgb3IgYXJyYXkuXHJcbiAqXHJcbiAqIEBwYXJhbSB7T2JqZWN0fEFycmF5fSBvYmogLSBUaGUgb2JqZWN0IG9yIGFycmF5IHRvIGJlIGRlZXBseSBjb3BpZWQuXHJcbiAqXHJcbiAqIEByZXR1cm5zIHtPYmplY3R8QXJyYXl9IC0gVGhlIGRlZXAgY29weSBvZiB0aGUgcHJvdmlkZWQgb2JqZWN0IG9yIGFycmF5LlxyXG4gKi9cclxuZXhwb3J0IGNvbnN0IGRlZXBDb3B5ID0gKG9iaikgPT4ge1xyXG4gIGlmIChvYmogPT09IG51bGwgfHwgdHlwZW9mIG9iaiAhPT0gJ29iamVjdCcpIHtcclxuICAgIHJldHVybiBvYmo7XHJcbiAgfVxyXG5cclxuICBjb25zdCBjb3B5ID0gQXJyYXkuaXNBcnJheShvYmopID8gW10gOiB7fTtcclxuXHJcbiAgZm9yIChjb25zdCBrZXkgaW4gb2JqKSB7XHJcbiAgICBpZiAoT2JqZWN0LnByb3RvdHlwZS5oYXNPd25Qcm9wZXJ0eS5jYWxsKG9iaiwga2V5KSkge1xyXG4gICAgICBjb3B5W2tleV0gPSBkZWVwQ29weShvYmpba2V5XSk7XHJcbiAgICB9XHJcbiAgfVxyXG5cclxuICByZXR1cm4gY29weTtcclxufTtcclxuXHJcbi8qKlxyXG4gKiBDb252ZXJ0cyB0aGUgcHJvdmlkZWQgb3B0aW9ucyBvYmplY3QgdG8gYSBKU09OLWZvcm1hdHRlZCBzdHJpbmcgd2l0aCB0aGVcclxuICogb3B0aW9uIHRvIHByZXNlcnZlIGZ1bmN0aW9ucy5cclxuICpcclxuICogQHBhcmFtIHtPYmplY3R9IG9wdGlvbnMgLSBUaGUgb3B0aW9ucyBvYmplY3QgdG8gYmUgY29udmVydGVkIHRvIGEgc3RyaW5nLlxyXG4gKiBAcGFyYW0ge2Jvb2xlYW59IGFsbG93RnVuY3Rpb25zIC0gSWYgc2V0IHRvIHRydWUsIGZ1bmN0aW9ucyBhcmUgcHJlc2VydmVkXHJcbiAqIGluIHRoZSBvdXRwdXQuXHJcbiAqXHJcbiAqIEByZXR1cm5zIHtzdHJpbmd9IC0gVGhlIEpTT04tZm9ybWF0dGVkIHN0cmluZyByZXByZXNlbnRpbmcgdGhlIG9wdGlvbnMuXHJcbiAqL1xyXG5leHBvcnQgY29uc3Qgb3B0aW9uc1N0cmluZ2lmeSA9IChvcHRpb25zLCBhbGxvd0Z1bmN0aW9ucykgPT4ge1xyXG4gIGNvbnN0IHJlcGxhY2VyQ2FsbGJhY2sgPSAobmFtZSwgdmFsdWUpID0+IHtcclxuICAgIGlmICh0eXBlb2YgdmFsdWUgPT09ICdzdHJpbmcnKSB7XHJcbiAgICAgIHZhbHVlID0gdmFsdWUudHJpbSgpO1xyXG5cclxuICAgICAgLy8gSWYgYWxsb3dGdW5jdGlvbnMgaXMgc2V0IHRvIHRydWUsIHByZXNlcnZlIGZ1bmN0aW9uc1xyXG4gICAgICBpZiAoXHJcbiAgICAgICAgKHZhbHVlLnN0YXJ0c1dpdGgoJ2Z1bmN0aW9uKCcpIHx8IHZhbHVlLnN0YXJ0c1dpdGgoJ2Z1bmN0aW9uICgnKSkgJiZcclxuICAgICAgICB2YWx1ZS5lbmRzV2l0aCgnfScpXHJcbiAgICAgICkge1xyXG4gICAgICAgIHZhbHVlID0gYWxsb3dGdW5jdGlvbnNcclxuICAgICAgICAgID8gYEVYUF9GVU4keyh2YWx1ZSArICcnKS5yZXBsYWNlQWxsKC9cXG58XFx0fFxcci9nLCAnICcpfUVYUF9GVU5gXHJcbiAgICAgICAgICA6IHVuZGVmaW5lZDtcclxuICAgICAgfVxyXG4gICAgfVxyXG5cclxuICAgIHJldHVybiB0eXBlb2YgdmFsdWUgPT09ICdmdW5jdGlvbidcclxuICAgICAgPyBgRVhQX0ZVTiR7KHZhbHVlICsgJycpLnJlcGxhY2VBbGwoL1xcbnxcXHR8XFxyL2csICcgJyl9RVhQX0ZVTmBcclxuICAgICAgOiB2YWx1ZTtcclxuICB9O1xyXG5cclxuICAvLyBTdHJpbmdpZnkgb3B0aW9ucyBhbmQgaWYgcmVxdWlyZWQsIHJlcGxhY2Ugc3BlY2lhbCBmdW5jdGlvbnMgbWFya3NcclxuICByZXR1cm4gSlNPTi5zdHJpbmdpZnkob3B0aW9ucywgcmVwbGFjZXJDYWxsYmFjaykucmVwbGFjZUFsbChcclxuICAgIC9cIkVYUF9GVU58RVhQX0ZVTlwiL2csXHJcbiAgICAnJ1xyXG4gICk7XHJcbn07XHJcblxyXG4vKipcclxuICogUHJpbnRzIHRoZSBIaWdoY2hhcnRzIEV4cG9ydCBTZXJ2ZXIgbG9nbyBhbmQgdmVyc2lvbiBpbmZvcm1hdGlvbi5cclxuICpcclxuICogQHBhcmFtIHtib29sZWFufSBub0xvZ28gLSBJZiB0cnVlLCBvbmx5IHByaW50cyB2ZXJzaW9uIGluZm9ybWF0aW9uIHdpdGhvdXRcclxuICogdGhlIGxvZ28uXHJcbiAqL1xyXG5leHBvcnQgY29uc3QgcHJpbnRMb2dvID0gKG5vTG9nbykgPT4ge1xyXG4gIC8vIEdldCBwYWNrYWdlIHZlcnNpb24gZWl0aGVyIGZyb20gZW52IG9yIGZyb20gcGFja2FnZS5qc29uXHJcbiAgY29uc3QgcGFja2FnZVZlcnNpb24gPSBKU09OLnBhcnNlKFxyXG4gICAgcmVhZEZpbGVTeW5jKGpvaW4oX19kaXJuYW1lLCAncGFja2FnZS5qc29uJykpXHJcbiAgKS52ZXJzaW9uO1xyXG5cclxuICAvLyBQcmludCB0ZXh0IG9ubHlcclxuICBpZiAobm9Mb2dvKSB7XHJcbiAgICBjb25zb2xlLmxvZyhgU3RhcnRpbmcgSGlnaGNoYXJ0cyBFeHBvcnQgU2VydmVyIHYke3BhY2thZ2VWZXJzaW9ufS4uLmApO1xyXG4gICAgcmV0dXJuO1xyXG4gIH1cclxuXHJcbiAgLy8gUHJpbnQgdGhlIGxvZ29cclxuICBjb25zb2xlLmxvZyhcclxuICAgIHJlYWRGaWxlU3luYyhfX2Rpcm5hbWUgKyAnL21zZy9zdGFydHVwLm1zZycpLnRvU3RyaW5nKCkuYm9sZC55ZWxsb3csXHJcbiAgICBgdiR7cGFja2FnZVZlcnNpb259YFxyXG4gICk7XHJcbn07XHJcblxyXG4vKipcclxuICogUHJpbnRzIHRoZSB1c2FnZSBpbmZvcm1hdGlvbiBmb3IgQ0xJIGFyZ3VtZW50cy4gSWYgcmVxdWlyZWQsIGl0IGNhbiBsaXN0XHJcbiAqIHByb3BlcnRpZXMgcmVjdXJzaXZlbHlcclxuICovXHJcbmV4cG9ydCBmdW5jdGlvbiBwcmludFVzYWdlKCkge1xyXG4gIGNvbnN0IHBhZCA9IDQ4O1xyXG4gIGNvbnN0IHJlYWRtZSA9ICdodHRwczovL2dpdGh1Yi5jb20vaGlnaGNoYXJ0cy9ub2RlLWV4cG9ydC1zZXJ2ZXIjcmVhZG1lJztcclxuXHJcbiAgLy8gRGlzcGxheSByZWFkbWUgaW5mb3JtYXRpb25cclxuICBjb25zb2xlLmxvZyhcclxuICAgICdcXG5Vc2FnZSBvZiBDTEkgYXJndW1lbnRzOicuYm9sZCxcclxuICAgICdcXG4tLS0tLS0nLFxyXG4gICAgYFxcbkZvciBtb3JlIGRldGFpbGVkIGluZm9ybWF0aW9uLCB2aXNpdCB0aGUgcmVhZG1lIGF0OiAke3JlYWRtZS5ib2xkLnllbGxvd30uYFxyXG4gICk7XHJcblxyXG4gIGNvbnN0IGN5Y2xlQ2F0ZWdvcmllcyA9IChvcHRpb25zKSA9PiB7XHJcbiAgICBmb3IgKGNvbnN0IFtuYW1lLCBvcHRpb25dIG9mIE9iamVjdC5lbnRyaWVzKG9wdGlvbnMpKSB7XHJcbiAgICAgIC8vIElmIGNhdGVnb3J5IGhhcyBtb3JlIGxldmVscywgZ28gZnVydGhlclxyXG4gICAgICBpZiAoIU9iamVjdC5wcm90b3R5cGUuaGFzT3duUHJvcGVydHkuY2FsbChvcHRpb24sICd2YWx1ZScpKSB7XHJcbiAgICAgICAgY3ljbGVDYXRlZ29yaWVzKG9wdGlvbik7XHJcbiAgICAgIH0gZWxzZSB7XHJcbiAgICAgICAgbGV0IGRlc2NOYW1lID0gYCAgLS0ke29wdGlvbi5jbGlOYW1lIHx8IG5hbWV9ICR7XHJcbiAgICAgICAgICAoJzwnICsgb3B0aW9uLnR5cGUgKyAnPicpLmdyZWVuXHJcbiAgICAgICAgfSBgO1xyXG4gICAgICAgIGlmIChkZXNjTmFtZS5sZW5ndGggPCBwYWQpIHtcclxuICAgICAgICAgIGZvciAobGV0IGkgPSBkZXNjTmFtZS5sZW5ndGg7IGkgPCBwYWQ7IGkrKykge1xyXG4gICAgICAgICAgICBkZXNjTmFtZSArPSAnLic7XHJcbiAgICAgICAgICB9XHJcbiAgICAgICAgfVxyXG5cclxuICAgICAgICAvLyBEaXNwbGF5IGNvcnJlY3RseSBhbGlnbmVkIG1lc3NhZ2VzXHJcbiAgICAgICAgY29uc29sZS5sb2coXHJcbiAgICAgICAgICBkZXNjTmFtZSxcclxuICAgICAgICAgIG9wdGlvbi5kZXNjcmlwdGlvbixcclxuICAgICAgICAgIGBbRGVmYXVsdDogJHtvcHRpb24udmFsdWUudG9TdHJpbmcoKS5ib2xkfV1gLmJsdWVcclxuICAgICAgICApO1xyXG4gICAgICB9XHJcbiAgICB9XHJcbiAgfTtcclxuXHJcbiAgLy8gQ3ljbGUgdGhyb3VnaCBvcHRpb25zIG9mIGVhY2ggY2F0ZWdvcmllcyBhbmQgZGlzcGxheSB0aGUgdXNhZ2UgaW5mb1xyXG4gIE9iamVjdC5rZXlzKGRlZmF1bHRDb25maWcpLmZvckVhY2goKGNhdGVnb3J5KSA9PiB7XHJcbiAgICAvLyBPbmx5IHB1cHBldGVlciBhbmQgaGlnaGNoYXJ0cyBjYXRlZ29yaWVzIGNhbm5vdCBiZSBjb25maWd1cmVkIHRocm91Z2ggQ0xJXHJcbiAgICBpZiAoIVsncHVwcGV0ZWVyJywgJ2hpZ2hjaGFydHMnXS5pbmNsdWRlcyhjYXRlZ29yeSkpIHtcclxuICAgICAgY29uc29sZS5sb2coYFxcbiR7Y2F0ZWdvcnkudG9VcHBlckNhc2UoKX1gLnJlZCk7XHJcbiAgICAgIGN5Y2xlQ2F0ZWdvcmllcyhkZWZhdWx0Q29uZmlnW2NhdGVnb3J5XSk7XHJcbiAgICB9XHJcbiAgfSk7XHJcbiAgY29uc29sZS5sb2coJ1xcbicpO1xyXG59XHJcblxyXG4vKipcclxuICogUm91bmRzIGEgbnVtYmVyIHRvIHRoZSBzcGVjaWZpZWQgcHJlY2lzaW9uLlxyXG4gKlxyXG4gKiBAcGFyYW0ge251bWJlcn0gdmFsdWUgLSBUaGUgbnVtYmVyIHRvIGJlIHJvdW5kZWQuXHJcbiAqIEBwYXJhbSB7bnVtYmVyfSBwcmVjaXNpb24gLSBUaGUgbnVtYmVyIG9mIGRlY2ltYWwgcGxhY2VzIHRvIHJvdW5kIHRvLlxyXG4gKlxyXG4gKiBAcmV0dXJucyB7bnVtYmVyfSAtIFRoZSByb3VuZGVkIG51bWJlci5cclxuICovXHJcbmV4cG9ydCBjb25zdCByb3VuZE51bWJlciA9ICh2YWx1ZSwgcHJlY2lzaW9uID0gMSkgPT4ge1xyXG4gIGNvbnN0IG11bHRpcGxpZXIgPSBNYXRoLnBvdygxMCwgcHJlY2lzaW9uIHx8IDApO1xyXG4gIHJldHVybiBNYXRoLnJvdW5kKCt2YWx1ZSAqIG11bHRpcGxpZXIpIC8gbXVsdGlwbGllcjtcclxufTtcclxuXHJcbi8qKlxyXG4gKiBDb252ZXJ0cyBhIHZhbHVlIHRvIGEgYm9vbGVhbi5cclxuICpcclxuICogQHBhcmFtIHthbnl9IGl0ZW0gLSBUaGUgdmFsdWUgdG8gYmUgY29udmVydGVkIHRvIGEgYm9vbGVhbi5cclxuICpcclxuICogQHJldHVybnMge2Jvb2xlYW59IC0gVGhlIGJvb2xlYW4gcmVwcmVzZW50YXRpb24gb2YgdGhlIGlucHV0IHZhbHVlLlxyXG4gKi9cclxuZXhwb3J0IGNvbnN0IHRvQm9vbGVhbiA9IChpdGVtKSA9PlxyXG4gIFsnZmFsc2UnLCAndW5kZWZpbmVkJywgJ251bGwnLCAnTmFOJywgJzAnLCAnJ10uaW5jbHVkZXMoaXRlbSlcclxuICAgID8gZmFsc2VcclxuICAgIDogISFpdGVtO1xyXG5cclxuLyoqXHJcbiAqIFdyYXBzIGN1c3RvbSBjb2RlIHRvIGV4ZWN1dGUgaXQgc2FmZWx5LlxyXG4gKlxyXG4gKiBAcGFyYW0ge3N0cmluZ30gY3VzdG9tQ29kZSAtIFRoZSBjdXN0b20gY29kZSB0byBiZSB3cmFwcGVkLlxyXG4gKiBAcGFyYW0ge2Jvb2xlYW59IGFsbG93RmlsZVJlc291cmNlcyAtIEZsYWcgdG8gYWxsb3cgbG9hZGluZyBjb2RlIGZyb20gYSBmaWxlLlxyXG4gKlxyXG4gKiBAcmV0dXJucyB7c3RyaW5nfGJvb2xlYW59IC0gVGhlIHdyYXBwZWQgY3VzdG9tIGNvZGUgb3IgZmFsc2UgaWYgd3JhcHBpbmdcclxuICogZmFpbHMuXHJcbiAqL1xyXG5leHBvcnQgY29uc3Qgd3JhcEFyb3VuZCA9IChjdXN0b21Db2RlLCBhbGxvd0ZpbGVSZXNvdXJjZXMpID0+IHtcclxuICBpZiAoY3VzdG9tQ29kZSAmJiB0eXBlb2YgY3VzdG9tQ29kZSA9PT0gJ3N0cmluZycpIHtcclxuICAgIGN1c3RvbUNvZGUgPSBjdXN0b21Db2RlLnRyaW0oKTtcclxuXHJcbiAgICBpZiAoY3VzdG9tQ29kZS5lbmRzV2l0aCgnLmpzJykpIHtcclxuICAgICAgcmV0dXJuIGFsbG93RmlsZVJlc291cmNlc1xyXG4gICAgICAgID8gd3JhcEFyb3VuZChyZWFkRmlsZVN5bmMoY3VzdG9tQ29kZSwgJ3V0ZjgnKSlcclxuICAgICAgICA6IGZhbHNlO1xyXG4gICAgfSBlbHNlIGlmIChcclxuICAgICAgY3VzdG9tQ29kZS5zdGFydHNXaXRoKCdmdW5jdGlvbigpJykgfHxcclxuICAgICAgY3VzdG9tQ29kZS5zdGFydHNXaXRoKCdmdW5jdGlvbiAoKScpIHx8XHJcbiAgICAgIGN1c3RvbUNvZGUuc3RhcnRzV2l0aCgnKCk9PicpIHx8XHJcbiAgICAgIGN1c3RvbUNvZGUuc3RhcnRzV2l0aCgnKCkgPT4nKVxyXG4gICAgKSB7XHJcbiAgICAgIHJldHVybiBgKCR7Y3VzdG9tQ29kZX0pKClgO1xyXG4gICAgfVxyXG4gICAgcmV0dXJuIGN1c3RvbUNvZGUucmVwbGFjZSgvOyQvLCAnJyk7XHJcbiAgfVxyXG59O1xyXG5cclxuLyoqXHJcbiAqIFV0aWxpdHkgdG8gbWVhc3VyZSBlbGFwc2VkIHRpbWUgdXNpbmcgdGhlIE5vZGUuanMgcHJvY2Vzcy5ocnRpbWUoKSBtZXRob2QuXHJcbiAqXHJcbiAqIEByZXR1cm5zIHtmdW5jdGlvbigpOiBudW1iZXJ9IC0gQSBmdW5jdGlvbiB0byBjYWxjdWxhdGUgdGhlIGVsYXBzZWQgdGltZVxyXG4gKiBpbiBtaWxsaXNlY29uZHMuXHJcbiAqL1xyXG5leHBvcnQgY29uc3QgbWVhc3VyZVRpbWUgPSAoKSA9PiB7XHJcbiAgY29uc3Qgc3RhcnQgPSBwcm9jZXNzLmhydGltZS5iaWdpbnQoKTtcclxuICByZXR1cm4gKCkgPT4gTnVtYmVyKHByb2Nlc3MuaHJ0aW1lLmJpZ2ludCgpIC0gc3RhcnQpIC8gMTAwMDAwMDtcclxufTtcclxuXHJcbmV4cG9ydCBkZWZhdWx0IHtcclxuICBfX2Rpcm5hbWUsXHJcbiAgY2xlYXJUZXh0LFxyXG4gIGV4cEJhY2tvZmYsXHJcbiAgZml4VHlwZSxcclxuICBoYW5kbGVSZXNvdXJjZXMsXHJcbiAgaXNDb3JyZWN0SlNPTixcclxuICBpc09iamVjdCxcclxuICBpc09iamVjdEVtcHR5LFxyXG4gIGlzUHJpdmF0ZVJhbmdlVXJsRm91bmQsXHJcbiAgb3B0aW9uc1N0cmluZ2lmeSxcclxuICBwcmludExvZ28sXHJcbiAgcHJpbnRVc2FnZSxcclxuICByb3VuZE51bWJlcixcclxuICB0b0Jvb2xlYW4sXHJcbiAgd3JhcEFyb3VuZCxcclxuICBtZWFzdXJlVGltZVxyXG59O1xyXG4iLCIvKipcclxuICogQGZpbGVvdmVydmlld1xyXG4gKiBUaGlzIGZpbGUgaXMgcmVzcG9uc2libGUgZm9yIHBhcnNpbmcgdGhlIGVudmlyb25tZW50IHZhcmlhYmxlcyB3aXRoIHRoZSAnem9kJyBsaWJyYXJ5LlxyXG4gKiBUaGUgcGFyc2VkIGVudmlyb25tZW50IHZhcmlhYmxlcyBhcmUgdGhlbiBleHBvcnRlZCB0byBiZSB1c2VkIGluIHRoZSBhcHBsaWNhdGlvbiBhcyBcImVudnNcIi5cclxuICogV2Ugc2hvdWxkIG5vdCB1c2UgcHJvY2Vzcy5lbnYgZGlyZWN0bHkgaW4gdGhlIGFwcGxpY2F0aW9uIGFzIHRoZXNlIHdvdWxkIG5vdCBiZSBwYXJzZWQgcHJvcGVybHkuXHJcbiAqXHJcbiAqIFRoZSBlbnZpcm9ubWVudCB2YXJpYWJsZXMgYXJlIHBhcnNlZCBhbmQgdmFsaWRhdGVkIG9ubHkgb25jZSB3aGVuIHRoZSBhcHBsaWNhdGlvbiBzdGFydHMuXHJcbiAqIFdlIHNob3VsZCB3cml0ZSBhIGN1c3RvbSB2YWxpZGF0b3Igb3IgYSB0cmFuc2Zvcm1lciBmb3IgZWFjaCBvZiB0aGUgb3B0aW9ucy5cclxuICpcclxuICogRm9yIGVudnMgbm90IGRlZmluZWQgaW4gY29uZmlnLmpzIHdpdGggZGVmYXVsdHMsIHdlIGFsc28gaW5jbHVkZSBkZWZhdWx0IHZhbHVlcyBoZXJlIChQUk9YWV8uLi4pLlxyXG4gKi9cclxuXHJcbmltcG9ydCB7IHogfSBmcm9tICd6b2QnO1xyXG5pbXBvcnQgZG90ZW52IGZyb20gJ2RvdGVudic7XHJcbmRvdGVudi5jb25maWcoKTtcclxuXHJcbi8vIE9iamVjdCB3aXRoIGN1c3RvbSB2YWxpZGF0b3JzIGFuZCB0cmFuc2Zvcm1lcnMsIHRvIGF2b2lkIHJlcGV0aXRpb24gaW4gdGhlIENvbmZpZyBvYmplY3RcclxuY29uc3QgdiA9IHtcclxuICBib29sZWFuOiAoKSA9PlxyXG4gICAgelxyXG4gICAgICAuZW51bShbJ3RydWUnLCAnZmFsc2UnXSlcclxuICAgICAgLnRyYW5zZm9ybSgodmFsdWUpID0+IHZhbHVlID09PSAndHJ1ZScpXHJcbiAgICAgIC5vcHRpb25hbCgpLFxyXG4gIGFycmF5OiAoKSA9PlxyXG4gICAgelxyXG4gICAgICAuc3RyaW5nKClcclxuICAgICAgLnRyYW5zZm9ybSgodmFsKSA9PiB2YWwuc3BsaXQoJywnKS5tYXAoKHYpID0+IHYudHJpbSgpKSlcclxuICAgICAgLm9wdGlvbmFsKClcclxufTtcclxuXHJcbmV4cG9ydCBjb25zdCBDb25maWcgPSB6Lm9iamVjdCh7XHJcbiAgLy8gaGlnaGNoYXJ0c1xyXG4gIEhJR0hDSEFSVFNfVkVSU0lPTjogelxyXG4gICAgLnN0cmluZygpXHJcbiAgICAucmVmaW5lKCh2YWx1ZSkgPT4gL14obGF0ZXN0fFxcZCsoXFwuXFxkKyl7MCwyfSkkLy50ZXN0KHZhbHVlKSwge1xyXG4gICAgICBtZXNzYWdlOlxyXG4gICAgICAgIFwiSElHSENIQVJUU19WRVJTSU9OIG11c3QgYmUgJ2xhdGVzdCcsIGEgbWFqb3IgdmVyc2lvbiwgb3IgaW4gdGhlIGZvcm0gWFguWVkuWlpcIlxyXG4gICAgfSlcclxuICAgIC5vcHRpb25hbCgpLCAvLyB0b2RvOiBjcmVhdGUgYW4gYXJyYXkgb2YgYXZhaWxhYmxlIEhpZ2hjaGFydHMgdmVyc2lvbnNcclxuICBISUdIQ0hBUlRTX0NETl9VUkw6IHpcclxuICAgIC5zdHJpbmcoKVxyXG4gICAgLnRyaW0oKVxyXG4gICAgLnJlZmluZSgodmFsKSA9PiB2YWwuc3RhcnRzV2l0aCgnaHR0cHM6Ly8nKSB8fCB2YWwuc3RhcnRzV2l0aCgnaHR0cDovLycpLCB7XHJcbiAgICAgIG1lc3NhZ2U6XHJcbiAgICAgICAgJ0ludmFsaWQgdmFsdWUgZm9yIEhJR0hDSEFSVFNfQ0ROX1VSTC4gSXQgc2hvdWxkIHN0YXJ0IHdpdGggaHR0cDovLyBvciBodHRwczovLy4nXHJcbiAgICB9KVxyXG4gICAgLm9wdGlvbmFsKCksXHJcbiAgSElHSENIQVJUU19DT1JFX1NDUklQVFM6IHYuYXJyYXkoKSxcclxuICBISUdIQ0hBUlRTX01PRFVMRVM6IHYuYXJyYXkoKSxcclxuICBISUdIQ0hBUlRTX0lORElDQVRPUlM6IHYuYXJyYXkoKSxcclxuICBISUdIQ0hBUlRTX0ZPUkNFX0ZFVENIOiB2LmJvb2xlYW4oKSxcclxuICBISUdIQ0hBUlRTX0NBQ0hFX1BBVEg6IHouc3RyaW5nKCkub3B0aW9uYWwoKSxcclxuICBISUdIQ0hBUlRTX0FETUlOX1RPS0VOOiB6LnN0cmluZygpLm9wdGlvbmFsKCksXHJcblxyXG4gIC8vIGV4cG9ydFxyXG4gIEVYUE9SVF9UWVBFOiB6LmVudW0oWydqcGVnJywgJ3BuZycsICdwZGYnLCAnc3ZnJ10pLm9wdGlvbmFsKCksXHJcbiAgRVhQT1JUX0NPTlNUUjogelxyXG4gICAgLnN0cmluZygpXHJcbiAgICAucmVmaW5lKFxyXG4gICAgICAodmFsKSA9PlxyXG4gICAgICAgIFsnY2hhcnQnLCAnc3RvY2tDaGFydCcsICdtYXBDaGFydCcsICdnYW50dENoYXJ0J10uaW5jbHVkZXModmFsIHx8ICcnKSxcclxuICAgICAgeyBtZXNzYWdlOiAnSW52YWxpZCB2YWx1ZSBmb3IgRVhQT1JUX0NPTlNUUi4gJyB9XHJcbiAgICApXHJcbiAgICAub3B0aW9uYWwoKSxcclxuICBFWFBPUlRfREVGQVVMVF9IRUlHSFQ6IHouY29lcmNlLm51bWJlcigpLnBvc2l0aXZlKCkub3B0aW9uYWwoKSxcclxuICBFWFBPUlRfREVGQVVMVF9XSURUSDogei5jb2VyY2UubnVtYmVyKCkucG9zaXRpdmUoKS5vcHRpb25hbCgpLFxyXG4gIEVYUE9SVF9ERUZBVUxUX1NDQUxFOiB6LmNvZXJjZS5udW1iZXIoKS5wb3NpdGl2ZSgpLm9wdGlvbmFsKCksXHJcbiAgRVhQT1JUX1JBU1RFUklaQVRJT05fVElNRU9VVDogei5jb2VyY2UubnVtYmVyKCkucG9zaXRpdmUoKS5vcHRpb25hbCgpLFxyXG5cclxuICAvLyBjdXN0b21cclxuICBDVVNUT01fTE9HSUNfQUxMT1dfQ09ERV9FWEVDVVRJT046IHYuYm9vbGVhbigpLFxyXG4gIENVU1RPTV9MT0dJQ19BTExPV19GSUxFTF9SRVNPVVJDRVM6IHYuYm9vbGVhbigpLFxyXG5cclxuICAvLyBzZXJ2ZXItcmVsYXRlZFxyXG4gIFNFUlZFUl9FTkFCTEU6IHYuYm9vbGVhbigpLFxyXG4gIFNFUlZFUl9IT1NUOiB6LnN0cmluZygpLm9wdGlvbmFsKCksXHJcbiAgU0VSVkVSX1BPUlQ6IHouY29lcmNlLm51bWJlcigpLm9wdGlvbmFsKCksXHJcbiAgU0VSVkVSX0JFTkNITUFSS0lORzogdi5ib29sZWFuKCksXHJcbiAgU0VSVkVSX1NTTF9FTkFCTEU6IHYuYm9vbGVhbigpLFxyXG4gIFNFUlZFUl9TU0xfRk9SQ0U6IHYuYm9vbGVhbigpLFxyXG4gIFNFUlZFUl9TU0xfUE9SVDogei5jb2VyY2UubnVtYmVyKCkub3B0aW9uYWwoKSxcclxuICBTRVJWRVJfU1NMX0NFUlRfUEFUSDogei5zdHJpbmcoKS5vcHRpb25hbCgpLFxyXG4gIFNFUlZFUl9SQVRFX0xJTUlUSU5HX0VOQUJMRTogdi5ib29sZWFuKCksXHJcbiAgU0VSVkVSX1JBVEVfTElNSVRJTkdfTUFYX1JFUVVFU1RTOiB6LmNvZXJjZS5udW1iZXIoKS5vcHRpb25hbCgpLFxyXG4gIFNFUlZFUl9SQVRFX0xJTUlUSU5HX1dJTkRPVzogei5jb2VyY2UubnVtYmVyKCkub3B0aW9uYWwoKSxcclxuICBTRVJWRVJfUkFURV9MSU1JVElOR19ERUxBWTogei5jb2VyY2UubnVtYmVyKCkub3B0aW9uYWwoKSxcclxuICBTRVJWRVJfUkFURV9MSU1JVElOR19UUlVTVF9QUk9YWTogdi5ib29sZWFuKCksXHJcbiAgU0VSVkVSX1JBVEVfTElNSVRJTkdfU0tJUF9LRVk6IHouc3RyaW5nKCkub3B0aW9uYWwoKSxcclxuICBTRVJWRVJfUkFURV9MSU1JVElOR19TS0lQX1RPS0VOOiB6LnN0cmluZygpLm9wdGlvbmFsKCksXHJcblxyXG4gIC8vIHBvb2xcclxuICBQT09MX01JTl9XT1JLRVJTOiB6LmNvZXJjZS5udW1iZXIoKS5vcHRpb25hbCgpLFxyXG4gIFBPT0xfTUFYX1dPUktFUlM6IHouY29lcmNlLm51bWJlcigpLm9wdGlvbmFsKCksXHJcbiAgUE9PTF9XT1JLX0xJTUlUOiB6LmNvZXJjZS5udW1iZXIoKS5vcHRpb25hbCgpLFxyXG4gIFBPT0xfQUNRVUlSRV9USU1FT1VUOiB6LmNvZXJjZS5udW1iZXIoKS5vcHRpb25hbCgpLFxyXG4gIFBPT0xfQ1JFQVRFX1RJTUVPVVQ6IHouY29lcmNlLm51bWJlcigpLm9wdGlvbmFsKCksXHJcbiAgUE9PTF9ERVNUUk9ZX1RJTUVPVVQ6IHouY29lcmNlLm51bWJlcigpLm9wdGlvbmFsKCksXHJcbiAgUE9PTF9JRExFX1RJTUVPVVQ6IHouY29lcmNlLm51bWJlcigpLm9wdGlvbmFsKCksXHJcbiAgUE9PTF9DUkVBVEVfUkVUUllfSU5URVJWQUw6IHouY29lcmNlLm51bWJlcigpLm9wdGlvbmFsKCksXHJcbiAgUE9PTF9SRUFQRVJfSU5URVJWQUw6IHouY29lcmNlLm51bWJlcigpLm9wdGlvbmFsKCksXHJcbiAgUE9PTF9CRU5DSE1BUktJTkc6IHYuYm9vbGVhbigpLFxyXG4gIFBPT0xfTElTVEVOX1RPX1BST0NFU1NfRVhJVFM6IHYuYm9vbGVhbigpLFxyXG5cclxuICAvLyBsb2dnZXJcclxuICBMT0dHSU5HX0xFVkVMOiB6LmNvZXJjZVxyXG4gICAgLm51bWJlcigpXHJcbiAgICAub3B0aW9uYWwoKVxyXG4gICAgLnJlZmluZSgodmFsKSA9PiAodmFsIHx8IDUpID49IDAgJiYgKHZhbCB8fCA1KSA8PSA1LCB7XHJcbiAgICAgIG1lc3NhZ2U6XHJcbiAgICAgICAgJ0ludmFsaWQgdmFsdWUgZm9yIExPR0dJTkdfTEVWRUwuIFdlIG9ubHkgYWNjZXB0IDAsIDEsIDIsIDMsIDQsIDUgYXMgbG9nZ2luZyBsZXZlbHMuJ1xyXG4gICAgfSksXHJcbiAgTE9HR0lOR19GSUxFOiB6LnN0cmluZygpLm9wdGlvbmFsKCksXHJcbiAgTE9HR0lOR19ERVNUOiB6LnN0cmluZygpLm9wdGlvbmFsKCksXHJcblxyXG4gIC8vIHVpXHJcbiAgVUlfRU5BQkxFOiB2LmJvb2xlYW4oKSxcclxuICBVSV9ST1VURTogei5zdHJpbmcoKS5vcHRpb25hbCgpLFxyXG5cclxuICAvLyBvdGhlclxyXG4gIE9USEVSX05PX0xPR086IHYuYm9vbGVhbigpLFxyXG4gIE5PREVfRU5WOiB6XHJcbiAgICAuZW51bShbJ2RldmVsb3BtZW50JywgJ3Byb2R1Y3Rpb24nLCAndGVzdCddKVxyXG4gICAgLm9wdGlvbmFsKClcclxuICAgIC5kZWZhdWx0KCdwcm9kdWN0aW9uJyksXHJcblxyXG4gIC8vIHByb3h5ICghIE5PVCBJTkNMVURFRCBJTiBDT05GSUcuSlMgISlcclxuICBQUk9YWV9TRVJWRVJfVElNRU9VVDogei5jb2VyY2UubnVtYmVyKCkucG9zaXRpdmUoKS5vcHRpb25hbCgpLmRlZmF1bHQoNTAwMCksXHJcbiAgUFJPWFlfU0VSVkVSX0hPU1Q6IHouc3RyaW5nKCkub3B0aW9uYWwoKS5kZWZhdWx0KCdsb2NhbGhvc3QnKSxcclxuICBQUk9YWV9TRVJWRVJfUE9SVDogei5jb2VyY2UubnVtYmVyKCkucG9zaXRpdmUoKS5vcHRpb25hbCgpLmRlZmF1bHQoODA4MClcclxufSk7XHJcblxyXG5leHBvcnQgY29uc3QgZW52cyA9IENvbmZpZy5wYXJzZShwcm9jZXNzLmVudik7XHJcbiIsImNsYXNzIEV4cG9ydEVycm9yIGV4dGVuZHMgRXJyb3Ige1xyXG4gIGNvbnN0cnVjdG9yKG1lc3NhZ2UpIHtcclxuICAgIHN1cGVyKCk7XHJcbiAgICB0aGlzLm1lc3NhZ2UgPSBtZXNzYWdlO1xyXG4gICAgdGhpcy5zdGFja01lc3NhZ2UgPSBtZXNzYWdlO1xyXG4gIH1cclxuXHJcbiAgc2V0RXJyb3IoZXJyb3IpIHtcclxuICAgIHRoaXMuZXJyb3IgPSBlcnJvcjtcclxuICAgIGlmIChlcnJvci5uYW1lKSB7XHJcbiAgICAgIHRoaXMubmFtZSA9IGVycm9yLm5hbWU7XHJcbiAgICB9XHJcbiAgICBpZiAoZXJyb3Iuc3RhdHVzQ29kZSkge1xyXG4gICAgICB0aGlzLnN0YXR1c0NvZGUgPSBlcnJvci5zdGF0dXNDb2RlO1xyXG4gICAgfVxyXG4gICAgaWYgKGVycm9yLnN0YWNrKSB7XHJcbiAgICAgIHRoaXMuc3RhY2tNZXNzYWdlID0gZXJyb3IubWVzc2FnZTtcclxuICAgICAgdGhpcy5zdGFjayA9IGVycm9yLnN0YWNrO1xyXG4gICAgfVxyXG4gICAgcmV0dXJuIHRoaXM7XHJcbiAgfVxyXG59XHJcblxyXG5leHBvcnQgZGVmYXVsdCBFeHBvcnRFcnJvcjtcclxuIiwiLyoqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKipcclxuXHJcbkhpZ2hjaGFydHMgRXhwb3J0IFNlcnZlclxyXG5cclxuQ29weXJpZ2h0IChjKSAyMDE2LTIwMjQsIEhpZ2hzb2Z0XHJcblxyXG5MaWNlbmNlZCB1bmRlciB0aGUgTUlUIGxpY2VuY2UuXHJcblxyXG5BZGRpdGlvbmFsbHkgYSB2YWxpZCBIaWdoY2hhcnRzIGxpY2Vuc2UgaXMgcmVxdWlyZWQgZm9yIHVzZS5cclxuXHJcblNlZSBMSUNFTlNFIGZpbGUgaW4gcm9vdCBmb3IgZGV0YWlscy5cclxuXHJcbioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKiovXHJcblxyXG4vLyBUaGUgY2FjaGUgbWFuYWdlciBtYW5hZ2VzIHRoZSBIaWdoY2hhcnRzIGxpYnJhcnkgYW5kIGl0cyBkZXBlbmRlbmNpZXMuXHJcbi8vIFRoZSBjYWNoZSBpdHNlbGYgaXMgc3RvcmVkIGluIC5jYWNoZSwgYW5kIGlzIGNoZWNrZWQgYnkgdGhlIGNvbmZpZyBzeXN0ZW1cclxuLy8gYmVmb3JlIHN0YXJ0aW5nIHRoZSBzZXJ2aWNlXHJcblxyXG5pbXBvcnQgeyBleGlzdHNTeW5jLCBta2RpclN5bmMsIHJlYWRGaWxlU3luYywgd3JpdGVGaWxlU3luYyB9IGZyb20gJ2ZzJztcclxuaW1wb3J0IHsgam9pbiB9IGZyb20gJ3BhdGgnO1xyXG5cclxuaW1wb3J0IHsgSHR0cHNQcm94eUFnZW50IH0gZnJvbSAnaHR0cHMtcHJveHktYWdlbnQnO1xyXG5cclxuaW1wb3J0IHsgZmV0Y2ggfSBmcm9tICcuL2ZldGNoLmpzJztcclxuaW1wb3J0IHsgbG9nIH0gZnJvbSAnLi9sb2dnZXIuanMnO1xyXG5pbXBvcnQgeyBfX2Rpcm5hbWUgfSBmcm9tICcuL3V0aWxzLmpzJztcclxuaW1wb3J0IHsgZW52cyB9IGZyb20gJy4vZW52cy5qcyc7XHJcblxyXG5pbXBvcnQgRXhwb3J0RXJyb3IgZnJvbSAnLi9lcnJvcnMvRXhwb3J0RXJyb3IuanMnO1xyXG5cclxuY29uc3QgY2FjaGUgPSB7XHJcbiAgY2RuVVJMOiAnaHR0cHM6Ly9jb2RlLmhpZ2hjaGFydHMuY29tLycsXHJcbiAgYWN0aXZlTWFuaWZlc3Q6IHt9LFxyXG4gIHNvdXJjZXM6ICcnLFxyXG4gIGhjVmVyc2lvbjogJydcclxufTtcclxuXHJcbi8vIFRPRE86IFRoZSBjb25maWcgc2hvdWxkIGJlIGFjY2Vzc3NpYmxlIGdsb2JhbGx5IHNvIHdlIGRvbid0IGhhdmUgdG8gZG8gdGhpcyBzb3J0IG9mIHRoaW5nLi5cclxubGV0IGFwcGxpZWRDb25maWcgPSBmYWxzZTtcclxuXHJcbi8qKlxyXG4gKiBFeHRyYWN0cyBhbmQgY2FjaGVzIHRoZSBIaWdoY2hhcnRzIHZlcnNpb24gZnJvbSB0aGUgc291cmNlcyBzdHJpbmcuXHJcbiAqXHJcbiAqIEByZXR1cm5zIHtzdHJpbmd9IFRoZSBleHRyYWN0ZWQgSGlnaGNoYXJ0cyB2ZXJzaW9uLlxyXG4gKi9cclxuZXhwb3J0IGNvbnN0IGV4dHJhY3RWZXJzaW9uID0gKGNhY2hlKSA9PiB7XHJcbiAgcmV0dXJuIGNhY2hlLnNvdXJjZXNcclxuICAgIC5zdWJzdHJpbmcoMCwgY2FjaGUuc291cmNlcy5pbmRleE9mKCcqLycpKVxyXG4gICAgLnJlcGxhY2UoJy8qJywgJycpXHJcbiAgICAucmVwbGFjZSgnKi8nLCAnJylcclxuICAgIC5yZXBsYWNlKC9cXG4vZywgJycpXHJcbiAgICAudHJpbSgpO1xyXG59O1xyXG5cclxuLyoqXHJcbiAqIEV4dHJhY3RzIHRoZSBIaWdoY2hhcnRzIG1vZHVsZSBuYW1lIGJhc2VkIG9uIHRoZSBzY3JpcHRQYXRoLlxyXG4gKi9cclxuZXhwb3J0IGNvbnN0IGV4dHJhY3RNb2R1bGVOYW1lID0gKHNjcmlwdFBhdGgpID0+IHtcclxuICByZXR1cm4gc2NyaXB0UGF0aC5yZXBsYWNlKFxyXG4gICAgLyguKilcXC98KC4qKW1vZHVsZXNcXC98c3RvY2tcXC8oLiopaW5kaWNhdG9yc1xcL3xtYXBzXFwvKC4qKW1vZHVsZXNcXC8vZ2ksXHJcbiAgICAnJ1xyXG4gICk7XHJcbn07XHJcblxyXG4vKipcclxuICogU2F2ZXMgdGhlIHByb3ZpZGVkIGNvbmZpZ3VyYXRpb24gYW5kIGZldGNoZWQgbW9kdWxlcyB0byB0aGUgY2FjaGUgbWFuaWZlc3RcclxuICogZmlsZS5cclxuICpcclxuICogQHBhcmFtIHtvYmplY3R9IGNvbmZpZyAtIEhpZ2hjaGFydHMtcmVsYXRlZCBjb25maWd1cmF0aW9uIG9iamVjdC5cclxuICogQHBhcmFtIHtvYmplY3R9IGZldGNoZWRNb2R1bGVzIC0gQW4gb2JqZWN0IHRoYXQgY29udGFpbnMgbWFwcGVkIG5hbWVzIG9mXHJcbiAqIGZldGNoZWQgSGlnaGNoYXJ0cyBtb2R1bGVzIHRvIHVzZS5cclxuICpcclxuICogQHRocm93cyB7RXhwb3J0RXJyb3J9IFRocm93cyBhbiBFeHBvcnRFcnJvciBpZiBhbiBlcnJvciBvY2N1cnMgd2hpbGUgd3JpdGluZ1xyXG4gKiB0aGUgY2FjaGUgbWFuaWZlc3QuXHJcbiAqL1xyXG5leHBvcnQgY29uc3Qgc2F2ZUNvbmZpZ1RvTWFuaWZlc3QgPSBhc3luYyAoY29uZmlnLCBmZXRjaGVkTW9kdWxlcykgPT4ge1xyXG4gIGNvbnN0IG5ld01hbmlmZXN0ID0ge1xyXG4gICAgdmVyc2lvbjogY29uZmlnLnZlcnNpb24sXHJcbiAgICBtb2R1bGVzOiBmZXRjaGVkTW9kdWxlcyB8fCB7fVxyXG4gIH07XHJcblxyXG4gIC8vIFVwZGF0ZSBjYWNoZSBvYmplY3Qgd2l0aCB0aGUgY3VycmVudCBtb2R1bGVzXHJcbiAgY2FjaGUuYWN0aXZlTWFuaWZlc3QgPSBuZXdNYW5pZmVzdDtcclxuXHJcbiAgbG9nKDMsICdbY2FjaGVdIFdyaXRpbmcgYSBuZXcgbWFuaWZlc3QuJyk7XHJcbiAgdHJ5IHtcclxuICAgIHdyaXRlRmlsZVN5bmMoXHJcbiAgICAgIGpvaW4oX19kaXJuYW1lLCBjb25maWcuY2FjaGVQYXRoLCAnbWFuaWZlc3QuanNvbicpLFxyXG4gICAgICBKU09OLnN0cmluZ2lmeShuZXdNYW5pZmVzdCksXHJcbiAgICAgICd1dGY4J1xyXG4gICAgKTtcclxuICB9IGNhdGNoIChlcnJvcikge1xyXG4gICAgdGhyb3cgbmV3IEV4cG9ydEVycm9yKCdbY2FjaGVdIEVycm9yIHdyaXRpbmcgdGhlIGNhY2hlIG1hbmlmZXN0LicpLnNldEVycm9yKFxyXG4gICAgICBlcnJvclxyXG4gICAgKTtcclxuICB9XHJcbn07XHJcblxyXG4vKipcclxuICogRmV0Y2hlcyBhIHNpbmdsZSBzY3JpcHQgYW5kIHVwZGF0ZXMgdGhlIGZldGNoZWRNb2R1bGVzIGFjY29yZGluZ2x5LlxyXG4gKlxyXG4gKiBAcGFyYW0ge3N0cmluZ30gc2NyaXB0IC0gQSBwYXRoIHRvIHNjcmlwdCB0byBnZXQuXHJcbiAqIEBwYXJhbSB7T2JqZWN0fSBwcm94eUFnZW50IC0gVGhlIHByb3h5IGFnZW50IHRvIHVzZSBmb3IgYSByZXF1ZXN0LlxyXG4gKiBAcGFyYW0ge09iamVjdH0gZmV0Y2hlZE1vZHVsZXMgLSBBbiBvYmplY3Qgd2hpY2ggdHJhY2tzIHdoaWNoIEhpZ2hjaGFydHMgbW9kdWxlcyBoYXZlIGJlZW4gZmV0Y2hlZC5cclxuICogQHBhcmFtIHtib29sZWFufSBzaG91bGRUaHJvd0Vycm9yIC0gQSBmbGFnIHRvIGluZGljYXRlIGlmIHRoZSBlcnJvciBzaG91bGQgYmUgdGhyb3duLiBUaGlzIHNob3VsZCBiZSB1c2VkIG9ubHkgZm9yIHRoZSBjb3JlIHNjcmlwdHMuXHJcbiAqXHJcbiAqIEByZXR1cm5zIHtQcm9taXNlPHN0cmluZz59IEEgUHJvbWlzZSByZXNvbHZpbmcgdG8gdGhlIHRleHQgcmVwcmVzZW50YXRpb25cclxuICogb2YgdGhlIGZldGNoZWQgc2NyaXB0LlxyXG4gKlxyXG4gKiBAdGhyb3dzIHtFeHBvcnRFcnJvcn0gVGhyb3dzIGFuIEV4cG9ydEVycm9yIGlmIHRoZXJlIGlzIGEgcHJvYmxlbSB3aXRoXHJcbiAqIGZldGNoaW5nIHRoZSBzY3JpcHQuXHJcbiAqL1xyXG5leHBvcnQgY29uc3QgZmV0Y2hBbmRQcm9jZXNzU2NyaXB0ID0gYXN5bmMgKFxyXG4gIHNjcmlwdCxcclxuICBwcm94eUFnZW50LFxyXG4gIGZldGNoZWRNb2R1bGVzLFxyXG4gIHNob3VsZFRocm93RXJyb3IgPSBmYWxzZVxyXG4pID0+IHtcclxuICAvLyBHZXQgcmlkIG9mIHRoZSAuanMgZnJvbSB0aGUgY3VzdG9tIHN0cmluZ3NcclxuICBpZiAoc2NyaXB0LmVuZHNXaXRoKCcuanMnKSkge1xyXG4gICAgc2NyaXB0ID0gc2NyaXB0LnN1YnN0cmluZygwLCBzY3JpcHQubGVuZ3RoIC0gMyk7XHJcbiAgfVxyXG5cclxuICBsb2coNCwgYFtjYWNoZV0gRmV0Y2hpbmcgc2NyaXB0IC0gJHtzY3JpcHR9LmpzYCk7XHJcblxyXG4gIC8vIElmIGV4aXN0cywgYWRkIHByb3h5IGFnZW50IHRvIHJlcXVlc3Qgb3B0aW9uc1xyXG4gIGNvbnN0IHJlcXVlc3RPcHRpb25zID0gcHJveHlBZ2VudFxyXG4gICAgPyB7XHJcbiAgICAgICAgYWdlbnQ6IHByb3h5QWdlbnQsXHJcbiAgICAgICAgdGltZW91dDogZW52cy5QUk9YWV9TRVJWRVJfVElNRU9VVFxyXG4gICAgICB9XHJcbiAgICA6IHt9O1xyXG5cclxuICAvLyBGZXRjaCB0aGUgc2NyaXB0XHJcbiAgY29uc3QgcmVzcG9uc2UgPSBhd2FpdCBmZXRjaChgJHtzY3JpcHR9LmpzYCwgcmVxdWVzdE9wdGlvbnMpO1xyXG5cclxuICAvLyBJZiBPSywgcmV0dXJuIGl0cyB0ZXh0IHJlcHJlc2VudGF0aW9uXHJcbiAgaWYgKHJlc3BvbnNlLnN0YXR1c0NvZGUgPT09IDIwMCAmJiB0eXBlb2YgcmVzcG9uc2UudGV4dCA9PSAnc3RyaW5nJykge1xyXG4gICAgaWYgKGZldGNoZWRNb2R1bGVzKSB7XHJcbiAgICAgIGNvbnN0IG1vZHVsZU5hbWUgPSBleHRyYWN0TW9kdWxlTmFtZShzY3JpcHQpO1xyXG4gICAgICBmZXRjaGVkTW9kdWxlc1ttb2R1bGVOYW1lXSA9IDE7XHJcbiAgICB9XHJcblxyXG4gICAgcmV0dXJuIHJlc3BvbnNlLnRleHQ7XHJcbiAgfVxyXG5cclxuICBpZiAoc2hvdWxkVGhyb3dFcnJvcikge1xyXG4gICAgdGhyb3cgbmV3IEV4cG9ydEVycm9yKFxyXG4gICAgICBgQ291bGQgbm90IGZldGNoIHRoZSAke3NjcmlwdH0uanMuIFRoZSBzY3JpcHQgbWlnaHQgbm90IGV4aXN0IGluIHRoZSByZXF1ZXN0ZWQgdmVyc2lvbiAoc3RhdHVzIGNvZGU6ICR7cmVzcG9uc2Uuc3RhdHVzQ29kZX0pLmBcclxuICAgICkuc2V0RXJyb3IocmVzcG9uc2UpO1xyXG4gIH0gZWxzZSB7XHJcbiAgICBsb2coXHJcbiAgICAgIDIsXHJcbiAgICAgIGBbY2FjaGVdIENvdWxkIG5vdCBmZXRjaCB0aGUgJHtzY3JpcHR9LmpzLiBUaGUgc2NyaXB0IG1pZ2h0IG5vdCBleGlzdCBpbiB0aGUgcmVxdWVzdGVkIHZlcnNpb24uYFxyXG4gICAgKTtcclxuICB9XHJcblxyXG4gIHJldHVybiAnJztcclxufTtcclxuXHJcbi8qKlxyXG4gKiBGZXRjaGVzIEhpZ2hjaGFydHMgc2NyaXB0cyBhbmQgY3VzdG9tU2NyaXB0cyBmcm9tIHRoZSBnaXZlbiBDRE5zLlxyXG4gKlxyXG4gKiBAcGFyYW0ge3N0cmluZ30gc2NyaXB0cyAtIEFycmF5IG9mIEhpZ2hjaGFydHMgbW9kdWxlcyB0byBmZXRjaC5cclxuICogQHBhcmFtIHtzdHJpbmd9IGN1c3RvbVNjcmlwdHMgLSBBcnJheSBvZiBjdXN0b20gc2NyaXB0IHBhdGhzIHRvIGZldGNoIChmdWxsIFVSTHMpLlxyXG4gKiBAcGFyYW0ge29iamVjdH0gcHJveHlBZ2VudCAtIFRoZSBwcm94eSBhZ2VudCB0byB1c2UgZm9yIGEgcmVxdWVzdC5cclxuICogQHBhcmFtIHtvYmplY3R9IGZldGNoZWRNb2R1bGVzIC0gQW4gb2JqZWN0IHdoaWNoIHRyYWNrcyB3aGljaCBIaWdoY2hhcnRzIG1vZHVsZXMgaGF2ZSBiZWVuIGZldGNoZWQuXHJcbiAqIEByZXR1cm5zIHtQcm9taXNlPHN0cmluZz59IFRoZSBmZXRjaGVkIHNjcmlwdHMgY29udGVudCBqb2luZWQuXHJcbiAqL1xyXG5leHBvcnQgY29uc3QgZmV0Y2hTY3JpcHRzID0gYXN5bmMgKFxyXG4gIGNvcmVTY3JpcHRzLFxyXG4gIG1vZHVsZVNjcmlwdHMsXHJcbiAgY3VzdG9tU2NyaXB0cyxcclxuICBjZG5VUkwsXHJcbiAgcHJveHlBZ2VudCxcclxuICBmZXRjaGVkTW9kdWxlc1xyXG4pID0+IHtcclxuICBjb25zdCBhbGxGZXRjaFByb21pc2VzID0gW1xyXG4gICAgLi4uY29yZVNjcmlwdHMubWFwKChzY3JpcHQpID0+XHJcbiAgICAgIGZldGNoQW5kUHJvY2Vzc1NjcmlwdChcclxuICAgICAgICBgJHtjZG5VUkx9JHtzY3JpcHR9YCxcclxuICAgICAgICBwcm94eUFnZW50LFxyXG4gICAgICAgIGZldGNoZWRNb2R1bGVzLFxyXG4gICAgICAgIHRydWVcclxuICAgICAgKVxyXG4gICAgKSxcclxuICAgIC4uLm1vZHVsZVNjcmlwdHMubWFwKChzY3JpcHQpID0+XHJcbiAgICAgIGZldGNoQW5kUHJvY2Vzc1NjcmlwdChgJHtjZG5VUkx9JHtzY3JpcHR9YCwgcHJveHlBZ2VudCwgZmV0Y2hlZE1vZHVsZXMpXHJcbiAgICApLFxyXG4gICAgLi4uY3VzdG9tU2NyaXB0cy5tYXAoKHNjcmlwdCkgPT5cclxuICAgICAgZmV0Y2hBbmRQcm9jZXNzU2NyaXB0KGAke3NjcmlwdH1gLCBwcm94eUFnZW50KVxyXG4gICAgKVxyXG4gIF07XHJcblxyXG4gIGNvbnN0IGZldGNoZWRTY3JpcHRzID0gYXdhaXQgUHJvbWlzZS5hbGwoYWxsRmV0Y2hQcm9taXNlcyk7XHJcbiAgcmV0dXJuIGZldGNoZWRTY3JpcHRzLmpvaW4oJztcXG4nKTtcclxufTtcclxuXHJcbi8qKlxyXG4gKiBVcGRhdGVzIHRoZSBsb2NhbCBjYWNoZSB3aXRoIEhpZ2hjaGFydHMgc2NyaXB0cyBhbmQgdGhlaXIgdmVyc2lvbnMuXHJcbiAqXHJcbiAqIEBwYXJhbSB7T2JqZWN0fSBjb25maWcgLSBUaGUgY29uZmlndXJhdGlvbiBvYmplY3QgY29udGFpbmluZyBpbmZvcm1hdGlvblxyXG4gKiBhYm91dCBzY3JpcHRzIGFuZCBtb2R1bGVzLlxyXG4gKiBAcGFyYW0ge3N0cmluZ30gc291cmNlUGF0aCAtIFRoZSBwYXRoIHRvIHRoZSBzb3VyY2UgZmlsZSBpbiB0aGUgY2FjaGUuXHJcbiAqXHJcbiAqIEByZXR1cm5zIHtQcm9taXNlPG9iamVjdD59IEEgUHJvbWlzZSByZXNvbHZpbmcgdG8gYW4gb2JqZWN0IHJlcHJlc2VudGluZ1xyXG4gKiB0aGUgZmV0Y2hlZCBtb2R1bGVzLlxyXG4gKlxyXG4gKiBAdGhyb3dzIHtFeHBvcnRFcnJvcn0gVGhyb3dzIGFuIEV4cG9ydEVycm9yIGlmIHRoZXJlIGlzIGFuIGlzc3VlIHVwZGF0aW5nXHJcbiAqIHRoZSBsb2NhbCBIaWdoY2hhcnRzIGNhY2hlLlxyXG4gKi9cclxuZXhwb3J0IGNvbnN0IHVwZGF0ZUNhY2hlID0gYXN5bmMgKGNvbmZpZywgc291cmNlUGF0aCkgPT4ge1xyXG4gIGNvbnN0IHsgY29yZVNjcmlwdHMsIG1vZHVsZXMsIGluZGljYXRvcnMsIHNjcmlwdHM6IGN1c3RvbVNjcmlwdHMgfSA9IGNvbmZpZztcclxuICBjb25zdCBoY1ZlcnNpb24gPVxyXG4gICAgY29uZmlnLnZlcnNpb24gPT09ICdsYXRlc3QnIHx8ICFjb25maWcudmVyc2lvbiA/ICcnIDogYCR7Y29uZmlnLnZlcnNpb259L2A7XHJcblxyXG4gIGxvZyhcclxuICAgIDMsXHJcbiAgICBgW2NhY2hlXSBVcGRhdGluZyBjYWNoZSB2ZXJzaW9uIHRvIEhpZ2hjaGFydHM6ICR7aGNWZXJzaW9uIHx8ICdsYXRlc3QnfS5gXHJcbiAgKTtcclxuXHJcbiAgLy8gQ29uZmlndXJlIHByb3h5IGlmIGV4aXN0c1xyXG4gIGxldCBwcm94eUFnZW50O1xyXG4gIGNvbnN0IHByb3h5SG9zdCA9IHByb2Nlc3MuZW52WydQUk9YWV9TRVJWRVJfSE9TVCddO1xyXG4gIGNvbnN0IHByb3h5UG9ydCA9IHByb2Nlc3MuZW52WydQUk9YWV9TRVJWRVJfUE9SVCddO1xyXG5cclxuICAvLyBUcnkgdG8gY3JlYXRlIGEgUHJveHkgQWdlbnRcclxuICBpZiAocHJveHlIb3N0ICYmIHByb3h5UG9ydCkge1xyXG4gICAgdHJ5IHtcclxuICAgICAgcHJveHlBZ2VudCA9IG5ldyBIdHRwc1Byb3h5QWdlbnQoe1xyXG4gICAgICAgIGhvc3Q6IHByb3h5SG9zdCxcclxuICAgICAgICBwb3J0OiArcHJveHlQb3J0XHJcbiAgICAgIH0pO1xyXG4gICAgfSBjYXRjaCAoZXJyb3IpIHtcclxuICAgICAgdGhyb3cgbmV3IEV4cG9ydEVycm9yKCdbY2FjaGVdIENvdWxkIG5vdCBjcmVhdGUgYSBQcm94eSBBZ2VudC4nKS5zZXRFcnJvcihcclxuICAgICAgICBlcnJvclxyXG4gICAgICApO1xyXG4gICAgfVxyXG4gIH1cclxuXHJcbiAgY29uc3QgZmV0Y2hlZE1vZHVsZXMgPSB7fTtcclxuICB0cnkge1xyXG4gICAgY2FjaGUuc291cmNlcyA9IGF3YWl0IGZldGNoU2NyaXB0cyhcclxuICAgICAgWy4uLmNvcmVTY3JpcHRzLm1hcCgoYykgPT4gYCR7aGNWZXJzaW9ufSR7Y31gKV0sXHJcbiAgICAgIFtcclxuICAgICAgICAuLi5tb2R1bGVzLm1hcCgobSkgPT5cclxuICAgICAgICAgIG0gPT09ICdtYXAnXHJcbiAgICAgICAgICAgID8gYG1hcHMvJHtoY1ZlcnNpb259bW9kdWxlcy8ke219YFxyXG4gICAgICAgICAgICA6IGAke2hjVmVyc2lvbn1tb2R1bGVzLyR7bX1gXHJcbiAgICAgICAgKSxcclxuICAgICAgICAuLi5pbmRpY2F0b3JzLm1hcCgoaSkgPT4gYHN0b2NrLyR7aGNWZXJzaW9ufWluZGljYXRvcnMvJHtpfWApXHJcbiAgICAgIF0sXHJcbiAgICAgIGN1c3RvbVNjcmlwdHMsXHJcbiAgICAgIGNvbmZpZy5jZG5VUkwgfHwgY2FjaGUuY2RuVVJMLFxyXG4gICAgICBwcm94eUFnZW50LFxyXG4gICAgICBmZXRjaGVkTW9kdWxlc1xyXG4gICAgKTtcclxuXHJcbiAgICBjYWNoZS5oY1ZlcnNpb24gPSBleHRyYWN0VmVyc2lvbihjYWNoZSk7XHJcblxyXG4gICAgLy8gU2F2ZSB0aGUgZmV0Y2hlZCBtb2R1bGVzIGludG8gY2FjaGVzJyBzb3VyY2UgSlNPTlxyXG4gICAgd3JpdGVGaWxlU3luYyhzb3VyY2VQYXRoLCBjYWNoZS5zb3VyY2VzKTtcclxuICAgIHJldHVybiBmZXRjaGVkTW9kdWxlcztcclxuICB9IGNhdGNoIChlcnJvcikge1xyXG4gICAgdGhyb3cgbmV3IEV4cG9ydEVycm9yKFxyXG4gICAgICAnW2NhY2hlXSBVbmFibGUgdG8gdXBkYXRlIHRoZSBsb2NhbCBIaWdoY2hhcnRzIGNhY2hlLidcclxuICAgICkuc2V0RXJyb3IoZXJyb3IpO1xyXG4gIH1cclxufTtcclxuXHJcbi8qKlxyXG4gKiBVcGRhdGVzIHRoZSBIaWdoY2hhcnRzIHZlcnNpb24gaW4gdGhlIGFwcGxpZWQgY29uZmlndXJhdGlvbiBhbmQgY2hlY2tzXHJcbiAqIHRoZSBjYWNoZSBmb3IgdGhlIG5ldyB2ZXJzaW9uLlxyXG4gKlxyXG4gKiBAcGFyYW0ge3N0cmluZ30gbmV3VmVyc2lvbiAtIFRoZSBuZXcgSGlnaGNoYXJ0cyB2ZXJzaW9uIHRvIGJlIGFwcGxpZWQuXHJcbiAqXHJcbiAqIEByZXR1cm5zIHtQcm9taXNlPChvYmplY3R8Ym9vbGVhbik+fSBBIFByb21pc2UgcmVzb2x2aW5nIHRvIHRoZSB1cGRhdGVkXHJcbiAqIGNvbmZpZ3VyYXRpb24gd2l0aCB0aGUgbmV3IHZlcnNpb24sIG9yIGZhbHNlIGlmIG5vIGFwcGxpZWQgY29uZmlndXJhdGlvblxyXG4gKiBleGlzdHMuXHJcbiAqL1xyXG5leHBvcnQgY29uc3QgdXBkYXRlVmVyc2lvbiA9IGFzeW5jIChuZXdWZXJzaW9uKSA9PlxyXG4gIGFwcGxpZWRDb25maWdcclxuICAgID8gYXdhaXQgY2hlY2tBbmRVcGRhdGVDYWNoZShcclxuICAgICAgICBPYmplY3QuYXNzaWduKGFwcGxpZWRDb25maWcsIHtcclxuICAgICAgICAgIHZlcnNpb246IG5ld1ZlcnNpb25cclxuICAgICAgICB9KVxyXG4gICAgICApXHJcbiAgICA6IGZhbHNlO1xyXG5cclxuLyoqXHJcbiAqIENoZWNrcyB0aGUgY2FjaGUgZm9yIEhpZ2hjaGFydHMgZGVwZW5kZW5jaWVzLCB1cGRhdGVzIHRoZSBjYWNoZSBpZiBuZWVkZWQsXHJcbiAqIGFuZCBsb2FkcyB0aGUgc291cmNlcy5cclxuICpcclxuICogQHBhcmFtIHtPYmplY3R9IGNvbmZpZyAtIEhpZ2hjaGFydHMtcmVsYXRlZCBjb25maWd1cmF0aW9uIG9iamVjdCBjb250YWluaW5nIGluZm9ybWF0aW9uXHJcbiAqIGFib3V0IHNjcmlwdHMgYW5kIG1vZHVsZXMuXHJcbiAqXHJcbiAqIEByZXR1cm5zIHtQcm9taXNlPHZvaWQ+fSBBIFByb21pc2UgdGhhdCByZXNvbHZlcyBvbmNlIHRoZSBjYWNoZSBpcyBjaGVja2VkXHJcbiAqIGFuZCB1cGRhdGVkLlxyXG4gKlxyXG4gKiBAdGhyb3dzIHtFeHBvcnRFcnJvcn0gVGhyb3dzIGFuIEV4cG9ydEVycm9yIGlmIHRoZXJlIGlzIGFuIGlzc3VlIHVwZGF0aW5nXHJcbiAqIG9yIHJlYWRpbmcgdGhlIGNhY2hlLlxyXG4gKi9cclxuZXhwb3J0IGNvbnN0IGNoZWNrQW5kVXBkYXRlQ2FjaGUgPSBhc3luYyAoY29uZmlnKSA9PiB7XHJcbiAgY29uc3QgY2FjaGVQYXRoID0gam9pbihfX2Rpcm5hbWUsIGNvbmZpZy5jYWNoZVBhdGgpO1xyXG5cclxuICBsZXQgZmV0Y2hlZE1vZHVsZXM7XHJcbiAgLy8gUHJlcGFyZSBwYXRocyB0byBtYW5pZmVzdCBhbmQgc291cmNlcyBmcm9tIHRoZSAuY2FjaGUgZm9sZGVyXHJcbiAgY29uc3QgbWFuaWZlc3RQYXRoID0gam9pbihjYWNoZVBhdGgsICdtYW5pZmVzdC5qc29uJyk7XHJcbiAgY29uc3Qgc291cmNlUGF0aCA9IGpvaW4oY2FjaGVQYXRoLCAnc291cmNlcy5qcycpO1xyXG5cclxuICAvLyBUT0RPOiBkZWFsIHdpdGggdHJ5aW5nIHRvIHN3aXRjaCB0byB0aGUgcnVubmluZyB2ZXJzaW9uXHJcbiAgLy8gY29uc3QgYWN0aXZlVmVyc2lvbiA9IGFwcGxpZWRDb25maWcgPyBhcHBsaWVkQ29uZmlnLnZlcnNpb24gOiBmYWxzZTtcclxuXHJcbiAgYXBwbGllZENvbmZpZyA9IGNvbmZpZztcclxuXHJcbiAgLy8gQ3JlYXRlIHRoZSBjYWNoZSBkZXN0aW5hdGlvbiBpZiBpdCBkb2Vzbid0IGV4aXN0IGFscmVhZHlcclxuICAhZXhpc3RzU3luYyhjYWNoZVBhdGgpICYmIG1rZGlyU3luYyhjYWNoZVBhdGgpO1xyXG5cclxuICAvLyBGZXRjaCBhbGwgdGhlIHNjcmlwdHMgZWl0aGVyIGlmIG1hbmlmZXN0Lmpzb24gZG9lcyBub3QgZXhpc3RcclxuICAvLyBvciBpZiB0aGUgZm9yY2VGZXRjaCBvcHRpb24gaXMgZW5hYmxlZFxyXG4gIGlmICghZXhpc3RzU3luYyhtYW5pZmVzdFBhdGgpIHx8IGNvbmZpZy5mb3JjZUZldGNoKSB7XHJcbiAgICBsb2coMywgJ1tjYWNoZV0gRmV0Y2hpbmcgYW5kIGNhY2hpbmcgSGlnaGNoYXJ0cyBkZXBlbmRlbmNpZXMuJyk7XHJcbiAgICBmZXRjaGVkTW9kdWxlcyA9IGF3YWl0IHVwZGF0ZUNhY2hlKGNvbmZpZywgc291cmNlUGF0aCk7XHJcbiAgfSBlbHNlIHtcclxuICAgIGxldCByZXF1ZXN0VXBkYXRlID0gZmFsc2U7XHJcblxyXG4gICAgLy8gUmVhZCB0aGUgbWFuaWZlc3QgSlNPTlxyXG4gICAgY29uc3QgbWFuaWZlc3QgPSBKU09OLnBhcnNlKHJlYWRGaWxlU3luYyhtYW5pZmVzdFBhdGgpKTtcclxuXHJcbiAgICAvLyBDaGVjayBpZiB0aGUgbW9kdWxlcyBpcyBhbiBhcnJheSwgaWYgc28sIHdlIHJld3JpdGUgaXQgdG8gYSBtYXAgdG8gbWFrZVxyXG4gICAgLy8gaXQgZWFzaWVyIHRvIHJlc29sdmUgbW9kdWxlcy5cclxuICAgIGlmIChtYW5pZmVzdC5tb2R1bGVzICYmIEFycmF5LmlzQXJyYXkobWFuaWZlc3QubW9kdWxlcykpIHtcclxuICAgICAgY29uc3QgbW9kdWxlTWFwID0ge307XHJcbiAgICAgIG1hbmlmZXN0Lm1vZHVsZXMuZm9yRWFjaCgobSkgPT4gKG1vZHVsZU1hcFttXSA9IDEpKTtcclxuICAgICAgbWFuaWZlc3QubW9kdWxlcyA9IG1vZHVsZU1hcDtcclxuICAgIH1cclxuXHJcbiAgICBjb25zdCB7IG1vZHVsZXMsIGNvcmVTY3JpcHRzLCBpbmRpY2F0b3JzIH0gPSBjb25maWc7XHJcbiAgICBjb25zdCBudW1iZXJPZk1vZHVsZXMgPVxyXG4gICAgICBtb2R1bGVzLmxlbmd0aCArIGNvcmVTY3JpcHRzLmxlbmd0aCArIGluZGljYXRvcnMubGVuZ3RoO1xyXG5cclxuICAgIC8vIENvbXBhcmUgdGhlIGxvYWRlZCBjb25maWcgd2l0aCB0aGUgY29udGVudHMgaW4gY2FjaGUuXHJcbiAgICAvLyBJZiB0aGVyZSBhcmUgY2hhbmdlcywgZmV0Y2ggcmVxdWVzdGVkIG1vZHVsZXMgYW5kIHByb2R1Y3RzLFxyXG4gICAgLy8gYW5kIGJha2UgdGhlbSBpbnRvIGEgZ2lhbnQgYmxvYi4gU2F2ZSB0aGUgYmxvYi5cclxuICAgIGlmIChtYW5pZmVzdC52ZXJzaW9uICE9PSBjb25maWcudmVyc2lvbikge1xyXG4gICAgICBsb2coXHJcbiAgICAgICAgMixcclxuICAgICAgICAnW2NhY2hlXSBBIEhpZ2hjaGFydHMgdmVyc2lvbiBtaXNtYXRjaCBpbiB0aGUgY2FjaGUsIG5lZWQgdG8gcmUtZmV0Y2guJ1xyXG4gICAgICApO1xyXG4gICAgICByZXF1ZXN0VXBkYXRlID0gdHJ1ZTtcclxuICAgIH0gZWxzZSBpZiAoT2JqZWN0LmtleXMobWFuaWZlc3QubW9kdWxlcyB8fCB7fSkubGVuZ3RoICE9PSBudW1iZXJPZk1vZHVsZXMpIHtcclxuICAgICAgbG9nKFxyXG4gICAgICAgIDIsXHJcbiAgICAgICAgJ1tjYWNoZV0gVGhlIGNhY2hlIGFuZCB0aGUgcmVxdWVzdGVkIG1vZHVsZXMgZG8gbm90IG1hdGNoLCBuZWVkIHRvIHJlLWZldGNoLidcclxuICAgICAgKTtcclxuICAgICAgcmVxdWVzdFVwZGF0ZSA9IHRydWU7XHJcbiAgICB9IGVsc2Uge1xyXG4gICAgICAvLyBDaGVjayBlYWNoIG1vZHVsZSwgaWYgYW55dGhpbmcgaXMgbWlzc2luZyByZWZldGNoIGV2ZXJ5dGhpbmdcclxuICAgICAgcmVxdWVzdFVwZGF0ZSA9IChjb25maWcubW9kdWxlcyB8fCBbXSkuc29tZSgobW9kdWxlTmFtZSkgPT4ge1xyXG4gICAgICAgIGlmICghbWFuaWZlc3QubW9kdWxlc1ttb2R1bGVOYW1lXSkge1xyXG4gICAgICAgICAgbG9nKFxyXG4gICAgICAgICAgICAyLFxyXG4gICAgICAgICAgICBgW2NhY2hlXSBUaGUgJHttb2R1bGVOYW1lfSBpcyBtaXNzaW5nIGluIHRoZSBjYWNoZSwgbmVlZCB0byByZS1mZXRjaC5gXHJcbiAgICAgICAgICApO1xyXG4gICAgICAgICAgcmV0dXJuIHRydWU7XHJcbiAgICAgICAgfVxyXG4gICAgICB9KTtcclxuICAgIH1cclxuXHJcbiAgICBpZiAocmVxdWVzdFVwZGF0ZSkge1xyXG4gICAgICBmZXRjaGVkTW9kdWxlcyA9IGF3YWl0IHVwZGF0ZUNhY2hlKGNvbmZpZywgc291cmNlUGF0aCk7XHJcbiAgICB9IGVsc2Uge1xyXG4gICAgICBsb2coMywgJ1tjYWNoZV0gRGVwZW5kZW5jeSBjYWNoZSBpcyB1cCB0byBkYXRlLCBwcm9jZWVkaW5nLicpO1xyXG5cclxuICAgICAgLy8gTG9hZCB0aGUgc291cmNlc1xyXG4gICAgICBjYWNoZS5zb3VyY2VzID0gcmVhZEZpbGVTeW5jKHNvdXJjZVBhdGgsICd1dGY4Jyk7XHJcblxyXG4gICAgICAvLyBHZXQgY3VycmVudCBtb2R1bGVzIG1hcFxyXG4gICAgICBmZXRjaGVkTW9kdWxlcyA9IG1hbmlmZXN0Lm1vZHVsZXM7XHJcblxyXG4gICAgICBjYWNoZS5oY1ZlcnNpb24gPSBleHRyYWN0VmVyc2lvbihjYWNoZSk7XHJcbiAgICB9XHJcbiAgfVxyXG5cclxuICAvLyBGaW5hbGx5LCBzYXZlIHRoZSBuZXcgbWFuaWZlc3QsIHdoaWNoIGlzIGJhc2ljYWxseSBvdXIgY3VycmVudCBjb25maWdcclxuICAvLyBpbiBhIHNsaWdodGx5IGRpZmZlcmVudCBmb3JtYXRcclxuICBhd2FpdCBzYXZlQ29uZmlnVG9NYW5pZmVzdChjb25maWcsIGZldGNoZWRNb2R1bGVzKTtcclxufTtcclxuXHJcbmV4cG9ydCBjb25zdCBnZXRDYWNoZVBhdGggPSAoKSA9PiB7XHJcbiAgcmV0dXJuIGpvaW4oX19kaXJuYW1lLCBhcHBsaWVkQ29uZmlnLmNhY2hlUGF0aCk7XHJcbn07XHJcblxyXG5leHBvcnQgZGVmYXVsdCB7XHJcbiAgY2hlY2tBbmRVcGRhdGVDYWNoZSxcclxuICBnZXRDYWNoZVBhdGgsXHJcbiAgdXBkYXRlVmVyc2lvbixcclxuICBnZXRDYWNoZTogKCkgPT4gY2FjaGUsXHJcbiAgaGlnaGNoYXJ0czogKCkgPT4gY2FjaGUuc291cmNlcyxcclxuICB2ZXJzaW9uOiAoKSA9PiBjYWNoZS5oY1ZlcnNpb25cclxufTtcclxuIiwiLyoqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKipcclxuXHJcbkhpZ2hjaGFydHMgRXhwb3J0IFNlcnZlclxyXG5cclxuQ29weXJpZ2h0IChjKSAyMDE2LTIwMjQsIEhpZ2hzb2Z0XHJcblxyXG5MaWNlbmNlZCB1bmRlciB0aGUgTUlUIGxpY2VuY2UuXHJcblxyXG5BZGRpdGlvbmFsbHkgYSB2YWxpZCBIaWdoY2hhcnRzIGxpY2Vuc2UgaXMgcmVxdWlyZWQgZm9yIHVzZS5cclxuXHJcblNlZSBMSUNFTlNFIGZpbGUgaW4gcm9vdCBmb3IgZGV0YWlscy5cclxuXHJcbioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKiovXHJcblxyXG5pbXBvcnQgeyBleGlzdHNTeW5jLCByZWFkRmlsZVN5bmMsIHByb21pc2VzIGFzIGZzUHJvbWlzZXMgfSBmcm9tICdmcyc7XHJcblxyXG5pbXBvcnQgcHJvbXB0cyBmcm9tICdwcm9tcHRzJztcclxuXHJcbmltcG9ydCB7XHJcbiAgYWJzb2x1dGVQcm9wcyxcclxuICBkZWZhdWx0Q29uZmlnLFxyXG4gIG5lc3RlZEFyZ3MsXHJcbiAgcHJvbXB0c0NvbmZpZ1xyXG59IGZyb20gJy4vc2NoZW1hcy9jb25maWcuanMnO1xyXG5pbXBvcnQgeyBsb2csIGxvZ1dpdGhTdGFjayB9IGZyb20gJy4vbG9nZ2VyLmpzJztcclxuaW1wb3J0IHsgZGVlcENvcHksIGlzT2JqZWN0LCBwcmludFVzYWdlLCB0b0Jvb2xlYW4gfSBmcm9tICcuL3V0aWxzLmpzJztcclxuaW1wb3J0IHsgZW52cyB9IGZyb20gJy4vZW52cy5qcyc7XHJcblxyXG5sZXQgZ2VuZXJhbE9wdGlvbnMgPSB7fTtcclxuXHJcbi8qKlxyXG4gKiBSZXRyaWV2ZXMgYW5kIHJldHVybnMgdGhlIGdlbmVyYWwgb3B0aW9ucyBmb3IgdGhlIGV4cG9ydCBwcm9jZXNzLlxyXG4gKlxyXG4gKiBAcmV0dXJucyB7T2JqZWN0fSBUaGUgZ2VuZXJhbCBvcHRpb25zIG9iamVjdC5cclxuICovXHJcbmV4cG9ydCBjb25zdCBnZXRPcHRpb25zID0gKCkgPT4gZ2VuZXJhbE9wdGlvbnM7XHJcblxyXG4vKipcclxuICogSW5pdGlhbGl6ZXMgYW5kIHNldHMgdGhlIGdlbmVyYWwgb3B0aW9ucyBmb3IgdGhlIHNlcnZlciBpbnN0YWNlLCBrZWVwaW5nXHJcbiAqIHRoZSBwcmluY2lwbGUgb2YgdGhlIG9wdGlvbnMgbG9hZCBwcmlvcml0eS4gSXQgYWNjZXB0cyBvcHRpb25hbCB1c2VyT3B0aW9uc1xyXG4gKiBhbmQgYXJncyBmcm9tIHRoZSBDTEkuXHJcbiAqXHJcbiAqIEBwYXJhbSB7T2JqZWN0fSB1c2VyT3B0aW9ucyAtIFVzZXItcHJvdmlkZWQgb3B0aW9ucyBmb3IgY3VzdG9taXphdGlvbi5cclxuICogQHBhcmFtIHtBcnJheX0gYXJncyAtIENvbW1hbmQtbGluZSBhcmd1bWVudHMgZm9yIGFkZGl0aW9uYWwgY29uZmlndXJhdGlvblxyXG4gKiAoQ0xJIHVzYWdlKS5cclxuICpcclxuICogQHJldHVybnMge09iamVjdH0gVGhlIHVwZGF0ZWQgZ2VuZXJhbCBvcHRpb25zIG9iamVjdC5cclxuICovXHJcbmV4cG9ydCBjb25zdCBzZXRPcHRpb25zID0gKHVzZXJPcHRpb25zLCBhcmdzKSA9PiB7XHJcbiAgLy8gT25seSBmb3IgdGhlIENMSSB1c2FnZVxyXG4gIGlmIChhcmdzPy5sZW5ndGgpIHtcclxuICAgIC8vIEdldCB0aGUgYWRkaXRpb25hbCBvcHRpb25zIGZyb20gdGhlIGN1c3RvbSBKU09OIGZpbGVcclxuICAgIGdlbmVyYWxPcHRpb25zID0gbG9hZENvbmZpZ0ZpbGUoYXJncyk7XHJcbiAgfVxyXG5cclxuICAvLyBVcGRhdGUgdGhlIGRlZmF1bHQgY29uZmlnIHdpdGggYSBjb3JyZWN0IG9wdGlvbiB2YWx1ZXNcclxuICB1cGRhdGVEZWZhdWx0Q29uZmlnKGRlZmF1bHRDb25maWcsIGdlbmVyYWxPcHRpb25zKTtcclxuXHJcbiAgLy8gU2V0IHZhbHVlcyBmb3Igc2VydmVyJ3Mgb3B0aW9ucyBhbmQgcmV0dXJucyB0aGVtXHJcbiAgZ2VuZXJhbE9wdGlvbnMgPSBpbml0T3B0aW9ucyhkZWZhdWx0Q29uZmlnKTtcclxuXHJcbiAgLy8gQXBwbHkgdXNlciBvcHRpb25zIGlmIHRoZXJlIGFyZSBhbnlcclxuICBpZiAodXNlck9wdGlvbnMpIHtcclxuICAgIC8vIE1lcmdlIHVzZXIgb3B0aW9uc1xyXG4gICAgZ2VuZXJhbE9wdGlvbnMgPSBtZXJnZUNvbmZpZ09wdGlvbnMoXHJcbiAgICAgIGdlbmVyYWxPcHRpb25zLFxyXG4gICAgICB1c2VyT3B0aW9ucyxcclxuICAgICAgYWJzb2x1dGVQcm9wc1xyXG4gICAgKTtcclxuICB9XHJcblxyXG4gIC8vIE9ubHkgZm9yIHRoZSBDTEkgdXNhZ2VcclxuICBpZiAoYXJncz8ubGVuZ3RoKSB7XHJcbiAgICAvLyBQYWlyIHByb3ZpZGVkIGFyZ3VtZW50c1xyXG4gICAgZ2VuZXJhbE9wdGlvbnMgPSBwYWlyQXJndW1lbnRWYWx1ZShnZW5lcmFsT3B0aW9ucywgYXJncywgZGVmYXVsdENvbmZpZyk7XHJcbiAgfVxyXG5cclxuICAvLyBSZXR1cm4gZmluYWwgZ2VuZXJhbCBvcHRpb25zXHJcbiAgcmV0dXJuIGdlbmVyYWxPcHRpb25zO1xyXG59O1xyXG5cclxuLyoqXHJcbiAqIEFsbG93cyBtYW51YWwgY29uZmlndXJhdGlvbiBiYXNlZCBvbiBzcGVjaWZpZWQgcHJvbXB0cyBhbmQgc2F2ZXNcclxuICogdGhlIGNvbmZpZ3VyYXRpb24gdG8gYSBmaWxlLlxyXG4gKlxyXG4gKiBAcGFyYW0ge3N0cmluZ30gY29uZmlnRmlsZU5hbWUgLSBUaGUgbmFtZSBvZiB0aGUgY29uZmlndXJhdGlvbiBmaWxlLlxyXG4gKlxyXG4gKiBAcmV0dXJucyB7UHJvbWlzZTxib29sZWFuPn0gQSBQcm9taXNlIHRoYXQgcmVzb2x2ZXMgdG8gdHJ1ZSBvbmNlIHRoZSBtYW51YWxcclxuICogY29uZmlndXJhdGlvbiBpcyBjb21wbGV0ZWQgYW5kIHNhdmVkLlxyXG4gKi9cclxuZXhwb3J0IGNvbnN0IG1hbnVhbENvbmZpZyA9IGFzeW5jIChjb25maWdGaWxlTmFtZSkgPT4ge1xyXG4gIC8vIFByZXBhcmUgYSBjb25maWcgb2JqZWN0XHJcbiAgbGV0IGNvbmZpZ0ZpbGUgPSB7fTtcclxuXHJcbiAgLy8gQ2hlY2sgaWYgcHJvdmlkZWQgY29uZmlnIGZpbGUgZXhpc3RzXHJcbiAgaWYgKGV4aXN0c1N5bmMoY29uZmlnRmlsZU5hbWUpKSB7XHJcbiAgICBjb25maWdGaWxlID0gSlNPTi5wYXJzZShyZWFkRmlsZVN5bmMoY29uZmlnRmlsZU5hbWUsICd1dGY4JykpO1xyXG4gIH1cclxuXHJcbiAgLy8gUXVlc3Rpb24gYWJvdXQgYSBjb25maWd1cmF0aW9uIGNhdGVnb3J5XHJcbiAgY29uc3Qgb25TdWJtaXQgPSBhc3luYyAocCwgY2F0ZWdvcmllcykgPT4ge1xyXG4gICAgbGV0IHF1ZXN0aW9uc0NvdW50ZXIgPSAwO1xyXG4gICAgbGV0IGFsbFF1ZXN0aW9ucyA9IFtdO1xyXG5cclxuICAgIC8vIENyZWF0ZSBhIGNvcnJlc3BvbmRpbmcgcHJvcGVydHkgaW4gdGhlIG1hbnVhbENvbmZpZyBvYmplY3RcclxuICAgIGZvciAoY29uc3Qgc2VjdGlvbiBvZiBjYXRlZ29yaWVzKSB7XHJcbiAgICAgIC8vIE1hcmsgZWFjaCBvcHRpb24gd2l0aCBhIHNlY3Rpb25cclxuICAgICAgcHJvbXB0c0NvbmZpZ1tzZWN0aW9uXSA9IHByb21wdHNDb25maWdbc2VjdGlvbl0ubWFwKChvcHRpb24pID0+ICh7XHJcbiAgICAgICAgLi4ub3B0aW9uLFxyXG4gICAgICAgIHNlY3Rpb25cclxuICAgICAgfSkpO1xyXG5cclxuICAgICAgLy8gQ29sbGVjdCB0aGUgcXVlc3Rpb25zXHJcbiAgICAgIGFsbFF1ZXN0aW9ucyA9IFsuLi5hbGxRdWVzdGlvbnMsIC4uLnByb21wdHNDb25maWdbc2VjdGlvbl1dO1xyXG4gICAgfVxyXG5cclxuICAgIGF3YWl0IHByb21wdHMoYWxsUXVlc3Rpb25zLCB7XHJcbiAgICAgIG9uU3VibWl0OiBhc3luYyAocHJvbXB0LCBhbnN3ZXIpID0+IHtcclxuICAgICAgICAvLyBHZXQgdGhlIGRlZmF1bHQgbW9kdWxlc1xyXG4gICAgICAgIGlmIChwcm9tcHQubmFtZSA9PT0gJ21vZHVsZXMnKSB7XHJcbiAgICAgICAgICBhbnN3ZXIgPSBhbnN3ZXIubGVuZ3RoXHJcbiAgICAgICAgICAgID8gYW5zd2VyLm1hcCgobW9kdWxlKSA9PiBwcm9tcHQuY2hvaWNlc1ttb2R1bGVdKVxyXG4gICAgICAgICAgICA6IHByb21wdC5jaG9pY2VzO1xyXG5cclxuICAgICAgICAgIGNvbmZpZ0ZpbGVbcHJvbXB0LnNlY3Rpb25dW3Byb21wdC5uYW1lXSA9IGFuc3dlcjtcclxuICAgICAgICB9IGVsc2Uge1xyXG4gICAgICAgICAgY29uZmlnRmlsZVtwcm9tcHQuc2VjdGlvbl0gPSByZWN1cnNpdmVQcm9wcyhcclxuICAgICAgICAgICAgT2JqZWN0LmFzc2lnbih7fSwgY29uZmlnRmlsZVtwcm9tcHQuc2VjdGlvbl0gfHwge30pLFxyXG4gICAgICAgICAgICBwcm9tcHQubmFtZS5zcGxpdCgnLicpLFxyXG4gICAgICAgICAgICBwcm9tcHQuY2hvaWNlcyA/IHByb21wdC5jaG9pY2VzW2Fuc3dlcl0gOiBhbnN3ZXJcclxuICAgICAgICAgICk7XHJcbiAgICAgICAgfVxyXG5cclxuICAgICAgICBpZiAoKytxdWVzdGlvbnNDb3VudGVyID09PSBhbGxRdWVzdGlvbnMubGVuZ3RoKSB7XHJcbiAgICAgICAgICB0cnkge1xyXG4gICAgICAgICAgICBhd2FpdCBmc1Byb21pc2VzLndyaXRlRmlsZShcclxuICAgICAgICAgICAgICBjb25maWdGaWxlTmFtZSxcclxuICAgICAgICAgICAgICBKU09OLnN0cmluZ2lmeShjb25maWdGaWxlLCBudWxsLCAyKSxcclxuICAgICAgICAgICAgICAndXRmOCdcclxuICAgICAgICAgICAgKTtcclxuICAgICAgICAgIH0gY2F0Y2ggKGVycm9yKSB7XHJcbiAgICAgICAgICAgIGxvZ1dpdGhTdGFjayhcclxuICAgICAgICAgICAgICAxLFxyXG4gICAgICAgICAgICAgIGVycm9yLFxyXG4gICAgICAgICAgICAgIGBbY29uZmlnXSBBbiBlcnJvciBvY2N1cnJlZCB3aGlsZSBjcmVhdGluZyB0aGUgJHtjb25maWdGaWxlTmFtZX0gZmlsZS5gXHJcbiAgICAgICAgICAgICk7XHJcbiAgICAgICAgICB9XHJcbiAgICAgICAgICByZXR1cm4gdHJ1ZTtcclxuICAgICAgICB9XHJcbiAgICAgIH1cclxuICAgIH0pO1xyXG5cclxuICAgIHJldHVybiB0cnVlO1xyXG4gIH07XHJcblxyXG4gIC8vIEZpbmQgdGhlIGNhdGVnb3JpZXNcclxuICBjb25zdCBjaG9pY2VzID0gT2JqZWN0LmtleXMocHJvbXB0c0NvbmZpZykubWFwKChjaG9pY2UpID0+ICh7XHJcbiAgICB0aXRsZTogYCR7Y2hvaWNlfSBvcHRpb25zYCxcclxuICAgIHZhbHVlOiBjaG9pY2VcclxuICB9KSk7XHJcblxyXG4gIC8vIENhdGVnb3J5IHByb21wdFxyXG4gIHJldHVybiBwcm9tcHRzKFxyXG4gICAge1xyXG4gICAgICB0eXBlOiAnbXVsdGlzZWxlY3QnLFxyXG4gICAgICBuYW1lOiAnY2F0ZWdvcnknLFxyXG4gICAgICBtZXNzYWdlOiAnV2hpY2ggY2F0ZWdvcnkgZG8geW91IHdhbnQgdG8gY29uZmlndXJlPycsXHJcbiAgICAgIGhpbnQ6ICdTcGFjZTogU2VsZWN0IHNwZWNpZmljLCBBOiBTZWxlY3QgYWxsLCBFbnRlcjogQ29uZmlybS4nLFxyXG4gICAgICBpbnN0cnVjdGlvbnM6ICcnLFxyXG4gICAgICBjaG9pY2VzXHJcbiAgICB9LFxyXG4gICAgeyBvblN1Ym1pdCB9XHJcbiAgKTtcclxufTtcclxuXHJcbi8qKlxyXG4gKiBNYXBzIG9sZC1zdHJ1Y3R1cmVkIChQaGFudG9tSlMpIG9wdGlvbnMgdG8gYSBuZXcgY29uZmlndXJhdGlvbiBmb3JtYXRcclxuICogKFB1cHBldGVlcikuXHJcbiAqXHJcbiAqIEBwYXJhbSB7T2JqZWN0fSBvbGRPcHRpb25zIC0gT2xkLXN0cnVjdHVyZWQgb3B0aW9ucyB0byBiZSBtYXBwZWQuXHJcbiAqXHJcbiAqIEByZXR1cm5zIHtPYmplY3R9IE5ldyBvcHRpb25zIHN0cnVjdHVyZWQgYmFzZWQgb24gdGhlIGRlZmluZWQgbmVzdGVkQXJnc1xyXG4gKiBtYXBwaW5nLlxyXG4gKi9cclxuZXhwb3J0IGNvbnN0IG1hcFRvTmV3Q29uZmlnID0gKG9sZE9wdGlvbnMpID0+IHtcclxuICBjb25zdCBuZXdPcHRpb25zID0ge307XHJcbiAgLy8gQ3ljbGUgdGhyb3VnaCBvbGQtc3RydWN0dXJlZCBvcHRpb25zXHJcbiAgZm9yIChjb25zdCBba2V5LCB2YWx1ZV0gb2YgT2JqZWN0LmVudHJpZXMob2xkT3B0aW9ucykpIHtcclxuICAgIGNvbnN0IHByb3BlcnRpZXNDaGFpbiA9IG5lc3RlZEFyZ3Nba2V5XSA/IG5lc3RlZEFyZ3Nba2V5XS5zcGxpdCgnLicpIDogW107XHJcblxyXG4gICAgLy8gUG9wdWxhdGUgb2JqZWN0IGluIGNvcnJlY3QgcHJvcGVydGllcyBsZXZlbHNcclxuICAgIHByb3BlcnRpZXNDaGFpbi5yZWR1Y2UoXHJcbiAgICAgIChvYmosIHByb3AsIGluZGV4KSA9PlxyXG4gICAgICAgIChvYmpbcHJvcF0gPVxyXG4gICAgICAgICAgcHJvcGVydGllc0NoYWluLmxlbmd0aCAtIDEgPT09IGluZGV4ID8gdmFsdWUgOiBvYmpbcHJvcF0gfHwge30pLFxyXG4gICAgICBuZXdPcHRpb25zXHJcbiAgICApO1xyXG4gIH1cclxuICByZXR1cm4gbmV3T3B0aW9ucztcclxufTtcclxuXHJcbi8qKlxyXG4gKiBNZXJnZXMgdHdvIHNldHMgb2YgY29uZmlndXJhdGlvbiBvcHRpb25zLCBjb25zaWRlcmluZyBhYnNvbHV0ZSBwcm9wZXJ0aWVzLlxyXG4gKlxyXG4gKiBAcGFyYW0ge09iamVjdH0gb3B0aW9ucyAtIE9yaWdpbmFsIGNvbmZpZ3VyYXRpb24gb3B0aW9ucy5cclxuICogQHBhcmFtIHtPYmplY3R9IG5ld09wdGlvbnMgLSBOZXcgY29uZmlndXJhdGlvbiBvcHRpb25zIHRvIGJlIG1lcmdlZC5cclxuICogQHBhcmFtIHtBcnJheX0gYWJzb2x1dGVQcm9wcyAtIExpc3Qgb2YgcHJvcGVydGllcyB0aGF0IHNob3VsZFxyXG4gKiBub3QgYmUgcmVjdXJzaXZlbHkgbWVyZ2VkLlxyXG4gKlxyXG4gKiBAcmV0dXJucyB7T2JqZWN0fSBNZXJnZWQgY29uZmlndXJhdGlvbiBvcHRpb25zLlxyXG4gKi9cclxuZXhwb3J0IGNvbnN0IG1lcmdlQ29uZmlnT3B0aW9ucyA9IChvcHRpb25zLCBuZXdPcHRpb25zLCBhYnNvbHV0ZVByb3BzID0gW10pID0+IHtcclxuICBjb25zdCBtZXJnZWRPcHRpb25zID0gZGVlcENvcHkob3B0aW9ucyk7XHJcblxyXG4gIGZvciAoY29uc3QgW2tleSwgdmFsdWVdIG9mIE9iamVjdC5lbnRyaWVzKG5ld09wdGlvbnMpKSB7XHJcbiAgICBtZXJnZWRPcHRpb25zW2tleV0gPVxyXG4gICAgICBpc09iamVjdCh2YWx1ZSkgJiZcclxuICAgICAgIWFic29sdXRlUHJvcHMuaW5jbHVkZXMoa2V5KSAmJlxyXG4gICAgICBtZXJnZWRPcHRpb25zW2tleV0gIT09IHVuZGVmaW5lZFxyXG4gICAgICAgID8gbWVyZ2VDb25maWdPcHRpb25zKG1lcmdlZE9wdGlvbnNba2V5XSwgdmFsdWUsIGFic29sdXRlUHJvcHMpXHJcbiAgICAgICAgOiB2YWx1ZSAhPT0gdW5kZWZpbmVkXHJcbiAgICAgICAgICA/IHZhbHVlXHJcbiAgICAgICAgICA6IG1lcmdlZE9wdGlvbnNba2V5XTtcclxuICB9XHJcblxyXG4gIHJldHVybiBtZXJnZWRPcHRpb25zO1xyXG59O1xyXG5cclxuLyoqXHJcbiAqIEluaXRpYWxpemVzIGV4cG9ydCBzZXR0aW5ncyBiYXNlZCBvbiBwcm92aWRlZCBleHBvcnRPcHRpb25zXHJcbiAqIGFuZCBnZW5lcmFsT3B0aW9ucy5cclxuICpcclxuICogQHBhcmFtIHtPYmplY3R9IGV4cG9ydE9wdGlvbnMgLSBPcHRpb25zIHNwZWNpZmljIHRvIHRoZSBleHBvcnQgcHJvY2Vzcy5cclxuICogQHBhcmFtIHtPYmplY3R9IGdlbmVyYWxPcHRpb25zIC0gR2VuZXJhbCBjb25maWd1cmF0aW9uIG9wdGlvbnMuXHJcbiAqXHJcbiAqIEByZXR1cm5zIHtPYmplY3R9IEluaXRpYWxpemVkIGV4cG9ydCBzZXR0aW5ncy5cclxuICovXHJcbmV4cG9ydCBjb25zdCBpbml0RXhwb3J0U2V0dGluZ3MgPSAoZXhwb3J0T3B0aW9ucywgZ2VuZXJhbE9wdGlvbnMgPSB7fSkgPT4ge1xyXG4gIGxldCBvcHRpb25zID0ge307XHJcblxyXG4gIGlmIChleHBvcnRPcHRpb25zLnN2Zykge1xyXG4gICAgb3B0aW9ucyA9IGRlZXBDb3B5KGdlbmVyYWxPcHRpb25zKTtcclxuICAgIG9wdGlvbnMuZXhwb3J0LnR5cGUgPSBleHBvcnRPcHRpb25zLnR5cGUgfHwgZXhwb3J0T3B0aW9ucy5leHBvcnQudHlwZTtcclxuICAgIG9wdGlvbnMuZXhwb3J0LnNjYWxlID0gZXhwb3J0T3B0aW9ucy5zY2FsZSB8fCBleHBvcnRPcHRpb25zLmV4cG9ydC5zY2FsZTtcclxuICAgIG9wdGlvbnMuZXhwb3J0Lm91dGZpbGUgPVxyXG4gICAgICBleHBvcnRPcHRpb25zLm91dGZpbGUgfHwgZXhwb3J0T3B0aW9ucy5leHBvcnQub3V0ZmlsZTtcclxuICAgIG9wdGlvbnMucGF5bG9hZCA9IHtcclxuICAgICAgc3ZnOiBleHBvcnRPcHRpb25zLnN2Z1xyXG4gICAgfTtcclxuICB9IGVsc2Uge1xyXG4gICAgb3B0aW9ucyA9IG1lcmdlQ29uZmlnT3B0aW9ucyhcclxuICAgICAgZ2VuZXJhbE9wdGlvbnMsXHJcbiAgICAgIGV4cG9ydE9wdGlvbnMsXHJcbiAgICAgIC8vIE9taXQgZ29pbmcgZG93biByZWN1cnNpdmVseSB3aXRoIHRoZSBiZWxvd3NcclxuICAgICAgYWJzb2x1dGVQcm9wc1xyXG4gICAgKTtcclxuICB9XHJcblxyXG4gIG9wdGlvbnMuZXhwb3J0Lm91dGZpbGUgPVxyXG4gICAgb3B0aW9ucy5leHBvcnQ/Lm91dGZpbGUgfHwgYGNoYXJ0LiR7b3B0aW9ucy5leHBvcnQ/LnR5cGUgfHwgJ3BuZyd9YDtcclxuICByZXR1cm4gb3B0aW9ucztcclxufTtcclxuXHJcbi8qKlxyXG4gKiBMb2FkcyBhZGRpdGlvbmFsIGNvbmZpZ3VyYXRpb24gZnJvbSBhIHNwZWNpZmllZCBmaWxlIHVzaW5nXHJcbiAqIHRoZSAtLWxvYWRDb25maWcgb3B0aW9uLlxyXG4gKlxyXG4gKiBAcGFyYW0ge0FycmF5fSBhcmdzIC0gQ29tbWFuZC1saW5lIGFyZ3VtZW50cyB0byBjaGVjayBmb3JcclxuICogdGhlIC0tbG9hZENvbmZpZyBvcHRpb24uXHJcbiAqXHJcbiAqIEByZXR1cm5zIHtPYmplY3R9IEFkZGl0aW9uYWwgY29uZmlndXJhdGlvbiBsb2FkZWQgZnJvbSB0aGUgc3BlY2lmaWVkIGZpbGUsXHJcbiAqIG9yIGFuIGVtcHR5IG9iamVjdCBpZiBub3QgZm91bmQgb3IgaW52YWxpZC5cclxuICovXHJcbmZ1bmN0aW9uIGxvYWRDb25maWdGaWxlKGFyZ3MpIHtcclxuICAvLyBDaGVjayBpZiB0aGUgLS1sb2FkQ29uZmlnIG9wdGlvbiB3YXMgdXNlZFxyXG4gIGNvbnN0IGNvbmZpZ0luZGV4ID0gYXJncy5maW5kSW5kZXgoXHJcbiAgICAoYXJnKSA9PiBhcmcucmVwbGFjZSgvLS9nLCAnJykgPT09ICdsb2FkQ29uZmlnJ1xyXG4gICk7XHJcblxyXG4gIC8vIENoZWNrIGlmIHRoZSAtLWxvYWRDb25maWcgaGFzIGEgdmFsdWVcclxuICBpZiAoY29uZmlnSW5kZXggPiAtMSAmJiBhcmdzW2NvbmZpZ0luZGV4ICsgMV0pIHtcclxuICAgIGNvbnN0IGZpbGVOYW1lID0gYXJnc1tjb25maWdJbmRleCArIDFdO1xyXG4gICAgdHJ5IHtcclxuICAgICAgLy8gQ2hlY2sgaWYgYW4gYWRkaXRpb25hbCBjb25maWcgZmlsZSBpcyBhIGNvcnJlY3QgSlNPTiBmaWxlXHJcbiAgICAgIGlmIChmaWxlTmFtZSAmJiBmaWxlTmFtZS5lbmRzV2l0aCgnLmpzb24nKSkge1xyXG4gICAgICAgIC8vIExvYWQgYW4gb3B0aW9uYWwgY3VzdG9tIEpTT04gY29uZmlnIGZpbGVcclxuICAgICAgICByZXR1cm4gSlNPTi5wYXJzZShyZWFkRmlsZVN5bmMoZmlsZU5hbWUpKTtcclxuICAgICAgfVxyXG4gICAgfSBjYXRjaCAoZXJyb3IpIHtcclxuICAgICAgbG9nV2l0aFN0YWNrKFxyXG4gICAgICAgIDIsXHJcbiAgICAgICAgZXJyb3IsXHJcbiAgICAgICAgYFtjb25maWddIFVuYWJsZSB0byBsb2FkIHRoZSBjb25maWd1cmF0aW9uIGZyb20gdGhlICR7ZmlsZU5hbWV9IGZpbGUuYFxyXG4gICAgICApO1xyXG4gICAgfVxyXG4gIH1cclxuXHJcbiAgLy8gTm8gYWRkaXRpb25hbCBvcHRpb25zIHRvIHJldHVyblxyXG4gIHJldHVybiB7fTtcclxufVxyXG5cclxuLyoqXHJcbiAqIFVwZGF0ZXMgdGhlIGRlZmF1bHQgY29uZmlndXJhdGlvbiBvYmplY3Qgd2l0aCB2YWx1ZXMgZnJvbSBhIGN1c3RvbSBvYmplY3RcclxuICogYW5kIGVudmlyb25tZW50IHZhcmlhYmxlcy5cclxuICpcclxuICogQHBhcmFtIHtPYmplY3R9IGNvbmZpZ09iaiAtIFRoZSBkZWZhdWx0IGNvbmZpZ3VyYXRpb24gb2JqZWN0LlxyXG4gKiBAcGFyYW0ge09iamVjdH0gY3VzdG9tT2JqIC0gQ3VzdG9tIGNvbmZpZ3VyYXRpb24gb2JqZWN0IHRvIG92ZXJyaWRlIGRlZmF1bHRzLlxyXG4gKiBAcGFyYW0ge3N0cmluZ30gcHJvcENoYWluIC0gUHJvcGVydHkgY2hhaW4gZm9yIHRyYWNraW5nIG5lc3RlZCBwcm9wZXJ0aWVzXHJcbiAqIGR1cmluZyByZWN1cnNpb24uXHJcbiAqL1xyXG5mdW5jdGlvbiB1cGRhdGVEZWZhdWx0Q29uZmlnKGNvbmZpZ09iaiwgY3VzdG9tT2JqID0ge30sIHByb3BDaGFpbiA9ICcnKSB7XHJcbiAgT2JqZWN0LmtleXMoY29uZmlnT2JqKS5mb3JFYWNoKChrZXkpID0+IHtcclxuICAgIGNvbnN0IGVudHJ5ID0gY29uZmlnT2JqW2tleV07XHJcbiAgICBjb25zdCBjdXN0b21WYWx1ZSA9IGN1c3RvbU9iaiAmJiBjdXN0b21PYmpba2V5XTtcclxuXHJcbiAgICBpZiAodHlwZW9mIGVudHJ5LnZhbHVlID09PSAndW5kZWZpbmVkJykge1xyXG4gICAgICB1cGRhdGVEZWZhdWx0Q29uZmlnKGVudHJ5LCBjdXN0b21WYWx1ZSwgYCR7cHJvcENoYWlufS4ke2tleX1gKTtcclxuICAgIH0gZWxzZSB7XHJcbiAgICAgIC8vIElmIGEgdmFsdWUgZnJvbSBhIGN1c3RvbSBKU09OIGV4aXN0cywgaXQgdGFrZSBwcmVjZWRlbmNlXHJcbiAgICAgIGlmIChjdXN0b21WYWx1ZSAhPT0gdW5kZWZpbmVkKSB7XHJcbiAgICAgICAgZW50cnkudmFsdWUgPSBjdXN0b21WYWx1ZTtcclxuICAgICAgfVxyXG5cclxuICAgICAgLy8gSWYgYSB2YWx1ZSBmcm9tIGFuIGVudiB2YXJpYWJsZSBleGlzdHMsIGl0IHRha2UgcHJlY2VkZW5jZVxyXG4gICAgICBpZiAoZW50cnkuZW52TGluayBpbiBlbnZzKSB7XHJcbiAgICAgICAgZW50cnkudmFsdWUgPSBlbnZzW2VudHJ5LmVudkxpbmtdO1xyXG4gICAgICB9XHJcbiAgICB9XHJcbiAgfSk7XHJcbn1cclxuXHJcbi8qKlxyXG4gKiBJbml0aWFsaXplcyBvcHRpb25zIG9iamVjdCBiYXNlZCBvbiBwcm92aWRlZCBpdGVtcywgc2V0dGluZyB2YWx1ZXMgZnJvbVxyXG4gKiBuZXN0ZWQgcHJvcGVydGllcyByZWN1cnNpdmVseS5cclxuICpcclxuICogQHBhcmFtIHtPYmplY3R9IGl0ZW1zIC0gQ29uZmlndXJhdGlvbiBpdGVtcyB0byBiZSB1c2VkIGZvciBpbml0aWFsaXppbmdcclxuICogb3B0aW9ucy5cclxuICpcclxuICogQHJldHVybnMge09iamVjdH0gSW5pdGlhbGl6ZWQgb3B0aW9ucyBvYmplY3QuXHJcbiAqL1xyXG5mdW5jdGlvbiBpbml0T3B0aW9ucyhpdGVtcykge1xyXG4gIGxldCBvcHRpb25zID0ge307XHJcbiAgZm9yIChjb25zdCBbbmFtZSwgaXRlbV0gb2YgT2JqZWN0LmVudHJpZXMoaXRlbXMpKSB7XHJcbiAgICBvcHRpb25zW25hbWVdID0gT2JqZWN0LnByb3RvdHlwZS5oYXNPd25Qcm9wZXJ0eS5jYWxsKGl0ZW0sICd2YWx1ZScpXHJcbiAgICAgID8gaXRlbS52YWx1ZVxyXG4gICAgICA6IGluaXRPcHRpb25zKGl0ZW0pO1xyXG4gIH1cclxuICByZXR1cm4gb3B0aW9ucztcclxufVxyXG5cclxuLyoqXHJcbiAqIFBhaXJzIGFyZ3VtZW50IHZhbHVlcyB3aXRoIGNvcnJlc3BvbmRpbmcgb3B0aW9ucyBpbiB0aGUgY29uZmlndXJhdGlvbixcclxuICogdXBkYXRpbmcgdGhlIG9wdGlvbnMgb2JqZWN0LlxyXG4gKlxyXG4gKiBAcGFyYW0ge09iamVjdH0gb3B0aW9ucyAtIENvbmZpZ3VyYXRpb24gb3B0aW9ucyBvYmplY3QgdG8gYmUgdXBkYXRlZC5cclxuICogQHBhcmFtIHtBcnJheX0gYXJncyAtIENvbW1hbmQtbGluZSBhcmd1bWVudHMgY29udGFpbmluZyB2YWx1ZXMgZm9yIHNwZWNpZmljXHJcbiAqIG9wdGlvbnMuXHJcbiAqIEBwYXJhbSB7T2JqZWN0fSBkZWZhdWx0Q29uZmlnIC0gRGVmYXVsdCBjb25maWd1cmF0aW9uIG9iamVjdCBmb3IgcmVmZXJlbmNlLlxyXG4gKlxyXG4gKiBAcmV0dXJucyB7T2JqZWN0fSBVcGRhdGVkIG9wdGlvbnMgb2JqZWN0LlxyXG4gKi9cclxuZnVuY3Rpb24gcGFpckFyZ3VtZW50VmFsdWUob3B0aW9ucywgYXJncywgZGVmYXVsdENvbmZpZykge1xyXG4gIGxldCBzaG93VXNhZ2UgPSBmYWxzZTtcclxuICBmb3IgKGxldCBpID0gMDsgaSA8IGFyZ3MubGVuZ3RoOyBpKyspIHtcclxuICAgIGNvbnN0IG9wdGlvbiA9IGFyZ3NbaV0ucmVwbGFjZSgvLS9nLCAnJyk7XHJcblxyXG4gICAgLy8gRmluZCB0aGUgcmlnaHQgcGxhY2UgZm9yIHByb3BlcnR5J3MgdmFsdWVcclxuICAgIGNvbnN0IHByb3BlcnRpZXNDaGFpbiA9IG5lc3RlZEFyZ3Nbb3B0aW9uXVxyXG4gICAgICA/IG5lc3RlZEFyZ3Nbb3B0aW9uXS5zcGxpdCgnLicpXHJcbiAgICAgIDogW107XHJcblxyXG4gICAgLy8gR2V0IHRoZSBjb3JyZWN0IHR5cGUgZm9yIENMSSBhcmdzIHdoaWNoIGFyZSBwYXNzZWQgYXMgc3RyaW5nc1xyXG4gICAgbGV0IGFyZ3VtZW50VHlwZTtcclxuICAgIHByb3BlcnRpZXNDaGFpbi5yZWR1Y2UoKG9iaiwgcHJvcCwgaW5kZXgpID0+IHtcclxuICAgICAgaWYgKHByb3BlcnRpZXNDaGFpbi5sZW5ndGggLSAxID09PSBpbmRleCkge1xyXG4gICAgICAgIGFyZ3VtZW50VHlwZSA9IG9ialtwcm9wXS50eXBlO1xyXG4gICAgICB9XHJcbiAgICAgIHJldHVybiBvYmpbcHJvcF07XHJcbiAgICB9LCBkZWZhdWx0Q29uZmlnKTtcclxuXHJcbiAgICBwcm9wZXJ0aWVzQ2hhaW4ucmVkdWNlKChvYmosIHByb3AsIGluZGV4KSA9PiB7XHJcbiAgICAgIGlmIChwcm9wZXJ0aWVzQ2hhaW4ubGVuZ3RoIC0gMSA9PT0gaW5kZXgpIHtcclxuICAgICAgICAvLyBGaW5kcyBhbiBvcHRpb24gYW5kIHNldCBhIGNvcnJlc3BvbmRpbmcgdmFsdWVcclxuICAgICAgICBpZiAodHlwZW9mIG9ialtwcm9wXSAhPT0gJ3VuZGVmaW5lZCcpIHtcclxuICAgICAgICAgIGlmIChhcmdzWysraV0pIHtcclxuICAgICAgICAgICAgaWYgKGFyZ3VtZW50VHlwZSA9PT0gJ2Jvb2xlYW4nKSB7XHJcbiAgICAgICAgICAgICAgb2JqW3Byb3BdID0gdG9Cb29sZWFuKGFyZ3NbaV0pO1xyXG4gICAgICAgICAgICB9IGVsc2UgaWYgKGFyZ3VtZW50VHlwZSA9PT0gJ251bWJlcicpIHtcclxuICAgICAgICAgICAgICBvYmpbcHJvcF0gPSArYXJnc1tpXTtcclxuICAgICAgICAgICAgfSBlbHNlIGlmIChhcmd1bWVudFR5cGUuaW5kZXhPZignXScpID49IDApIHtcclxuICAgICAgICAgICAgICBvYmpbcHJvcF0gPSBhcmdzW2ldLnNwbGl0KCcsJyk7XHJcbiAgICAgICAgICAgIH0gZWxzZSB7XHJcbiAgICAgICAgICAgICAgb2JqW3Byb3BdID0gYXJnc1tpXTtcclxuICAgICAgICAgICAgfVxyXG4gICAgICAgICAgfSBlbHNlIHtcclxuICAgICAgICAgICAgbG9nKFxyXG4gICAgICAgICAgICAgIDIsXHJcbiAgICAgICAgICAgICAgYFtjb25maWddIE1pc3NpbmcgdmFsdWUgZm9yIHRoZSAnJHtvcHRpb259JyBhcmd1bWVudC4gVXNpbmcgdGhlIGRlZmF1bHQgdmFsdWUuYFxyXG4gICAgICAgICAgICApO1xyXG4gICAgICAgICAgICBzaG93VXNhZ2UgPSB0cnVlO1xyXG4gICAgICAgICAgfVxyXG4gICAgICAgIH1cclxuICAgICAgfVxyXG4gICAgICByZXR1cm4gb2JqW3Byb3BdO1xyXG4gICAgfSwgb3B0aW9ucyk7XHJcbiAgfVxyXG5cclxuICAvLyBEaXNwbGF5IHRoZSB1c2FnZSBmb3IgdGhlIHJlZmVyZW5jZSBpZiBuZWVkZWRcclxuICBpZiAoc2hvd1VzYWdlKSB7XHJcbiAgICBwcmludFVzYWdlKGRlZmF1bHRDb25maWcpO1xyXG4gIH1cclxuXHJcbiAgcmV0dXJuIG9wdGlvbnM7XHJcbn1cclxuXHJcbi8qKlxyXG4gKiBSZWN1cnNpdmVseSB1cGRhdGVzIHByb3BlcnRpZXMgaW4gYW4gb2JqZWN0IGJhc2VkIG9uIG5lc3RlZCBuYW1lcyBhbmQgYXNzaWduc1xyXG4gKiB0aGUgZmluYWwgdmFsdWUuXHJcbiAqXHJcbiAqIEBwYXJhbSB7T2JqZWN0fSBvYmplY3RUb1VwZGF0ZSAtIFRoZSBvYmplY3QgdG8gYmUgdXBkYXRlZC5cclxuICogQHBhcmFtIHtBcnJheX0gbmVzdGVkTmFtZXMgLSBBcnJheSBvZiBuZXN0ZWQgcHJvcGVydHkgbmFtZXMuXHJcbiAqIEBwYXJhbSB7YW55fSB2YWx1ZSAtIFRoZSBmaW5hbCB2YWx1ZSB0byBiZSBhc3NpZ25lZC5cclxuICpcclxuICogQHJldHVybnMge09iamVjdH0gVXBkYXRlZCBvYmplY3Qgd2l0aCBhc3NpZ25lZCB2YWx1ZXMuXHJcbiAqL1xyXG5mdW5jdGlvbiByZWN1cnNpdmVQcm9wcyhvYmplY3RUb1VwZGF0ZSwgbmVzdGVkTmFtZXMsIHZhbHVlKSB7XHJcbiAgd2hpbGUgKG5lc3RlZE5hbWVzLmxlbmd0aCA+IDEpIHtcclxuICAgIGNvbnN0IHByb3BOYW1lID0gbmVzdGVkTmFtZXMuc2hpZnQoKTtcclxuXHJcbiAgICAvLyBDcmVhdGUgYSBwcm9wZXJ0eSBpbiBvYmplY3QgaWYgaXQgZG9lc24ndCBleGlzdFxyXG4gICAgaWYgKCFPYmplY3QucHJvdG90eXBlLmhhc093blByb3BlcnR5LmNhbGwob2JqZWN0VG9VcGRhdGUsIHByb3BOYW1lKSkge1xyXG4gICAgICBvYmplY3RUb1VwZGF0ZVtwcm9wTmFtZV0gPSB7fTtcclxuICAgIH1cclxuXHJcbiAgICAvLyBDYWxsIGZ1bmN0aW9uIGFnYWluIGlmIHRoZXJlIHN0aWxsIG5hbWVzIHRvIGdvXHJcbiAgICBvYmplY3RUb1VwZGF0ZVtwcm9wTmFtZV0gPSByZWN1cnNpdmVQcm9wcyhcclxuICAgICAgT2JqZWN0LmFzc2lnbih7fSwgb2JqZWN0VG9VcGRhdGVbcHJvcE5hbWVdKSxcclxuICAgICAgbmVzdGVkTmFtZXMsXHJcbiAgICAgIHZhbHVlXHJcbiAgICApO1xyXG5cclxuICAgIHJldHVybiBvYmplY3RUb1VwZGF0ZTtcclxuICB9XHJcblxyXG4gIC8vIEFzc2lnbiB0aGUgZmluYWwgdmFsdWVcclxuICBvYmplY3RUb1VwZGF0ZVtuZXN0ZWROYW1lc1swXV0gPSB2YWx1ZTtcclxuICByZXR1cm4gb2JqZWN0VG9VcGRhdGU7XHJcbn1cclxuXHJcbmV4cG9ydCBkZWZhdWx0IHtcclxuICBnZXRPcHRpb25zLFxyXG4gIHNldE9wdGlvbnMsXHJcbiAgbWFudWFsQ29uZmlnLFxyXG4gIG1hcFRvTmV3Q29uZmlnLFxyXG4gIG1lcmdlQ29uZmlnT3B0aW9ucyxcclxuICBpbml0RXhwb3J0U2V0dGluZ3NcclxufTtcclxuIiwiLyoqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKipcclxuXHJcbkhpZ2hjaGFydHMgRXhwb3J0IFNlcnZlclxyXG5cclxuQ29weXJpZ2h0IChjKSAyMDE2LTIwMjQsIEhpZ2hzb2Z0XHJcblxyXG5MaWNlbmNlZCB1bmRlciB0aGUgTUlUIGxpY2VuY2UuXHJcblxyXG5BZGRpdGlvbmFsbHkgYSB2YWxpZCBIaWdoY2hhcnRzIGxpY2Vuc2UgaXMgcmVxdWlyZWQgZm9yIHVzZS5cclxuXHJcblNlZSBMSUNFTlNFIGZpbGUgaW4gcm9vdCBmb3IgZGV0YWlscy5cclxuXHJcbioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKiovXHJcblxyXG5pbXBvcnQgZnMgZnJvbSAnZnMnO1xyXG5pbXBvcnQgKiBhcyB1cmwgZnJvbSAndXJsJztcclxuaW1wb3J0IHBhdGggZnJvbSAnbm9kZTpwYXRoJztcclxuXHJcbmltcG9ydCBwdXBwZXRlZXIgZnJvbSAncHVwcGV0ZWVyJztcclxuXHJcbi8vIFdvcmthcm91bmQgZm9yIGh0dHBzOi8vYnVncy5jaHJvbWl1bS5vcmcvcC9jaHJvbWl1bS9pc3N1ZXMvZGV0YWlsP2lkPTE0NjMzMjhcclxuLy8gTm90IGlkZWFsIC0gbGVhdmVzIHRyYXNoIGluIHRoZSBGU1xyXG5pbXBvcnQgeyByYW5kb21CeXRlcyB9IGZyb20gJ25vZGU6Y3J5cHRvJztcclxuXHJcbmltcG9ydCB7IGdldENhY2hlUGF0aCB9IGZyb20gJy4vY2FjaGUuanMnO1xyXG5pbXBvcnQgeyBsb2csIGxvZ1dpdGhTdGFjayB9IGZyb20gJy4vbG9nZ2VyLmpzJztcclxuXHJcbmltcG9ydCBFeHBvcnRFcnJvciBmcm9tICcuL2Vycm9ycy9FeHBvcnRFcnJvci5qcyc7XHJcblxyXG5jb25zdCBSQU5ET01fUElEID0gcmFuZG9tQnl0ZXMoNjQpLnRvU3RyaW5nKCdiYXNlNjR1cmwnKTtcclxuY29uc3QgUFVQUEVURUVSX0RJUiA9IHBhdGguam9pbigndG1wJywgYHB1cHBldGVlci0ke1JBTkRPTV9QSUR9YCk7XHJcbmNvbnN0IERBVEFfRElSID0gcGF0aC5qb2luKFBVUFBFVEVFUl9ESVIsICdwcm9maWxlJyk7XHJcblxyXG4vLyBUaGUgbWluaW1hbCBhcmdzIHRvIHNwZWVkIHVwIHRoZSBicm93c2VyXHJcbmNvbnN0IG1pbmltYWxBcmdzID0gW1xyXG4gIGAtLXVzZXItZGF0YS1kaXI9JHtEQVRBX0RJUn1gLFxyXG4gICctLWF1dG9wbGF5LXBvbGljeT11c2VyLWdlc3R1cmUtcmVxdWlyZWQnLFxyXG4gICctLWRpc2FibGUtYmFja2dyb3VuZC1uZXR3b3JraW5nJyxcclxuICAnLS1kaXNhYmxlLWJhY2tncm91bmQtdGltZXItdGhyb3R0bGluZycsXHJcbiAgJy0tZGlzYWJsZS1iYWNrZ3JvdW5kaW5nLW9jY2x1ZGVkLXdpbmRvd3MnLFxyXG4gICctLWRpc2FibGUtYnJlYWtwYWQnLFxyXG4gICctLWRpc2FibGUtY2xpZW50LXNpZGUtcGhpc2hpbmctZGV0ZWN0aW9uJyxcclxuICAnLS1kaXNhYmxlLWNvbXBvbmVudC11cGRhdGUnLFxyXG4gICctLWRpc2FibGUtZGVmYXVsdC1hcHBzJyxcclxuICAnLS1kaXNhYmxlLWRldi1zaG0tdXNhZ2UnLFxyXG4gICctLWRpc2FibGUtZG9tYWluLXJlbGlhYmlsaXR5JyxcclxuICAnLS1kaXNhYmxlLWV4dGVuc2lvbnMnLFxyXG4gICctLWRpc2FibGUtZmVhdHVyZXM9QXVkaW9TZXJ2aWNlT3V0T2ZQcm9jZXNzJyxcclxuICAnLS1kaXNhYmxlLWhhbmctbW9uaXRvcicsXHJcbiAgJy0tZGlzYWJsZS1pcGMtZmxvb2RpbmctcHJvdGVjdGlvbicsXHJcbiAgJy0tZGlzYWJsZS1ub3RpZmljYXRpb25zJyxcclxuICAnLS1kaXNhYmxlLW9mZmVyLXN0b3JlLXVubWFza2VkLXdhbGxldC1jYXJkcycsXHJcbiAgJy0tZGlzYWJsZS1wb3B1cC1ibG9ja2luZycsXHJcbiAgJy0tZGlzYWJsZS1wcmludC1wcmV2aWV3JyxcclxuICAnLS1kaXNhYmxlLXByb21wdC1vbi1yZXBvc3QnLFxyXG4gICctLWRpc2FibGUtcmVuZGVyZXItYmFja2dyb3VuZGluZycsXHJcbiAgJy0tZGlzYWJsZS1zZXNzaW9uLWNyYXNoZWQtYnViYmxlJyxcclxuICAnLS1kaXNhYmxlLXNldHVpZC1zYW5kYm94JyxcclxuICAnLS1kaXNhYmxlLXNwZWVjaC1hcGknLFxyXG4gICctLWRpc2FibGUtc3luYycsXHJcbiAgJy0taGlkZS1jcmFzaC1yZXN0b3JlLWJ1YmJsZScsXHJcbiAgJy0taGlkZS1zY3JvbGxiYXJzJyxcclxuICAnLS1pZ25vcmUtZ3B1LWJsYWNrbGlzdCcsXHJcbiAgJy0tbWV0cmljcy1yZWNvcmRpbmctb25seScsXHJcbiAgJy0tbXV0ZS1hdWRpbycsXHJcbiAgJy0tbm8tZGVmYXVsdC1icm93c2VyLWNoZWNrJyxcclxuICAnLS1uby1maXJzdC1ydW4nLFxyXG4gICctLW5vLXBpbmdzJyxcclxuICAnLS1uby1zYW5kYm94JyxcclxuICAnLS1uby16eWdvdGUnLFxyXG4gICctLXBhc3N3b3JkLXN0b3JlPWJhc2ljJyxcclxuICAnLS11c2UtbW9jay1rZXljaGFpbidcclxuXTtcclxuXHJcbmNvbnN0IF9fZGlybmFtZSA9IHVybC5maWxlVVJMVG9QYXRoKG5ldyBVUkwoJy4nLCBpbXBvcnQubWV0YS51cmwpKTtcclxuXHJcbmNvbnN0IHRlbXBsYXRlID0gZnMucmVhZEZpbGVTeW5jKFxyXG4gIF9fZGlybmFtZSArICcvLi4vdGVtcGxhdGVzL3RlbXBsYXRlLmh0bWwnLFxyXG4gICd1dGY4J1xyXG4pO1xyXG5cclxubGV0IGJyb3dzZXI7XHJcblxyXG4vKipcclxuICogU2V0cyB0aGUgY29udGVudCBmb3IgYSBQdXBwZXRlZXIgUGFnZSB1c2luZyBhIHByZWRlZmluZWQgdGVtcGxhdGVcclxuICogYW5kIGFkZGl0aW9uYWwgc2NyaXB0cy4gQWxzbywgc2V0cyB0aGUgcGFnZWVycm9yIGluIG9yZGVyIHRvIGNhdGNoXHJcbiAqIGFuZCBkaXNwbGF5IGVycm9ycyBmcm9tIHRoZSB3aW5kb3cgY29udGV4dC5cclxuICpcclxuICogQHBhcmFtIHtPYmplY3R9IHBhZ2UgLSBUaGUgUHVwcGV0ZWVyIFBhZ2Ugb2JqZWN0IGZvciB3aGljaCB0aGUgY29udGVudFxyXG4gKiBpcyBiZWluZyBzZXQuXHJcbiAqL1xyXG5jb25zdCBzZXRQYWdlQ29udGVudCA9IGFzeW5jIChwYWdlKSA9PiB7XHJcbiAgYXdhaXQgcGFnZS5zZXRDb250ZW50KHRlbXBsYXRlKTtcclxuICBhd2FpdCBwYWdlLmFkZFNjcmlwdFRhZyh7IHBhdGg6IGAke2dldENhY2hlUGF0aCgpfS9zb3VyY2VzLmpzYCB9KTtcclxuICAvLyBlc2xpbnQtZGlzYWJsZS1uZXh0LWxpbmUgbm8tdW5kZWZcclxuICBhd2FpdCBwYWdlLmV2YWx1YXRlKCgpID0+IHdpbmRvdy5zZXR1cEhpZ2hjaGFydHMoKSk7XHJcblxyXG4gIHBhZ2Uub24oJ3BhZ2VlcnJvcicsIGFzeW5jIChlcnJvcikgPT4ge1xyXG4gICAgLy8gVE9ETzogQ29uc2lkZXIgYWRkaW5nIGEgc3dpdGNoIGhlcmUgdGhhdCB0dXJucyBvbiBsb2coMCkgbG9nZ2luZ1xyXG4gICAgLy8gb24gcGFnZSBlcnJvcnMuXHJcbiAgICBhd2FpdCBwYWdlLiRldmFsKFxyXG4gICAgICAnI2NvbnRhaW5lcicsXHJcbiAgICAgIChlbGVtZW50LCBlcnJvck1lc3NhZ2UpID0+IHtcclxuICAgICAgICAvLyBlc2xpbnQtZGlzYWJsZS1uZXh0LWxpbmUgbm8tdW5kZWZcclxuICAgICAgICBpZiAod2luZG93Ll9kaXNwbGF5RXJyb3JzKSB7XHJcbiAgICAgICAgICBlbGVtZW50LmlubmVySFRNTCA9IGVycm9yTWVzc2FnZTtcclxuICAgICAgICB9XHJcbiAgICAgIH0sXHJcbiAgICAgIGA8aDE+Q2hhcnQgaW5wdXQgZGF0YSBlcnJvcjwvaDE+JHtlcnJvci50b1N0cmluZygpfWBcclxuICAgICk7XHJcbiAgfSk7XHJcbn07XHJcblxyXG4vKipcclxuICogQ2xlYXJzIHRoZSBjb250ZW50IG9mIGEgUHVwcGV0ZWVyIFBhZ2UgYmFzZWQgb24gdGhlIHNwZWNpZmllZCBtb2RlLlxyXG4gKlxyXG4gKiBAcGFyYW0ge09iamVjdH0gcGFnZSAtIFRoZSBQdXBwZXRlZXIgUGFnZSBvYmplY3QgdG8gYmUgY2xlYXJlZC5cclxuICogQHBhcmFtIHtib29sZWFufSBoYXJkUmVzZXQgLSBBIGZsYWcgaW5kaWNhdGluZyB0aGUgdHlwZSBvZiBjbGVhcmluZ1xyXG4gKiB0byBiZSBwZXJmb3JtZWQuIElmIHRydWUsIG5hdmlnYXRlcyB0byAnYWJvdXQ6YmxhbmsnIGFuZCByZXNldHMgY29udGVudFxyXG4gKiBhbmQgc2NyaXB0cy4gSWYgZmFsc2UsIGNsZWFycyB0aGUgYm9keSBjb250ZW50IGJ5IHNldHRpbmcgYSBwcmVkZWZpbmVkIEhUTUxcclxuICogc3RydWN0dXJlLlxyXG4gKlxyXG4gKiBAdGhyb3dzIHtFcnJvcn0gTG9ncyB0aHJvd24gZXJyb3IgaWYgY2xlYXJpbmcgdGhlIHBhZ2UgY29udGVudCBmYWlscy5cclxuICovXHJcbmV4cG9ydCBjb25zdCBjbGVhclBhZ2UgPSBhc3luYyAocGFnZSwgaGFyZFJlc2V0ID0gZmFsc2UpID0+IHtcclxuICB0cnkge1xyXG4gICAgaWYgKGhhcmRSZXNldCkge1xyXG4gICAgICAvLyBOYXZpZ2F0ZSB0byBhYm91dDpibGFua1xyXG4gICAgICBhd2FpdCBwYWdlLmdvdG8oJ2Fib3V0OmJsYW5rJyk7XHJcblxyXG4gICAgICAvLyBTZXQgdGhlIGNvbnRlbnQgYW5kIGFuZCBzY3JpcHRzIGFnYWluXHJcbiAgICAgIGF3YWl0IHNldFBhZ2VDb250ZW50KHBhZ2UpO1xyXG4gICAgfSBlbHNlIHtcclxuICAgICAgLy8gQ2xlYXIgYm9keSBjb250ZW50XHJcbiAgICAgIGF3YWl0IHBhZ2UuZXZhbHVhdGUoKCkgPT4ge1xyXG4gICAgICAgIGRvY3VtZW50LmJvZHkuaW5uZXJIVE1MID1cclxuICAgICAgICAgICc8ZGl2IGlkPVwiY2hhcnQtY29udGFpbmVyXCI+PGRpdiBpZD1cImNvbnRhaW5lclwiPjwvZGl2PjwvZGl2Pic7XHJcbiAgICAgIH0pO1xyXG4gICAgfVxyXG4gIH0gY2F0Y2ggKGVycm9yKSB7XHJcbiAgICBsb2dXaXRoU3RhY2soXHJcbiAgICAgIDIsXHJcbiAgICAgIGVycm9yLFxyXG4gICAgICAnW2Jyb3dzZXJdIENvdWxkIG5vdCBjbGVhciB0aGUgY29udGVudCBvZiB0aGUgcGFnZS4nXHJcbiAgICApO1xyXG4gIH1cclxufTtcclxuXHJcbi8qKlxyXG4gKiBDcmVhdGVzIGEgbmV3IFB1cHBldGVlciBQYWdlIHdpdGhpbiBhbiBleGlzdGluZyBicm93c2VyIGluc3RhbmNlLlxyXG4gKlxyXG4gKiBJZiB0aGUgYnJvd3NlciBpbnN0YW5jZSBpcyBub3QgYXZhaWxhYmxlLCByZXR1cm5zIGZhbHNlLlxyXG4gKlxyXG4gKiBUaGUgZnVuY3Rpb24gY3JlYXRlcyBhIG5ldyBwYWdlLCBkaXNhYmxlcyBjYWNoaW5nLCBzZXRzIGNvbnRlbnQgdXNpbmdcclxuICogc2V0UGFnZUNvbnRlbnQoKSwgYW5kIHJldHVybnMgdGhlIGNyZWF0ZWQgUHVwcGV0ZWVyIFBhZ2UuXHJcbiAqXHJcbiAqIEByZXR1cm5zIHsoYm9vbGVhbnxvYmplY3QpfSBSZXR1cm5zIGZhbHNlIGlmIHRoZSBicm93c2VyIGluc3RhbmNlIGlzIG5vdFxyXG4gKiBhdmFpbGFibGUsIG9yIGEgUHVwcGV0ZWVyIFBhZ2Ugb2JqZWN0IHJlcHJlc2VudGluZyB0aGUgbmV3bHkgY3JlYXRlZCBwYWdlLlxyXG4gKi9cclxuZXhwb3J0IGNvbnN0IG5ld1BhZ2UgPSBhc3luYyAoKSA9PiB7XHJcbiAgaWYgKCFicm93c2VyKSB7XHJcbiAgICByZXR1cm4gZmFsc2U7XHJcbiAgfVxyXG5cclxuICBjb25zdCBwYWdlID0gYXdhaXQgYnJvd3Nlci5uZXdQYWdlKCk7XHJcblxyXG4gIC8vIERpc2FibGUgY2FjaGVcclxuICBhd2FpdCBwYWdlLnNldENhY2hlRW5hYmxlZChmYWxzZSk7XHJcblxyXG4gIC8vIFNldCB0aGUgY29udGVudFxyXG4gIGF3YWl0IHNldFBhZ2VDb250ZW50KHBhZ2UpO1xyXG4gIHJldHVybiBwYWdlO1xyXG59O1xyXG5cclxuLyoqXHJcbiAqIENyZWF0ZXMgYSBQdXBwZXRlZXIgYnJvd3NlciBpbnN0YW5jZSB3aXRoIHRoZSBzcGVjaWZpZWQgYXJndW1lbnRzLlxyXG4gKlxyXG4gKiBAcGFyYW0ge0FycmF5fSBwdXBwZXRlZXJBcmdzIC0gQWRkaXRpb25hbCBhcmd1bWVudHMgZm9yIFB1cHBldGVlciBsYXVuY2guXHJcbiAqXHJcbiAqIEByZXR1cm5zIHtQcm9taXNlPG9iamVjdD59IEEgUHJvbWlzZSByZXNvbHZpbmcgdG8gdGhlIFB1cHBldGVlciBicm93c2VyXHJcbiAqIGluc3RhbmNlLlxyXG4gKlxyXG4gKiBAdGhyb3dzIHtFeHBvcnRFcnJvcn0gVGhyb3dzIGFuIEV4cG9ydEVycm9yIGlmIG1heCByZXRyaWVzIHRvIG9wZW4gYSBicm93c2VyXHJcbiAqIGluc3RhbmNlIGFyZSByZWFjaGVkLCBvciBpZiBubyBicm93c2VyIGluc3RhbmNlIGlzIGZvdW5kIGFmdGVyIHJldHJpZXMuXHJcbiAqL1xyXG5leHBvcnQgY29uc3QgY3JlYXRlID0gYXN5bmMgKHB1cHBldGVlckFyZ3MpID0+IHtcclxuICBjb25zdCBhbGxBcmdzID0gWy4uLm1pbmltYWxBcmdzLCAuLi4ocHVwcGV0ZWVyQXJncyB8fCBbXSldO1xyXG5cclxuICAvLyBDcmVhdGUgYSBicm93c2VyXHJcbiAgaWYgKCFicm93c2VyKSB7XHJcbiAgICBsZXQgdHJ5Q291bnQgPSAwO1xyXG5cclxuICAgIGNvbnN0IG9wZW4gPSBhc3luYyAoKSA9PiB7XHJcbiAgICAgIHRyeSB7XHJcbiAgICAgICAgbG9nKFxyXG4gICAgICAgICAgMyxcclxuICAgICAgICAgIGBbYnJvd3Nlcl0gQXR0ZW1wdGluZyB0byBnZXQgYSBicm93c2VyIGluc3RhbmNlICh0cnkgJHsrK3RyeUNvdW50fSkuYFxyXG4gICAgICAgICk7XHJcbiAgICAgICAgYnJvd3NlciA9IGF3YWl0IHB1cHBldGVlci5sYXVuY2goe1xyXG4gICAgICAgICAgaGVhZGxlc3M6ICduZXcnLFxyXG4gICAgICAgICAgYXJnczogYWxsQXJncyxcclxuICAgICAgICAgIHVzZXJEYXRhRGlyOiAnLi90bXAvJ1xyXG4gICAgICAgIH0pO1xyXG4gICAgICB9IGNhdGNoIChlcnJvcikge1xyXG4gICAgICAgIGxvZ1dpdGhTdGFjayhcclxuICAgICAgICAgIDEsXHJcbiAgICAgICAgICBlcnJvcixcclxuICAgICAgICAgICdbYnJvd3Nlcl0gRmFpbGVkIHRvIGxhdW5jaCBhIGJyb3dzZXIgaW5zdGFuY2UuJ1xyXG4gICAgICAgICk7XHJcblxyXG4gICAgICAgIC8vIFJldHJ5IHRvIGxhdW5jaCBicm93c2VyIHVudGlsIHJlYWNoaW5nIG1heCBhdHRlbXB0c1xyXG4gICAgICAgIGlmICh0cnlDb3VudCA8IDI1KSB7XHJcbiAgICAgICAgICBsb2coMywgYFticm93c2VyXSBSZXRyeSB0byBvcGVuIGEgYnJvd3NlciAoJHt0cnlDb3VudH0gb3V0IG9mIDI1KS5gKTtcclxuICAgICAgICAgIGF3YWl0IG5ldyBQcm9taXNlKChyZXNwb25zZSkgPT4gc2V0VGltZW91dChyZXNwb25zZSwgNDAwMCkpO1xyXG4gICAgICAgICAgYXdhaXQgb3BlbigpO1xyXG4gICAgICAgIH0gZWxzZSB7XHJcbiAgICAgICAgICB0aHJvdyBlcnJvcjtcclxuICAgICAgICB9XHJcbiAgICAgIH1cclxuICAgIH07XHJcblxyXG4gICAgdHJ5IHtcclxuICAgICAgYXdhaXQgb3BlbigpO1xyXG4gICAgfSBjYXRjaCAoZXJyb3IpIHtcclxuICAgICAgdGhyb3cgbmV3IEV4cG9ydEVycm9yKFxyXG4gICAgICAgICdbYnJvd3Nlcl0gTWF4aW11bSByZXRyaWVzIHRvIG9wZW4gYSBicm93c2VyIGluc3RhbmNlIHJlYWNoZWQuJ1xyXG4gICAgICApLnNldEVycm9yKGVycm9yKTtcclxuICAgIH1cclxuXHJcbiAgICBpZiAoIWJyb3dzZXIpIHtcclxuICAgICAgdGhyb3cgbmV3IEV4cG9ydEVycm9yKCdbYnJvd3Nlcl0gQ2Fubm90IGZpbmQgYSBicm93c2VyIHRvIG9wZW4uJyk7XHJcbiAgICB9XHJcbiAgfVxyXG5cclxuICAvLyBSZXR1cm4gYSBicm93c2VyIHByb21pc2VcclxuICByZXR1cm4gYnJvd3NlcjtcclxufTtcclxuXHJcbi8qKlxyXG4gKiBSZXRyaWV2ZXMgdGhlIGV4aXN0aW5nIFB1cHBldGVlciBicm93c2VyIGluc3RhbmNlLlxyXG4gKlxyXG4gKiBAcmV0dXJucyB7UHJvbWlzZTxvYmplY3Q+fSBBIFByb21pc2UgcmVzb2x2aW5nIHRvIHRoZSBQdXBwZXRlZXIgYnJvd3NlclxyXG4gKiBpbnN0YW5jZS5cclxuICpcclxuICogQHRocm93cyB7RXhwb3J0RXJyb3J9IFRocm93cyBhbiBFeHBvcnRFcnJvciBpZiBubyB2YWxpZCBicm93c2VyIGhhcyBiZWVuXHJcbiAqIGNyZWF0ZWQuXHJcbiAqL1xyXG5leHBvcnQgY29uc3QgZ2V0ID0gYXN5bmMgKCkgPT4ge1xyXG4gIGlmICghYnJvd3Nlcikge1xyXG4gICAgdGhyb3cgbmV3IEV4cG9ydEVycm9yKCdbYnJvd3Nlcl0gTm8gdmFsaWQgYnJvd3NlciBoYXMgYmVlbiBjcmVhdGVkLicpO1xyXG4gIH1cclxuXHJcbiAgcmV0dXJuIGJyb3dzZXI7XHJcbn07XHJcblxyXG4vKipcclxuICogQ2xvc2VzIHRoZSBQdXBwZXRlZXIgYnJvd3NlciBpbnN0YW5jZSBpZiBpdCBpcyBjb25uZWN0ZWQuXHJcbiAqXHJcbiAqIEByZXR1cm5zIHtQcm9taXNlPGJvb2xlYW4+fSBBIFByb21pc2UgcmVzb2x2aW5nIHRvIHRydWUgYWZ0ZXIgdGhlIGJyb3dzZXJcclxuICogaXMgY2xvc2VkLlxyXG4gKi9cclxuZXhwb3J0IGNvbnN0IGNsb3NlID0gYXN5bmMgKCkgPT4ge1xyXG4gIC8vIENsb3NlIHRoZSBicm93c2VyIHdoZW4gY29ubm5lY3RlZFxyXG4gIGlmIChicm93c2VyPy5pc0Nvbm5lY3RlZCgpKSB7XHJcbiAgICBhd2FpdCBicm93c2VyLmNsb3NlKCk7XHJcbiAgICBsb2coNCwgJ1ticm93c2VyXSBDbG9zZWQgdGhlIGJyb3dzZXIuJyk7XHJcbiAgfVxyXG4gIHJldHVybiB0cnVlO1xyXG59O1xyXG5cclxuZXhwb3J0IGRlZmF1bHQge1xyXG4gIG5ld1BhZ2UsXHJcbiAgY2xlYXJQYWdlLFxyXG4gIGdldCxcclxuICBjbG9zZVxyXG59O1xyXG4iLCIvKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKlxyXG5cclxuSGlnaGNoYXJ0cyBFeHBvcnQgU2VydmVyXHJcblxyXG5Db3B5cmlnaHQgKGMpIDIwMTYtMjAyNCwgSGlnaHNvZnRcclxuXHJcbkxpY2VuY2VkIHVuZGVyIHRoZSBNSVQgbGljZW5jZS5cclxuXHJcbkFkZGl0aW9uYWxseSBhIHZhbGlkIEhpZ2hjaGFydHMgbGljZW5zZSBpcyByZXF1aXJlZCBmb3IgdXNlLlxyXG5cclxuU2VlIExJQ0VOU0UgZmlsZSBpbiByb290IGZvciBkZXRhaWxzLlxyXG5cclxuKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKi9cclxuXHJcbmltcG9ydCB7IHJlYWRGaWxlU3luYyB9IGZyb20gJ2ZzJztcclxuaW1wb3J0IHBhdGggZnJvbSAncGF0aCc7XHJcbmltcG9ydCAqIGFzIHVybCBmcm9tICd1cmwnO1xyXG5cclxuaW1wb3J0IGNhY2hlIGZyb20gJy4vY2FjaGUuanMnO1xyXG5pbXBvcnQgeyBsb2csIGxvZ1dpdGhTdGFjayB9IGZyb20gJy4vbG9nZ2VyLmpzJztcclxuaW1wb3J0IHN2Z1RlbXBsYXRlIGZyb20gJy4vLi4vdGVtcGxhdGVzL3N2Z19leHBvcnQvc3ZnX2V4cG9ydC5qcyc7XHJcblxyXG5pbXBvcnQgRXhwb3J0RXJyb3IgZnJvbSAnLi9lcnJvcnMvRXhwb3J0RXJyb3IuanMnO1xyXG5cclxuY29uc3QgX19iYXNlZGlyID0gdXJsLmZpbGVVUkxUb1BhdGgobmV3IFVSTCgnLicsIGltcG9ydC5tZXRhLnVybCkpO1xyXG5cclxuLyoqXHJcbiAqIFJldHJpZXZlcyB0aGUgY2xpcHBpbmcgcmVnaW9uIGNvb3JkaW5hdGVzIG9mIHRoZSBzcGVjaWZpZWQgcGFnZSBlbGVtZW50IHdpdGhcclxuICogdGhlIGlkICdjaGFydC1jb250YWluZXInLlxyXG4gKlxyXG4gKiBAcGFyYW0ge09iamVjdH0gcGFnZSAtIFB1cHBldGVlciBwYWdlIG9iamVjdC5cclxuICpcclxuICogQHJldHVybnMge1Byb21pc2U8T2JqZWN0Pn0gUHJvbWlzZSByZXNvbHZpbmcgdG8gYW4gb2JqZWN0IGNvbnRhaW5pbmdcclxuICogeCwgeSwgd2lkdGgsIGFuZCBoZWlnaHQgcHJvcGVydGllcy5cclxuICovXHJcbmNvbnN0IGdldENsaXBSZWdpb24gPSAocGFnZSkgPT5cclxuICBwYWdlLiRldmFsKCcjY2hhcnQtY29udGFpbmVyJywgKGVsZW1lbnQpID0+IHtcclxuICAgIGNvbnN0IHsgeCwgeSwgd2lkdGgsIGhlaWdodCB9ID0gZWxlbWVudC5nZXRCb3VuZGluZ0NsaWVudFJlY3QoKTtcclxuICAgIHJldHVybiB7XHJcbiAgICAgIHgsXHJcbiAgICAgIHksXHJcbiAgICAgIHdpZHRoLFxyXG4gICAgICBoZWlnaHQ6IE1hdGgudHJ1bmMoaGVpZ2h0ID4gMSA/IGhlaWdodCA6IDUwMClcclxuICAgIH07XHJcbiAgfSk7XHJcblxyXG4vKipcclxuICogQ3JlYXRlcyBhbiBpbWFnZSB1c2luZyBQdXBwZXRlZXIncyBwYWdlIHNjcmVlbnNob3QgZnVuY3Rpb25hbGl0eSB3aXRoXHJcbiAqIHNwZWNpZmllZCBvcHRpb25zLlxyXG4gKlxyXG4gKiBAcGFyYW0ge09iamVjdH0gcGFnZSAtIFB1cHBldGVlciBwYWdlIG9iamVjdC5cclxuICogQHBhcmFtIHtzdHJpbmd9IHR5cGUgLSBJbWFnZSB0eXBlLlxyXG4gKiBAcGFyYW0ge3N0cmluZ30gZW5jb2RpbmcgLSBJbWFnZSBlbmNvZGluZy5cclxuICogQHBhcmFtIHtPYmplY3R9IGNsaXAgLSBDbGlwcGluZyByZWdpb24gY29vcmRpbmF0ZXMuXHJcbiAqIEBwYXJhbSB7bnVtYmVyfSByYXN0ZXJpemF0aW9uVGltZW91dCAtIFRpbWVvdXQgZm9yIHJhc3Rlcml6YXRpb25cclxuICogaW4gbWlsbGlzZWNvbmRzLlxyXG4gKlxyXG4gKiBAcmV0dXJucyB7UHJvbWlzZTxCdWZmZXI+fSBQcm9taXNlIHJlc29sdmluZyB0byB0aGUgaW1hZ2UgYnVmZmVyIG9yIHJlamVjdGluZ1xyXG4gKiB3aXRoIGFuIEV4cG9ydEVycm9yIGZvciB0aW1lb3V0LlxyXG4gKi9cclxuY29uc3QgY3JlYXRlSW1hZ2UgPSAocGFnZSwgdHlwZSwgZW5jb2RpbmcsIGNsaXAsIHJhc3Rlcml6YXRpb25UaW1lb3V0KSA9PlxyXG4gIFByb21pc2UucmFjZShbXHJcbiAgICBwYWdlLnNjcmVlbnNob3Qoe1xyXG4gICAgICB0eXBlLFxyXG4gICAgICBlbmNvZGluZyxcclxuICAgICAgY2xpcCxcclxuXHJcbiAgICAgIC8vICM0NDcsICM0NjMgLSBhbHdheXMgcmVuZGVyIG9uIGEgdHJhbnNwYXJlbnQgcGFnZSBpZiB0aGUgZXhwZWN0ZWQgdHlwZVxyXG4gICAgICAvLyBmb3JtYXQgaXMgUE5HXHJcbiAgICAgIG9taXRCYWNrZ3JvdW5kOiB0eXBlID09ICdwbmcnXHJcbiAgICB9KSxcclxuICAgIG5ldyBQcm9taXNlKChfcmVzb2x2ZSwgcmVqZWN0KSA9PlxyXG4gICAgICBzZXRUaW1lb3V0KFxyXG4gICAgICAgICgpID0+IHJlamVjdChuZXcgRXhwb3J0RXJyb3IoJ1Jhc3Rlcml6YXRpb24gdGltZW91dCcpKSxcclxuICAgICAgICByYXN0ZXJpemF0aW9uVGltZW91dCB8fCAxNTAwXHJcbiAgICAgIClcclxuICAgIClcclxuICBdKTtcclxuXHJcbi8qKlxyXG4gKiBDcmVhdGVzIGEgUERGIHVzaW5nIFB1cHBldGVlcidzIHBhZ2UgcGRmIGZ1bmN0aW9uYWxpdHkgd2l0aCBzcGVjaWZpZWRcclxuICogb3B0aW9ucy5cclxuICpcclxuICogQHBhcmFtIHtPYmplY3R9IHBhZ2UgLSBQdXBwZXRlZXIgcGFnZSBvYmplY3QuXHJcbiAqIEBwYXJhbSB7bnVtYmVyfSBoZWlnaHQgLSBQREYgaGVpZ2h0LlxyXG4gKiBAcGFyYW0ge251bWJlcn0gd2lkdGggLSBQREYgd2lkdGguXHJcbiAqIEBwYXJhbSB7c3RyaW5nfSBlbmNvZGluZyAtIFBERiBlbmNvZGluZy5cclxuICpcclxuICogQHJldHVybnMge1Byb21pc2U8QnVmZmVyPn0gUHJvbWlzZSByZXNvbHZpbmcgdG8gdGhlIFBERiBidWZmZXIuXHJcbiAqL1xyXG5jb25zdCBjcmVhdGVQREYgPSAocGFnZSwgaGVpZ2h0LCB3aWR0aCwgZW5jb2RpbmcpID0+XHJcbiAgcGFnZS5wZGYoe1xyXG4gICAgLy8gVGhpcyB3aWxsIHJlbW92ZSBhbiBleHRyYSBlbXB0eSBwYWdlIGluIFBERiBleHBvcnRzXHJcbiAgICBoZWlnaHQ6IGhlaWdodCArIDEsXHJcbiAgICB3aWR0aCxcclxuICAgIGVuY29kaW5nXHJcbiAgfSk7XHJcblxyXG4vKipcclxuICogQ3JlYXRlcyBhbiBTVkcgc3RyaW5nIGJ5IGV2YWx1YXRpbmcgdGhlIG91dGVySFRNTCBvZiB0aGUgZmlyc3QgJ3N2ZycgZWxlbWVudFxyXG4gKiBpbnNpZGUgYW4gZWxlbWVudCB3aXRoIHRoZSBpZCAnY29udGFpbmVyJy5cclxuICpcclxuICogQHBhcmFtIHtPYmplY3R9IHBhZ2UgLSBQdXBwZXRlZXIgcGFnZSBvYmplY3QuXHJcbiAqXHJcbiAqIEByZXR1cm5zIHtQcm9taXNlPHN0cmluZz59IFByb21pc2UgcmVzb2x2aW5nIHRvIHRoZSBTVkcgc3RyaW5nLlxyXG4gKi9cclxuY29uc3QgY3JlYXRlU1ZHID0gKHBhZ2UpID0+XHJcbiAgcGFnZS4kZXZhbCgnI2NvbnRhaW5lciBzdmc6Zmlyc3Qtb2YtdHlwZScsIChlbGVtZW50KSA9PiBlbGVtZW50Lm91dGVySFRNTCk7XHJcblxyXG4vKipcclxuICogU2V0cyB0aGUgc3BlY2lmaWVkIGNoYXJ0IGFuZCBvcHRpb25zIGFzIGNvbmZpZ3VyYXRpb24gaW50byB0aGUgdHJpZ2dlckV4cG9ydFxyXG4gKiBmdW5jdGlvbiB3aXRoaW4gdGhlIHdpbmRvdyBjb250ZXh0IHVzaW5nIHBhZ2UuZXZhbHVhdGUuXHJcbiAqXHJcbiAqIEBwYXJhbSB7T2JqZWN0fSBwYWdlIC0gUHVwcGV0ZWVyIHBhZ2Ugb2JqZWN0LlxyXG4gKiBAcGFyYW0ge2FueX0gY2hhcnQgLSBUaGUgY2hhcnQgb2JqZWN0IHRvIGJlIGNvbmZpZ3VyZWQuXHJcbiAqIEBwYXJhbSB7T2JqZWN0fSBvcHRpb25zIC0gQ29uZmlndXJhdGlvbiBvcHRpb25zIGZvciB0aGUgY2hhcnQuXHJcbiAqXHJcbiAqIEByZXR1cm5zIHtQcm9taXNlPHZvaWQ+fSBQcm9taXNlIHJlc29sdmluZyBhZnRlciB0aGUgY29uZmlndXJhdGlvbiBpcyBzZXQuXHJcbiAqL1xyXG5jb25zdCBzZXRBc0NvbmZpZyA9IChwYWdlLCBjaGFydCwgb3B0aW9ucykgPT5cclxuICBwYWdlLmV2YWx1YXRlKFxyXG4gICAgLy8gZXNsaW50LWRpc2FibGUtbmV4dC1saW5lIG5vLXVuZGVmXHJcbiAgICAoY2hhcnQsIG9wdGlvbnMpID0+IHdpbmRvdy50cmlnZ2VyRXhwb3J0KGNoYXJ0LCBvcHRpb25zKSxcclxuICAgIGNoYXJ0LFxyXG4gICAgb3B0aW9uc1xyXG4gICk7XHJcblxyXG4vKipcclxuICogRXhwb3J0cyB0byBhIGNoYXJ0IGZyb20gYSBwYWdlIHVzaW5nIFB1cHBldGVlci5cclxuICpcclxuICogQHBhcmFtIHtPYmplY3R9IHBhZ2UgLSBQdXBwZXRlZXIgcGFnZSBvYmplY3QuXHJcbiAqIEBwYXJhbSB7YW55fSBjaGFydCAtIFRoZSBjaGFydCBvYmplY3Qgb3IgU1ZHIGNvbmZpZ3VyYXRpb24gdG8gYmUgZXhwb3J0ZWQuXHJcbiAqIEBwYXJhbSB7T2JqZWN0fSBvcHRpb25zIC0gRXhwb3J0IG9wdGlvbnMgYW5kIGNvbmZpZ3VyYXRpb24uXHJcbiAqXHJcbiAqIEByZXR1cm5zIHtQcm9taXNlPHN0cmluZyB8IEJ1ZmZlciB8IEV4cG9ydEVycm9yPn0gUHJvbWlzZSByZXNvbHZpbmcgdG9cclxuICogdGhlIGV4cG9ydGVkIGRhdGEgb3IgcmVqZWN0aW5nIHdpdGggYW4gRXhwb3J0RXJyb3IuXHJcbiAqL1xyXG5leHBvcnQgZGVmYXVsdCBhc3luYyAocGFnZSwgY2hhcnQsIG9wdGlvbnMpID0+IHtcclxuICAvKipcclxuICAgKiBLZWVwcyB0cmFjayBvZiBhbGwgcmVzb3VyY2VzIGFkZGVkIG9uIHRoZSBwYWdlIHdpdGggYWRkWFhYVGFnLiBldGNcclxuICAgKiBJdCdzIFZJVEFMIHRoYXQgYWxsIGFkZGVkIHJlc291cmNlcyBlbmRzIHVwIGhlcmUgc28gd2UgY2FuIGNsZWFyIHRoaW5nc1xyXG4gICAqIG91dCB3aGVuIGRvaW5nIGEgbmV3IGV4cG9ydCBpbiB0aGUgc2FtZSBwYWdlIVxyXG4gICAqL1xyXG4gIGNvbnN0IGluamVjdGVkUmVzb3VyY2VzID0gW107XHJcblxyXG4gIC8qKiBDbGVhciBvdXQgYWxsIHN0YXRlIHNldCBvbiB0aGUgcGFnZSB3aXRoIGFkZFNjcmlwdFRhZy9hZGRTdHlsZVRhZy4gKi9cclxuICBjb25zdCBjbGVhckluamVjdGVkID0gYXN5bmMgKHBhZ2UpID0+IHtcclxuICAgIGZvciAoY29uc3QgcmVzIG9mIGluamVjdGVkUmVzb3VyY2VzKSB7XHJcbiAgICAgIGF3YWl0IHJlcy5kaXNwb3NlKCk7XHJcbiAgICB9XHJcblxyXG4gICAgLy8gUmVzZXQgYWxsIENTUyBhbmQgc2NyaXB0IHRhZ3NcclxuICAgIGF3YWl0IHBhZ2UuZXZhbHVhdGUoKCkgPT4ge1xyXG4gICAgICAvLyBlc2xpbnQtZGlzYWJsZS1uZXh0LWxpbmUgbm8tdW5kZWZcclxuICAgICAgY29uc3QgWywgLi4uc2NyaXB0c1RvUmVtb3ZlXSA9IGRvY3VtZW50LmdldEVsZW1lbnRzQnlUYWdOYW1lKCdzY3JpcHQnKTtcclxuICAgICAgLy8gZXNsaW50LWRpc2FibGUtbmV4dC1saW5lIG5vLXVuZGVmXHJcbiAgICAgIGNvbnN0IFssIC4uLnN0eWxlc1RvUmVtb3ZlXSA9IGRvY3VtZW50LmdldEVsZW1lbnRzQnlUYWdOYW1lKCdzdHlsZScpO1xyXG4gICAgICAvLyBlc2xpbnQtZGlzYWJsZS1uZXh0LWxpbmUgbm8tdW5kZWZcclxuICAgICAgY29uc3QgWy4uLmxpbmtzVG9SZW1vdmVdID0gZG9jdW1lbnQuZ2V0RWxlbWVudHNCeVRhZ05hbWUoJ2xpbmsnKTtcclxuXHJcbiAgICAgIC8vIFJlbW92ZSB0YWdzXHJcbiAgICAgIGZvciAoY29uc3QgZWxlbWVudCBvZiBbXHJcbiAgICAgICAgLi4uc2NyaXB0c1RvUmVtb3ZlLFxyXG4gICAgICAgIC4uLnN0eWxlc1RvUmVtb3ZlLFxyXG4gICAgICAgIC4uLmxpbmtzVG9SZW1vdmVcclxuICAgICAgXSkge1xyXG4gICAgICAgIGVsZW1lbnQucmVtb3ZlKCk7XHJcbiAgICAgIH1cclxuICAgIH0pO1xyXG4gIH07XHJcblxyXG4gIHRyeSB7XHJcbiAgICBsb2coNCwgJ1tleHBvcnRdIERldGVybWluaW5nIGV4cG9ydCBwYXRoLicpO1xyXG5cclxuICAgIGNvbnN0IGV4cG9ydE9wdGlvbnMgPSBvcHRpb25zLmV4cG9ydDtcclxuXHJcbiAgICAvLyBGb3JjZSBhIHJBRlxyXG4gICAgLy8gU2VlIGh0dHBzOi8vZ2l0aHViLmNvbS9wdXBwZXRlZXIvcHVwcGV0ZWVyL2lzc3Vlcy83NTA3XHJcbiAgICAvLyBlc2xpbnQtZGlzYWJsZS1uZXh0LWxpbmUgbm8tdW5kZWZcclxuICAgIGF3YWl0IHBhZ2UuZXZhbHVhdGUoKCkgPT4gcmVxdWVzdEFuaW1hdGlvbkZyYW1lKCgpID0+IHt9KSk7XHJcblxyXG4gICAgLy8gRGVjaWRlIHdoZXRoZXIgZGlzcGxheSBlcnJvciBvciBkZWJidWdlciB3cmFwcGVyIGFyb3VuZCBpdFxyXG4gICAgY29uc3QgZGlzcGxheUVycm9ycyA9XHJcbiAgICAgIGV4cG9ydE9wdGlvbnM/Lm9wdGlvbnM/LmNoYXJ0Py5kaXNwbGF5RXJyb3JzICYmXHJcbiAgICAgIGNhY2hlLmdldENhY2hlKCkuYWN0aXZlTWFuaWZlc3QubW9kdWxlcy5kZWJ1Z2dlcjtcclxuXHJcbiAgICAvLyBlc2xpbnQtZGlzYWJsZS1uZXh0LWxpbmUgbm8tdW5kZWZcclxuICAgIGF3YWl0IHBhZ2UuZXZhbHVhdGUoKGQpID0+ICh3aW5kb3cuX2Rpc3BsYXlFcnJvcnMgPSBkKSwgZGlzcGxheUVycm9ycyk7XHJcblxyXG4gICAgbGV0IGlzU1ZHO1xyXG4gICAgaWYgKFxyXG4gICAgICBjaGFydC5pbmRleE9mICYmXHJcbiAgICAgIChjaGFydC5pbmRleE9mKCc8c3ZnJykgPj0gMCB8fCBjaGFydC5pbmRleE9mKCc8P3htbCcpID49IDApXHJcbiAgICApIHtcclxuICAgICAgLy8gU1ZHIGlucHV0IGhhbmRsaW5nXHJcbiAgICAgIGxvZyg0LCAnW2V4cG9ydF0gVHJlYXRpbmcgYXMgU1ZHLicpO1xyXG5cclxuICAgICAgLy8gSWYgaW5wdXQgaXMgYWxzbyBTVkcsIGp1c3QgcmV0dXJuIGl0XHJcbiAgICAgIGlmIChleHBvcnRPcHRpb25zLnR5cGUgPT09ICdzdmcnKSB7XHJcbiAgICAgICAgcmV0dXJuIGNoYXJ0O1xyXG4gICAgICB9XHJcblxyXG4gICAgICBpc1NWRyA9IHRydWU7XHJcbiAgICAgIGF3YWl0IHBhZ2Uuc2V0Q29udGVudChzdmdUZW1wbGF0ZShjaGFydCkpO1xyXG4gICAgfSBlbHNlIHtcclxuICAgICAgLy8gSlNPTiBjb25maWcgaGFuZGxpbmdcclxuICAgICAgbG9nKDQsICdbZXhwb3J0XSBUcmVhdGluZyBhcyBjb25maWcuJyk7XHJcblxyXG4gICAgICAvLyBOZWVkIHRvIHBlcmZvcm0gc3RyYWlnaHQgaW5qZWN0XHJcbiAgICAgIGlmIChleHBvcnRPcHRpb25zLnN0ckluaikge1xyXG4gICAgICAgIC8vIEluamVjdGlvbiBiYXNlZCBjb25maWd1cmF0aW9uIGV4cG9ydFxyXG4gICAgICAgIGF3YWl0IHNldEFzQ29uZmlnKFxyXG4gICAgICAgICAgcGFnZSxcclxuICAgICAgICAgIHtcclxuICAgICAgICAgICAgY2hhcnQ6IHtcclxuICAgICAgICAgICAgICBoZWlnaHQ6IGV4cG9ydE9wdGlvbnMuaGVpZ2h0LFxyXG4gICAgICAgICAgICAgIHdpZHRoOiBleHBvcnRPcHRpb25zLndpZHRoXHJcbiAgICAgICAgICAgIH1cclxuICAgICAgICAgIH0sXHJcbiAgICAgICAgICBvcHRpb25zXHJcbiAgICAgICAgKTtcclxuICAgICAgfSBlbHNlIHtcclxuICAgICAgICAvLyBCYXNpYyBjb25maWd1cmF0aW9uIGV4cG9ydFxyXG4gICAgICAgIGNoYXJ0LmNoYXJ0LmhlaWdodCA9IGV4cG9ydE9wdGlvbnMuaGVpZ2h0O1xyXG4gICAgICAgIGNoYXJ0LmNoYXJ0LndpZHRoID0gZXhwb3J0T3B0aW9ucy53aWR0aDtcclxuXHJcbiAgICAgICAgYXdhaXQgc2V0QXNDb25maWcocGFnZSwgY2hhcnQsIG9wdGlvbnMpO1xyXG4gICAgICB9XHJcbiAgICB9XHJcblxyXG4gICAgLy8gVXNlIHJlc291cmNlc1xyXG4gICAgY29uc3QgcmVzb3VyY2VzID0gb3B0aW9ucy5jdXN0b21Mb2dpYy5yZXNvdXJjZXM7XHJcbiAgICBpZiAocmVzb3VyY2VzKSB7XHJcbiAgICAgIC8vIExvYWQgY3VzdG9tIEpTIGNvZGVcclxuICAgICAgaWYgKHJlc291cmNlcy5qcykge1xyXG4gICAgICAgIGluamVjdGVkUmVzb3VyY2VzLnB1c2goXHJcbiAgICAgICAgICBhd2FpdCBwYWdlLmFkZFNjcmlwdFRhZyh7XHJcbiAgICAgICAgICAgIGNvbnRlbnQ6IHJlc291cmNlcy5qc1xyXG4gICAgICAgICAgfSlcclxuICAgICAgICApO1xyXG4gICAgICB9XHJcblxyXG4gICAgICAvLyBMb2FkIHNjcmlwdHMgZnJvbSBhbGwgY3VzdG9tIGZpbGVzXHJcbiAgICAgIGlmIChyZXNvdXJjZXMuZmlsZXMpIHtcclxuICAgICAgICBmb3IgKGNvbnN0IGZpbGUgb2YgcmVzb3VyY2VzLmZpbGVzKSB7XHJcbiAgICAgICAgICB0cnkge1xyXG4gICAgICAgICAgICBjb25zdCBpc0xvY2FsID0gIWZpbGUuc3RhcnRzV2l0aCgnaHR0cCcpID8gdHJ1ZSA6IGZhbHNlO1xyXG5cclxuICAgICAgICAgICAgLy8gQWRkIGVhY2ggY3VzdG9tIHNjcmlwdCBmcm9tIHJlc291cmNlcycgZmlsZXNcclxuICAgICAgICAgICAgaW5qZWN0ZWRSZXNvdXJjZXMucHVzaChcclxuICAgICAgICAgICAgICBhd2FpdCBwYWdlLmFkZFNjcmlwdFRhZyhcclxuICAgICAgICAgICAgICAgIGlzTG9jYWxcclxuICAgICAgICAgICAgICAgICAgPyB7XHJcbiAgICAgICAgICAgICAgICAgICAgICBjb250ZW50OiByZWFkRmlsZVN5bmMoZmlsZSwgJ3V0ZjgnKVxyXG4gICAgICAgICAgICAgICAgICAgIH1cclxuICAgICAgICAgICAgICAgICAgOiB7XHJcbiAgICAgICAgICAgICAgICAgICAgICB1cmw6IGZpbGVcclxuICAgICAgICAgICAgICAgICAgICB9XHJcbiAgICAgICAgICAgICAgKVxyXG4gICAgICAgICAgICApO1xyXG4gICAgICAgICAgfSBjYXRjaCAoZXJyb3IpIHtcclxuICAgICAgICAgICAgbG9nV2l0aFN0YWNrKFxyXG4gICAgICAgICAgICAgIDIsXHJcbiAgICAgICAgICAgICAgZXJyb3IsXHJcbiAgICAgICAgICAgICAgYFtleHBvcnRdIFRoZSBKUyBmaWxlICR7ZmlsZX0gY2Fubm90IGJlIGxvYWRlZC5gXHJcbiAgICAgICAgICAgICk7XHJcbiAgICAgICAgICB9XHJcbiAgICAgICAgfVxyXG4gICAgICB9XHJcblxyXG4gICAgICAvLyBMb2FkIENTU1xyXG4gICAgICBpZiAocmVzb3VyY2VzLmNzcykge1xyXG4gICAgICAgIGxldCBjc3NJbXBvcnRzID0gcmVzb3VyY2VzLmNzcy5tYXRjaCgvQGltcG9ydFxccyooW147XSopOy9nKTtcclxuICAgICAgICBpZiAoY3NzSW1wb3J0cykge1xyXG4gICAgICAgICAgLy8gSGFuZGxlIGNzcyBzZWN0aW9uXHJcbiAgICAgICAgICBmb3IgKGxldCBjc3NJbXBvcnRQYXRoIG9mIGNzc0ltcG9ydHMpIHtcclxuICAgICAgICAgICAgaWYgKGNzc0ltcG9ydFBhdGgpIHtcclxuICAgICAgICAgICAgICBjc3NJbXBvcnRQYXRoID0gY3NzSW1wb3J0UGF0aFxyXG4gICAgICAgICAgICAgICAgLnJlcGxhY2UoJ3VybCgnLCAnJylcclxuICAgICAgICAgICAgICAgIC5yZXBsYWNlKCdAaW1wb3J0JywgJycpXHJcbiAgICAgICAgICAgICAgICAucmVwbGFjZSgvXCIvZywgJycpXHJcbiAgICAgICAgICAgICAgICAucmVwbGFjZSgvJy9nLCAnJylcclxuICAgICAgICAgICAgICAgIC5yZXBsYWNlKC87LywgJycpXHJcbiAgICAgICAgICAgICAgICAucmVwbGFjZSgvXFwpL2csICcnKVxyXG4gICAgICAgICAgICAgICAgLnRyaW0oKTtcclxuXHJcbiAgICAgICAgICAgICAgLy8gQWRkIGVhY2ggY3VzdG9tIGNzcyBmcm9tIHJlc291cmNlc1xyXG4gICAgICAgICAgICAgIGlmIChjc3NJbXBvcnRQYXRoLnN0YXJ0c1dpdGgoJ2h0dHAnKSkge1xyXG4gICAgICAgICAgICAgICAgaW5qZWN0ZWRSZXNvdXJjZXMucHVzaChcclxuICAgICAgICAgICAgICAgICAgYXdhaXQgcGFnZS5hZGRTdHlsZVRhZyh7XHJcbiAgICAgICAgICAgICAgICAgICAgdXJsOiBjc3NJbXBvcnRQYXRoXHJcbiAgICAgICAgICAgICAgICAgIH0pXHJcbiAgICAgICAgICAgICAgICApO1xyXG4gICAgICAgICAgICAgIH0gZWxzZSBpZiAob3B0aW9ucy5jdXN0b21Mb2dpYy5hbGxvd0ZpbGVSZXNvdXJjZXMpIHtcclxuICAgICAgICAgICAgICAgIGluamVjdGVkUmVzb3VyY2VzLnB1c2goXHJcbiAgICAgICAgICAgICAgICAgIGF3YWl0IHBhZ2UuYWRkU3R5bGVUYWcoe1xyXG4gICAgICAgICAgICAgICAgICAgIHBhdGg6IHBhdGguam9pbihfX2Jhc2VkaXIsIGNzc0ltcG9ydFBhdGgpXHJcbiAgICAgICAgICAgICAgICAgIH0pXHJcbiAgICAgICAgICAgICAgICApO1xyXG4gICAgICAgICAgICAgIH1cclxuICAgICAgICAgICAgfVxyXG4gICAgICAgICAgfVxyXG4gICAgICAgIH1cclxuXHJcbiAgICAgICAgLy8gVGhlIHJlc3Qgb2YgdGhlIENTUyBzZWN0aW9uIHdpbGwgYmUgY29udGVudCBieSBub3dcclxuICAgICAgICBpbmplY3RlZFJlc291cmNlcy5wdXNoKFxyXG4gICAgICAgICAgYXdhaXQgcGFnZS5hZGRTdHlsZVRhZyh7XHJcbiAgICAgICAgICAgIGNvbnRlbnQ6IHJlc291cmNlcy5jc3MucmVwbGFjZSgvQGltcG9ydFxccyooW147XSopOy9nLCAnJykgfHwgJyAnXHJcbiAgICAgICAgICB9KVxyXG4gICAgICAgICk7XHJcbiAgICAgIH1cclxuICAgIH1cclxuXHJcbiAgICAvLyBHZXQgdGhlIHJlYWwgY2hhcnQgc2l6ZVxyXG4gICAgY29uc3Qgc2l6ZSA9IGlzU1ZHXHJcbiAgICAgID8gYXdhaXQgcGFnZS4kZXZhbChcclxuICAgICAgICAgICcjY2hhcnQtY29udGFpbmVyIHN2ZzpmaXJzdC1vZi10eXBlJyxcclxuICAgICAgICAgIChlbGVtZW50LCBzY2FsZSkgPT4gKHtcclxuICAgICAgICAgICAgY2hhcnRIZWlnaHQ6IGVsZW1lbnQuaGVpZ2h0LmJhc2VWYWwudmFsdWUgKiBzY2FsZSxcclxuICAgICAgICAgICAgY2hhcnRXaWR0aDogZWxlbWVudC53aWR0aC5iYXNlVmFsLnZhbHVlICogc2NhbGVcclxuICAgICAgICAgIH0pLFxyXG4gICAgICAgICAgcGFyc2VGbG9hdChleHBvcnRPcHRpb25zLnNjYWxlKVxyXG4gICAgICAgIClcclxuICAgICAgOiBhd2FpdCBwYWdlLmV2YWx1YXRlKCgpID0+IHtcclxuICAgICAgICAgIC8vIGVzbGludC1kaXNhYmxlLW5leHQtbGluZSBuby11bmRlZlxyXG4gICAgICAgICAgY29uc3QgeyBjaGFydEhlaWdodCwgY2hhcnRXaWR0aCB9ID0gd2luZG93LkhpZ2hjaGFydHMuY2hhcnRzWzBdO1xyXG4gICAgICAgICAgcmV0dXJuIHtcclxuICAgICAgICAgICAgY2hhcnRIZWlnaHQsXHJcbiAgICAgICAgICAgIGNoYXJ0V2lkdGhcclxuICAgICAgICAgIH07XHJcbiAgICAgICAgfSk7XHJcblxyXG4gICAgLy8gU2V0IGZpbmFsIGhlaWdodCBhbmQgd2lkdGggZm9yIHZpZXdwb3J0XHJcbiAgICBjb25zdCB2aWV3cG9ydEhlaWdodCA9IE1hdGguY2VpbChzaXplPy5jaGFydEhlaWdodCB8fCBleHBvcnRPcHRpb25zLmhlaWdodCk7XHJcbiAgICBjb25zdCB2aWV3cG9ydFdpZHRoID0gTWF0aC5jZWlsKHNpemU/LmNoYXJ0V2lkdGggfHwgZXhwb3J0T3B0aW9ucy53aWR0aCk7XHJcblxyXG4gICAgLy8gU2V0IHRoZSB2aWV3cG9ydCBmb3IgdGhlIGZpcnN0IHRpbWVcclxuICAgIC8vIE5PVEU6IHRoZSBjYWxsIHRvIHNldFZpZXdwb3J0IGlzIGV4cGVuc2l2ZSAtIGNhbiB3ZSBnZXQgYXdheSB3aXRoIG9ubHlcclxuICAgIC8vIGNhbGxpbmcgaXQgb25jZSwgZS5nLiBtb3ZpbmcgdGhpcyBvbmUgaW50byB0aGUgaXNTVkcgY29uZGl0aW9uIGJlbG93P1xyXG4gICAgYXdhaXQgcGFnZS5zZXRWaWV3cG9ydCh7XHJcbiAgICAgIGhlaWdodDogdmlld3BvcnRIZWlnaHQsXHJcbiAgICAgIHdpZHRoOiB2aWV3cG9ydFdpZHRoLFxyXG4gICAgICBkZXZpY2VTY2FsZUZhY3RvcjogaXNTVkcgPyAxIDogcGFyc2VGbG9hdChleHBvcnRPcHRpb25zLnNjYWxlKVxyXG4gICAgfSk7XHJcblxyXG4gICAgLy8gUHJlcGFyZSBhIHpvb20gY2FsbGJhY2sgZm9yIHRoZSBuZXh0IGV2YWx1YXRlIGNhbGxcclxuICAgIGNvbnN0IHpvb21DYWxsYmFjayA9IGlzU1ZHXHJcbiAgICAgID8gLy8gSW4gY2FzZSBvZiBTVkcgdGhlIHpvb20gbXVzdCBiZSBzZXQgZGlyZWN0bHkgZm9yIGJvZHlcclxuICAgICAgICAoc2NhbGUpID0+IHtcclxuICAgICAgICAgIC8vIFNldCB0aGUgem9vbSBhcyBzY2FsZVxyXG4gICAgICAgICAgLy8gZXNsaW50LWRpc2FibGUtbmV4dC1saW5lIG5vLXVuZGVmXHJcbiAgICAgICAgICBkb2N1bWVudC5ib2R5LnN0eWxlLnpvb20gPSBzY2FsZTtcclxuXHJcbiAgICAgICAgICAvLyBTZXQgdGhlIG1hcmdpbiB0byAwcHhcclxuICAgICAgICAgIC8vIGVzbGludC1kaXNhYmxlLW5leHQtbGluZSBuby11bmRlZlxyXG4gICAgICAgICAgZG9jdW1lbnQuYm9keS5zdHlsZS5tYXJnaW4gPSAnMHB4JztcclxuICAgICAgICB9XHJcbiAgICAgIDogLy8gTm8gbmVlZCBmb3Igc3VjaCBzY2FsZSBtYW5pcHVsYXRpb24gaW4gY2FzZSBvZiBvdGhlciB0eXBlcyBvZiBleHBvcnRzXHJcbiAgICAgICAgKCkgPT4ge1xyXG4gICAgICAgICAgLy8gUmVzZXQgdGhlIHpvb20gZm9yIG90aGVyIGV4cG9ydHMgdGhhbiB0byBTVkdzXHJcbiAgICAgICAgICAvLyBlc2xpbnQtZGlzYWJsZS1uZXh0LWxpbmUgbm8tdW5kZWZcclxuICAgICAgICAgIGRvY3VtZW50LmJvZHkuc3R5bGUuem9vbSA9IDE7XHJcbiAgICAgICAgfTtcclxuXHJcbiAgICAvLyBTZXQgdGhlIHpvb20gYWNjb3JkaW5nbHlcclxuICAgIGF3YWl0IHBhZ2UuZXZhbHVhdGUoem9vbUNhbGxiYWNrLCBwYXJzZUZsb2F0KGV4cG9ydE9wdGlvbnMuc2NhbGUpKTtcclxuXHJcbiAgICAvLyBHZXQgdGhlIGNsaXAgcmVnaW9uIGZvciB0aGUgcGFnZVxyXG4gICAgY29uc3QgeyBoZWlnaHQsIHdpZHRoLCB4LCB5IH0gPSBhd2FpdCBnZXRDbGlwUmVnaW9uKHBhZ2UpO1xyXG5cclxuICAgIGlmICghaXNTVkcpIHtcclxuICAgICAgLy8gU2V0IHRoZSBmaW5hbCB2aWV3cG9ydCBub3cgdGhhdCB3ZSBoYXZlIHRoZSByZWFsIGhlaWdodFxyXG4gICAgICBhd2FpdCBwYWdlLnNldFZpZXdwb3J0KHtcclxuICAgICAgICB3aWR0aDogTWF0aC5yb3VuZCh3aWR0aCksXHJcbiAgICAgICAgaGVpZ2h0OiBNYXRoLnJvdW5kKGhlaWdodCksXHJcbiAgICAgICAgZGV2aWNlU2NhbGVGYWN0b3I6IHBhcnNlRmxvYXQoZXhwb3J0T3B0aW9ucy5zY2FsZSlcclxuICAgICAgfSk7XHJcbiAgICB9XHJcblxyXG4gICAgbGV0IGRhdGE7XHJcbiAgICAvLyBSQVNURVJJWkFUSU9OXHJcbiAgICBpZiAoZXhwb3J0T3B0aW9ucy50eXBlID09PSAnc3ZnJykge1xyXG4gICAgICAvLyBTVkdcclxuICAgICAgZGF0YSA9IGF3YWl0IGNyZWF0ZVNWRyhwYWdlKTtcclxuICAgIH0gZWxzZSBpZiAoWydwbmcnLCAnanBlZyddLmluY2x1ZGVzKGV4cG9ydE9wdGlvbnMudHlwZSkpIHtcclxuICAgICAgLy8gUE5HIG9yIEpQRUdcclxuICAgICAgZGF0YSA9IGF3YWl0IGNyZWF0ZUltYWdlKFxyXG4gICAgICAgIHBhZ2UsXHJcbiAgICAgICAgZXhwb3J0T3B0aW9ucy50eXBlLFxyXG4gICAgICAgICdiYXNlNjQnLFxyXG4gICAgICAgIHtcclxuICAgICAgICAgIHdpZHRoOiB2aWV3cG9ydFdpZHRoLFxyXG4gICAgICAgICAgaGVpZ2h0OiB2aWV3cG9ydEhlaWdodCxcclxuICAgICAgICAgIHgsXHJcbiAgICAgICAgICB5XHJcbiAgICAgICAgfSxcclxuICAgICAgICBleHBvcnRPcHRpb25zLnJhc3Rlcml6YXRpb25UaW1lb3V0XHJcbiAgICAgICk7XHJcbiAgICB9IGVsc2UgaWYgKGV4cG9ydE9wdGlvbnMudHlwZSA9PT0gJ3BkZicpIHtcclxuICAgICAgLy8gUERGXHJcbiAgICAgIGRhdGEgPSBhd2FpdCBjcmVhdGVQREYocGFnZSwgdmlld3BvcnRIZWlnaHQsIHZpZXdwb3J0V2lkdGgsICdiYXNlNjQnKTtcclxuICAgIH0gZWxzZSB7XHJcbiAgICAgIHRocm93IG5ldyBFeHBvcnRFcnJvcihcclxuICAgICAgICBgW2V4cG9ydF0gVW5zdXBwb3J0ZWQgb3V0cHV0IGZvcm1hdCAke2V4cG9ydE9wdGlvbnMudHlwZX0uYFxyXG4gICAgICApO1xyXG4gICAgfVxyXG5cclxuICAgIC8vIERlc3Ryb3kgb2xkIGNoYXJ0cyBhZnRlciB0aGUgZXhwb3J0IGlzIGRvbmVcclxuICAgIGF3YWl0IHBhZ2UuZXZhbHVhdGUoKCkgPT4ge1xyXG4gICAgICAvLyBXZSBhcmUgbm90IGd1YXJhbnRlZWQgdGhhdCBIaWdoY2hhcnRzIGlzIGxvYWRlZCwgZSxnLCB3aGVuIGRvaW5nIFNWR1xyXG4gICAgICAvLyBleHBvcnRzXHJcbiAgICAgIGlmICh0eXBlb2YgSGlnaGNoYXJ0cyAhPT0gJ3VuZGVmaW5lZCcpIHtcclxuICAgICAgICAvLyBlc2xpbnQtZGlzYWJsZS1uZXh0LWxpbmUgbm8tdW5kZWZcclxuICAgICAgICBjb25zdCBvbGRDaGFydHMgPSBIaWdoY2hhcnRzLmNoYXJ0cztcclxuXHJcbiAgICAgICAgLy8gQ2hlY2sgaW4gYW55IGFscmVhZHkgZXhpc3RpbmcgY2hhcnRzXHJcbiAgICAgICAgaWYgKEFycmF5LmlzQXJyYXkob2xkQ2hhcnRzKSAmJiBvbGRDaGFydHMubGVuZ3RoKSB7XHJcbiAgICAgICAgICAvLyBEZXN0cm95IG9sZCBjaGFydHNcclxuICAgICAgICAgIGZvciAoY29uc3Qgb2xkQ2hhcnQgb2Ygb2xkQ2hhcnRzKSB7XHJcbiAgICAgICAgICAgIG9sZENoYXJ0ICYmIG9sZENoYXJ0LmRlc3Ryb3koKTtcclxuICAgICAgICAgICAgLy8gZXNsaW50LWRpc2FibGUtbmV4dC1saW5lIG5vLXVuZGVmXHJcbiAgICAgICAgICAgIEhpZ2hjaGFydHMuY2hhcnRzLnNoaWZ0KCk7XHJcbiAgICAgICAgICB9XHJcbiAgICAgICAgfVxyXG4gICAgICB9XHJcbiAgICB9KTtcclxuXHJcbiAgICBhd2FpdCBjbGVhckluamVjdGVkKHBhZ2UpO1xyXG4gICAgcmV0dXJuIGRhdGE7XHJcbiAgfSBjYXRjaCAoZXJyb3IpIHtcclxuICAgIGF3YWl0IGNsZWFySW5qZWN0ZWQocGFnZSk7XHJcbiAgICByZXR1cm4gZXJyb3I7XHJcbiAgfVxyXG59O1xyXG4iLCIvKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKlxyXG5cclxuSGlnaGNoYXJ0cyBFeHBvcnQgU2VydmVyXHJcblxyXG5Db3B5cmlnaHQgKGMpIDIwMTYtMjAyNCwgSGlnaHNvZnRcclxuXHJcbkxpY2VuY2VkIHVuZGVyIHRoZSBNSVQgbGljZW5jZS5cclxuXHJcbkFkZGl0aW9uYWxseSBhIHZhbGlkIEhpZ2hjaGFydHMgbGljZW5zZSBpcyByZXF1aXJlZCBmb3IgdXNlLlxyXG5cclxuU2VlIExJQ0VOU0UgZmlsZSBpbiByb290IGZvciBkZXRhaWxzLlxyXG5cclxuKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKi9cclxuXHJcbmltcG9ydCBjc3NUZW1wbGF0ZSBmcm9tICcuL2Nzcy5qcyc7XHJcblxyXG5leHBvcnQgZGVmYXVsdCAoY2hhcnQpID0+IGBcclxuPCFET0NUWVBFIGh0bWw+XHJcbjxodG1sIGxhbmc9J2VuLVVTJz5cclxuICA8aGVhZD5cclxuICAgIDxtZXRhIGh0dHAtZXF1aXY9XCJDb250ZW50LVR5cGVcIiBjb250ZW50PVwidGV4dC9odG1sOyBjaGFyc2V0PXV0Zi04XCI+XHJcbiAgICA8dGl0bGU+SGlnaGNhcnRzIEV4cG9ydDwvdGl0bGU+XHJcbiAgPC9oZWFkPlxyXG4gIDxzdHlsZT5cclxuICAgICR7Y3NzVGVtcGxhdGUoKX1cclxuICA8L3N0eWxlPlxyXG4gIDxib2R5PlxyXG4gICAgPGRpdiBpZD1cImNoYXJ0LWNvbnRhaW5lclwiPlxyXG4gICAgICAke2NoYXJ0fVxyXG4gICAgPC9kaXY+XHJcbiAgPC9ib2R5PlxyXG48L2h0bWw+XHJcblxyXG5gO1xyXG4iLCIvKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKlxyXG5cclxuSGlnaGNoYXJ0cyBFeHBvcnQgU2VydmVyXHJcblxyXG5Db3B5cmlnaHQgKGMpIDIwMTYtMjAyNCwgSGlnaHNvZnRcclxuXHJcbkxpY2VuY2VkIHVuZGVyIHRoZSBNSVQgbGljZW5jZS5cclxuXHJcbkFkZGl0aW9uYWxseSBhIHZhbGlkIEhpZ2hjaGFydHMgbGljZW5zZSBpcyByZXF1aXJlZCBmb3IgdXNlLlxyXG5cclxuU2VlIExJQ0VOU0UgZmlsZSBpbiByb290IGZvciBkZXRhaWxzLlxyXG5cclxuKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKi9cclxuXHJcbmltcG9ydCB7IFBvb2wgfSBmcm9tICd0YXJuJztcclxuaW1wb3J0IHsgdjQgYXMgdXVpZCB9IGZyb20gJ3V1aWQnO1xyXG5cclxuaW1wb3J0IHtcclxuICBjbG9zZSBhcyBicm93c2VyQ2xvc2UsXHJcbiAgY3JlYXRlIGFzIGNyZWF0ZUJyb3dzZXIsXHJcbiAgbmV3UGFnZSBhcyBicm93c2VyTmV3UGFnZSxcclxuICBjbGVhclBhZ2VcclxufSBmcm9tICcuL2Jyb3dzZXIuanMnO1xyXG5pbXBvcnQgcHVwcGV0ZWVyRXhwb3J0IGZyb20gJy4vZXhwb3J0LmpzJztcclxuaW1wb3J0IHsgbG9nLCBsb2dXaXRoU3RhY2sgfSBmcm9tICcuL2xvZ2dlci5qcyc7XHJcbmltcG9ydCB7IG1lYXN1cmVUaW1lIH0gZnJvbSAnLi91dGlscy5qcyc7XHJcblxyXG5pbXBvcnQgRXhwb3J0RXJyb3IgZnJvbSAnLi9lcnJvcnMvRXhwb3J0RXJyb3IuanMnO1xyXG5cclxuLy8gUG9vbCBzdGF0aXN0aWNzXHJcbmV4cG9ydCBjb25zdCBzdGF0cyA9IHtcclxuICBwZXJmb3JtZWRFeHBvcnRzOiAwLFxyXG4gIGV4cG9ydEF0dGVtcHRzOiAwLFxyXG4gIGV4cG9ydEZyb21TdmdBdHRlbXB0czogMCxcclxuICB0aW1lU3BlbnQ6IDAsXHJcbiAgZHJvcHBlZEV4cG9ydHM6IDAsXHJcbiAgc3BlbnRBdmVyYWdlOiAwXHJcbn07XHJcblxyXG5sZXQgcG9vbENvbmZpZyA9IHt9O1xyXG5cclxuLy8gVGhlIHBvb2wgaW5zdGFuY2VcclxubGV0IHBvb2wgPSBmYWxzZTtcclxuXHJcbi8vIEN1c3RvbSBwdXBwZXRlZXIgYXJndW1lbnRzXHJcbmxldCBwdXBwZXRlZXJBcmdzO1xyXG5cclxuY29uc3QgZmFjdG9yeSA9IHtcclxuICAvKipcclxuICAgKiBDcmVhdGVzIGEgbmV3IHdvcmtlciBwYWdlIGZvciB0aGUgZXhwb3J0IHBvb2wuXHJcbiAgICpcclxuICAgKiBAcmV0dXJucyB7T2JqZWN0fSAtIEFuIG9iamVjdCBjb250YWluaW5nIHRoZSB3b3JrZXIgSUQsIGEgcmVmZXJlbmNlIHRvIHRoZVxyXG4gICAqIGJyb3dzZXIgcGFnZSwgYW5kIGluaXRpYWwgd29yayBjb3VudC5cclxuICAgKlxyXG4gICAqIEB0aHJvd3Mge0V4cG9ydEVycm9yfSAtIElmIHRoZXJlJ3MgYW4gZXJyb3IgZHVyaW5nIHRoZSBjcmVhdGlvbiBvZiB0aGUgbmV3XHJcbiAgICogcGFnZS5cclxuICAgKi9cclxuICBjcmVhdGU6IGFzeW5jICgpID0+IHtcclxuICAgIGxldCBwYWdlID0gZmFsc2U7XHJcblxyXG4gICAgY29uc3QgaWQgPSB1dWlkKCk7XHJcbiAgICBjb25zdCBzdGFydERhdGUgPSBuZXcgRGF0ZSgpLmdldFRpbWUoKTtcclxuXHJcbiAgICB0cnkge1xyXG4gICAgICBwYWdlID0gYXdhaXQgYnJvd3Nlck5ld1BhZ2UoKTtcclxuXHJcbiAgICAgIGlmICghcGFnZSB8fCBwYWdlLmlzQ2xvc2VkKCkpIHtcclxuICAgICAgICB0aHJvdyBuZXcgRXhwb3J0RXJyb3IoJ1RoZSBwYWdlIGlzIGludmFsaWQgb3IgY2xvc2VkLicpO1xyXG4gICAgICB9XHJcblxyXG4gICAgICBsb2coXHJcbiAgICAgICAgMyxcclxuICAgICAgICBgW3Bvb2xdIFN1Y2Nlc3NmdWxseSBjcmVhdGVkIGEgd29ya2VyICR7aWR9IC0gdG9vayAke1xyXG4gICAgICAgICAgbmV3IERhdGUoKS5nZXRUaW1lKCkgLSBzdGFydERhdGVcclxuICAgICAgICB9IG1zLmBcclxuICAgICAgKTtcclxuICAgIH0gY2F0Y2ggKGVycm9yKSB7XHJcbiAgICAgIHRocm93IG5ldyBFeHBvcnRFcnJvcihcclxuICAgICAgICAnRXJyb3IgZW5jb3VudGVyZWQgd2hlbiBjcmVhdGluZyBhIG5ldyBwYWdlLidcclxuICAgICAgKS5zZXRFcnJvcihlcnJvcik7XHJcbiAgICB9XHJcblxyXG4gICAgcmV0dXJuIHtcclxuICAgICAgaWQsXHJcbiAgICAgIHBhZ2UsXHJcbiAgICAgIC8vIFRyeSB0byBkaXN0cmlidXRlIHRoZSBpbml0aWFsIHdvcmsgY291bnRcclxuICAgICAgd29ya0NvdW50OiBNYXRoLnJvdW5kKE1hdGgucmFuZG9tKCkgKiAocG9vbENvbmZpZy53b3JrTGltaXQgLyAyKSlcclxuICAgIH07XHJcbiAgfSxcclxuXHJcbiAgLyoqXHJcbiAgICogVmFsaWRhdGVzIGEgd29ya2VyIHBhZ2UgaW4gdGhlIGV4cG9ydCBwb29sLCBjaGVja2luZyBpZiBpdCBoYXMgZXhjZWVkZWRcclxuICAgKiB0aGUgd29yayBsaW1pdC5cclxuICAgKlxyXG4gICAqIEBwYXJhbSB7T2JqZWN0fSB3b3JrZXJIYW5kbGUgLSBUaGUgaGFuZGxlIHRvIHRoZSB3b3JrZXIsIGNvbnRhaW5pbmcgdGhlXHJcbiAgICogd29ya2VyJ3MgSUQsIGEgcmVmZXJlbmNlIHRvIHRoZSBicm93c2VyIHBhZ2UsIGFuZCB3b3JrIGNvdW50LlxyXG4gICAqXHJcbiAgICogQHJldHVybnMge2Jvb2xlYW59IC0gUmV0dXJucyB0cnVlIGlmIHRoZSB3b3JrZXIgaXMgdmFsaWQgYW5kIHdpdGhpblxyXG4gICAqIHRoZSB3b3JrIGxpbWl0OyBvdGhlcndpc2UsIHJldHVybnMgZmFsc2UuXHJcbiAgICovXHJcbiAgdmFsaWRhdGU6IGFzeW5jICh3b3JrZXJIYW5kbGUpID0+IHtcclxuICAgIGlmIChcclxuICAgICAgcG9vbENvbmZpZy53b3JrTGltaXQgJiZcclxuICAgICAgKyt3b3JrZXJIYW5kbGUud29ya0NvdW50ID4gcG9vbENvbmZpZy53b3JrTGltaXRcclxuICAgICkge1xyXG4gICAgICBsb2coXHJcbiAgICAgICAgMyxcclxuICAgICAgICBgW3Bvb2xdIFdvcmtlciBmYWlsZWQgdmFsaWRhdGlvbjogZXhjZWVkZWQgd29yayBsaW1pdCAobGltaXQgaXMgJHtwb29sQ29uZmlnLndvcmtMaW1pdH0pLmBcclxuICAgICAgKTtcclxuICAgICAgcmV0dXJuIGZhbHNlO1xyXG4gICAgfVxyXG5cclxuICAgIC8vIENsZWFyIHBhZ2VcclxuICAgIGF3YWl0IGNsZWFyUGFnZSh3b3JrZXJIYW5kbGUucGFnZSwgdHJ1ZSk7XHJcbiAgICByZXR1cm4gdHJ1ZTtcclxuICB9LFxyXG5cclxuICAvKipcclxuICAgKiBEZXN0cm95cyBhIHdvcmtlciBlbnRyeSBpbiB0aGUgZXhwb3J0IHBvb2wsIGNsb3NpbmcgaXRzIGFzc29jaWF0ZWQgcGFnZS5cclxuICAgKlxyXG4gICAqIEBwYXJhbSB7T2JqZWN0fSB3b3JrZXJIYW5kbGUgLSBUaGUgaGFuZGxlIHRvIHRoZSB3b3JrZXIsIGNvbnRhaW5pbmdcclxuICAgKiB0aGUgd29ya2VyJ3MgSUQgYW5kIGEgcmVmZXJlbmNlIHRvIHRoZSBicm93c2VyIHBhZ2UuXHJcbiAgICovXHJcbiAgZGVzdHJveTogKHdvcmtlckhhbmRsZSkgPT4ge1xyXG4gICAgbG9nKDMsIGBbcG9vbF0gRGVzdHJveWluZyBwb29sIGVudHJ5ICR7d29ya2VySGFuZGxlLmlkfS5gKTtcclxuXHJcbiAgICBpZiAod29ya2VySGFuZGxlLnBhZ2UpIHtcclxuICAgICAgLy8gV2UgZG9uJ3QgcmVhbGx5IG5lZWQgdG8gd2FpdCBhcm91bmQgZm9yIHRoaXMuXHJcbiAgICAgIHdvcmtlckhhbmRsZS5wYWdlLmNsb3NlKCk7XHJcbiAgICB9XHJcbiAgfVxyXG59O1xyXG5cclxuLyoqXHJcbiAqIEluaXRpYWxpemVzIHRoZSBleHBvcnQgcG9vbCB3aXRoIHRoZSBwcm92aWRlZCBjb25maWd1cmF0aW9uLCBjcmVhdGluZ1xyXG4gKiBhIGJyb3dzZXIgaW5zdGFuY2UgYW5kIHNldHRpbmcgdXAgd29ya2VyIHJlc291cmNlcy5cclxuICpcclxuICogQHBhcmFtIHtPYmplY3R9IGNvbmZpZyAtIENvbmZpZ3VyYXRpb24gb3B0aW9ucyBmb3IgdGhlIGV4cG9ydCBwb29sIGFsb25nXHJcbiAqIHdpdGggY3VzdG9tIHB1cHBldGVlciBhcmd1bWVudHMgZm9yIHRoZSBwdXBwZXRlZXIubGF1bmNoIGZ1bmN0aW9uLlxyXG4gKi9cclxuZXhwb3J0IGNvbnN0IGluaXRQb29sID0gYXN5bmMgKGNvbmZpZykgPT4ge1xyXG4gIC8vIEZvciB0aGUgbW9kdWxlIHNjb3BlIHVzYWdlXHJcbiAgcG9vbENvbmZpZyA9IGNvbmZpZyAmJiBjb25maWcucG9vbCA/IHsgLi4uY29uZmlnLnBvb2wgfSA6IHt9O1xyXG5cclxuICAvLyBBdHRhY2ggcHJvY2VzcycgZXhpdCBsaXN0ZW5lcnNcclxuICBpZiAocG9vbENvbmZpZy5saXN0ZW5Ub1Byb2Nlc3NFeGl0cykge1xyXG4gICAgYXR0YWNoUHJvY2Vzc0V4aXRMaXN0ZW5lcnMoKTtcclxuICB9XHJcblxyXG4gIC8vIFRoZSBuZXdlc3QgcHVwcGV0ZWVyIGFyZ3VtZW50cyBmb3IgdGhlIGJyb3dzZXIgY3JlYXRpb25cclxuICBwdXBwZXRlZXJBcmdzID0gY29uZmlnLnB1cHBldGVlckFyZ3M7XHJcblxyXG4gIC8vIENyZWF0ZSBhIGJyb3dzZXIgaW5zdGFuY2VcclxuICBhd2FpdCBjcmVhdGVCcm93c2VyKHB1cHBldGVlckFyZ3MpO1xyXG5cclxuICBsb2coXHJcbiAgICAzLFxyXG4gICAgYFtwb29sXSBJbml0aWFsaXppbmcgcG9vbCB3aXRoIHdvcmtlcnM6IG1pbiAke3Bvb2xDb25maWcubWluV29ya2Vyc30sIG1heCAke3Bvb2xDb25maWcubWF4V29ya2Vyc30uYFxyXG4gICk7XHJcblxyXG4gIGlmIChwb29sKSB7XHJcbiAgICByZXR1cm4gbG9nKFxyXG4gICAgICA0LFxyXG4gICAgICAnW3Bvb2xdIEFscmVhZHkgaW5pdGlhbGl6ZWQsIHBsZWFzZSBraWxsIGl0IGJlZm9yZSBjcmVhdGluZyBhIG5ldyBvbmUuJ1xyXG4gICAgKTtcclxuICB9XHJcblxyXG4gIGlmIChwYXJzZUludChwb29sQ29uZmlnLm1pbldvcmtlcnMpID4gcGFyc2VJbnQocG9vbENvbmZpZy5tYXhXb3JrZXJzKSkge1xyXG4gICAgcG9vbENvbmZpZy5taW5Xb3JrZXJzID0gcG9vbENvbmZpZy5tYXhXb3JrZXJzO1xyXG4gIH1cclxuXHJcbiAgdHJ5IHtcclxuICAgIC8vIENyZWF0ZSBhIHBvb2wgYWxvbmcgd2l0aCBhIG1pbmltYWwgbnVtYmVyIG9mIHJlc291cmNlc1xyXG4gICAgcG9vbCA9IG5ldyBQb29sKHtcclxuICAgICAgLy8gR2V0IHRoZSBjcmVhdGUvdmFsaWRhdGUvZGVzdHJveS9sb2cgZnVuY3Rpb25zXHJcbiAgICAgIC4uLmZhY3RvcnksXHJcbiAgICAgIG1pbjogcGFyc2VJbnQocG9vbENvbmZpZy5taW5Xb3JrZXJzKSxcclxuICAgICAgbWF4OiBwYXJzZUludChwb29sQ29uZmlnLm1heFdvcmtlcnMpLFxyXG4gICAgICBhY3F1aXJlVGltZW91dE1pbGxpczogcG9vbENvbmZpZy5hY3F1aXJlVGltZW91dCxcclxuICAgICAgY3JlYXRlVGltZW91dE1pbGxpczogcG9vbENvbmZpZy5jcmVhdGVUaW1lb3V0LFxyXG4gICAgICBkZXN0cm95VGltZW91dE1pbGxpczogcG9vbENvbmZpZy5kZXN0cm95VGltZW91dCxcclxuICAgICAgaWRsZVRpbWVvdXRNaWxsaXM6IHBvb2xDb25maWcuaWRsZVRpbWVvdXQsXHJcbiAgICAgIGNyZWF0ZVJldHJ5SW50ZXJ2YWxNaWxsaXM6IHBvb2xDb25maWcuY3JlYXRlUmV0cnlJbnRlcnZhbCxcclxuICAgICAgcmVhcEludGVydmFsTWlsbGlzOiBwb29sQ29uZmlnLnJlYXBlckludGVydmFsLFxyXG4gICAgICBwcm9wYWdhdGVDcmVhdGVFcnJvcjogZmFsc2VcclxuICAgIH0pO1xyXG5cclxuICAgIC8vIFNldCBldmVudHNcclxuICAgIHBvb2wub24oJ3JlbGVhc2UnLCBhc3luYyAocmVzb3VyY2UpID0+IHtcclxuICAgICAgLy8gQ2xlYXIgcGFnZVxyXG4gICAgICBhd2FpdCBjbGVhclBhZ2UocmVzb3VyY2UucGFnZSwgZmFsc2UpO1xyXG4gICAgICBsb2coNCwgYFtwb29sXSBSZWxlYXNpbmcgYSB3b3JrZXIgd2l0aCBJRCAke3Jlc291cmNlLmlkfS5gKTtcclxuICAgIH0pO1xyXG5cclxuICAgIHBvb2wub24oJ2Rlc3Ryb3lTdWNjZXNzJywgKGV2ZW50SWQsIHJlc291cmNlKSA9PiB7XHJcbiAgICAgIGxvZyg0LCBgW3Bvb2xdIERlc3Ryb3llZCBhIHdvcmtlciB3aXRoIElEICR7cmVzb3VyY2UuaWR9LmApO1xyXG4gICAgfSk7XHJcblxyXG4gICAgY29uc3QgaW5pdGlhbFJlc291cmNlcyA9IFtdO1xyXG4gICAgLy8gQ3JlYXRlIGFuIGluaXRpYWwgbnVtYmVyIG9mIHJlc291cmNlc1xyXG4gICAgZm9yIChsZXQgaSA9IDA7IGkgPCBwb29sQ29uZmlnLm1pbldvcmtlcnM7IGkrKykge1xyXG4gICAgICB0cnkge1xyXG4gICAgICAgIGNvbnN0IHJlc291cmNlID0gYXdhaXQgcG9vbC5hY3F1aXJlKCkucHJvbWlzZTtcclxuICAgICAgICBpbml0aWFsUmVzb3VyY2VzLnB1c2gocmVzb3VyY2UpO1xyXG4gICAgICB9IGNhdGNoIChlcnJvcikge1xyXG4gICAgICAgIGxvZ1dpdGhTdGFjaygyLCBlcnJvciwgJ1twb29sXSBDb3VsZCBub3QgY3JlYXRlIGFuIGluaXRpYWwgcmVzb3VyY2UuJyk7XHJcbiAgICAgIH1cclxuICAgIH1cclxuXHJcbiAgICAvLyBSZWxlYXNlIHRoZSBpbml0aWFsIG51bWJlciBvZiByZXNvdXJjZXMgYmFjayB0byB0aGUgcG9vbFxyXG4gICAgaW5pdGlhbFJlc291cmNlcy5mb3JFYWNoKChyZXNvdXJjZSkgPT4ge1xyXG4gICAgICBwb29sLnJlbGVhc2UocmVzb3VyY2UpO1xyXG4gICAgfSk7XHJcblxyXG4gICAgbG9nKFxyXG4gICAgICAzLFxyXG4gICAgICBgW3Bvb2xdIFRoZSBwb29sIGlzIHJlYWR5JHtpbml0aWFsUmVzb3VyY2VzLmxlbmd0aCA/IGAgd2l0aCAke2luaXRpYWxSZXNvdXJjZXMubGVuZ3RofSBpbml0aWFsIHJlc291cmNlcyB3YWl0aW5nLmAgOiAnLid9YFxyXG4gICAgKTtcclxuICB9IGNhdGNoIChlcnJvcikge1xyXG4gICAgLy8gQ2xvc2UgYnJvd3NlciBpZiBmb3Igc29tZSByZWFzb24gY2Fubm90IGVzdGFibGlzaCB0aGUgcG9vbFxyXG4gICAgYXdhaXQgYnJvd3NlckNsb3NlKCk7XHJcbiAgICB0aHJvdyBuZXcgRXhwb3J0RXJyb3IoXHJcbiAgICAgICdbcG9vbF0gQ291bGQgbm90IGNyZWF0ZSB0aGUgcG9vbCBvZiB3b3JrZXJzLidcclxuICAgICkuc2V0RXJyb3IoZXJyb3IpO1xyXG4gIH1cclxufTtcclxuXHJcbi8qKlxyXG4gKiBBdHRhY2hlcyBleGl0IGxpc3RlbmVycyB0byB0aGUgcHJvY2VzcywgZW5zdXJpbmcgcHJvcGVyIGNsZWFudXAgb2YgcmVzb3VyY2VzXHJcbiAqIGFuZCB0ZXJtaW5hdGlvbiBvbiBleGl0IHNpZ25hbHMuIEhhbmRsZXMgJ2V4aXQnLCAnU0lHSU5UJywgJ1NJR1RFUk0nLCBhbmRcclxuICogJ3VuY2F1Z2h0RXhjZXB0aW9uJyBldmVudHMuXHJcbiAqL1xyXG5leHBvcnQgZnVuY3Rpb24gYXR0YWNoUHJvY2Vzc0V4aXRMaXN0ZW5lcnMoKSB7XHJcbiAgbG9nKDMsICdbcG9vbF0gQXR0YWNoaW5nIGV4aXQgbGlzdGVuZXJzIHRvIHRoZSBwcm9jZXNzLicpO1xyXG5cclxuICAvLyBLaWxsIGFsbCBwb29sIHJlc291cmNlcyBvbiBleGl0XHJcbiAgcHJvY2Vzcy5vbignZXhpdCcsIGFzeW5jIChjb2RlKSA9PiB7XHJcbiAgICBsb2coNCwgYFByb2Nlc3MgZXhpdGVkIHdpdGggY29kZSAke2NvZGV9LmApO1xyXG4gICAgYXdhaXQga2lsbFBvb2woKTtcclxuICB9KTtcclxuXHJcbiAgLy8gSGFuZGxlciBmb3IgdGhlIFNJR0lOVFxyXG4gIHByb2Nlc3Mub24oJ1NJR0lOVCcsIChuYW1lLCBjb2RlKSA9PiB7XHJcbiAgICBsb2coNCwgYFRoZSAke25hbWV9IGV2ZW50IHdpdGggY29kZTogJHtjb2RlfS5gKTtcclxuICAgIHByb2Nlc3MuZXhpdCgxKTtcclxuICB9KTtcclxuXHJcbiAgLy8gSGFuZGxlciBmb3IgdGhlIFNJR1RFUk1cclxuICBwcm9jZXNzLm9uKCdTSUdURVJNJywgKG5hbWUsIGNvZGUpID0+IHtcclxuICAgIGxvZyg0LCBgVGhlICR7bmFtZX0gZXZlbnQgd2l0aCBjb2RlOiAke2NvZGV9LmApO1xyXG4gICAgcHJvY2Vzcy5leGl0KDEpO1xyXG4gIH0pO1xyXG5cclxuICAvLyBIYW5kbGVyIGZvciB0aGUgdW5jYXVnaHRFeGNlcHRpb25cclxuICBwcm9jZXNzLm9uKCd1bmNhdWdodEV4Y2VwdGlvbicsIGFzeW5jIChlcnJvciwgbmFtZSkgPT4ge1xyXG4gICAgbG9nV2l0aFN0YWNrKDEsIGVycm9yLCBgVGhlICR7bmFtZX0gZXJyb3IuYCk7XHJcbiAgICBhd2FpdCBraWxsUG9vbCgpO1xyXG4gICAgcHJvY2Vzcy5leGl0KDEpO1xyXG4gIH0pO1xyXG59XHJcblxyXG4vKipcclxuICogS2lsbHMgYWxsIHdvcmtlcnMgaW4gdGhlIHBvb2wsIGRlc3Ryb3lzIHRoZSBwb29sLCBhbmQgY2xvc2VzIHRoZSBicm93c2VyXHJcbiAqIGluc3RhbmNlLlxyXG4gKlxyXG4gKiBAcmV0dXJucyB7UHJvbWlzZTx2b2lkPn0gQSBwcm9taXNlIHRoYXQgcmVzb2x2ZXMgYWZ0ZXIgdGhlIHdvcmtlcnMgYXJlXHJcbiAqIGtpbGxlZCwgdGhlIHBvb2wgaXMgZGVzdHJveWVkLCBhbmQgdGhlIGJyb3dzZXIgaXMgY2xvc2VkLlxyXG4gKi9cclxuZXhwb3J0IGFzeW5jIGZ1bmN0aW9uIGtpbGxQb29sKCkge1xyXG4gIGxvZygzLCAnW3Bvb2xdIEtpbGxpbmcgYWxsIHBvb2wgd29ya2VycyBhbmQgYnJvd3NlciwgaWYgYW55IGV4aXN0LicpO1xyXG5cclxuICAvLyBSZXR1cm4gdHJ1ZSB3aGVuIHRoZSBwb29sIGlzIGFscmVhZHkgZGVzdHJveWVkXHJcbiAgaWYgKHBvb2w/LmRlc3Ryb3llZCkge1xyXG4gICAgLy8gQ2xvc2UgdGhlIGJyb3dzZXIgaW5zdGFuY2UgaWYgc3RpbGwgY29ubmVjdGVkXHJcbiAgICByZXR1cm4gYnJvd3NlckNsb3NlKCk7XHJcbiAgfVxyXG5cclxuICAvLyBJZiBzdGlsbCBhbGl2ZSwgZGVzdHJveSB0aGUgcG9vbCBvZiBwYWdlcyBiZWZvcmUgY2xvc2luZyBhIGJyb3dzZXJcclxuICBpZiAocG9vbCkge1xyXG4gICAgYXdhaXQgcG9vbC5kZXN0cm95KCk7XHJcbiAgICBsb2coNCwgJ1ticm93c2VyXSBEZXN0cm95ZWQgdGhlIHBvb2wgb2YgcmVzb3VyY2VzLicpO1xyXG4gIH1cclxuXHJcbiAgLy8gQ2xvc2UgdGhlIGJyb3dzZXIgaW5zdGFuY2VcclxuICByZXR1cm4gYnJvd3NlckNsb3NlKCk7XHJcbn1cclxuXHJcbi8qKlxyXG4gKiBQcm9jZXNzZXMgdGhlIGV4cG9ydCB3b3JrIHVzaW5nIGEgd29ya2VyIGZyb20gdGhlIHBvb2wuIEFjcXVpcmVzIGEgd29ya2VyXHJcbiAqIGhhbmRsZSBmcm9tIHRoZSBwb29sLCBwZXJmb3JtcyB0aGUgZXhwb3J0IHVzaW5nIHB1cHBldGVlciwgYW5kIHJlbGVhc2VzXHJcbiAqIHRoZSB3b3JrZXIgaGFuZGxlIGJhY2sgdG8gdGhlIHBvb2wuXHJcbiAqXHJcbiAqIEBwYXJhbSB7c3RyaW5nfSBjaGFydCAtIFRoZSBjaGFydCBkYXRhIG9yIGNvbmZpZ3VyYXRpb24gdG8gYmUgZXhwb3J0ZWQuXHJcbiAqIEBwYXJhbSB7T2JqZWN0fSBvcHRpb25zIC0gRXhwb3J0IG9wdGlvbnMgYW5kIGNvbmZpZ3VyYXRpb24uXHJcbiAqXHJcbiAqIEByZXR1cm5zIHtQcm9taXNlPE9iamVjdD59IEEgcHJvbWlzZSB0aGF0IHJlc29sdmVzIHdpdGggdGhlIGV4cG9ydCByZXN1bHRhbmRcclxuICogb3B0aW9ucy5cclxuICpcclxuICogQHRocm93cyB7RXhwb3J0RXJyb3J9IElmIGFuIGVycm9yIG9jY3VycyBkdXJpbmcgdGhlIGV4cG9ydCBwcm9jZXNzLlxyXG4gKi9cclxuZXhwb3J0IGNvbnN0IHBvc3RXb3JrID0gYXN5bmMgKGNoYXJ0LCBvcHRpb25zKSA9PiB7XHJcbiAgbGV0IHdvcmtlckhhbmRsZTtcclxuXHJcbiAgdHJ5IHtcclxuICAgIGxvZyg0LCAnW3Bvb2xdIFdvcmsgcmVjZWl2ZWQsIHN0YXJ0aW5nIHRvIHByb2Nlc3MuJyk7XHJcblxyXG4gICAgKytzdGF0cy5leHBvcnRBdHRlbXB0cztcclxuICAgIGlmIChwb29sQ29uZmlnLmJlbmNobWFya2luZykge1xyXG4gICAgICBnZXRQb29sSW5mbygpO1xyXG4gICAgfVxyXG5cclxuICAgIGlmICghcG9vbCkge1xyXG4gICAgICB0aHJvdyBuZXcgRXhwb3J0RXJyb3IoJ1dvcmsgcmVjZWl2ZWQsIGJ1dCBwb29sIGhhcyBub3QgYmVlbiBzdGFydGVkLicpO1xyXG4gICAgfVxyXG5cclxuICAgIC8vIEFjcXVpcmUgdGhlIHdvcmtlciBhbG9uZyB3aXRoIHRoZSBpZCBvZiByZXNvdXJjZSBhbmQgd29yayBjb3VudFxyXG4gICAgdHJ5IHtcclxuICAgICAgbG9nKDQsICdbcG9vbF0gQWNxdWlyaW5nIGEgd29ya2VyIGhhbmRsZS4nKTtcclxuICAgICAgY29uc3QgYWNxdWlyZUNvdW50ZXIgPSBtZWFzdXJlVGltZSgpO1xyXG4gICAgICB3b3JrZXJIYW5kbGUgPSBhd2FpdCBwb29sLmFjcXVpcmUoKS5wcm9taXNlO1xyXG5cclxuICAgICAgLy8gQ2hlY2sgdGhlIHBhZ2UgYWNxdWlyZSB0aW1lXHJcbiAgICAgIGlmIChvcHRpb25zLnNlcnZlci5iZW5jaG1hcmtpbmcpIHtcclxuICAgICAgICBsb2coXHJcbiAgICAgICAgICA1LFxyXG4gICAgICAgICAgb3B0aW9ucy5wYXlsb2FkPy5yZXF1ZXN0SWRcclxuICAgICAgICAgICAgPyBgW2JlbmNobWFya10gUmVxdWVzdCB3aXRoIElEICR7b3B0aW9ucy5wYXlsb2FkPy5yZXF1ZXN0SWR9IC1gXHJcbiAgICAgICAgICAgIDogJ1tiZW5jaG1hcmtdJyxcclxuICAgICAgICAgIGBBY3F1aXJlZCBhIHdvcmtlciBoYW5kbGU6ICR7YWNxdWlyZUNvdW50ZXIoKX1tcy5gXHJcbiAgICAgICAgKTtcclxuICAgICAgfVxyXG4gICAgfSBjYXRjaCAoZXJyb3IpIHtcclxuICAgICAgdGhyb3cgbmV3IEV4cG9ydEVycm9yKFxyXG4gICAgICAgICdFcnJvciBlbmNvdW50ZXJlZCB3aGVuIGFjcXVpcmluZyBhbiBhdmFpbGFibGUgZW50cnkuJ1xyXG4gICAgICApLnNldEVycm9yKGVycm9yKTtcclxuICAgIH1cclxuICAgIGxvZyg0LCAnW3Bvb2xdIEFjcXVpcmVkIGEgd29ya2VyIGhhbmRsZS4nKTtcclxuXHJcbiAgICBpZiAoIXdvcmtlckhhbmRsZS5wYWdlKSB7XHJcbiAgICAgIHRocm93IG5ldyBFeHBvcnRFcnJvcihcclxuICAgICAgICAnUmVzb2x2ZWQgd29ya2VyIHBhZ2UgaXMgaW52YWxpZDogdGhlIHBvb2wgc2V0dXAgaXMgd29ua3kuJ1xyXG4gICAgICApO1xyXG4gICAgfVxyXG5cclxuICAgIC8vIFNhdmUgdGhlIHN0YXJ0IHRpbWVcclxuICAgIGxldCB3b3JrU3RhcnQgPSBuZXcgRGF0ZSgpLmdldFRpbWUoKTtcclxuXHJcbiAgICBsb2coNCwgYFtwb29sXSBTdGFydGluZyB3b3JrIG9uIHBvb2wgZW50cnkgd2l0aCBJRCAke3dvcmtlckhhbmRsZS5pZH0uYCk7XHJcblxyXG4gICAgLy8gUGVyZm9ybSBhbiBleHBvcnQgb24gYSBwdXBwZXRlZXIgbGV2ZWxcclxuICAgIGNvbnN0IGV4cG9ydENvdW50ZXIgPSBtZWFzdXJlVGltZSgpO1xyXG4gICAgY29uc3QgcmVzdWx0ID0gYXdhaXQgcHVwcGV0ZWVyRXhwb3J0KHdvcmtlckhhbmRsZS5wYWdlLCBjaGFydCwgb3B0aW9ucyk7XHJcblxyXG4gICAgLy8gQ2hlY2sgaWYgaXQncyBhbiBlcnJvclxyXG4gICAgaWYgKHJlc3VsdCBpbnN0YW5jZW9mIEVycm9yKSB7XHJcbiAgICAgIC8vIFRPRE86IElmIHRoZSBleHBvcnQgZmFpbGVkIGJlY2F1c2UgcHVwcGV0ZWVyIHRpbWVkIG91dCwgd2UgbmVlZCB0byBmb3JjZSBraWxsIHRoZSB3b3JrZXIgc28gd2UgZ2V0IGEgbmV3IHBhZ2UuIFRoYXQgbmVlZHMgdG8gYmUgaGFuZGxlZCBiZXR0ZXIgdGhhbiB0aGlzIGhhY2suXHJcbiAgICAgIGlmIChyZXN1bHQubWVzc2FnZSA9PT0gJ1Jhc3Rlcml6YXRpb24gdGltZW91dCcpIHtcclxuICAgICAgICB3b3JrZXJIYW5kbGUucGFnZS5jbG9zZSgpO1xyXG4gICAgICAgIHdvcmtlckhhbmRsZS5wYWdlID0gYXdhaXQgYnJvd3Nlck5ld1BhZ2UoKTtcclxuICAgICAgfVxyXG5cclxuICAgICAgdGhyb3cgbmV3IEV4cG9ydEVycm9yKCdFcnJvciBlbmNvdW50ZXJlZCBkdXJpbmcgZXhwb3J0LicpLnNldEVycm9yKFxyXG4gICAgICAgIHJlc3VsdFxyXG4gICAgICApO1xyXG4gICAgfVxyXG5cclxuICAgIC8vIENoZWNrIHRoZSBQdXBwZXRlZXIgZXhwb3J0IHRpbWVcclxuICAgIGlmIChvcHRpb25zLnNlcnZlci5iZW5jaG1hcmtpbmcpIHtcclxuICAgICAgbG9nKFxyXG4gICAgICAgIDUsXHJcbiAgICAgICAgb3B0aW9ucy5wYXlsb2FkPy5yZXF1ZXN0SWRcclxuICAgICAgICAgID8gYFtiZW5jaG1hcmtdIFJlcXVlc3Qgd2l0aCBJRCAke29wdGlvbnMucGF5bG9hZD8ucmVxdWVzdElkfSAtYFxyXG4gICAgICAgICAgOiAnW2JlbmNobWFya10nLFxyXG4gICAgICAgIGBFeHBvcnRlZCBhIGNoYXJ0IHN1Y2Vzc2Z1bGx5OiAke2V4cG9ydENvdW50ZXIoKX1tcy5gXHJcbiAgICAgICk7XHJcbiAgICB9XHJcblxyXG4gICAgLy8gUmVsZWFzZSB0aGUgcmVzb3VyY2UgYmFjayB0byB0aGUgcG9vbFxyXG4gICAgcG9vbC5yZWxlYXNlKHdvcmtlckhhbmRsZSk7XHJcblxyXG4gICAgLy8gVXNlZCBmb3Igc3RhdGlzdGljcyBpbiBhdmVyYWdlVGltZSBhbmQgcHJvY2Vzc2VkV29ya0NvdW50LCB3aGljaFxyXG4gICAgLy8gaW4gdHVybiBpcyB1c2VkIGJ5IHRoZSAvaGVhbHRoIHJvdXRlLlxyXG4gICAgY29uc3Qgd29ya0VuZCA9IG5ldyBEYXRlKCkuZ2V0VGltZSgpO1xyXG4gICAgY29uc3QgZXhwb3J0VGltZSA9IHdvcmtFbmQgLSB3b3JrU3RhcnQ7XHJcbiAgICBzdGF0cy50aW1lU3BlbnQgKz0gZXhwb3J0VGltZTtcclxuICAgIHN0YXRzLnNwZW50QXZlcmFnZSA9IHN0YXRzLnRpbWVTcGVudCAvICsrc3RhdHMucGVyZm9ybWVkRXhwb3J0cztcclxuXHJcbiAgICBsb2coNCwgYFtwb29sXSBXb3JrIGNvbXBsZXRlZCBpbiAke2V4cG9ydFRpbWV9IG1zLmApO1xyXG5cclxuICAgIC8vIE90aGVyd2lzZSByZXR1cm4gdGhlIHJlc3VsdFxyXG4gICAgcmV0dXJuIHtcclxuICAgICAgcmVzdWx0LFxyXG4gICAgICBvcHRpb25zXHJcbiAgICB9O1xyXG4gIH0gY2F0Y2ggKGVycm9yKSB7XHJcbiAgICArK3N0YXRzLmRyb3BwZWRFeHBvcnRzO1xyXG5cclxuICAgIGlmICh3b3JrZXJIYW5kbGUpIHtcclxuICAgICAgcG9vbC5yZWxlYXNlKHdvcmtlckhhbmRsZSk7XHJcbiAgICB9XHJcblxyXG4gICAgdGhyb3cgbmV3IEV4cG9ydEVycm9yKGBbcG9vbF0gSW4gcG9vbC5wb3N0V29yazogJHtlcnJvci5tZXNzYWdlfWApLnNldEVycm9yKFxyXG4gICAgICBlcnJvclxyXG4gICAgKTtcclxuICB9XHJcbn07XHJcblxyXG4vKipcclxuICogUmV0cmlldmVzIHRoZSBjdXJyZW50IHBvb2wgaW5zdGFuY2UuXHJcbiAqXHJcbiAqIEByZXR1cm5zIHtPYmplY3R8bnVsbH0gVGhlIGN1cnJlbnQgcG9vbCBpbnN0YW5jZSBpZiBpbml0aWFsaXplZCwgb3IgbnVsbFxyXG4gKiBpZiB0aGUgcG9vbCBoYXMgbm90IGJlZW4gY3JlYXRlZC5cclxuICovXHJcbmV4cG9ydCBmdW5jdGlvbiBnZXRQb29sKCkge1xyXG4gIHJldHVybiBwb29sO1xyXG59XHJcblxyXG4vKipcclxuICogUmV0cmlldmVzIHBvb2wgaW5mb3JtYXRpb24gaW4gSlNPTiBmb3JtYXQsIGluY2x1ZGluZyBtaW5pbXVtIGFuZCBtYXhpbXVtXHJcbiAqIHdvcmtlcnMsIGF2YWlsYWJsZSB3b3JrZXJzLCB3b3JrZXJzIGluIHVzZSwgYW5kIHBlbmRpbmcgYWNxdWlyZSByZXF1ZXN0cy5cclxuICpcclxuICogQHJldHVybnMge09iamVjdH0gUG9vbCBpbmZvcm1hdGlvbiBpbiBKU09OIGZvcm1hdC5cclxuICovXHJcbmV4cG9ydCBjb25zdCBnZXRQb29sSW5mb0pTT04gPSAoKSA9PiAoe1xyXG4gIG1pbjogcG9vbC5taW4sXHJcbiAgbWF4OiBwb29sLm1heCxcclxuICBhdmFpbGFibGU6IHBvb2wubnVtRnJlZSgpLFxyXG4gIGluVXNlOiBwb29sLm51bVVzZWQoKSxcclxuICBwZW5kaW5nQWNxdWlyZTogcG9vbC5udW1QZW5kaW5nQWNxdWlyZXMoKVxyXG59KTtcclxuXHJcbi8qKlxyXG4gKiBMb2dzIGluZm9ybWF0aW9uIGFib3V0IHRoZSBjdXJyZW50IHN0YXRlIG9mIHRoZSBwb29sLCBpbmNsdWRpbmcgdGhlIG1pbmltdW1cclxuICogYW5kIG1heGltdW0gd29ya2VycywgYXZhaWxhYmxlIHdvcmtlcnMsIHdvcmtlcnMgaW4gdXNlLCBhbmQgcGVuZGluZyBhY3F1aXJlXHJcbiAqIHJlcXVlc3RzLlxyXG4gKi9cclxuZXhwb3J0IGZ1bmN0aW9uIGdldFBvb2xJbmZvKCkge1xyXG4gIGNvbnN0IHsgbWluLCBtYXggfSA9IHBvb2w7XHJcblxyXG4gIGxvZyg1LCBgW3Bvb2xdIFRoZSBtaW5pbXVtIG51bWJlciBvZiByZXNvdXJjZXMgYWxsb3dlZCBieSBwb29sOiAke21pbn0uYCk7XHJcbiAgbG9nKDUsIGBbcG9vbF0gVGhlIG1heGltdW0gbnVtYmVyIG9mIHJlc291cmNlcyBhbGxvd2VkIGJ5IHBvb2w6ICR7bWF4fS5gKTtcclxuICBsb2coXHJcbiAgICA1LFxyXG4gICAgYFtwb29sXSBUaGUgbnVtYmVyIG9mIHJlc291cmNlcyB0aGF0IGFyZSBjdXJyZW50bHkgYXZhaWxhYmxlOiAke3Bvb2wubnVtRnJlZSgpfS5gXHJcbiAgKTtcclxuICBsb2coXHJcbiAgICA1LFxyXG4gICAgYFtwb29sXSBUaGUgbnVtYmVyIG9mIHJlc291cmNlcyB0aGF0IGFyZSBjdXJyZW50bHkgYWNxdWlyZWQ6ICR7cG9vbC5udW1Vc2VkKCl9LmBcclxuICApO1xyXG4gIGxvZyhcclxuICAgIDUsXHJcbiAgICBgW3Bvb2xdIFRoZSBudW1iZXIgb2YgY2FsbGVycyB3YWl0aW5nIHRvIGFjcXVpcmUgYSByZXNvdXJjZTogJHtwb29sLm51bVBlbmRpbmdBY3F1aXJlcygpfS5gXHJcbiAgKTtcclxufVxyXG5cclxuZXhwb3J0IGRlZmF1bHQge1xyXG4gIGluaXRQb29sLFxyXG4gIGtpbGxQb29sLFxyXG4gIHBvc3RXb3JrLFxyXG4gIGdldFBvb2wsXHJcbiAgZ2V0UG9vbEluZm8sXHJcbiAgZ2V0UG9vbEluZm9KU09OLFxyXG4gIGdldFN0YXRzOiAoKSA9PiBzdGF0c1xyXG59O1xyXG4iLCIvKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKlxyXG5cclxuSGlnaGNoYXJ0cyBFeHBvcnQgU2VydmVyXHJcblxyXG5Db3B5cmlnaHQgKGMpIDIwMTYtMjAyNCwgSGlnaHNvZnRcclxuXHJcbkxpY2VuY2VkIHVuZGVyIHRoZSBNSVQgbGljZW5jZS5cclxuXHJcbkFkZGl0aW9uYWxseSBhIHZhbGlkIEhpZ2hjaGFydHMgbGljZW5zZSBpcyByZXF1aXJlZCBmb3IgdXNlLlxyXG5cclxuU2VlIExJQ0VOU0UgZmlsZSBpbiByb290IGZvciBkZXRhaWxzLlxyXG5cclxuKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKi9cclxuXHJcbmltcG9ydCB7IHJlYWRGaWxlU3luYywgd3JpdGVGaWxlU3luYyB9IGZyb20gJ2ZzJztcclxuXHJcbmltcG9ydCB7IGdldE9wdGlvbnMsIGluaXRFeHBvcnRTZXR0aW5ncyB9IGZyb20gJy4vY29uZmlnLmpzJztcclxuaW1wb3J0IHsgbG9nLCBsb2dXaXRoU3RhY2sgfSBmcm9tICcuL2xvZ2dlci5qcyc7XHJcbmltcG9ydCB7IGtpbGxQb29sLCBwb3N0V29yaywgc3RhdHMgfSBmcm9tICcuL3Bvb2wuanMnO1xyXG5pbXBvcnQge1xyXG4gIGZpeFR5cGUsXHJcbiAgaGFuZGxlUmVzb3VyY2VzLFxyXG4gIGlzQ29ycmVjdEpTT04sXHJcbiAgb3B0aW9uc1N0cmluZ2lmeSxcclxuICByb3VuZE51bWJlcixcclxuICB0b0Jvb2xlYW4sXHJcbiAgd3JhcEFyb3VuZFxyXG59IGZyb20gJy4vdXRpbHMuanMnO1xyXG5cclxuaW1wb3J0IEV4cG9ydEVycm9yIGZyb20gJy4vZXJyb3JzL0V4cG9ydEVycm9yLmpzJztcclxuXHJcbmxldCBhbGxvd0NvZGVFeGVjdXRpb24gPSBmYWxzZTtcclxuXHJcbi8qKlxyXG4gKiBTdGFydHMgYW4gZXhwb3J0IHByb2Nlc3MuIFRoZSBgc2V0dGluZ3NgIGNvbnRhaW5zIGZpbmFsIG9wdGlvbnMgZ2F0aGVyZWRcclxuICogZnJvbSBhbGwgcG9zc2libGUgc291cmNlcyAoY29uZmlnLCBlbnYsIGNsaSwganNvbikuIFRoZSBgZW5kQ2FsbGJhY2tgIGlzXHJcbiAqIGNhbGxlZCB3aGVuIHRoZSBleHBvcnQgaXMgY29tcGxldGVkLCB3aXRoIGFuIGVycm9yIG9iamVjdCBhcyB0aGUgZmlyc3RcclxuICogYXJndW1lbnQgYW5kIHRoZSBzZWNvbmQgY29udGFpbmluZyB0aGUgYmFzZTY0IHJlc3ByZXNlbnRhdGlvbiBvZiBhIGNoYXJ0LlxyXG4gKlxyXG4gKiBAcGFyYW0ge09iamVjdH0gc2V0dGluZ3MgLSBUaGUgc2V0dGluZ3Mgb2JqZWN0IGNvbnRhaW5pbmcgZXhwb3J0XHJcbiAqIGNvbmZpZ3VyYXRpb24uXHJcbiAqIEBwYXJhbSB7ZnVuY3Rpb259IGVuZENhbGxiYWNrIC0gVGhlIGNhbGxiYWNrIGZ1bmN0aW9uIHRvIGJlIGludm9rZWQgdXBvblxyXG4gKiBmaW5hbGl6aW5nIHdvcmsgb3IgdXBvbiBlcnJvciBvY2N1cmFuY2Ugb2YgdGhlIGV4cG9ydGluZyBwcm9jZXNzLlxyXG4gKlxyXG4gKiBAcmV0dXJucyB7dm9pZH0gVGhpcyBmdW5jdGlvbiBkb2VzIG5vdCByZXR1cm4gYSB2YWx1ZSBkaXJlY3RseTsgaW5zdGVhZCxcclxuICogaXQgY29tbXVuaWNhdGVzIHJlc3VsdHMgdmlhIHRoZSBlbmRDYWxsYmFjay5cclxuICovXHJcbmV4cG9ydCBjb25zdCBzdGFydEV4cG9ydCA9IGFzeW5jIChzZXR0aW5ncywgZW5kQ2FsbGJhY2spID0+IHtcclxuICAvLyBTdGFydGluZyBleHBvcnRpbmcgcHJvY2VzcyBtZXNzYWdlXHJcbiAgbG9nKDQsICdbY2hhcnRdIFN0YXJ0aW5nIHRoZSBleHBvcnRpbmcgcHJvY2Vzcy4nKTtcclxuXHJcbiAgLy8gSW5pdGlhbGl6ZSBvcHRpb25zXHJcbiAgY29uc3Qgb3B0aW9ucyA9IGluaXRFeHBvcnRTZXR0aW5ncyhzZXR0aW5ncywgZ2V0T3B0aW9ucygpKTtcclxuXHJcbiAgLy8gR2V0IHRoZSBleHBvcnQgb3B0aW9uc1xyXG4gIGNvbnN0IGV4cG9ydE9wdGlvbnMgPSBvcHRpb25zLmV4cG9ydDtcclxuXHJcbiAgLy8gSWYgU1ZHIGlzIGFuIGlucHV0IChhcmd1bWVudCBjYW4gYmUgc2VudCBvbmx5IGJ5IHRoZSByZXF1ZXN0KVxyXG4gIGlmIChvcHRpb25zLnBheWxvYWQ/LnN2ZyAmJiBvcHRpb25zLnBheWxvYWQuc3ZnICE9PSAnJykge1xyXG4gICAgdHJ5IHtcclxuICAgICAgbG9nKDQsICdbY2hhcnRdIEF0dGVtcHRpbmcgdG8gZXhwb3J0IGZyb20gYSBTVkcgaW5wdXQuJyk7XHJcbiAgICAgIGNvbnN0IHJlc3VsdCA9IGV4cG9ydEFzU3RyaW5nKFxyXG4gICAgICAgIG9wdGlvbnMucGF5bG9hZC5zdmcudHJpbSgpLFxyXG4gICAgICAgIG9wdGlvbnMsXHJcbiAgICAgICAgZW5kQ2FsbGJhY2tcclxuICAgICAgKTtcclxuICAgICAgKytzdGF0cy5leHBvcnRGcm9tU3ZnQXR0ZW1wdHM7XHJcbiAgICAgIHJldHVybiByZXN1bHQ7XHJcbiAgICB9IGNhdGNoIChlcnJvcikge1xyXG4gICAgICByZXR1cm4gZW5kQ2FsbGJhY2soXHJcbiAgICAgICAgbmV3IEV4cG9ydEVycm9yKCdbY2hhcnRdIEVycm9yIGxvYWRpbmcgU1ZHIGlucHV0LicpLnNldEVycm9yKGVycm9yKVxyXG4gICAgICApO1xyXG4gICAgfVxyXG4gIH1cclxuXHJcbiAgLy8gRXhwb3J0IHVzaW5nIG9wdGlvbnMgZnJvbSB0aGUgZmlsZVxyXG4gIGlmIChleHBvcnRPcHRpb25zLmluZmlsZSAmJiBleHBvcnRPcHRpb25zLmluZmlsZS5sZW5ndGgpIHtcclxuICAgIC8vIFRyeSB0byByZWFkIHRoZSBmaWxlIHRvIGdldCB0aGUgc3RyaW5nIHJlcHJlc2VudGF0aW9uXHJcbiAgICB0cnkge1xyXG4gICAgICBsb2coNCwgJ1tjaGFydF0gQXR0ZW1wdGluZyB0byBleHBvcnQgZnJvbSBhbiBpbnB1dCBmaWxlLicpO1xyXG4gICAgICBvcHRpb25zLmV4cG9ydC5pbnN0ciA9IHJlYWRGaWxlU3luYyhleHBvcnRPcHRpb25zLmluZmlsZSwgJ3V0ZjgnKTtcclxuICAgICAgcmV0dXJuIGV4cG9ydEFzU3RyaW5nKG9wdGlvbnMuZXhwb3J0Lmluc3RyLnRyaW0oKSwgb3B0aW9ucywgZW5kQ2FsbGJhY2spO1xyXG4gICAgfSBjYXRjaCAoZXJyb3IpIHtcclxuICAgICAgcmV0dXJuIGVuZENhbGxiYWNrKFxyXG4gICAgICAgIG5ldyBFeHBvcnRFcnJvcignW2NoYXJ0XSBFcnJvciBsb2FkaW5nIGlucHV0IGZpbGUuJykuc2V0RXJyb3IoZXJyb3IpXHJcbiAgICAgICk7XHJcbiAgICB9XHJcbiAgfVxyXG5cclxuICAvLyBFeHBvcnQgd2l0aCBvcHRpb25zIGZyb20gdGhlIHJhdyByZXByZXNlbnRhdGlvblxyXG4gIGlmIChcclxuICAgIChleHBvcnRPcHRpb25zLmluc3RyICYmIGV4cG9ydE9wdGlvbnMuaW5zdHIgIT09ICcnKSB8fFxyXG4gICAgKGV4cG9ydE9wdGlvbnMub3B0aW9ucyAmJiBleHBvcnRPcHRpb25zLm9wdGlvbnMgIT09ICcnKVxyXG4gICkge1xyXG4gICAgdHJ5IHtcclxuICAgICAgbG9nKDQsICdbY2hhcnRdIEF0dGVtcHRpbmcgdG8gZXhwb3J0IGZyb20gYSByYXcgaW5wdXQuJyk7XHJcblxyXG4gICAgICAvLyBQZXJmb3JtIGEgZGlyZWN0IGluamVjdCB3aGVuIGZvcmNlZFxyXG4gICAgICBpZiAodG9Cb29sZWFuKG9wdGlvbnMuY3VzdG9tTG9naWM/LmFsbG93Q29kZUV4ZWN1dGlvbikpIHtcclxuICAgICAgICByZXR1cm4gZG9TdHJhaWdodEluamVjdChvcHRpb25zLCBlbmRDYWxsYmFjayk7XHJcbiAgICAgIH1cclxuXHJcbiAgICAgIC8vIEVpdGhlciB0cnkgdG8gcGFyc2UgdG8gSlNPTiBmaXJzdCBvciBkbyB0aGUgZGlyZWN0IGV4cG9ydFxyXG4gICAgICByZXR1cm4gdHlwZW9mIGV4cG9ydE9wdGlvbnMuaW5zdHIgPT09ICdzdHJpbmcnXHJcbiAgICAgICAgPyBleHBvcnRBc1N0cmluZyhleHBvcnRPcHRpb25zLmluc3RyLnRyaW0oKSwgb3B0aW9ucywgZW5kQ2FsbGJhY2spXHJcbiAgICAgICAgOiBkb0V4cG9ydChcclxuICAgICAgICAgICAgb3B0aW9ucyxcclxuICAgICAgICAgICAgZXhwb3J0T3B0aW9ucy5pbnN0ciB8fCBleHBvcnRPcHRpb25zLm9wdGlvbnMsXHJcbiAgICAgICAgICAgIGVuZENhbGxiYWNrXHJcbiAgICAgICAgICApO1xyXG4gICAgfSBjYXRjaCAoZXJyb3IpIHtcclxuICAgICAgcmV0dXJuIGVuZENhbGxiYWNrKFxyXG4gICAgICAgIG5ldyBFeHBvcnRFcnJvcignW2NoYXJ0XSBFcnJvciBsb2FkaW5nIHJhdyBpbnB1dC4nKS5zZXRFcnJvcihlcnJvcilcclxuICAgICAgKTtcclxuICAgIH1cclxuICB9XHJcblxyXG4gIC8vIE5vIGlucHV0IHNwZWNpZmllZCwgcGFzcyBhbiBlcnJvciBtZXNzYWdlIHRvIHRoZSBjYWxsYmFja1xyXG4gIHJldHVybiBlbmRDYWxsYmFjayhcclxuICAgIG5ldyBFeHBvcnRFcnJvcihcclxuICAgICAgYFtjaGFydF0gTm8gdmFsaWQgaW5wdXQgc3BlY2lmaWVkLiBDaGVjayBpZiBhdCBsZWFzdCBvbmUgb2YgdGhlIGZvbGxvd2luZyBwYXJhbWV0ZXJzIGlzIGNvcnJlY3RseSBzZXQ6ICdpbmZpbGUnLCAnaW5zdHInLCAnb3B0aW9ucycsIG9yICdzdmcnLmBcclxuICAgIClcclxuICApO1xyXG59O1xyXG5cclxuLyoqXHJcbiAqIFN0YXJ0cyBhIGJhdGNoIGV4cG9ydCBwcm9jZXNzIGZvciBtdWx0aXBsZSBjaGFydHMgYmFzZWQgb24gdGhlIGluZm9ybWF0aW9uXHJcbiAqIGluIHRoZSBiYXRjaCBvcHRpb24uIFRoZSBiYXRjaCBpcyBhIHN0cmluZyBpbiB0aGUgZm9sbG93aW5nIGZvcm1hdDpcclxuICogXCJpbmZpbGUxLmpzb249b3V0ZmlsZTEucG5nO2luZmlsZTIuanNvbj1vdXRmaWxlMi5wbmc7Li4uXCJcclxuICpcclxuICogQHBhcmFtIHtPYmplY3R9IG9wdGlvbnMgLSBUaGUgb3B0aW9ucyBvYmplY3QgY29udGFpbmluZyBjb25maWd1cmF0aW9uIGZvclxyXG4gKiBhIGJhdGNoIGV4cG9ydC5cclxuICpcclxuICogQHJldHVybnMge1Byb21pc2U8dm9pZD59IEEgUHJvbWlzZSB0aGF0IHJlc29sdmVzIG9uY2UgdGhlIGJhdGNoIGV4cG9ydFxyXG4gKiBwcm9jZXNzIGlzIGNvbXBsZXRlZC5cclxuICpcclxuICogQHRocm93cyB7RXhwb3J0RXJyb3J9IFRocm93cyBhbiBFeHBvcnRFcnJvciBpZiBhbiBlcnJvciBvY2N1cnMgZHVyaW5nXHJcbiAqIGFueSBvZiB0aGUgYmF0Y2ggZXhwb3J0IHByb2Nlc3MuXHJcbiAqL1xyXG5leHBvcnQgY29uc3QgYmF0Y2hFeHBvcnQgPSBhc3luYyAob3B0aW9ucykgPT4ge1xyXG4gIGNvbnN0IGJhdGNoRnVuY3Rpb25zID0gW107XHJcblxyXG4gIC8vIFNwbGl0IGFuZCBwYWlyIHRoZSAtLWJhdGNoIGFyZ3VtZW50c1xyXG4gIGZvciAobGV0IHBhaXIgb2Ygb3B0aW9ucy5leHBvcnQuYmF0Y2guc3BsaXQoJzsnKSkge1xyXG4gICAgcGFpciA9IHBhaXIuc3BsaXQoJz0nKTtcclxuICAgIGlmIChwYWlyLmxlbmd0aCA9PT0gMikge1xyXG4gICAgICBiYXRjaEZ1bmN0aW9ucy5wdXNoKFxyXG4gICAgICAgIHN0YXJ0RXhwb3J0KFxyXG4gICAgICAgICAge1xyXG4gICAgICAgICAgICAuLi5vcHRpb25zLFxyXG4gICAgICAgICAgICBleHBvcnQ6IHtcclxuICAgICAgICAgICAgICAuLi5vcHRpb25zLmV4cG9ydCxcclxuICAgICAgICAgICAgICBpbmZpbGU6IHBhaXJbMF0sXHJcbiAgICAgICAgICAgICAgb3V0ZmlsZTogcGFpclsxXVxyXG4gICAgICAgICAgICB9XHJcbiAgICAgICAgICB9LFxyXG4gICAgICAgICAgKGVycm9yLCBpbmZvKSA9PiB7XHJcbiAgICAgICAgICAgIC8vIFRocm93IGFuIGVycm9yXHJcbiAgICAgICAgICAgIGlmIChlcnJvcikge1xyXG4gICAgICAgICAgICAgIHRocm93IGVycm9yO1xyXG4gICAgICAgICAgICB9XHJcblxyXG4gICAgICAgICAgICAvLyBTYXZlIHRoZSBiYXNlNjQgZnJvbSBhIGJ1ZmZlciB0byBhIGNvcnJlY3QgaW1hZ2UgZmlsZVxyXG4gICAgICAgICAgICB3cml0ZUZpbGVTeW5jKFxyXG4gICAgICAgICAgICAgIGluZm8ub3B0aW9ucy5leHBvcnQub3V0ZmlsZSxcclxuICAgICAgICAgICAgICBCdWZmZXIuZnJvbShpbmZvLnJlc3VsdCwgJ2Jhc2U2NCcpXHJcbiAgICAgICAgICAgICk7XHJcbiAgICAgICAgICB9XHJcbiAgICAgICAgKVxyXG4gICAgICApO1xyXG4gICAgfVxyXG4gIH1cclxuXHJcbiAgdHJ5IHtcclxuICAgIC8vIEF3YWl0IGFsbCBleHBvcnRzIGFyZSBkb25lXHJcbiAgICBhd2FpdCBQcm9taXNlLmFsbChiYXRjaEZ1bmN0aW9ucyk7XHJcblxyXG4gICAgLy8gS2lsbCBwb29sIGFuZCBjbG9zZSBicm93c2VyIGFmdGVyIGZpbmlzaGluZyBiYXRjaCBleHBvcnRcclxuICAgIGF3YWl0IGtpbGxQb29sKCk7XHJcbiAgfSBjYXRjaCAoZXJyb3IpIHtcclxuICAgIHRocm93IG5ldyBFeHBvcnRFcnJvcihcclxuICAgICAgJ1tjaGFydF0gRXJyb3IgZW5jb3VudGVyZWQgZHVyaW5nIGJhdGNoIGV4cG9ydC4nXHJcbiAgICApLnNldEVycm9yKGVycm9yKTtcclxuICB9XHJcbn07XHJcblxyXG4vKipcclxuICogU3RhcnRzIGEgc2luZ2xlIGV4cG9ydCBwcm9jZXNzIGJhc2VkIG9uIHRoZSBzcGVjaWZpZWQgb3B0aW9ucy5cclxuICpcclxuICogQHBhcmFtIHtPYmplY3R9IG9wdGlvbnMgLSBUaGUgb3B0aW9ucyBvYmplY3QgY29udGFpbmluZyBjb25maWd1cmF0aW9uIGZvclxyXG4gKiBhIHNpbmdsZSBleHBvcnQuXHJcbiAqXHJcbiAqIEByZXR1cm5zIHtQcm9taXNlPHZvaWQ+fSBBIFByb21pc2UgdGhhdCByZXNvbHZlcyBvbmNlIHRoZSBzaW5nbGUgZXhwb3J0XHJcbiAqIHByb2Nlc3MgaXMgY29tcGxldGVkLlxyXG4gKlxyXG4gKiBAdGhyb3dzIHtFeHBvcnRFcnJvcn0gVGhyb3dzIGFuIEV4cG9ydEVycm9yIGlmIGFuIGVycm9yIG9jY3VycyBkdXJpbmdcclxuICogdGhlIHNpbmdsZSBleHBvcnQgcHJvY2Vzcy5cclxuICovXHJcbmV4cG9ydCBjb25zdCBzaW5nbGVFeHBvcnQgPSBhc3luYyAob3B0aW9ucykgPT4ge1xyXG4gIC8vIFVzZSBpbnN0ciBvciBpdHMgYWxpYXMsIG9wdGlvbnNcclxuICBvcHRpb25zLmV4cG9ydC5pbnN0ciA9IG9wdGlvbnMuZXhwb3J0Lmluc3RyIHx8IG9wdGlvbnMuZXhwb3J0Lm9wdGlvbnM7XHJcblxyXG4gIC8vIFBlcmZvcm0gYW4gZXhwb3J0XHJcbiAgYXdhaXQgc3RhcnRFeHBvcnQob3B0aW9ucywgYXN5bmMgKGVycm9yLCBpbmZvKSA9PiB7XHJcbiAgICAvLyBFeGl0IHByb2Nlc3Mgd2hlbiBlcnJvclxyXG4gICAgaWYgKGVycm9yKSB7XHJcbiAgICAgIHRocm93IGVycm9yO1xyXG4gICAgfVxyXG5cclxuICAgIGNvbnN0IHsgb3V0ZmlsZSwgdHlwZSB9ID0gaW5mby5vcHRpb25zLmV4cG9ydDtcclxuXHJcbiAgICAvLyBTYXZlIHRoZSBiYXNlNjQgZnJvbSBhIGJ1ZmZlciB0byBhIGNvcnJlY3QgaW1hZ2UgZmlsZVxyXG4gICAgd3JpdGVGaWxlU3luYyhcclxuICAgICAgb3V0ZmlsZSB8fCBgY2hhcnQuJHt0eXBlfWAsXHJcbiAgICAgIHR5cGUgIT09ICdzdmcnID8gQnVmZmVyLmZyb20oaW5mby5yZXN1bHQsICdiYXNlNjQnKSA6IGluZm8ucmVzdWx0XHJcbiAgICApO1xyXG5cclxuICAgIC8vIEtpbGwgdGhlIHBvb2xcclxuICAgIGF3YWl0IGtpbGxQb29sKCk7XHJcbiAgfSk7XHJcbn07XHJcblxyXG4vKipcclxuICogRGV0ZXJtaW5lcyB0aGUgc2l6ZSBhbmQgc2NhbGUgZm9yIGNoYXJ0IGV4cG9ydCBiYXNlZCBvbiB0aGUgcHJvdmlkZWQgb3B0aW9ucy5cclxuICpcclxuICogQHBhcmFtIHtPYmplY3R9IG9wdGlvbnMgLSBUaGUgb3B0aW9ucyBvYmplY3QgY29udGFpbmluZyBjb25maWd1cmF0aW9uIGZvclxyXG4gKiBjaGFydCBleHBvcnQuXHJcbiAqXHJcbiAqIEByZXR1cm5zIHtPYmplY3R9IEFuIG9iamVjdCBjb250YWluaW5nIHRoZSBjYWxjdWxhdGVkIGhlaWdodCwgd2lkdGgsXHJcbiAqIGFuZCBzY2FsZSBmb3IgdGhlIGNoYXJ0IGV4cG9ydC5cclxuICovXHJcbmV4cG9ydCBjb25zdCBmaW5kQ2hhcnRTaXplID0gKG9wdGlvbnMpID0+IHtcclxuICBjb25zdCB7IGNoYXJ0LCBleHBvcnRpbmcgfSA9XHJcbiAgICBvcHRpb25zLmV4cG9ydD8ub3B0aW9ucyB8fCBpc0NvcnJlY3RKU09OKG9wdGlvbnMuZXhwb3J0Py5pbnN0cik7XHJcblxyXG4gIC8vIFNlZSBpZiBnbG9iYWxPcHRpb25zIGhvbGRzIGNoYXJ0IG9yIGV4cG9ydGluZyBzaXplXHJcbiAgY29uc3QgZ2xvYmFsT3B0aW9ucyA9IGlzQ29ycmVjdEpTT04ob3B0aW9ucy5leHBvcnQ/Lmdsb2JhbE9wdGlvbnMpO1xyXG5cclxuICAvLyBTZWN1cmUgc2NhbGUgdmFsdWVcclxuICBsZXQgc2NhbGUgPVxyXG4gICAgb3B0aW9ucy5leHBvcnQ/LnNjYWxlIHx8XHJcbiAgICBleHBvcnRpbmc/LnNjYWxlIHx8XHJcbiAgICBnbG9iYWxPcHRpb25zPy5leHBvcnRpbmc/LnNjYWxlIHx8XHJcbiAgICBvcHRpb25zLmV4cG9ydD8uZGVmYXVsdFNjYWxlIHx8XHJcbiAgICAxO1xyXG5cclxuICAvLyB0aGUgc2NhbGUgY2Fubm90IGJlIGxvd2VyIHRoYW4gMC4xIGFuZCBjYW5ub3QgYmUgaGlnaGVyIHRoYW4gNS4wXHJcbiAgc2NhbGUgPSBNYXRoLm1heCgwLjEsIE1hdGgubWluKHNjYWxlLCA1LjApKTtcclxuXHJcbiAgLy8gd2Ugd2FudCB0byByb3VuZCB0aGUgbnVtYmVycyBsaWtlIDAuMjMyMzQgLT4gMC4yM1xyXG4gIHNjYWxlID0gcm91bmROdW1iZXIoc2NhbGUsIDIpO1xyXG5cclxuICAvLyBGaW5kIGNoYXJ0IHNpemUgYW5kIHNjYWxlXHJcbiAgY29uc3Qgc2l6ZSA9IHtcclxuICAgIGhlaWdodDpcclxuICAgICAgb3B0aW9ucy5leHBvcnQ/LmhlaWdodCB8fFxyXG4gICAgICBleHBvcnRpbmc/LnNvdXJjZUhlaWdodCB8fFxyXG4gICAgICBjaGFydD8uaGVpZ2h0IHx8XHJcbiAgICAgIGdsb2JhbE9wdGlvbnM/LmV4cG9ydGluZz8uc291cmNlSGVpZ2h0IHx8XHJcbiAgICAgIGdsb2JhbE9wdGlvbnM/LmNoYXJ0Py5oZWlnaHQgfHxcclxuICAgICAgb3B0aW9ucy5leHBvcnQ/LmRlZmF1bHRIZWlnaHQgfHxcclxuICAgICAgNDAwLFxyXG4gICAgd2lkdGg6XHJcbiAgICAgIG9wdGlvbnMuZXhwb3J0Py53aWR0aCB8fFxyXG4gICAgICBleHBvcnRpbmc/LnNvdXJjZVdpZHRoIHx8XHJcbiAgICAgIGNoYXJ0Py53aWR0aCB8fFxyXG4gICAgICBnbG9iYWxPcHRpb25zPy5leHBvcnRpbmc/LnNvdXJjZVdpZHRoIHx8XHJcbiAgICAgIGdsb2JhbE9wdGlvbnM/LmNoYXJ0Py53aWR0aCB8fFxyXG4gICAgICBvcHRpb25zLmV4cG9ydD8uZGVmYXVsdFdpZHRoIHx8XHJcbiAgICAgIDYwMCxcclxuICAgIHNjYWxlXHJcbiAgfTtcclxuXHJcbiAgLy8gR2V0IHJpZCBvZiBwb3RlbnRpYWwgcHggYW5kICVcclxuICBmb3IgKGxldCBbcGFyYW0sIHZhbHVlXSBvZiBPYmplY3QuZW50cmllcyhzaXplKSkge1xyXG4gICAgc2l6ZVtwYXJhbV0gPVxyXG4gICAgICB0eXBlb2YgdmFsdWUgPT09ICdzdHJpbmcnID8gK3ZhbHVlLnJlcGxhY2UoL3B4fCUvZ2ksICcnKSA6IHZhbHVlO1xyXG4gIH1cclxuICByZXR1cm4gc2l6ZTtcclxufTtcclxuXHJcbi8qKlxyXG4gKiBGdW5jdGlvbiBmb3IgZmluYWxpemluZyBvcHRpb25zIGJlZm9yZSBleHBvcnQuXHJcbiAqXHJcbiAqIEBwYXJhbSB7T2JqZWN0fSBvcHRpb25zIC0gVGhlIG9wdGlvbnMgb2JqZWN0IGNvbnRhaW5pbmcgY29uZmlndXJhdGlvbiBmb3JcclxuICogdGhlIGV4cG9ydCBwcm9jZXNzLlxyXG4gKiBAcGFyYW0ge09iamVjdH0gY2hhcnRKc29uIC0gVGhlIEpTT04gcmVwcmVzZW50YXRpb24gb2YgdGhlIGNoYXJ0LlxyXG4gKiBAcGFyYW0ge0Z1bmN0aW9ufSBlbmRDYWxsYmFjayAtIFRoZSBjYWxsYmFjayBmdW5jdGlvbiB0byBiZSBjYWxsZWQgdXBvblxyXG4gKiBjb21wbGV0aW9uIG9yIGVycm9yLlxyXG4gKiBAcGFyYW0ge3N0cmluZ30gc3ZnIC0gVGhlIFNWRyByZXByZXNlbnRhdGlvbiBvZiB0aGUgY2hhcnQuXHJcbiAqXHJcbiAqIEByZXR1cm5zIHtQcm9taXNlPHZvaWQ+fSBBIFByb21pc2UgdGhhdCByZXNvbHZlcyBvbmNlIHRoZSBleHBvcnQgcHJvY2Vzc1xyXG4gKiBpcyBjb21wbGV0ZWQuXHJcbiAqL1xyXG5jb25zdCBkb0V4cG9ydCA9IGFzeW5jIChvcHRpb25zLCBjaGFydEpzb24sIGVuZENhbGxiYWNrLCBzdmcpID0+IHtcclxuICBsZXQgeyBleHBvcnQ6IGV4cG9ydE9wdGlvbnMsIGN1c3RvbUxvZ2ljOiBjdXN0b21Mb2dpY09wdGlvbnMgfSA9IG9wdGlvbnM7XHJcblxyXG4gIGNvbnN0IGFsbG93Q29kZUV4ZWN1dGlvblNjb3BlZCA9XHJcbiAgICB0eXBlb2YgY3VzdG9tTG9naWNPcHRpb25zLmFsbG93Q29kZUV4ZWN1dGlvbiA9PT0gJ2Jvb2xlYW4nXHJcbiAgICAgID8gY3VzdG9tTG9naWNPcHRpb25zLmFsbG93Q29kZUV4ZWN1dGlvblxyXG4gICAgICA6IGFsbG93Q29kZUV4ZWN1dGlvbjtcclxuXHJcbiAgaWYgKCFjdXN0b21Mb2dpY09wdGlvbnMpIHtcclxuICAgIGN1c3RvbUxvZ2ljT3B0aW9ucyA9IG9wdGlvbnMuY3VzdG9tTG9naWMgPSB7fTtcclxuICB9IGVsc2UgaWYgKGFsbG93Q29kZUV4ZWN1dGlvblNjb3BlZCkge1xyXG4gICAgaWYgKHR5cGVvZiBvcHRpb25zLmN1c3RvbUxvZ2ljLnJlc291cmNlcyA9PT0gJ3N0cmluZycpIHtcclxuICAgICAgLy8gUHJvY2VzcyByZXNvdXJjZXNcclxuICAgICAgb3B0aW9ucy5jdXN0b21Mb2dpYy5yZXNvdXJjZXMgPSBoYW5kbGVSZXNvdXJjZXMoXHJcbiAgICAgICAgb3B0aW9ucy5jdXN0b21Mb2dpYy5yZXNvdXJjZXMsXHJcbiAgICAgICAgdG9Cb29sZWFuKG9wdGlvbnMuY3VzdG9tTG9naWMuYWxsb3dGaWxlUmVzb3VyY2VzKVxyXG4gICAgICApO1xyXG4gICAgfSBlbHNlIGlmICghb3B0aW9ucy5jdXN0b21Mb2dpYy5yZXNvdXJjZXMpIHtcclxuICAgICAgdHJ5IHtcclxuICAgICAgICBjb25zdCByZXNvdXJjZXMgPSByZWFkRmlsZVN5bmMoJ3Jlc291cmNlcy5qc29uJywgJ3V0ZjgnKTtcclxuICAgICAgICBvcHRpb25zLmN1c3RvbUxvZ2ljLnJlc291cmNlcyA9IGhhbmRsZVJlc291cmNlcyhcclxuICAgICAgICAgIHJlc291cmNlcyxcclxuICAgICAgICAgIHRvQm9vbGVhbihvcHRpb25zLmN1c3RvbUxvZ2ljLmFsbG93RmlsZVJlc291cmNlcylcclxuICAgICAgICApO1xyXG4gICAgICB9IGNhdGNoIChlcnJvcikge1xyXG4gICAgICAgIGxvZ1dpdGhTdGFjayhcclxuICAgICAgICAgIDIsXHJcbiAgICAgICAgICBlcnJvcixcclxuICAgICAgICAgIGBbY2hhcnRdIFVuYWJsZSB0byBsb2FkIHRoZSBkZWZhdWx0IHJlc291cmNlcy5qc29uIGZpbGUuYFxyXG4gICAgICAgICk7XHJcbiAgICAgIH1cclxuICAgIH1cclxuICB9XHJcblxyXG4gIC8vIElmIHRoZSBhbGxvd0NvZGVFeGVjdXRpb24gZmxhZyBpc24ndCBzZXQsIHdlIHNob3VsZCByZWZ1c2UgdGhlIHVzYWdlXHJcbiAgLy8gb2YgY2FsbGJhY2ssIHJlc291cmNlcywgYW5kIGN1c3RvbSBjb2RlLiBBZGRpdGlvbmFsbHksIHRoZSB3b3JrZXIgd2lsbFxyXG4gIC8vIHJlZnVzZSB0byBydW4gYXJiaXRyYXJ5IEphdmFTY3JpcHQuIFByaW9yaXRpemVkIHNob3VsZCBiZSB0aGUgc2NvcGVkXHJcbiAgLy8gb3B0aW9uLCB0aGVuIHdlIHNob3VsZCB0YWtlIGEgbG9vayBhdCB0aGUgb3ZlcmFsbCBwb29sIG9wdGlvbi5cclxuICBpZiAoIWFsbG93Q29kZUV4ZWN1dGlvblNjb3BlZCAmJiBjdXN0b21Mb2dpY09wdGlvbnMpIHtcclxuICAgIGlmIChcclxuICAgICAgY3VzdG9tTG9naWNPcHRpb25zLmNhbGxiYWNrIHx8XHJcbiAgICAgIGN1c3RvbUxvZ2ljT3B0aW9ucy5yZXNvdXJjZXMgfHxcclxuICAgICAgY3VzdG9tTG9naWNPcHRpb25zLmN1c3RvbUNvZGVcclxuICAgICkge1xyXG4gICAgICAvLyBTZW5kIGJhY2sgYSBmcmllbmRseSBtZXNzYWdlIHNheWluZyB0aGF0IHRoZSBleHBvcnRlciBkb2VzIG5vdCBzdXBwb3J0XHJcbiAgICAgIC8vIHRoZXNlIHNldHRpbmdzLlxyXG4gICAgICByZXR1cm4gZW5kQ2FsbGJhY2soXHJcbiAgICAgICAgbmV3IEV4cG9ydEVycm9yKFxyXG4gICAgICAgICAgYFtjaGFydF0gVGhlICdjYWxsYmFjaycsICdyZXNvdXJjZXMnIGFuZCAnY3VzdG9tQ29kZScgb3B0aW9ucyBoYXZlIGJlZW4gZGlzYWJsZWQgZm9yIHRoaXMgc2VydmVyLmBcclxuICAgICAgICApXHJcbiAgICAgICk7XHJcbiAgICB9XHJcblxyXG4gICAgLy8gUmVzZXQgYWxsIGFkZGl0aW9uYWwgY3VzdG9tIGNvZGVcclxuICAgIGN1c3RvbUxvZ2ljT3B0aW9ucy5jYWxsYmFjayA9IGZhbHNlO1xyXG4gICAgY3VzdG9tTG9naWNPcHRpb25zLnJlc291cmNlcyA9IGZhbHNlO1xyXG4gICAgY3VzdG9tTG9naWNPcHRpb25zLmN1c3RvbUNvZGUgPSBmYWxzZTtcclxuICB9XHJcblxyXG4gIC8vIENsZWFuIHByb3BlcnRpZXMgdG8ga2VlcCBpdCBsZWFuIGFuZCBtZWFuXHJcbiAgaWYgKGNoYXJ0SnNvbikge1xyXG4gICAgY2hhcnRKc29uLmNoYXJ0ID0gY2hhcnRKc29uLmNoYXJ0IHx8IHt9O1xyXG4gICAgY2hhcnRKc29uLmV4cG9ydGluZyA9IGNoYXJ0SnNvbi5leHBvcnRpbmcgfHwge307XHJcbiAgICBjaGFydEpzb24uZXhwb3J0aW5nLmVuYWJsZWQgPSBmYWxzZTtcclxuICB9XHJcblxyXG4gIGV4cG9ydE9wdGlvbnMuY29uc3RyID0gZXhwb3J0T3B0aW9ucy5jb25zdHIgfHwgJ2NoYXJ0JztcclxuICBleHBvcnRPcHRpb25zLnR5cGUgPSBmaXhUeXBlKGV4cG9ydE9wdGlvbnMudHlwZSwgZXhwb3J0T3B0aW9ucy5vdXRmaWxlKTtcclxuICBpZiAoZXhwb3J0T3B0aW9ucy50eXBlID09PSAnc3ZnJykge1xyXG4gICAgZXhwb3J0T3B0aW9ucy53aWR0aCA9IGZhbHNlO1xyXG4gIH1cclxuXHJcbiAgLy8gUHJlcGFyZSBnbG9iYWwgYW5kIHRoZW1lIG9wdGlvbnNcclxuICBbJ2dsb2JhbE9wdGlvbnMnLCAndGhlbWVPcHRpb25zJ10uZm9yRWFjaCgob3B0aW9uc05hbWUpID0+IHtcclxuICAgIHRyeSB7XHJcbiAgICAgIGlmIChleHBvcnRPcHRpb25zICYmIGV4cG9ydE9wdGlvbnNbb3B0aW9uc05hbWVdKSB7XHJcbiAgICAgICAgaWYgKFxyXG4gICAgICAgICAgdHlwZW9mIGV4cG9ydE9wdGlvbnNbb3B0aW9uc05hbWVdID09PSAnc3RyaW5nJyAmJlxyXG4gICAgICAgICAgZXhwb3J0T3B0aW9uc1tvcHRpb25zTmFtZV0uZW5kc1dpdGgoJy5qc29uJylcclxuICAgICAgICApIHtcclxuICAgICAgICAgIGV4cG9ydE9wdGlvbnNbb3B0aW9uc05hbWVdID0gaXNDb3JyZWN0SlNPTihcclxuICAgICAgICAgICAgcmVhZEZpbGVTeW5jKGV4cG9ydE9wdGlvbnNbb3B0aW9uc05hbWVdLCAndXRmOCcpLFxyXG4gICAgICAgICAgICB0cnVlXHJcbiAgICAgICAgICApO1xyXG4gICAgICAgIH0gZWxzZSB7XHJcbiAgICAgICAgICBleHBvcnRPcHRpb25zW29wdGlvbnNOYW1lXSA9IGlzQ29ycmVjdEpTT04oXHJcbiAgICAgICAgICAgIGV4cG9ydE9wdGlvbnNbb3B0aW9uc05hbWVdLFxyXG4gICAgICAgICAgICB0cnVlXHJcbiAgICAgICAgICApO1xyXG4gICAgICAgIH1cclxuICAgICAgfVxyXG4gICAgfSBjYXRjaCAoZXJyb3IpIHtcclxuICAgICAgZXhwb3J0T3B0aW9uc1tvcHRpb25zTmFtZV0gPSB7fTtcclxuICAgICAgbG9nV2l0aFN0YWNrKDIsIGVycm9yLCBgW2NoYXJ0XSBUaGUgJyR7b3B0aW9uc05hbWV9JyBjYW5ub3QgYmUgbG9hZGVkLmApO1xyXG4gICAgfVxyXG4gIH0pO1xyXG5cclxuICAvLyBQcmVwYXJlIHRoZSBjdXN0b21Db2RlXHJcbiAgaWYgKGN1c3RvbUxvZ2ljT3B0aW9ucy5hbGxvd0NvZGVFeGVjdXRpb24pIHtcclxuICAgIHRyeSB7XHJcbiAgICAgIGN1c3RvbUxvZ2ljT3B0aW9ucy5jdXN0b21Db2RlID0gd3JhcEFyb3VuZChcclxuICAgICAgICBjdXN0b21Mb2dpY09wdGlvbnMuY3VzdG9tQ29kZSxcclxuICAgICAgICBjdXN0b21Mb2dpY09wdGlvbnMuYWxsb3dGaWxlUmVzb3VyY2VzXHJcbiAgICAgICk7XHJcbiAgICB9IGNhdGNoIChlcnJvcikge1xyXG4gICAgICBsb2dXaXRoU3RhY2soMiwgZXJyb3IsIGBbY2hhcnRdIFRoZSAnY3VzdG9tQ29kZScgY2Fubm90IGJlIGxvYWRlZC5gKTtcclxuICAgIH1cclxuICB9XHJcblxyXG4gIC8vIEdldCB0aGUgY2FsbGJhY2tcclxuICBpZiAoXHJcbiAgICBjdXN0b21Mb2dpY09wdGlvbnMgJiZcclxuICAgIGN1c3RvbUxvZ2ljT3B0aW9ucy5jYWxsYmFjayAmJlxyXG4gICAgY3VzdG9tTG9naWNPcHRpb25zLmNhbGxiYWNrPy5pbmRleE9mKCd7JykgPCAwXHJcbiAgKSB7XHJcbiAgICAvLyBUaGUgYWxsb3dGaWxlUmVzb3VyY2VzIGlzIGFsd2F5cyBzZXQgdG8gZmFsc2UgZm9yIEhUVFAgcmVxdWVzdHMgdG8gYXZvaWRcclxuICAgIC8vIGluamVjdGluZyBhcmJpdHJhcnkgZmlsZXMgZnJvbSB0aGUgZnNcclxuICAgIGlmIChjdXN0b21Mb2dpY09wdGlvbnMuYWxsb3dGaWxlUmVzb3VyY2VzKSB7XHJcbiAgICAgIHRyeSB7XHJcbiAgICAgICAgY3VzdG9tTG9naWNPcHRpb25zLmNhbGxiYWNrID0gcmVhZEZpbGVTeW5jKFxyXG4gICAgICAgICAgY3VzdG9tTG9naWNPcHRpb25zLmNhbGxiYWNrLFxyXG4gICAgICAgICAgJ3V0ZjgnXHJcbiAgICAgICAgKTtcclxuICAgICAgfSBjYXRjaCAoZXJyb3IpIHtcclxuICAgICAgICBjdXN0b21Mb2dpY09wdGlvbnMuY2FsbGJhY2sgPSBmYWxzZTtcclxuICAgICAgICBsb2dXaXRoU3RhY2soMiwgZXJyb3IsIGBbY2hhcnRdIFRoZSAnY2FsbGJhY2snIGNhbm5vdCBiZSBsb2FkZWQuYCk7XHJcbiAgICAgIH1cclxuICAgIH0gZWxzZSB7XHJcbiAgICAgIGN1c3RvbUxvZ2ljT3B0aW9ucy5jYWxsYmFjayA9IGZhbHNlO1xyXG4gICAgfVxyXG4gIH1cclxuXHJcbiAgLy8gU2l6ZSBzZWFyY2hcclxuICBvcHRpb25zLmV4cG9ydCA9IHtcclxuICAgIC4uLm9wdGlvbnMuZXhwb3J0LFxyXG4gICAgLi4uZmluZENoYXJ0U2l6ZShvcHRpb25zKVxyXG4gIH07XHJcblxyXG4gIC8vIFBvc3QgdGhlIHdvcmsgdG8gdGhlIHBvb2xcclxuICB0cnkge1xyXG4gICAgY29uc3QgcmVzdWx0ID0gYXdhaXQgcG9zdFdvcmsoXHJcbiAgICAgIGV4cG9ydE9wdGlvbnMuc3RySW5qIHx8IGNoYXJ0SnNvbiB8fCBzdmcsXHJcbiAgICAgIG9wdGlvbnNcclxuICAgICk7XHJcbiAgICByZXR1cm4gZW5kQ2FsbGJhY2soZmFsc2UsIHJlc3VsdCk7XHJcbiAgfSBjYXRjaCAoZXJyb3IpIHtcclxuICAgIHJldHVybiBlbmRDYWxsYmFjayhlcnJvcik7XHJcbiAgfVxyXG59O1xyXG5cclxuLyoqXHJcbiAqIFBlcmZvcm1zIGEgZGlyZWN0IGluamVjdCBvZiBvcHRpb25zIGJlZm9yZSBleHBvcnQuIFRoZSBmdW5jdGlvbiBhdHRlbXB0c1xyXG4gKiB0byBzdHJpbmdpZnkgdGhlIHByb3ZpZGVkIG9wdGlvbnMgYW5kIHJlbW92ZXMgdW5uZWNlc3NhcnkgY2hhcmFjdGVycyxcclxuICogZW5zdXJpbmcgYSBjbGVhbiBhbmQgZm9ybWF0dGVkIGlucHV0LiBUaGUgcmVzdWx0aW5nIHN0cmluZyBpcyBzYXZlZCBhc1xyXG4gKiBhIFwic3RyaWdodCBpbmplY3RcIiBzdHJpbmcgaW4gdGhlIGV4cG9ydCBvcHRpb25zLiBJdCB0aGVuIGludm9rZXMgdGhlXHJcbiAqIGRvRXhwb3J0IGZ1bmN0aW9uIHdpdGggdGhlIHVwZGF0ZWQgb3B0aW9ucy5cclxuICpcclxuICogSU1QT1JUQU5UOiBEYW5nZXJvdXMgYW5kIG11c3QgYmUgdXNlZCBkZWxpYmVyYXRlbHkgYnkgc29tZW9uZSB3aG8gc2V0cyB1cFxyXG4gKiBhIHNlcnZlciAoc2VlIHRoZSAgLS1hbGxvd0NvZGVFeGVjdXRpb24gb3B0aW9uKS5cclxuICpcclxuICogQHBhcmFtIHtPYmplY3R9IG9wdGlvbnMgLSBUaGUgZXhwb3J0IG9wdGlvbnMgY29udGFpbmluZyB0aGUgaW5wdXRcclxuICogdG8gYmUgaW5qZWN0ZWQuXHJcbiAqIEBwYXJhbSB7ZnVuY3Rpb259IGVuZENhbGxiYWNrIC0gVGhlIGNhbGxiYWNrIGZ1bmN0aW9uIHRvIGJlIGludm9rZWRcclxuICogYXQgdGhlIGVuZCBvZiB0aGUgcHJvY2Vzcy5cclxuICpcclxuICogQHJldHVybnMge1Byb21pc2V9IEEgUHJvbWlzZSB0aGF0IHJlc29sdmVzIHdpdGggdGhlIHJlc3VsdCBvZiB0aGUgZXhwb3J0XHJcbiAqIG9wZXJhdGlvbiBvciByZWplY3RzIHdpdGggYW4gZXJyb3IgaWYgYW55IGlzc3VlcyBvY2N1ciBkdXJpbmcgdGhlIHByb2Nlc3MuXHJcbiAqL1xyXG5jb25zdCBkb1N0cmFpZ2h0SW5qZWN0ID0gKG9wdGlvbnMsIGVuZENhbGxiYWNrKSA9PiB7XHJcbiAgdHJ5IHtcclxuICAgIGxldCBzdHJJbmo7XHJcbiAgICBsZXQgaW5zdHIgPSBvcHRpb25zLmV4cG9ydC5pbnN0ciB8fCBvcHRpb25zLmV4cG9ydC5vcHRpb25zO1xyXG5cclxuICAgIGlmICh0eXBlb2YgaW5zdHIgIT09ICdzdHJpbmcnKSB7XHJcbiAgICAgIC8vIFRyeSB0byBzdHJpbmdpZnkgb3B0aW9uc1xyXG4gICAgICBzdHJJbmogPSBpbnN0ciA9IG9wdGlvbnNTdHJpbmdpZnkoXHJcbiAgICAgICAgaW5zdHIsXHJcbiAgICAgICAgb3B0aW9ucy5jdXN0b21Mb2dpYz8uYWxsb3dDb2RlRXhlY3V0aW9uXHJcbiAgICAgICk7XHJcbiAgICB9XHJcbiAgICBzdHJJbmogPSBpbnN0ci5yZXBsYWNlQWxsKC9cXHR8XFxufFxcci9nLCAnJykudHJpbSgpO1xyXG5cclxuICAgIC8vIEdldCByaWQgb2YgdGhlIDtcclxuICAgIGlmIChzdHJJbmpbc3RySW5qLmxlbmd0aCAtIDFdID09PSAnOycpIHtcclxuICAgICAgc3RySW5qID0gc3RySW5qLnN1YnN0cmluZygwLCBzdHJJbmoubGVuZ3RoIC0gMSk7XHJcbiAgICB9XHJcblxyXG4gICAgLy8gU2F2ZSBhcyBzdHJpZ2h0IGluamVjdCBzdHJpbmdcclxuICAgIG9wdGlvbnMuZXhwb3J0LnN0ckluaiA9IHN0ckluajtcclxuICAgIHJldHVybiBkb0V4cG9ydChvcHRpb25zLCBmYWxzZSwgZW5kQ2FsbGJhY2spO1xyXG4gIH0gY2F0Y2ggKGVycm9yKSB7XHJcbiAgICByZXR1cm4gZW5kQ2FsbGJhY2soXHJcbiAgICAgIG5ldyBFeHBvcnRFcnJvcihcclxuICAgICAgICBgW2NoYXJ0XSBNYWxmb3JtZWQgaW5wdXQgZGV0ZWN0ZWQgZm9yICR7b3B0aW9ucy5leHBvcnQ/LnJlcXVlc3RJZCB8fCAnPyd9LiBQbGVhc2UgbWFrZSBzdXJlIHRoYXQgeW91ciBKU09OL0phdmFTY3JpcHQgb3B0aW9ucyBhcmUgc2VudCB1c2luZyB0aGUgXCJvcHRpb25zXCIgYXR0cmlidXRlLCBhbmQgdGhhdCBpZiB5b3UncmUgdXNpbmcgU1ZHLCBpdCBpcyB1bmVzY2FwZWQuYFxyXG4gICAgICApLnNldEVycm9yKGVycm9yKVxyXG4gICAgKTtcclxuICB9XHJcbn07XHJcblxyXG4vKipcclxuICogRXhwb3J0cyBhIHN0cmluZyBiYXNlZCBvbiB0aGUgcHJvdmlkZWQgb3B0aW9ucyBhbmQgaW52b2tlcyBhbiBlbmQgY2FsbGJhY2suXHJcbiAqXHJcbiAqIEBwYXJhbSB7c3RyaW5nfSBzdHJpbmdUb0V4cG9ydCAtIFRoZSBzdHJpbmcgY29udGVudCB0byBiZSBleHBvcnRlZC5cclxuICogQHBhcmFtIHtPYmplY3R9IG9wdGlvbnMgLSBFeHBvcnQgb3B0aW9ucywgaW5jbHVkaW5nIGN1c3RvbUxvZ2ljIHdpdGhcclxuICogYWxsb3dDb2RlRXhlY3V0aW9uIGZsYWcuXHJcbiAqIEBwYXJhbSB7RnVuY3Rpb259IGVuZENhbGxiYWNrIC0gQ2FsbGJhY2sgZnVuY3Rpb24gdG8gYmUgaW52b2tlZCBhdCB0aGUgZW5kXHJcbiAqIG9mIHRoZSBleHBvcnQgcHJvY2Vzcy5cclxuICpcclxuICogQHJldHVybnMge2FueX0gUmVzdWx0IG9mIHRoZSBleHBvcnQgcHJvY2VzcyBvciBhbiBlcnJvciBpZiBlbmNvdW50ZXJlZC5cclxuICovXHJcbmNvbnN0IGV4cG9ydEFzU3RyaW5nID0gKHN0cmluZ1RvRXhwb3J0LCBvcHRpb25zLCBlbmRDYWxsYmFjaykgPT4ge1xyXG4gIGNvbnN0IHsgYWxsb3dDb2RlRXhlY3V0aW9uIH0gPSBvcHRpb25zLmN1c3RvbUxvZ2ljO1xyXG5cclxuICAvLyBDaGVjayBpZiBpdCBpcyBTVkdcclxuICBpZiAoXHJcbiAgICBzdHJpbmdUb0V4cG9ydC5pbmRleE9mKCc8c3ZnJykgPj0gMCB8fFxyXG4gICAgc3RyaW5nVG9FeHBvcnQuaW5kZXhPZignPD94bWwnKSA+PSAwXHJcbiAgKSB7XHJcbiAgICBsb2coNCwgJ1tjaGFydF0gUGFyc2luZyBpbnB1dCBhcyBTVkcuJyk7XHJcbiAgICByZXR1cm4gZG9FeHBvcnQob3B0aW9ucywgZmFsc2UsIGVuZENhbGxiYWNrLCBzdHJpbmdUb0V4cG9ydCk7XHJcbiAgfVxyXG5cclxuICB0cnkge1xyXG4gICAgLy8gVHJ5IHRvIHBhcnNlIHRvIEpTT04gYW5kIGNhbGwgdGhlIGRvRXhwb3J0IGZ1bmN0aW9uXHJcbiAgICBjb25zdCBjaGFydEpTT04gPSBKU09OLnBhcnNlKHN0cmluZ1RvRXhwb3J0LnJlcGxhY2VBbGwoL1xcdHxcXG58XFxyL2csICcgJykpO1xyXG5cclxuICAgIC8vIElmIGEgY29ycmVjdCBKU09OLCBkbyB0aGUgZXhwb3J0XHJcbiAgICByZXR1cm4gZG9FeHBvcnQob3B0aW9ucywgY2hhcnRKU09OLCBlbmRDYWxsYmFjayk7XHJcbiAgfSBjYXRjaCAoZXJyb3IpIHtcclxuICAgIC8vIE5vdCBhIHZhbGlkIEpTT05cclxuICAgIGlmICh0b0Jvb2xlYW4oYWxsb3dDb2RlRXhlY3V0aW9uKSkge1xyXG4gICAgICByZXR1cm4gZG9TdHJhaWdodEluamVjdChvcHRpb25zLCBlbmRDYWxsYmFjayk7XHJcbiAgICB9IGVsc2Uge1xyXG4gICAgICAvLyBEbyBub3QgYWxsb3cgc3RyYWlnaHQgaW5qZWN0aW9uIHdpdGhvdXQgdGhlIGFsbG93Q29kZUV4ZWN1dGlvbiBmbGFnXHJcbiAgICAgIHJldHVybiBlbmRDYWxsYmFjayhcclxuICAgICAgICBuZXcgRXhwb3J0RXJyb3IoXHJcbiAgICAgICAgICAnW2NoYXJ0XSBPbmx5IEpTT04gY29uZmlndXJhdGlvbnMgYW5kIFNWRyBhcmUgYWxsb3dlZCBmb3IgdGhpcyBzZXJ2ZXIuIElmIHRoaXMgaXMgeW91ciBzZXJ2ZXIsIEphdmFTY3JpcHQgY3VzdG9tIGNvZGUgY2FuIGJlIGVuYWJsZWQgYnkgc3RhcnRpbmcgdGhlIHNlcnZlciB3aXRoIHRoZSAtLWFsbG93Q29kZUV4ZWN1dGlvbiBmbGFnLidcclxuICAgICAgICApLnNldEVycm9yKGVycm9yKVxyXG4gICAgICApO1xyXG4gICAgfVxyXG4gIH1cclxufTtcclxuXHJcbi8qKlxyXG4gKiBSZXRyaWV2ZXMgYW5kIHJldHVybnMgdGhlIGN1cnJlbnQgc3RhdHVzIG9mIGNvZGUgZXhlY3V0aW9uIHBlcm1pc3Npb24uXHJcbiAqXHJcbiAqIEByZXR1cm5zIHthbnl9IFRoZSB2YWx1ZSBvZiBhbGxvd0NvZGVFeGVjdXRpb24uXHJcbiAqL1xyXG5leHBvcnQgY29uc3QgZ2V0QWxsb3dDb2RlRXhlY3V0aW9uID0gKCkgPT4gYWxsb3dDb2RlRXhlY3V0aW9uO1xyXG5cclxuLyoqXHJcbiAqIFNldHMgdGhlIGNvZGUgZXhlY3V0aW9uIHBlcm1pc3Npb24gYmFzZWQgb24gdGhlIHByb3ZpZGVkIGJvb2xlYW4gdmFsdWUuXHJcbiAqXHJcbiAqIEBwYXJhbSB7YW55fSB2YWx1ZSAtIFRoZSB2YWx1ZSB0byBiZSBjb252ZXJ0ZWQgYW5kIGFzc2lnbmVkXHJcbiAqIHRvIGFsbG93Q29kZUV4ZWN1dGlvbi5cclxuICovXHJcbmV4cG9ydCBjb25zdCBzZXRBbGxvd0NvZGVFeGVjdXRpb24gPSAodmFsdWUpID0+IHtcclxuICBhbGxvd0NvZGVFeGVjdXRpb24gPSB0b0Jvb2xlYW4odmFsdWUpO1xyXG59O1xyXG5cclxuZXhwb3J0IGRlZmF1bHQge1xyXG4gIGJhdGNoRXhwb3J0LFxyXG4gIHNpbmdsZUV4cG9ydCxcclxuICBnZXRBbGxvd0NvZGVFeGVjdXRpb24sXHJcbiAgc2V0QWxsb3dDb2RlRXhlY3V0aW9uLFxyXG4gIHN0YXJ0RXhwb3J0LFxyXG4gIGZpbmRDaGFydFNpemVcclxufTtcclxuIiwiaW1wb3J0IHsgbG9nV2l0aFN0YWNrIH0gZnJvbSAnLi4vbG9nZ2VyLmpzJztcclxuaW1wb3J0IHsgZW52cyB9IGZyb20gJy4uL2VudnMuanMnO1xyXG5cclxuLyoqXHJcbiAqIE1pZGRsZXdhcmUgZm9yIGxvZ2dpbmcgZXJyb3JzIHdpdGggc3RhY2sgdHJhY2UgYW5kIGhhbmRsaW5nIGVycm9yIHJlc3BvbnNlLlxyXG4gKlxyXG4gKiBAcGFyYW0ge0Vycm9yfSBlcnJvciAtIFRoZSBlcnJvciBvYmplY3QuXHJcbiAqIEBwYXJhbSB7RXhwcmVzcy5SZXF1ZXN0fSByZXEgLSBUaGUgRXhwcmVzcyByZXF1ZXN0IG9iamVjdC5cclxuICogQHBhcmFtIHtFeHByZXNzLlJlc3BvbnNlfSByZXMgLSBUaGUgRXhwcmVzcyByZXNwb25zZSBvYmplY3QuXHJcbiAqIEBwYXJhbSB7RnVuY3Rpb259IG5leHQgLSBUaGUgbmV4dCBtaWRkbGV3YXJlIGZ1bmN0aW9uLlxyXG4gKi9cclxuY29uc3QgbG9nRXJyb3JNaWRkbGV3YXJlID0gKGVycm9yLCByZXEsIHJlcywgbmV4dCkgPT4ge1xyXG4gIC8vIERpc3BsYXkgdGhlIGVycm9yIHdpdGggc3RhY2sgaW4gYSBjb3JyZWN0IGZvcm1hdFxyXG4gIGxvZ1dpdGhTdGFjaygxLCBlcnJvcik7XHJcblxyXG4gIC8vIERlbGV0ZSB0aGUgc3RhY2sgZm9yIHRoZSBlbnZpcm9ubWVudCBvdGhlciB0aGFuIHRoZSBkZXZlbG9wbWVudFxyXG4gIGlmIChlbnZzLk5PREVfRU5WICE9PSAnZGV2ZWxvcG1lbnQnKSB7XHJcbiAgICBkZWxldGUgZXJyb3Iuc3RhY2s7XHJcbiAgfVxyXG5cclxuICAvLyBDYWxsIHRoZSByZXR1cm5FcnJvck1pZGRsZXdhcmVcclxuICBuZXh0KGVycm9yKTtcclxufTtcclxuXHJcbi8qKlxyXG4gKiBNaWRkbGV3YXJlIGZvciByZXR1cm5pbmcgZXJyb3IgcmVzcG9uc2UuXHJcbiAqXHJcbiAqIEBwYXJhbSB7RXJyb3J9IGVycm9yIC0gVGhlIGVycm9yIG9iamVjdC5cclxuICogQHBhcmFtIHtFeHByZXNzLlJlcXVlc3R9IHJlcSAtIFRoZSBFeHByZXNzIHJlcXVlc3Qgb2JqZWN0LlxyXG4gKiBAcGFyYW0ge0V4cHJlc3MuUmVzcG9uc2V9IHJlcyAtIFRoZSBFeHByZXNzIHJlc3BvbnNlIG9iamVjdC5cclxuICogQHBhcmFtIHtGdW5jdGlvbn0gbmV4dCAtIFRoZSBuZXh0IG1pZGRsZXdhcmUgZnVuY3Rpb24uXHJcbiAqL1xyXG5jb25zdCByZXR1cm5FcnJvck1pZGRsZXdhcmUgPSAoZXJyb3IsIHJlcSwgcmVzLCBuZXh0KSA9PiB7XHJcbiAgLy8gR2F0aGVyIGFsbCByZXF1aWVkIGluZm9ybWF0aW9uIGZvciB0aGUgcmVzcG9uc2VcclxuICBjb25zdCB7IHN0YXR1c0NvZGU6IHN0Q29kZSwgc3RhdHVzLCBtZXNzYWdlLCBzdGFjayB9ID0gZXJyb3I7XHJcbiAgY29uc3Qgc3RhdHVzQ29kZSA9IHN0Q29kZSB8fCBzdGF0dXMgfHwgNTAwO1xyXG5cclxuICAvLyBTZXQgYW5kIHJldHVybiByZXNwb25zZVxyXG4gIHJlcy5zdGF0dXMoc3RhdHVzQ29kZSkuanNvbih7IHN0YXR1c0NvZGUsIG1lc3NhZ2UsIHN0YWNrIH0pO1xyXG59O1xyXG5cclxuZXhwb3J0IGRlZmF1bHQgKGFwcCkgPT4ge1xyXG4gIC8vIEFkZCBsb2cgZXJyb3IgbWlkZGxld2FyZVxyXG4gIGFwcC51c2UobG9nRXJyb3JNaWRkbGV3YXJlKTtcclxuXHJcbiAgLy8gQWRkIHNldCBzdGF0dXMgYW5kIHJldHVybiBlcnJvciBtaWRkbGV3YXJlXHJcbiAgYXBwLnVzZShyZXR1cm5FcnJvck1pZGRsZXdhcmUpO1xyXG59O1xyXG4iLCIvKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKlxyXG5cclxuSGlnaGNoYXJ0cyBFeHBvcnQgU2VydmVyXHJcblxyXG5Db3B5cmlnaHQgKGMpIDIwMTYtMjAyNCwgSGlnaHNvZnRcclxuXHJcbkxpY2VuY2VkIHVuZGVyIHRoZSBNSVQgbGljZW5jZS5cclxuXHJcbkFkZGl0aW9uYWxseSBhIHZhbGlkIEhpZ2hjaGFydHMgbGljZW5zZSBpcyByZXF1aXJlZCBmb3IgdXNlLlxyXG5cclxuU2VlIExJQ0VOU0UgZmlsZSBpbiByb290IGZvciBkZXRhaWxzLlxyXG5cclxuKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKi9cclxuXHJcbmltcG9ydCByYXRlTGltaXQgZnJvbSAnZXhwcmVzcy1yYXRlLWxpbWl0JztcclxuXHJcbmltcG9ydCB7IGxvZyB9IGZyb20gJy4uL2xvZ2dlci5qcyc7XHJcblxyXG4vKipcclxuICogTWlkZGxld2FyZSBmb3IgZW5hYmxpbmcgcmF0ZSBsaW1pdGluZyBvbiB0aGUgc3BlY2lmaWVkIEV4cHJlc3MgYXBwLlxyXG4gKlxyXG4gKiBAcGFyYW0ge0V4cHJlc3N9IGFwcCAtIFRoZSBFeHByZXNzIGFwcCBpbnN0YW5jZS5cclxuICogQHBhcmFtIHtPYmplY3R9IGxpbWl0Q29uZmlnIC0gQ29uZmlndXJhdGlvbiBvcHRpb25zIGZvciByYXRlIGxpbWl0aW5nLlxyXG4gKi9cclxuZXhwb3J0IGRlZmF1bHQgKGFwcCwgbGltaXRDb25maWcpID0+IHtcclxuICBjb25zdCBtc2cgPVxyXG4gICAgJ1RvbyBtYW55IHJlcXVlc3RzLCB5b3UgaGF2ZSBiZWVuIHJhdGUgbGltaXRlZC4gUGxlYXNlIHRyeSBhZ2FpbiBsYXRlci4nO1xyXG5cclxuICAvLyBPcHRpb25zIGZvciB0aGUgcmF0ZSBsaW1pdGVyXHJcbiAgY29uc3QgcmF0ZU9wdGlvbnMgPSB7XHJcbiAgICBtYXg6IGxpbWl0Q29uZmlnLm1heFJlcXVlc3RzIHx8IDMwLFxyXG4gICAgd2luZG93OiBsaW1pdENvbmZpZy53aW5kb3cgfHwgMSxcclxuICAgIGRlbGF5OiBsaW1pdENvbmZpZy5kZWxheSB8fCAwLFxyXG4gICAgdHJ1c3RQcm94eTogbGltaXRDb25maWcudHJ1c3RQcm94eSB8fCBmYWxzZSxcclxuICAgIHNraXBLZXk6IGxpbWl0Q29uZmlnLnNraXBLZXkgfHwgZmFsc2UsXHJcbiAgICBza2lwVG9rZW46IGxpbWl0Q29uZmlnLnNraXBUb2tlbiB8fCBmYWxzZVxyXG4gIH07XHJcblxyXG4gIC8vIFNldCBpZiBiZWhpbmQgYSBwcm94eVxyXG4gIGlmIChyYXRlT3B0aW9ucy50cnVzdFByb3h5KSB7XHJcbiAgICBhcHAuZW5hYmxlKCd0cnVzdCBwcm94eScpO1xyXG4gIH1cclxuXHJcbiAgLy8gQ3JlYXRlIGEgbGltaXRlclxyXG4gIGNvbnN0IGxpbWl0ZXIgPSByYXRlTGltaXQoe1xyXG4gICAgd2luZG93TXM6IHJhdGVPcHRpb25zLndpbmRvdyAqIDYwICogMTAwMCxcclxuICAgIC8vIExpbWl0IGVhY2ggSVAgdG8gMTAwIHJlcXVlc3RzIHBlciB3aW5kb3dNc1xyXG4gICAgbWF4OiByYXRlT3B0aW9ucy5tYXgsXHJcbiAgICAvLyBEaXNhYmxlIGRlbGF5aW5nLCBmdWxsIHNwZWVkIHVudGlsIHRoZSBtYXggbGltaXQgaXMgcmVhY2hlZFxyXG4gICAgZGVsYXlNczogcmF0ZU9wdGlvbnMuZGVsYXksXHJcbiAgICBoYW5kbGVyOiAocmVxdWVzdCwgcmVzcG9uc2UpID0+IHtcclxuICAgICAgcmVzcG9uc2UuZm9ybWF0KHtcclxuICAgICAgICBqc29uOiAoKSA9PiB7XHJcbiAgICAgICAgICByZXNwb25zZS5zdGF0dXMoNDI5KS5zZW5kKHsgbWVzc2FnZTogbXNnIH0pO1xyXG4gICAgICAgIH0sXHJcbiAgICAgICAgZGVmYXVsdDogKCkgPT4ge1xyXG4gICAgICAgICAgcmVzcG9uc2Uuc3RhdHVzKDQyOSkuc2VuZChtc2cpO1xyXG4gICAgICAgIH1cclxuICAgICAgfSk7XHJcbiAgICB9LFxyXG4gICAgc2tpcDogKHJlcXVlc3QpID0+IHtcclxuICAgICAgLy8gQWxsb3cgYnlwYXNzaW5nIHRoZSBsaW1pdGVyIGlmIGEgdmFsaWQga2V5L3Rva2VuIGhhcyBiZWVuIHNlbnRcclxuICAgICAgaWYgKFxyXG4gICAgICAgIHJhdGVPcHRpb25zLnNraXBLZXkgIT09IGZhbHNlICYmXHJcbiAgICAgICAgcmF0ZU9wdGlvbnMuc2tpcFRva2VuICE9PSBmYWxzZSAmJlxyXG4gICAgICAgIHJlcXVlc3QucXVlcnkua2V5ID09PSByYXRlT3B0aW9ucy5za2lwS2V5ICYmXHJcbiAgICAgICAgcmVxdWVzdC5xdWVyeS5hY2Nlc3NfdG9rZW4gPT09IHJhdGVPcHRpb25zLnNraXBUb2tlblxyXG4gICAgICApIHtcclxuICAgICAgICBsb2coNCwgJ1tyYXRlIGxpbWl0aW5nXSBTa2lwcGluZyByYXRlIGxpbWl0ZXIuJyk7XHJcbiAgICAgICAgcmV0dXJuIHRydWU7XHJcbiAgICAgIH1cclxuICAgICAgcmV0dXJuIGZhbHNlO1xyXG4gICAgfVxyXG4gIH0pO1xyXG5cclxuICAvLyBVc2UgYSBsaW1pdGVyIGFzIGEgbWlkZGxld2FyZVxyXG4gIGFwcC51c2UobGltaXRlcik7XHJcblxyXG4gIGxvZyhcclxuICAgIDMsXHJcbiAgICBgW3JhdGUgbGltaXRpbmddIEVuYWJsZWQgcmF0ZSBsaW1pdGluZyB3aXRoICR7cmF0ZU9wdGlvbnMubWF4fSByZXF1ZXN0cyBwZXIgJHtyYXRlT3B0aW9ucy53aW5kb3d9IG1pbnV0ZSBmb3IgZWFjaCBJUCwgdHJ1c3RpbmcgcHJveHk6ICR7cmF0ZU9wdGlvbnMudHJ1c3RQcm94eX0uYFxyXG4gICk7XHJcbn07XHJcbiIsImltcG9ydCBFeHBvcnRFcnJvciBmcm9tICcuL0V4cG9ydEVycm9yLmpzJztcclxuXHJcbmNsYXNzIEh0dHBFcnJvciBleHRlbmRzIEV4cG9ydEVycm9yIHtcclxuICBjb25zdHJ1Y3RvcihtZXNzYWdlLCBzdGF0dXMpIHtcclxuICAgIHN1cGVyKG1lc3NhZ2UpO1xyXG4gICAgdGhpcy5zdGF0dXMgPSB0aGlzLnN0YXR1c0NvZGUgPSBzdGF0dXM7XHJcbiAgfVxyXG5cclxuICBzZXRTdGF0dXMoc3RhdHVzKSB7XHJcbiAgICB0aGlzLnN0YXR1cyA9IHN0YXR1cztcclxuICAgIHJldHVybiB0aGlzO1xyXG4gIH1cclxufVxyXG5cclxuZXhwb3J0IGRlZmF1bHQgSHR0cEVycm9yO1xyXG4iLCIvKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKlxyXG5cclxuSGlnaGNoYXJ0cyBFeHBvcnQgU2VydmVyXHJcblxyXG5Db3B5cmlnaHQgKGMpIDIwMTYtMjAyNCwgSGlnaHNvZnRcclxuXHJcbkxpY2VuY2VkIHVuZGVyIHRoZSBNSVQgbGljZW5jZS5cclxuXHJcbkFkZGl0aW9uYWxseSBhIHZhbGlkIEhpZ2hjaGFydHMgbGljZW5zZSBpcyByZXF1aXJlZCBmb3IgdXNlLlxyXG5cclxuU2VlIExJQ0VOU0UgZmlsZSBpbiByb290IGZvciBkZXRhaWxzLlxyXG5cclxuKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKi9cclxuXHJcbmltcG9ydCB7IHY0IGFzIHV1aWQgfSBmcm9tICd1dWlkJztcclxuXHJcbmltcG9ydCB7IGdldEFsbG93Q29kZUV4ZWN1dGlvbiwgc3RhcnRFeHBvcnQgfSBmcm9tICcuLi8uLi9jaGFydC5qcyc7XHJcbmltcG9ydCB7IGdldE9wdGlvbnMsIG1lcmdlQ29uZmlnT3B0aW9ucyB9IGZyb20gJy4uLy4uL2NvbmZpZy5qcyc7XHJcbmltcG9ydCB7IGxvZyB9IGZyb20gJy4uLy4uL2xvZ2dlci5qcyc7XHJcbmltcG9ydCB7XHJcbiAgZml4VHlwZSxcclxuICBpc0NvcnJlY3RKU09OLFxyXG4gIGlzT2JqZWN0RW1wdHksXHJcbiAgaXNQcml2YXRlUmFuZ2VVcmxGb3VuZCxcclxuICBvcHRpb25zU3RyaW5naWZ5LFxyXG4gIG1lYXN1cmVUaW1lXHJcbn0gZnJvbSAnLi4vLi4vdXRpbHMuanMnO1xyXG5cclxuaW1wb3J0IEh0dHBFcnJvciBmcm9tICcuLi8uLi9lcnJvcnMvSHR0cEVycm9yLmpzJztcclxuXHJcbi8vIFJldmVyc2VkIE1JTUUgdHlwZXNcclxuY29uc3QgcmV2ZXJzZWRNaW1lID0ge1xyXG4gIHBuZzogJ2ltYWdlL3BuZycsXHJcbiAganBlZzogJ2ltYWdlL2pwZWcnLFxyXG4gIGdpZjogJ2ltYWdlL2dpZicsXHJcbiAgcGRmOiAnYXBwbGljYXRpb24vcGRmJyxcclxuICBzdmc6ICdpbWFnZS9zdmcreG1sJ1xyXG59O1xyXG5cclxuLy8gVGhlIHJlcXVlc3RzIGNvdW50ZXJcclxubGV0IHJlcXVlc3RzQ291bnRlciA9IDA7XHJcblxyXG4vLyBUaGUgYXJyYXkgb2YgY2FsbGJhY2tzIHRvIGNhbGwgYmVmb3JlIGEgcmVxdWVzdFxyXG5jb25zdCBiZWZvcmVSZXF1ZXN0ID0gW107XHJcblxyXG4vLyBUaGUgYXJyYXkgb2YgY2FsbGJhY2tzIHRvIGNhbGwgYWZ0ZXIgYSByZXF1ZXN0XHJcbmNvbnN0IGFmdGVyUmVxdWVzdCA9IFtdO1xyXG5cclxuLyoqXHJcbiAqIEludm9rZXMgYW4gYXJyYXkgb2YgY2FsbGJhY2sgZnVuY3Rpb25zIHdpdGggc3BlY2lmaWVkIHBhcmFtZXRlcnMsIGFsbG93aW5nXHJcbiAqIGN1c3RvbWl6YXRpb24gb2YgcmVxdWVzdCBoYW5kbGluZy5cclxuICpcclxuICogQHBhcmFtIHtGdW5jdGlvbltdfSBjYWxsYmFja3MgLSBBbiBhcnJheSBvZiBjYWxsYmFjayBmdW5jdGlvbnNcclxuICogdG8gYmUgZXhlY3V0ZWQuXHJcbiAqIEBwYXJhbSB7RXhwcmVzcy5SZXF1ZXN0fSByZXF1ZXN0IC0gVGhlIEV4cHJlc3MgcmVxdWVzdCBvYmplY3QuXHJcbiAqIEBwYXJhbSB7RXhwcmVzcy5SZXNwb25zZX0gcmVzcG9uc2UgLSBUaGUgRXhwcmVzcyByZXNwb25zZSBvYmplY3QuXHJcbiAqIEBwYXJhbSB7T2JqZWN0fSBkYXRhIC0gQW4gb2JqZWN0IGNvbnRhaW5pbmcgcGFyYW1ldGVycyBsaWtlIGlkLCB1bmlxdWVJZCxcclxuICogdHlwZSwgYW5kIGJvZHkuXHJcbiAqXHJcbiAqIEByZXR1cm5zIHtib29sZWFufSAtIFJldHVybnMgYSBib29sZWFuIGluZGljYXRpbmcgdGhlIG92ZXJhbGwgcmVzdWx0XHJcbiAqIG9mIHRoZSBjYWxsYmFjayBpbnZvY2F0aW9ucy5cclxuICovXHJcbmNvbnN0IGRvQ2FsbGJhY2tzID0gKGNhbGxiYWNrcywgcmVxdWVzdCwgcmVzcG9uc2UsIGRhdGEpID0+IHtcclxuICBsZXQgcmVzdWx0ID0gdHJ1ZTtcclxuICBjb25zdCB7IGlkLCB1bmlxdWVJZCwgdHlwZSwgYm9keSB9ID0gZGF0YTtcclxuXHJcbiAgY2FsbGJhY2tzLnNvbWUoKGNhbGxiYWNrKSA9PiB7XHJcbiAgICBpZiAoY2FsbGJhY2spIHtcclxuICAgICAgbGV0IGNhbGxSZXNwb25zZSA9IGNhbGxiYWNrKHJlcXVlc3QsIHJlc3BvbnNlLCBpZCwgdW5pcXVlSWQsIHR5cGUsIGJvZHkpO1xyXG5cclxuICAgICAgaWYgKGNhbGxSZXNwb25zZSAhPT0gdW5kZWZpbmVkICYmIGNhbGxSZXNwb25zZSAhPT0gdHJ1ZSkge1xyXG4gICAgICAgIHJlc3VsdCA9IGNhbGxSZXNwb25zZTtcclxuICAgICAgfVxyXG5cclxuICAgICAgcmV0dXJuIHRydWU7XHJcbiAgICB9XHJcbiAgfSk7XHJcblxyXG4gIHJldHVybiByZXN1bHQ7XHJcbn07XHJcblxyXG4vKipcclxuICogSGFuZGxlcyB0aGUgZXhwb3J0IHJlcXVlc3RzIGZyb20gdGhlIGNsaWVudC5cclxuICpcclxuICogQHBhcmFtIHtFeHByZXNzLlJlcXVlc3R9IHJlcXVlc3QgLSBUaGUgRXhwcmVzcyByZXF1ZXN0IG9iamVjdC5cclxuICogQHBhcmFtIHtFeHByZXNzLlJlc3BvbnNlfSByZXNwb25zZSAtIFRoZSBFeHByZXNzIHJlc3BvbnNlIG9iamVjdC5cclxuICogQHBhcmFtIHtGdW5jdGlvbn0gbmV4dCAtIFRoZSBuZXh0IG1pZGRsZXdhcmUgZnVuY3Rpb24uXHJcbiAqXHJcbiAqIEByZXR1cm5zIHtQcm9taXNlPHZvaWQ+fSAtIEEgcHJvbWlzZSB0aGF0IHJlc29sdmVzIG9uY2UgdGhlIGV4cG9ydCBwcm9jZXNzXHJcbiAqIGlzIGNvbXBsZXRlLlxyXG4gKi9cclxuY29uc3QgZXhwb3J0SGFuZGxlciA9IGFzeW5jIChyZXF1ZXN0LCByZXNwb25zZSwgbmV4dCkgPT4ge1xyXG4gIHRyeSB7XHJcbiAgICAvLyBTdGFydCBjb3VudGluZyB0aW1lXHJcbiAgICBjb25zdCBzdG9wQ291bnRlciA9IG1lYXN1cmVUaW1lKCk7XHJcblxyXG4gICAgLy8gQ3JlYXRlIGEgdW5pcXVlIElEIGZvciBhIHJlcXVlc3RcclxuICAgIGNvbnN0IHVuaXF1ZUlkID0gdXVpZCgpLnJlcGxhY2UoLy0vZywgJycpO1xyXG5cclxuICAgIC8vIEdldCB0aGUgY3VycmVudCBzZXJ2ZXIncyBnZW5lcmFsIG9wdGlvbnNcclxuICAgIGNvbnN0IGRlZmF1bHRPcHRpb25zID0gZ2V0T3B0aW9ucygpO1xyXG5cclxuICAgIGNvbnN0IGJvZHkgPSByZXF1ZXN0LmJvZHk7XHJcbiAgICBjb25zdCBpZCA9ICsrcmVxdWVzdHNDb3VudGVyO1xyXG5cclxuICAgIGxldCB0eXBlID0gZml4VHlwZShib2R5LnR5cGUpO1xyXG5cclxuICAgIC8vIFRocm93ICdCYWQgUmVxdWVzdCcgaWYgdGhlcmUncyBubyBib2R5XHJcbiAgICBpZiAoIWJvZHkgfHwgaXNPYmplY3RFbXB0eShib2R5KSkge1xyXG4gICAgICB0aHJvdyBuZXcgSHR0cEVycm9yKFxyXG4gICAgICAgICdUaGUgcmVxdWVzdCBib2R5IGlzIHJlcXVpcmVkLiBQbGVhc2UgZW5zdXJlIHRoYXQgeW91ciBDb250ZW50LVR5cGUgaGVhZGVyIGlzIGNvcnJlY3QgKGFjY2VwdGVkIHR5cGVzIGFyZSBhcHBsaWNhdGlvbi9qc29uIGFuZCBtdWx0aXBhcnQvZm9ybS1kYXRhKS4nLFxyXG4gICAgICAgIDQwMFxyXG4gICAgICApO1xyXG4gICAgfVxyXG5cclxuICAgIC8vIEFsbCBvZiB0aGUgYmVsb3cgY2FuIGJlIHVzZWRcclxuICAgIGxldCBpbnN0ciA9IGlzQ29ycmVjdEpTT04oYm9keS5pbmZpbGUgfHwgYm9keS5vcHRpb25zIHx8IGJvZHkuZGF0YSk7XHJcblxyXG4gICAgLy8gVGhyb3cgJ0JhZCBSZXF1ZXN0JyBpZiB0aGVyZSdzIG5vIEpTT04gb3IgU1ZHIHRvIGV4cG9ydFxyXG4gICAgaWYgKCFpbnN0ciAmJiAhYm9keS5zdmcpIHtcclxuICAgICAgbG9nKFxyXG4gICAgICAgIDIsXHJcbiAgICAgICAgYFRoZSByZXF1ZXN0IHdpdGggSUQgJHt1bmlxdWVJZH0gZnJvbSAke1xyXG4gICAgICAgICAgcmVxdWVzdC5oZWFkZXJzWyd4LWZvcndhcmRlZC1mb3InXSB8fCByZXF1ZXN0LmNvbm5lY3Rpb24ucmVtb3RlQWRkcmVzc1xyXG4gICAgICAgIH0gd2FzIGluY29ycmVjdC4gUGF5bG9hZCByZWNlaXZlZDogJHtKU09OLnN0cmluZ2lmeShib2R5KX0uYFxyXG4gICAgICApO1xyXG5cclxuICAgICAgdGhyb3cgbmV3IEh0dHBFcnJvcihcclxuICAgICAgICBcIk5vIGNvcnJlY3QgY2hhcnQgZGF0YSBmb3VuZC4gRW5zdXJlIHRoYXQgeW91IGFyZSB1c2luZyBlaXRoZXIgYXBwbGljYXRpb24vanNvbiBvciBtdWx0aXBhcnQvZm9ybS1kYXRhIGhlYWRlcnMuIElmIHNlbmRpbmcgSlNPTiwgbWFrZSBzdXJlIHRoZSBjaGFydCBkYXRhIGlzIGluIHRoZSAnaW5maWxlJywgJ29wdGlvbnMnLCBvciAnZGF0YScgYXR0cmlidXRlLiBJZiBzZW5kaW5nIFNWRywgZW5zdXJlIGl0IGlzIGluIHRoZSAnc3ZnJyBhdHRyaWJ1dGUuXCIsXHJcbiAgICAgICAgNDAwXHJcbiAgICAgICk7XHJcbiAgICB9XHJcblxyXG4gICAgbGV0IGNhbGxSZXNwb25zZSA9IGZhbHNlO1xyXG5cclxuICAgIC8vIENhbGwgdGhlIGJlZm9yZSByZXF1ZXN0IGZ1bmN0aW9uc1xyXG4gICAgY2FsbFJlc3BvbnNlID0gZG9DYWxsYmFja3MoYmVmb3JlUmVxdWVzdCwgcmVxdWVzdCwgcmVzcG9uc2UsIHtcclxuICAgICAgaWQsXHJcbiAgICAgIHVuaXF1ZUlkLFxyXG4gICAgICB0eXBlLFxyXG4gICAgICBib2R5XHJcbiAgICB9KTtcclxuXHJcbiAgICAvLyBCbG9jayB0aGUgcmVxdWVzdCBpZiBvbmUgb2YgYSBjYWxsYmFja3MgZmFpbGVkXHJcbiAgICBpZiAoY2FsbFJlc3BvbnNlICE9PSB0cnVlKSB7XHJcbiAgICAgIHJldHVybiByZXNwb25zZS5zZW5kKGNhbGxSZXNwb25zZSk7XHJcbiAgICB9XHJcblxyXG4gICAgbGV0IGNvbm5lY3Rpb25BYm9ydGVkID0gZmFsc2U7XHJcblxyXG4gICAgLy8gSW4gY2FzZSB0aGUgY29ubmVjdGlvbiBpcyBjbG9zZWQsIGZvcmNlIHRvIGFib3J0IGZ1cnRoZXIgYWN0aW9uc1xyXG4gICAgcmVxdWVzdC5zb2NrZXQub24oJ2Nsb3NlJywgKCkgPT4ge1xyXG4gICAgICBjb25uZWN0aW9uQWJvcnRlZCA9IHRydWU7XHJcbiAgICB9KTtcclxuXHJcbiAgICBsb2coNCwgYFtleHBvcnRdIEdvdCBhbiBpbmNvbWluZyBIVFRQIHJlcXVlc3Qgd2l0aCBJRCAke3VuaXF1ZUlkfS5gKTtcclxuXHJcbiAgICBib2R5LmNvbnN0ciA9ICh0eXBlb2YgYm9keS5jb25zdHIgPT09ICdzdHJpbmcnICYmIGJvZHkuY29uc3RyKSB8fCAnY2hhcnQnO1xyXG5cclxuICAgIC8vIEdhdGhlciBhbmQgb3JnYW5pemUgb3B0aW9ucyBmcm9tIHRoZSBwYXlsb2FkXHJcbiAgICBjb25zdCByZXF1ZXN0T3B0aW9ucyA9IHtcclxuICAgICAgZXhwb3J0OiB7XHJcbiAgICAgICAgaW5zdHIsXHJcbiAgICAgICAgdHlwZSxcclxuICAgICAgICBjb25zdHI6IGJvZHkuY29uc3RyWzBdLnRvTG93ZXJDYXNlKCkgKyBib2R5LmNvbnN0ci5zdWJzdHIoMSksXHJcbiAgICAgICAgaGVpZ2h0OiBib2R5LmhlaWdodCxcclxuICAgICAgICB3aWR0aDogYm9keS53aWR0aCxcclxuICAgICAgICBzY2FsZTogYm9keS5zY2FsZSB8fCBkZWZhdWx0T3B0aW9ucy5leHBvcnQuc2NhbGUsXHJcbiAgICAgICAgZ2xvYmFsT3B0aW9uczogaXNDb3JyZWN0SlNPTihib2R5Lmdsb2JhbE9wdGlvbnMsIHRydWUpLFxyXG4gICAgICAgIHRoZW1lT3B0aW9uczogaXNDb3JyZWN0SlNPTihib2R5LnRoZW1lT3B0aW9ucywgdHJ1ZSlcclxuICAgICAgfSxcclxuICAgICAgY3VzdG9tTG9naWM6IHtcclxuICAgICAgICBhbGxvd0NvZGVFeGVjdXRpb246IGdldEFsbG93Q29kZUV4ZWN1dGlvbigpLFxyXG4gICAgICAgIGFsbG93RmlsZVJlc291cmNlczogZmFsc2UsXHJcbiAgICAgICAgcmVzb3VyY2VzOiBpc0NvcnJlY3RKU09OKGJvZHkucmVzb3VyY2VzLCB0cnVlKSxcclxuICAgICAgICBjYWxsYmFjazogYm9keS5jYWxsYmFjayxcclxuICAgICAgICBjdXN0b21Db2RlOiBib2R5LmN1c3RvbUNvZGVcclxuICAgICAgfVxyXG4gICAgfTtcclxuXHJcbiAgICBpZiAoaW5zdHIpIHtcclxuICAgICAgLy8gU3RyaW5naWZ5IEpTT04gd2l0aCBvcHRpb25zXHJcbiAgICAgIHJlcXVlc3RPcHRpb25zLmV4cG9ydC5pbnN0ciA9IG9wdGlvbnNTdHJpbmdpZnkoXHJcbiAgICAgICAgaW5zdHIsXHJcbiAgICAgICAgcmVxdWVzdE9wdGlvbnMuY3VzdG9tTG9naWMuYWxsb3dDb2RlRXhlY3V0aW9uXHJcbiAgICAgICk7XHJcbiAgICB9XHJcblxyXG4gICAgLy8gTWVyZ2UgdGhlIHJlcXVlc3Qgb3B0aW9ucyBpbnRvIGRlZmF1bHQgb25lc1xyXG4gICAgY29uc3Qgb3B0aW9ucyA9IG1lcmdlQ29uZmlnT3B0aW9ucyhkZWZhdWx0T3B0aW9ucywgcmVxdWVzdE9wdGlvbnMpO1xyXG5cclxuICAgIC8vIFNhdmUgdGhlIEpTT04gaWYgZXhpc3RzXHJcbiAgICBvcHRpb25zLmV4cG9ydC5vcHRpb25zID0gaW5zdHI7XHJcblxyXG4gICAgLy8gTGFzdGx5LCBhZGQgdGhlIHNlcnZlciBzcGVjaWZpYyBhcmd1bWVudHMgaW50byBvcHRpb25zIGFzIHBheWxvYWRcclxuICAgIG9wdGlvbnMucGF5bG9hZCA9IHtcclxuICAgICAgc3ZnOiBib2R5LnN2ZyB8fCBmYWxzZSxcclxuICAgICAgYjY0OiBib2R5LmI2NCB8fCBmYWxzZSxcclxuICAgICAgbm9Eb3dubG9hZDogYm9keS5ub0Rvd25sb2FkIHx8IGZhbHNlLFxyXG4gICAgICByZXF1ZXN0SWQ6IHVuaXF1ZUlkXHJcbiAgICB9O1xyXG5cclxuICAgIC8vIFRlc3QgeGxpbms6aHJlZiBlbGVtZW50cyBmcm9tIHBheWxvYWQncyBTVkdcclxuICAgIGlmIChib2R5LnN2ZyAmJiBpc1ByaXZhdGVSYW5nZVVybEZvdW5kKG9wdGlvbnMucGF5bG9hZC5zdmcpKSB7XHJcbiAgICAgIHRocm93IG5ldyBIdHRwRXJyb3IoXHJcbiAgICAgICAgJ1NWRyBwb3RlbnRpYWxseSBjb250YWluIGF0IGxlYXN0IG9uZSBmb3JiaWRkZW4gVVJMIGluIHhsaW5rOmhyZWYgZWxlbWVudC4gUGxlYXNlIHJldmlldyB0aGUgU1ZHIGNvbnRlbnQgYW5kIGVuc3VyZSB0aGF0IGFsbCByZWZlcmVuY2VkIFVSTHMgY29tcGx5IHdpdGggc2VjdXJpdHkgcG9saWNpZXMuJyxcclxuICAgICAgICA0MDBcclxuICAgICAgKTtcclxuICAgIH1cclxuXHJcbiAgICAvLyBTdGFydCB0aGUgZXhwb3J0IHByb2Nlc3NcclxuICAgIGF3YWl0IHN0YXJ0RXhwb3J0KG9wdGlvbnMsIChlcnJvciwgaW5mbykgPT4ge1xyXG4gICAgICAvLyBSZW1vdmUgdGhlIGNsb3NlIGV2ZW50IGZyb20gdGhlIHNvY2tldFxyXG4gICAgICByZXF1ZXN0LnNvY2tldC5yZW1vdmVBbGxMaXN0ZW5lcnMoJ2Nsb3NlJyk7XHJcblxyXG4gICAgICAvLyBBZnRlciB0aGUgd2hvbGUgZXhwb3J0aW5nIHByb2Nlc3NcclxuICAgICAgaWYgKGRlZmF1bHRPcHRpb25zLnNlcnZlci5iZW5jaG1hcmtpbmcpIHtcclxuICAgICAgICBsb2coXHJcbiAgICAgICAgICA1LFxyXG4gICAgICAgICAgYFtiZW5jaG1hcmtdIFJlcXVlc3Qgd2l0aCBJRCAke3VuaXF1ZUlkfSAtIEFmdGVyIHRoZSB3aG9sZSBleHBvcnRpbmcgcHJvY2VzczogJHtzdG9wQ291bnRlcigpfW1zLmBcclxuICAgICAgICApO1xyXG4gICAgICB9XHJcblxyXG4gICAgICAvLyBJZiB0aGUgY29ubmVjdGlvbiB3YXMgY2xvc2VkLCBkbyBub3RoaW5nXHJcbiAgICAgIGlmIChjb25uZWN0aW9uQWJvcnRlZCkge1xyXG4gICAgICAgIHJldHVybiBsb2coXHJcbiAgICAgICAgICAzLFxyXG4gICAgICAgICAgYFtleHBvcnRdIFRoZSBjbGllbnQgY2xvc2VkIHRoZSBjb25uZWN0aW9uIGJlZm9yZSB0aGUgY2hhcnQgZmluaXNoZWQgcHJvY2Vzc2luZy5gXHJcbiAgICAgICAgKTtcclxuICAgICAgfVxyXG5cclxuICAgICAgLy8gSWYgZXJyb3IsIGxvZyBpdCBhbmQgc2VuZCBpdCB0byB0aGUgZXJyb3IgbWlkZGxld2FyZVxyXG4gICAgICBpZiAoZXJyb3IpIHtcclxuICAgICAgICB0aHJvdyBlcnJvcjtcclxuICAgICAgfVxyXG5cclxuICAgICAgLy8gSWYgZGF0YSBpcyBtaXNzaW5nLCBsb2cgdGhlIG1lc3NhZ2UgYW5kIHNlbmQgaXQgdG8gdGhlIGVycm9yIG1pZGRsZXdhcmVcclxuICAgICAgaWYgKCFpbmZvIHx8ICFpbmZvLnJlc3VsdCkge1xyXG4gICAgICAgIHRocm93IG5ldyBIdHRwRXJyb3IoXHJcbiAgICAgICAgICBgVW5leHBlY3RlZCByZXR1cm4gZnJvbSBjaGFydCBnZW5lcmF0aW9uLiBQbGVhc2UgY2hlY2sgeW91ciByZXF1ZXN0IGRhdGEuIEZvciB0aGUgcmVxdWVzdCB3aXRoIElEICR7dW5pcXVlSWR9LCB0aGUgcmVzdWx0IGlzICR7aW5mby5yZXN1bHR9LmAsXHJcbiAgICAgICAgICA0MDBcclxuICAgICAgICApO1xyXG4gICAgICB9XHJcblxyXG4gICAgICAvLyBHZXQgdGhlIHR5cGUgZnJvbSBvcHRpb25zXHJcbiAgICAgIHR5cGUgPSBpbmZvLm9wdGlvbnMuZXhwb3J0LnR5cGU7XHJcblxyXG4gICAgICAvLyBUaGUgYWZ0ZXIgcmVxdWVzdCBjYWxsYmFja3NcclxuICAgICAgZG9DYWxsYmFja3MoYWZ0ZXJSZXF1ZXN0LCByZXF1ZXN0LCByZXNwb25zZSwgeyBpZCwgYm9keTogaW5mby5yZXN1bHQgfSk7XHJcblxyXG4gICAgICBpZiAoaW5mby5yZXN1bHQpIHtcclxuICAgICAgICAvLyBJZiBvbmx5IGJhc2U2NCBpcyByZXF1aXJlZCwgcmV0dXJuIGl0XHJcbiAgICAgICAgaWYgKGJvZHkuYjY0KSB7XHJcbiAgICAgICAgICAvLyBTVkcgRXhjZXB0aW9uIGZvciB0aGUgSGlnaGNoYXJ0cyAxMS4zLjAgdmVyc2lvblxyXG4gICAgICAgICAgaWYgKHR5cGUgPT09ICdwZGYnIHx8IHR5cGUgPT0gJ3N2ZycpIHtcclxuICAgICAgICAgICAgcmV0dXJuIHJlc3BvbnNlLnNlbmQoXHJcbiAgICAgICAgICAgICAgQnVmZmVyLmZyb20oaW5mby5yZXN1bHQsICd1dGY4JykudG9TdHJpbmcoJ2Jhc2U2NCcpXHJcbiAgICAgICAgICAgICk7XHJcbiAgICAgICAgICB9XHJcblxyXG4gICAgICAgICAgcmV0dXJuIHJlc3BvbnNlLnNlbmQoaW5mby5yZXN1bHQpO1xyXG4gICAgICAgIH1cclxuXHJcbiAgICAgICAgLy8gU2V0IGNvcnJlY3QgY29udGVudCB0eXBlXHJcbiAgICAgICAgcmVzcG9uc2UuaGVhZGVyKCdDb250ZW50LVR5cGUnLCByZXZlcnNlZE1pbWVbdHlwZV0gfHwgJ2ltYWdlL3BuZycpO1xyXG5cclxuICAgICAgICAvLyBEZWNpZGUgd2hldGhlciB0byBkb3dubG9hZCBvciBub3QgY2hhcnQgZmlsZVxyXG4gICAgICAgIGlmICghYm9keS5ub0Rvd25sb2FkKSB7XHJcbiAgICAgICAgICByZXNwb25zZS5hdHRhY2htZW50KFxyXG4gICAgICAgICAgICBgJHtyZXF1ZXN0LnBhcmFtcy5maWxlbmFtZSB8fCByZXF1ZXN0LmJvZHkuZmlsZW5hbWUgfHwgJ2NoYXJ0J30uJHtcclxuICAgICAgICAgICAgICB0eXBlIHx8ICdwbmcnXHJcbiAgICAgICAgICAgIH1gXHJcbiAgICAgICAgICApO1xyXG4gICAgICAgIH1cclxuXHJcbiAgICAgICAgLy8gSWYgU1ZHLCByZXR1cm4gcGxhaW4gY29udGVudFxyXG4gICAgICAgIHJldHVybiB0eXBlID09PSAnc3ZnJ1xyXG4gICAgICAgICAgPyByZXNwb25zZS5zZW5kKGluZm8ucmVzdWx0KVxyXG4gICAgICAgICAgOiByZXNwb25zZS5zZW5kKEJ1ZmZlci5mcm9tKGluZm8ucmVzdWx0LCAnYmFzZTY0JykpO1xyXG4gICAgICB9XHJcbiAgICB9KTtcclxuICB9IGNhdGNoIChlcnJvcikge1xyXG4gICAgbmV4dChlcnJvcik7XHJcbiAgfVxyXG59O1xyXG5cclxuZXhwb3J0IGRlZmF1bHQgKGFwcCkgPT4ge1xyXG4gIC8qKlxyXG4gICAqIEFkZHMgdGhlIFBPU1QgLyBhIHJvdXRlIGZvciBoYW5kbGluZyBQT1NUIHJlcXVlc3RzIGF0IHRoZSByb290IGVuZHBvaW50LlxyXG4gICAqL1xyXG4gIGFwcC5wb3N0KCcvJywgZXhwb3J0SGFuZGxlcik7XHJcblxyXG4gIC8qKlxyXG4gICAqIEFkZHMgdGhlIFBPU1QgLzpmaWxlbmFtZSBhIHJvdXRlIGZvciBoYW5kbGluZyBQT1NUIHJlcXVlc3RzIHdpdGhcclxuICAgKiBhIHNwZWNpZmllZCBmaWxlbmFtZSBwYXJhbWV0ZXIuXHJcbiAgICovXHJcbiAgYXBwLnBvc3QoJy86ZmlsZW5hbWUnLCBleHBvcnRIYW5kbGVyKTtcclxufTtcclxuIiwiLyoqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKipcclxuXHJcbkhpZ2hjaGFydHMgRXhwb3J0IFNlcnZlclxyXG5cclxuQ29weXJpZ2h0IChjKSAyMDE2LTIwMjQsIEhpZ2hzb2Z0XHJcblxyXG5MaWNlbmNlZCB1bmRlciB0aGUgTUlUIGxpY2VuY2UuXHJcblxyXG5BZGRpdGlvbmFsbHkgYSB2YWxpZCBIaWdoY2hhcnRzIGxpY2Vuc2UgaXMgcmVxdWlyZWQgZm9yIHVzZS5cclxuXHJcblNlZSBMSUNFTlNFIGZpbGUgaW4gcm9vdCBmb3IgZGV0YWlscy5cclxuXHJcbioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKiovXHJcblxyXG5pbXBvcnQgeyByZWFkRmlsZVN5bmMgfSBmcm9tICdmcyc7XHJcbmltcG9ydCB7IGpvaW4gYXMgcGF0aGVyIH0gZnJvbSAncGF0aCc7XHJcbmltcG9ydCB7IGxvZyB9IGZyb20gJy4uLy4uL2xvZ2dlci5qcyc7XHJcblxyXG5pbXBvcnQgY2FjaGUgZnJvbSAnLi4vLi4vY2FjaGUuanMnO1xyXG5pbXBvcnQgcG9vbCBmcm9tICcuLi8uLi9wb29sLmpzJztcclxuaW1wb3J0IHsgX19kaXJuYW1lIH0gZnJvbSAnLi4vLi4vdXRpbHMuanMnO1xyXG5cclxuY29uc3QgcGtnRmlsZSA9IEpTT04ucGFyc2UocmVhZEZpbGVTeW5jKHBhdGhlcihfX2Rpcm5hbWUsICdwYWNrYWdlLmpzb24nKSkpO1xyXG5cclxuY29uc3Qgc2VydmVyU3RhcnRUaW1lID0gbmV3IERhdGUoKTtcclxuXHJcbmNvbnN0IHN1Y2Nlc3NSYXRlcyA9IFtdO1xyXG5jb25zdCByZWNvcmRJbnRlcnZhbCA9IDYwICogMTAwMDsgLy8gcmVjb3JkIGV2ZXJ5IG1pbnV0ZVxyXG5jb25zdCB3aW5kb3dTaXplID0gMzA7IC8vIDMwIG1pbnV0ZXNcclxuXHJcbmZ1bmN0aW9uIHJlY29yZFN1Y2Nlc3NSYXRlKCkge1xyXG4gIGNvbnN0IHN0YXRzID0gcG9vbC5nZXRTdGF0cygpO1xyXG4gIGNvbnN0IHN1Y2Nlc3NSYXRpbyA9XHJcbiAgICBzdGF0cy5leHBvcnRBdHRlbXB0cyA9PT0gMFxyXG4gICAgICA/IDFcclxuICAgICAgOiAoc3RhdHMucGVyZm9ybWVkRXhwb3J0cyAvIHN0YXRzLmV4cG9ydEF0dGVtcHRzKSAqIDEwMDtcclxuXHJcbiAgc3VjY2Vzc1JhdGVzLnB1c2goc3VjY2Vzc1JhdGlvKTtcclxuICBpZiAoc3VjY2Vzc1JhdGVzLmxlbmd0aCA+IHdpbmRvd1NpemUpIHtcclxuICAgIHN1Y2Nlc3NSYXRlcy5zaGlmdCgpO1xyXG4gIH1cclxufVxyXG5cclxuZnVuY3Rpb24gY2FsY3VsYXRlTW92aW5nQXZlcmFnZSgpIHtcclxuICBjb25zdCBzdW0gPSBzdWNjZXNzUmF0ZXMucmVkdWNlKChhLCBiKSA9PiBhICsgYiwgMCk7XHJcbiAgcmV0dXJuIHN1bSAvIHN1Y2Nlc3NSYXRlcy5sZW5ndGg7XHJcbn1cclxuXHJcbnNldEludGVydmFsKHJlY29yZFN1Y2Nlc3NSYXRlLCByZWNvcmRJbnRlcnZhbCk7XHJcblxyXG4vKipcclxuICogQWRkcyB0aGUgL2hlYWx0aCBhbmQgL3N1Y2Nlc3MtbW92aW5nLWF2ZXJhZ2Ugcm91dGVzXHJcbiAqIHdoaWNoIG91dHB1dCBiYXNpYyBzdGF0cyBmb3IgdGhlIHNlcnZlci5cclxuICovXHJcbmV4cG9ydCBkZWZhdWx0IGZ1bmN0aW9uIGFkZEhlYWx0aFJvdXRlcyhhcHApIHtcclxuICBpZiAoIWFwcCkge1xyXG4gICAgcmV0dXJuIGZhbHNlO1xyXG4gIH1cclxuXHJcbiAgYXBwLmdldCgnL2hlYWx0aCcsIChfLCByZXMpID0+IHtcclxuICAgIGNvbnN0IHN0YXRzID0gcG9vbC5nZXRTdGF0cygpO1xyXG4gICAgY29uc3QgcGVyaW9kID0gc3VjY2Vzc1JhdGVzLmxlbmd0aDtcclxuICAgIGNvbnN0IG1vdmluZ0F2ZXJhZ2UgPSBjYWxjdWxhdGVNb3ZpbmdBdmVyYWdlKCk7XHJcblxyXG4gICAgbG9nKDQsICdbaGVhbHRoLmpzXSBHRVQgL2hlYWx0aCBbMjAwXSAtIHJldHVybmluZyBzZXJ2ZXIgaGVhbHRoLicpO1xyXG5cclxuICAgIHJlcy5zZW5kKHtcclxuICAgICAgc3RhdHVzOiAnT0snLFxyXG4gICAgICBib290VGltZTogc2VydmVyU3RhcnRUaW1lLFxyXG4gICAgICB1cHRpbWU6XHJcbiAgICAgICAgTWF0aC5mbG9vcihcclxuICAgICAgICAgIChuZXcgRGF0ZSgpLmdldFRpbWUoKSAtIHNlcnZlclN0YXJ0VGltZS5nZXRUaW1lKCkpIC8gMTAwMCAvIDYwXHJcbiAgICAgICAgKSArICcgbWludXRlcycsXHJcbiAgICAgIHZlcnNpb246IHBrZ0ZpbGUudmVyc2lvbixcclxuICAgICAgaGlnaGNoYXJ0c1ZlcnNpb246IGNhY2hlLnZlcnNpb24oKSxcclxuICAgICAgYXZlcmFnZVByb2Nlc3NpbmdUaW1lOiBzdGF0cy5zcGVudEF2ZXJhZ2UsXHJcbiAgICAgIHBlcmZvcm1lZEV4cG9ydHM6IHN0YXRzLnBlcmZvcm1lZEV4cG9ydHMsXHJcbiAgICAgIGZhaWxlZEV4cG9ydHM6IHN0YXRzLmRyb3BwZWRFeHBvcnRzLFxyXG4gICAgICBleHBvcnRBdHRlbXB0czogc3RhdHMuZXhwb3J0QXR0ZW1wdHMsXHJcbiAgICAgIHN1Y2Vzc1JhdGlvOiAoc3RhdHMucGVyZm9ybWVkRXhwb3J0cyAvIHN0YXRzLmV4cG9ydEF0dGVtcHRzKSAqIDEwMCxcclxuICAgICAgLy8gZXNsaW50LWRpc2FibGUtbmV4dC1saW5lIGltcG9ydC9uby1uYW1lZC1hcy1kZWZhdWx0LW1lbWJlclxyXG4gICAgICBwb29sOiBwb29sLmdldFBvb2xJbmZvSlNPTigpLFxyXG5cclxuICAgICAgLy8gTW92aW5nIGF2ZXJhZ2VcclxuICAgICAgcGVyaW9kLFxyXG4gICAgICBtb3ZpbmdBdmVyYWdlLFxyXG4gICAgICBtZXNzYWdlOiBgTGFzdCAke3BlcmlvZH0gbWludXRlcyBoYWQgYSBzdWNjZXNzIHJhdGUgb2YgJHttb3ZpbmdBdmVyYWdlLnRvRml4ZWQoMil9JS5gLFxyXG5cclxuICAgICAgLy8gU1ZHL0pTT04gYXR0ZW1wdHNcclxuICAgICAgc3ZnRXhwb3J0QXR0ZW1wdHM6IHN0YXRzLmV4cG9ydEZyb21TdmdBdHRlbXB0cyxcclxuICAgICAganNvbkV4cG9ydEF0dGVtcHRzOiBzdGF0cy5wZXJmb3JtZWRFeHBvcnRzIC0gc3RhdHMuZXhwb3J0RnJvbVN2Z0F0dGVtcHRzXHJcbiAgICB9KTtcclxuICB9KTtcclxufVxyXG4iLCIvKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKlxyXG5cclxuSGlnaGNoYXJ0cyBFeHBvcnQgU2VydmVyXHJcblxyXG5Db3B5cmlnaHQgKGMpIDIwMTYtMjAyNCwgSGlnaHNvZnRcclxuXHJcbkxpY2VuY2VkIHVuZGVyIHRoZSBNSVQgbGljZW5jZS5cclxuXHJcbkFkZGl0aW9uYWxseSBhIHZhbGlkIEhpZ2hjaGFydHMgbGljZW5zZSBpcyByZXF1aXJlZCBmb3IgdXNlLlxyXG5cclxuU2VlIExJQ0VOU0UgZmlsZSBpbiByb290IGZvciBkZXRhaWxzLlxyXG5cclxuKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKi9cclxuXHJcbmltcG9ydCB7IHByb21pc2VzIGFzIGZzUHJvbWlzZXMgfSBmcm9tICdmcyc7XHJcbmltcG9ydCB7IHBvc2l4IH0gZnJvbSAncGF0aCc7XHJcblxyXG5pbXBvcnQgY29ycyBmcm9tICdjb3JzJztcclxuaW1wb3J0IGV4cHJlc3MgZnJvbSAnZXhwcmVzcyc7XHJcbmltcG9ydCBodHRwIGZyb20gJ2h0dHAnO1xyXG5pbXBvcnQgaHR0cHMgZnJvbSAnaHR0cHMnO1xyXG5pbXBvcnQgbXVsdGVyIGZyb20gJ211bHRlcic7XHJcblxyXG5pbXBvcnQgZXJyb3JIYW5kbGVyIGZyb20gJy4vZXJyb3IuanMnO1xyXG5pbXBvcnQgeyBsb2csIGxvZ1dpdGhTdGFjayB9IGZyb20gJy4uL2xvZ2dlci5qcyc7XHJcbmltcG9ydCByYXRlTGltaXQgZnJvbSAnLi9yYXRlX2xpbWl0LmpzJztcclxuaW1wb3J0IHsgX19kaXJuYW1lIH0gZnJvbSAnLi4vdXRpbHMuanMnO1xyXG5cclxuaW1wb3J0IHZTd2l0Y2hSb3V0ZSBmcm9tICcuL3JvdXRlcy9jaGFuZ2VfaGNfdmVyc2lvbi5qcyc7XHJcbmltcG9ydCBleHBvcnRSb3V0ZXMgZnJvbSAnLi9yb3V0ZXMvZXhwb3J0LmpzJztcclxuaW1wb3J0IGhlYWx0aFJvdXRlIGZyb20gJy4vcm91dGVzL2hlYWx0aC5qcyc7XHJcbmltcG9ydCB1aVJvdXRlIGZyb20gJy4vcm91dGVzL3VpLmpzJztcclxuXHJcbmltcG9ydCBFeHBvcnRFcnJvciBmcm9tICcuLi9lcnJvcnMvRXhwb3J0RXJyb3IuanMnO1xyXG5cclxuLy8gQ3JlYXRlIGV4cHJlc3MgYXBwXHJcbmNvbnN0IGFwcCA9IGV4cHJlc3MoKTtcclxuXHJcbi8vIERpc2FibGUgdGhlIFgtUG93ZXJlZC1CeSBoZWFkZXJcclxuYXBwLmRpc2FibGUoJ3gtcG93ZXJlZC1ieScpO1xyXG5cclxuLy8gRW5hYmxlIENPUlMgc3VwcG9ydFxyXG5hcHAudXNlKGNvcnMoKSk7XHJcblxyXG4vLyBFbmFibGUgcGFyc2luZyBvZiBmb3JtIGRhdGEgKGZpbGVzKSB3aXRoIE11bHRlciBwYWNrYWdlXHJcbmNvbnN0IHN0b3JhZ2UgPSBtdWx0ZXIubWVtb3J5U3RvcmFnZSgpO1xyXG5jb25zdCB1cGxvYWQgPSBtdWx0ZXIoe1xyXG4gIHN0b3JhZ2UsXHJcbiAgbGltaXRzOiB7XHJcbiAgICBmaWVsZFNpemU6IDUwICogMTAyNCAqIDEwMjRcclxuICB9XHJcbn0pO1xyXG5cclxuLy8gRW5hYmxlIGJvZHkgcGFyc2VyXHJcbmFwcC51c2UoZXhwcmVzcy5qc29uKHsgbGltaXQ6IDUwICogMTAyNCAqIDEwMjQgfSkpO1xyXG5hcHAudXNlKGV4cHJlc3MudXJsZW5jb2RlZCh7IGV4dGVuZGVkOiB0cnVlLCBsaW1pdDogNTAgKiAxMDI0ICogMTAyNCB9KSk7XHJcblxyXG4vLyBVc2Ugb25seSBub24tZmlsZSBtdWx0aXBhcnQgZm9ybSBmaWVsZHNcclxuYXBwLnVzZSh1cGxvYWQubm9uZSgpKTtcclxuXHJcbi8qKlxyXG4gKiBBdHRhY2ggZXJyb3IgaGFuZGxlcnMgdG8gdGhlIHNlcnZlci5cclxuICpcclxuICogQHBhcmFtIHtodHRwLlNlcnZlcn0gc2VydmVyIC0gVGhlIEhUVFAvSFRUUFMgc2VydmVyIGluc3RhbmNlLlxyXG4gKi9cclxuY29uc3QgYXR0YWNoRXJyb3JIYW5kbGVycyA9IChzZXJ2ZXIpID0+IHtcclxuICBzZXJ2ZXIub24oJ2NsaWVudEVycm9yJywgKGVycm9yKSA9PiB7XHJcbiAgICBsb2dXaXRoU3RhY2soMSwgZXJyb3IsIGBbc2VydmVyXSBDbGllbnQgZXJyb3I6ICR7ZXJyb3IubWVzc2FnZX1gKTtcclxuICB9KTtcclxuICBzZXJ2ZXIub24oJ2Vycm9yJywgKGVycm9yKSA9PiB7XHJcbiAgICBsb2dXaXRoU3RhY2soMSwgZXJyb3IsIGBbc2VydmVyXSBTZXJ2ZXIgZXJyb3I6ICR7ZXJyb3IubWVzc2FnZX1gKTtcclxuICB9KTtcclxuICBzZXJ2ZXIub24oJ2Nvbm5lY3Rpb24nLCAoc29ja2V0KSA9PiB7XHJcbiAgICBzb2NrZXQub24oJ2Vycm9yJywgKGVycm9yKSA9PiB7XHJcbiAgICAgIGxvZ1dpdGhTdGFjaygxLCBlcnJvciwgYFtzZXJ2ZXJdIFNvY2tldCBlcnJvcjogJHtlcnJvci5tZXNzYWdlfWApO1xyXG4gICAgfSk7XHJcbiAgfSk7XHJcbn07XHJcblxyXG4vKipcclxuICogU3RhcnRzIGFuIEhUVFAgc2VydmVyIGJhc2VkIG9uIHRoZSBwcm92aWRlZCBjb25maWd1cmF0aW9uLiBUaGUgYHNlcnZlckNvbmZpZ2BcclxuICogb2JqZWN0IGNvbnRhaW5zIGFsbCBzZXJ2ZXIgcmVsYXRlZCBwcm9wZXJ0aWVzIChzZWUgdGhlIGBzZXJ2ZXJgIHNlY3Rpb25cclxuICogaW4gdGhlIGBsaWIvc2NoZW1hcy9jb25maWcuanNgIGZpbGUgZm9yIGEgcmVmZXJlbmNlKS5cclxuICpcclxuICogQHBhcmFtIHtPYmplY3R9IHNlcnZlckNvbmZpZyAtIFRoZSBzZXJ2ZXIgY29uZmlndXJhdGlvbiBvYmplY3QuXHJcbiAqXHJcbiAqIEB0aHJvd3Mge0V4cG9ydEVycm9yfSAtIFRocm93cyBhbiBlcnJvciBpZiB0aGUgc2VydmVyIGNhbm5vdCBiZSBjb25maWd1cmVkXHJcbiAqIGFuZCBzdGFydGVkLlxyXG4gKi9cclxuZXhwb3J0IGNvbnN0IHN0YXJ0U2VydmVyID0gYXN5bmMgKHNlcnZlckNvbmZpZykgPT4ge1xyXG4gIHRyeSB7XHJcbiAgICAvLyBTdG9wIGlmIG5vdCBlbmFibGVkXHJcbiAgICBpZiAoIXNlcnZlckNvbmZpZy5lbmFibGUpIHtcclxuICAgICAgcmV0dXJuIGZhbHNlO1xyXG4gICAgfVxyXG5cclxuICAgIC8vIExpc3RlbiBIVFRQIHNlcnZlclxyXG4gICAgaWYgKCFzZXJ2ZXJDb25maWcuc3NsLmZvcmNlKSB7XHJcbiAgICAgIC8vIE1haW4gc2VydmVyIGluc3RhbmNlIChIVFRQKVxyXG4gICAgICBjb25zdCBodHRwU2VydmVyID0gaHR0cC5jcmVhdGVTZXJ2ZXIoYXBwKTtcclxuXHJcbiAgICAgIC8vIEF0dGFjaCBlcnJvciBoYW5kbGVycyBhbmQgbGlzdGVuIHRvIHRoZSBzZXJ2ZXJcclxuICAgICAgYXR0YWNoRXJyb3JIYW5kbGVycyhodHRwU2VydmVyKTtcclxuXHJcbiAgICAgIC8vIExpc3RlblxyXG4gICAgICBodHRwU2VydmVyLmxpc3RlbihzZXJ2ZXJDb25maWcucG9ydCwgc2VydmVyQ29uZmlnLmhvc3QpO1xyXG5cclxuICAgICAgbG9nKFxyXG4gICAgICAgIDMsXHJcbiAgICAgICAgYFtzZXJ2ZXJdIFN0YXJ0ZWQgSFRUUCBzZXJ2ZXIgb24gJHtzZXJ2ZXJDb25maWcuaG9zdH06JHtzZXJ2ZXJDb25maWcucG9ydH0uYFxyXG4gICAgICApO1xyXG4gICAgfVxyXG5cclxuICAgIC8vIExpc3RlbiBIVFRQUyBzZXJ2ZXJcclxuICAgIGlmIChzZXJ2ZXJDb25maWcuc3NsLmVuYWJsZSkge1xyXG4gICAgICAvLyBTZXQgdXAgYW4gU1NMIHNlcnZlciBhbHNvXHJcbiAgICAgIGxldCBrZXksIGNlcnQ7XHJcblxyXG4gICAgICB0cnkge1xyXG4gICAgICAgIC8vIEdldCB0aGUgU1NMIGtleVxyXG4gICAgICAgIGtleSA9IGF3YWl0IGZzUHJvbWlzZXMucmVhZEZpbGUoXHJcbiAgICAgICAgICBwb3NpeC5qb2luKHNlcnZlckNvbmZpZy5zc2wuY2VydFBhdGgsICdzZXJ2ZXIua2V5JyksXHJcbiAgICAgICAgICAndXRmOCdcclxuICAgICAgICApO1xyXG5cclxuICAgICAgICAvLyBHZXQgdGhlIFNTTCBjZXJ0aWZpY2F0ZVxyXG4gICAgICAgIGNlcnQgPSBhd2FpdCBmc1Byb21pc2VzLnJlYWRGaWxlKFxyXG4gICAgICAgICAgcG9zaXguam9pbihzZXJ2ZXJDb25maWcuc3NsLmNlcnRQYXRoLCAnc2VydmVyLmNydCcpLFxyXG4gICAgICAgICAgJ3V0ZjgnXHJcbiAgICAgICAgKTtcclxuICAgICAgfSBjYXRjaCAoZXJyb3IpIHtcclxuICAgICAgICBsb2coXHJcbiAgICAgICAgICAyLFxyXG4gICAgICAgICAgYFtzZXJ2ZXJdIFVuYWJsZSB0byBsb2FkIGtleS9jZXJ0aWZpY2F0ZSBmcm9tIHRoZSAnJHtzZXJ2ZXJDb25maWcuc3NsLmNlcnRQYXRofScgcGF0aC4gQ291bGQgbm90IHJ1biBzZWN1cmVkIGxheWVyIHNlcnZlci5gXHJcbiAgICAgICAgKTtcclxuICAgICAgfVxyXG5cclxuICAgICAgaWYgKGtleSAmJiBjZXJ0KSB7XHJcbiAgICAgICAgLy8gTWFpbiBzZXJ2ZXIgaW5zdGFuY2UgKEhUVFBTKVxyXG4gICAgICAgIGNvbnN0IGh0dHBzU2VydmVyID0gaHR0cHMuY3JlYXRlU2VydmVyKHsga2V5LCBjZXJ0IH0sIGFwcCk7XHJcblxyXG4gICAgICAgIC8vIEF0dGFjaCBlcnJvciBoYW5kbGVycyBhbmQgbGlzdGVuIHRvIHRoZSBzZXJ2ZXJcclxuICAgICAgICBhdHRhY2hFcnJvckhhbmRsZXJzKGh0dHBzU2VydmVyKTtcclxuXHJcbiAgICAgICAgLy8gTGlzdGVuXHJcbiAgICAgICAgaHR0cHNTZXJ2ZXIubGlzdGVuKHNlcnZlckNvbmZpZy5zc2wucG9ydCwgc2VydmVyQ29uZmlnLmhvc3QpO1xyXG5cclxuICAgICAgICBsb2coXHJcbiAgICAgICAgICAzLFxyXG4gICAgICAgICAgYFtzZXJ2ZXJdIFN0YXJ0ZWQgSFRUUFMgc2VydmVyIG9uICR7c2VydmVyQ29uZmlnLmhvc3R9OiR7c2VydmVyQ29uZmlnLnNzbC5wb3J0fS5gXHJcbiAgICAgICAgKTtcclxuICAgICAgfVxyXG4gICAgfVxyXG5cclxuICAgIC8vIEVuYWJsZSB0aGUgcmF0ZSBsaW1pdGVyIGlmIGNvbmZpZyBzYXlzIHNvXHJcbiAgICBpZiAoXHJcbiAgICAgIHNlcnZlckNvbmZpZy5yYXRlTGltaXRpbmcgJiZcclxuICAgICAgc2VydmVyQ29uZmlnLnJhdGVMaW1pdGluZy5lbmFibGUgJiZcclxuICAgICAgIVswLCBOYU5dLmluY2x1ZGVzKHNlcnZlckNvbmZpZy5yYXRlTGltaXRpbmcubWF4UmVxdWVzdHMpXHJcbiAgICApIHtcclxuICAgICAgcmF0ZUxpbWl0KGFwcCwgc2VydmVyQ29uZmlnLnJhdGVMaW1pdGluZyk7XHJcbiAgICB9XHJcblxyXG4gICAgLy8gU2V0IHVwIHN0YXRpYyBmb2xkZXIncyByb3V0ZVxyXG4gICAgYXBwLnVzZShleHByZXNzLnN0YXRpYyhwb3NpeC5qb2luKF9fZGlybmFtZSwgJ3B1YmxpYycpKSk7XHJcblxyXG4gICAgLy8gU2V0IHVwIHJvdXRlc1xyXG4gICAgaGVhbHRoUm91dGUoYXBwKTtcclxuICAgIGV4cG9ydFJvdXRlcyhhcHApO1xyXG4gICAgdWlSb3V0ZShhcHApO1xyXG4gICAgdlN3aXRjaFJvdXRlKGFwcCk7XHJcblxyXG4gICAgLy8gU2V0IHVwIGNlbnRyYWxpemVkIGVycm9yIGhhbmRsZXJcclxuICAgIGVycm9ySGFuZGxlcihhcHApO1xyXG4gIH0gY2F0Y2ggKGVycm9yKSB7XHJcbiAgICB0aHJvdyBuZXcgRXhwb3J0RXJyb3IoXHJcbiAgICAgICdbc2VydmVyXSBDb3VsZCBub3QgY29uZmlndXJlIGFuZCBzdGFydCB0aGUgc2VydmVyLidcclxuICAgICkuc2V0RXJyb3IoZXJyb3IpO1xyXG4gIH1cclxufTtcclxuXHJcbi8qKlxyXG4gKiBFbmFibGUgcmF0ZSBsaW1pdGluZyBmb3IgdGhlIHNlcnZlci5cclxuICpcclxuICogQHBhcmFtIHtPYmplY3R9IGxpbWl0Q29uZmlnIC0gQ29uZmlndXJhdGlvbiBvYmplY3QgZm9yIHJhdGUgbGltaXRpbmcuXHJcbiAqL1xyXG5leHBvcnQgY29uc3QgZW5hYmxlUmF0ZUxpbWl0aW5nID0gKGxpbWl0Q29uZmlnKSA9PiByYXRlTGltaXQoYXBwLCBsaW1pdENvbmZpZyk7XHJcblxyXG4vKipcclxuICogR2V0IHRoZSBFeHByZXNzIGluc3RhbmNlLlxyXG4gKlxyXG4gKiBAcmV0dXJucyB7T2JqZWN0fSAtIFRoZSBFeHByZXNzIGluc3RhbmNlLlxyXG4gKi9cclxuZXhwb3J0IGNvbnN0IGdldEV4cHJlc3MgPSAoKSA9PiBleHByZXNzO1xyXG5cclxuLyoqXHJcbiAqIEdldCB0aGUgRXhwcmVzcyBhcHAgaW5zdGFuY2UuXHJcbiAqXHJcbiAqIEByZXR1cm5zIHtPYmplY3R9IC0gVGhlIEV4cHJlc3MgYXBwIGluc3RhbmNlLlxyXG4gKi9cclxuZXhwb3J0IGNvbnN0IGdldEFwcCA9ICgpID0+IGFwcDtcclxuXHJcbi8qKlxyXG4gKiBBcHBseSBtaWRkbGV3YXJlKHMpIHRvIGEgc3BlY2lmaWMgcGF0aC5cclxuICpcclxuICogQHBhcmFtIHtzdHJpbmd9IHBhdGggLSBUaGUgcGF0aCB0byB3aGljaCB0aGUgbWlkZGxld2FyZShzKSBzaG91bGQgYmUgYXBwbGllZC5cclxuICogQHBhcmFtIHsuLi5GdW5jdGlvbn0gbWlkZGxld2FyZXMgLSBUaGUgbWlkZGxld2FyZSBmdW5jdGlvbnMgdG8gYmUgYXBwbGllZC5cclxuICovXHJcbmV4cG9ydCBjb25zdCB1c2UgPSAocGF0aCwgLi4ubWlkZGxld2FyZXMpID0+IHtcclxuICBhcHAudXNlKHBhdGgsIC4uLm1pZGRsZXdhcmVzKTtcclxufTtcclxuXHJcbi8qKlxyXG4gKiBTZXQgdXAgYSByb3V0ZSB3aXRoIEdFVCBtZXRob2QgYW5kIGFwcGx5IG1pZGRsZXdhcmUocykuXHJcbiAqXHJcbiAqIEBwYXJhbSB7c3RyaW5nfSBwYXRoIC0gVGhlIHJvdXRlIHBhdGguXHJcbiAqIEBwYXJhbSB7Li4uRnVuY3Rpb259IG1pZGRsZXdhcmVzIC0gVGhlIG1pZGRsZXdhcmUgZnVuY3Rpb25zIHRvIGJlIGFwcGxpZWQuXHJcbiAqL1xyXG5leHBvcnQgY29uc3QgZ2V0ID0gKHBhdGgsIC4uLm1pZGRsZXdhcmVzKSA9PiB7XHJcbiAgYXBwLmdldChwYXRoLCAuLi5taWRkbGV3YXJlcyk7XHJcbn07XHJcblxyXG4vKipcclxuICogU2V0IHVwIGEgcm91dGUgd2l0aCBQT1NUIG1ldGhvZCBhbmQgYXBwbHkgbWlkZGxld2FyZShzKS5cclxuICpcclxuICogQHBhcmFtIHtzdHJpbmd9IHBhdGggLSBUaGUgcm91dGUgcGF0aC5cclxuICogQHBhcmFtIHsuLi5GdW5jdGlvbn0gbWlkZGxld2FyZXMgLSBUaGUgbWlkZGxld2FyZSBmdW5jdGlvbnMgdG8gYmUgYXBwbGllZC5cclxuICovXHJcbmV4cG9ydCBjb25zdCBwb3N0ID0gKHBhdGgsIC4uLm1pZGRsZXdhcmVzKSA9PiB7XHJcbiAgYXBwLnBvc3QocGF0aCwgLi4ubWlkZGxld2FyZXMpO1xyXG59O1xyXG5cclxuZXhwb3J0IGRlZmF1bHQge1xyXG4gIHN0YXJ0U2VydmVyLFxyXG4gIGVuYWJsZVJhdGVMaW1pdGluZyxcclxuICBnZXRFeHByZXNzLFxyXG4gIGdldEFwcCxcclxuICB1c2UsXHJcbiAgZ2V0LFxyXG4gIHBvc3RcclxufTtcclxuIiwiLyoqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKipcclxuXHJcbkhpZ2hjaGFydHMgRXhwb3J0IFNlcnZlclxyXG5cclxuQ29weXJpZ2h0IChjKSAyMDE2LTIwMjQsIEhpZ2hzb2Z0XHJcblxyXG5MaWNlbmNlZCB1bmRlciB0aGUgTUlUIGxpY2VuY2UuXHJcblxyXG5BZGRpdGlvbmFsbHkgYSB2YWxpZCBIaWdoY2hhcnRzIGxpY2Vuc2UgaXMgcmVxdWlyZWQgZm9yIHVzZS5cclxuXHJcblNlZSBMSUNFTlNFIGZpbGUgaW4gcm9vdCBmb3IgZGV0YWlscy5cclxuXHJcbioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKiovXHJcblxyXG5pbXBvcnQgeyBqb2luIH0gZnJvbSAncGF0aCc7XHJcblxyXG5pbXBvcnQgeyBfX2Rpcm5hbWUgfSBmcm9tICcuLi8uLi91dGlscy5qcyc7XHJcblxyXG4vKipcclxuICogQWRkcyB0aGUgR0VUIC8gcm91dGUgZm9yIGEgVUkgd2hlbiBlbmFibGVkIG9uIHRoZSBleHBvcnQgc2VydmVyLlxyXG4gKi9cclxuZXhwb3J0IGRlZmF1bHQgKGFwcCkgPT5cclxuICAhYXBwXHJcbiAgICA/IGZhbHNlXHJcbiAgICA6IGFwcC5nZXQoJy8nLCAocmVxdWVzdCwgcmVzcG9uc2UpID0+IHtcclxuICAgICAgICByZXNwb25zZS5zZW5kRmlsZShqb2luKF9fZGlybmFtZSwgJ3B1YmxpYycsICdpbmRleC5odG1sJykpO1xyXG4gICAgICB9KTtcclxuIiwiLyoqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKipcclxuXHJcbkhpZ2hjaGFydHMgRXhwb3J0IFNlcnZlclxyXG5cclxuQ29weXJpZ2h0IChjKSAyMDE2LTIwMjQsIEhpZ2hzb2Z0XHJcblxyXG5MaWNlbmNlZCB1bmRlciB0aGUgTUlUIGxpY2VuY2UuXHJcblxyXG5BZGRpdGlvbmFsbHkgYSB2YWxpZCBIaWdoY2hhcnRzIGxpY2Vuc2UgaXMgcmVxdWlyZWQgZm9yIHVzZS5cclxuXHJcblNlZSBMSUNFTlNFIGZpbGUgaW4gcm9vdCBmb3IgZGV0YWlscy5cclxuXHJcbioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKiovXHJcblxyXG5pbXBvcnQgY2FjaGUgZnJvbSAnLi4vLi4vY2FjaGUuanMnO1xyXG5pbXBvcnQgSHR0cEVycm9yIGZyb20gJy4uLy4uL2Vycm9ycy9IdHRwRXJyb3IuanMnO1xyXG5pbXBvcnQgeyBlbnZzIH0gZnJvbSAnLi4vLi4vZW52cy5qcyc7XHJcblxyXG4vKipcclxuICogQWRkcyB0aGUgUE9TVCAvY2hhbmdlX2hjX3ZlcnNpb24vOm5ld1ZlcnNpb24gcm91dGUgdGhhdCBjYW4gYmUgdXRpbGl6ZWQgdG8gbW9kaWZ5XHJcbiAqIHRoZSBIaWdoY2hhcnRzIHZlcnNpb24gb24gdGhlIHNlcnZlci5cclxuICpcclxuICogVE9ETzogQWRkIGF1dGggdG9rZW4gYW5kIGNvbm5lY3QgdG8gQVBJXHJcbiAqL1xyXG5leHBvcnQgZGVmYXVsdCAoYXBwKSA9PlxyXG4gICFhcHBcclxuICAgID8gZmFsc2VcclxuICAgIDogYXBwLnBvc3QoXHJcbiAgICAgICAgJy92ZXJzaW9uL2NoYW5nZS86bmV3VmVyc2lvbicsXHJcbiAgICAgICAgYXN5bmMgKHJlcXVlc3QsIHJlc3BvbnNlLCBuZXh0KSA9PiB7XHJcbiAgICAgICAgICB0cnkge1xyXG4gICAgICAgICAgICBjb25zdCBhZG1pblRva2VuID0gZW52cy5ISUdIQ0hBUlRTX0FETUlOX1RPS0VOO1xyXG5cclxuICAgICAgICAgICAgLy8gQ2hlY2sgdGhlIGV4aXN0ZW5jZSBvZiB0aGUgdG9rZW5cclxuICAgICAgICAgICAgaWYgKCFhZG1pblRva2VuIHx8ICFhZG1pblRva2VuLmxlbmd0aCkge1xyXG4gICAgICAgICAgICAgIHRocm93IG5ldyBIdHRwRXJyb3IoXHJcbiAgICAgICAgICAgICAgICAnVGhlIHNlcnZlciBpcyBub3QgY29uZmlndXJlZCB0byBwZXJmb3JtIHJ1bi10aW1lIHZlcnNpb24gY2hhbmdlczogSElHSENIQVJUU19BRE1JTl9UT0tFTiBpcyBub3Qgc2V0LicsXHJcbiAgICAgICAgICAgICAgICA0MDFcclxuICAgICAgICAgICAgICApO1xyXG4gICAgICAgICAgICB9XHJcblxyXG4gICAgICAgICAgICAvLyBDaGVjayBpZiB0aGUgaGMtYXV0aCBoZWFkZXIgY29udGFpbiBhIGNvcnJlY3QgdG9rZW5cclxuICAgICAgICAgICAgY29uc3QgdG9rZW4gPSByZXF1ZXN0LmdldCgnaGMtYXV0aCcpO1xyXG4gICAgICAgICAgICBpZiAoIXRva2VuIHx8IHRva2VuICE9PSBhZG1pblRva2VuKSB7XHJcbiAgICAgICAgICAgICAgdGhyb3cgbmV3IEh0dHBFcnJvcihcclxuICAgICAgICAgICAgICAgICdJbnZhbGlkIG9yIG1pc3NpbmcgdG9rZW46IFNldCB0aGUgdG9rZW4gaW4gdGhlIGhjLWF1dGggaGVhZGVyLicsXHJcbiAgICAgICAgICAgICAgICA0MDFcclxuICAgICAgICAgICAgICApO1xyXG4gICAgICAgICAgICB9XHJcblxyXG4gICAgICAgICAgICAvLyBDb21wYXJlIHZlcnNpb25zXHJcbiAgICAgICAgICAgIGNvbnN0IG5ld1ZlcnNpb24gPSByZXF1ZXN0LnBhcmFtcy5uZXdWZXJzaW9uO1xyXG4gICAgICAgICAgICBpZiAobmV3VmVyc2lvbikge1xyXG4gICAgICAgICAgICAgIHRyeSB7XHJcbiAgICAgICAgICAgICAgICAvLyBlc2xpbnQtZGlzYWJsZS1uZXh0LWxpbmUgaW1wb3J0L25vLW5hbWVkLWFzLWRlZmF1bHQtbWVtYmVyXHJcbiAgICAgICAgICAgICAgICBhd2FpdCBjYWNoZS51cGRhdGVWZXJzaW9uKG5ld1ZlcnNpb24pO1xyXG4gICAgICAgICAgICAgIH0gY2F0Y2ggKGVycm9yKSB7XHJcbiAgICAgICAgICAgICAgICB0aHJvdyBuZXcgSHR0cEVycm9yKFxyXG4gICAgICAgICAgICAgICAgICBgVmVyc2lvbiBjaGFuZ2U6ICR7ZXJyb3IubWVzc2FnZX1gLFxyXG4gICAgICAgICAgICAgICAgICBlcnJvci5zdGF0dXNDb2RlXHJcbiAgICAgICAgICAgICAgICApLnNldEVycm9yKGVycm9yKTtcclxuICAgICAgICAgICAgICB9XHJcblxyXG4gICAgICAgICAgICAgIC8vIFN1Y2Nlc3NcclxuICAgICAgICAgICAgICByZXNwb25zZS5zdGF0dXMoMjAwKS5zZW5kKHtcclxuICAgICAgICAgICAgICAgIHN0YXR1c0NvZGU6IDIwMCxcclxuICAgICAgICAgICAgICAgIHZlcnNpb246IGNhY2hlLnZlcnNpb24oKSxcclxuICAgICAgICAgICAgICAgIG1lc3NhZ2U6IGBTdWNjZXNzZnVsbHkgdXBkYXRlZCBIaWdoY2hhcnRzIHRvIHZlcnNpb246ICR7bmV3VmVyc2lvbn0uYFxyXG4gICAgICAgICAgICAgIH0pO1xyXG4gICAgICAgICAgICB9IGVsc2Uge1xyXG4gICAgICAgICAgICAgIC8vIE5vIHZlcnNpb24gc3BlY2lmaWVkXHJcbiAgICAgICAgICAgICAgdGhyb3cgbmV3IEh0dHBFcnJvcignTm8gbmV3IHZlcnNpb24gc3VwcGxpZWQuJywgNDAwKTtcclxuICAgICAgICAgICAgfVxyXG4gICAgICAgICAgfSBjYXRjaCAoZXJyb3IpIHtcclxuICAgICAgICAgICAgbmV4dChlcnJvcik7XHJcbiAgICAgICAgICB9XHJcbiAgICAgICAgfVxyXG4gICAgICApO1xyXG4iLCIvKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKlxyXG5cclxuSGlnaGNoYXJ0cyBFeHBvcnQgU2VydmVyXHJcblxyXG5Db3B5cmlnaHQgKGMpIDIwMTYtMjAyNCwgSGlnaHNvZnRcclxuXHJcbkxpY2VuY2VkIHVuZGVyIHRoZSBNSVQgbGljZW5jZS5cclxuXHJcbkFkZGl0aW9uYWxseSBhIHZhbGlkIEhpZ2hjaGFydHMgbGljZW5zZSBpcyByZXF1aXJlZCBmb3IgdXNlLlxyXG5cclxuU2VlIExJQ0VOU0UgZmlsZSBpbiByb290IGZvciBkZXRhaWxzLlxyXG5cclxuKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKi9cclxuXHJcbmltcG9ydCAnY29sb3JzJztcclxuaW1wb3J0IGRvdGVudiBmcm9tICdkb3RlbnYnO1xyXG5cclxuaW1wb3J0IHsgY2hlY2tBbmRVcGRhdGVDYWNoZSB9IGZyb20gJy4vY2FjaGUuanMnO1xyXG5pbXBvcnQge1xyXG4gIGJhdGNoRXhwb3J0LFxyXG4gIHNldEFsbG93Q29kZUV4ZWN1dGlvbixcclxuICBzaW5nbGVFeHBvcnQsXHJcbiAgc3RhcnRFeHBvcnRcclxufSBmcm9tICcuL2NoYXJ0LmpzJztcclxuaW1wb3J0IHsgbWFwVG9OZXdDb25maWcsIG1hbnVhbENvbmZpZywgc2V0T3B0aW9ucyB9IGZyb20gJy4vY29uZmlnLmpzJztcclxuaW1wb3J0IHtcclxuICBpbml0TG9nZ2luZyxcclxuICBsb2csXHJcbiAgbG9nV2l0aFN0YWNrLFxyXG4gIHNldExvZ0xldmVsLFxyXG4gIGVuYWJsZUZpbGVMb2dnaW5nXHJcbn0gZnJvbSAnLi9sb2dnZXIuanMnO1xyXG5pbXBvcnQgeyBpbml0UG9vbCwga2lsbFBvb2wgfSBmcm9tICcuL3Bvb2wuanMnO1xyXG5pbXBvcnQgc2VydmVyLCB7IHN0YXJ0U2VydmVyIH0gZnJvbSAnLi9zZXJ2ZXIvc2VydmVyLmpzJztcclxuaW1wb3J0IHsgcHJpbnRMb2dvLCBwcmludFVzYWdlIH0gZnJvbSAnLi91dGlscy5qcyc7XHJcblxyXG4vLyBMb2FkIC5lbnYgaW50byBlbnZpcm9ubWVudCB2YXJpYWJsZXNcclxuZG90ZW52LmNvbmZpZygpO1xyXG5cclxuLyoqXHJcbiAqIEluaXRpYWxpemVzIHRoZSBleHBvcnQgcHJvY2Vzcy4gVGFza3Mgc3VjaCBhcyBjb25maWd1cmluZyBsb2dnaW5nLCBjaGVja2luZ1xyXG4gKiBjYWNoZSBhbmQgc291cmNlcywgYW5kIGluaXRpYWxpemluZyB0aGUgcG9vbCBvZiByZXNvdXJjZXMgaGFwcGVuIGR1cmluZ1xyXG4gKiB0aGlzIHN0YWdlLiBGdW5jdGlvbiB0aGF0IGlzIHJlcXVpcmVkIHRvIGJlIGNhbGxlZCBiZWZvcmUgdHJ5aW5nIHRvIGV4cG9ydCBjaGFydHMgb3Igc2V0dGluZyBhIHNlcnZlci4gVGhlIGBvcHRpb25zYCBpcyBhbiBvYmplY3QgdGhhdCBjb250YWlucyBhbGwgb3B0aW9ucy5cclxuICpcclxuICogQHBhcmFtIHtPYmplY3R9IG9wdGlvbnMgLSBBbGwgZXhwb3J0IG9wdGlvbnMuXHJcbiAqXHJcbiAqIEByZXR1cm5zIHtQcm9taXNlPE9iamVjdD59IFByb21pc2UgcmVzb2x2aW5nIHRvIHRoZSB1cGRhdGVkIGV4cG9ydCBvcHRpb25zLlxyXG4gKi9cclxuY29uc3QgaW5pdEV4cG9ydCA9IGFzeW5jIChvcHRpb25zKSA9PiB7XHJcbiAgLy8gU2V0IHRoZSBhbGxvd0NvZGVFeGVjdXRpb24gcGVyIGV4cG9ydCBtb2R1bGUgc2NvcGVcclxuICBzZXRBbGxvd0NvZGVFeGVjdXRpb24oXHJcbiAgICBvcHRpb25zLmN1c3RvbUxvZ2ljICYmIG9wdGlvbnMuY3VzdG9tTG9naWMuYWxsb3dDb2RlRXhlY3V0aW9uXHJcbiAgKTtcclxuXHJcbiAgLy8gSW5pdCB0aGUgbG9nZ2luZ1xyXG4gIGluaXRMb2dnaW5nKG9wdGlvbnMubG9nZ2luZyk7XHJcblxyXG4gIC8vIENoZWNrIGlmIGNhY2hlIG5lZWRzIHRvIGJlIHVwZGF0ZWRcclxuICBhd2FpdCBjaGVja0FuZFVwZGF0ZUNhY2hlKG9wdGlvbnMuaGlnaGNoYXJ0cyB8fCB7IHZlcnNpb246ICdsYXRlc3QnIH0pO1xyXG5cclxuICAvLyBJbml0IHRoZSBwb29sXHJcbiAgYXdhaXQgaW5pdFBvb2woe1xyXG4gICAgcG9vbDogb3B0aW9ucy5wb29sIHx8IHtcclxuICAgICAgbWluV29ya2VyczogMSxcclxuICAgICAgbWF4V29ya2VyczogMVxyXG4gICAgfSxcclxuICAgIHB1cHBldGVlckFyZ3M6IG9wdGlvbnMucHVwcGV0ZWVyPy5hcmdzIHx8IFtdXHJcbiAgfSk7XHJcblxyXG4gIC8vIFJldHVybiB1cGRhdGVkIG9wdGlvbnNcclxuICByZXR1cm4gb3B0aW9ucztcclxufTtcclxuXHJcbmV4cG9ydCBkZWZhdWx0IHtcclxuICAvLyBTZXJ2ZXJcclxuICBzZXJ2ZXIsXHJcbiAgc3RhcnRTZXJ2ZXIsXHJcbiAgc2V0T3B0aW9ucyxcclxuXHJcbiAgLy8gRXhwb3J0aW5nXHJcbiAgaW5pdEV4cG9ydCxcclxuICBzaW5nbGVFeHBvcnQsXHJcbiAgYmF0Y2hFeHBvcnQsXHJcbiAgc3RhcnRFeHBvcnQsXHJcbiAga2lsbFBvb2wsXHJcblxyXG4gIC8vIExvZ3NcclxuICBsb2csXHJcbiAgbG9nV2l0aFN0YWNrLFxyXG4gIHNldExvZ0xldmVsLFxyXG4gIGVuYWJsZUZpbGVMb2dnaW5nLFxyXG5cclxuICAvLyBVdGlsc1xyXG4gIG1hcFRvTmV3Q29uZmlnLFxyXG4gIG1hbnVhbENvbmZpZyxcclxuICBwcmludExvZ28sXHJcbiAgcHJpbnRVc2FnZVxyXG59O1xyXG4iXSwibmFtZXMiOlsiYXN5bmMiLCJmZXRjaCIsInVybCIsInJlcXVlc3RPcHRpb25zIiwiUHJvbWlzZSIsInJlc29sdmUiLCJyZWplY3QiLCJwcm90b2NvbCIsInN0YXJ0c1dpdGgiLCJodHRwcyIsImh0dHAiLCJnZXRQcm90b2NvbCIsImdldCIsInJlcyIsImRhdGEiLCJvbiIsImNodW5rIiwidGV4dCIsImVycm9yIiwiZGVmYXVsdENvbmZpZyIsInB1cHBldGVlciIsImFyZ3MiLCJ2YWx1ZSIsInR5cGUiLCJkZXNjcmlwdGlvbiIsImhpZ2hjaGFydHMiLCJ2ZXJzaW9uIiwiZW52TGluayIsImNkblVSTCIsImNvcmVTY3JpcHRzIiwibW9kdWxlcyIsImluZGljYXRvcnMiLCJzY3JpcHRzIiwiZm9yY2VGZXRjaCIsImNhY2hlUGF0aCIsImV4cG9ydCIsImluZmlsZSIsImluc3RyIiwib3B0aW9ucyIsIm91dGZpbGUiLCJjb25zdHIiLCJkZWZhdWx0SGVpZ2h0IiwiZGVmYXVsdFdpZHRoIiwiZGVmYXVsdFNjYWxlIiwiaGVpZ2h0Iiwid2lkdGgiLCJzY2FsZSIsImdsb2JhbE9wdGlvbnMiLCJ0aGVtZU9wdGlvbnMiLCJiYXRjaCIsInJhc3Rlcml6YXRpb25UaW1lb3V0IiwiY3VzdG9tTG9naWMiLCJhbGxvd0NvZGVFeGVjdXRpb24iLCJhbGxvd0ZpbGVSZXNvdXJjZXMiLCJjdXN0b21Db2RlIiwiY2FsbGJhY2siLCJyZXNvdXJjZXMiLCJsb2FkQ29uZmlnIiwibGVnYWN5TmFtZSIsImNyZWF0ZUNvbmZpZyIsInNlcnZlciIsImVuYWJsZSIsImNsaU5hbWUiLCJob3N0IiwicG9ydCIsImJlbmNobWFya2luZyIsInNzbCIsImZvcmNlIiwiY2VydFBhdGgiLCJyYXRlTGltaXRpbmciLCJtYXhSZXF1ZXN0cyIsIndpbmRvdyIsImRlbGF5IiwidHJ1c3RQcm94eSIsInNraXBLZXkiLCJza2lwVG9rZW4iLCJwb29sIiwibWluV29ya2VycyIsIm1heFdvcmtlcnMiLCJ3b3JrTGltaXQiLCJhY3F1aXJlVGltZW91dCIsImNyZWF0ZVRpbWVvdXQiLCJkZXN0cm95VGltZW91dCIsImlkbGVUaW1lb3V0IiwiY3JlYXRlUmV0cnlJbnRlcnZhbCIsInJlYXBlckludGVydmFsIiwibGlzdGVuVG9Qcm9jZXNzRXhpdHMiLCJsb2dnaW5nIiwibGV2ZWwiLCJmaWxlIiwiZGVzdCIsInVpIiwicm91dGUiLCJvdGhlciIsIm5vTG9nbyIsInByb21wdHNDb25maWciLCJuYW1lIiwibWVzc2FnZSIsImluaXRpYWwiLCJqb2luIiwic2VwYXJhdG9yIiwiaW5zdHJ1Y3Rpb25zIiwiY2hvaWNlcyIsImhpbnQiLCJtaW4iLCJtYXgiLCJyb3VuZCIsImFic29sdXRlUHJvcHMiLCJuZXN0ZWRBcmdzIiwiY3JlYXRlTmVzdGVkQXJncyIsIm9iaiIsInByb3BDaGFpbiIsIk9iamVjdCIsImtleXMiLCJmb3JFYWNoIiwiayIsImluY2x1ZGVzIiwiZW50cnkiLCJzdWJzdHJpbmciLCJ1bmRlZmluZWQiLCJjb2xvcnMiLCJ0b0NvbnNvbGUiLCJ0b0ZpbGUiLCJwYXRoQ3JlYXRlZCIsImxldmVsc0Rlc2MiLCJ0aXRsZSIsImNvbG9yIiwibGlzdGVuZXJzIiwia2V5Iiwib3B0aW9uIiwiZW50cmllcyIsImxvZ1RvRmlsZSIsInRleHRzIiwicHJlZml4IiwiZXhpc3RzU3luYyIsIm1rZGlyU3luYyIsImFwcGVuZEZpbGUiLCJjb25jYXQiLCJjb25zb2xlIiwibG9nIiwibmV3TGV2ZWwiLCJsZW5ndGgiLCJEYXRlIiwidG9TdHJpbmciLCJzcGxpdCIsInRyaW0iLCJmbiIsImFwcGx5IiwibG9nV2l0aFN0YWNrIiwiY3VzdG9tTWVzc2FnZSIsIm1haW5NZXNzYWdlIiwic3RhY2tNZXNzYWdlIiwic3RhY2siLCJzbGljZSIsInNldExvZ0xldmVsIiwiZW5hYmxlRmlsZUxvZ2dpbmciLCJsb2dEZXN0IiwibG9nRmlsZSIsImVuZHNXaXRoIiwiX19kaXJuYW1lIiwiZmlsZVVSTFRvUGF0aCIsIlVSTCIsImRvY3VtZW50IiwicmVxdWlyZSIsInBhdGhUb0ZpbGVVUkwiLCJfX2ZpbGVuYW1lIiwiaHJlZiIsIl9kb2N1bWVudEN1cnJlbnRTY3JpcHQiLCJzcmMiLCJiYXNlVVJJIiwiZml4VHlwZSIsImZvcm1hdHMiLCJvdXRUeXBlIiwicG9wIiwiZmluZCIsInQiLCJoYW5kbGVSZXNvdXJjZXMiLCJhbGxvd2VkUHJvcHMiLCJoYW5kbGVkUmVzb3VyY2VzIiwiY29ycmVjdFJlc291cmNlcyIsImlzQ29ycmVjdEpTT04iLCJyZWFkRmlsZVN5bmMiLCJmaWxlcyIsInByb3BOYW1lIiwibWFwIiwiaXRlbSIsInBhcnNlZERhdGEiLCJKU09OIiwicGFyc2UiLCJzdHJpbmdpZnkiLCJkZWVwQ29weSIsImNvcHkiLCJBcnJheSIsImlzQXJyYXkiLCJwcm90b3R5cGUiLCJoYXNPd25Qcm9wZXJ0eSIsImNhbGwiLCJvcHRpb25zU3RyaW5naWZ5IiwiYWxsb3dGdW5jdGlvbnMiLCJyZXBsYWNlQWxsIiwicHJpbnRVc2FnZSIsImJvbGQiLCJ5ZWxsb3ciLCJjeWNsZUNhdGVnb3JpZXMiLCJkZXNjTmFtZSIsImdyZWVuIiwiaSIsImJsdWUiLCJjYXRlZ29yeSIsInRvVXBwZXJDYXNlIiwicmVkIiwidG9Cb29sZWFuIiwid3JhcEFyb3VuZCIsInJlcGxhY2UiLCJtZWFzdXJlVGltZSIsInN0YXJ0IiwicHJvY2VzcyIsImhydGltZSIsImJpZ2ludCIsIk51bWJlciIsImRvdGVudiIsImNvbmZpZyIsInYiLCJ6IiwiZW51bSIsInRyYW5zZm9ybSIsIm9wdGlvbmFsIiwic3RyaW5nIiwidmFsIiwiZW52cyIsIm9iamVjdCIsIkhJR0hDSEFSVFNfVkVSU0lPTiIsInJlZmluZSIsInRlc3QiLCJISUdIQ0hBUlRTX0NETl9VUkwiLCJISUdIQ0hBUlRTX0NPUkVfU0NSSVBUUyIsIkhJR0hDSEFSVFNfTU9EVUxFUyIsIkhJR0hDSEFSVFNfSU5ESUNBVE9SUyIsIkhJR0hDSEFSVFNfRk9SQ0VfRkVUQ0giLCJISUdIQ0hBUlRTX0NBQ0hFX1BBVEgiLCJISUdIQ0hBUlRTX0FETUlOX1RPS0VOIiwiRVhQT1JUX1RZUEUiLCJFWFBPUlRfQ09OU1RSIiwiRVhQT1JUX0RFRkFVTFRfSEVJR0hUIiwiY29lcmNlIiwibnVtYmVyIiwicG9zaXRpdmUiLCJFWFBPUlRfREVGQVVMVF9XSURUSCIsIkVYUE9SVF9ERUZBVUxUX1NDQUxFIiwiRVhQT1JUX1JBU1RFUklaQVRJT05fVElNRU9VVCIsIkNVU1RPTV9MT0dJQ19BTExPV19DT0RFX0VYRUNVVElPTiIsIkNVU1RPTV9MT0dJQ19BTExPV19GSUxFTF9SRVNPVVJDRVMiLCJTRVJWRVJfRU5BQkxFIiwiU0VSVkVSX0hPU1QiLCJTRVJWRVJfUE9SVCIsIlNFUlZFUl9CRU5DSE1BUktJTkciLCJTRVJWRVJfU1NMX0VOQUJMRSIsIlNFUlZFUl9TU0xfRk9SQ0UiLCJTRVJWRVJfU1NMX1BPUlQiLCJTRVJWRVJfU1NMX0NFUlRfUEFUSCIsIlNFUlZFUl9SQVRFX0xJTUlUSU5HX0VOQUJMRSIsIlNFUlZFUl9SQVRFX0xJTUlUSU5HX01BWF9SRVFVRVNUUyIsIlNFUlZFUl9SQVRFX0xJTUlUSU5HX1dJTkRPVyIsIlNFUlZFUl9SQVRFX0xJTUlUSU5HX0RFTEFZIiwiU0VSVkVSX1JBVEVfTElNSVRJTkdfVFJVU1RfUFJPWFkiLCJTRVJWRVJfUkFURV9MSU1JVElOR19TS0lQX0tFWSIsIlNFUlZFUl9SQVRFX0xJTUlUSU5HX1NLSVBfVE9LRU4iLCJQT09MX01JTl9XT1JLRVJTIiwiUE9PTF9NQVhfV09SS0VSUyIsIlBPT0xfV09SS19MSU1JVCIsIlBPT0xfQUNRVUlSRV9USU1FT1VUIiwiUE9PTF9DUkVBVEVfVElNRU9VVCIsIlBPT0xfREVTVFJPWV9USU1FT1VUIiwiUE9PTF9JRExFX1RJTUVPVVQiLCJQT09MX0NSRUFURV9SRVRSWV9JTlRFUlZBTCIsIlBPT0xfUkVBUEVSX0lOVEVSVkFMIiwiUE9PTF9CRU5DSE1BUktJTkciLCJQT09MX0xJU1RFTl9UT19QUk9DRVNTX0VYSVRTIiwiTE9HR0lOR19MRVZFTCIsIkxPR0dJTkdfRklMRSIsIkxPR0dJTkdfREVTVCIsIlVJX0VOQUJMRSIsIlVJX1JPVVRFIiwiT1RIRVJfTk9fTE9HTyIsIk5PREVfRU5WIiwiZGVmYXVsdCIsIlBST1hZX1NFUlZFUl9USU1FT1VUIiwiUFJPWFlfU0VSVkVSX0hPU1QiLCJQUk9YWV9TRVJWRVJfUE9SVCIsImVudiIsIkV4cG9ydEVycm9yIiwiRXJyb3IiLCJjb25zdHJ1Y3RvciIsInN1cGVyIiwidGhpcyIsInNldEVycm9yIiwic3RhdHVzQ29kZSIsImNhY2hlIiwiYWN0aXZlTWFuaWZlc3QiLCJzb3VyY2VzIiwiaGNWZXJzaW9uIiwiYXBwbGllZENvbmZpZyIsImV4dHJhY3RWZXJzaW9uIiwiaW5kZXhPZiIsImZldGNoQW5kUHJvY2Vzc1NjcmlwdCIsInNjcmlwdCIsInByb3h5QWdlbnQiLCJmZXRjaGVkTW9kdWxlcyIsInNob3VsZFRocm93RXJyb3IiLCJhZ2VudCIsInRpbWVvdXQiLCJyZXNwb25zZSIsInVwZGF0ZUNhY2hlIiwic291cmNlUGF0aCIsImN1c3RvbVNjcmlwdHMiLCJwcm94eUhvc3QiLCJwcm94eVBvcnQiLCJIdHRwc1Byb3h5QWdlbnQiLCJtb2R1bGVTY3JpcHRzIiwiYWxsRmV0Y2hQcm9taXNlcyIsImFsbCIsImZldGNoU2NyaXB0cyIsImMiLCJtIiwid3JpdGVGaWxlU3luYyIsImNoZWNrQW5kVXBkYXRlQ2FjaGUiLCJtYW5pZmVzdFBhdGgiLCJyZXF1ZXN0VXBkYXRlIiwibWFuaWZlc3QiLCJtb2R1bGVNYXAiLCJudW1iZXJPZk1vZHVsZXMiLCJzb21lIiwibW9kdWxlTmFtZSIsIm5ld01hbmlmZXN0Iiwic2F2ZUNvbmZpZ1RvTWFuaWZlc3QiLCJnZXRDYWNoZVBhdGgiLCJjYWNoZSQxIiwibmV3VmVyc2lvbiIsImFzc2lnbiIsImdlbmVyYWxPcHRpb25zIiwiZ2V0T3B0aW9ucyIsIm1lcmdlQ29uZmlnT3B0aW9ucyIsIm5ld09wdGlvbnMiLCJtZXJnZWRPcHRpb25zIiwidXBkYXRlRGVmYXVsdENvbmZpZyIsImNvbmZpZ09iaiIsImN1c3RvbU9iaiIsImN1c3RvbVZhbHVlIiwiaW5pdE9wdGlvbnMiLCJpdGVtcyIsInJlY3Vyc2l2ZVByb3BzIiwib2JqZWN0VG9VcGRhdGUiLCJuZXN0ZWROYW1lcyIsInNoaWZ0IiwiUkFORE9NX1BJRCIsInJhbmRvbUJ5dGVzIiwiUFVQUEVURUVSX0RJUiIsInBhdGgiLCJtaW5pbWFsQXJncyIsInRlbXBsYXRlIiwiZnMiLCJicm93c2VyIiwic2V0UGFnZUNvbnRlbnQiLCJwYWdlIiwic2V0Q29udGVudCIsImFkZFNjcmlwdFRhZyIsImV2YWx1YXRlIiwic2V0dXBIaWdoY2hhcnRzIiwiJGV2YWwiLCJlbGVtZW50IiwiZXJyb3JNZXNzYWdlIiwiX2Rpc3BsYXlFcnJvcnMiLCJpbm5lckhUTUwiLCJjbGVhclBhZ2UiLCJoYXJkUmVzZXQiLCJnb3RvIiwiYm9keSIsIm5ld1BhZ2UiLCJzZXRDYWNoZUVuYWJsZWQiLCJjbG9zZSIsImlzQ29ubmVjdGVkIiwiX19iYXNlZGlyIiwic2V0QXNDb25maWciLCJjaGFydCIsInRyaWdnZXJFeHBvcnQiLCJwdXBwZXRlZXJFeHBvcnQiLCJpbmplY3RlZFJlc291cmNlcyIsImNsZWFySW5qZWN0ZWQiLCJkaXNwb3NlIiwic2NyaXB0c1RvUmVtb3ZlIiwiZ2V0RWxlbWVudHNCeVRhZ05hbWUiLCJzdHlsZXNUb1JlbW92ZSIsImxpbmtzVG9SZW1vdmUiLCJyZW1vdmUiLCJleHBvcnRPcHRpb25zIiwicmVxdWVzdEFuaW1hdGlvbkZyYW1lIiwiZGlzcGxheUVycm9ycyIsImRlYnVnZ2VyIiwiaXNTVkciLCJkIiwic3ZnVGVtcGxhdGUiLCJzdHJJbmoiLCJqcyIsInB1c2giLCJjb250ZW50IiwiaXNMb2NhbCIsImNzcyIsImNzc0ltcG9ydHMiLCJtYXRjaCIsImNzc0ltcG9ydFBhdGgiLCJhZGRTdHlsZVRhZyIsInNpemUiLCJjaGFydEhlaWdodCIsImJhc2VWYWwiLCJjaGFydFdpZHRoIiwicGFyc2VGbG9hdCIsIkhpZ2hjaGFydHMiLCJjaGFydHMiLCJ2aWV3cG9ydEhlaWdodCIsIk1hdGgiLCJjZWlsIiwidmlld3BvcnRXaWR0aCIsInNldFZpZXdwb3J0IiwiZGV2aWNlU2NhbGVGYWN0b3IiLCJ6b29tQ2FsbGJhY2siLCJzdHlsZSIsInpvb20iLCJtYXJnaW4iLCJ4IiwieSIsImdldEJvdW5kaW5nQ2xpZW50UmVjdCIsInRydW5jIiwiZ2V0Q2xpcFJlZ2lvbiIsIm91dGVySFRNTCIsImNyZWF0ZVNWRyIsImVuY29kaW5nIiwiY2xpcCIsInJhY2UiLCJzY3JlZW5zaG90Iiwib21pdEJhY2tncm91bmQiLCJfcmVzb2x2ZSIsInNldFRpbWVvdXQiLCJjcmVhdGVJbWFnZSIsInBkZiIsImNyZWF0ZVBERiIsIm9sZENoYXJ0cyIsIm9sZENoYXJ0IiwiZGVzdHJveSIsInN0YXRzIiwicGVyZm9ybWVkRXhwb3J0cyIsImV4cG9ydEF0dGVtcHRzIiwiZXhwb3J0RnJvbVN2Z0F0dGVtcHRzIiwidGltZVNwZW50IiwiZHJvcHBlZEV4cG9ydHMiLCJzcGVudEF2ZXJhZ2UiLCJwdXBwZXRlZXJBcmdzIiwicG9vbENvbmZpZyIsImZhY3RvcnkiLCJjcmVhdGUiLCJpZCIsInV1aWQiLCJzdGFydERhdGUiLCJnZXRUaW1lIiwiYnJvd3Nlck5ld1BhZ2UiLCJpc0Nsb3NlZCIsIndvcmtDb3VudCIsInJhbmRvbSIsInZhbGlkYXRlIiwid29ya2VySGFuZGxlIiwiaW5pdFBvb2wiLCJjb2RlIiwia2lsbFBvb2wiLCJleGl0IiwiYWxsQXJncyIsInRyeUNvdW50Iiwib3BlbiIsImxhdW5jaCIsImhlYWRsZXNzIiwidXNlckRhdGFEaXIiLCJjcmVhdGVCcm93c2VyIiwicGFyc2VJbnQiLCJQb29sIiwiYWNxdWlyZVRpbWVvdXRNaWxsaXMiLCJjcmVhdGVUaW1lb3V0TWlsbGlzIiwiZGVzdHJveVRpbWVvdXRNaWxsaXMiLCJpZGxlVGltZW91dE1pbGxpcyIsImNyZWF0ZVJldHJ5SW50ZXJ2YWxNaWxsaXMiLCJyZWFwSW50ZXJ2YWxNaWxsaXMiLCJwcm9wYWdhdGVDcmVhdGVFcnJvciIsInJlc291cmNlIiwiZXZlbnRJZCIsImluaXRpYWxSZXNvdXJjZXMiLCJhY3F1aXJlIiwicHJvbWlzZSIsInJlbGVhc2UiLCJicm93c2VyQ2xvc2UiLCJkZXN0cm95ZWQiLCJwb3N0V29yayIsImdldFBvb2xJbmZvIiwiYWNxdWlyZUNvdW50ZXIiLCJwYXlsb2FkIiwicmVxdWVzdElkIiwid29ya1N0YXJ0IiwiZXhwb3J0Q291bnRlciIsInJlc3VsdCIsImV4cG9ydFRpbWUiLCJudW1GcmVlIiwibnVtVXNlZCIsIm51bVBlbmRpbmdBY3F1aXJlcyIsInBvb2wkMSIsImF2YWlsYWJsZSIsImluVXNlIiwicGVuZGluZ0FjcXVpcmUiLCJzdGFydEV4cG9ydCIsInNldHRpbmdzIiwiZW5kQ2FsbGJhY2siLCJzdmciLCJpbml0RXhwb3J0U2V0dGluZ3MiLCJleHBvcnRBc1N0cmluZyIsImRvU3RyYWlnaHRJbmplY3QiLCJkb0V4cG9ydCIsImZpbmRDaGFydFNpemUiLCJleHBvcnRpbmciLCJwcmVjaXNpb24iLCJtdWx0aXBsaWVyIiwicG93Iiwicm91bmROdW1iZXIiLCJzb3VyY2VIZWlnaHQiLCJzb3VyY2VXaWR0aCIsInBhcmFtIiwiY2hhcnRKc29uIiwiY3VzdG9tTG9naWNPcHRpb25zIiwiYWxsb3dDb2RlRXhlY3V0aW9uU2NvcGVkIiwiZW5hYmxlZCIsIm9wdGlvbnNOYW1lIiwic3RyaW5nVG9FeHBvcnQiLCJjaGFydEpTT04iLCJsb2dFcnJvck1pZGRsZXdhcmUiLCJyZXEiLCJuZXh0IiwicmV0dXJuRXJyb3JNaWRkbGV3YXJlIiwic3RDb2RlIiwic3RhdHVzIiwianNvbiIsInJhdGVMaW1pdCIsImFwcCIsImxpbWl0Q29uZmlnIiwibXNnIiwicmF0ZU9wdGlvbnMiLCJsaW1pdGVyIiwid2luZG93TXMiLCJkZWxheU1zIiwiaGFuZGxlciIsInJlcXVlc3QiLCJmb3JtYXQiLCJzZW5kIiwic2tpcCIsInF1ZXJ5IiwiYWNjZXNzX3Rva2VuIiwidXNlIiwiSHR0cEVycm9yIiwic2V0U3RhdHVzIiwicmV2ZXJzZWRNaW1lIiwicG5nIiwianBlZyIsImdpZiIsInJlcXVlc3RzQ291bnRlciIsImJlZm9yZVJlcXVlc3QiLCJhZnRlclJlcXVlc3QiLCJkb0NhbGxiYWNrcyIsImNhbGxiYWNrcyIsInVuaXF1ZUlkIiwiY2FsbFJlc3BvbnNlIiwiZXhwb3J0SGFuZGxlciIsInN0b3BDb3VudGVyIiwiZGVmYXVsdE9wdGlvbnMiLCJoZWFkZXJzIiwiY29ubmVjdGlvbiIsInJlbW90ZUFkZHJlc3MiLCJjb25uZWN0aW9uQWJvcnRlZCIsInNvY2tldCIsInRvTG93ZXJDYXNlIiwic3Vic3RyIiwiYjY0Iiwibm9Eb3dubG9hZCIsInBhdHRlcm4iLCJpc1ByaXZhdGVSYW5nZVVybEZvdW5kIiwiaW5mbyIsInJlbW92ZUFsbExpc3RlbmVycyIsIkJ1ZmZlciIsImZyb20iLCJoZWFkZXIiLCJhdHRhY2htZW50IiwicGFyYW1zIiwiZmlsZW5hbWUiLCJwa2dGaWxlIiwicGF0aGVyIiwic2VydmVyU3RhcnRUaW1lIiwic3VjY2Vzc1JhdGVzIiwiYWRkSGVhbHRoUm91dGVzIiwiXyIsInBlcmlvZCIsIm1vdmluZ0F2ZXJhZ2UiLCJyZWR1Y2UiLCJhIiwiYiIsImJvb3RUaW1lIiwidXB0aW1lIiwiZmxvb3IiLCJoaWdoY2hhcnRzVmVyc2lvbiIsImF2ZXJhZ2VQcm9jZXNzaW5nVGltZSIsImZhaWxlZEV4cG9ydHMiLCJzdWNlc3NSYXRpbyIsInRvRml4ZWQiLCJzdmdFeHBvcnRBdHRlbXB0cyIsImpzb25FeHBvcnRBdHRlbXB0cyIsInNldEludGVydmFsIiwic3VjY2Vzc1JhdGlvIiwiZXhwcmVzcyIsImRpc2FibGUiLCJjb3JzIiwic3RvcmFnZSIsIm11bHRlciIsIm1lbW9yeVN0b3JhZ2UiLCJ1cGxvYWQiLCJsaW1pdHMiLCJmaWVsZFNpemUiLCJsaW1pdCIsInVybGVuY29kZWQiLCJleHRlbmRlZCIsIm5vbmUiLCJhdHRhY2hFcnJvckhhbmRsZXJzIiwic3RhcnRTZXJ2ZXIiLCJzZXJ2ZXJDb25maWciLCJodHRwU2VydmVyIiwiY3JlYXRlU2VydmVyIiwibGlzdGVuIiwiY2VydCIsImZzUHJvbWlzZXMiLCJyZWFkRmlsZSIsInBvc2l4IiwiaHR0cHNTZXJ2ZXIiLCJOYU4iLCJzdGF0aWMiLCJoZWFsdGhSb3V0ZSIsInBvc3QiLCJleHBvcnRSb3V0ZXMiLCJzZW5kRmlsZSIsInVpUm91dGUiLCJhZG1pblRva2VuIiwidG9rZW4iLCJ2U3dpdGNoUm91dGUiLCJlcnJvckhhbmRsZXIiLCJlbmFibGVSYXRlTGltaXRpbmciLCJnZXRFeHByZXNzIiwiZ2V0QXBwIiwibWlkZGxld2FyZXMiLCJpbmRleCIsInNldE9wdGlvbnMiLCJ1c2VyT3B0aW9ucyIsImNvbmZpZ0luZGV4IiwiZmluZEluZGV4IiwiYXJnIiwiZmlsZU5hbWUiLCJsb2FkQ29uZmlnRmlsZSIsInNob3dVc2FnZSIsInByb3BlcnRpZXNDaGFpbiIsImFyZ3VtZW50VHlwZSIsInByb3AiLCJwYWlyQXJndW1lbnRWYWx1ZSIsImluaXRFeHBvcnQiLCJpbml0TG9nZ2luZyIsInNpbmdsZUV4cG9ydCIsImJhdGNoRXhwb3J0IiwiYmF0Y2hGdW5jdGlvbnMiLCJwYWlyIiwibWFwVG9OZXdDb25maWciLCJvbGRPcHRpb25zIiwibWFudWFsQ29uZmlnIiwiY29uZmlnRmlsZU5hbWUiLCJjb25maWdGaWxlIiwiY2hvaWNlIiwicHJvbXB0cyIsIm9uU3VibWl0IiwicCIsImNhdGVnb3JpZXMiLCJxdWVzdGlvbnNDb3VudGVyIiwiYWxsUXVlc3Rpb25zIiwic2VjdGlvbiIsInByb21wdCIsImFuc3dlciIsIm1vZHVsZSIsInByb21pc2VzIiwid3JpdGVGaWxlIiwicHJpbnRMb2dvIiwicGFja2FnZVZlcnNpb24iXSwibWFwcGluZ3MiOiJtdUJBeUJBQSxlQUFlQyxFQUFNQyxFQUFLQyxFQUFpQixJQUN6QyxPQUFPLElBQUlDLFNBQVEsQ0FBQ0MsRUFBU0MsS0FDM0IsTUFBTUMsRUFiVSxDQUFDTCxHQUFTQSxFQUFJTSxXQUFXLFNBQVdDLEVBQVFDLEVBYTNDQyxDQUFZVCxHQUU3QkssRUFDR0ssSUFBSVYsRUFBS0MsR0FBaUJVLElBQ3pCLElBQUlDLEVB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
diff --git a/dist/index.esm.js b/dist/index.esm.js
index 4f4416eb..9a8d9090 100644
--- a/dist/index.esm.js
+++ b/dist/index.esm.js
@@ -1,2 +1,2 @@
-import"colors";import e from"dotenv";import t,{existsSync as r,mkdirSync as o,appendFile as i,readFileSync as n,writeFileSync as a,promises as s}from"fs";import l,{join as c,posix as p}from"path";import{HttpsProxyAgent as h}from"https-proxy-agent";import u from"http";import d from"https";import*as g from"url";import{fileURLToPath as m}from"url";import{z as f}from"zod";import v from"prompts";import{Pool as y}from"tarn";import{v4 as b}from"uuid";import w from"node:path";import E from"puppeteer";import{randomBytes as T}from"node:crypto";import R from"cors";import S from"express";import k from"multer";import L from"express-rate-limit";async function x(e,t={}){return new Promise(((r,o)=>{const i=(e=>e.startsWith("https")?d:u)(e);i.get(e,t,(e=>{let t="";e.on("data",(e=>{t+=e})),e.on("end",(()=>{t||o("Nothing was fetched from the URL."),e.text=t,r(e)}))})).on("error",(e=>{o(e)}))}))}const O={puppeteer:{args:{value:[],type:"string[]",description:"Arguments array to send to Puppeteer."}},highcharts:{version:{value:"latest",envLink:"HIGHCHARTS_VERSION",type:"string",description:"The Highcharts version to be used."},cdnURL:{value:"https://code.highcharts.com/",envLink:"HIGHCHARTS_CDN_URL",type:"string",description:"The CDN URL for Highcharts scripts to be used."},coreScripts:{envLink:"HIGHCHARTS_CORE_SCRIPTS",value:["highcharts","highcharts-more","highcharts-3d"],type:"string[]",description:"The core Highcharts scripts to fetch."},modules:{envLink:"HIGHCHARTS_MODULES",value:["stock","map","gantt","exporting","export-data","parallel-coordinates","accessibility","annotations-advanced","boost-canvas","boost","data","data-tools","draggable-points","static-scale","broken-axis","heatmap","tilemap","tiledwebmap","timeline","treemap","treegraph","item-series","drilldown","histogram-bellcurve","bullet","funnel","funnel3d","geoheatmap","pyramid3d","networkgraph","overlapping-datalabels","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","flowmap"],type:"string[]",description:"The modules of Highcharts to fetch."},indicators:{envLink:"HIGHCHARTS_INDICATORS",value:["indicators-all"],type:"string[]",description:"The indicators of Highcharts 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 optional scripts or dependencies to fetch."},forceFetch:{envLink:"HIGHCHARTS_FORCE_FETCH",value:!1,type:"boolean",description:"The flag to determine whether to refetch all scripts after each server rerun."},cachePath:{envLink:"HIGHCHARTS_CACHE_PATH",value:".cache",type:"string",description:"The path to the cache directory. It is used to store the Highcharts scripts and custom scripts."}},export:{infile:{value:!1,type:"string",description:"The input file should include a name and a type (json or svg). It must be correctly formatted as a JSON or SVG file."},instr:{value:!1,type:"string",description:"Input, provided in the form of a stringified JSON or SVG file, will override the --infile option."},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). This will ignore the --type flag."},type:{envLink:"EXPORT_TYPE",value:"png",type:"string",description:"The file export format. It can be jpeg, png, pdf, or svg."},constr:{envLink:"EXPORT_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 no value is set."},defaultWidth:{envLink:"EXPORT_DEFAULT_WIDTH",value:600,type:"number",description:"The default width of the exported chart. Used when no value is set."},defaultScale:{envLink:"EXPORT_DEFAULT_SCALE",value:1,type:"number",description:"The default scale of the exported chart. Used when no value is set."},height:{type:"number",value:!1,description:"The height of the exported chart, overriding the option in the chart settings."},width:{type:"number",value:!1,description:"The width of the exported chart, overriding the option in the chart settings."},scale:{value:!1,type:"number",description:"The scale of the exported chart, overriding the option in the chart settings. Ranges between 0.1 and 5.0."},globalOptions:{value:!1,type:"string",description:"Either a stringified JSON or a filename containing options to be passed into the Highcharts.setOptions."},themeOptions:{value:!1,type:"string",description:"Either a stringified JSON or a filename containing theme options to be passed into the Highcharts.setOptions."},batch:{value:!1,type:"string",description:'Initiates a batch job with a string containing input/output pairs: "in=out;in=out;...".'},rasterizationTimeout:{envLink:"EXPORT_RASTERIZATION_TIMEOUT",value:1500,type:"number",description:"The duration in milliseconds to wait for rendering a webpage."}},customLogic:{allowCodeExecution:{envLink:"CUSTOM_LOGIC_ALLOW_CODE_EXECUTION",value:!1,type:"boolean",description:"Controls whether the execution of arbitrary code is allowed during the exporting process."},allowFileResources:{envLink:"CUSTOM_LOGIC_ALLOW_FILE_RESOURCES",value:!1,type:"boolean",description:"Controls the ability to inject resources from the filesystem. This setting has no effect when running as a server."},customCode:{value:!1,type:"string",description:"Custom code to execute before chart initialization. It can be a function, code wrapped within a function, or a filename with the .js extension."},callback:{value:!1,type:"string",description:"JavaScript code to run during construction. It can be a function or a filename with the .js extension."},resources:{value:!1,type:"string",description:"Additional resource in the form of a stringified JSON, which may contain files, js, and css sections."},loadConfig:{value:!1,type:"string",legacyName:"fromFile",description:"A file containing a pre-defined configuration to use."},createConfig:{value:!1,type:"string",description:"Enables setting options through a prompt and saving them in a provided config file."}},server:{enable:{envLink:"SERVER_ENABLE",value:!1,type:"boolean",cliName:"enableServer",description:"When set to true, the server starts on the local IP address 0.0.0.0."},host:{envLink:"SERVER_HOST",value:"0.0.0.0",type:"string",description:"The hostname of the server. Additionally, it starts a server on the provided hostname."},port:{envLink:"SERVER_PORT",value:7801,type:"number",description:"The server port when enabled."},benchmarking:{envLink:"SERVER_BENCHMARKING",value:!1,type:"boolean",cliName:"serverBenchmarking",description:"Indicates whether to display the duration, in milliseconds, of specific actions that occur on the server while serving a request."},ssl:{enable:{envLink:"SERVER_SSL_ENABLE",value:!1,type:"boolean",cliName:"enableSsl",description:"Enables or disables the SSL protocol."},force:{envLink:"SERVER_SSL_FORCE",value:!1,type:"boolean",cliName:"sslForced",legacyName:"sslOnly",description:"When set to true, the server is forced to serve only over HTTPS."},port:{envLink:"SERVER_SSL_PORT",value:443,type:"number",cliName:"sslPort",description:"The port on which to run the SSL server."},certPath:{envLink:"SERVER_SSL_CERT_PATH",value:"",type:"string",legacyName:"sslPath",description:"The path to the SSL certificate/key file."}},rateLimiting:{enable:{envLink:"SERVER_RATE_LIMITING_ENABLE",value:!1,type:"boolean",cliName:"enableRateLimiting",description:"Enables rate limiting for the server."},maxRequests:{envLink:"SERVER_RATE_LIMITING_MAX_REQUESTS",value:10,type:"number",legacyName:"rateLimit",description:"The maximum number of requests allowed in one minute."},window:{envLink:"SERVER_RATE_LIMITING_WINDOW",value:1,type:"number",description:"The time window, in minutes, for the rate limiting."},delay:{envLink:"SERVER_RATE_LIMITING_DELAY",value:0,type:"number",description:"The delay duration for each successive request before reaching the maximum limit."},trustProxy:{envLink:"SERVER_RATE_LIMITING_TRUST_PROXY",value:!1,type:"boolean",description:"Set this to true if the server is behind a load balancer."},skipKey:{envLink:"SERVER_RATE_LIMITING_SKIP_KEY",value:"",type:"string",description:"Allows bypassing the rate limiter and should be provided with the skipToken argument."},skipToken:{envLink:"SERVER_RATE_LIMITING_SKIP_TOKEN",value:"",type:"string",description:"Allows bypassing the rate limiter and should be provided with the skipKey argument."}}},pool:{minWorkers:{envLink:"POOL_MIN_WORKERS",value:4,type:"number",description:"The number of minimum and initial pool workers to spawn."},maxWorkers:{envLink:"POOL_MAX_WORKERS",value:8,type:"number",legacyName:"workers",description:"The number of maximum pool workers to spawn."},workLimit:{envLink:"POOL_WORK_LIMIT",value:40,type:"number",description:"The number of work pieces that can be performed before restarting the worker process."},acquireTimeout:{envLink:"POOL_ACQUIRE_TIMEOUT",value:5e3,type:"number",description:"The duration, in milliseconds, to wait for acquiring a resource."},createTimeout:{envLink:"POOL_CREATE_TIMEOUT",value:5e3,type:"number",description:"The duration, in milliseconds, to wait for creating a resource."},destroyTimeout:{envLink:"POOL_DESTROY_TIMEOUT",value:5e3,type:"number",description:"The duration, in milliseconds, to wait for destroying a resource."},idleTimeout:{envLink:"POOL_IDLE_TIMEOUT",value:3e4,type:"number",description:"The duration, in milliseconds, after which an idle resource is destroyed."},createRetryInterval:{envLink:"POOL_CREATE_RETRY_INTERVAL",value:200,type:"number",description:"The duration, in milliseconds, to wait before retrying the create process in case of a failure."},reaperInterval:{envLink:"POOL_REAPER_INTERVAL",value:1e3,type:"number",description:"The duration, in milliseconds, after which the check for idle resources to destroy is triggered."},benchmarking:{envLink:"POOL_BENCHMARKING",value:!1,type:"boolean",cliName:"poolBenchmarking",description:"Indicate whether to show statistics for the pool of resources or not."},listenToProcessExits:{envLink:"POOL_LISTEN_TO_PROCESS_EXITS",value:!0,type:"boolean",description:"Decides whether or not to attach process.exit handlers."}},logging:{level:{envLink:"LOGGING_LEVEL",value:4,type:"number",cliName:"logLevel",description:"The logging level to be used."},file:{envLink:"LOGGING_FILE",value:"highcharts-export-server.log",type:"string",cliName:"logFile",description:"The name of a log file. The logDest option also needs to be set to enable file logging."},dest:{envLink:"LOGGING_DEST",value:"log/",type:"string",cliName:"logDest",description:"The path to store log files. This also enables file logging."}},ui:{enable:{envLink:"UI_ENABLE",value:!1,type:"boolean",cliName:"enableUi",description:"Enables or disables the user interface (UI) for the export server."},route:{envLink:"UI_ROUTE",value:"/",type:"string",cliName:"uiRoute",description:"The endpoint route to which the user interface (UI) should be attached."}},other:{noLogo:{envLink:"OTHER_NO_LOGO",value:!1,type:"boolean",description:"Skip printing the logo on a startup. Will be replaced by a simple text."}}},_={puppeteer:[{type:"list",name:"args",message:"Puppeteer arguments",initial:O.puppeteer.args.value.join(","),separator:","}],highcharts:[{type:"text",name:"version",message:"Highcharts version",initial:O.highcharts.version.value},{type:"text",name:"cdnURL",message:"The URL of CDN",initial:O.highcharts.cdnURL.value},{type:"multiselect",name:"modules",message:"Available modules",instructions:"Space: Select specific, A: Select all, Enter: Confirm.",choices:O.highcharts.modules.value},{type:"list",name:"scripts",message:"Custom scripts",initial:O.highcharts.scripts.value.join(","),separator:","},{type:"toggle",name:"forceFetch",message:"Force re-fetch the scripts",initial:O.highcharts.forceFetch.value},{type:"text",name:"cachePath",message:"The path to the cache directory",initial:O.highcharts.cachePath.value}],export:[{type:"select",name:"type",message:"The default export file type",hint:`Default: ${O.export.type.value}`,initial:0,choices:["png","jpeg","pdf","svg"]},{type:"select",name:"constr",message:"The default constructor for Highcharts",hint:`Default: ${O.export.constr.value}`,initial:0,choices:["chart","stockChart","mapChart","ganttChart"]},{type:"number",name:"defaultHeight",message:"The default fallback height of the exported chart",initial:O.export.defaultHeight.value},{type:"number",name:"defaultWidth",message:"The default fallback width of the exported chart",initial:O.export.defaultWidth.value},{type:"number",name:"defaultScale",message:"The default fallback scale of the exported chart",initial:O.export.defaultScale.value,min:.1,max:5},{type:"number",name:"rasterizationTimeout",message:"The rendering webpage timeout in milliseconds",initial:O.export.rasterizationTimeout.value}],customLogic:[{type:"toggle",name:"allowCodeExecution",message:"Enable execution of custom code",initial:O.customLogic.allowCodeExecution.value},{type:"toggle",name:"allowFileResources",message:"Enable file resources",initial:O.customLogic.allowFileResources.value}],server:[{type:"toggle",name:"enable",message:"Starts the server on 0.0.0.0",initial:O.server.enable.value},{type:"text",name:"host",message:"Server hostname",initial:O.server.host.value},{type:"number",name:"port",message:"Server port",initial:O.server.port.value},{type:"toggle",name:"benchmarking",message:"Enable server benchmarking",initial:O.server.benchmarking.value},{type:"toggle",name:"ssl.enable",message:"Enable SSL protocol",initial:O.server.ssl.enable.value},{type:"toggle",name:"ssl.force",message:"Force serving only over HTTPS",initial:O.server.ssl.force.value},{type:"number",name:"ssl.port",message:"SSL server port",initial:O.server.ssl.port.value},{type:"text",name:"ssl.certPath",message:"The path to find the SSL certificate/key",initial:O.server.ssl.certPath.value},{type:"toggle",name:"rateLimiting.enable",message:"Enable rate limiting",initial:O.server.rateLimiting.enable.value},{type:"number",name:"rateLimiting.maxRequests",message:"The maximum requests allowed per minute",initial:O.server.rateLimiting.maxRequests.value},{type:"number",name:"rateLimiting.window",message:"The rate-limiting time window in minutes",initial:O.server.rateLimiting.window.value},{type:"number",name:"rateLimiting.delay",message:"The delay for each successive request before reaching the maximum",initial:O.server.rateLimiting.delay.value},{type:"toggle",name:"rateLimiting.trustProxy",message:"Set to true if behind a load balancer",initial:O.server.rateLimiting.trustProxy.value},{type:"text",name:"rateLimiting.skipKey",message:"Allows bypassing the rate limiter when provided with the skipToken argument",initial:O.server.rateLimiting.skipKey.value},{type:"text",name:"rateLimiting.skipToken",message:"Allows bypassing the rate limiter when provided with the skipKey argument",initial:O.server.rateLimiting.skipToken.value}],pool:[{type:"number",name:"minWorkers",message:"The initial number of workers to spawn",initial:O.pool.minWorkers.value},{type:"number",name:"maxWorkers",message:"The maximum number of workers to spawn",initial:O.pool.maxWorkers.value},{type:"number",name:"workLimit",message:"The pieces of work that can be performed before restarting a Puppeteer process",initial:O.pool.workLimit.value},{type:"number",name:"acquireTimeout",message:"The number of milliseconds to wait for acquiring a resource",initial:O.pool.acquireTimeout.value},{type:"number",name:"createTimeout",message:"The number of milliseconds to wait for creating a resource",initial:O.pool.createTimeout.value},{type:"number",name:"destroyTimeout",message:"The number of milliseconds to wait for destroying a resource",initial:O.pool.destroyTimeout.value},{type:"number",name:"idleTimeout",message:"The number of milliseconds after an idle resource is destroyed",initial:O.pool.idleTimeout.value},{type:"number",name:"createRetryInterval",message:"The retry interval in milliseconds after a create process fails",initial:O.pool.createRetryInterval.value},{type:"number",name:"reaperInterval",message:"The reaper interval in milliseconds after triggering the check for idle resources to destroy",initial:O.pool.reaperInterval.value},{type:"toggle",name:"benchmarking",message:"Enable benchmarking for a resource pool",initial:O.pool.benchmarking.value},{type:"toggle",name:"listenToProcessExits",message:"Set to false to skip attaching process.exit handlers",initial:O.pool.listenToProcessExits.value}],logging:[{type:"number",name:"level",message:"The log level (0: silent, 1: error, 2: warning, 3: notice, 4: verbose, 5: benchmark)",initial:O.logging.level.value,round:0,min:0,max:5},{type:"text",name:"file",message:"A log file name. Set with the --logDest to enable file logging",initial:O.logging.file.value},{type:"text",name:"dest",message:"The path to log files. Enables file logging",initial:O.logging.dest.value}],ui:[{type:"toggle",name:"enable",message:"Enable UI for the export server",initial:O.ui.enable.value},{type:"text",name:"route",message:"A route to attach the UI",initial:O.ui.route.value}],other:[{type:"toggle",name:"noLogo",message:"Skip printing the logo on startup. Replaced by simple text",initial:O.other.noLogo.value}]},I=["options","globalOptions","themeOptions","resources","payload"],C={},A=(e,t="")=>{Object.keys(e).forEach((r=>{if(!["puppeteer","highcharts"].includes(r)){const o=e[r];void 0===o.value?A(o,`${t}.${r}`):(C[o.cliName||r]=`${t}.${r}`.substring(1),void 0!==o.legacyName&&(C[o.legacyName]=`${t}.${r}`.substring(1)))}}))};A(O);const N=["red","yellow","blue","gray","green"];let P={toConsole:!0,toFile:!1,pathCreated:!1,levelsDesc:[{title:"error",color:N[0]},{title:"warning",color:N[1]},{title:"notice",color:N[2]},{title:"verbose",color:N[3]},{title:"benchmark",color:N[4]}],listeners:[]};for(const[e,t]of Object.entries(O.logging))P[e]=t.value;const H=(e,t)=>{P.toFile&&(P.pathCreated||(!r(P.dest)&&o(P.dest),P.pathCreated=!0),i(`${P.dest}${P.file}`,[t].concat(e).join(" ")+"\n",(e=>{e&&(console.log(`[logger] Unable to write to log file: ${e}`),P.toFile=!1)})))},$=(...e)=>{const[t,...r]=e,{level:o,levelsDesc:i}=P;if(5!==t&&(0===t||t>o||o>i.length))return;const n=`${(new Date).toString().split("(")[0].trim()} [${i[t-1].title}] -`;P.listeners.forEach((e=>{e(n,r.join(" "))})),P.toConsole&&console.log.apply(void 0,[n.toString()[P.levelsDesc[t-1].color]].concat(r)),H(r,n)},j=(e,t,r)=>{const o=r||t.message,{level:i,levelsDesc:n}=P;if(0===e||e>i||i>n.length)return;const a=`${(new Date).toString().split("(")[0].trim()} [${n[e-1].title}] -`,s=t.message!==t.stackMessage||void 0===t.stackMessage?t.stack:t.stack.split("\n").slice(1).join("\n"),l=[o,"\n",s];P.toConsole&&console.log.apply(void 0,[a.toString()[P.levelsDesc[e-1].color]].concat([o[N[e-1]],"\n",s])),P.listeners.forEach((e=>{e(a,l.join(" "))})),H(l,a)},U=e=>{e>=0&&e<=P.levelsDesc.length&&(P.level=e)},M=(e,t)=>{if(P={...P,dest:e||P.dest,file:t||P.file,toFile:!0},0===P.dest.length)return $(1,"[logger] File logging initialization: no path supplied.");P.dest.endsWith("/")||(P.dest+="/")},G=m(new URL("../.",import.meta.url)),D=(e,t)=>{const r=["png","jpeg","pdf","svg"];if(t){const o=t.split(".").pop();"jpg"===o?e="jpeg":r.includes(o)&&e!==o&&(e=o)}return{"image/png":"png","image/jpeg":"jpeg","application/pdf":"pdf","image/svg+xml":"svg"}[e]||r.find((t=>t===e))||"png"},W=(e=!1,t)=>{const r=["js","css","files"];let o=e,i=!1;if(t&&e.endsWith(".json"))try{o=V(n(e,"utf8"))}catch(e){return j(2,e,"[cli] No resources found.")}else o=V(e),o&&!t&&delete o.files;for(const e in o)r.includes(e)?i||(i=!0):delete o[e];return i?(o.files&&(o.files=o.files.map((e=>e.trim())),(!o.files||o.files.length<=0)&&delete o.files),o):$(3,"[cli] No resources found.")};function V(e,t){try{const r=JSON.parse("string"!=typeof e?JSON.stringify(e):e);return"string"!=typeof r&&t?JSON.stringify(r):r}catch{return!1}}const F=e=>{if(null===e||"object"!=typeof e)return e;const t=Array.isArray(e)?[]:{};for(const r in e)Object.prototype.hasOwnProperty.call(e,r)&&(t[r]=F(e[r]));return t},q=(e,t)=>JSON.stringify(e,((e,r)=>("string"==typeof r&&((r=r.trim()).startsWith("function(")||r.startsWith("function ("))&&r.endsWith("}")&&(r=t?`EXP_FUN${(r+"").replaceAll(/\n|\t|\r/g," ")}EXP_FUN`:void 0),"function"==typeof r?`EXP_FUN${(r+"").replaceAll(/\n|\t|\r/g," ")}EXP_FUN`:r))).replaceAll(/"EXP_FUN|EXP_FUN"/g,"");function X(){console.log("\nUsage of CLI arguments:".bold,"\n------",`\nFor more detailed information, visit the readme at: ${"https://github.com/highcharts/node-export-server#readme".bold.yellow}.`);const e=t=>{for(const[r,o]of Object.entries(t))if(Object.prototype.hasOwnProperty.call(o,"value")){let e=` --${o.cliName||r} ${("<"+o.type+">").green} `;if(e.length<48)for(let t=e.length;t<48;t++)e+=".";console.log(e,o.description,`[Default: ${o.value.toString().bold}]`.blue)}else e(o)};Object.keys(O).forEach((t=>{["puppeteer","highcharts"].includes(t)||(console.log(`\n${t.toUpperCase()}`.red),e(O[t]))})),console.log("\n")}const K=e=>!["false","undefined","null","NaN","0",""].includes(e)&&!!e,J=(e,t)=>{if(e&&"string"==typeof e)return(e=e.trim()).endsWith(".js")?!!t&&J(n(e,"utf8")):e.startsWith("function()")||e.startsWith("function ()")||e.startsWith("()=>")||e.startsWith("() =>")?`(${e})()`:e.replace(/;$/,"")},B=()=>{const e=process.hrtime.bigint();return()=>Number(process.hrtime.bigint()-e)/1e6};e.config();const z=()=>f.enum(["true","false"]).transform((e=>"true"===e)).optional(),Y=()=>f.string().transform((e=>e.split(",").map((e=>e.trim())))).optional(),Q=f.object({HIGHCHARTS_VERSION:f.string().refine((e=>/^(latest|\d+(\.\d+){0,2})$/.test(e)),{message:"HIGHCHARTS_VERSION must be 'latest', a major version, or in the form XX.YY.ZZ"}).optional(),HIGHCHARTS_CDN_URL:f.string().trim().refine((e=>e.startsWith("https://")||e.startsWith("http://")),{message:"Invalid value for HIGHCHARTS_CDN_URL. It should start with http:// or https://."}).optional(),HIGHCHARTS_CORE_SCRIPTS:Y(),HIGHCHARTS_MODULES:Y(),HIGHCHARTS_INDICATORS:Y(),HIGHCHARTS_FORCE_FETCH:z(),HIGHCHARTS_CACHE_PATH:f.string().optional(),HIGHCHARTS_ADMIN_TOKEN:f.string().optional(),EXPORT_TYPE:f.enum(["jpeg","png","pdf","svg"]).optional(),EXPORT_CONSTR:f.string().refine((e=>["chart","stockChart","mapChart","ganttChart"].includes(e||"")),{message:"Invalid value for EXPORT_CONSTR. "}).optional(),EXPORT_DEFAULT_HEIGHT:f.coerce.number().positive().optional(),EXPORT_DEFAULT_WIDTH:f.coerce.number().positive().optional(),EXPORT_DEFAULT_SCALE:f.coerce.number().positive().optional(),EXPORT_RASTERIZATION_TIMEOUT:f.coerce.number().positive().optional(),CUSTOM_LOGIC_ALLOW_CODE_EXECUTION:z(),CUSTOM_LOGIC_ALLOW_FILEL_RESOURCES:z(),SERVER_ENABLE:z(),SERVER_HOST:f.string().optional(),SERVER_PORT:f.coerce.number().optional(),SERVER_BENCHMARKING:z(),SERVER_SSL_ENABLE:z(),SERVER_SSL_FORCE:z(),SERVER_SSL_PORT:f.coerce.number().optional(),SERVER_SSL_CERT_PATH:f.string().optional(),SERVER_RATE_LIMITING_ENABLE:z(),SERVER_RATE_LIMITING_MAX_REQUESTS:f.coerce.number().optional(),SERVER_RATE_LIMITING_WINDOW:f.coerce.number().optional(),SERVER_RATE_LIMITING_DELAY:f.coerce.number().optional(),SERVER_RATE_LIMITING_TRUST_PROXY:z(),SERVER_RATE_LIMITING_SKIP_KEY:f.string().optional(),SERVER_RATE_LIMITING_SKIP_TOKEN:f.string().optional(),POOL_MIN_WORKERS:f.coerce.number().optional(),POOL_MAX_WORKERS:f.coerce.number().optional(),POOL_WORK_LIMIT:f.coerce.number().optional(),POOL_ACQUIRE_TIMEOUT:f.coerce.number().optional(),POOL_CREATE_TIMEOUT:f.coerce.number().optional(),POOL_DESTROY_TIMEOUT:f.coerce.number().optional(),POOL_IDLE_TIMEOUT:f.coerce.number().optional(),POOL_CREATE_RETRY_INTERVAL:f.coerce.number().optional(),POOL_REAPER_INTERVAL:f.coerce.number().optional(),POOL_BENCHMARKING:z(),POOL_LISTEN_TO_PROCESS_EXITS:z(),LOGGING_LEVEL:f.coerce.number().optional().refine((e=>(e||4)>=0&&(e||4)<=4),{message:"Invalid value for LOGGING_LEVEL. We only accept 0, 1, 2, 3, 4 as logging levels."}),LOGGING_FILE:f.string().optional(),LOGGING_DEST:f.string().optional(),UI_ENABLE:z(),UI_ROUTE:f.string().optional(),OTHER_NO_LOGO:z(),NODE_ENV:f.enum(["development","production","test"]).optional().default("production"),PROXY_SERVER_TIMEOUT:f.coerce.number().positive().optional().default(5e3),PROXY_SERVER_HOST:f.string().optional().default("localhost"),PROXY_SERVER_PORT:f.coerce.number().positive().optional().default(8080)}).parse(process.env);class Z extends Error{constructor(e){super(),this.message=e,this.stackMessage=e}setError(e){return this.error=e,e.name&&(this.name=e.name),e.statusCode&&(this.statusCode=e.statusCode),e.stack&&(this.stackMessage=e.message,this.stack=e.stack),this}}const ee={cdnURL:"https://code.highcharts.com/",activeManifest:{},sources:"",hcVersion:""};let te=!1;const re=e=>e.sources.substring(0,e.sources.indexOf("*/")).replace("/*","").replace("*/","").replace(/\n/g,"").trim(),oe=async(e,t,r,o=!1)=>{e.endsWith(".js")&&(e=e.substring(0,e.length-3)),$(4,`[cache] Fetching script - ${e}.js`);const i=t?{agent:t,timeout:Q.PROXY_SERVER_TIMEOUT}:{},n=await x(`${e}.js`,i);if(200===n.statusCode&&"string"==typeof n.text){if(r){r[e.replace(/(.*)\/|(.*)modules\/|stock\/(.*)indicators\/|maps\/(.*)modules\//gi,"")]=1}return n.text}if(o)throw new Z(`Could not fetch the ${e}.js. The script might not exist in the requested version (status code: ${n.statusCode}).`).setError(n);return $(2,`[cache] Could not fetch the ${e}.js. The script might not exist in the requested version.`),""},ie=async(e,t)=>{const{coreScripts:r,modules:o,indicators:i,scripts:n}=e,s="latest"!==e.version&&e.version?`${e.version}/`:"";let l;$(3,`[cache] Updating cache version to Highcharts: ${s||"latest"}.`);const c=process.env.PROXY_SERVER_HOST,p=process.env.PROXY_SERVER_PORT;if(c&&p)try{l=new h({host:c,port:+p})}catch(e){throw new Z("[cache] Could not create a Proxy Agent.").setError(e)}const u={};try{return ee.sources=await(async(e,t,r,o,i,n)=>{const a=[...e.map((e=>oe(`${o}${e}`,i,n,!0))),...t.map((e=>oe(`${o}${e}`,i,n))),...r.map((e=>oe(`${e}`,i)))];return(await Promise.all(a)).join(";\n")})([...r.map((e=>`${s}${e}`))],[...o.map((e=>"map"===e?`maps/${s}modules/${e}`:`${s}modules/${e}`)),...i.map((e=>`stock/${s}indicators/${e}`))],n,e.cdnURL||ee.cdnURL,l,u),ee.hcVersion=re(ee),a(t,ee.sources),u}catch(e){throw new Z("[cache] Unable to update the local Highcharts cache.").setError(e)}},ne=async e=>{const t=c(G,e.cachePath);let i;const s=c(t,"manifest.json"),l=c(t,"sources.js");if(te=e,!r(t)&&o(t),!r(s)||e.forceFetch)$(3,"[cache] Fetching and caching Highcharts dependencies."),i=await ie(e,l);else{let t=!1;const r=JSON.parse(n(s));if(r.modules&&Array.isArray(r.modules)){const e={};r.modules.forEach((t=>e[t]=1)),r.modules=e}const{modules:o,coreScripts:a,indicators:c}=e,p=o.length+a.length+c.length;r.version!==e.version?($(2,"[cache] A Highcharts version mismatch in the cache, need to re-fetch."),t=!0):Object.keys(r.modules||{}).length!==p?($(2,"[cache] The cache and the requested modules do not match, need to re-fetch."),t=!0):t=(e.modules||[]).some((e=>{if(!r.modules[e])return $(2,`[cache] The ${e} is missing in the cache, need to re-fetch.`),!0})),t?i=await ie(e,l):($(3,"[cache] Dependency cache is up to date, proceeding."),ee.sources=n(l,"utf8"),i=r.modules,ee.hcVersion=re(ee))}await(async(e,t)=>{const r={version:e.version,modules:t||{}};ee.activeManifest=r,$(3,"[cache] Writing a new manifest.");try{a(c(G,e.cachePath,"manifest.json"),JSON.stringify(r),"utf8")}catch(e){throw new Z("[cache] Error writing the cache manifest.").setError(e)}})(e,i)},ae=()=>c(G,te.cachePath);var se=async e=>!!te&&await ne(Object.assign(te,{version:e})),le=()=>ee,ce=()=>ee.hcVersion;let pe={};const he=()=>pe,ue=(e,t,r=[])=>{const o=F(e);for(const[e,n]of Object.entries(t))o[e]="object"!=typeof(i=n)||Array.isArray(i)||null===i||r.includes(e)||void 0===o[e]?void 0!==n?n:o[e]:ue(o[e],n,r);var i;return o};function de(e,t={},r=""){Object.keys(e).forEach((o=>{const i=e[o],n=t&&t[o];void 0===i.value?de(i,n,`${r}.${o}`):(void 0!==n&&(i.value=n),i.envLink in Q&&(i.value=Q[i.envLink]))}))}function ge(e){let t={};for(const[r,o]of Object.entries(e))t[r]=Object.prototype.hasOwnProperty.call(o,"value")?o.value:ge(o);return t}function me(e,t,r){for(;t.length>1;){const o=t.shift();return Object.prototype.hasOwnProperty.call(e,o)||(e[o]={}),e[o]=me(Object.assign({},e[o]),t,r),e}return e[t[0]]=r,e}const fe=T(64).toString("base64url"),ve=w.join("tmp",`puppeteer-${fe}`),ye=[`--user-data-dir=${w.join(ve,"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"],be=g.fileURLToPath(new URL(".",import.meta.url)),we=t.readFileSync(be+"/../templates/template.html","utf8");let Ee;const Te=async e=>{await e.setContent(we),await e.addScriptTag({path:`${ae()}/sources.js`}),await e.evaluate((()=>window.setupHighcharts())),e.on("pageerror",(async t=>{await e.$eval("#container",((e,t)=>{window._displayErrors&&(e.innerHTML=t)}),`Chart input data error ${t.toString()}`)}))},Re=async(e,t=!1)=>{try{t?(await e.goto("about:blank"),await Te(e)):await e.evaluate((()=>{document.body.innerHTML=''}))}catch(e){j(2,e,"[browser] Could not clear the content of the page.")}},Se=async()=>{if(!Ee)return!1;const e=await Ee.newPage();return await e.setCacheEnabled(!1),await Te(e),e},ke=async()=>(Ee?.isConnected()&&(await Ee.close(),$(4,"[browser] Closed the browser.")),!0);const Le=g.fileURLToPath(new URL(".",import.meta.url)),xe=(e,t,r)=>e.evaluate(((e,t)=>window.triggerExport(e,t)),t,r);var Oe=async(e,t,r)=>{const o=[],i=async e=>{for(const e of o)await e.dispose();await e.evaluate((()=>{const[,...e]=document.getElementsByTagName("script"),[,...t]=document.getElementsByTagName("style"),[...r]=document.getElementsByTagName("link");for(const o of[...e,...t,...r])o.remove()}))};try{$(4,"[export] Determining export path.");const a=r.export;await e.evaluate((()=>requestAnimationFrame((()=>{}))));const s=a?.options?.chart?.displayErrors&&le().activeManifest.modules.debugger;let c;if(await e.evaluate((e=>window._displayErrors=e),s),t.indexOf&&(t.indexOf("=0||t.indexOf("=0)){if($(4,"[export] Treating as SVG."),"svg"===a.type)return t;c=!0,await e.setContent((e=>`\n\n\n \n \n Highcarts Export \n \n \n \n \n ${e}\n
\n \n\n\n`)(t))}else $(4,"[export] Treating as config."),a.strInj?await xe(e,{chart:{height:a.height,width:a.width}},r):(t.chart.height=a.height,t.chart.width=a.width,await xe(e,t,r));const p=r.customLogic.resources;if(p){if(p.js&&o.push(await e.addScriptTag({content:p.js})),p.files)for(const t of p.files)try{const r=!t.startsWith("http");o.push(await e.addScriptTag(r?{content:n(t,"utf8")}:{url:t}))}catch(e){j(2,e,`[export] The JS file ${t} cannot be loaded.`)}if(p.css){let t=p.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")?o.push(await e.addStyleTag({url:i})):r.customLogic.allowFileResources&&o.push(await e.addStyleTag({path:l.join(Le,i)})));o.push(await e.addStyleTag({content:p.css.replace(/@import\s*([^;]*);/g,"")||" "}))}}const h=c?await e.$eval("#chart-container svg:first-of-type",((e,t)=>({chartHeight:e.height.baseVal.value*t,chartWidth:e.width.baseVal.value*t})),parseFloat(a.scale)):await e.evaluate((()=>{const{chartHeight:e,chartWidth:t}=window.Highcharts.charts[0];return{chartHeight:e,chartWidth:t}})),u=Math.ceil(h?.chartHeight||a.height),d=Math.ceil(h?.chartWidth||a.width);await e.setViewport({height:u,width:d,deviceScaleFactor:c?1:parseFloat(a.scale)});const g=c?e=>{document.body.style.zoom=e,document.body.style.margin="0px"}:()=>{document.body.style.zoom=1};await e.evaluate(g,parseFloat(a.scale));const{height:m,width:f,x:v,y:y}=await(e=>e.$eval("#chart-container",(e=>{const{x:t,y:r,width:o,height:i}=e.getBoundingClientRect();return{x:t,y:r,width:o,height:Math.trunc(i>1?i:500)}})))(e);let b;if(c||await e.setViewport({width:Math.round(f),height:Math.round(m),deviceScaleFactor:parseFloat(a.scale)}),"svg"===a.type)b=await(e=>e.$eval("#container svg:first-of-type",(e=>e.outerHTML)))(e);else if(["png","jpeg"].includes(a.type))b=await((e,t,r,o,i)=>Promise.race([e.screenshot({type:t,encoding:r,clip:o,omitBackground:"png"==t}),new Promise(((e,t)=>setTimeout((()=>t(new Z("Rasterization timeout"))),i||1500)))]))(e,a.type,"base64",{width:d,height:u,x:v,y:y},a.rasterizationTimeout);else{if("pdf"!==a.type)throw new Z(`[export] Unsupported output format ${a.type}.`);b=await((e,t,r,o)=>e.pdf({height:t+1,width:r,encoding:o}))(e,u,d,"base64")}return await e.evaluate((()=>{if("undefined"!=typeof Highcharts){const e=Highcharts.charts;if(Array.isArray(e)&&e.length)for(const t of e)t&&t.destroy(),Highcharts.charts.shift()}})),await i(e),b}catch(t){return await i(e),t}};let _e,Ie=0,Ce=0,Ae=0,Ne=0,Pe=0,He={},$e=!1;const je={create:async()=>{let e=!1;const t=b(),r=(new Date).getTime();try{if(e=await Se(),!e||e.isClosed())throw new Z("The page is invalid or closed.");$(3,`[pool] Successfully created a worker ${t} - took ${(new Date).getTime()-r} ms.`)}catch(e){throw new Z("Error encountered when creating a new page.").setError(e)}return{id:t,page:e,workCount:Math.round(Math.random()*(He.workLimit/2))}},validate:async e=>He.workLimit&&++e.workCount>He.workLimit?($(3,`[pool] Worker failed validation: exceeded work limit (limit is ${He.workLimit}).`),!1):(await Re(e.page,!0),!0),destroy:e=>{$(3,`[pool] Destroying pool entry ${e.id}.`),e.page&&e.page.close()}},Ue=async e=>{if(He=e&&e.pool?{...e.pool}:{},He.listenToProcessExits&&($(3,"[pool] Attaching exit listeners to the process."),process.on("exit",(async e=>{$(4,`Process exited with code ${e}.`),await Me()})),process.on("SIGINT",((e,t)=>{$(4,`The ${e} event with code: ${t}.`),process.exit(1)})),process.on("SIGTERM",((e,t)=>{$(4,`The ${e} event with code: ${t}.`),process.exit(1)})),process.on("uncaughtException",(async(e,t)=>{j(1,e,`The ${t} error.`),await Me(),process.exit(1)}))),_e=e.puppeteerArgs,await(async e=>{const t=[...ye,...e||[]];if(!Ee){let e=0;const r=async()=>{try{$(3,`[browser] Attempting to get a browser instance (try ${++e}).`),Ee=await E.launch({headless:"new",args:t,userDataDir:"./tmp/"})}catch(t){if(j(1,t,"[browser] Failed to launch a browser instance."),!(e<25))throw t;$(3,`[browser] Retry to open a browser (${e} out of 25).`),await new Promise((e=>setTimeout(e,4e3))),await r()}};try{await r()}catch(e){throw new Z("[browser] Maximum retries to open a browser instance reached.").setError(e)}if(!Ee)throw new Z("[browser] Cannot find a browser to open.")}return Ee})(_e),$(3,`[pool] Initializing pool with workers: min ${He.minWorkers}, max ${He.maxWorkers}.`),$e)return $(4,"[pool] Already initialized, please kill it before creating a new one.");parseInt(He.minWorkers)>parseInt(He.maxWorkers)&&(He.minWorkers=He.maxWorkers);try{$e=new y({...je,min:parseInt(He.minWorkers),max:parseInt(He.maxWorkers),acquireTimeoutMillis:He.acquireTimeout,createTimeoutMillis:He.createTimeout,destroyTimeoutMillis:He.destroyTimeout,idleTimeoutMillis:He.idleTimeout,createRetryIntervalMillis:He.createRetryInterval,reapIntervalMillis:He.reaperInterval,propagateCreateError:!1}),$e.on("release",(async e=>{await Re(e.page,!1),$(4,`[pool] Releasing a worker with ID ${e.id}.`)})),$e.on("destroySuccess",((e,t)=>{$(4,`[pool] Destroyed a worker with ID ${t.id}.`)}));const e=[];for(let t=0;t{$e.release(e)})),$(3,"[pool] The pool is ready"+(e.length?` with ${e.length} initial resources waiting.`:"."))}catch(e){throw await ke(),new Z("[pool] Could not create the pool of workers.").setError(e)}};async function Me(){return $(3,"[pool] Killing all pool workers and browser, if any exist."),$e?.destroyed||$e&&(await $e.destroy(),$(4,"[browser] Destroyed the pool of resources.")),ke()}const Ge=async(e,t)=>{let r;try{if($(4,"[pool] Work received, starting to process."),++Ce,He.benchmarking&&De(),!$e)throw new Z("Work received, but pool has not been started.");try{$(4,"[pool] Acquiring a worker handle.");const e=B();r=await $e.acquire().promise,t.server.benchmarking&&$(5,t.payload?.requestId?`[benchmark] Request with ID ${t.payload?.requestId} -`:"[benchmark]",`Acquired a worker handle: ${e()}ms.`)}catch(e){throw new Z("Error encountered when acquiring an available entry.").setError(e)}if($(4,"[pool] Acquired a worker handle."),!r.page)throw new Z("Resolved worker page is invalid: the pool setup is wonky.");let o=(new Date).getTime();$(4,`[pool] Starting work on pool entry with ID ${r.id}.`);const i=B(),n=await Oe(r.page,e,t);if(n instanceof Error)throw"Rasterization timeout"===n.message&&(r.page.close(),r.page=await Se()),new Z("Error encountered during export.").setError(n);t.server.benchmarking&&$(5,t.payload?.requestId?`[benchmark] Request with ID ${t.payload?.requestId} -`:"[benchmark]",`Exported a chart sucessfully: ${i()}ms.`),$e.release(r);const a=(new Date).getTime()-o;return Ae+=a,Pe=Ae/++Ie,$(4,`[pool] Work completed in ${a} ms.`),{result:n,options:t}}catch(e){throw++Ne,r&&$e.release(r),new Z(`[pool] In pool.postWork: ${e.message}`).setError(e)}};function De(){const{min:e,max:t}=$e;$(5,`[pool] The minimum number of resources allowed by pool: ${e}.`),$(5,`[pool] The maximum number of resources allowed by pool: ${t}.`),$(5,`[pool] The number of resources that are currently available: ${$e.numFree()}.`),$(5,`[pool] The number of resources that are currently acquired: ${$e.numUsed()}.`),$(5,`[pool] The number of callers waiting to acquire a resource: ${$e.numPendingAcquires()}.`)}var We=()=>({min:$e.min,max:$e.max,available:$e.numFree(),inUse:$e.numUsed(),pendingAcquire:$e.numPendingAcquires()}),Ve=()=>Ce,Fe=()=>Ne,qe=()=>Pe,Xe=()=>Ie;let Ke=!1;const Je=async(e,t)=>{$(4,"[chart] Starting the exporting process.");const r=((e,t={})=>{let r={};return e.svg?(r=F(t),r.export.type=e.type||e.export.type,r.export.scale=e.scale||e.export.scale,r.export.outfile=e.outfile||e.export.outfile,r.payload={svg:e.svg}):r=ue(t,e,I),r.export.outfile=r.export?.outfile||`chart.${r.export?.type||"png"}`,r})(e,he()),o=r.export;if(r.payload?.svg&&""!==r.payload.svg)try{return $(4,"[chart] Attempting to export from a SVG input."),Qe(r.payload.svg.trim(),r,t)}catch(e){return t(new Z("[chart] Error loading SVG input.").setError(e))}if(o.infile&&o.infile.length)try{return $(4,"[chart] Attempting to export from an input file."),r.export.instr=n(o.infile,"utf8"),Qe(r.export.instr.trim(),r,t)}catch(e){return t(new Z("[chart] Error loading input file.").setError(e))}if(o.instr&&""!==o.instr||o.options&&""!==o.options)try{return $(4,"[chart] Attempting to export from a raw input."),K(r.customLogic?.allowCodeExecution)?Ye(r,t):"string"==typeof o.instr?Qe(o.instr.trim(),r,t):ze(r,o.instr||o.options,t)}catch(e){return t(new Z("[chart] Error loading raw input.").setError(e))}return t(new Z("[chart] No valid input specified. Check if at least one of the following parameters is correctly set: 'infile', 'instr', 'options', or 'svg'."))},Be=e=>{const{chart:t,exporting:r}=e.export?.options||V(e.export?.instr),o=V(e.export?.globalOptions);let i=e.export?.scale||r?.scale||o?.exporting?.scale||e.export?.defaultScale||1;i=Math.max(.1,Math.min(i,5)),i=((e,t=1)=>{const r=Math.pow(10,t||0);return Math.round(+e*r)/r})(i,2);const n={height:e.export?.height||r?.sourceHeight||t?.height||o?.exporting?.sourceHeight||o?.chart?.height||e.export?.defaultHeight||400,width:e.export?.width||r?.sourceWidth||t?.width||o?.exporting?.sourceWidth||o?.chart?.width||e.export?.defaultWidth||600,scale:i};for(let[e,t]of Object.entries(n))n[e]="string"==typeof t?+t.replace(/px|%/gi,""):t;return n},ze=async(e,t,r,o)=>{let{export:i,customLogic:a}=e;const s="boolean"==typeof a.allowCodeExecution?a.allowCodeExecution:Ke;if(a){if(s)if("string"==typeof e.customLogic.resources)e.customLogic.resources=W(e.customLogic.resources,K(e.customLogic.allowFileResources));else if(!e.customLogic.resources)try{const t=n("resources.json","utf8");e.customLogic.resources=W(t,K(e.customLogic.allowFileResources))}catch(e){j(2,e,"[chart] Unable to load the default resources.json file.")}}else a=e.customLogic={};if(!s&&a){if(a.callback||a.resources||a.customCode)return r(new Z("[chart] The 'callback', 'resources' and 'customCode' options have been disabled for this server."));a.callback=!1,a.resources=!1,a.customCode=!1}if(t&&(t.chart=t.chart||{},t.exporting=t.exporting||{},t.exporting.enabled=!1),i.constr=i.constr||"chart",i.type=D(i.type,i.outfile),"svg"===i.type&&(i.width=!1),["globalOptions","themeOptions"].forEach((e=>{try{i&&i[e]&&("string"==typeof i[e]&&i[e].endsWith(".json")?i[e]=V(n(i[e],"utf8"),!0):i[e]=V(i[e],!0))}catch(t){i[e]={},j(2,t,`[chart] The '${e}' cannot be loaded.`)}})),a.allowCodeExecution)try{a.customCode=J(a.customCode,a.allowFileResources)}catch(e){j(2,e,"[chart] The 'customCode' cannot be loaded.")}if(a&&a.callback&&a.callback?.indexOf("{")<0)if(a.allowFileResources)try{a.callback=n(a.callback,"utf8")}catch(e){a.callback=!1,j(2,e,"[chart] The 'callback' cannot be loaded.")}else a.callback=!1;e.export={...e.export,...Be(e)};try{return r(!1,await Ge(i.strInj||t||o,e))}catch(e){return r(e)}},Ye=(e,t)=>{try{let r,o=e.export.instr||e.export.options;return"string"!=typeof o&&(r=o=q(o,e.customLogic?.allowCodeExecution)),r=o.replaceAll(/\t|\n|\r/g,"").trim(),";"===r[r.length-1]&&(r=r.substring(0,r.length-1)),e.export.strInj=r,ze(e,!1,t)}catch(r){return t(new Z(`[chart] Malformed input detected for ${e.export?.requestId||"?"}. Please make sure that your JSON/JavaScript options are sent using the "options" attribute, and that if you're using SVG, it is unescaped.`).setError(r))}},Qe=(e,t,r)=>{const{allowCodeExecution:o}=t.customLogic;if(e.indexOf("=0||e.indexOf("=0)return $(4,"[chart] Parsing input as SVG."),ze(t,!1,r,e);try{const o=JSON.parse(e.replaceAll(/\t|\n|\r/g," "));return ze(t,o,r)}catch(e){return K(o)?Ye(t,r):r(new Z("[chart] Only JSON configurations and SVG are allowed for this server. If this is your server, JavaScript custom code can be enabled by starting the server with the --allowCodeExecution flag.").setError(e))}},Ze=(e,t,r,o)=>{j(1,e),"development"!==Q.NODE_ENV&&delete e.stack,o(e)},et=(e,t,r,o)=>{const{statusCode:i,status:n,message:a,stack:s}=e,l=i||n||500;r.status(l).json({statusCode:l,message:a,stack:s})};var tt=(e,t)=>{const r="Too many requests, you have been rate limited. Please try again later.",o={max:t.maxRequests||30,window:t.window||1,delay:t.delay||0,trustProxy:t.trustProxy||!1,skipKey:t.skipKey||!1,skipToken:t.skipToken||!1};o.trustProxy&&e.enable("trust proxy");const i=L({windowMs:60*o.window*1e3,max:o.max,delayMs:o.delay,handler:(e,t)=>{t.format({json:()=>{t.status(429).send({message:r})},default:()=>{t.status(429).send(r)}})},skip:e=>!1!==o.skipKey&&!1!==o.skipToken&&e.query.key===o.skipKey&&e.query.access_token===o.skipToken&&($(4,"[rate limiting] Skipping rate limiter."),!0)});e.use(i),$(3,`[rate limiting] Enabled rate limiting with ${o.max} requests per ${o.window} minute for each IP, trusting proxy: ${o.trustProxy}.`)};class rt extends Z{constructor(e,t){super(e),this.status=this.statusCode=t}setStatus(e){return this.status=e,this}}const ot={png:"image/png",jpeg:"image/jpeg",gif:"image/gif",pdf:"application/pdf",svg:"image/svg+xml"};let it=0;const nt=[],at=[],st=(e,t,r,o)=>{let i=!0;const{id:n,uniqueId:a,type:s,body:l}=o;return e.some((e=>{if(e){let o=e(t,r,n,a,s,l);return void 0!==o&&!0!==o&&(i=o),!0}})),i},lt=async(e,t,r)=>{try{const r=B(),i=b().replace(/-/g,""),n=he(),a=e.body,s=++it;let l=D(a.type);if(!a||"object"==typeof(o=a)&&!Array.isArray(o)&&null!==o&&0===Object.keys(o).length)throw new rt("The request body is required. Please ensure that your Content-Type header is correct (accepted types are application/json and multipart/form-data).",400);let c=V(a.infile||a.options||a.data);if(!c&&!a.svg)throw $(2,`The request with ID ${i} from ${e.headers["x-forwarded-for"]||e.connection.remoteAddress} was incorrect. Payload received: ${JSON.stringify(a)}.`),new rt("No correct chart data found. Ensure that you are using either application/json or multipart/form-data headers. If sending JSON, make sure the chart data is in the 'infile', 'options', or 'data' attribute. If sending SVG, ensure it is in the 'svg' attribute.",400);let p=!1;if(p=st(nt,e,t,{id:s,uniqueId:i,type:l,body:a}),!0!==p)return t.send(p);let h=!1;e.socket.on("close",(()=>{h=!0})),$(4,`[export] Got an incoming HTTP request with ID ${i}.`),a.constr="string"==typeof a.constr&&a.constr||"chart";const u={export:{instr:c,type:l,constr:a.constr[0].toLowerCase()+a.constr.substr(1),height:a.height,width:a.width,scale:a.scale||n.export.scale,globalOptions:V(a.globalOptions,!0),themeOptions:V(a.themeOptions,!0)},customLogic:{allowCodeExecution:Ke,allowFileResources:!1,resources:V(a.resources,!0),callback:a.callback,customCode:a.customCode}};c&&(u.export.instr=q(c,u.customLogic.allowCodeExecution));const d=ue(n,u);if(d.export.options=c,d.payload={svg:a.svg||!1,b64:a.b64||!1,noDownload:a.noDownload||!1,requestId:i},a.svg&&(e=>[/xlink:href="(?:http:\/\/|https:\/\/)?localhost\b/,/xlink:href="(?:http:\/\/|https:\/\/)?10\.\d{1,3}\.\d{1,3}\.\d{1,3}\b/,/xlink:href="(?:http:\/\/|https:\/\/)?127\.\d{1,3}\.\d{1,3}\.\d{1,3}\b/,/xlink:href="(?:http:\/\/|https:\/\/)?172\.(1[6-9]|2[0-9]|3[0-1])\.\d{1,3}\.\d{1,3}\b/,/xlink:href="(?:http:\/\/|https:\/\/)?192\.168\.\d{1,3}\.\d{1,3}\b/].some((t=>t.test(e))))(d.payload.svg))throw new rt("SVG potentially contain at least one forbidden URL in xlink:href element. Please review the SVG content and ensure that all referenced URLs comply with security policies.",400);await Je(d,((o,c)=>{if(e.socket.removeAllListeners("close"),n.server.benchmarking&&$(5,`[benchmark] Request with ID ${i} - After the whole exporting process: ${r()}ms.`),h)return $(3,"[export] The client closed the connection before the chart finished processing.");if(o)throw o;if(!c||!c.result)throw new rt(`Unexpected return from chart generation. Please check your request data. For the request with ID ${i}, the result is ${c.result}.`,400);return l=c.options.export.type,st(at,e,t,{id:s,body:c.result}),c.result?a.b64?"pdf"===l||"svg"==l?t.send(Buffer.from(c.result,"utf8").toString("base64")):t.send(c.result):(t.header("Content-Type",ot[l]||"image/png"),a.noDownload||t.attachment(`${e.params.filename||e.body.filename||"chart"}.${l||"png"}`),"svg"===l?t.send(c.result):t.send(Buffer.from(c.result,"base64"))):void 0}))}catch(e){r(e)}var o};const ct=JSON.parse(n(c(G,"package.json"))),pt=new Date;const ht=S();ht.disable("x-powered-by"),ht.use(R());const ut=k.memoryStorage(),dt=k({storage:ut,limits:{fieldSize:52428800}});ht.use(S.json({limit:52428800})),ht.use(S.urlencoded({extended:!0,limit:52428800})),ht.use(dt.none());const gt=e=>{e.on("clientError",(e=>{j(1,e,`[server] Client error: ${e.message}`)})),e.on("error",(e=>{j(1,e,`[server] Server error: ${e.message}`)})),e.on("connection",(e=>{e.on("error",(e=>{j(1,e,`[server] Socket error: ${e.message}`)}))}))},mt=async e=>{try{if(!e.enable)return!1;if(!e.ssl.force){const t=u.createServer(ht);gt(t),t.listen(e.port,e.host),$(3,`[server] Started HTTP server on ${e.host}:${e.port}.`)}if(e.ssl.enable){let t,r;try{t=await s.readFile(p.join(e.ssl.certPath,"server.key"),"utf8"),r=await s.readFile(p.join(e.ssl.certPath,"server.crt"),"utf8")}catch(t){$(2,`[server] Unable to load key/certificate from the '${e.ssl.certPath}' path. Could not run secured layer server.`)}if(t&&r){const o=d.createServer({key:t,cert:r},ht);gt(o),o.listen(e.ssl.port,e.host),$(3,`[server] Started HTTPS server on ${e.host}:${e.ssl.port}.`)}}e.rateLimiting&&e.rateLimiting.enable&&![0,NaN].includes(e.rateLimiting.maxRequests)&&tt(ht,e.rateLimiting),ht.use(S.static(p.join(G,"public"))),(e=>{!!e&&e.get("/health",((e,t)=>{t.send({status:"OK",bootTime:pt,uptime:Math.floor(((new Date).getTime()-pt.getTime())/1e3/60)+" minutes",version:ct.version,highchartsVersion:ce(),averageProcessingTime:qe(),performedExports:Xe(),failedExports:Fe(),exportAttempts:Ve(),sucessRatio:Xe()/Ve()*100,pool:We()})}))})(ht),(e=>{e.post("/",lt),e.post("/:filename",lt)})(ht),(e=>{!!e&&e.get("/",((e,t)=>{t.sendFile(c(G,"public","index.html"))}))})(ht),(e=>{!!e&&e.post("/version/change/:newVersion",(async(e,t,r)=>{try{const r=Q.HIGHCHARTS_ADMIN_TOKEN;if(!r||!r.length)throw new rt("The server is not configured to perform run-time version changes: HIGHCHARTS_ADMIN_TOKEN is not set.",401);const o=e.get("hc-auth");if(!o||o!==r)throw new rt("Invalid or missing token: Set the token in the hc-auth header.",401);const i=e.params.newVersion;if(!i)throw new rt("No new version supplied.",400);try{await se(i)}catch(e){throw new rt(`Version change: ${e.message}`,e.statusCode).setError(e)}t.status(200).send({statusCode:200,version:ce(),message:`Successfully updated Highcharts to version: ${i}.`})}catch(e){r(e)}}))})(ht),(e=>{e.use(Ze),e.use(et)})(ht)}catch(e){throw new Z("[server] Could not configure and start the server.").setError(e)}};var ft={startServer:mt,enableRateLimiting:e=>tt(ht,e),getExpress:()=>S,getApp:()=>ht,use:(e,...t)=>{ht.use(e,...t)},get:(e,...t)=>{ht.get(e,...t)},post:(e,...t)=>{ht.post(e,...t)}};e.config();var vt={server:ft,startServer:mt,setOptions:(e,t)=>(t?.length&&(pe=function(e){const t=e.findIndex((e=>"loadConfig"===e.replace(/-/g,"")));if(t>-1&&e[t+1]){const r=e[t+1];try{if(r&&r.endsWith(".json"))return JSON.parse(n(r))}catch(e){j(2,e,`[config] Unable to load the configuration from the ${r} file.`)}}return{}}(t)),de(O,pe),pe=ge(O),e&&(pe=ue(pe,e,I)),t?.length&&(pe=function(e,t,r){let o=!1;for(let i=0;i(a.length-1===r&&(s=e[t].type),e[t])),r),a.reduce(((e,r,l)=>(a.length-1===l&&void 0!==e[r]&&(t[++i]?"boolean"===s?e[r]=K(t[i]):"number"===s?e[r]=+t[i]:s.indexOf("]")>=0?e[r]=t[i].split(","):e[r]=t[i]:($(2,`[config] Missing value for the '${n}' argument. Using the default value.`),o=!0)),e[r])),e)}o&&X();return e}(pe,t,O)),pe),initExport:async e=>{var t;return t=e.customLogic&&e.customLogic.allowCodeExecution,Ke=K(t),(e=>{U(e&&parseInt(e.level)),e&&e.dest&&M(e.dest,e.file||"highcharts-export-server.log")})(e.logging),await ne(e.highcharts||{version:"latest"}),await Ue({pool:e.pool||{minWorkers:1,maxWorkers:1},puppeteerArgs:e.puppeteer?.args||[]}),e},singleExport:async e=>{e.export.instr=e.export.instr||e.export.options,await Je(e,(async(e,t)=>{if(e)throw e;const{outfile:r,type:o}=t.options.export;a(r||`chart.${o}`,"svg"!==o?Buffer.from(t.result,"base64"):t.result),await Me()}))},batchExport:async e=>{const t=[];for(let r of e.export.batch.split(";"))r=r.split("="),2===r.length&&t.push(Je({...e,export:{...e.export,infile:r[0],outfile:r[1]}},((e,t)=>{if(e)throw e;a(t.options.export.outfile,Buffer.from(t.result,"base64"))})));try{await Promise.all(t),await Me()}catch(e){throw new Z("[chart] Error encountered during batch export.").setError(e)}},startExport:Je,killPool:Me,log:$,logWithStack:j,setLogLevel:U,enableFileLogging:M,mapToNewConfig:e=>{const t={};for(const[r,o]of Object.entries(e)){const e=C[r]?C[r].split("."):[];e.reduce(((t,r,i)=>t[r]=e.length-1===i?o:t[r]||{}),t)}return t},manualConfig:async e=>{let t={};r(e)&&(t=JSON.parse(n(e,"utf8")));const o=Object.keys(_).map((e=>({title:`${e} options`,value:e})));return v({type:"multiselect",name:"category",message:"Which category do you want to configure?",hint:"Space: Select specific, A: Select all, Enter: Confirm.",instructions:"",choices:o},{onSubmit:async(r,o)=>{let i=0,n=[];for(const e of o)_[e]=_[e].map((t=>({...t,section:e}))),n=[...n,..._[e]];return await v(n,{onSubmit:async(r,o)=>{if("modules"===r.name?(o=o.length?o.map((e=>r.choices[e])):r.choices,t[r.section][r.name]=o):t[r.section]=me(Object.assign({},t[r.section]||{}),r.name.split("."),r.choices?r.choices[o]:o),++i===n.length){try{await s.writeFile(e,JSON.stringify(t,null,2),"utf8")}catch(t){j(1,t,`[config] An error occurred while creating the ${e} file.`)}return!0}}}),!0}})},printLogo:e=>{const t=JSON.parse(n(c(G,"package.json"))).version;e?console.log(`Starting Highcharts Export Server v${t}...`):console.log(n(G+"/msg/startup.msg").toString().bold.yellow,`v${t}`)},printUsage:X};export{vt as default};
+import"colors";import e from"dotenv";import t,{existsSync as r,mkdirSync as o,appendFile as i,readFileSync as n,writeFileSync as s,promises as a}from"fs";import l,{join as c,posix as p}from"path";import{HttpsProxyAgent as h}from"https-proxy-agent";import u from"http";import d from"https";import*as m from"url";import{fileURLToPath as g}from"url";import{z as f}from"zod";import v from"prompts";import{Pool as y}from"tarn";import{v4 as b}from"uuid";import w from"node:path";import E from"puppeteer";import{randomBytes as T}from"node:crypto";import x from"cors";import R from"express";import S from"multer";import k from"express-rate-limit";async function L(e,t={}){return new Promise(((r,o)=>{const i=(e=>e.startsWith("https")?d:u)(e);i.get(e,t,(e=>{let t="";e.on("data",(e=>{t+=e})),e.on("end",(()=>{t||o("Nothing was fetched from the URL."),e.text=t,r(e)}))})).on("error",(e=>{o(e)}))}))}const O={puppeteer:{args:{value:[],type:"string[]",description:"Arguments array to send to Puppeteer."}},highcharts:{version:{value:"latest",envLink:"HIGHCHARTS_VERSION",type:"string",description:"The Highcharts version to be used."},cdnURL:{value:"https://code.highcharts.com/",envLink:"HIGHCHARTS_CDN_URL",type:"string",description:"The CDN URL for Highcharts scripts to be used."},coreScripts:{envLink:"HIGHCHARTS_CORE_SCRIPTS",value:["highcharts","highcharts-more","highcharts-3d"],type:"string[]",description:"The core Highcharts scripts to fetch."},modules:{envLink:"HIGHCHARTS_MODULES",value:["stock","map","gantt","exporting","export-data","parallel-coordinates","accessibility","annotations-advanced","boost-canvas","boost","data","data-tools","draggable-points","static-scale","broken-axis","heatmap","tilemap","tiledwebmap","timeline","treemap","treegraph","item-series","drilldown","histogram-bellcurve","bullet","funnel","funnel3d","geoheatmap","pyramid3d","networkgraph","overlapping-datalabels","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","flowmap"],type:"string[]",description:"The modules of Highcharts to fetch."},indicators:{envLink:"HIGHCHARTS_INDICATORS",value:["indicators-all"],type:"string[]",description:"The indicators of Highcharts 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 optional scripts or dependencies to fetch."},forceFetch:{envLink:"HIGHCHARTS_FORCE_FETCH",value:!1,type:"boolean",description:"The flag to determine whether to refetch all scripts after each server rerun."},cachePath:{envLink:"HIGHCHARTS_CACHE_PATH",value:".cache",type:"string",description:"The path to the cache directory. It is used to store the Highcharts scripts and custom scripts."}},export:{infile:{value:!1,type:"string",description:"The input file should include a name and a type (json or svg). It must be correctly formatted as a JSON or SVG file."},instr:{value:!1,type:"string",description:"Input, provided in the form of a stringified JSON or SVG file, will override the --infile option."},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). This will ignore the --type flag."},type:{envLink:"EXPORT_TYPE",value:"png",type:"string",description:"The file export format. It can be jpeg, png, pdf, or svg."},constr:{envLink:"EXPORT_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 no value is set."},defaultWidth:{envLink:"EXPORT_DEFAULT_WIDTH",value:600,type:"number",description:"The default width of the exported chart. Used when no value is set."},defaultScale:{envLink:"EXPORT_DEFAULT_SCALE",value:1,type:"number",description:"The default scale of the exported chart. Used when no value is set."},height:{type:"number",value:!1,description:"The height of the exported chart, overriding the option in the chart settings."},width:{type:"number",value:!1,description:"The width of the exported chart, overriding the option in the chart settings."},scale:{value:!1,type:"number",description:"The scale of the exported chart, overriding the option in the chart settings. Ranges between 0.1 and 5.0."},globalOptions:{value:!1,type:"string",description:"Either a stringified JSON or a filename containing options to be passed into the Highcharts.setOptions."},themeOptions:{value:!1,type:"string",description:"Either a stringified JSON or a filename containing theme options to be passed into the Highcharts.setOptions."},batch:{value:!1,type:"string",description:'Initiates a batch job with a string containing input/output pairs: "in=out;in=out;...".'},rasterizationTimeout:{envLink:"EXPORT_RASTERIZATION_TIMEOUT",value:1500,type:"number",description:"The duration in milliseconds to wait for rendering a webpage."}},customLogic:{allowCodeExecution:{envLink:"CUSTOM_LOGIC_ALLOW_CODE_EXECUTION",value:!1,type:"boolean",description:"Controls whether the execution of arbitrary code is allowed during the exporting process."},allowFileResources:{envLink:"CUSTOM_LOGIC_ALLOW_FILE_RESOURCES",value:!1,type:"boolean",description:"Controls the ability to inject resources from the filesystem. This setting has no effect when running as a server."},customCode:{value:!1,type:"string",description:"Custom code to execute before chart initialization. It can be a function, code wrapped within a function, or a filename with the .js extension."},callback:{value:!1,type:"string",description:"JavaScript code to run during construction. It can be a function or a filename with the .js extension."},resources:{value:!1,type:"string",description:"Additional resource in the form of a stringified JSON, which may contain files, js, and css sections."},loadConfig:{value:!1,type:"string",legacyName:"fromFile",description:"A file containing a pre-defined configuration to use."},createConfig:{value:!1,type:"string",description:"Enables setting options through a prompt and saving them in a provided config file."}},server:{enable:{envLink:"SERVER_ENABLE",value:!1,type:"boolean",cliName:"enableServer",description:"When set to true, the server starts on the local IP address 0.0.0.0."},host:{envLink:"SERVER_HOST",value:"0.0.0.0",type:"string",description:"The hostname of the server. Additionally, it starts a server on the provided hostname."},port:{envLink:"SERVER_PORT",value:7801,type:"number",description:"The server port when enabled."},benchmarking:{envLink:"SERVER_BENCHMARKING",value:!1,type:"boolean",cliName:"serverBenchmarking",description:"Indicates whether to display the duration, in milliseconds, of specific actions that occur on the server while serving a request."},ssl:{enable:{envLink:"SERVER_SSL_ENABLE",value:!1,type:"boolean",cliName:"enableSsl",description:"Enables or disables the SSL protocol."},force:{envLink:"SERVER_SSL_FORCE",value:!1,type:"boolean",cliName:"sslForced",legacyName:"sslOnly",description:"When set to true, the server is forced to serve only over HTTPS."},port:{envLink:"SERVER_SSL_PORT",value:443,type:"number",cliName:"sslPort",description:"The port on which to run the SSL server."},certPath:{envLink:"SERVER_SSL_CERT_PATH",value:"",type:"string",legacyName:"sslPath",description:"The path to the SSL certificate/key file."}},rateLimiting:{enable:{envLink:"SERVER_RATE_LIMITING_ENABLE",value:!1,type:"boolean",cliName:"enableRateLimiting",description:"Enables rate limiting for the server."},maxRequests:{envLink:"SERVER_RATE_LIMITING_MAX_REQUESTS",value:10,type:"number",legacyName:"rateLimit",description:"The maximum number of requests allowed in one minute."},window:{envLink:"SERVER_RATE_LIMITING_WINDOW",value:1,type:"number",description:"The time window, in minutes, for the rate limiting."},delay:{envLink:"SERVER_RATE_LIMITING_DELAY",value:0,type:"number",description:"The delay duration for each successive request before reaching the maximum limit."},trustProxy:{envLink:"SERVER_RATE_LIMITING_TRUST_PROXY",value:!1,type:"boolean",description:"Set this to true if the server is behind a load balancer."},skipKey:{envLink:"SERVER_RATE_LIMITING_SKIP_KEY",value:"",type:"string",description:"Allows bypassing the rate limiter and should be provided with the skipToken argument."},skipToken:{envLink:"SERVER_RATE_LIMITING_SKIP_TOKEN",value:"",type:"string",description:"Allows bypassing the rate limiter and should be provided with the skipKey argument."}}},pool:{minWorkers:{envLink:"POOL_MIN_WORKERS",value:4,type:"number",description:"The number of minimum and initial pool workers to spawn."},maxWorkers:{envLink:"POOL_MAX_WORKERS",value:8,type:"number",legacyName:"workers",description:"The number of maximum pool workers to spawn."},workLimit:{envLink:"POOL_WORK_LIMIT",value:40,type:"number",description:"The number of work pieces that can be performed before restarting the worker process."},acquireTimeout:{envLink:"POOL_ACQUIRE_TIMEOUT",value:5e3,type:"number",description:"The duration, in milliseconds, to wait for acquiring a resource."},createTimeout:{envLink:"POOL_CREATE_TIMEOUT",value:5e3,type:"number",description:"The duration, in milliseconds, to wait for creating a resource."},destroyTimeout:{envLink:"POOL_DESTROY_TIMEOUT",value:5e3,type:"number",description:"The duration, in milliseconds, to wait for destroying a resource."},idleTimeout:{envLink:"POOL_IDLE_TIMEOUT",value:3e4,type:"number",description:"The duration, in milliseconds, after which an idle resource is destroyed."},createRetryInterval:{envLink:"POOL_CREATE_RETRY_INTERVAL",value:200,type:"number",description:"The duration, in milliseconds, to wait before retrying the create process in case of a failure."},reaperInterval:{envLink:"POOL_REAPER_INTERVAL",value:1e3,type:"number",description:"The duration, in milliseconds, after which the check for idle resources to destroy is triggered."},benchmarking:{envLink:"POOL_BENCHMARKING",value:!1,type:"boolean",cliName:"poolBenchmarking",description:"Indicate whether to show statistics for the pool of resources or not."},listenToProcessExits:{envLink:"POOL_LISTEN_TO_PROCESS_EXITS",value:!0,type:"boolean",description:"Decides whether or not to attach process.exit handlers."}},logging:{level:{envLink:"LOGGING_LEVEL",value:4,type:"number",cliName:"logLevel",description:"The logging level to be used."},file:{envLink:"LOGGING_FILE",value:"highcharts-export-server.log",type:"string",cliName:"logFile",description:"The name of a log file. The logDest option also needs to be set to enable file logging."},dest:{envLink:"LOGGING_DEST",value:"log/",type:"string",cliName:"logDest",description:"The path to store log files. This also enables file logging."}},ui:{enable:{envLink:"UI_ENABLE",value:!1,type:"boolean",cliName:"enableUi",description:"Enables or disables the user interface (UI) for the export server."},route:{envLink:"UI_ROUTE",value:"/",type:"string",cliName:"uiRoute",description:"The endpoint route to which the user interface (UI) should be attached."}},other:{noLogo:{envLink:"OTHER_NO_LOGO",value:!1,type:"boolean",description:"Skip printing the logo on a startup. Will be replaced by a simple text."}}},_={puppeteer:[{type:"list",name:"args",message:"Puppeteer arguments",initial:O.puppeteer.args.value.join(","),separator:","}],highcharts:[{type:"text",name:"version",message:"Highcharts version",initial:O.highcharts.version.value},{type:"text",name:"cdnURL",message:"The URL of CDN",initial:O.highcharts.cdnURL.value},{type:"multiselect",name:"modules",message:"Available modules",instructions:"Space: Select specific, A: Select all, Enter: Confirm.",choices:O.highcharts.modules.value},{type:"list",name:"scripts",message:"Custom scripts",initial:O.highcharts.scripts.value.join(","),separator:","},{type:"toggle",name:"forceFetch",message:"Force re-fetch the scripts",initial:O.highcharts.forceFetch.value},{type:"text",name:"cachePath",message:"The path to the cache directory",initial:O.highcharts.cachePath.value}],export:[{type:"select",name:"type",message:"The default export file type",hint:`Default: ${O.export.type.value}`,initial:0,choices:["png","jpeg","pdf","svg"]},{type:"select",name:"constr",message:"The default constructor for Highcharts",hint:`Default: ${O.export.constr.value}`,initial:0,choices:["chart","stockChart","mapChart","ganttChart"]},{type:"number",name:"defaultHeight",message:"The default fallback height of the exported chart",initial:O.export.defaultHeight.value},{type:"number",name:"defaultWidth",message:"The default fallback width of the exported chart",initial:O.export.defaultWidth.value},{type:"number",name:"defaultScale",message:"The default fallback scale of the exported chart",initial:O.export.defaultScale.value,min:.1,max:5},{type:"number",name:"rasterizationTimeout",message:"The rendering webpage timeout in milliseconds",initial:O.export.rasterizationTimeout.value}],customLogic:[{type:"toggle",name:"allowCodeExecution",message:"Enable execution of custom code",initial:O.customLogic.allowCodeExecution.value},{type:"toggle",name:"allowFileResources",message:"Enable file resources",initial:O.customLogic.allowFileResources.value}],server:[{type:"toggle",name:"enable",message:"Starts the server on 0.0.0.0",initial:O.server.enable.value},{type:"text",name:"host",message:"Server hostname",initial:O.server.host.value},{type:"number",name:"port",message:"Server port",initial:O.server.port.value},{type:"toggle",name:"benchmarking",message:"Enable server benchmarking",initial:O.server.benchmarking.value},{type:"toggle",name:"ssl.enable",message:"Enable SSL protocol",initial:O.server.ssl.enable.value},{type:"toggle",name:"ssl.force",message:"Force serving only over HTTPS",initial:O.server.ssl.force.value},{type:"number",name:"ssl.port",message:"SSL server port",initial:O.server.ssl.port.value},{type:"text",name:"ssl.certPath",message:"The path to find the SSL certificate/key",initial:O.server.ssl.certPath.value},{type:"toggle",name:"rateLimiting.enable",message:"Enable rate limiting",initial:O.server.rateLimiting.enable.value},{type:"number",name:"rateLimiting.maxRequests",message:"The maximum requests allowed per minute",initial:O.server.rateLimiting.maxRequests.value},{type:"number",name:"rateLimiting.window",message:"The rate-limiting time window in minutes",initial:O.server.rateLimiting.window.value},{type:"number",name:"rateLimiting.delay",message:"The delay for each successive request before reaching the maximum",initial:O.server.rateLimiting.delay.value},{type:"toggle",name:"rateLimiting.trustProxy",message:"Set to true if behind a load balancer",initial:O.server.rateLimiting.trustProxy.value},{type:"text",name:"rateLimiting.skipKey",message:"Allows bypassing the rate limiter when provided with the skipToken argument",initial:O.server.rateLimiting.skipKey.value},{type:"text",name:"rateLimiting.skipToken",message:"Allows bypassing the rate limiter when provided with the skipKey argument",initial:O.server.rateLimiting.skipToken.value}],pool:[{type:"number",name:"minWorkers",message:"The initial number of workers to spawn",initial:O.pool.minWorkers.value},{type:"number",name:"maxWorkers",message:"The maximum number of workers to spawn",initial:O.pool.maxWorkers.value},{type:"number",name:"workLimit",message:"The pieces of work that can be performed before restarting a Puppeteer process",initial:O.pool.workLimit.value},{type:"number",name:"acquireTimeout",message:"The number of milliseconds to wait for acquiring a resource",initial:O.pool.acquireTimeout.value},{type:"number",name:"createTimeout",message:"The number of milliseconds to wait for creating a resource",initial:O.pool.createTimeout.value},{type:"number",name:"destroyTimeout",message:"The number of milliseconds to wait for destroying a resource",initial:O.pool.destroyTimeout.value},{type:"number",name:"idleTimeout",message:"The number of milliseconds after an idle resource is destroyed",initial:O.pool.idleTimeout.value},{type:"number",name:"createRetryInterval",message:"The retry interval in milliseconds after a create process fails",initial:O.pool.createRetryInterval.value},{type:"number",name:"reaperInterval",message:"The reaper interval in milliseconds after triggering the check for idle resources to destroy",initial:O.pool.reaperInterval.value},{type:"toggle",name:"benchmarking",message:"Enable benchmarking for a resource pool",initial:O.pool.benchmarking.value},{type:"toggle",name:"listenToProcessExits",message:"Set to false to skip attaching process.exit handlers",initial:O.pool.listenToProcessExits.value}],logging:[{type:"number",name:"level",message:"The log level (0: silent, 1: error, 2: warning, 3: notice, 4: verbose, 5: benchmark)",initial:O.logging.level.value,round:0,min:0,max:5},{type:"text",name:"file",message:"A log file name. Set with the --logDest to enable file logging",initial:O.logging.file.value},{type:"text",name:"dest",message:"The path to log files. Enables file logging",initial:O.logging.dest.value}],ui:[{type:"toggle",name:"enable",message:"Enable UI for the export server",initial:O.ui.enable.value},{type:"text",name:"route",message:"A route to attach the UI",initial:O.ui.route.value}],other:[{type:"toggle",name:"noLogo",message:"Skip printing the logo on startup. Replaced by simple text",initial:O.other.noLogo.value}]},I=["options","globalOptions","themeOptions","resources","payload"],C={},A=(e,t="")=>{Object.keys(e).forEach((r=>{if(!["puppeteer","highcharts"].includes(r)){const o=e[r];void 0===o.value?A(o,`${t}.${r}`):(C[o.cliName||r]=`${t}.${r}`.substring(1),void 0!==o.legacyName&&(C[o.legacyName]=`${t}.${r}`.substring(1)))}}))};A(O);const N=["red","yellow","blue","gray","green"];let P={toConsole:!0,toFile:!1,pathCreated:!1,levelsDesc:[{title:"error",color:N[0]},{title:"warning",color:N[1]},{title:"notice",color:N[2]},{title:"verbose",color:N[3]},{title:"benchmark",color:N[4]}],listeners:[]};for(const[e,t]of Object.entries(O.logging))P[e]=t.value;const $=(e,t)=>{P.toFile&&(P.pathCreated||(!r(P.dest)&&o(P.dest),P.pathCreated=!0),i(`${P.dest}${P.file}`,[t].concat(e).join(" ")+"\n",(e=>{e&&(console.log(`[logger] Unable to write to log file: ${e}`),P.toFile=!1)})))},H=(...e)=>{const[t,...r]=e,{level:o,levelsDesc:i}=P;if(5!==t&&(0===t||t>o||o>i.length))return;const n=`${(new Date).toString().split("(")[0].trim()} [${i[t-1].title}] -`;P.listeners.forEach((e=>{e(n,r.join(" "))})),P.toConsole&&console.log.apply(void 0,[n.toString()[P.levelsDesc[t-1].color]].concat(r)),$(r,n)},j=(e,t,r)=>{const o=r||t.message,{level:i,levelsDesc:n}=P;if(0===e||e>i||i>n.length)return;const s=`${(new Date).toString().split("(")[0].trim()} [${n[e-1].title}] -`,a=t.message!==t.stackMessage||void 0===t.stackMessage?t.stack:t.stack.split("\n").slice(1).join("\n"),l=[o,"\n",a];P.toConsole&&console.log.apply(void 0,[s.toString()[P.levelsDesc[e-1].color]].concat([o[N[e-1]],"\n",a])),P.listeners.forEach((e=>{e(s,l.join(" "))})),$(l,s)},U=e=>{e>=0&&e<=P.levelsDesc.length&&(P.level=e)},G=(e,t)=>{if(P={...P,dest:e||P.dest,file:t||P.file,toFile:!0},0===P.dest.length)return H(1,"[logger] File logging initialization: no path supplied.");P.dest.endsWith("/")||(P.dest+="/")},M=g(new URL("../.",import.meta.url)),D=(e,t)=>{const r=["png","jpeg","pdf","svg"];if(t){const o=t.split(".").pop();"jpg"===o?e="jpeg":r.includes(o)&&e!==o&&(e=o)}return{"image/png":"png","image/jpeg":"jpeg","application/pdf":"pdf","image/svg+xml":"svg"}[e]||r.find((t=>t===e))||"png"},W=(e=!1,t)=>{const r=["js","css","files"];let o=e,i=!1;if(t&&e.endsWith(".json"))try{o=V(n(e,"utf8"))}catch(e){return j(2,e,"[cli] No resources found.")}else o=V(e),o&&!t&&delete o.files;for(const e in o)r.includes(e)?i||(i=!0):delete o[e];return i?(o.files&&(o.files=o.files.map((e=>e.trim())),(!o.files||o.files.length<=0)&&delete o.files),o):H(3,"[cli] No resources found.")};function V(e,t){try{const r=JSON.parse("string"!=typeof e?JSON.stringify(e):e);return"string"!=typeof r&&t?JSON.stringify(r):r}catch{return!1}}const F=e=>{if(null===e||"object"!=typeof e)return e;const t=Array.isArray(e)?[]:{};for(const r in e)Object.prototype.hasOwnProperty.call(e,r)&&(t[r]=F(e[r]));return t},q=(e,t)=>JSON.stringify(e,((e,r)=>("string"==typeof r&&((r=r.trim()).startsWith("function(")||r.startsWith("function ("))&&r.endsWith("}")&&(r=t?`EXP_FUN${(r+"").replaceAll(/\n|\t|\r/g," ")}EXP_FUN`:void 0),"function"==typeof r?`EXP_FUN${(r+"").replaceAll(/\n|\t|\r/g," ")}EXP_FUN`:r))).replaceAll(/"EXP_FUN|EXP_FUN"/g,"");function X(){console.log("\nUsage of CLI arguments:".bold,"\n------",`\nFor more detailed information, visit the readme at: ${"https://github.com/highcharts/node-export-server#readme".bold.yellow}.`);const e=t=>{for(const[r,o]of Object.entries(t))if(Object.prototype.hasOwnProperty.call(o,"value")){let e=` --${o.cliName||r} ${("<"+o.type+">").green} `;if(e.length<48)for(let t=e.length;t<48;t++)e+=".";console.log(e,o.description,`[Default: ${o.value.toString().bold}]`.blue)}else e(o)};Object.keys(O).forEach((t=>{["puppeteer","highcharts"].includes(t)||(console.log(`\n${t.toUpperCase()}`.red),e(O[t]))})),console.log("\n")}const K=e=>!["false","undefined","null","NaN","0",""].includes(e)&&!!e,J=(e,t)=>{if(e&&"string"==typeof e)return(e=e.trim()).endsWith(".js")?!!t&&J(n(e,"utf8")):e.startsWith("function()")||e.startsWith("function ()")||e.startsWith("()=>")||e.startsWith("() =>")?`(${e})()`:e.replace(/;$/,"")},B=()=>{const e=process.hrtime.bigint();return()=>Number(process.hrtime.bigint()-e)/1e6};e.config();const z=()=>f.enum(["true","false"]).transform((e=>"true"===e)).optional(),Y=()=>f.string().transform((e=>e.split(",").map((e=>e.trim())))).optional(),Q=f.object({HIGHCHARTS_VERSION:f.string().refine((e=>/^(latest|\d+(\.\d+){0,2})$/.test(e)),{message:"HIGHCHARTS_VERSION must be 'latest', a major version, or in the form XX.YY.ZZ"}).optional(),HIGHCHARTS_CDN_URL:f.string().trim().refine((e=>e.startsWith("https://")||e.startsWith("http://")),{message:"Invalid value for HIGHCHARTS_CDN_URL. It should start with http:// or https://."}).optional(),HIGHCHARTS_CORE_SCRIPTS:Y(),HIGHCHARTS_MODULES:Y(),HIGHCHARTS_INDICATORS:Y(),HIGHCHARTS_FORCE_FETCH:z(),HIGHCHARTS_CACHE_PATH:f.string().optional(),HIGHCHARTS_ADMIN_TOKEN:f.string().optional(),EXPORT_TYPE:f.enum(["jpeg","png","pdf","svg"]).optional(),EXPORT_CONSTR:f.string().refine((e=>["chart","stockChart","mapChart","ganttChart"].includes(e||"")),{message:"Invalid value for EXPORT_CONSTR. "}).optional(),EXPORT_DEFAULT_HEIGHT:f.coerce.number().positive().optional(),EXPORT_DEFAULT_WIDTH:f.coerce.number().positive().optional(),EXPORT_DEFAULT_SCALE:f.coerce.number().positive().optional(),EXPORT_RASTERIZATION_TIMEOUT:f.coerce.number().positive().optional(),CUSTOM_LOGIC_ALLOW_CODE_EXECUTION:z(),CUSTOM_LOGIC_ALLOW_FILEL_RESOURCES:z(),SERVER_ENABLE:z(),SERVER_HOST:f.string().optional(),SERVER_PORT:f.coerce.number().optional(),SERVER_BENCHMARKING:z(),SERVER_SSL_ENABLE:z(),SERVER_SSL_FORCE:z(),SERVER_SSL_PORT:f.coerce.number().optional(),SERVER_SSL_CERT_PATH:f.string().optional(),SERVER_RATE_LIMITING_ENABLE:z(),SERVER_RATE_LIMITING_MAX_REQUESTS:f.coerce.number().optional(),SERVER_RATE_LIMITING_WINDOW:f.coerce.number().optional(),SERVER_RATE_LIMITING_DELAY:f.coerce.number().optional(),SERVER_RATE_LIMITING_TRUST_PROXY:z(),SERVER_RATE_LIMITING_SKIP_KEY:f.string().optional(),SERVER_RATE_LIMITING_SKIP_TOKEN:f.string().optional(),POOL_MIN_WORKERS:f.coerce.number().optional(),POOL_MAX_WORKERS:f.coerce.number().optional(),POOL_WORK_LIMIT:f.coerce.number().optional(),POOL_ACQUIRE_TIMEOUT:f.coerce.number().optional(),POOL_CREATE_TIMEOUT:f.coerce.number().optional(),POOL_DESTROY_TIMEOUT:f.coerce.number().optional(),POOL_IDLE_TIMEOUT:f.coerce.number().optional(),POOL_CREATE_RETRY_INTERVAL:f.coerce.number().optional(),POOL_REAPER_INTERVAL:f.coerce.number().optional(),POOL_BENCHMARKING:z(),POOL_LISTEN_TO_PROCESS_EXITS:z(),LOGGING_LEVEL:f.coerce.number().optional().refine((e=>(e||5)>=0&&(e||5)<=5),{message:"Invalid value for LOGGING_LEVEL. We only accept 0, 1, 2, 3, 4, 5 as logging levels."}),LOGGING_FILE:f.string().optional(),LOGGING_DEST:f.string().optional(),UI_ENABLE:z(),UI_ROUTE:f.string().optional(),OTHER_NO_LOGO:z(),NODE_ENV:f.enum(["development","production","test"]).optional().default("production"),PROXY_SERVER_TIMEOUT:f.coerce.number().positive().optional().default(5e3),PROXY_SERVER_HOST:f.string().optional().default("localhost"),PROXY_SERVER_PORT:f.coerce.number().positive().optional().default(8080)}).parse(process.env);class Z extends Error{constructor(e){super(),this.message=e,this.stackMessage=e}setError(e){return this.error=e,e.name&&(this.name=e.name),e.statusCode&&(this.statusCode=e.statusCode),e.stack&&(this.stackMessage=e.message,this.stack=e.stack),this}}const ee={cdnURL:"https://code.highcharts.com/",activeManifest:{},sources:"",hcVersion:""};let te=!1;const re=e=>e.sources.substring(0,e.sources.indexOf("*/")).replace("/*","").replace("*/","").replace(/\n/g,"").trim(),oe=async(e,t,r,o=!1)=>{e.endsWith(".js")&&(e=e.substring(0,e.length-3)),H(4,`[cache] Fetching script - ${e}.js`);const i=t?{agent:t,timeout:Q.PROXY_SERVER_TIMEOUT}:{},n=await L(`${e}.js`,i);if(200===n.statusCode&&"string"==typeof n.text){if(r){r[e.replace(/(.*)\/|(.*)modules\/|stock\/(.*)indicators\/|maps\/(.*)modules\//gi,"")]=1}return n.text}if(o)throw new Z(`Could not fetch the ${e}.js. The script might not exist in the requested version (status code: ${n.statusCode}).`).setError(n);return H(2,`[cache] Could not fetch the ${e}.js. The script might not exist in the requested version.`),""},ie=async(e,t)=>{const{coreScripts:r,modules:o,indicators:i,scripts:n}=e,a="latest"!==e.version&&e.version?`${e.version}/`:"";let l;H(3,`[cache] Updating cache version to Highcharts: ${a||"latest"}.`);const c=process.env.PROXY_SERVER_HOST,p=process.env.PROXY_SERVER_PORT;if(c&&p)try{l=new h({host:c,port:+p})}catch(e){throw new Z("[cache] Could not create a Proxy Agent.").setError(e)}const u={};try{return ee.sources=await(async(e,t,r,o,i,n)=>{const s=[...e.map((e=>oe(`${o}${e}`,i,n,!0))),...t.map((e=>oe(`${o}${e}`,i,n))),...r.map((e=>oe(`${e}`,i)))];return(await Promise.all(s)).join(";\n")})([...r.map((e=>`${a}${e}`))],[...o.map((e=>"map"===e?`maps/${a}modules/${e}`:`${a}modules/${e}`)),...i.map((e=>`stock/${a}indicators/${e}`))],n,e.cdnURL||ee.cdnURL,l,u),ee.hcVersion=re(ee),s(t,ee.sources),u}catch(e){throw new Z("[cache] Unable to update the local Highcharts cache.").setError(e)}},ne=async e=>{const t=c(M,e.cachePath);let i;const a=c(t,"manifest.json"),l=c(t,"sources.js");if(te=e,!r(t)&&o(t),!r(a)||e.forceFetch)H(3,"[cache] Fetching and caching Highcharts dependencies."),i=await ie(e,l);else{let t=!1;const r=JSON.parse(n(a));if(r.modules&&Array.isArray(r.modules)){const e={};r.modules.forEach((t=>e[t]=1)),r.modules=e}const{modules:o,coreScripts:s,indicators:c}=e,p=o.length+s.length+c.length;r.version!==e.version?(H(2,"[cache] A Highcharts version mismatch in the cache, need to re-fetch."),t=!0):Object.keys(r.modules||{}).length!==p?(H(2,"[cache] The cache and the requested modules do not match, need to re-fetch."),t=!0):t=(e.modules||[]).some((e=>{if(!r.modules[e])return H(2,`[cache] The ${e} is missing in the cache, need to re-fetch.`),!0})),t?i=await ie(e,l):(H(3,"[cache] Dependency cache is up to date, proceeding."),ee.sources=n(l,"utf8"),i=r.modules,ee.hcVersion=re(ee))}await(async(e,t)=>{const r={version:e.version,modules:t||{}};ee.activeManifest=r,H(3,"[cache] Writing a new manifest.");try{s(c(M,e.cachePath,"manifest.json"),JSON.stringify(r),"utf8")}catch(e){throw new Z("[cache] Error writing the cache manifest.").setError(e)}})(e,i)},se=()=>c(M,te.cachePath);var ae=async e=>!!te&&await ne(Object.assign(te,{version:e})),le=()=>ee,ce=()=>ee.hcVersion;let pe={};const he=()=>pe,ue=(e,t,r=[])=>{const o=F(e);for(const[e,n]of Object.entries(t))o[e]="object"!=typeof(i=n)||Array.isArray(i)||null===i||r.includes(e)||void 0===o[e]?void 0!==n?n:o[e]:ue(o[e],n,r);var i;return o};function de(e,t={},r=""){Object.keys(e).forEach((o=>{const i=e[o],n=t&&t[o];void 0===i.value?de(i,n,`${r}.${o}`):(void 0!==n&&(i.value=n),i.envLink in Q&&(i.value=Q[i.envLink]))}))}function me(e){let t={};for(const[r,o]of Object.entries(e))t[r]=Object.prototype.hasOwnProperty.call(o,"value")?o.value:me(o);return t}function ge(e,t,r){for(;t.length>1;){const o=t.shift();return Object.prototype.hasOwnProperty.call(e,o)||(e[o]={}),e[o]=ge(Object.assign({},e[o]),t,r),e}return e[t[0]]=r,e}const fe=T(64).toString("base64url"),ve=w.join("tmp",`puppeteer-${fe}`),ye=[`--user-data-dir=${w.join(ve,"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"],be=m.fileURLToPath(new URL(".",import.meta.url)),we=t.readFileSync(be+"/../templates/template.html","utf8");let Ee;const Te=async e=>{await e.setContent(we),await e.addScriptTag({path:`${se()}/sources.js`}),await e.evaluate((()=>window.setupHighcharts())),e.on("pageerror",(async t=>{await e.$eval("#container",((e,t)=>{window._displayErrors&&(e.innerHTML=t)}),`Chart input data error ${t.toString()}`)}))},xe=async(e,t=!1)=>{try{t?(await e.goto("about:blank"),await Te(e)):await e.evaluate((()=>{document.body.innerHTML=''}))}catch(e){j(2,e,"[browser] Could not clear the content of the page.")}},Re=async()=>{if(!Ee)return!1;const e=await Ee.newPage();return await e.setCacheEnabled(!1),await Te(e),e},Se=async()=>(Ee?.isConnected()&&(await Ee.close(),H(4,"[browser] Closed the browser.")),!0);const ke=m.fileURLToPath(new URL(".",import.meta.url)),Le=(e,t,r)=>e.evaluate(((e,t)=>window.triggerExport(e,t)),t,r);var Oe=async(e,t,r)=>{const o=[],i=async e=>{for(const e of o)await e.dispose();await e.evaluate((()=>{const[,...e]=document.getElementsByTagName("script"),[,...t]=document.getElementsByTagName("style"),[...r]=document.getElementsByTagName("link");for(const o of[...e,...t,...r])o.remove()}))};try{H(4,"[export] Determining export path.");const s=r.export;await e.evaluate((()=>requestAnimationFrame((()=>{}))));const a=s?.options?.chart?.displayErrors&&le().activeManifest.modules.debugger;let c;if(await e.evaluate((e=>window._displayErrors=e),a),t.indexOf&&(t.indexOf("=0||t.indexOf("=0)){if(H(4,"[export] Treating as SVG."),"svg"===s.type)return t;c=!0,await e.setContent((e=>`\n\n\n \n \n Highcarts Export \n \n \n \n \n ${e}\n
\n \n\n\n`)(t))}else H(4,"[export] Treating as config."),s.strInj?await Le(e,{chart:{height:s.height,width:s.width}},r):(t.chart.height=s.height,t.chart.width=s.width,await Le(e,t,r));const p=r.customLogic.resources;if(p){if(p.js&&o.push(await e.addScriptTag({content:p.js})),p.files)for(const t of p.files)try{const r=!t.startsWith("http");o.push(await e.addScriptTag(r?{content:n(t,"utf8")}:{url:t}))}catch(e){j(2,e,`[export] The JS file ${t} cannot be loaded.`)}if(p.css){let t=p.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")?o.push(await e.addStyleTag({url:i})):r.customLogic.allowFileResources&&o.push(await e.addStyleTag({path:l.join(ke,i)})));o.push(await e.addStyleTag({content:p.css.replace(/@import\s*([^;]*);/g,"")||" "}))}}const h=c?await e.$eval("#chart-container svg:first-of-type",((e,t)=>({chartHeight:e.height.baseVal.value*t,chartWidth:e.width.baseVal.value*t})),parseFloat(s.scale)):await e.evaluate((()=>{const{chartHeight:e,chartWidth:t}=window.Highcharts.charts[0];return{chartHeight:e,chartWidth:t}})),u=Math.ceil(h?.chartHeight||s.height),d=Math.ceil(h?.chartWidth||s.width);await e.setViewport({height:u,width:d,deviceScaleFactor:c?1:parseFloat(s.scale)});const m=c?e=>{document.body.style.zoom=e,document.body.style.margin="0px"}:()=>{document.body.style.zoom=1};await e.evaluate(m,parseFloat(s.scale));const{height:g,width:f,x:v,y:y}=await(e=>e.$eval("#chart-container",(e=>{const{x:t,y:r,width:o,height:i}=e.getBoundingClientRect();return{x:t,y:r,width:o,height:Math.trunc(i>1?i:500)}})))(e);let b;if(c||await e.setViewport({width:Math.round(f),height:Math.round(g),deviceScaleFactor:parseFloat(s.scale)}),"svg"===s.type)b=await(e=>e.$eval("#container svg:first-of-type",(e=>e.outerHTML)))(e);else if(["png","jpeg"].includes(s.type))b=await((e,t,r,o,i)=>Promise.race([e.screenshot({type:t,encoding:r,clip:o,omitBackground:"png"==t}),new Promise(((e,t)=>setTimeout((()=>t(new Z("Rasterization timeout"))),i||1500)))]))(e,s.type,"base64",{width:d,height:u,x:v,y:y},s.rasterizationTimeout);else{if("pdf"!==s.type)throw new Z(`[export] Unsupported output format ${s.type}.`);b=await((e,t,r,o)=>e.pdf({height:t+1,width:r,encoding:o}))(e,u,d,"base64")}return await e.evaluate((()=>{if("undefined"!=typeof Highcharts){const e=Highcharts.charts;if(Array.isArray(e)&&e.length)for(const t of e)t&&t.destroy(),Highcharts.charts.shift()}})),await i(e),b}catch(t){return await i(e),t}};const _e={performedExports:0,exportAttempts:0,exportFromSvgAttempts:0,timeSpent:0,droppedExports:0,spentAverage:0};let Ie,Ce={},Ae=!1;const Ne={create:async()=>{let e=!1;const t=b(),r=(new Date).getTime();try{if(e=await Re(),!e||e.isClosed())throw new Z("The page is invalid or closed.");H(3,`[pool] Successfully created a worker ${t} - took ${(new Date).getTime()-r} ms.`)}catch(e){throw new Z("Error encountered when creating a new page.").setError(e)}return{id:t,page:e,workCount:Math.round(Math.random()*(Ce.workLimit/2))}},validate:async e=>Ce.workLimit&&++e.workCount>Ce.workLimit?(H(3,`[pool] Worker failed validation: exceeded work limit (limit is ${Ce.workLimit}).`),!1):(await xe(e.page,!0),!0),destroy:e=>{H(3,`[pool] Destroying pool entry ${e.id}.`),e.page&&e.page.close()}},Pe=async e=>{if(Ce=e&&e.pool?{...e.pool}:{},Ce.listenToProcessExits&&(H(3,"[pool] Attaching exit listeners to the process."),process.on("exit",(async e=>{H(4,`Process exited with code ${e}.`),await $e()})),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)=>{j(1,e,`The ${t} error.`),await $e(),process.exit(1)}))),Ie=e.puppeteerArgs,await(async e=>{const t=[...ye,...e||[]];if(!Ee){let e=0;const r=async()=>{try{H(3,`[browser] Attempting to get a browser instance (try ${++e}).`),Ee=await E.launch({headless:"new",args:t,userDataDir:"./tmp/"})}catch(t){if(j(1,t,"[browser] Failed to launch a browser instance."),!(e<25))throw t;H(3,`[browser] Retry to open a browser (${e} out of 25).`),await new Promise((e=>setTimeout(e,4e3))),await r()}};try{await r()}catch(e){throw new Z("[browser] Maximum retries to open a browser instance reached.").setError(e)}if(!Ee)throw new Z("[browser] Cannot find a browser to open.")}return Ee})(Ie),H(3,`[pool] Initializing pool with workers: min ${Ce.minWorkers}, max ${Ce.maxWorkers}.`),Ae)return H(4,"[pool] Already initialized, please kill it before creating a new one.");parseInt(Ce.minWorkers)>parseInt(Ce.maxWorkers)&&(Ce.minWorkers=Ce.maxWorkers);try{Ae=new y({...Ne,min:parseInt(Ce.minWorkers),max:parseInt(Ce.maxWorkers),acquireTimeoutMillis:Ce.acquireTimeout,createTimeoutMillis:Ce.createTimeout,destroyTimeoutMillis:Ce.destroyTimeout,idleTimeoutMillis:Ce.idleTimeout,createRetryIntervalMillis:Ce.createRetryInterval,reapIntervalMillis:Ce.reaperInterval,propagateCreateError:!1}),Ae.on("release",(async e=>{await xe(e.page,!1),H(4,`[pool] Releasing a worker with ID ${e.id}.`)})),Ae.on("destroySuccess",((e,t)=>{H(4,`[pool] Destroyed a worker with ID ${t.id}.`)}));const e=[];for(let t=0;t{Ae.release(e)})),H(3,"[pool] The pool is ready"+(e.length?` with ${e.length} initial resources waiting.`:"."))}catch(e){throw await Se(),new Z("[pool] Could not create the pool of workers.").setError(e)}};async function $e(){return H(3,"[pool] Killing all pool workers and browser, if any exist."),Ae?.destroyed||Ae&&(await Ae.destroy(),H(4,"[browser] Destroyed the pool of resources.")),Se()}const He=async(e,t)=>{let r;try{if(H(4,"[pool] Work received, starting to process."),++_e.exportAttempts,Ce.benchmarking&&je(),!Ae)throw new Z("Work received, but pool has not been started.");try{H(4,"[pool] Acquiring a worker handle.");const e=B();r=await Ae.acquire().promise,t.server.benchmarking&&H(5,t.payload?.requestId?`[benchmark] Request with ID ${t.payload?.requestId} -`:"[benchmark]",`Acquired a worker handle: ${e()}ms.`)}catch(e){throw new Z("Error encountered when acquiring an available entry.").setError(e)}if(H(4,"[pool] Acquired a worker handle."),!r.page)throw new Z("Resolved worker page is invalid: the pool setup is wonky.");let o=(new Date).getTime();H(4,`[pool] Starting work on pool entry with ID ${r.id}.`);const i=B(),n=await Oe(r.page,e,t);if(n instanceof Error)throw"Rasterization timeout"===n.message&&(r.page.close(),r.page=await Re()),new Z("Error encountered during export.").setError(n);t.server.benchmarking&&H(5,t.payload?.requestId?`[benchmark] Request with ID ${t.payload?.requestId} -`:"[benchmark]",`Exported a chart sucessfully: ${i()}ms.`),Ae.release(r);const s=(new Date).getTime()-o;return _e.timeSpent+=s,_e.spentAverage=_e.timeSpent/++_e.performedExports,H(4,`[pool] Work completed in ${s} ms.`),{result:n,options:t}}catch(e){throw++_e.droppedExports,r&&Ae.release(r),new Z(`[pool] In pool.postWork: ${e.message}`).setError(e)}};function je(){const{min:e,max:t}=Ae;H(5,`[pool] The minimum number of resources allowed by pool: ${e}.`),H(5,`[pool] The maximum number of resources allowed by pool: ${t}.`),H(5,`[pool] The number of resources that are currently available: ${Ae.numFree()}.`),H(5,`[pool] The number of resources that are currently acquired: ${Ae.numUsed()}.`),H(5,`[pool] The number of callers waiting to acquire a resource: ${Ae.numPendingAcquires()}.`)}var Ue=()=>({min:Ae.min,max:Ae.max,available:Ae.numFree(),inUse:Ae.numUsed(),pendingAcquire:Ae.numPendingAcquires()}),Ge=()=>_e;let Me=!1;const De=async(e,t)=>{H(4,"[chart] Starting the exporting process.");const r=((e,t={})=>{let r={};return e.svg?(r=F(t),r.export.type=e.type||e.export.type,r.export.scale=e.scale||e.export.scale,r.export.outfile=e.outfile||e.export.outfile,r.payload={svg:e.svg}):r=ue(t,e,I),r.export.outfile=r.export?.outfile||`chart.${r.export?.type||"png"}`,r})(e,he()),o=r.export;if(r.payload?.svg&&""!==r.payload.svg)try{H(4,"[chart] Attempting to export from a SVG input.");const e=qe(r.payload.svg.trim(),r,t);return++_e.exportFromSvgAttempts,e}catch(e){return t(new Z("[chart] Error loading SVG input.").setError(e))}if(o.infile&&o.infile.length)try{return H(4,"[chart] Attempting to export from an input file."),r.export.instr=n(o.infile,"utf8"),qe(r.export.instr.trim(),r,t)}catch(e){return t(new Z("[chart] Error loading input file.").setError(e))}if(o.instr&&""!==o.instr||o.options&&""!==o.options)try{return H(4,"[chart] Attempting to export from a raw input."),K(r.customLogic?.allowCodeExecution)?Fe(r,t):"string"==typeof o.instr?qe(o.instr.trim(),r,t):Ve(r,o.instr||o.options,t)}catch(e){return t(new Z("[chart] Error loading raw input.").setError(e))}return t(new Z("[chart] No valid input specified. Check if at least one of the following parameters is correctly set: 'infile', 'instr', 'options', or 'svg'."))},We=e=>{const{chart:t,exporting:r}=e.export?.options||V(e.export?.instr),o=V(e.export?.globalOptions);let i=e.export?.scale||r?.scale||o?.exporting?.scale||e.export?.defaultScale||1;i=Math.max(.1,Math.min(i,5)),i=((e,t=1)=>{const r=Math.pow(10,t||0);return Math.round(+e*r)/r})(i,2);const n={height:e.export?.height||r?.sourceHeight||t?.height||o?.exporting?.sourceHeight||o?.chart?.height||e.export?.defaultHeight||400,width:e.export?.width||r?.sourceWidth||t?.width||o?.exporting?.sourceWidth||o?.chart?.width||e.export?.defaultWidth||600,scale:i};for(let[e,t]of Object.entries(n))n[e]="string"==typeof t?+t.replace(/px|%/gi,""):t;return n},Ve=async(e,t,r,o)=>{let{export:i,customLogic:s}=e;const a="boolean"==typeof s.allowCodeExecution?s.allowCodeExecution:Me;if(s){if(a)if("string"==typeof e.customLogic.resources)e.customLogic.resources=W(e.customLogic.resources,K(e.customLogic.allowFileResources));else if(!e.customLogic.resources)try{const t=n("resources.json","utf8");e.customLogic.resources=W(t,K(e.customLogic.allowFileResources))}catch(e){j(2,e,"[chart] Unable to load the default resources.json file.")}}else s=e.customLogic={};if(!a&&s){if(s.callback||s.resources||s.customCode)return r(new Z("[chart] The 'callback', 'resources' and 'customCode' options have been disabled for this server."));s.callback=!1,s.resources=!1,s.customCode=!1}if(t&&(t.chart=t.chart||{},t.exporting=t.exporting||{},t.exporting.enabled=!1),i.constr=i.constr||"chart",i.type=D(i.type,i.outfile),"svg"===i.type&&(i.width=!1),["globalOptions","themeOptions"].forEach((e=>{try{i&&i[e]&&("string"==typeof i[e]&&i[e].endsWith(".json")?i[e]=V(n(i[e],"utf8"),!0):i[e]=V(i[e],!0))}catch(t){i[e]={},j(2,t,`[chart] The '${e}' cannot be loaded.`)}})),s.allowCodeExecution)try{s.customCode=J(s.customCode,s.allowFileResources)}catch(e){j(2,e,"[chart] The 'customCode' cannot be loaded.")}if(s&&s.callback&&s.callback?.indexOf("{")<0)if(s.allowFileResources)try{s.callback=n(s.callback,"utf8")}catch(e){s.callback=!1,j(2,e,"[chart] The 'callback' cannot be loaded.")}else s.callback=!1;e.export={...e.export,...We(e)};try{return r(!1,await He(i.strInj||t||o,e))}catch(e){return r(e)}},Fe=(e,t)=>{try{let r,o=e.export.instr||e.export.options;return"string"!=typeof o&&(r=o=q(o,e.customLogic?.allowCodeExecution)),r=o.replaceAll(/\t|\n|\r/g,"").trim(),";"===r[r.length-1]&&(r=r.substring(0,r.length-1)),e.export.strInj=r,Ve(e,!1,t)}catch(r){return t(new Z(`[chart] Malformed input detected for ${e.export?.requestId||"?"}. Please make sure that your JSON/JavaScript options are sent using the "options" attribute, and that if you're using SVG, it is unescaped.`).setError(r))}},qe=(e,t,r)=>{const{allowCodeExecution:o}=t.customLogic;if(e.indexOf("=0||e.indexOf("=0)return H(4,"[chart] Parsing input as SVG."),Ve(t,!1,r,e);try{const o=JSON.parse(e.replaceAll(/\t|\n|\r/g," "));return Ve(t,o,r)}catch(e){return K(o)?Fe(t,r):r(new Z("[chart] Only JSON configurations and SVG are allowed for this server. If this is your server, JavaScript custom code can be enabled by starting the server with the --allowCodeExecution flag.").setError(e))}},Xe=(e,t,r,o)=>{j(1,e),"development"!==Q.NODE_ENV&&delete e.stack,o(e)},Ke=(e,t,r,o)=>{const{statusCode:i,status:n,message:s,stack:a}=e,l=i||n||500;r.status(l).json({statusCode:l,message:s,stack:a})};var Je=(e,t)=>{const r="Too many requests, you have been rate limited. Please try again later.",o={max:t.maxRequests||30,window:t.window||1,delay:t.delay||0,trustProxy:t.trustProxy||!1,skipKey:t.skipKey||!1,skipToken:t.skipToken||!1};o.trustProxy&&e.enable("trust proxy");const i=k({windowMs:60*o.window*1e3,max:o.max,delayMs:o.delay,handler:(e,t)=>{t.format({json:()=>{t.status(429).send({message:r})},default:()=>{t.status(429).send(r)}})},skip:e=>!1!==o.skipKey&&!1!==o.skipToken&&e.query.key===o.skipKey&&e.query.access_token===o.skipToken&&(H(4,"[rate limiting] Skipping rate limiter."),!0)});e.use(i),H(3,`[rate limiting] Enabled rate limiting with ${o.max} requests per ${o.window} minute for each IP, trusting proxy: ${o.trustProxy}.`)};class Be extends Z{constructor(e,t){super(e),this.status=this.statusCode=t}setStatus(e){return this.status=e,this}}const ze={png:"image/png",jpeg:"image/jpeg",gif:"image/gif",pdf:"application/pdf",svg:"image/svg+xml"};let Ye=0;const Qe=[],Ze=[],et=(e,t,r,o)=>{let i=!0;const{id:n,uniqueId:s,type:a,body:l}=o;return e.some((e=>{if(e){let o=e(t,r,n,s,a,l);return void 0!==o&&!0!==o&&(i=o),!0}})),i},tt=async(e,t,r)=>{try{const r=B(),i=b().replace(/-/g,""),n=he(),s=e.body,a=++Ye;let l=D(s.type);if(!s||"object"==typeof(o=s)&&!Array.isArray(o)&&null!==o&&0===Object.keys(o).length)throw new Be("The request body is required. Please ensure that your Content-Type header is correct (accepted types are application/json and multipart/form-data).",400);let c=V(s.infile||s.options||s.data);if(!c&&!s.svg)throw H(2,`The request with ID ${i} from ${e.headers["x-forwarded-for"]||e.connection.remoteAddress} was incorrect. Payload received: ${JSON.stringify(s)}.`),new Be("No correct chart data found. Ensure that you are using either application/json or multipart/form-data headers. If sending JSON, make sure the chart data is in the 'infile', 'options', or 'data' attribute. If sending SVG, ensure it is in the 'svg' attribute.",400);let p=!1;if(p=et(Qe,e,t,{id:a,uniqueId:i,type:l,body:s}),!0!==p)return t.send(p);let h=!1;e.socket.on("close",(()=>{h=!0})),H(4,`[export] Got an incoming HTTP request with ID ${i}.`),s.constr="string"==typeof s.constr&&s.constr||"chart";const u={export:{instr:c,type:l,constr:s.constr[0].toLowerCase()+s.constr.substr(1),height:s.height,width:s.width,scale:s.scale||n.export.scale,globalOptions:V(s.globalOptions,!0),themeOptions:V(s.themeOptions,!0)},customLogic:{allowCodeExecution:Me,allowFileResources:!1,resources:V(s.resources,!0),callback:s.callback,customCode:s.customCode}};c&&(u.export.instr=q(c,u.customLogic.allowCodeExecution));const d=ue(n,u);if(d.export.options=c,d.payload={svg:s.svg||!1,b64:s.b64||!1,noDownload:s.noDownload||!1,requestId:i},s.svg&&(e=>[/xlink:href="(?:http:\/\/|https:\/\/)?localhost\b/,/xlink:href="(?:http:\/\/|https:\/\/)?10\.\d{1,3}\.\d{1,3}\.\d{1,3}\b/,/xlink:href="(?:http:\/\/|https:\/\/)?127\.\d{1,3}\.\d{1,3}\.\d{1,3}\b/,/xlink:href="(?:http:\/\/|https:\/\/)?172\.(1[6-9]|2[0-9]|3[0-1])\.\d{1,3}\.\d{1,3}\b/,/xlink:href="(?:http:\/\/|https:\/\/)?192\.168\.\d{1,3}\.\d{1,3}\b/].some((t=>t.test(e))))(d.payload.svg))throw new Be("SVG potentially contain at least one forbidden URL in xlink:href element. Please review the SVG content and ensure that all referenced URLs comply with security policies.",400);await De(d,((o,c)=>{if(e.socket.removeAllListeners("close"),n.server.benchmarking&&H(5,`[benchmark] Request with ID ${i} - After the whole exporting process: ${r()}ms.`),h)return H(3,"[export] The client closed the connection before the chart finished processing.");if(o)throw o;if(!c||!c.result)throw new Be(`Unexpected return from chart generation. Please check your request data. For the request with ID ${i}, the result is ${c.result}.`,400);return l=c.options.export.type,et(Ze,e,t,{id:a,body:c.result}),c.result?s.b64?"pdf"===l||"svg"==l?t.send(Buffer.from(c.result,"utf8").toString("base64")):t.send(c.result):(t.header("Content-Type",ze[l]||"image/png"),s.noDownload||t.attachment(`${e.params.filename||e.body.filename||"chart"}.${l||"png"}`),"svg"===l?t.send(c.result):t.send(Buffer.from(c.result,"base64"))):void 0}))}catch(e){r(e)}var o};const rt=JSON.parse(n(c(M,"package.json"))),ot=new Date,it=[];function nt(e){if(!e)return!1;e.get("/health",((e,t)=>{const r=Ge(),o=it.length,i=it.reduce(((e,t)=>e+t),0)/it.length;H(4,"[health.js] GET /health [200] - returning server health."),t.send({status:"OK",bootTime:ot,uptime:Math.floor(((new Date).getTime()-ot.getTime())/1e3/60)+" minutes",version:rt.version,highchartsVersion:ce(),averageProcessingTime:r.spentAverage,performedExports:r.performedExports,failedExports:r.droppedExports,exportAttempts:r.exportAttempts,sucessRatio:r.performedExports/r.exportAttempts*100,pool:Ue(),period:o,movingAverage:i,message:`Last ${o} minutes had a success rate of ${i.toFixed(2)}%.`,svgExportAttempts:r.exportFromSvgAttempts,jsonExportAttempts:r.performedExports-r.exportFromSvgAttempts})}))}setInterval((function(){const e=Ge(),t=0===e.exportAttempts?1:e.performedExports/e.exportAttempts*100;it.push(t),it.length>30&&it.shift()}),6e4);const st=R();st.disable("x-powered-by"),st.use(x());const at=S.memoryStorage(),lt=S({storage:at,limits:{fieldSize:52428800}});st.use(R.json({limit:52428800})),st.use(R.urlencoded({extended:!0,limit:52428800})),st.use(lt.none());const ct=e=>{e.on("clientError",(e=>{j(1,e,`[server] Client error: ${e.message}`)})),e.on("error",(e=>{j(1,e,`[server] Server error: ${e.message}`)})),e.on("connection",(e=>{e.on("error",(e=>{j(1,e,`[server] Socket error: ${e.message}`)}))}))},pt=async e=>{try{if(!e.enable)return!1;if(!e.ssl.force){const t=u.createServer(st);ct(t),t.listen(e.port,e.host),H(3,`[server] Started HTTP server on ${e.host}:${e.port}.`)}if(e.ssl.enable){let t,r;try{t=await a.readFile(p.join(e.ssl.certPath,"server.key"),"utf8"),r=await a.readFile(p.join(e.ssl.certPath,"server.crt"),"utf8")}catch(t){H(2,`[server] Unable to load key/certificate from the '${e.ssl.certPath}' path. Could not run secured layer server.`)}if(t&&r){const o=d.createServer({key:t,cert:r},st);ct(o),o.listen(e.ssl.port,e.host),H(3,`[server] Started HTTPS server on ${e.host}:${e.ssl.port}.`)}}e.rateLimiting&&e.rateLimiting.enable&&![0,NaN].includes(e.rateLimiting.maxRequests)&&Je(st,e.rateLimiting),st.use(R.static(p.join(M,"public"))),nt(st),(e=>{e.post("/",tt),e.post("/:filename",tt)})(st),(e=>{!!e&&e.get("/",((e,t)=>{t.sendFile(c(M,"public","index.html"))}))})(st),(e=>{!!e&&e.post("/version/change/:newVersion",(async(e,t,r)=>{try{const r=Q.HIGHCHARTS_ADMIN_TOKEN;if(!r||!r.length)throw new Be("The server is not configured to perform run-time version changes: HIGHCHARTS_ADMIN_TOKEN is not set.",401);const o=e.get("hc-auth");if(!o||o!==r)throw new Be("Invalid or missing token: Set the token in the hc-auth header.",401);const i=e.params.newVersion;if(!i)throw new Be("No new version supplied.",400);try{await ae(i)}catch(e){throw new Be(`Version change: ${e.message}`,e.statusCode).setError(e)}t.status(200).send({statusCode:200,version:ce(),message:`Successfully updated Highcharts to version: ${i}.`})}catch(e){r(e)}}))})(st),(e=>{e.use(Xe),e.use(Ke)})(st)}catch(e){throw new Z("[server] Could not configure and start the server.").setError(e)}};var ht={startServer:pt,enableRateLimiting:e=>Je(st,e),getExpress:()=>R,getApp:()=>st,use:(e,...t)=>{st.use(e,...t)},get:(e,...t)=>{st.get(e,...t)},post:(e,...t)=>{st.post(e,...t)}};e.config();var ut={server:ht,startServer:pt,setOptions:(e,t)=>(t?.length&&(pe=function(e){const t=e.findIndex((e=>"loadConfig"===e.replace(/-/g,"")));if(t>-1&&e[t+1]){const r=e[t+1];try{if(r&&r.endsWith(".json"))return JSON.parse(n(r))}catch(e){j(2,e,`[config] Unable to load the configuration from the ${r} file.`)}}return{}}(t)),de(O,pe),pe=me(O),e&&(pe=ue(pe,e,I)),t?.length&&(pe=function(e,t,r){let o=!1;for(let i=0;i(s.length-1===r&&(a=e[t].type),e[t])),r),s.reduce(((e,r,l)=>(s.length-1===l&&void 0!==e[r]&&(t[++i]?"boolean"===a?e[r]=K(t[i]):"number"===a?e[r]=+t[i]:a.indexOf("]")>=0?e[r]=t[i].split(","):e[r]=t[i]:(H(2,`[config] Missing value for the '${n}' argument. Using the default value.`),o=!0)),e[r])),e)}o&&X();return e}(pe,t,O)),pe),initExport:async e=>{var t;return t=e.customLogic&&e.customLogic.allowCodeExecution,Me=K(t),(e=>{U(e&&parseInt(e.level)),e&&e.dest&&G(e.dest,e.file||"highcharts-export-server.log")})(e.logging),await ne(e.highcharts||{version:"latest"}),await Pe({pool:e.pool||{minWorkers:1,maxWorkers:1},puppeteerArgs:e.puppeteer?.args||[]}),e},singleExport:async e=>{e.export.instr=e.export.instr||e.export.options,await De(e,(async(e,t)=>{if(e)throw e;const{outfile:r,type:o}=t.options.export;s(r||`chart.${o}`,"svg"!==o?Buffer.from(t.result,"base64"):t.result),await $e()}))},batchExport:async e=>{const t=[];for(let r of e.export.batch.split(";"))r=r.split("="),2===r.length&&t.push(De({...e,export:{...e.export,infile:r[0],outfile:r[1]}},((e,t)=>{if(e)throw e;s(t.options.export.outfile,Buffer.from(t.result,"base64"))})));try{await Promise.all(t),await $e()}catch(e){throw new Z("[chart] Error encountered during batch export.").setError(e)}},startExport:De,killPool:$e,log:H,logWithStack:j,setLogLevel:U,enableFileLogging:G,mapToNewConfig:e=>{const t={};for(const[r,o]of Object.entries(e)){const e=C[r]?C[r].split("."):[];e.reduce(((t,r,i)=>t[r]=e.length-1===i?o:t[r]||{}),t)}return t},manualConfig:async e=>{let t={};r(e)&&(t=JSON.parse(n(e,"utf8")));const o=Object.keys(_).map((e=>({title:`${e} options`,value:e})));return v({type:"multiselect",name:"category",message:"Which category do you want to configure?",hint:"Space: Select specific, A: Select all, Enter: Confirm.",instructions:"",choices:o},{onSubmit:async(r,o)=>{let i=0,n=[];for(const e of o)_[e]=_[e].map((t=>({...t,section:e}))),n=[...n,..._[e]];return await v(n,{onSubmit:async(r,o)=>{if("modules"===r.name?(o=o.length?o.map((e=>r.choices[e])):r.choices,t[r.section][r.name]=o):t[r.section]=ge(Object.assign({},t[r.section]||{}),r.name.split("."),r.choices?r.choices[o]:o),++i===n.length){try{await a.writeFile(e,JSON.stringify(t,null,2),"utf8")}catch(t){j(1,t,`[config] An error occurred while creating the ${e} file.`)}return!0}}}),!0}})},printLogo:e=>{const t=JSON.parse(n(c(M,"package.json"))).version;e?console.log(`Starting Highcharts Export Server v${t}...`):console.log(n(M+"/msg/startup.msg").toString().bold.yellow,`v${t}`)},printUsage:X};export{ut as default};
//# sourceMappingURL=index.esm.js.map
diff --git a/dist/index.esm.js.map b/dist/index.esm.js.map
index 2cee4e7d..a5448071 100644
--- a/dist/index.esm.js.map
+++ b/dist/index.esm.js.map
@@ -1 +1 @@
-{"version":3,"file":"index.esm.js","sources":["../lib/fetch.js","../lib/schemas/config.js","../lib/logger.js","../lib/utils.js","../lib/envs.js","../lib/errors/ExportError.js","../lib/cache.js","../lib/config.js","../lib/browser.js","../lib/export.js","../templates/svg_export/svg_export.js","../lib/pool.js","../lib/chart.js","../lib/server/error.js","../lib/server/rate_limit.js","../lib/errors/HttpError.js","../lib/server/routes/export.js","../lib/server/routes/health.js","../lib/server/server.js","../lib/server/routes/ui.js","../lib/server/routes/change_hc_version.js","../lib/index.js"],"sourcesContent":["/**\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 * Returns the HTTP or HTTPS protocol module based on the provided URL.\r\n *\r\n * @param {string} url - The URL to determine the protocol.\r\n *\r\n * @returns {Object} The HTTP or HTTPS protocol module (http or https).\r\n */\r\nconst getProtocol = (url) => (url.startsWith('https') ? https : http);\r\n\r\n/**\r\n * Fetches data from the specified URL using either HTTP or HTTPS protocol.\r\n *\r\n * @param {string} url - The URL to fetch data from.\r\n * @param {Object} requestOptions - Options for the HTTP request (optional).\r\n *\r\n * @returns {Promise} Promise resolving to the HTTP response object\r\n * with added 'text' property or rejecting with an error.\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 provided JSON body using\r\n * either HTTP or HTTPS protocol.\r\n *\r\n * @param {string} url - The URL to send the POST request to.\r\n * @param {Object} body - The JSON body to include in the POST request\r\n * (optional, default is an empty object).\r\n * @param {Object} requestOptions - Options for the HTTP request (optional).\r\n *\r\n * @returns {Promise} Promise resolving to the HTTP response object with\r\n * added 'text' property or rejecting with an error.\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-2024, 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// 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: 'Arguments array 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: 'The Highcharts version to be used.'\r\n },\r\n cdnURL: {\r\n value: 'https://code.highcharts.com/',\r\n envLink: 'HIGHCHARTS_CDN_URL',\r\n type: 'string',\r\n description: 'The CDN URL for Highcharts scripts to be used.'\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: 'The core Highcharts 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 'data-tools',\r\n 'draggable-points',\r\n 'static-scale',\r\n 'broken-axis',\r\n 'heatmap',\r\n 'tilemap',\r\n 'tiledwebmap',\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 'geoheatmap',\r\n 'pyramid3d',\r\n 'networkgraph',\r\n 'overlapping-datalabels',\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 'flowmap'\r\n ],\r\n type: 'string[]',\r\n description: 'The modules of Highcharts to fetch.'\r\n },\r\n indicators: {\r\n envLink: 'HIGHCHARTS_INDICATORS',\r\n value: ['indicators-all'],\r\n type: 'string[]',\r\n description: 'The indicators of Highcharts 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: 'Additional optional scripts or dependencies to fetch.'\r\n },\r\n forceFetch: {\r\n envLink: 'HIGHCHARTS_FORCE_FETCH',\r\n value: false,\r\n type: 'boolean',\r\n description:\r\n 'The flag to determine whether to refetch all scripts after each server rerun.'\r\n },\r\n cachePath: {\r\n envLink: 'HIGHCHARTS_CACHE_PATH',\r\n value: '.cache',\r\n type: 'string',\r\n description:\r\n 'The path to the cache directory. It is used to store the Highcharts scripts and custom scripts.'\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 should include a name and a type (json or svg). It must be correctly formatted as a JSON or SVG file.'\r\n },\r\n instr: {\r\n value: false,\r\n type: 'string',\r\n description:\r\n 'Input, provided in the form of a stringified JSON or SVG file, will override the --infile option.'\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). This will ignore the --type flag.'\r\n },\r\n type: {\r\n envLink: 'EXPORT_TYPE',\r\n value: 'png',\r\n type: 'string',\r\n description: 'The file export format. It can be jpeg, png, pdf, or svg.'\r\n },\r\n constr: {\r\n envLink: 'EXPORT_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 no value is 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 no value is 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. Used when no value is set.'\r\n },\r\n height: {\r\n type: 'number',\r\n value: false,\r\n description:\r\n 'The height of the exported chart, overriding 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, overriding the option in the chart settings.'\r\n },\r\n scale: {\r\n value: false,\r\n type: 'number',\r\n description:\r\n 'The scale of the exported chart, overriding the option in the chart settings. Ranges between 0.1 and 5.0.'\r\n },\r\n globalOptions: {\r\n value: false,\r\n type: 'string',\r\n description:\r\n 'Either a stringified JSON or a filename containing 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 'Either a stringified JSON or a filename containing 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 'Initiates a batch job with a string containing input/output pairs: \"in=out;in=out;...\".'\r\n },\r\n rasterizationTimeout: {\r\n envLink: 'EXPORT_RASTERIZATION_TIMEOUT',\r\n value: 1500,\r\n type: 'number',\r\n description:\r\n 'The duration in milliseconds to wait for rendering a webpage.'\r\n }\r\n },\r\n customLogic: {\r\n allowCodeExecution: {\r\n envLink: 'CUSTOM_LOGIC_ALLOW_CODE_EXECUTION',\r\n value: false,\r\n type: 'boolean',\r\n description:\r\n 'Controls whether the execution of arbitrary code is allowed during the exporting process.'\r\n },\r\n allowFileResources: {\r\n envLink: 'CUSTOM_LOGIC_ALLOW_FILE_RESOURCES',\r\n value: false,\r\n type: 'boolean',\r\n description:\r\n 'Controls the ability to inject resources from the filesystem. This setting 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 'Custom code to execute before chart initialization. It can be a function, code wrapped within a function, or a filename with the .js extension.'\r\n },\r\n callback: {\r\n value: false,\r\n type: 'string',\r\n description:\r\n 'JavaScript code to run during construction. It can be a function or a filename with the .js extension.'\r\n },\r\n resources: {\r\n value: false,\r\n type: 'string',\r\n description:\r\n 'Additional resource in the form of a stringified JSON, which may contain files, js, and css sections.'\r\n },\r\n loadConfig: {\r\n value: false,\r\n type: 'string',\r\n legacyName: 'fromFile',\r\n description: 'A file containing a pre-defined configuration to use.'\r\n },\r\n createConfig: {\r\n value: false,\r\n type: 'string',\r\n description:\r\n 'Enables setting options through a prompt and saving them in a provided config file.'\r\n }\r\n },\r\n server: {\r\n enable: {\r\n envLink: 'SERVER_ENABLE',\r\n value: false,\r\n type: 'boolean',\r\n cliName: 'enableServer',\r\n description:\r\n 'When set to true, the server starts on the local IP address 0.0.0.0.'\r\n },\r\n host: {\r\n envLink: 'SERVER_HOST',\r\n value: '0.0.0.0',\r\n type: 'string',\r\n description:\r\n 'The hostname of the server. Additionally, it starts a server on the provided hostname.'\r\n },\r\n port: {\r\n envLink: 'SERVER_PORT',\r\n value: 7801,\r\n type: 'number',\r\n description: 'The server port when enabled.'\r\n },\r\n benchmarking: {\r\n envLink: 'SERVER_BENCHMARKING',\r\n value: false,\r\n type: 'boolean',\r\n cliName: 'serverBenchmarking',\r\n description:\r\n 'Indicates whether to display the duration, in milliseconds, of specific actions that occur on the server while serving a request.'\r\n },\r\n ssl: {\r\n enable: {\r\n envLink: 'SERVER_SSL_ENABLE',\r\n value: false,\r\n type: 'boolean',\r\n cliName: 'enableSsl',\r\n description: 'Enables or disables the SSL protocol.'\r\n },\r\n force: {\r\n envLink: 'SERVER_SSL_FORCE',\r\n value: false,\r\n type: 'boolean',\r\n cliName: 'sslForced',\r\n legacyName: 'sslOnly',\r\n description:\r\n 'When set to true, the server is forced to serve only over HTTPS.'\r\n },\r\n port: {\r\n envLink: '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: 'SERVER_SSL_CERT_PATH',\r\n value: '',\r\n type: 'string',\r\n legacyName: 'sslPath',\r\n description: 'The path to the SSL certificate/key file.'\r\n }\r\n },\r\n rateLimiting: {\r\n enable: {\r\n envLink: 'SERVER_RATE_LIMITING_ENABLE',\r\n value: false,\r\n type: 'boolean',\r\n cliName: 'enableRateLimiting',\r\n description: 'Enables rate limiting for the server.'\r\n },\r\n maxRequests: {\r\n envLink: 'SERVER_RATE_LIMITING_MAX_REQUESTS',\r\n value: 10,\r\n type: 'number',\r\n legacyName: 'rateLimit',\r\n description: 'The maximum number of requests allowed in one minute.'\r\n },\r\n window: {\r\n envLink: 'SERVER_RATE_LIMITING_WINDOW',\r\n value: 1,\r\n type: 'number',\r\n description: 'The time window, in minutes, for the rate limiting.'\r\n },\r\n delay: {\r\n envLink: 'SERVER_RATE_LIMITING_DELAY',\r\n value: 0,\r\n type: 'number',\r\n description:\r\n 'The delay duration for each successive request before reaching the maximum limit.'\r\n },\r\n trustProxy: {\r\n envLink: 'SERVER_RATE_LIMITING_TRUST_PROXY',\r\n value: false,\r\n type: 'boolean',\r\n description: 'Set this to true if the server is behind a load balancer.'\r\n },\r\n skipKey: {\r\n envLink: 'SERVER_RATE_LIMITING_SKIP_KEY',\r\n value: '',\r\n type: 'string',\r\n description:\r\n 'Allows bypassing the rate limiter and should be provided with the skipToken argument.'\r\n },\r\n skipToken: {\r\n envLink: 'SERVER_RATE_LIMITING_SKIP_TOKEN',\r\n value: '',\r\n type: 'string',\r\n description:\r\n 'Allows bypassing the rate limiter and should be provided with the skipKey argument.'\r\n }\r\n }\r\n },\r\n pool: {\r\n minWorkers: {\r\n envLink: 'POOL_MIN_WORKERS',\r\n value: 4,\r\n type: 'number',\r\n description: 'The number of minimum and initial pool workers to spawn.'\r\n },\r\n maxWorkers: {\r\n envLink: 'POOL_MAX_WORKERS',\r\n value: 8,\r\n type: 'number',\r\n legacyName: 'workers',\r\n description: 'The number of maximum pool workers to spawn.'\r\n },\r\n workLimit: {\r\n envLink: 'POOL_WORK_LIMIT',\r\n value: 40,\r\n type: 'number',\r\n description:\r\n 'The number of work pieces that can be performed before restarting the worker process.'\r\n },\r\n acquireTimeout: {\r\n envLink: 'POOL_ACQUIRE_TIMEOUT',\r\n value: 5000,\r\n type: 'number',\r\n description:\r\n 'The duration, in milliseconds, to wait for acquiring a resource.'\r\n },\r\n createTimeout: {\r\n envLink: 'POOL_CREATE_TIMEOUT',\r\n value: 5000,\r\n type: 'number',\r\n description:\r\n 'The duration, in milliseconds, to wait for creating a resource.'\r\n },\r\n destroyTimeout: {\r\n envLink: 'POOL_DESTROY_TIMEOUT',\r\n value: 5000,\r\n type: 'number',\r\n description:\r\n 'The duration, in milliseconds, to wait for destroying a resource.'\r\n },\r\n idleTimeout: {\r\n envLink: 'POOL_IDLE_TIMEOUT',\r\n value: 30000,\r\n type: 'number',\r\n description:\r\n 'The duration, in milliseconds, after which an idle resource is destroyed.'\r\n },\r\n createRetryInterval: {\r\n envLink: 'POOL_CREATE_RETRY_INTERVAL',\r\n value: 200,\r\n type: 'number',\r\n description:\r\n 'The duration, in milliseconds, to wait before retrying the create process in case of a failure.'\r\n },\r\n reaperInterval: {\r\n envLink: 'POOL_REAPER_INTERVAL',\r\n value: 1000,\r\n type: 'number',\r\n description:\r\n 'The duration, in milliseconds, after which the check for idle resources to destroy is triggered.'\r\n },\r\n benchmarking: {\r\n envLink: 'POOL_BENCHMARKING',\r\n value: false,\r\n type: 'boolean',\r\n cliName: 'poolBenchmarking',\r\n description:\r\n 'Indicate whether to show statistics for the pool of resources or not.'\r\n },\r\n listenToProcessExits: {\r\n envLink: 'POOL_LISTEN_TO_PROCESS_EXITS',\r\n value: true,\r\n type: 'boolean',\r\n description: 'Decides whether or not to attach process.exit handlers.'\r\n }\r\n },\r\n logging: {\r\n level: {\r\n envLink: 'LOGGING_LEVEL',\r\n value: 4,\r\n type: 'number',\r\n cliName: 'logLevel',\r\n description: 'The logging level to be used.'\r\n },\r\n file: {\r\n envLink: 'LOGGING_FILE',\r\n value: 'highcharts-export-server.log',\r\n type: 'string',\r\n cliName: 'logFile',\r\n description:\r\n 'The name of a log file. The logDest option also needs to be set to enable file logging.'\r\n },\r\n dest: {\r\n envLink: 'LOGGING_DEST',\r\n value: 'log/',\r\n type: 'string',\r\n cliName: 'logDest',\r\n description:\r\n 'The path to store log files. This also enables file logging.'\r\n }\r\n },\r\n ui: {\r\n enable: {\r\n envLink: 'UI_ENABLE',\r\n value: false,\r\n type: 'boolean',\r\n cliName: 'enableUi',\r\n description:\r\n 'Enables or disables the user interface (UI) for the export server.'\r\n },\r\n route: {\r\n envLink: 'UI_ROUTE',\r\n value: '/',\r\n type: 'string',\r\n cliName: 'uiRoute',\r\n description:\r\n 'The endpoint route to which the user interface (UI) should be attached.'\r\n }\r\n },\r\n other: {\r\n noLogo: {\r\n envLink: 'OTHER_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};\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: 'Force re-fetch the scripts',\r\n initial: defaultConfig.highcharts.forceFetch.value\r\n },\r\n {\r\n type: 'text',\r\n name: 'cachePath',\r\n message: 'The path to the cache directory',\r\n initial: defaultConfig.highcharts.cachePath.value\r\n }\r\n ],\r\n export: [\r\n {\r\n type: 'select',\r\n name: 'type',\r\n message: 'The default export file type',\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',\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 type: 'number',\r\n name: 'rasterizationTimeout',\r\n message: 'The rendering webpage timeout in milliseconds',\r\n initial: defaultConfig.export.rasterizationTimeout.value\r\n }\r\n ],\r\n customLogic: [\r\n {\r\n type: 'toggle',\r\n name: 'allowCodeExecution',\r\n message: 'Enable execution of custom code',\r\n initial: defaultConfig.customLogic.allowCodeExecution.value\r\n },\r\n {\r\n type: 'toggle',\r\n name: 'allowFileResources',\r\n message: 'Enable file resources',\r\n initial: defaultConfig.customLogic.allowFileResources.value\r\n }\r\n ],\r\n server: [\r\n {\r\n type: 'toggle',\r\n name: 'enable',\r\n message: 'Starts the 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: 'Server hostname',\r\n initial: defaultConfig.server.host.value\r\n },\r\n {\r\n type: 'number',\r\n name: 'port',\r\n message: 'Server port',\r\n initial: defaultConfig.server.port.value\r\n },\r\n {\r\n type: 'toggle',\r\n name: 'benchmarking',\r\n message: 'Enable server benchmarking',\r\n initial: defaultConfig.server.benchmarking.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 serving only 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: 'SSL server port',\r\n initial: defaultConfig.server.ssl.port.value\r\n },\r\n {\r\n type: 'text',\r\n name: 'ssl.certPath',\r\n message: 'The path 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: 'The maximum requests allowed per 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 rate-limiting time window in minutes',\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 delay for each successive request before reaching the maximum',\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 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 when provided with the 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 when provided with the 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 initial number of 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 maximum number of 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: 'createRetryInterval',\r\n message:\r\n 'The retry interval in milliseconds after a create process fails',\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 reaper interval in milliseconds after triggering the check for idle resources to destroy',\r\n initial: defaultConfig.pool.reaperInterval.value\r\n },\r\n {\r\n type: 'toggle',\r\n name: 'benchmarking',\r\n message: 'Enable benchmarking for a resource pool',\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 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, 5: benchmark)',\r\n initial: defaultConfig.logging.level.value,\r\n round: 0,\r\n min: 0,\r\n max: 5\r\n },\r\n {\r\n type: 'text',\r\n name: 'file',\r\n message: 'A log file name. Set with the --logDest 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: 'The path to log files. 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',\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: 'Skip printing the logo on startup. Replaced by 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 * Recursively creates a chain of nested arguments from an object.\r\n *\r\n * @param {Object} obj - The object containing nested arguments.\r\n * @param {string} propChain - The current chain of nested properties\r\n * (used internally during recursion).\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 // Support for the legacy, PhantomJS properties names\r\n if (entry.legacyName !== undefined) {\r\n nestedArgs[entry.legacyName] = `${propChain}.${k}`.substring(1);\r\n }\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-2024, 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 available colors\r\nconst colors = ['red', 'yellow', 'blue', 'gray', 'green'];\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: colors[0]\r\n },\r\n {\r\n title: 'warning',\r\n color: colors[1]\r\n },\r\n {\r\n title: 'notice',\r\n color: colors[2]\r\n },\r\n {\r\n title: 'verbose',\r\n color: colors[3]\r\n },\r\n {\r\n title: 'benchmark',\r\n color: colors[4]\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 the provided texts to a file, if file logging is enabled. It creates\r\n * the necessary directory structure if not already created and appends the\r\n * content, including an optional prefix, to the specified log file.\r\n *\r\n * @param {string[]} texts - An array of texts to be logged.\r\n * @param {string} prefix - An optional prefix to be added to each log entry.\r\n */\r\nconst logToFile = (texts, prefix) => {\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\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 or is a benchmark log\r\n if (\r\n newLevel !== 5 &&\r\n (newLevel === 0 || newLevel > level || level > levelsDesc.length)\r\n ) {\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 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 // Log to file\r\n logToFile(texts, prefix);\r\n};\r\n\r\n/**\r\n * Logs an error message with its stack trace. Optionally, a custom message\r\n * can be provided.\r\n *\r\n * @param {number} level - The log level.\r\n * @param {Error} error - The error object.\r\n * @param {string} customMessage - An optional custom message to be logged along\r\n * with the error.\r\n */\r\nexport const logWithStack = (newLevel, error, customMessage) => {\r\n // Get the main message\r\n const mainMessage = customMessage || error.message;\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 // If the customMessage exists, we want to display the whole stack message\r\n const stackMessage =\r\n error.message !== error.stackMessage || error.stackMessage === undefined\r\n ? error.stack\r\n : error.stack.split('\\n').slice(1).join('\\n');\r\n\r\n // Combine custom message or error message with error stack message\r\n const texts = [mainMessage, '\\n', stackMessage];\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([\r\n mainMessage[colors[newLevel - 1]],\r\n '\\n',\r\n stackMessage\r\n ])\r\n );\r\n }\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 logToFile(texts, prefix);\r\n};\r\n\r\n/**\r\n * Sets the log level to the specified value. Log levels are (0 = no logging,\r\n * 1 = error, 2 = warning, 3 = notice, 4 = verbose or 5 = benchmark)\r\n *\r\n * @param {number} newLevel - The new log level to be set.\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 file logging with the specified destination and log file.\r\n *\r\n * @param {string} logDest - The destination path for log files.\r\n * @param {string} logFile - The log file name.\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 initialization: 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 * Initializes logging with the specified logging configuration.\r\n *\r\n * @param {Object} logging - The logging configuration object.\r\n */\r\nexport const initLogging = (logging) => {\r\n // Set the log level\r\n setLogLevel(logging && parseInt(logging.level));\r\n\r\n // Set the log file path and name\r\n if (logging && logging.dest) {\r\n enableFileLogging(\r\n logging.dest,\r\n logging.file || 'highcharts-export-server.log'\r\n );\r\n }\r\n};\r\n\r\n/**\r\n * Adds a listener function to the logging system.\r\n *\r\n * @param {function} fn - The listener function to be added.\r\n */\r\nexport const listen = (fn) => {\r\n logging.listeners.push(fn);\r\n};\r\n\r\n/**\r\n * Toggles the standard output (console) logging.\r\n *\r\n * @param {boolean} enabled - If true, enables console logging; if false,\r\n * disables it.\r\n */\r\nexport const toggleSTDOut = (enabled) => {\r\n logging.toConsole = enabled;\r\n};\r\n\r\nexport default {\r\n log,\r\n logWithStack,\r\n setLogLevel,\r\n enableFileLogging,\r\n initLogging,\r\n listen,\r\n toggleSTDOut\r\n};\r\n","/*******************************************************************************\r\n\r\nHighcharts Export Server\r\n\r\nCopyright (c) 2016-2024, 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 { join } from 'path';\r\nimport { fileURLToPath } from 'url';\r\n\r\nimport { defaultConfig } from '../lib/schemas/config.js';\r\nimport { log, logWithStack } 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 and standardizes text by replacing multiple consecutive whitespace\r\n * characters with a single space and trimming any leading or trailing\r\n * whitespace.\r\n *\r\n * @param {string} text - The input text to be cleared.\r\n * @param {RegExp} [rule=/\\s\\s+/g] - The regular expression rule to match\r\n * multiple consecutive whitespace characters.\r\n * @param {string} [replacer=' '] - The string used to replace multiple\r\n * consecutive whitespace characters.\r\n *\r\n * @returns {string} - The cleared and standardized 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 * Implements an exponential backoff strategy for retrying a function until\r\n * a certain number of attempts are reached.\r\n *\r\n * @param {Function} fn - The function to be retried.\r\n * @param {number} [attempt=0] - The current attempt number.\r\n * @param {...any} args - Arguments to be passed to the function.\r\n *\r\n * @returns {Promise} - A promise that resolves to the result of the function\r\n * if successful.\r\n *\r\n * @throws {Error} - Throws an error if the maximum number of attempts\r\n * is reached.\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 the export type based on MIME types and file extensions.\r\n *\r\n * @param {string} type - The original export type.\r\n * @param {string} outfile - The file path or name.\r\n *\r\n * @returns {string} - The corrected export type.\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 if (outType === 'jpg') {\r\n type = 'jpeg';\r\n } else 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 and validates resources for export.\r\n *\r\n * @param {Object|string} resources - The resources to be handled. Can be either\r\n * a JSON object, stringified JSON or a path to a JSON file.\r\n * @param {boolean} allowFileResources - Whether to allow loading resources from\r\n * files.\r\n *\r\n * @returns {Object|undefined} - The handled resources or undefined if no valid\r\n * resources are found.\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 handledResources = isCorrectJSON(readFileSync(resources, 'utf8'));\r\n } catch (error) {\r\n return logWithStack(2, error, `[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 (handledResources && !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 * Validates and parses JSON data. Checks if provided data is or can\r\n * be a correct JSON. If a primitive is provided, it is stringified and returned.\r\n *\r\n * @param {Object|string} data - The JSON data to be validated and parsed.\r\n * @param {boolean} toString - Whether to return a stringified representation\r\n * of the parsed JSON.\r\n *\r\n * @returns {Object|string|boolean} - The parsed JSON object, stringified JSON,\r\n * or false if validation fails.\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 {\r\n return false;\r\n }\r\n}\r\n\r\n/**\r\n * Checks if the given item is an object.\r\n *\r\n * @param {any} item - The item to be checked.\r\n *\r\n * @returns {boolean} - True if the item is an object, false otherwise.\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 the given object is empty.\r\n *\r\n * @param {Object} item - The object to be checked.\r\n *\r\n * @returns {boolean} - True if the object is empty, false otherwise.\r\n */\r\nexport const isObjectEmpty = (item) =>\r\n typeof item === 'object' &&\r\n !Array.isArray(item) &&\r\n item !== null &&\r\n Object.keys(item).length === 0;\r\n\r\n/**\r\n * Checks if a private IP range URL is found in the given string.\r\n *\r\n * @param {string} item - The string to be checked for a private IP range URL.\r\n *\r\n * @returns {boolean} - True if a private IP range URL is found, false\r\n * otherwise.\r\n */\r\nexport const isPrivateRangeUrlFound = (item) => {\r\n const regexPatterns = [\r\n /xlink:href=\"(?:http:\\/\\/|https:\\/\\/)?localhost\\b/,\r\n /xlink:href=\"(?:http:\\/\\/|https:\\/\\/)?10\\.\\d{1,3}\\.\\d{1,3}\\.\\d{1,3}\\b/,\r\n /xlink:href=\"(?:http:\\/\\/|https:\\/\\/)?127\\.\\d{1,3}\\.\\d{1,3}\\.\\d{1,3}\\b/,\r\n /xlink:href=\"(?:http:\\/\\/|https:\\/\\/)?172\\.(1[6-9]|2[0-9]|3[0-1])\\.\\d{1,3}\\.\\d{1,3}\\b/,\r\n /xlink:href=\"(?:http:\\/\\/|https:\\/\\/)?192\\.168\\.\\d{1,3}\\.\\d{1,3}\\b/\r\n ];\r\n\r\n return regexPatterns.some((pattern) => pattern.test(item));\r\n};\r\n\r\n/**\r\n * Creates a deep copy of the given object or array.\r\n *\r\n * @param {Object|Array} obj - The object or array to be deeply copied.\r\n *\r\n * @returns {Object|Array} - The deep copy of the provided object or array.\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 * Converts the provided options object to a JSON-formatted string with the\r\n * option to preserve functions.\r\n *\r\n * @param {Object} options - The options object to be converted to a string.\r\n * @param {boolean} allowFunctions - If set to true, functions are preserved\r\n * in the output.\r\n *\r\n * @returns {string} - The JSON-formatted string representing the options.\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 Highcharts Export Server logo and version information.\r\n *\r\n * @param {boolean} noLogo - If true, only prints version information without\r\n * the logo.\r\n */\r\nexport const printLogo = (noLogo) => {\r\n // Get package version either from env or from package.json\r\n const packageVersion = JSON.parse(\r\n readFileSync(join(__dirname, 'package.json'))\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 usage information for CLI arguments. If required, it can list\r\n * 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 '\\nUsage of CLI arguments:'.bold,\r\n '\\n------',\r\n `\\nFor more detailed information, visit the readme at: ${readme.bold.yellow}.`\r\n );\r\n\r\n const cycleCategories = (options) => {\r\n for (const [name, option] of Object.entries(options)) {\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 a number to the specified precision.\r\n *\r\n * @param {number} value - The number to be rounded.\r\n * @param {number} precision - The number of decimal places to round to.\r\n *\r\n * @returns {number} - The rounded number.\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 * Converts a value to a boolean.\r\n *\r\n * @param {any} item - The value to be converted to a boolean.\r\n *\r\n * @returns {boolean} - The boolean representation of the input value.\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 * Wraps custom code to execute it safely.\r\n *\r\n * @param {string} customCode - The custom code to be wrapped.\r\n * @param {boolean} allowFileResources - Flag to allow loading code from a file.\r\n *\r\n * @returns {string|boolean} - The wrapped custom code or false if wrapping\r\n * fails.\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 elapsed time using the Node.js process.hrtime() method.\r\n *\r\n * @returns {function(): number} - A function to calculate the elapsed time\r\n * in milliseconds.\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 isObjectEmpty,\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 * @fileoverview\r\n * This file is responsible for parsing the environment variables with the 'zod' library.\r\n * The parsed environment variables are then exported to be used in the application as \"envs\".\r\n * We should not use process.env directly in the application as these would not be parsed properly.\r\n *\r\n * The environment variables are parsed and validated only once when the application starts.\r\n * We should write a custom validator or a transformer for each of the options.\r\n *\r\n * For envs not defined in config.js with defaults, we also include default values here (PROXY_...).\r\n */\r\n\r\nimport { z } from 'zod';\r\nimport dotenv from 'dotenv';\r\ndotenv.config();\r\n\r\n// Object with custom validators and transformers, to avoid repetition in the Config object\r\nconst v = {\r\n boolean: () =>\r\n z\r\n .enum(['true', 'false'])\r\n .transform((value) => value === 'true')\r\n .optional(),\r\n array: () =>\r\n z\r\n .string()\r\n .transform((val) => val.split(',').map((v) => v.trim()))\r\n .optional()\r\n};\r\n\r\nexport const Config = z.object({\r\n // highcharts\r\n HIGHCHARTS_VERSION: z\r\n .string()\r\n .refine((value) => /^(latest|\\d+(\\.\\d+){0,2})$/.test(value), {\r\n message:\r\n \"HIGHCHARTS_VERSION must be 'latest', a major version, or in the form XX.YY.ZZ\"\r\n })\r\n .optional(), // todo: create an array of available Highcharts versions\r\n HIGHCHARTS_CDN_URL: z\r\n .string()\r\n .trim()\r\n .refine((val) => val.startsWith('https://') || val.startsWith('http://'), {\r\n message:\r\n 'Invalid value for HIGHCHARTS_CDN_URL. It should start with http:// or https://.'\r\n })\r\n .optional(),\r\n HIGHCHARTS_CORE_SCRIPTS: v.array(),\r\n HIGHCHARTS_MODULES: v.array(),\r\n HIGHCHARTS_INDICATORS: v.array(),\r\n HIGHCHARTS_FORCE_FETCH: v.boolean(),\r\n HIGHCHARTS_CACHE_PATH: z.string().optional(),\r\n HIGHCHARTS_ADMIN_TOKEN: z.string().optional(),\r\n\r\n // export\r\n EXPORT_TYPE: z.enum(['jpeg', 'png', 'pdf', 'svg']).optional(),\r\n EXPORT_CONSTR: z\r\n .string()\r\n .refine(\r\n (val) =>\r\n ['chart', 'stockChart', 'mapChart', 'ganttChart'].includes(val || ''),\r\n { message: 'Invalid value for EXPORT_CONSTR. ' }\r\n )\r\n .optional(),\r\n EXPORT_DEFAULT_HEIGHT: z.coerce.number().positive().optional(),\r\n EXPORT_DEFAULT_WIDTH: z.coerce.number().positive().optional(),\r\n EXPORT_DEFAULT_SCALE: z.coerce.number().positive().optional(),\r\n EXPORT_RASTERIZATION_TIMEOUT: z.coerce.number().positive().optional(),\r\n\r\n // custom\r\n CUSTOM_LOGIC_ALLOW_CODE_EXECUTION: v.boolean(),\r\n CUSTOM_LOGIC_ALLOW_FILEL_RESOURCES: v.boolean(),\r\n\r\n // server-related\r\n SERVER_ENABLE: v.boolean(),\r\n SERVER_HOST: z.string().optional(),\r\n SERVER_PORT: z.coerce.number().optional(),\r\n SERVER_BENCHMARKING: v.boolean(),\r\n SERVER_SSL_ENABLE: v.boolean(),\r\n SERVER_SSL_FORCE: v.boolean(),\r\n SERVER_SSL_PORT: z.coerce.number().optional(),\r\n SERVER_SSL_CERT_PATH: z.string().optional(),\r\n SERVER_RATE_LIMITING_ENABLE: v.boolean(),\r\n SERVER_RATE_LIMITING_MAX_REQUESTS: z.coerce.number().optional(),\r\n SERVER_RATE_LIMITING_WINDOW: z.coerce.number().optional(),\r\n SERVER_RATE_LIMITING_DELAY: z.coerce.number().optional(),\r\n SERVER_RATE_LIMITING_TRUST_PROXY: v.boolean(),\r\n SERVER_RATE_LIMITING_SKIP_KEY: z.string().optional(),\r\n SERVER_RATE_LIMITING_SKIP_TOKEN: z.string().optional(),\r\n\r\n // pool\r\n POOL_MIN_WORKERS: z.coerce.number().optional(),\r\n POOL_MAX_WORKERS: z.coerce.number().optional(),\r\n POOL_WORK_LIMIT: z.coerce.number().optional(),\r\n POOL_ACQUIRE_TIMEOUT: z.coerce.number().optional(),\r\n POOL_CREATE_TIMEOUT: z.coerce.number().optional(),\r\n POOL_DESTROY_TIMEOUT: z.coerce.number().optional(),\r\n POOL_IDLE_TIMEOUT: z.coerce.number().optional(),\r\n POOL_CREATE_RETRY_INTERVAL: z.coerce.number().optional(),\r\n POOL_REAPER_INTERVAL: z.coerce.number().optional(),\r\n POOL_BENCHMARKING: v.boolean(),\r\n POOL_LISTEN_TO_PROCESS_EXITS: v.boolean(),\r\n\r\n // logger\r\n LOGGING_LEVEL: z.coerce\r\n .number()\r\n .optional()\r\n .refine((val) => (val || 4) >= 0 && (val || 4) <= 4, {\r\n message:\r\n 'Invalid value for LOGGING_LEVEL. We only accept 0, 1, 2, 3, 4 as logging levels.'\r\n }),\r\n LOGGING_FILE: z.string().optional(),\r\n LOGGING_DEST: z.string().optional(),\r\n\r\n // ui\r\n UI_ENABLE: v.boolean(),\r\n UI_ROUTE: z.string().optional(),\r\n\r\n // other\r\n OTHER_NO_LOGO: v.boolean(),\r\n NODE_ENV: z\r\n .enum(['development', 'production', 'test'])\r\n .optional()\r\n .default('production'),\r\n\r\n // proxy (! NOT INCLUDED IN CONFIG.JS !)\r\n PROXY_SERVER_TIMEOUT: z.coerce.number().positive().optional().default(5000),\r\n PROXY_SERVER_HOST: z.string().optional().default('localhost'),\r\n PROXY_SERVER_PORT: z.coerce.number().positive().optional().default(8080)\r\n});\r\n\r\nexport const envs = Config.parse(process.env);\r\n","class ExportError extends Error {\r\n constructor(message) {\r\n super();\r\n this.message = message;\r\n this.stackMessage = message;\r\n }\r\n\r\n setError(error) {\r\n this.error = error;\r\n if (error.name) {\r\n this.name = error.name;\r\n }\r\n if (error.statusCode) {\r\n this.statusCode = error.statusCode;\r\n }\r\n if (error.stack) {\r\n this.stackMessage = error.message;\r\n this.stack = error.stack;\r\n }\r\n return this;\r\n }\r\n}\r\n\r\nexport default ExportError;\r\n","/*******************************************************************************\r\n\r\nHighcharts Export Server\r\n\r\nCopyright (c) 2016-2024, 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 { HttpsProxyAgent } from 'https-proxy-agent';\r\n\r\nimport { fetch } from './fetch.js';\r\nimport { log } from './logger.js';\r\nimport { __dirname } from './utils.js';\r\nimport { envs } from './envs.js';\r\n\r\nimport ExportError from './errors/ExportError.js';\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 and caches the Highcharts version from the sources string.\r\n *\r\n * @returns {string} The extracted Highcharts version.\r\n */\r\nexport const extractVersion = (cache) => {\r\n return cache.sources\r\n .substring(0, cache.sources.indexOf('*/'))\r\n .replace('/*', '')\r\n .replace('*/', '')\r\n .replace(/\\n/g, '')\r\n .trim();\r\n};\r\n\r\n/**\r\n * Extracts the Highcharts module name based on the scriptPath.\r\n */\r\nexport const extractModuleName = (scriptPath) => {\r\n return scriptPath.replace(\r\n /(.*)\\/|(.*)modules\\/|stock\\/(.*)indicators\\/|maps\\/(.*)modules\\//gi,\r\n ''\r\n );\r\n};\r\n\r\n/**\r\n * Saves the provided configuration and fetched modules to the cache manifest\r\n * file.\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\n * @throws {ExportError} Throws an ExportError if an error occurs while writing\r\n * the cache manifest.\r\n */\r\nexport const 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(3, '[cache] Writing a new manifest.');\r\n try {\r\n writeFileSync(\r\n join(__dirname, config.cachePath, 'manifest.json'),\r\n JSON.stringify(newManifest),\r\n 'utf8'\r\n );\r\n } catch (error) {\r\n throw new ExportError('[cache] Error writing the cache manifest.').setError(\r\n error\r\n );\r\n }\r\n};\r\n\r\n/**\r\n * Fetches a single script and updates the fetchedModules accordingly.\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 * @param {Object} fetchedModules - An object which tracks which Highcharts modules have been fetched.\r\n * @param {boolean} shouldThrowError - A flag to indicate if the error should be thrown. This should be used only for the core scripts.\r\n *\r\n * @returns {Promise} A Promise resolving to the text representation\r\n * of the fetched script.\r\n *\r\n * @throws {ExportError} Throws an ExportError if there is a problem with\r\n * fetching the script.\r\n */\r\nexport const fetchAndProcessScript = async (\r\n script,\r\n proxyAgent,\r\n fetchedModules,\r\n shouldThrowError = false\r\n) => {\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: envs.PROXY_SERVER_TIMEOUT\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 && typeof response.text == 'string') {\r\n if (fetchedModules) {\r\n const moduleName = extractModuleName(script);\r\n fetchedModules[moduleName] = 1;\r\n }\r\n\r\n return response.text;\r\n }\r\n\r\n if (shouldThrowError) {\r\n throw new ExportError(\r\n `Could not fetch the ${script}.js. The script might not exist in the requested version (status code: ${response.statusCode}).`\r\n ).setError(response);\r\n } else {\r\n log(\r\n 2,\r\n `[cache] Could not fetch the ${script}.js. The script might not exist in the requested version.`\r\n );\r\n }\r\n\r\n return '';\r\n};\r\n\r\n/**\r\n * Fetches Highcharts scripts and customScripts from the given CDNs.\r\n *\r\n * @param {string} scripts - Array of Highcharts modules to fetch.\r\n * @param {string} customScripts - Array of custom script paths to fetch (full URLs).\r\n * @param {object} proxyAgent - The proxy agent to use for a request.\r\n * @param {object} fetchedModules - An object which tracks which Highcharts modules have been fetched.\r\n * @returns {Promise} The fetched scripts content joined.\r\n */\r\nexport const fetchScripts = async (\r\n coreScripts,\r\n moduleScripts,\r\n customScripts,\r\n cdnURL,\r\n proxyAgent,\r\n fetchedModules\r\n) => {\r\n const allFetchPromises = [\r\n ...coreScripts.map((script) =>\r\n fetchAndProcessScript(\r\n `${cdnURL}${script}`,\r\n proxyAgent,\r\n fetchedModules,\r\n true\r\n )\r\n ),\r\n ...moduleScripts.map((script) =>\r\n fetchAndProcessScript(`${cdnURL}${script}`, proxyAgent, fetchedModules)\r\n ),\r\n ...customScripts.map((script) =>\r\n fetchAndProcessScript(`${script}`, proxyAgent)\r\n )\r\n ];\r\n\r\n const fetchedScripts = await Promise.all(allFetchPromises);\r\n return fetchedScripts.join(';\\n');\r\n};\r\n\r\n/**\r\n * Updates the local cache with Highcharts scripts and their versions.\r\n *\r\n * @param {Object} config - The configuration object containing information\r\n * about scripts and modules.\r\n * @param {string} sourcePath - The path to the source file in the cache.\r\n *\r\n * @returns {Promise} A Promise resolving to an object representing\r\n * the fetched modules.\r\n *\r\n * @throws {ExportError} Throws an ExportError if there is an issue updating\r\n * the local Highcharts cache.\r\n */\r\nexport const 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(\r\n 3,\r\n `[cache] Updating cache version to Highcharts: ${hcVersion || 'latest'}.`\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 // Try to create a Proxy Agent\r\n if (proxyHost && proxyPort) {\r\n try {\r\n proxyAgent = new HttpsProxyAgent({\r\n host: proxyHost,\r\n port: +proxyPort\r\n });\r\n } catch (error) {\r\n throw new ExportError('[cache] Could not create a Proxy Agent.').setError(\r\n error\r\n );\r\n }\r\n }\r\n\r\n const fetchedModules = {};\r\n try {\r\n cache.sources = await fetchScripts(\r\n [...coreScripts.map((c) => `${hcVersion}${c}`)],\r\n [\r\n ...modules.map((m) =>\r\n m === 'map'\r\n ? `maps/${hcVersion}modules/${m}`\r\n : `${hcVersion}modules/${m}`\r\n ),\r\n ...indicators.map((i) => `stock/${hcVersion}indicators/${i}`)\r\n ],\r\n customScripts,\r\n config.cdnURL || cache.cdnURL,\r\n proxyAgent,\r\n fetchedModules\r\n );\r\n\r\n cache.hcVersion = extractVersion(cache);\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 throw new ExportError(\r\n '[cache] Unable to update the local Highcharts cache.'\r\n ).setError(error);\r\n }\r\n};\r\n\r\n/**\r\n * Updates the Highcharts version in the applied configuration and checks\r\n * the cache for the new version.\r\n *\r\n * @param {string} newVersion - The new Highcharts version to be applied.\r\n *\r\n * @returns {Promise<(object|boolean)>} A Promise resolving to the updated\r\n * configuration with the new version, or false if no applied configuration\r\n * exists.\r\n */\r\nexport const updateVersion = async (newVersion) =>\r\n appliedConfig\r\n ? await checkAndUpdateCache(\r\n Object.assign(appliedConfig, {\r\n version: newVersion\r\n })\r\n )\r\n : false;\r\n\r\n/**\r\n * Checks the cache for Highcharts dependencies, updates the cache if needed,\r\n * and loads the sources.\r\n *\r\n * @param {Object} config - Highcharts-related configuration object containing information\r\n * about scripts and modules.\r\n *\r\n * @returns {Promise} A Promise that resolves once the cache is checked\r\n * and updated.\r\n *\r\n * @throws {ExportError} Throws an ExportError if there is an issue updating\r\n * or reading the cache.\r\n */\r\nexport const checkAndUpdateCache = async (config) => {\r\n const cachePath = join(__dirname, config.cachePath);\r\n\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(\r\n 2,\r\n '[cache] A Highcharts version mismatch in the cache, need to re-fetch.'\r\n );\r\n requestUpdate = true;\r\n } else if (Object.keys(manifest.modules || {}).length !== numberOfModules) {\r\n log(\r\n 2,\r\n '[cache] The cache and the requested modules do 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 2,\r\n `[cache] The ${moduleName} is missing in the 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\r\n cache.hcVersion = extractVersion(cache);\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 const getCachePath = () => {\r\n return join(__dirname, appliedConfig.cachePath);\r\n};\r\n\r\nexport default {\r\n checkAndUpdateCache,\r\n getCachePath,\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-2024, 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 {\r\n absoluteProps,\r\n defaultConfig,\r\n nestedArgs,\r\n promptsConfig\r\n} from './schemas/config.js';\r\nimport { log, logWithStack } from './logger.js';\r\nimport { deepCopy, isObject, printUsage, toBoolean } from './utils.js';\r\nimport { envs } from './envs.js';\r\n\r\nlet generalOptions = {};\r\n\r\n/**\r\n * Retrieves and returns the general options for the export process.\r\n *\r\n * @returns {Object} The 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, keeping\r\n * the principle of the options load priority. It accepts optional userOptions\r\n * and args from the CLI.\r\n *\r\n * @param {Object} userOptions - User-provided options for customization.\r\n * @param {Array} args - Command-line arguments for additional configuration\r\n * (CLI usage).\r\n *\r\n * @returns {Object} The updated 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 * Allows manual configuration based on specified prompts and saves\r\n * the configuration to a file.\r\n *\r\n * @param {string} configFileName - The name of the configuration file.\r\n *\r\n * @returns {Promise} A Promise that resolves to true once the manual\r\n * configuration is completed and saved.\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 prompt.choices ? prompt.choices[answer] : 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 logWithStack(\r\n 1,\r\n error,\r\n `[config] An error occurred while creating the ${configFileName} file.`\r\n );\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 old-structured (PhantomJS) options to a new configuration format\r\n * (Puppeteer).\r\n *\r\n * @param {Object} oldOptions - Old-structured options to be mapped.\r\n *\r\n * @returns {Object} New options structured based on the defined nestedArgs\r\n * mapping.\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 two sets of configuration options, considering absolute properties.\r\n *\r\n * @param {Object} options - Original configuration options.\r\n * @param {Object} newOptions - New configuration options to be merged.\r\n * @param {Array} absoluteProps - List of properties that should\r\n * not be recursively merged.\r\n *\r\n * @returns {Object} Merged configuration options.\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 export settings based on provided exportOptions\r\n * and generalOptions.\r\n *\r\n * @param {Object} exportOptions - Options specific to the export process.\r\n * @param {Object} generalOptions - General configuration options.\r\n *\r\n * @returns {Object} Initialized export settings.\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 additional configuration from a specified file using\r\n * the --loadConfig option.\r\n *\r\n * @param {Array} args - Command-line arguments to check for\r\n * the --loadConfig option.\r\n *\r\n * @returns {Object} Additional configuration loaded from the specified file,\r\n * or an empty object if not found or invalid.\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 logWithStack(\r\n 2,\r\n error,\r\n `[config] Unable to load the configuration from the ${fileName} file.`\r\n );\r\n }\r\n }\r\n\r\n // No additional options to return\r\n return {};\r\n}\r\n\r\n/**\r\n * Updates the default configuration object with values from a custom object\r\n * and environment variables.\r\n *\r\n * @param {Object} configObj - The default configuration object.\r\n * @param {Object} customObj - Custom configuration object to override defaults.\r\n * @param {string} propChain - Property chain for tracking nested properties\r\n * during recursion.\r\n */\r\nfunction updateDefaultConfig(configObj, customObj = {}, propChain = '') {\r\n Object.keys(configObj).forEach((key) => {\r\n const entry = configObj[key];\r\n const customValue = customObj && customObj[key];\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 in envs) {\r\n entry.value = envs[entry.envLink];\r\n }\r\n }\r\n });\r\n}\r\n\r\n/**\r\n * Initializes options object based on provided items, setting values from\r\n * nested properties recursively.\r\n *\r\n * @param {Object} items - Configuration items to be used for initializing\r\n * options.\r\n *\r\n * @returns {Object} Initialized 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 values with corresponding options in the configuration,\r\n * updating the options object.\r\n *\r\n * @param {Object} options - Configuration options object to be updated.\r\n * @param {Array} args - Command-line arguments containing values for specific\r\n * options.\r\n * @param {Object} defaultConfig - Default configuration object for reference.\r\n *\r\n * @returns {Object} Updated options object.\r\n */\r\nfunction pairArgumentValue(options, args, defaultConfig) {\r\n let showUsage = false;\r\n for (let i = 0; i < args.length; i++) {\r\n const 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 // Get the correct type for CLI args which are passed as strings\r\n let argumentType;\r\n propertiesChain.reduce((obj, prop, index) => {\r\n if (propertiesChain.length - 1 === index) {\r\n argumentType = obj[prop].type;\r\n }\r\n return obj[prop];\r\n }, defaultConfig);\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 if (argumentType === 'boolean') {\r\n obj[prop] = toBoolean(args[i]);\r\n } else if (argumentType === 'number') {\r\n obj[prop] = +args[i];\r\n } else if (argumentType.indexOf(']') >= 0) {\r\n obj[prop] = args[i].split(',');\r\n } else {\r\n obj[prop] = args[i];\r\n }\r\n } else {\r\n log(\r\n 2,\r\n `[config] Missing value for the '${option}' argument. Using the default value.`\r\n );\r\n showUsage = true;\r\n }\r\n }\r\n }\r\n return obj[prop];\r\n }, options);\r\n }\r\n\r\n // Display the usage for the reference if needed\r\n if (showUsage) {\r\n printUsage(defaultConfig);\r\n }\r\n\r\n return options;\r\n}\r\n\r\n/**\r\n * Recursively updates properties in an object based on nested names and assigns\r\n * the final value.\r\n *\r\n * @param {Object} objectToUpdate - The object to be updated.\r\n * @param {Array} nestedNames - Array of nested property names.\r\n * @param {any} value - The final value to be assigned.\r\n *\r\n * @returns {Object} Updated object with assigned values.\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-2024, 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 fs from 'fs';\r\nimport * as url from 'url';\r\nimport path from 'node:path';\r\n\r\nimport puppeteer from 'puppeteer';\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\nimport { getCachePath } from './cache.js';\r\nimport { log, logWithStack } from './logger.js';\r\n\r\nimport ExportError from './errors/ExportError.js';\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\n/**\r\n * Sets the content for a Puppeteer Page using a predefined template\r\n * and additional scripts. Also, sets the pageerror in order to catch\r\n * and display errors from the window context.\r\n *\r\n * @param {Object} page - The Puppeteer Page object for which the content\r\n * is being set.\r\n */\r\nconst setPageContent = async (page) => {\r\n await page.setContent(template);\r\n await page.addScriptTag({ path: `${getCachePath()}/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 (error) => {\r\n // TODO: Consider adding a switch here that turns on log(0) logging\r\n // on page errors.\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 ${error.toString()}`\r\n );\r\n });\r\n};\r\n\r\n/**\r\n * Clears the content of a Puppeteer Page based on the specified mode.\r\n *\r\n * @param {Object} page - The Puppeteer Page object to be cleared.\r\n * @param {boolean} hardReset - A flag indicating the type of clearing\r\n * to be performed. If true, navigates to 'about:blank' and resets content\r\n * and scripts. If false, clears the body content by setting a predefined HTML\r\n * structure.\r\n *\r\n * @throws {Error} Logs thrown error if clearing the page content fails.\r\n */\r\nexport const clearPage = async (page, hardReset = false) => {\r\n try {\r\n if (hardReset) {\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 } else {\r\n // Clear body content\r\n await page.evaluate(() => {\r\n document.body.innerHTML =\r\n '';\r\n });\r\n }\r\n } catch (error) {\r\n logWithStack(\r\n 2,\r\n error,\r\n '[browser] Could not clear the content of the page.'\r\n );\r\n }\r\n};\r\n\r\n/**\r\n * Creates a new Puppeteer Page within an existing browser instance.\r\n *\r\n * If the browser instance is not available, returns false.\r\n *\r\n * The function creates a new page, disables caching, sets content using\r\n * setPageContent(), and returns the created Puppeteer Page.\r\n *\r\n * @returns {(boolean|object)} Returns false if the browser instance is not\r\n * available, or a Puppeteer Page object representing the newly created page.\r\n */\r\nexport const newPage = async () => {\r\n if (!browser) {\r\n return false;\r\n }\r\n\r\n const page = await browser.newPage();\r\n\r\n // Disable cache\r\n await page.setCacheEnabled(false);\r\n\r\n // Set the content\r\n await setPageContent(page);\r\n return page;\r\n};\r\n\r\n/**\r\n * Creates a Puppeteer browser instance with the specified arguments.\r\n *\r\n * @param {Array} puppeteerArgs - Additional arguments for Puppeteer launch.\r\n *\r\n * @returns {Promise} A Promise resolving to the Puppeteer browser\r\n * instance.\r\n *\r\n * @throws {ExportError} Throws an ExportError if max retries to open a browser\r\n * instance are reached, or if no browser instance is found after retries.\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 ${++tryCount}).`\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 (error) {\r\n logWithStack(\r\n 1,\r\n error,\r\n '[browser] Failed to launch a browser instance.'\r\n );\r\n\r\n // Retry to launch browser until reaching max attempts\r\n if (tryCount < 25) {\r\n log(3, `[browser] Retry to open a browser (${tryCount} out of 25).`);\r\n await new Promise((response) => setTimeout(response, 4000));\r\n await open();\r\n } else {\r\n throw error;\r\n }\r\n }\r\n };\r\n\r\n try {\r\n await open();\r\n } catch (error) {\r\n throw new ExportError(\r\n '[browser] Maximum retries to open a browser instance reached.'\r\n ).setError(error);\r\n }\r\n\r\n if (!browser) {\r\n throw new ExportError('[browser] Cannot find a browser to open.');\r\n }\r\n }\r\n\r\n // Return a browser promise\r\n return browser;\r\n};\r\n\r\n/**\r\n * Retrieves the existing Puppeteer browser instance.\r\n *\r\n * @returns {Promise} A Promise resolving to the Puppeteer browser\r\n * instance.\r\n *\r\n * @throws {ExportError} Throws an ExportError if no valid browser has been\r\n * created.\r\n */\r\nexport const get = async () => {\r\n if (!browser) {\r\n throw new ExportError('[browser] No valid browser has been created.');\r\n }\r\n\r\n return browser;\r\n};\r\n\r\n/**\r\n * Closes the Puppeteer browser instance if it is connected.\r\n *\r\n * @returns {Promise} A Promise resolving to true after the browser\r\n * is closed.\r\n */\r\nexport const close = async () => {\r\n // Close the browser when connnected\r\n if (browser?.isConnected()) {\r\n await browser.close();\r\n log(4, '[browser] Closed the browser.');\r\n }\r\n return true;\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-2024, 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 path from 'path';\r\nimport * as url from 'url';\r\n\r\nimport cache from './cache.js';\r\nimport { log, logWithStack } from './logger.js';\r\nimport svgTemplate from './../templates/svg_export/svg_export.js';\r\n\r\nimport ExportError from './errors/ExportError.js';\r\n\r\nconst __basedir = url.fileURLToPath(new URL('.', import.meta.url));\r\n\r\n/**\r\n * Retrieves the clipping region coordinates of the specified page element with\r\n * the id 'chart-container'.\r\n *\r\n * @param {Object} page - Puppeteer page object.\r\n *\r\n * @returns {Promise} Promise resolving to an object containing\r\n * x, y, width, and height properties.\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 * Creates an image using Puppeteer's page screenshot functionality with\r\n * specified options.\r\n *\r\n * @param {Object} page - Puppeteer page object.\r\n * @param {string} type - Image type.\r\n * @param {string} encoding - Image encoding.\r\n * @param {Object} clip - Clipping region coordinates.\r\n * @param {number} rasterizationTimeout - Timeout for rasterization\r\n * in milliseconds.\r\n *\r\n * @returns {Promise} Promise resolving to the image buffer or rejecting\r\n * with an ExportError for timeout.\r\n */\r\nconst createImage = (page, type, encoding, clip, rasterizationTimeout) =>\r\n 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 the expected type\r\n // format is PNG\r\n omitBackground: type == 'png'\r\n }),\r\n new Promise((_resolve, reject) =>\r\n setTimeout(\r\n () => reject(new ExportError('Rasterization timeout')),\r\n rasterizationTimeout || 1500\r\n )\r\n )\r\n ]);\r\n\r\n/**\r\n * Creates a PDF using Puppeteer's page pdf functionality with specified\r\n * options.\r\n *\r\n * @param {Object} page - Puppeteer page object.\r\n * @param {number} height - PDF height.\r\n * @param {number} width - PDF width.\r\n * @param {string} encoding - PDF encoding.\r\n *\r\n * @returns {Promise} Promise resolving to the PDF buffer.\r\n */\r\nconst createPDF = (page, height, width, encoding) =>\r\n 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 * Creates an SVG string by evaluating the outerHTML of the first 'svg' element\r\n * inside an element with the id 'container'.\r\n *\r\n * @param {Object} page - Puppeteer page object.\r\n *\r\n * @returns {Promise} Promise resolving to the SVG string.\r\n */\r\nconst createSVG = (page) =>\r\n page.$eval('#container svg:first-of-type', (element) => element.outerHTML);\r\n\r\n/**\r\n * Sets the specified chart and options as configuration into the triggerExport\r\n * function within the window context using page.evaluate.\r\n *\r\n * @param {Object} page - Puppeteer page object.\r\n * @param {any} chart - The chart object to be configured.\r\n * @param {Object} options - Configuration options for the chart.\r\n *\r\n * @returns {Promise} Promise resolving after the configuration is set.\r\n */\r\nconst setAsConfig = (page, chart, options) =>\r\n 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/**\r\n * Exports to a chart from a page using Puppeteer.\r\n *\r\n * @param {Object} page - Puppeteer page object.\r\n * @param {any} chart - The chart object or SVG configuration to be exported.\r\n * @param {Object} options - Export options and configuration.\r\n *\r\n * @returns {Promise} Promise resolving to\r\n * the exported data or rejecting with an ExportError.\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 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 let isSVG;\r\n if (\r\n chart.indexOf &&\r\n (chart.indexOf('= 0 || chart.indexOf('= 0)\r\n ) {\r\n // SVG input handling\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 await page.setContent(svgTemplate(chart));\r\n } else {\r\n // JSON config handling\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 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 } else {\r\n // Basic configuration export\r\n chart.chart.height = exportOptions.height;\r\n chart.chart.width = exportOptions.width;\r\n\r\n await setAsConfig(page, chart, options);\r\n }\r\n }\r\n\r\n // Use resources\r\n const resources = options.customLogic.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 (error) {\r\n logWithStack(\r\n 2,\r\n error,\r\n `[export] The JS file ${file} cannot be loaded.`\r\n );\r\n }\r\n }\r\n }\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.customLogic.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\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 (element, scale) => ({\r\n chartHeight: element.height.baseVal.value * scale,\r\n chartWidth: element.width.baseVal.value * scale\r\n }),\r\n parseFloat(exportOptions.scale)\r\n )\r\n : await page.evaluate(() => {\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 // 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 let data;\r\n // RASTERIZATION\r\n if (exportOptions.type === 'svg') {\r\n // SVG\r\n data = await createSVG(page);\r\n } else if (['png', 'jpeg'].includes(exportOptions.type)) {\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 exportOptions.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 new ExportError(\r\n `[export] Unsupported output format ${exportOptions.type}.`\r\n );\r\n }\r\n\r\n // Destroy old charts after the export is done\r\n await page.evaluate(() => {\r\n // We are not guaranteed that Highcharts is loaded, e,g, when doing SVG\r\n // exports\r\n if (typeof Highcharts !== 'undefined') {\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 (Array.isArray(oldCharts) && 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\r\n await clearInjected(page);\r\n return data;\r\n } catch (error) {\r\n await clearInjected(page);\r\n return error;\r\n }\r\n};\r\n","/*******************************************************************************\r\n\r\nHighcharts Export Server\r\n\r\nCopyright (c) 2016-2024, 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-2024, 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 { Pool } from 'tarn';\r\nimport { v4 as uuid } from 'uuid';\r\n\r\nimport {\r\n close as browserClose,\r\n create as createBrowser,\r\n newPage as browserNewPage,\r\n clearPage\r\n} from './browser.js';\r\nimport puppeteerExport from './export.js';\r\nimport { log, logWithStack } from './logger.js';\r\nimport { measureTime } from './utils.js';\r\n\r\nimport ExportError from './errors/ExportError.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 page for the export pool.\r\n *\r\n * @returns {Object} - An object containing the worker ID, a reference to the\r\n * browser page, and initial work count.\r\n *\r\n * @throws {ExportError} - If there's an error during the creation of the new\r\n * page.\r\n */\r\n create: async () => {\r\n let page = false;\r\n\r\n const id = uuid();\r\n const startDate = new Date().getTime();\r\n\r\n try {\r\n page = await browserNewPage();\r\n\r\n if (!page || page.isClosed()) {\r\n throw new ExportError('The page is invalid or closed.');\r\n }\r\n\r\n log(\r\n 3,\r\n `[pool] Successfully created a worker ${id} - took ${\r\n new Date().getTime() - startDate\r\n } ms.`\r\n );\r\n } catch (error) {\r\n throw new ExportError(\r\n 'Error encountered when creating a new page.'\r\n ).setError(error);\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 page in the export pool, checking if it has exceeded\r\n * the work limit.\r\n *\r\n * @param {Object} workerHandle - The handle to the worker, containing the\r\n * worker's ID, a reference to the browser page, and work count.\r\n *\r\n * @returns {boolean} - Returns true if the worker is valid and within\r\n * the work limit; otherwise, returns false.\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: 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, true);\r\n return true;\r\n },\r\n\r\n /**\r\n * Destroys a worker entry in the export pool, closing its associated page.\r\n *\r\n * @param {Object} workerHandle - The handle to the worker, containing\r\n * the worker's ID and a reference to the browser page.\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\r\n/**\r\n * Initializes the export pool with the provided configuration, creating\r\n * a browser instance and setting up worker resources.\r\n *\r\n * @param {Object} config - Configuration options for the export pool along\r\n * with custom puppeteer arguments for the puppeteer.launch function.\r\n */\r\nexport const initPool = async (config) => {\r\n // For the module scope usage\r\n poolConfig = config && config.pool ? { ...config.pool } : {};\r\n\r\n // Attach process' exit listeners\r\n if (poolConfig.listenToProcessExits) {\r\n attachProcessExitListeners();\r\n }\r\n\r\n // The newest puppeteer arguments for the browser creation\r\n puppeteerArgs = config.puppeteerArgs;\r\n\r\n // Create a browser instance\r\n await createBrowser(puppeteerArgs);\r\n\r\n log(\r\n 3,\r\n `[pool] Initializing pool with workers: 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 if (parseInt(poolConfig.minWorkers) > parseInt(poolConfig.maxWorkers)) {\r\n poolConfig.minWorkers = poolConfig.maxWorkers;\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: parseInt(poolConfig.minWorkers),\r\n max: parseInt(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('release', async (resource) => {\r\n // Clear page\r\n await clearPage(resource.page, false);\r\n log(4, `[pool] Releasing a worker with ID ${resource.id}.`);\r\n });\r\n\r\n pool.on('destroySuccess', (eventId, resource) => {\r\n log(4, `[pool] Destroyed a worker with 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 logWithStack(2, error, '[pool] Could not create an initial resource.');\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${initialResources.length ? ` with ${initialResources.length} initial resources waiting.` : '.'}`\r\n );\r\n } catch (error) {\r\n // Close browser if for some reason cannot establish the pool\r\n await browserClose();\r\n throw new ExportError(\r\n '[pool] Could not create the pool of workers.'\r\n ).setError(error);\r\n }\r\n};\r\n\r\n/**\r\n * Attaches exit listeners to the process, ensuring proper cleanup of resources\r\n * and termination on exit signals. Handles 'exit', 'SIGINT', 'SIGTERM', and\r\n * 'uncaughtException' events.\r\n */\r\nexport function attachProcessExitListeners() {\r\n log(3, '[pool] Attaching exit listeners to the process.');\r\n\r\n // Kill all pool resources on exit\r\n process.on('exit', async (code) => {\r\n log(4, `Process exited with code ${code}.`);\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 logWithStack(1, error, `The ${name} error.`);\r\n await killPool();\r\n process.exit(1);\r\n });\r\n}\r\n\r\n/**\r\n * Kills all workers in the pool, destroys the pool, and closes the browser\r\n * instance.\r\n *\r\n * @returns {Promise} A promise that resolves after the workers are\r\n * killed, the pool is destroyed, and the browser is closed.\r\n */\r\nexport async function killPool() {\r\n log(3, '[pool] Killing all pool workers and browser, if any exist.');\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 return browserClose();\r\n }\r\n\r\n // If still alive, destroy the pool of pages before closing a browser\r\n if (pool) {\r\n await pool.destroy();\r\n log(4, '[browser] Destroyed the pool of resources.');\r\n }\r\n\r\n // Close the browser instance\r\n return browserClose();\r\n}\r\n\r\n/**\r\n * Processes the export work using a worker from the pool. Acquires a worker\r\n * handle from the pool, performs the export using puppeteer, and releases\r\n * the worker handle back to the pool.\r\n *\r\n * @param {string} chart - The chart data or configuration to be exported.\r\n * @param {Object} options - Export options and configuration.\r\n *\r\n * @returns {Promise} A promise that resolves with the export resultand\r\n * options.\r\n *\r\n * @throws {ExportError} If an error occurs during the export process.\r\n */\r\nexport const postWork = async (chart, options) => {\r\n let workerHandle;\r\n\r\n try {\r\n log(4, '[pool] Work received, starting to process.');\r\n\r\n ++exportAttempts;\r\n if (poolConfig.benchmarking) {\r\n getPoolInfo();\r\n }\r\n\r\n if (!pool) {\r\n throw new ExportError('Work received, but pool has not been started.');\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 a worker handle.');\r\n const acquireCounter = measureTime();\r\n workerHandle = await pool.acquire().promise;\r\n\r\n // Check the page acquire time\r\n if (options.server.benchmarking) {\r\n log(\r\n 5,\r\n options.payload?.requestId\r\n ? `[benchmark] Request with ID ${options.payload?.requestId} -`\r\n : '[benchmark]',\r\n `Acquired a worker handle: ${acquireCounter()}ms.`\r\n );\r\n }\r\n } catch (error) {\r\n throw new ExportError(\r\n 'Error encountered when acquiring an available entry.'\r\n ).setError(error);\r\n }\r\n log(4, '[pool] Acquired a worker handle.');\r\n\r\n if (!workerHandle.page) {\r\n throw new ExportError(\r\n 'Resolved worker page is invalid: the pool setup is wonky.'\r\n );\r\n }\r\n\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 with ID ${workerHandle.id}.`);\r\n\r\n // Perform an export on a puppeteer level\r\n const exportCounter = measureTime();\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 throw new ExportError('Error encountered during export.').setError(\r\n result\r\n );\r\n }\r\n\r\n // Check the Puppeteer export time\r\n if (options.server.benchmarking) {\r\n log(\r\n 5,\r\n options.payload?.requestId\r\n ? `[benchmark] Request with ID ${options.payload?.requestId} -`\r\n : '[benchmark]',\r\n `Exported a chart sucessfully: ${exportCounter()}ms.`\r\n );\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 result,\r\n options\r\n };\r\n } catch (error) {\r\n ++droppedExports;\r\n\r\n if (workerHandle) {\r\n pool.release(workerHandle);\r\n }\r\n\r\n throw new ExportError(`[pool] In pool.postWork: ${error.message}`).setError(\r\n error\r\n );\r\n }\r\n};\r\n\r\n/**\r\n * Retrieves the current pool instance.\r\n *\r\n * @returns {Object|null} The current pool instance if initialized, or null\r\n * if the pool has not been created.\r\n */\r\nexport function getPool() {\r\n return pool;\r\n}\r\n\r\n/**\r\n * Retrieves pool information in JSON format, including minimum and maximum\r\n * workers, available workers, workers in use, and pending acquire requests.\r\n *\r\n * @returns {Object} Pool information in JSON format.\r\n */\r\nexport const getPoolInfoJSON = () => ({\r\n min: pool.min,\r\n max: pool.max,\r\n available: pool.numFree(),\r\n inUse: pool.numUsed(),\r\n pendingAcquire: pool.numPendingAcquires()\r\n});\r\n\r\n/**\r\n * Logs information about the current state of the pool, including the minimum\r\n * and maximum workers, available workers, workers in use, and pending acquire\r\n * requests.\r\n */\r\nexport function getPoolInfo() {\r\n const { min, max } = pool;\r\n\r\n log(5, `[pool] The minimum number of resources allowed by pool: ${min}.`);\r\n log(5, `[pool] The maximum number of resources allowed by pool: ${max}.`);\r\n log(\r\n 5,\r\n `[pool] The number of resources that are currently available: ${pool.numFree()}.`\r\n );\r\n log(\r\n 5,\r\n `[pool] The number of resources that are currently acquired: ${pool.numUsed()}.`\r\n );\r\n log(\r\n 5,\r\n `[pool] The number of callers waiting to acquire a resource: ${pool.numPendingAcquires()}.`\r\n );\r\n}\r\n\r\nexport default {\r\n initPool,\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-2024, 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, writeFileSync } from 'fs';\r\n\r\nimport { getOptions, initExportSettings } from './config.js';\r\nimport { log, logWithStack } from './logger.js';\r\nimport { killPool, postWork } from './pool.js';\r\nimport {\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\n\r\nimport ExportError from './errors/ExportError.js';\r\n\r\nlet allowCodeExecution = false;\r\n\r\n/**\r\n * Starts an export process. The `settings` contains final options gathered\r\n * from all possible sources (config, env, cli, json). The `endCallback` is\r\n * called when the export is completed, with an error object as the first\r\n * argument and the second containing the base64 respresentation of a chart.\r\n *\r\n * @param {Object} settings - The settings object containing export\r\n * configuration.\r\n * @param {function} endCallback - The callback function to be invoked upon\r\n * finalizing work or upon error occurance of the exporting process.\r\n *\r\n * @returns {void} This function does not return a value directly; instead,\r\n * it communicates results via the endCallback.\r\n */\r\nexport const startExport = async (settings, endCallback) => {\r\n // Starting exporting process message\r\n log(4, '[chart] Starting the 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 try {\r\n log(4, '[chart] Attempting to export from a SVG input.');\r\n return exportAsString(options.payload.svg.trim(), options, endCallback);\r\n } catch (error) {\r\n return endCallback(\r\n new ExportError('[chart] Error loading SVG input.').setError(error)\r\n );\r\n }\r\n }\r\n\r\n // Export using options from the file\r\n if (exportOptions.infile && exportOptions.infile.length) {\r\n // Try to read the file to get the string representation\r\n try {\r\n log(4, '[chart] Attempting to export from an input file.');\r\n options.export.instr = readFileSync(exportOptions.infile, 'utf8');\r\n return exportAsString(options.export.instr.trim(), options, endCallback);\r\n } catch (error) {\r\n return endCallback(\r\n new ExportError('[chart] Error loading input file.').setError(error)\r\n );\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 try {\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.customLogic?.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 } catch (error) {\r\n return endCallback(\r\n new ExportError('[chart] Error loading raw input.').setError(error)\r\n );\r\n }\r\n }\r\n\r\n // No input specified, pass an error message to the callback\r\n return endCallback(\r\n new ExportError(\r\n `[chart] No valid input specified. Check if at least one of the following parameters is correctly set: 'infile', 'instr', 'options', or 'svg'.`\r\n )\r\n );\r\n};\r\n\r\n/**\r\n * Starts a batch export process for multiple charts based on the information\r\n * in the batch option. The batch is a string in the following format:\r\n * \"infile1.json=outfile1.png;infile2.json=outfile2.png;...\"\r\n *\r\n * @param {Object} options - The options object containing configuration for\r\n * a batch export.\r\n *\r\n * @returns {Promise} A Promise that resolves once the batch export\r\n * process is completed.\r\n *\r\n * @throws {ExportError} Throws an ExportError if an error occurs during\r\n * any of the batch export process.\r\n */\r\nexport const batchExport = async (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 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 (error, info) => {\r\n // Throw an error\r\n if (error) {\r\n throw 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.result, 'base64')\r\n );\r\n }\r\n )\r\n );\r\n }\r\n }\r\n\r\n try {\r\n // Await all exports are done\r\n await Promise.all(batchFunctions);\r\n\r\n // Kill pool and close browser after finishing batch export\r\n await killPool();\r\n } catch (error) {\r\n throw new ExportError(\r\n '[chart] Error encountered during batch export.'\r\n ).setError(error);\r\n }\r\n};\r\n\r\n/**\r\n * Starts a single export process based on the specified options.\r\n *\r\n * @param {Object} options - The options object containing configuration for\r\n * a single export.\r\n *\r\n * @returns {Promise} A Promise that resolves once the single export\r\n * process is completed.\r\n *\r\n * @throws {ExportError} Throws an ExportError if an error occurs during\r\n * the single export process.\r\n */\r\nexport const singleExport = async (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 await startExport(options, async (error, info) => {\r\n // Exit process when error\r\n if (error) {\r\n throw error;\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.result, 'base64') : info.result\r\n );\r\n\r\n // Kill the pool\r\n await killPool();\r\n });\r\n};\r\n\r\n/**\r\n * Determines the size and scale for chart export based on the provided options.\r\n *\r\n * @param {Object} options - The options object containing configuration for\r\n * chart export.\r\n *\r\n * @returns {Object} An object containing the calculated height, width,\r\n * and scale for the chart export.\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 const size = {\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 // Get rid of potential px and %\r\n for (let [param, value] of Object.entries(size)) {\r\n size[param] =\r\n typeof value === 'string' ? +value.replace(/px|%/gi, '') : value;\r\n }\r\n return size;\r\n};\r\n\r\n/**\r\n * Function for finalizing options before export.\r\n *\r\n * @param {Object} options - The options object containing configuration for\r\n * the export process.\r\n * @param {Object} chartJson - The JSON representation of the chart.\r\n * @param {Function} endCallback - The callback function to be called upon\r\n * completion or error.\r\n * @param {string} svg - The SVG representation of the chart.\r\n *\r\n * @returns {Promise} A Promise that resolves once the export process\r\n * is completed.\r\n */\r\nconst doExport = async (options, chartJson, endCallback, svg) => {\r\n let { export: exportOptions, customLogic: customLogicOptions } = options;\r\n\r\n const allowCodeExecutionScoped =\r\n typeof customLogicOptions.allowCodeExecution === 'boolean'\r\n ? customLogicOptions.allowCodeExecution\r\n : allowCodeExecution;\r\n\r\n if (!customLogicOptions) {\r\n customLogicOptions = options.customLogic = {};\r\n } else if (allowCodeExecutionScoped) {\r\n if (typeof options.customLogic.resources === 'string') {\r\n // Process resources\r\n options.customLogic.resources = handleResources(\r\n options.customLogic.resources,\r\n toBoolean(options.customLogic.allowFileResources)\r\n );\r\n } else if (!options.customLogic.resources) {\r\n try {\r\n const resources = readFileSync('resources.json', 'utf8');\r\n options.customLogic.resources = handleResources(\r\n resources,\r\n toBoolean(options.customLogic.allowFileResources)\r\n );\r\n } catch (error) {\r\n logWithStack(\r\n 2,\r\n error,\r\n `[chart] Unable to load the default resources.json file.`\r\n );\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 && customLogicOptions) {\r\n if (\r\n customLogicOptions.callback ||\r\n customLogicOptions.resources ||\r\n customLogicOptions.customCode\r\n ) {\r\n // Send back a friendly message saying that the exporter does not support\r\n // these settings.\r\n return endCallback(\r\n new ExportError(\r\n `[chart] The 'callback', 'resources' and 'customCode' options have been disabled for this server.`\r\n )\r\n );\r\n }\r\n\r\n // Reset all additional custom code\r\n customLogicOptions.callback = false;\r\n customLogicOptions.resources = false;\r\n customLogicOptions.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 logWithStack(2, error, `[chart] The '${optionsName}' cannot be loaded.`);\r\n }\r\n });\r\n\r\n // Prepare the customCode\r\n if (customLogicOptions.allowCodeExecution) {\r\n try {\r\n customLogicOptions.customCode = wrapAround(\r\n customLogicOptions.customCode,\r\n customLogicOptions.allowFileResources\r\n );\r\n } catch (error) {\r\n logWithStack(2, error, `[chart] The 'customCode' cannot be loaded.`);\r\n }\r\n }\r\n\r\n // Get the callback\r\n if (\r\n customLogicOptions &&\r\n customLogicOptions.callback &&\r\n customLogicOptions.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 (customLogicOptions.allowFileResources) {\r\n try {\r\n customLogicOptions.callback = readFileSync(\r\n customLogicOptions.callback,\r\n 'utf8'\r\n );\r\n } catch (error) {\r\n customLogicOptions.callback = false;\r\n logWithStack(2, error, `[chart] The 'callback' cannot be loaded.`);\r\n }\r\n } else {\r\n customLogicOptions.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 try {\r\n const result = await postWork(\r\n exportOptions.strInj || chartJson || svg,\r\n options\r\n );\r\n return endCallback(false, result);\r\n } catch (error) {\r\n return endCallback(error);\r\n }\r\n};\r\n\r\n/**\r\n * Performs a direct inject of options before export. The function attempts\r\n * to stringify the provided options and removes unnecessary characters,\r\n * ensuring a clean and formatted input. The resulting string is saved as\r\n * a \"stright inject\" string in the export options. It then invokes the\r\n * doExport function with the updated options.\r\n *\r\n * IMPORTANT: Dangerous and must be used deliberately by someone who sets up\r\n * a server (see the --allowCodeExecution option).\r\n *\r\n * @param {Object} options - The export options containing the input\r\n * to be injected.\r\n * @param {function} endCallback - The callback function to be invoked\r\n * at the end of the process.\r\n *\r\n * @returns {Promise} A Promise that resolves with the result of the export\r\n * operation or rejects with an error if any issues occur during the process.\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.customLogic?.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 return endCallback(\r\n new ExportError(\r\n `[chart] Malformed input detected for ${options.export?.requestId || '?'}. Please make sure that your JSON/JavaScript options are sent using the \"options\" attribute, and that if you're using SVG, it is unescaped.`\r\n ).setError(error)\r\n );\r\n }\r\n};\r\n\r\n/**\r\n * Exports a string based on the provided options and invokes an end callback.\r\n *\r\n * @param {string} stringToExport - The string content to be exported.\r\n * @param {Object} options - Export options, including customLogic with\r\n * allowCodeExecution flag.\r\n * @param {Function} endCallback - Callback function to be invoked at the end\r\n * of the export process.\r\n *\r\n * @returns {any} Result of the export process or an error if encountered.\r\n */\r\nconst exportAsString = (stringToExport, options, endCallback) => {\r\n const { allowCodeExecution } = options.customLogic;\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 endCallback(\r\n new ExportError(\r\n '[chart] Only JSON configurations and SVG are allowed for this server. If this is your server, JavaScript custom code can be enabled by starting the server with the --allowCodeExecution flag.'\r\n ).setError(error)\r\n );\r\n }\r\n }\r\n};\r\n\r\n/**\r\n * Retrieves and returns the current status of code execution permission.\r\n *\r\n * @returns {any} The value of allowCodeExecution.\r\n */\r\nexport const getAllowCodeExecution = () => allowCodeExecution;\r\n\r\n/**\r\n * Sets the code execution permission based on the provided boolean value.\r\n *\r\n * @param {any} value - The value to be converted and assigned\r\n * to allowCodeExecution.\r\n */\r\nexport const setAllowCodeExecution = (value) => {\r\n allowCodeExecution = toBoolean(value);\r\n};\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","import { logWithStack } from '../logger.js';\r\nimport { envs } from '../envs.js';\r\n\r\n/**\r\n * Middleware for logging errors with stack trace and handling error response.\r\n *\r\n * @param {Error} error - The error object.\r\n * @param {Express.Request} req - The Express request object.\r\n * @param {Express.Response} res - The Express response object.\r\n * @param {Function} next - The next middleware function.\r\n */\r\nconst logErrorMiddleware = (error, req, res, next) => {\r\n // Display the error with stack in a correct format\r\n logWithStack(1, error);\r\n\r\n // Delete the stack for the environment other than the development\r\n if (envs.NODE_ENV !== 'development') {\r\n delete error.stack;\r\n }\r\n\r\n // Call the returnErrorMiddleware\r\n next(error);\r\n};\r\n\r\n/**\r\n * Middleware for returning error response.\r\n *\r\n * @param {Error} error - The error object.\r\n * @param {Express.Request} req - The Express request object.\r\n * @param {Express.Response} res - The Express response object.\r\n * @param {Function} next - The next middleware function.\r\n */\r\nconst returnErrorMiddleware = (error, req, res, next) => {\r\n // Gather all requied information for the response\r\n const { statusCode: stCode, status, message, stack } = error;\r\n const statusCode = stCode || status || 500;\r\n\r\n // Set and return response\r\n res.status(statusCode).json({ statusCode, message, stack });\r\n};\r\n\r\nexport default (app) => {\r\n // Add log error middleware\r\n app.use(logErrorMiddleware);\r\n\r\n // Add set status and return error middleware\r\n app.use(returnErrorMiddleware);\r\n};\r\n","/*******************************************************************************\r\n\r\nHighcharts Export Server\r\n\r\nCopyright (c) 2016-2024, 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 { log } from '../logger.js';\r\n\r\n/**\r\n * Middleware for enabling rate limiting on the specified Express app.\r\n *\r\n * @param {Express} app - The Express app instance.\r\n * @param {Object} limitConfig - Configuration options for 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 `[rate limiting] Enabled rate limiting with ${rateOptions.max} requests per ${rateOptions.window} minute for each IP, trusting proxy: ${rateOptions.trustProxy}.`\r\n );\r\n};\r\n","import ExportError from './ExportError.js';\r\n\r\nclass HttpError extends ExportError {\r\n constructor(message, status) {\r\n super(message);\r\n this.status = this.statusCode = status;\r\n }\r\n\r\n setStatus(status) {\r\n this.status = status;\r\n return this;\r\n }\r\n}\r\n\r\nexport default HttpError;\r\n","/*******************************************************************************\r\n\r\nHighcharts Export Server\r\n\r\nCopyright (c) 2016-2024, 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 fixType,\r\n isCorrectJSON,\r\n isObjectEmpty,\r\n isPrivateRangeUrlFound,\r\n optionsStringify,\r\n measureTime\r\n} from '../../utils.js';\r\n\r\nimport HttpError from '../../errors/HttpError.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\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 * Invokes an array of callback functions with specified parameters, allowing\r\n * customization of request handling.\r\n *\r\n * @param {Function[]} callbacks - An array of callback functions\r\n * to be executed.\r\n * @param {Express.Request} request - The Express request object.\r\n * @param {Express.Response} response - The Express response object.\r\n * @param {Object} data - An object containing parameters like id, uniqueId,\r\n * type, and body.\r\n *\r\n * @returns {boolean} - Returns a boolean indicating the overall result\r\n * of the callback invocations.\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 the export requests from the client.\r\n *\r\n * @param {Express.Request} request - The Express request object.\r\n * @param {Express.Response} response - The Express response object.\r\n * @param {Function} next - The next middleware function.\r\n *\r\n * @returns {Promise} - A promise that resolves once the export process\r\n * is complete.\r\n */\r\nconst exportHandler = async (request, response, next) => {\r\n try {\r\n // Start counting time\r\n const stopCounter = measureTime();\r\n\r\n // Create a unique ID for a request\r\n const uniqueId = uuid().replace(/-/g, '');\r\n\r\n // Get the current server's general options\r\n const defaultOptions = getOptions();\r\n\r\n const body = request.body;\r\n const id = ++requestsCounter;\r\n\r\n let type = fixType(body.type);\r\n\r\n // Throw 'Bad Request' if there's no body\r\n if (!body || isObjectEmpty(body)) {\r\n throw new HttpError(\r\n 'The request body is required. Please ensure that your Content-Type header is correct (accepted types are application/json and multipart/form-data).',\r\n 400\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 // 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 `The request with ID ${uniqueId} from ${\r\n request.headers['x-forwarded-for'] || request.connection.remoteAddress\r\n } was incorrect. Payload received: ${JSON.stringify(body)}.`\r\n );\r\n\r\n throw new HttpError(\r\n \"No correct chart data found. Ensure that you are using either application/json or multipart/form-data headers. If sending JSON, make sure the chart data is in the 'infile', 'options', or 'data' attribute. If sending SVG, ensure it is in the 'svg' attribute.\",\r\n 400\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 // 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 with ID ${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 customLogic: {\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 if (instr) {\r\n // Stringify JSON with options\r\n requestOptions.export.instr = optionsStringify(\r\n instr,\r\n requestOptions.customLogic.allowCodeExecution\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 // 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 noDownload: body.noDownload || false,\r\n requestId: uniqueId\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 throw new HttpError(\r\n 'SVG potentially contain at least one forbidden URL in xlink:href element. Please review the SVG content and ensure that all referenced URLs comply with security policies.',\r\n 400\r\n );\r\n }\r\n\r\n // Start the export process\r\n await startExport(options, (error, info) => {\r\n // Remove the close event from the socket\r\n request.socket.removeAllListeners('close');\r\n\r\n // After the whole exporting process\r\n if (defaultOptions.server.benchmarking) {\r\n log(\r\n 5,\r\n `[benchmark] Request with ID ${uniqueId} - After the whole exporting process: ${stopCounter()}ms.`\r\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 `[export] The client closed the connection before the chart finished processing.`\r\n );\r\n }\r\n\r\n // If error, log it and send it to the error middleware\r\n if (error) {\r\n throw error;\r\n }\r\n\r\n // If data is missing, log the message and send it to the error middleware\r\n if (!info || !info.result) {\r\n throw new HttpError(\r\n `Unexpected return from chart generation. Please check your request data. For the request with ID ${uniqueId}, the result is ${info.result}.`,\r\n 400\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.result });\r\n\r\n if (info.result) {\r\n // If only base64 is required, return it\r\n if (body.b64) {\r\n // SVG Exception for the Highcharts 11.3.0 version\r\n if (type === 'pdf' || type == 'svg') {\r\n return response.send(\r\n Buffer.from(info.result, 'utf8').toString('base64')\r\n );\r\n }\r\n\r\n return response.send(info.result);\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.result)\r\n : response.send(Buffer.from(info.result, 'base64'));\r\n }\r\n });\r\n } catch (error) {\r\n next(error);\r\n }\r\n};\r\n\r\nexport default (app) => {\r\n /**\r\n * Adds the POST / a route for handling POST requests at the root endpoint.\r\n */\r\n app.post('/', exportHandler);\r\n\r\n /**\r\n * Adds the POST /:filename a route for handling POST requests with\r\n * a specified filename parameter.\r\n */\r\n app.post('/:filename', exportHandler);\r\n};\r\n","/*******************************************************************************\r\n\r\nHighcharts Export Server\r\n\r\nCopyright (c) 2016-2024, 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 { join as pather } from 'path';\r\n\r\nimport cache from '../../cache.js';\r\nimport pool from '../../pool.js';\r\nimport { __dirname } from '../../utils.js';\r\n\r\nconst pkgFile = JSON.parse(readFileSync(pather(__dirname, 'package.json')));\r\n\r\nconst serverStartTime = new Date();\r\n\r\n/**\r\n * Adds the GET /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: pkgFile.version,\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-2024, 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 cors from 'cors';\r\nimport express from 'express';\r\nimport http from 'http';\r\nimport https from 'https';\r\nimport multer from 'multer';\r\n\r\nimport errorHandler from './error.js';\r\nimport { log, logWithStack } from '../logger.js';\r\nimport rateLimit from './rate_limit.js';\r\nimport { __dirname } from '../utils.js';\r\n\r\nimport vSwitchRoute from './routes/change_hc_version.js';\r\nimport exportRoutes from './routes/export.js';\r\nimport healthRoute from './routes/health.js';\r\nimport uiRoute from './routes/ui.js';\r\n\r\nimport ExportError from '../errors/ExportError.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 fieldSize: 50 * 1024 * 1024\r\n }\r\n});\r\n\r\n// Enable body parser\r\napp.use(express.json({ limit: 50 * 1024 * 1024 }));\r\napp.use(express.urlencoded({ extended: true, limit: 50 * 1024 * 1024 }));\r\n\r\n// Use only non-file multipart form fields\r\napp.use(upload.none());\r\n\r\n/**\r\n * Attach error handlers to the server.\r\n *\r\n * @param {http.Server} server - The HTTP/HTTPS server instance.\r\n */\r\nconst attachErrorHandlers = (server) => {\r\n server.on('clientError', (error) => {\r\n logWithStack(1, error, `[server] Client error: ${error.message}`);\r\n });\r\n server.on('error', (error) => {\r\n logWithStack(1, error, `[server] Server error: ${error.message}`);\r\n });\r\n server.on('connection', (socket) => {\r\n socket.on('error', (error) => {\r\n logWithStack(1, error, `[server] Socket error: ${error.message}`);\r\n });\r\n });\r\n};\r\n\r\n/**\r\n * Starts an HTTP server based on the provided configuration. The `serverConfig`\r\n * object contains all server related properties (see the `server` section\r\n * in the `lib/schemas/config.js` file for a reference).\r\n *\r\n * @param {Object} serverConfig - The server configuration object.\r\n *\r\n * @throws {ExportError} - Throws an error if the server cannot be configured\r\n * and started.\r\n */\r\nexport const startServer = async (serverConfig) => {\r\n try {\r\n // Stop if not enabled\r\n if (!serverConfig.enable) {\r\n return false;\r\n }\r\n\r\n // Listen HTTP server\r\n if (!serverConfig.ssl.force) {\r\n // Main server instance (HTTP)\r\n const httpServer = http.createServer(app);\r\n\r\n // Attach error handlers and listen to the server\r\n attachErrorHandlers(httpServer);\r\n\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 2,\r\n `[server] Unable to load key/certificate from the '${serverConfig.ssl.certPath}' path. Could not run secured layer server.`\r\n );\r\n }\r\n\r\n if (key && cert) {\r\n // Main server instance (HTTPS)\r\n const httpsServer = https.createServer({ key, cert }, app);\r\n\r\n // Attach error handlers and listen to the server\r\n attachErrorHandlers(httpsServer);\r\n\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 // Set up centralized error handler\r\n errorHandler(app);\r\n } catch (error) {\r\n throw new ExportError(\r\n '[server] Could not configure and start the server.'\r\n ).setError(error);\r\n }\r\n};\r\n\r\n/**\r\n * Enable rate limiting for the server.\r\n *\r\n * @param {Object} limitConfig - Configuration object for rate limiting.\r\n */\r\nexport const enableRateLimiting = (limitConfig) => rateLimit(app, limitConfig);\r\n\r\n/**\r\n * Get the Express instance.\r\n *\r\n * @returns {Object} - The Express instance.\r\n */\r\nexport const getExpress = () => express;\r\n\r\n/**\r\n * Get the Express app instance.\r\n *\r\n * @returns {Object} - The Express app instance.\r\n */\r\nexport const getApp = () => app;\r\n\r\n/**\r\n * Apply middleware(s) to a specific path.\r\n *\r\n * @param {string} path - The path to which the middleware(s) should be applied.\r\n * @param {...Function} middlewares - The middleware functions to be applied.\r\n */\r\nexport const use = (path, ...middlewares) => {\r\n app.use(path, ...middlewares);\r\n};\r\n\r\n/**\r\n * Set up a route with GET method and apply middleware(s).\r\n *\r\n * @param {string} path - The route path.\r\n * @param {...Function} middlewares - The middleware functions to be applied.\r\n */\r\nexport const get = (path, ...middlewares) => {\r\n app.get(path, ...middlewares);\r\n};\r\n\r\n/**\r\n * Set up a route with POST method and apply middleware(s).\r\n *\r\n * @param {string} path - The route path.\r\n * @param {...Function} middlewares - The middleware functions to be applied.\r\n */\r\nexport const post = (path, ...middlewares) => {\r\n app.post(path, ...middlewares);\r\n};\r\n\r\nexport default {\r\n startServer,\r\n enableRateLimiting,\r\n getExpress,\r\n getApp,\r\n use,\r\n get,\r\n post\r\n};\r\n","/*******************************************************************************\r\n\r\nHighcharts Export Server\r\n\r\nCopyright (c) 2016-2024, 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/**\r\n * Adds the GET / route for a UI when enabled on 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-2024, 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 HttpError from '../../errors/HttpError.js';\r\nimport { envs } from '../../envs.js';\r\n\r\n/**\r\n * Adds the POST /change_hc_version/:newVersion route that can be utilized to modify\r\n * the Highcharts version on the server.\r\n *\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(\r\n '/version/change/:newVersion',\r\n async (request, response, next) => {\r\n try {\r\n const adminToken = envs.HIGHCHARTS_ADMIN_TOKEN;\r\n\r\n // Check the existence of the token\r\n if (!adminToken || !adminToken.length) {\r\n throw new HttpError(\r\n 'The server is not configured to perform run-time version changes: HIGHCHARTS_ADMIN_TOKEN is not set.',\r\n 401\r\n );\r\n }\r\n\r\n // Check if the hc-auth header contain a correct token\r\n const token = request.get('hc-auth');\r\n if (!token || token !== adminToken) {\r\n throw new HttpError(\r\n 'Invalid or missing token: Set the token in the hc-auth header.',\r\n 401\r\n );\r\n }\r\n\r\n // Compare versions\r\n const newVersion = request.params.newVersion;\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 (error) {\r\n throw new HttpError(\r\n `Version change: ${error.message}`,\r\n error.statusCode\r\n ).setError(error);\r\n }\r\n\r\n // Success\r\n response.status(200).send({\r\n statusCode: 200,\r\n version: cache.version(),\r\n message: `Successfully updated Highcharts to version: ${newVersion}.`\r\n });\r\n } else {\r\n // No version specified\r\n throw new HttpError('No new version supplied.', 400);\r\n }\r\n } catch (error) {\r\n next(error);\r\n }\r\n }\r\n );\r\n","/*******************************************************************************\r\n\r\nHighcharts Export Server\r\n\r\nCopyright (c) 2016-2024, 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 'colors';\r\nimport dotenv from 'dotenv';\r\n\r\nimport { checkAndUpdateCache } from './cache.js';\r\nimport {\r\n batchExport,\r\n setAllowCodeExecution,\r\n singleExport,\r\n startExport\r\n} from './chart.js';\r\nimport { mapToNewConfig, manualConfig, setOptions } from './config.js';\r\nimport {\r\n initLogging,\r\n log,\r\n logWithStack,\r\n setLogLevel,\r\n enableFileLogging\r\n} from './logger.js';\r\nimport { initPool, killPool } from './pool.js';\r\nimport server, { startServer } from './server/server.js';\r\nimport { printLogo, printUsage } from './utils.js';\r\n\r\n// Load .env into environment variables\r\ndotenv.config();\r\n\r\n/**\r\n * Initializes the export process. Tasks such as configuring logging, checking\r\n * cache and sources, and initializing the pool of resources happen during\r\n * this stage. Function that is required to be called before trying to export charts or setting a server. The `options` is an object that contains all options.\r\n *\r\n * @param {Object} options - All export options.\r\n *\r\n * @returns {Promise} Promise resolving to the updated export options.\r\n */\r\nconst initExport = async (options) => {\r\n // Set the allowCodeExecution per export module scope\r\n setAllowCodeExecution(\r\n options.customLogic && options.customLogic.allowCodeExecution\r\n );\r\n\r\n // Init the logging\r\n initLogging(options.logging);\r\n\r\n // Check if cache needs to be updated\r\n await checkAndUpdateCache(options.highcharts || { version: 'latest' });\r\n\r\n // Init the pool\r\n await initPool({\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\nexport default {\r\n // Server\r\n server,\r\n startServer,\r\n setOptions,\r\n\r\n // Exporting\r\n initExport,\r\n singleExport,\r\n batchExport,\r\n startExport,\r\n killPool,\r\n\r\n // Logs\r\n log,\r\n logWithStack,\r\n setLogLevel,\r\n enableFileLogging,\r\n\r\n // Utils\r\n mapToNewConfig,\r\n manualConfig,\r\n printLogo,\r\n printUsage\r\n};\r\n"],"names":["async","fetch","url","requestOptions","Promise","resolve","reject","protocol","startsWith","https","http","getProtocol","get","res","data","on","chunk","text","error","defaultConfig","puppeteer","args","value","type","description","highcharts","version","envLink","cdnURL","coreScripts","modules","indicators","scripts","forceFetch","cachePath","export","infile","instr","options","outfile","constr","defaultHeight","defaultWidth","defaultScale","height","width","scale","globalOptions","themeOptions","batch","rasterizationTimeout","customLogic","allowCodeExecution","allowFileResources","customCode","callback","resources","loadConfig","legacyName","createConfig","server","enable","cliName","host","port","benchmarking","ssl","force","certPath","rateLimiting","maxRequests","window","delay","trustProxy","skipKey","skipToken","pool","minWorkers","maxWorkers","workLimit","acquireTimeout","createTimeout","destroyTimeout","idleTimeout","createRetryInterval","reaperInterval","listenToProcessExits","logging","level","file","dest","ui","route","other","noLogo","promptsConfig","name","message","initial","join","separator","instructions","choices","hint","min","max","round","absoluteProps","nestedArgs","createNestedArgs","obj","propChain","Object","keys","forEach","k","includes","entry","substring","undefined","colors","toConsole","toFile","pathCreated","levelsDesc","title","color","listeners","key","option","entries","logToFile","texts","prefix","existsSync","mkdirSync","appendFile","concat","console","log","newLevel","length","Date","toString","split","trim","fn","apply","logWithStack","customMessage","mainMessage","stackMessage","stack","slice","setLogLevel","enableFileLogging","logDest","logFile","endsWith","__dirname","fileURLToPath","URL","fixType","formats","outType","pop","find","t","handleResources","allowedProps","handledResources","correctResources","isCorrectJSON","readFileSync","files","propName","map","item","parsedData","JSON","parse","stringify","deepCopy","copy","Array","isArray","prototype","hasOwnProperty","call","optionsStringify","allowFunctions","replaceAll","printUsage","bold","yellow","cycleCategories","descName","green","i","blue","category","toUpperCase","red","toBoolean","wrapAround","replace","measureTime","start","process","hrtime","bigint","Number","dotenv","config","v","z","enum","transform","optional","string","val","envs","object","HIGHCHARTS_VERSION","refine","test","HIGHCHARTS_CDN_URL","HIGHCHARTS_CORE_SCRIPTS","HIGHCHARTS_MODULES","HIGHCHARTS_INDICATORS","HIGHCHARTS_FORCE_FETCH","HIGHCHARTS_CACHE_PATH","HIGHCHARTS_ADMIN_TOKEN","EXPORT_TYPE","EXPORT_CONSTR","EXPORT_DEFAULT_HEIGHT","coerce","number","positive","EXPORT_DEFAULT_WIDTH","EXPORT_DEFAULT_SCALE","EXPORT_RASTERIZATION_TIMEOUT","CUSTOM_LOGIC_ALLOW_CODE_EXECUTION","CUSTOM_LOGIC_ALLOW_FILEL_RESOURCES","SERVER_ENABLE","SERVER_HOST","SERVER_PORT","SERVER_BENCHMARKING","SERVER_SSL_ENABLE","SERVER_SSL_FORCE","SERVER_SSL_PORT","SERVER_SSL_CERT_PATH","SERVER_RATE_LIMITING_ENABLE","SERVER_RATE_LIMITING_MAX_REQUESTS","SERVER_RATE_LIMITING_WINDOW","SERVER_RATE_LIMITING_DELAY","SERVER_RATE_LIMITING_TRUST_PROXY","SERVER_RATE_LIMITING_SKIP_KEY","SERVER_RATE_LIMITING_SKIP_TOKEN","POOL_MIN_WORKERS","POOL_MAX_WORKERS","POOL_WORK_LIMIT","POOL_ACQUIRE_TIMEOUT","POOL_CREATE_TIMEOUT","POOL_DESTROY_TIMEOUT","POOL_IDLE_TIMEOUT","POOL_CREATE_RETRY_INTERVAL","POOL_REAPER_INTERVAL","POOL_BENCHMARKING","POOL_LISTEN_TO_PROCESS_EXITS","LOGGING_LEVEL","LOGGING_FILE","LOGGING_DEST","UI_ENABLE","UI_ROUTE","OTHER_NO_LOGO","NODE_ENV","default","PROXY_SERVER_TIMEOUT","PROXY_SERVER_HOST","PROXY_SERVER_PORT","env","ExportError","Error","constructor","super","this","setError","statusCode","cache","activeManifest","sources","hcVersion","appliedConfig","extractVersion","indexOf","fetchAndProcessScript","script","proxyAgent","fetchedModules","shouldThrowError","agent","timeout","response","updateCache","sourcePath","customScripts","proxyHost","proxyPort","HttpsProxyAgent","moduleScripts","allFetchPromises","all","fetchScripts","c","m","writeFileSync","checkAndUpdateCache","manifestPath","requestUpdate","manifest","moduleMap","numberOfModules","some","moduleName","newManifest","saveConfigToManifest","getCachePath","cache$1","newVersion","assign","generalOptions","getOptions","mergeConfigOptions","newOptions","mergedOptions","updateDefaultConfig","configObj","customObj","customValue","initOptions","items","recursiveProps","objectToUpdate","nestedNames","shift","RANDOM_PID","randomBytes","PUPPETEER_DIR","path","minimalArgs","template","fs","browser","setPageContent","page","setContent","addScriptTag","evaluate","setupHighcharts","$eval","element","errorMessage","_displayErrors","innerHTML","clearPage","hardReset","goto","document","body","newPage","setCacheEnabled","close","isConnected","__basedir","setAsConfig","chart","triggerExport","puppeteerExport","injectedResources","clearInjected","dispose","scriptsToRemove","getElementsByTagName","stylesToRemove","linksToRemove","remove","exportOptions","requestAnimationFrame","displayErrors","debugger","isSVG","d","svgTemplate","strInj","js","push","content","isLocal","css","cssImports","match","cssImportPath","addStyleTag","size","chartHeight","baseVal","chartWidth","parseFloat","Highcharts","charts","viewportHeight","Math","ceil","viewportWidth","setViewport","deviceScaleFactor","zoomCallback","style","zoom","margin","x","y","getBoundingClientRect","trunc","getClipRegion","outerHTML","createSVG","encoding","clip","race","screenshot","omitBackground","_resolve","setTimeout","createImage","pdf","createPDF","oldCharts","oldChart","destroy","puppeteerArgs","performedExports","exportAttempts","timeSpent","droppedExports","spentAverage","poolConfig","factory","create","id","uuid","startDate","getTime","browserNewPage","isClosed","workCount","random","validate","workerHandle","initPool","code","killPool","exit","allArgs","tryCount","open","launch","headless","userDataDir","createBrowser","parseInt","Pool","acquireTimeoutMillis","createTimeoutMillis","destroyTimeoutMillis","idleTimeoutMillis","createRetryIntervalMillis","reapIntervalMillis","propagateCreateError","resource","eventId","initialResources","acquire","promise","release","browserClose","destroyed","postWork","getPoolInfo","acquireCounter","payload","requestId","workStart","exportCounter","result","exportTime","numFree","numUsed","numPendingAcquires","pool$1","available","inUse","pendingAcquire","startExport","settings","endCallback","svg","initExportSettings","exportAsString","doStraightInject","doExport","findChartSize","exporting","precision","multiplier","pow","roundNumber","sourceHeight","sourceWidth","param","chartJson","customLogicOptions","allowCodeExecutionScoped","enabled","optionsName","stringToExport","chartJSON","logErrorMiddleware","req","next","returnErrorMiddleware","stCode","status","json","rateLimit","app","limitConfig","msg","rateOptions","limiter","windowMs","delayMs","handler","request","format","send","skip","query","access_token","use","HttpError","setStatus","reversedMime","png","jpeg","gif","requestsCounter","beforeRequest","afterRequest","doCallbacks","callbacks","uniqueId","callResponse","exportHandler","stopCounter","defaultOptions","headers","connection","remoteAddress","connectionAborted","socket","toLowerCase","substr","b64","noDownload","pattern","isPrivateRangeUrlFound","info","removeAllListeners","Buffer","from","header","attachment","params","filename","pkgFile","pather","serverStartTime","express","disable","cors","storage","multer","memoryStorage","upload","limits","fieldSize","limit","urlencoded","extended","none","attachErrorHandlers","startServer","serverConfig","httpServer","createServer","listen","cert","fsPromises","readFile","posix","httpsServer","NaN","static","bootTime","uptime","floor","highchartsVersion","averageProcessingTime","failedExports","sucessRatio","healthRoute","post","exportRoutes","sendFile","uiRoute","adminToken","token","vSwitchRoute","errorHandler","enableRateLimiting","getExpress","getApp","middlewares","index","setOptions","userOptions","configIndex","findIndex","arg","fileName","loadConfigFile","showUsage","propertiesChain","argumentType","reduce","prop","pairArgumentValue","initExport","initLogging","singleExport","batchExport","batchFunctions","pair","mapToNewConfig","oldOptions","manualConfig","configFileName","configFile","choice","prompts","onSubmit","p","categories","questionsCounter","allQuestions","section","prompt","answer","module","writeFile","printLogo","packageVersion"],"mappings":"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"}
\ No newline at end of file
+{"version":3,"file":"index.esm.js","sources":["../lib/fetch.js","../lib/schemas/config.js","../lib/logger.js","../lib/utils.js","../lib/envs.js","../lib/errors/ExportError.js","../lib/cache.js","../lib/config.js","../lib/browser.js","../lib/export.js","../templates/svg_export/svg_export.js","../lib/pool.js","../lib/chart.js","../lib/server/error.js","../lib/server/rate_limit.js","../lib/errors/HttpError.js","../lib/server/routes/export.js","../lib/server/routes/health.js","../lib/server/server.js","../lib/server/routes/ui.js","../lib/server/routes/change_hc_version.js","../lib/index.js"],"sourcesContent":["/**\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 * Returns the HTTP or HTTPS protocol module based on the provided URL.\r\n *\r\n * @param {string} url - The URL to determine the protocol.\r\n *\r\n * @returns {Object} The HTTP or HTTPS protocol module (http or https).\r\n */\r\nconst getProtocol = (url) => (url.startsWith('https') ? https : http);\r\n\r\n/**\r\n * Fetches data from the specified URL using either HTTP or HTTPS protocol.\r\n *\r\n * @param {string} url - The URL to fetch data from.\r\n * @param {Object} requestOptions - Options for the HTTP request (optional).\r\n *\r\n * @returns {Promise} Promise resolving to the HTTP response object\r\n * with added 'text' property or rejecting with an error.\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 provided JSON body using\r\n * either HTTP or HTTPS protocol.\r\n *\r\n * @param {string} url - The URL to send the POST request to.\r\n * @param {Object} body - The JSON body to include in the POST request\r\n * (optional, default is an empty object).\r\n * @param {Object} requestOptions - Options for the HTTP request (optional).\r\n *\r\n * @returns {Promise} Promise resolving to the HTTP response object with\r\n * added 'text' property or rejecting with an error.\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-2024, 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// 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: 'Arguments array 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: 'The Highcharts version to be used.'\r\n },\r\n cdnURL: {\r\n value: 'https://code.highcharts.com/',\r\n envLink: 'HIGHCHARTS_CDN_URL',\r\n type: 'string',\r\n description: 'The CDN URL for Highcharts scripts to be used.'\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: 'The core Highcharts 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 'data-tools',\r\n 'draggable-points',\r\n 'static-scale',\r\n 'broken-axis',\r\n 'heatmap',\r\n 'tilemap',\r\n 'tiledwebmap',\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 'geoheatmap',\r\n 'pyramid3d',\r\n 'networkgraph',\r\n 'overlapping-datalabels',\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 'flowmap'\r\n ],\r\n type: 'string[]',\r\n description: 'The modules of Highcharts to fetch.'\r\n },\r\n indicators: {\r\n envLink: 'HIGHCHARTS_INDICATORS',\r\n value: ['indicators-all'],\r\n type: 'string[]',\r\n description: 'The indicators of Highcharts 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: 'Additional optional scripts or dependencies to fetch.'\r\n },\r\n forceFetch: {\r\n envLink: 'HIGHCHARTS_FORCE_FETCH',\r\n value: false,\r\n type: 'boolean',\r\n description:\r\n 'The flag to determine whether to refetch all scripts after each server rerun.'\r\n },\r\n cachePath: {\r\n envLink: 'HIGHCHARTS_CACHE_PATH',\r\n value: '.cache',\r\n type: 'string',\r\n description:\r\n 'The path to the cache directory. It is used to store the Highcharts scripts and custom scripts.'\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 should include a name and a type (json or svg). It must be correctly formatted as a JSON or SVG file.'\r\n },\r\n instr: {\r\n value: false,\r\n type: 'string',\r\n description:\r\n 'Input, provided in the form of a stringified JSON or SVG file, will override the --infile option.'\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). This will ignore the --type flag.'\r\n },\r\n type: {\r\n envLink: 'EXPORT_TYPE',\r\n value: 'png',\r\n type: 'string',\r\n description: 'The file export format. It can be jpeg, png, pdf, or svg.'\r\n },\r\n constr: {\r\n envLink: 'EXPORT_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 no value is 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 no value is 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. Used when no value is set.'\r\n },\r\n height: {\r\n type: 'number',\r\n value: false,\r\n description:\r\n 'The height of the exported chart, overriding 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, overriding the option in the chart settings.'\r\n },\r\n scale: {\r\n value: false,\r\n type: 'number',\r\n description:\r\n 'The scale of the exported chart, overriding the option in the chart settings. Ranges between 0.1 and 5.0.'\r\n },\r\n globalOptions: {\r\n value: false,\r\n type: 'string',\r\n description:\r\n 'Either a stringified JSON or a filename containing 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 'Either a stringified JSON or a filename containing 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 'Initiates a batch job with a string containing input/output pairs: \"in=out;in=out;...\".'\r\n },\r\n rasterizationTimeout: {\r\n envLink: 'EXPORT_RASTERIZATION_TIMEOUT',\r\n value: 1500,\r\n type: 'number',\r\n description:\r\n 'The duration in milliseconds to wait for rendering a webpage.'\r\n }\r\n },\r\n customLogic: {\r\n allowCodeExecution: {\r\n envLink: 'CUSTOM_LOGIC_ALLOW_CODE_EXECUTION',\r\n value: false,\r\n type: 'boolean',\r\n description:\r\n 'Controls whether the execution of arbitrary code is allowed during the exporting process.'\r\n },\r\n allowFileResources: {\r\n envLink: 'CUSTOM_LOGIC_ALLOW_FILE_RESOURCES',\r\n value: false,\r\n type: 'boolean',\r\n description:\r\n 'Controls the ability to inject resources from the filesystem. This setting 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 'Custom code to execute before chart initialization. It can be a function, code wrapped within a function, or a filename with the .js extension.'\r\n },\r\n callback: {\r\n value: false,\r\n type: 'string',\r\n description:\r\n 'JavaScript code to run during construction. It can be a function or a filename with the .js extension.'\r\n },\r\n resources: {\r\n value: false,\r\n type: 'string',\r\n description:\r\n 'Additional resource in the form of a stringified JSON, which may contain files, js, and css sections.'\r\n },\r\n loadConfig: {\r\n value: false,\r\n type: 'string',\r\n legacyName: 'fromFile',\r\n description: 'A file containing a pre-defined configuration to use.'\r\n },\r\n createConfig: {\r\n value: false,\r\n type: 'string',\r\n description:\r\n 'Enables setting options through a prompt and saving them in a provided config file.'\r\n }\r\n },\r\n server: {\r\n enable: {\r\n envLink: 'SERVER_ENABLE',\r\n value: false,\r\n type: 'boolean',\r\n cliName: 'enableServer',\r\n description:\r\n 'When set to true, the server starts on the local IP address 0.0.0.0.'\r\n },\r\n host: {\r\n envLink: 'SERVER_HOST',\r\n value: '0.0.0.0',\r\n type: 'string',\r\n description:\r\n 'The hostname of the server. Additionally, it starts a server on the provided hostname.'\r\n },\r\n port: {\r\n envLink: 'SERVER_PORT',\r\n value: 7801,\r\n type: 'number',\r\n description: 'The server port when enabled.'\r\n },\r\n benchmarking: {\r\n envLink: 'SERVER_BENCHMARKING',\r\n value: false,\r\n type: 'boolean',\r\n cliName: 'serverBenchmarking',\r\n description:\r\n 'Indicates whether to display the duration, in milliseconds, of specific actions that occur on the server while serving a request.'\r\n },\r\n ssl: {\r\n enable: {\r\n envLink: 'SERVER_SSL_ENABLE',\r\n value: false,\r\n type: 'boolean',\r\n cliName: 'enableSsl',\r\n description: 'Enables or disables the SSL protocol.'\r\n },\r\n force: {\r\n envLink: 'SERVER_SSL_FORCE',\r\n value: false,\r\n type: 'boolean',\r\n cliName: 'sslForced',\r\n legacyName: 'sslOnly',\r\n description:\r\n 'When set to true, the server is forced to serve only over HTTPS.'\r\n },\r\n port: {\r\n envLink: '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: 'SERVER_SSL_CERT_PATH',\r\n value: '',\r\n type: 'string',\r\n legacyName: 'sslPath',\r\n description: 'The path to the SSL certificate/key file.'\r\n }\r\n },\r\n rateLimiting: {\r\n enable: {\r\n envLink: 'SERVER_RATE_LIMITING_ENABLE',\r\n value: false,\r\n type: 'boolean',\r\n cliName: 'enableRateLimiting',\r\n description: 'Enables rate limiting for the server.'\r\n },\r\n maxRequests: {\r\n envLink: 'SERVER_RATE_LIMITING_MAX_REQUESTS',\r\n value: 10,\r\n type: 'number',\r\n legacyName: 'rateLimit',\r\n description: 'The maximum number of requests allowed in one minute.'\r\n },\r\n window: {\r\n envLink: 'SERVER_RATE_LIMITING_WINDOW',\r\n value: 1,\r\n type: 'number',\r\n description: 'The time window, in minutes, for the rate limiting.'\r\n },\r\n delay: {\r\n envLink: 'SERVER_RATE_LIMITING_DELAY',\r\n value: 0,\r\n type: 'number',\r\n description:\r\n 'The delay duration for each successive request before reaching the maximum limit.'\r\n },\r\n trustProxy: {\r\n envLink: 'SERVER_RATE_LIMITING_TRUST_PROXY',\r\n value: false,\r\n type: 'boolean',\r\n description: 'Set this to true if the server is behind a load balancer.'\r\n },\r\n skipKey: {\r\n envLink: 'SERVER_RATE_LIMITING_SKIP_KEY',\r\n value: '',\r\n type: 'string',\r\n description:\r\n 'Allows bypassing the rate limiter and should be provided with the skipToken argument.'\r\n },\r\n skipToken: {\r\n envLink: 'SERVER_RATE_LIMITING_SKIP_TOKEN',\r\n value: '',\r\n type: 'string',\r\n description:\r\n 'Allows bypassing the rate limiter and should be provided with the skipKey argument.'\r\n }\r\n }\r\n },\r\n pool: {\r\n minWorkers: {\r\n envLink: 'POOL_MIN_WORKERS',\r\n value: 4,\r\n type: 'number',\r\n description: 'The number of minimum and initial pool workers to spawn.'\r\n },\r\n maxWorkers: {\r\n envLink: 'POOL_MAX_WORKERS',\r\n value: 8,\r\n type: 'number',\r\n legacyName: 'workers',\r\n description: 'The number of maximum pool workers to spawn.'\r\n },\r\n workLimit: {\r\n envLink: 'POOL_WORK_LIMIT',\r\n value: 40,\r\n type: 'number',\r\n description:\r\n 'The number of work pieces that can be performed before restarting the worker process.'\r\n },\r\n acquireTimeout: {\r\n envLink: 'POOL_ACQUIRE_TIMEOUT',\r\n value: 5000,\r\n type: 'number',\r\n description:\r\n 'The duration, in milliseconds, to wait for acquiring a resource.'\r\n },\r\n createTimeout: {\r\n envLink: 'POOL_CREATE_TIMEOUT',\r\n value: 5000,\r\n type: 'number',\r\n description:\r\n 'The duration, in milliseconds, to wait for creating a resource.'\r\n },\r\n destroyTimeout: {\r\n envLink: 'POOL_DESTROY_TIMEOUT',\r\n value: 5000,\r\n type: 'number',\r\n description:\r\n 'The duration, in milliseconds, to wait for destroying a resource.'\r\n },\r\n idleTimeout: {\r\n envLink: 'POOL_IDLE_TIMEOUT',\r\n value: 30000,\r\n type: 'number',\r\n description:\r\n 'The duration, in milliseconds, after which an idle resource is destroyed.'\r\n },\r\n createRetryInterval: {\r\n envLink: 'POOL_CREATE_RETRY_INTERVAL',\r\n value: 200,\r\n type: 'number',\r\n description:\r\n 'The duration, in milliseconds, to wait before retrying the create process in case of a failure.'\r\n },\r\n reaperInterval: {\r\n envLink: 'POOL_REAPER_INTERVAL',\r\n value: 1000,\r\n type: 'number',\r\n description:\r\n 'The duration, in milliseconds, after which the check for idle resources to destroy is triggered.'\r\n },\r\n benchmarking: {\r\n envLink: 'POOL_BENCHMARKING',\r\n value: false,\r\n type: 'boolean',\r\n cliName: 'poolBenchmarking',\r\n description:\r\n 'Indicate whether to show statistics for the pool of resources or not.'\r\n },\r\n listenToProcessExits: {\r\n envLink: 'POOL_LISTEN_TO_PROCESS_EXITS',\r\n value: true,\r\n type: 'boolean',\r\n description: 'Decides whether or not to attach process.exit handlers.'\r\n }\r\n },\r\n logging: {\r\n level: {\r\n envLink: 'LOGGING_LEVEL',\r\n value: 4,\r\n type: 'number',\r\n cliName: 'logLevel',\r\n description: 'The logging level to be used.'\r\n },\r\n file: {\r\n envLink: 'LOGGING_FILE',\r\n value: 'highcharts-export-server.log',\r\n type: 'string',\r\n cliName: 'logFile',\r\n description:\r\n 'The name of a log file. The logDest option also needs to be set to enable file logging.'\r\n },\r\n dest: {\r\n envLink: 'LOGGING_DEST',\r\n value: 'log/',\r\n type: 'string',\r\n cliName: 'logDest',\r\n description:\r\n 'The path to store log files. This also enables file logging.'\r\n }\r\n },\r\n ui: {\r\n enable: {\r\n envLink: 'UI_ENABLE',\r\n value: false,\r\n type: 'boolean',\r\n cliName: 'enableUi',\r\n description:\r\n 'Enables or disables the user interface (UI) for the export server.'\r\n },\r\n route: {\r\n envLink: 'UI_ROUTE',\r\n value: '/',\r\n type: 'string',\r\n cliName: 'uiRoute',\r\n description:\r\n 'The endpoint route to which the user interface (UI) should be attached.'\r\n }\r\n },\r\n other: {\r\n noLogo: {\r\n envLink: 'OTHER_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};\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: 'Force re-fetch the scripts',\r\n initial: defaultConfig.highcharts.forceFetch.value\r\n },\r\n {\r\n type: 'text',\r\n name: 'cachePath',\r\n message: 'The path to the cache directory',\r\n initial: defaultConfig.highcharts.cachePath.value\r\n }\r\n ],\r\n export: [\r\n {\r\n type: 'select',\r\n name: 'type',\r\n message: 'The default export file type',\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',\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 type: 'number',\r\n name: 'rasterizationTimeout',\r\n message: 'The rendering webpage timeout in milliseconds',\r\n initial: defaultConfig.export.rasterizationTimeout.value\r\n }\r\n ],\r\n customLogic: [\r\n {\r\n type: 'toggle',\r\n name: 'allowCodeExecution',\r\n message: 'Enable execution of custom code',\r\n initial: defaultConfig.customLogic.allowCodeExecution.value\r\n },\r\n {\r\n type: 'toggle',\r\n name: 'allowFileResources',\r\n message: 'Enable file resources',\r\n initial: defaultConfig.customLogic.allowFileResources.value\r\n }\r\n ],\r\n server: [\r\n {\r\n type: 'toggle',\r\n name: 'enable',\r\n message: 'Starts the 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: 'Server hostname',\r\n initial: defaultConfig.server.host.value\r\n },\r\n {\r\n type: 'number',\r\n name: 'port',\r\n message: 'Server port',\r\n initial: defaultConfig.server.port.value\r\n },\r\n {\r\n type: 'toggle',\r\n name: 'benchmarking',\r\n message: 'Enable server benchmarking',\r\n initial: defaultConfig.server.benchmarking.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 serving only 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: 'SSL server port',\r\n initial: defaultConfig.server.ssl.port.value\r\n },\r\n {\r\n type: 'text',\r\n name: 'ssl.certPath',\r\n message: 'The path 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: 'The maximum requests allowed per 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 rate-limiting time window in minutes',\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 delay for each successive request before reaching the maximum',\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 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 when provided with the 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 when provided with the 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 initial number of 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 maximum number of 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: 'createRetryInterval',\r\n message:\r\n 'The retry interval in milliseconds after a create process fails',\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 reaper interval in milliseconds after triggering the check for idle resources to destroy',\r\n initial: defaultConfig.pool.reaperInterval.value\r\n },\r\n {\r\n type: 'toggle',\r\n name: 'benchmarking',\r\n message: 'Enable benchmarking for a resource pool',\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 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, 5: benchmark)',\r\n initial: defaultConfig.logging.level.value,\r\n round: 0,\r\n min: 0,\r\n max: 5\r\n },\r\n {\r\n type: 'text',\r\n name: 'file',\r\n message: 'A log file name. Set with the --logDest 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: 'The path to log files. 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',\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: 'Skip printing the logo on startup. Replaced by 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 * Recursively creates a chain of nested arguments from an object.\r\n *\r\n * @param {Object} obj - The object containing nested arguments.\r\n * @param {string} propChain - The current chain of nested properties\r\n * (used internally during recursion).\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 // Support for the legacy, PhantomJS properties names\r\n if (entry.legacyName !== undefined) {\r\n nestedArgs[entry.legacyName] = `${propChain}.${k}`.substring(1);\r\n }\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-2024, 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 available colors\r\nconst colors = ['red', 'yellow', 'blue', 'gray', 'green'];\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: colors[0]\r\n },\r\n {\r\n title: 'warning',\r\n color: colors[1]\r\n },\r\n {\r\n title: 'notice',\r\n color: colors[2]\r\n },\r\n {\r\n title: 'verbose',\r\n color: colors[3]\r\n },\r\n {\r\n title: 'benchmark',\r\n color: colors[4]\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 the provided texts to a file, if file logging is enabled. It creates\r\n * the necessary directory structure if not already created and appends the\r\n * content, including an optional prefix, to the specified log file.\r\n *\r\n * @param {string[]} texts - An array of texts to be logged.\r\n * @param {string} prefix - An optional prefix to be added to each log entry.\r\n */\r\nconst logToFile = (texts, prefix) => {\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\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 or is a benchmark log\r\n if (\r\n newLevel !== 5 &&\r\n (newLevel === 0 || newLevel > level || level > levelsDesc.length)\r\n ) {\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 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 // Log to file\r\n logToFile(texts, prefix);\r\n};\r\n\r\n/**\r\n * Logs an error message with its stack trace. Optionally, a custom message\r\n * can be provided.\r\n *\r\n * @param {number} level - The log level.\r\n * @param {Error} error - The error object.\r\n * @param {string} customMessage - An optional custom message to be logged along\r\n * with the error.\r\n */\r\nexport const logWithStack = (newLevel, error, customMessage) => {\r\n // Get the main message\r\n const mainMessage = customMessage || error.message;\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 // If the customMessage exists, we want to display the whole stack message\r\n const stackMessage =\r\n error.message !== error.stackMessage || error.stackMessage === undefined\r\n ? error.stack\r\n : error.stack.split('\\n').slice(1).join('\\n');\r\n\r\n // Combine custom message or error message with error stack message\r\n const texts = [mainMessage, '\\n', stackMessage];\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([\r\n mainMessage[colors[newLevel - 1]],\r\n '\\n',\r\n stackMessage\r\n ])\r\n );\r\n }\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 logToFile(texts, prefix);\r\n};\r\n\r\n/**\r\n * Sets the log level to the specified value. Log levels are (0 = no logging,\r\n * 1 = error, 2 = warning, 3 = notice, 4 = verbose or 5 = benchmark)\r\n *\r\n * @param {number} newLevel - The new log level to be set.\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 file logging with the specified destination and log file.\r\n *\r\n * @param {string} logDest - The destination path for log files.\r\n * @param {string} logFile - The log file name.\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 initialization: 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 * Initializes logging with the specified logging configuration.\r\n *\r\n * @param {Object} logging - The logging configuration object.\r\n */\r\nexport const initLogging = (logging) => {\r\n // Set the log level\r\n setLogLevel(logging && parseInt(logging.level));\r\n\r\n // Set the log file path and name\r\n if (logging && logging.dest) {\r\n enableFileLogging(\r\n logging.dest,\r\n logging.file || 'highcharts-export-server.log'\r\n );\r\n }\r\n};\r\n\r\n/**\r\n * Adds a listener function to the logging system.\r\n *\r\n * @param {function} fn - The listener function to be added.\r\n */\r\nexport const listen = (fn) => {\r\n logging.listeners.push(fn);\r\n};\r\n\r\n/**\r\n * Toggles the standard output (console) logging.\r\n *\r\n * @param {boolean} enabled - If true, enables console logging; if false,\r\n * disables it.\r\n */\r\nexport const toggleSTDOut = (enabled) => {\r\n logging.toConsole = enabled;\r\n};\r\n\r\nexport default {\r\n log,\r\n logWithStack,\r\n setLogLevel,\r\n enableFileLogging,\r\n initLogging,\r\n listen,\r\n toggleSTDOut\r\n};\r\n","/*******************************************************************************\r\n\r\nHighcharts Export Server\r\n\r\nCopyright (c) 2016-2024, 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 { join } from 'path';\r\nimport { fileURLToPath } from 'url';\r\n\r\nimport { defaultConfig } from '../lib/schemas/config.js';\r\nimport { log, logWithStack } 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 and standardizes text by replacing multiple consecutive whitespace\r\n * characters with a single space and trimming any leading or trailing\r\n * whitespace.\r\n *\r\n * @param {string} text - The input text to be cleared.\r\n * @param {RegExp} [rule=/\\s\\s+/g] - The regular expression rule to match\r\n * multiple consecutive whitespace characters.\r\n * @param {string} [replacer=' '] - The string used to replace multiple\r\n * consecutive whitespace characters.\r\n *\r\n * @returns {string} - The cleared and standardized 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 * Implements an exponential backoff strategy for retrying a function until\r\n * a certain number of attempts are reached.\r\n *\r\n * @param {Function} fn - The function to be retried.\r\n * @param {number} [attempt=0] - The current attempt number.\r\n * @param {...any} args - Arguments to be passed to the function.\r\n *\r\n * @returns {Promise} - A promise that resolves to the result of the function\r\n * if successful.\r\n *\r\n * @throws {Error} - Throws an error if the maximum number of attempts\r\n * is reached.\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 the export type based on MIME types and file extensions.\r\n *\r\n * @param {string} type - The original export type.\r\n * @param {string} outfile - The file path or name.\r\n *\r\n * @returns {string} - The corrected export type.\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 if (outType === 'jpg') {\r\n type = 'jpeg';\r\n } else 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 and validates resources for export.\r\n *\r\n * @param {Object|string} resources - The resources to be handled. Can be either\r\n * a JSON object, stringified JSON or a path to a JSON file.\r\n * @param {boolean} allowFileResources - Whether to allow loading resources from\r\n * files.\r\n *\r\n * @returns {Object|undefined} - The handled resources or undefined if no valid\r\n * resources are found.\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 handledResources = isCorrectJSON(readFileSync(resources, 'utf8'));\r\n } catch (error) {\r\n return logWithStack(2, error, `[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 (handledResources && !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 * Validates and parses JSON data. Checks if provided data is or can\r\n * be a correct JSON. If a primitive is provided, it is stringified and returned.\r\n *\r\n * @param {Object|string} data - The JSON data to be validated and parsed.\r\n * @param {boolean} toString - Whether to return a stringified representation\r\n * of the parsed JSON.\r\n *\r\n * @returns {Object|string|boolean} - The parsed JSON object, stringified JSON,\r\n * or false if validation fails.\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 {\r\n return false;\r\n }\r\n}\r\n\r\n/**\r\n * Checks if the given item is an object.\r\n *\r\n * @param {any} item - The item to be checked.\r\n *\r\n * @returns {boolean} - True if the item is an object, false otherwise.\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 the given object is empty.\r\n *\r\n * @param {Object} item - The object to be checked.\r\n *\r\n * @returns {boolean} - True if the object is empty, false otherwise.\r\n */\r\nexport const isObjectEmpty = (item) =>\r\n typeof item === 'object' &&\r\n !Array.isArray(item) &&\r\n item !== null &&\r\n Object.keys(item).length === 0;\r\n\r\n/**\r\n * Checks if a private IP range URL is found in the given string.\r\n *\r\n * @param {string} item - The string to be checked for a private IP range URL.\r\n *\r\n * @returns {boolean} - True if a private IP range URL is found, false\r\n * otherwise.\r\n */\r\nexport const isPrivateRangeUrlFound = (item) => {\r\n const regexPatterns = [\r\n /xlink:href=\"(?:http:\\/\\/|https:\\/\\/)?localhost\\b/,\r\n /xlink:href=\"(?:http:\\/\\/|https:\\/\\/)?10\\.\\d{1,3}\\.\\d{1,3}\\.\\d{1,3}\\b/,\r\n /xlink:href=\"(?:http:\\/\\/|https:\\/\\/)?127\\.\\d{1,3}\\.\\d{1,3}\\.\\d{1,3}\\b/,\r\n /xlink:href=\"(?:http:\\/\\/|https:\\/\\/)?172\\.(1[6-9]|2[0-9]|3[0-1])\\.\\d{1,3}\\.\\d{1,3}\\b/,\r\n /xlink:href=\"(?:http:\\/\\/|https:\\/\\/)?192\\.168\\.\\d{1,3}\\.\\d{1,3}\\b/\r\n ];\r\n\r\n return regexPatterns.some((pattern) => pattern.test(item));\r\n};\r\n\r\n/**\r\n * Creates a deep copy of the given object or array.\r\n *\r\n * @param {Object|Array} obj - The object or array to be deeply copied.\r\n *\r\n * @returns {Object|Array} - The deep copy of the provided object or array.\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 * Converts the provided options object to a JSON-formatted string with the\r\n * option to preserve functions.\r\n *\r\n * @param {Object} options - The options object to be converted to a string.\r\n * @param {boolean} allowFunctions - If set to true, functions are preserved\r\n * in the output.\r\n *\r\n * @returns {string} - The JSON-formatted string representing the options.\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 Highcharts Export Server logo and version information.\r\n *\r\n * @param {boolean} noLogo - If true, only prints version information without\r\n * the logo.\r\n */\r\nexport const printLogo = (noLogo) => {\r\n // Get package version either from env or from package.json\r\n const packageVersion = JSON.parse(\r\n readFileSync(join(__dirname, 'package.json'))\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 usage information for CLI arguments. If required, it can list\r\n * 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 '\\nUsage of CLI arguments:'.bold,\r\n '\\n------',\r\n `\\nFor more detailed information, visit the readme at: ${readme.bold.yellow}.`\r\n );\r\n\r\n const cycleCategories = (options) => {\r\n for (const [name, option] of Object.entries(options)) {\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 a number to the specified precision.\r\n *\r\n * @param {number} value - The number to be rounded.\r\n * @param {number} precision - The number of decimal places to round to.\r\n *\r\n * @returns {number} - The rounded number.\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 * Converts a value to a boolean.\r\n *\r\n * @param {any} item - The value to be converted to a boolean.\r\n *\r\n * @returns {boolean} - The boolean representation of the input value.\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 * Wraps custom code to execute it safely.\r\n *\r\n * @param {string} customCode - The custom code to be wrapped.\r\n * @param {boolean} allowFileResources - Flag to allow loading code from a file.\r\n *\r\n * @returns {string|boolean} - The wrapped custom code or false if wrapping\r\n * fails.\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 elapsed time using the Node.js process.hrtime() method.\r\n *\r\n * @returns {function(): number} - A function to calculate the elapsed time\r\n * in milliseconds.\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 isObjectEmpty,\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 * @fileoverview\r\n * This file is responsible for parsing the environment variables with the 'zod' library.\r\n * The parsed environment variables are then exported to be used in the application as \"envs\".\r\n * We should not use process.env directly in the application as these would not be parsed properly.\r\n *\r\n * The environment variables are parsed and validated only once when the application starts.\r\n * We should write a custom validator or a transformer for each of the options.\r\n *\r\n * For envs not defined in config.js with defaults, we also include default values here (PROXY_...).\r\n */\r\n\r\nimport { z } from 'zod';\r\nimport dotenv from 'dotenv';\r\ndotenv.config();\r\n\r\n// Object with custom validators and transformers, to avoid repetition in the Config object\r\nconst v = {\r\n boolean: () =>\r\n z\r\n .enum(['true', 'false'])\r\n .transform((value) => value === 'true')\r\n .optional(),\r\n array: () =>\r\n z\r\n .string()\r\n .transform((val) => val.split(',').map((v) => v.trim()))\r\n .optional()\r\n};\r\n\r\nexport const Config = z.object({\r\n // highcharts\r\n HIGHCHARTS_VERSION: z\r\n .string()\r\n .refine((value) => /^(latest|\\d+(\\.\\d+){0,2})$/.test(value), {\r\n message:\r\n \"HIGHCHARTS_VERSION must be 'latest', a major version, or in the form XX.YY.ZZ\"\r\n })\r\n .optional(), // todo: create an array of available Highcharts versions\r\n HIGHCHARTS_CDN_URL: z\r\n .string()\r\n .trim()\r\n .refine((val) => val.startsWith('https://') || val.startsWith('http://'), {\r\n message:\r\n 'Invalid value for HIGHCHARTS_CDN_URL. It should start with http:// or https://.'\r\n })\r\n .optional(),\r\n HIGHCHARTS_CORE_SCRIPTS: v.array(),\r\n HIGHCHARTS_MODULES: v.array(),\r\n HIGHCHARTS_INDICATORS: v.array(),\r\n HIGHCHARTS_FORCE_FETCH: v.boolean(),\r\n HIGHCHARTS_CACHE_PATH: z.string().optional(),\r\n HIGHCHARTS_ADMIN_TOKEN: z.string().optional(),\r\n\r\n // export\r\n EXPORT_TYPE: z.enum(['jpeg', 'png', 'pdf', 'svg']).optional(),\r\n EXPORT_CONSTR: z\r\n .string()\r\n .refine(\r\n (val) =>\r\n ['chart', 'stockChart', 'mapChart', 'ganttChart'].includes(val || ''),\r\n { message: 'Invalid value for EXPORT_CONSTR. ' }\r\n )\r\n .optional(),\r\n EXPORT_DEFAULT_HEIGHT: z.coerce.number().positive().optional(),\r\n EXPORT_DEFAULT_WIDTH: z.coerce.number().positive().optional(),\r\n EXPORT_DEFAULT_SCALE: z.coerce.number().positive().optional(),\r\n EXPORT_RASTERIZATION_TIMEOUT: z.coerce.number().positive().optional(),\r\n\r\n // custom\r\n CUSTOM_LOGIC_ALLOW_CODE_EXECUTION: v.boolean(),\r\n CUSTOM_LOGIC_ALLOW_FILEL_RESOURCES: v.boolean(),\r\n\r\n // server-related\r\n SERVER_ENABLE: v.boolean(),\r\n SERVER_HOST: z.string().optional(),\r\n SERVER_PORT: z.coerce.number().optional(),\r\n SERVER_BENCHMARKING: v.boolean(),\r\n SERVER_SSL_ENABLE: v.boolean(),\r\n SERVER_SSL_FORCE: v.boolean(),\r\n SERVER_SSL_PORT: z.coerce.number().optional(),\r\n SERVER_SSL_CERT_PATH: z.string().optional(),\r\n SERVER_RATE_LIMITING_ENABLE: v.boolean(),\r\n SERVER_RATE_LIMITING_MAX_REQUESTS: z.coerce.number().optional(),\r\n SERVER_RATE_LIMITING_WINDOW: z.coerce.number().optional(),\r\n SERVER_RATE_LIMITING_DELAY: z.coerce.number().optional(),\r\n SERVER_RATE_LIMITING_TRUST_PROXY: v.boolean(),\r\n SERVER_RATE_LIMITING_SKIP_KEY: z.string().optional(),\r\n SERVER_RATE_LIMITING_SKIP_TOKEN: z.string().optional(),\r\n\r\n // pool\r\n POOL_MIN_WORKERS: z.coerce.number().optional(),\r\n POOL_MAX_WORKERS: z.coerce.number().optional(),\r\n POOL_WORK_LIMIT: z.coerce.number().optional(),\r\n POOL_ACQUIRE_TIMEOUT: z.coerce.number().optional(),\r\n POOL_CREATE_TIMEOUT: z.coerce.number().optional(),\r\n POOL_DESTROY_TIMEOUT: z.coerce.number().optional(),\r\n POOL_IDLE_TIMEOUT: z.coerce.number().optional(),\r\n POOL_CREATE_RETRY_INTERVAL: z.coerce.number().optional(),\r\n POOL_REAPER_INTERVAL: z.coerce.number().optional(),\r\n POOL_BENCHMARKING: v.boolean(),\r\n POOL_LISTEN_TO_PROCESS_EXITS: v.boolean(),\r\n\r\n // logger\r\n LOGGING_LEVEL: z.coerce\r\n .number()\r\n .optional()\r\n .refine((val) => (val || 5) >= 0 && (val || 5) <= 5, {\r\n message:\r\n 'Invalid value for LOGGING_LEVEL. We only accept 0, 1, 2, 3, 4, 5 as logging levels.'\r\n }),\r\n LOGGING_FILE: z.string().optional(),\r\n LOGGING_DEST: z.string().optional(),\r\n\r\n // ui\r\n UI_ENABLE: v.boolean(),\r\n UI_ROUTE: z.string().optional(),\r\n\r\n // other\r\n OTHER_NO_LOGO: v.boolean(),\r\n NODE_ENV: z\r\n .enum(['development', 'production', 'test'])\r\n .optional()\r\n .default('production'),\r\n\r\n // proxy (! NOT INCLUDED IN CONFIG.JS !)\r\n PROXY_SERVER_TIMEOUT: z.coerce.number().positive().optional().default(5000),\r\n PROXY_SERVER_HOST: z.string().optional().default('localhost'),\r\n PROXY_SERVER_PORT: z.coerce.number().positive().optional().default(8080)\r\n});\r\n\r\nexport const envs = Config.parse(process.env);\r\n","class ExportError extends Error {\r\n constructor(message) {\r\n super();\r\n this.message = message;\r\n this.stackMessage = message;\r\n }\r\n\r\n setError(error) {\r\n this.error = error;\r\n if (error.name) {\r\n this.name = error.name;\r\n }\r\n if (error.statusCode) {\r\n this.statusCode = error.statusCode;\r\n }\r\n if (error.stack) {\r\n this.stackMessage = error.message;\r\n this.stack = error.stack;\r\n }\r\n return this;\r\n }\r\n}\r\n\r\nexport default ExportError;\r\n","/*******************************************************************************\r\n\r\nHighcharts Export Server\r\n\r\nCopyright (c) 2016-2024, 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 { HttpsProxyAgent } from 'https-proxy-agent';\r\n\r\nimport { fetch } from './fetch.js';\r\nimport { log } from './logger.js';\r\nimport { __dirname } from './utils.js';\r\nimport { envs } from './envs.js';\r\n\r\nimport ExportError from './errors/ExportError.js';\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 and caches the Highcharts version from the sources string.\r\n *\r\n * @returns {string} The extracted Highcharts version.\r\n */\r\nexport const extractVersion = (cache) => {\r\n return cache.sources\r\n .substring(0, cache.sources.indexOf('*/'))\r\n .replace('/*', '')\r\n .replace('*/', '')\r\n .replace(/\\n/g, '')\r\n .trim();\r\n};\r\n\r\n/**\r\n * Extracts the Highcharts module name based on the scriptPath.\r\n */\r\nexport const extractModuleName = (scriptPath) => {\r\n return scriptPath.replace(\r\n /(.*)\\/|(.*)modules\\/|stock\\/(.*)indicators\\/|maps\\/(.*)modules\\//gi,\r\n ''\r\n );\r\n};\r\n\r\n/**\r\n * Saves the provided configuration and fetched modules to the cache manifest\r\n * file.\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\n * @throws {ExportError} Throws an ExportError if an error occurs while writing\r\n * the cache manifest.\r\n */\r\nexport const 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(3, '[cache] Writing a new manifest.');\r\n try {\r\n writeFileSync(\r\n join(__dirname, config.cachePath, 'manifest.json'),\r\n JSON.stringify(newManifest),\r\n 'utf8'\r\n );\r\n } catch (error) {\r\n throw new ExportError('[cache] Error writing the cache manifest.').setError(\r\n error\r\n );\r\n }\r\n};\r\n\r\n/**\r\n * Fetches a single script and updates the fetchedModules accordingly.\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 * @param {Object} fetchedModules - An object which tracks which Highcharts modules have been fetched.\r\n * @param {boolean} shouldThrowError - A flag to indicate if the error should be thrown. This should be used only for the core scripts.\r\n *\r\n * @returns {Promise} A Promise resolving to the text representation\r\n * of the fetched script.\r\n *\r\n * @throws {ExportError} Throws an ExportError if there is a problem with\r\n * fetching the script.\r\n */\r\nexport const fetchAndProcessScript = async (\r\n script,\r\n proxyAgent,\r\n fetchedModules,\r\n shouldThrowError = false\r\n) => {\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: envs.PROXY_SERVER_TIMEOUT\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 && typeof response.text == 'string') {\r\n if (fetchedModules) {\r\n const moduleName = extractModuleName(script);\r\n fetchedModules[moduleName] = 1;\r\n }\r\n\r\n return response.text;\r\n }\r\n\r\n if (shouldThrowError) {\r\n throw new ExportError(\r\n `Could not fetch the ${script}.js. The script might not exist in the requested version (status code: ${response.statusCode}).`\r\n ).setError(response);\r\n } else {\r\n log(\r\n 2,\r\n `[cache] Could not fetch the ${script}.js. The script might not exist in the requested version.`\r\n );\r\n }\r\n\r\n return '';\r\n};\r\n\r\n/**\r\n * Fetches Highcharts scripts and customScripts from the given CDNs.\r\n *\r\n * @param {string} scripts - Array of Highcharts modules to fetch.\r\n * @param {string} customScripts - Array of custom script paths to fetch (full URLs).\r\n * @param {object} proxyAgent - The proxy agent to use for a request.\r\n * @param {object} fetchedModules - An object which tracks which Highcharts modules have been fetched.\r\n * @returns {Promise} The fetched scripts content joined.\r\n */\r\nexport const fetchScripts = async (\r\n coreScripts,\r\n moduleScripts,\r\n customScripts,\r\n cdnURL,\r\n proxyAgent,\r\n fetchedModules\r\n) => {\r\n const allFetchPromises = [\r\n ...coreScripts.map((script) =>\r\n fetchAndProcessScript(\r\n `${cdnURL}${script}`,\r\n proxyAgent,\r\n fetchedModules,\r\n true\r\n )\r\n ),\r\n ...moduleScripts.map((script) =>\r\n fetchAndProcessScript(`${cdnURL}${script}`, proxyAgent, fetchedModules)\r\n ),\r\n ...customScripts.map((script) =>\r\n fetchAndProcessScript(`${script}`, proxyAgent)\r\n )\r\n ];\r\n\r\n const fetchedScripts = await Promise.all(allFetchPromises);\r\n return fetchedScripts.join(';\\n');\r\n};\r\n\r\n/**\r\n * Updates the local cache with Highcharts scripts and their versions.\r\n *\r\n * @param {Object} config - The configuration object containing information\r\n * about scripts and modules.\r\n * @param {string} sourcePath - The path to the source file in the cache.\r\n *\r\n * @returns {Promise} A Promise resolving to an object representing\r\n * the fetched modules.\r\n *\r\n * @throws {ExportError} Throws an ExportError if there is an issue updating\r\n * the local Highcharts cache.\r\n */\r\nexport const 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(\r\n 3,\r\n `[cache] Updating cache version to Highcharts: ${hcVersion || 'latest'}.`\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 // Try to create a Proxy Agent\r\n if (proxyHost && proxyPort) {\r\n try {\r\n proxyAgent = new HttpsProxyAgent({\r\n host: proxyHost,\r\n port: +proxyPort\r\n });\r\n } catch (error) {\r\n throw new ExportError('[cache] Could not create a Proxy Agent.').setError(\r\n error\r\n );\r\n }\r\n }\r\n\r\n const fetchedModules = {};\r\n try {\r\n cache.sources = await fetchScripts(\r\n [...coreScripts.map((c) => `${hcVersion}${c}`)],\r\n [\r\n ...modules.map((m) =>\r\n m === 'map'\r\n ? `maps/${hcVersion}modules/${m}`\r\n : `${hcVersion}modules/${m}`\r\n ),\r\n ...indicators.map((i) => `stock/${hcVersion}indicators/${i}`)\r\n ],\r\n customScripts,\r\n config.cdnURL || cache.cdnURL,\r\n proxyAgent,\r\n fetchedModules\r\n );\r\n\r\n cache.hcVersion = extractVersion(cache);\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 throw new ExportError(\r\n '[cache] Unable to update the local Highcharts cache.'\r\n ).setError(error);\r\n }\r\n};\r\n\r\n/**\r\n * Updates the Highcharts version in the applied configuration and checks\r\n * the cache for the new version.\r\n *\r\n * @param {string} newVersion - The new Highcharts version to be applied.\r\n *\r\n * @returns {Promise<(object|boolean)>} A Promise resolving to the updated\r\n * configuration with the new version, or false if no applied configuration\r\n * exists.\r\n */\r\nexport const updateVersion = async (newVersion) =>\r\n appliedConfig\r\n ? await checkAndUpdateCache(\r\n Object.assign(appliedConfig, {\r\n version: newVersion\r\n })\r\n )\r\n : false;\r\n\r\n/**\r\n * Checks the cache for Highcharts dependencies, updates the cache if needed,\r\n * and loads the sources.\r\n *\r\n * @param {Object} config - Highcharts-related configuration object containing information\r\n * about scripts and modules.\r\n *\r\n * @returns {Promise} A Promise that resolves once the cache is checked\r\n * and updated.\r\n *\r\n * @throws {ExportError} Throws an ExportError if there is an issue updating\r\n * or reading the cache.\r\n */\r\nexport const checkAndUpdateCache = async (config) => {\r\n const cachePath = join(__dirname, config.cachePath);\r\n\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(\r\n 2,\r\n '[cache] A Highcharts version mismatch in the cache, need to re-fetch.'\r\n );\r\n requestUpdate = true;\r\n } else if (Object.keys(manifest.modules || {}).length !== numberOfModules) {\r\n log(\r\n 2,\r\n '[cache] The cache and the requested modules do 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 2,\r\n `[cache] The ${moduleName} is missing in the 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\r\n cache.hcVersion = extractVersion(cache);\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 const getCachePath = () => {\r\n return join(__dirname, appliedConfig.cachePath);\r\n};\r\n\r\nexport default {\r\n checkAndUpdateCache,\r\n getCachePath,\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-2024, 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 {\r\n absoluteProps,\r\n defaultConfig,\r\n nestedArgs,\r\n promptsConfig\r\n} from './schemas/config.js';\r\nimport { log, logWithStack } from './logger.js';\r\nimport { deepCopy, isObject, printUsage, toBoolean } from './utils.js';\r\nimport { envs } from './envs.js';\r\n\r\nlet generalOptions = {};\r\n\r\n/**\r\n * Retrieves and returns the general options for the export process.\r\n *\r\n * @returns {Object} The 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, keeping\r\n * the principle of the options load priority. It accepts optional userOptions\r\n * and args from the CLI.\r\n *\r\n * @param {Object} userOptions - User-provided options for customization.\r\n * @param {Array} args - Command-line arguments for additional configuration\r\n * (CLI usage).\r\n *\r\n * @returns {Object} The updated 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 * Allows manual configuration based on specified prompts and saves\r\n * the configuration to a file.\r\n *\r\n * @param {string} configFileName - The name of the configuration file.\r\n *\r\n * @returns {Promise} A Promise that resolves to true once the manual\r\n * configuration is completed and saved.\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 prompt.choices ? prompt.choices[answer] : 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 logWithStack(\r\n 1,\r\n error,\r\n `[config] An error occurred while creating the ${configFileName} file.`\r\n );\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 old-structured (PhantomJS) options to a new configuration format\r\n * (Puppeteer).\r\n *\r\n * @param {Object} oldOptions - Old-structured options to be mapped.\r\n *\r\n * @returns {Object} New options structured based on the defined nestedArgs\r\n * mapping.\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 two sets of configuration options, considering absolute properties.\r\n *\r\n * @param {Object} options - Original configuration options.\r\n * @param {Object} newOptions - New configuration options to be merged.\r\n * @param {Array} absoluteProps - List of properties that should\r\n * not be recursively merged.\r\n *\r\n * @returns {Object} Merged configuration options.\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 export settings based on provided exportOptions\r\n * and generalOptions.\r\n *\r\n * @param {Object} exportOptions - Options specific to the export process.\r\n * @param {Object} generalOptions - General configuration options.\r\n *\r\n * @returns {Object} Initialized export settings.\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 additional configuration from a specified file using\r\n * the --loadConfig option.\r\n *\r\n * @param {Array} args - Command-line arguments to check for\r\n * the --loadConfig option.\r\n *\r\n * @returns {Object} Additional configuration loaded from the specified file,\r\n * or an empty object if not found or invalid.\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 logWithStack(\r\n 2,\r\n error,\r\n `[config] Unable to load the configuration from the ${fileName} file.`\r\n );\r\n }\r\n }\r\n\r\n // No additional options to return\r\n return {};\r\n}\r\n\r\n/**\r\n * Updates the default configuration object with values from a custom object\r\n * and environment variables.\r\n *\r\n * @param {Object} configObj - The default configuration object.\r\n * @param {Object} customObj - Custom configuration object to override defaults.\r\n * @param {string} propChain - Property chain for tracking nested properties\r\n * during recursion.\r\n */\r\nfunction updateDefaultConfig(configObj, customObj = {}, propChain = '') {\r\n Object.keys(configObj).forEach((key) => {\r\n const entry = configObj[key];\r\n const customValue = customObj && customObj[key];\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 in envs) {\r\n entry.value = envs[entry.envLink];\r\n }\r\n }\r\n });\r\n}\r\n\r\n/**\r\n * Initializes options object based on provided items, setting values from\r\n * nested properties recursively.\r\n *\r\n * @param {Object} items - Configuration items to be used for initializing\r\n * options.\r\n *\r\n * @returns {Object} Initialized 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 values with corresponding options in the configuration,\r\n * updating the options object.\r\n *\r\n * @param {Object} options - Configuration options object to be updated.\r\n * @param {Array} args - Command-line arguments containing values for specific\r\n * options.\r\n * @param {Object} defaultConfig - Default configuration object for reference.\r\n *\r\n * @returns {Object} Updated options object.\r\n */\r\nfunction pairArgumentValue(options, args, defaultConfig) {\r\n let showUsage = false;\r\n for (let i = 0; i < args.length; i++) {\r\n const 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 // Get the correct type for CLI args which are passed as strings\r\n let argumentType;\r\n propertiesChain.reduce((obj, prop, index) => {\r\n if (propertiesChain.length - 1 === index) {\r\n argumentType = obj[prop].type;\r\n }\r\n return obj[prop];\r\n }, defaultConfig);\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 if (argumentType === 'boolean') {\r\n obj[prop] = toBoolean(args[i]);\r\n } else if (argumentType === 'number') {\r\n obj[prop] = +args[i];\r\n } else if (argumentType.indexOf(']') >= 0) {\r\n obj[prop] = args[i].split(',');\r\n } else {\r\n obj[prop] = args[i];\r\n }\r\n } else {\r\n log(\r\n 2,\r\n `[config] Missing value for the '${option}' argument. Using the default value.`\r\n );\r\n showUsage = true;\r\n }\r\n }\r\n }\r\n return obj[prop];\r\n }, options);\r\n }\r\n\r\n // Display the usage for the reference if needed\r\n if (showUsage) {\r\n printUsage(defaultConfig);\r\n }\r\n\r\n return options;\r\n}\r\n\r\n/**\r\n * Recursively updates properties in an object based on nested names and assigns\r\n * the final value.\r\n *\r\n * @param {Object} objectToUpdate - The object to be updated.\r\n * @param {Array} nestedNames - Array of nested property names.\r\n * @param {any} value - The final value to be assigned.\r\n *\r\n * @returns {Object} Updated object with assigned values.\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-2024, 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 fs from 'fs';\r\nimport * as url from 'url';\r\nimport path from 'node:path';\r\n\r\nimport puppeteer from 'puppeteer';\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\nimport { getCachePath } from './cache.js';\r\nimport { log, logWithStack } from './logger.js';\r\n\r\nimport ExportError from './errors/ExportError.js';\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\n/**\r\n * Sets the content for a Puppeteer Page using a predefined template\r\n * and additional scripts. Also, sets the pageerror in order to catch\r\n * and display errors from the window context.\r\n *\r\n * @param {Object} page - The Puppeteer Page object for which the content\r\n * is being set.\r\n */\r\nconst setPageContent = async (page) => {\r\n await page.setContent(template);\r\n await page.addScriptTag({ path: `${getCachePath()}/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 (error) => {\r\n // TODO: Consider adding a switch here that turns on log(0) logging\r\n // on page errors.\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 ${error.toString()}`\r\n );\r\n });\r\n};\r\n\r\n/**\r\n * Clears the content of a Puppeteer Page based on the specified mode.\r\n *\r\n * @param {Object} page - The Puppeteer Page object to be cleared.\r\n * @param {boolean} hardReset - A flag indicating the type of clearing\r\n * to be performed. If true, navigates to 'about:blank' and resets content\r\n * and scripts. If false, clears the body content by setting a predefined HTML\r\n * structure.\r\n *\r\n * @throws {Error} Logs thrown error if clearing the page content fails.\r\n */\r\nexport const clearPage = async (page, hardReset = false) => {\r\n try {\r\n if (hardReset) {\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 } else {\r\n // Clear body content\r\n await page.evaluate(() => {\r\n document.body.innerHTML =\r\n '';\r\n });\r\n }\r\n } catch (error) {\r\n logWithStack(\r\n 2,\r\n error,\r\n '[browser] Could not clear the content of the page.'\r\n );\r\n }\r\n};\r\n\r\n/**\r\n * Creates a new Puppeteer Page within an existing browser instance.\r\n *\r\n * If the browser instance is not available, returns false.\r\n *\r\n * The function creates a new page, disables caching, sets content using\r\n * setPageContent(), and returns the created Puppeteer Page.\r\n *\r\n * @returns {(boolean|object)} Returns false if the browser instance is not\r\n * available, or a Puppeteer Page object representing the newly created page.\r\n */\r\nexport const newPage = async () => {\r\n if (!browser) {\r\n return false;\r\n }\r\n\r\n const page = await browser.newPage();\r\n\r\n // Disable cache\r\n await page.setCacheEnabled(false);\r\n\r\n // Set the content\r\n await setPageContent(page);\r\n return page;\r\n};\r\n\r\n/**\r\n * Creates a Puppeteer browser instance with the specified arguments.\r\n *\r\n * @param {Array} puppeteerArgs - Additional arguments for Puppeteer launch.\r\n *\r\n * @returns {Promise} A Promise resolving to the Puppeteer browser\r\n * instance.\r\n *\r\n * @throws {ExportError} Throws an ExportError if max retries to open a browser\r\n * instance are reached, or if no browser instance is found after retries.\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 ${++tryCount}).`\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 (error) {\r\n logWithStack(\r\n 1,\r\n error,\r\n '[browser] Failed to launch a browser instance.'\r\n );\r\n\r\n // Retry to launch browser until reaching max attempts\r\n if (tryCount < 25) {\r\n log(3, `[browser] Retry to open a browser (${tryCount} out of 25).`);\r\n await new Promise((response) => setTimeout(response, 4000));\r\n await open();\r\n } else {\r\n throw error;\r\n }\r\n }\r\n };\r\n\r\n try {\r\n await open();\r\n } catch (error) {\r\n throw new ExportError(\r\n '[browser] Maximum retries to open a browser instance reached.'\r\n ).setError(error);\r\n }\r\n\r\n if (!browser) {\r\n throw new ExportError('[browser] Cannot find a browser to open.');\r\n }\r\n }\r\n\r\n // Return a browser promise\r\n return browser;\r\n};\r\n\r\n/**\r\n * Retrieves the existing Puppeteer browser instance.\r\n *\r\n * @returns {Promise} A Promise resolving to the Puppeteer browser\r\n * instance.\r\n *\r\n * @throws {ExportError} Throws an ExportError if no valid browser has been\r\n * created.\r\n */\r\nexport const get = async () => {\r\n if (!browser) {\r\n throw new ExportError('[browser] No valid browser has been created.');\r\n }\r\n\r\n return browser;\r\n};\r\n\r\n/**\r\n * Closes the Puppeteer browser instance if it is connected.\r\n *\r\n * @returns {Promise} A Promise resolving to true after the browser\r\n * is closed.\r\n */\r\nexport const close = async () => {\r\n // Close the browser when connnected\r\n if (browser?.isConnected()) {\r\n await browser.close();\r\n log(4, '[browser] Closed the browser.');\r\n }\r\n return true;\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-2024, 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 path from 'path';\r\nimport * as url from 'url';\r\n\r\nimport cache from './cache.js';\r\nimport { log, logWithStack } from './logger.js';\r\nimport svgTemplate from './../templates/svg_export/svg_export.js';\r\n\r\nimport ExportError from './errors/ExportError.js';\r\n\r\nconst __basedir = url.fileURLToPath(new URL('.', import.meta.url));\r\n\r\n/**\r\n * Retrieves the clipping region coordinates of the specified page element with\r\n * the id 'chart-container'.\r\n *\r\n * @param {Object} page - Puppeteer page object.\r\n *\r\n * @returns {Promise} Promise resolving to an object containing\r\n * x, y, width, and height properties.\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 * Creates an image using Puppeteer's page screenshot functionality with\r\n * specified options.\r\n *\r\n * @param {Object} page - Puppeteer page object.\r\n * @param {string} type - Image type.\r\n * @param {string} encoding - Image encoding.\r\n * @param {Object} clip - Clipping region coordinates.\r\n * @param {number} rasterizationTimeout - Timeout for rasterization\r\n * in milliseconds.\r\n *\r\n * @returns {Promise} Promise resolving to the image buffer or rejecting\r\n * with an ExportError for timeout.\r\n */\r\nconst createImage = (page, type, encoding, clip, rasterizationTimeout) =>\r\n 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 the expected type\r\n // format is PNG\r\n omitBackground: type == 'png'\r\n }),\r\n new Promise((_resolve, reject) =>\r\n setTimeout(\r\n () => reject(new ExportError('Rasterization timeout')),\r\n rasterizationTimeout || 1500\r\n )\r\n )\r\n ]);\r\n\r\n/**\r\n * Creates a PDF using Puppeteer's page pdf functionality with specified\r\n * options.\r\n *\r\n * @param {Object} page - Puppeteer page object.\r\n * @param {number} height - PDF height.\r\n * @param {number} width - PDF width.\r\n * @param {string} encoding - PDF encoding.\r\n *\r\n * @returns {Promise} Promise resolving to the PDF buffer.\r\n */\r\nconst createPDF = (page, height, width, encoding) =>\r\n 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 * Creates an SVG string by evaluating the outerHTML of the first 'svg' element\r\n * inside an element with the id 'container'.\r\n *\r\n * @param {Object} page - Puppeteer page object.\r\n *\r\n * @returns {Promise} Promise resolving to the SVG string.\r\n */\r\nconst createSVG = (page) =>\r\n page.$eval('#container svg:first-of-type', (element) => element.outerHTML);\r\n\r\n/**\r\n * Sets the specified chart and options as configuration into the triggerExport\r\n * function within the window context using page.evaluate.\r\n *\r\n * @param {Object} page - Puppeteer page object.\r\n * @param {any} chart - The chart object to be configured.\r\n * @param {Object} options - Configuration options for the chart.\r\n *\r\n * @returns {Promise} Promise resolving after the configuration is set.\r\n */\r\nconst setAsConfig = (page, chart, options) =>\r\n 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/**\r\n * Exports to a chart from a page using Puppeteer.\r\n *\r\n * @param {Object} page - Puppeteer page object.\r\n * @param {any} chart - The chart object or SVG configuration to be exported.\r\n * @param {Object} options - Export options and configuration.\r\n *\r\n * @returns {Promise} Promise resolving to\r\n * the exported data or rejecting with an ExportError.\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 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 let isSVG;\r\n if (\r\n chart.indexOf &&\r\n (chart.indexOf('= 0 || chart.indexOf('= 0)\r\n ) {\r\n // SVG input handling\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 await page.setContent(svgTemplate(chart));\r\n } else {\r\n // JSON config handling\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 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 } else {\r\n // Basic configuration export\r\n chart.chart.height = exportOptions.height;\r\n chart.chart.width = exportOptions.width;\r\n\r\n await setAsConfig(page, chart, options);\r\n }\r\n }\r\n\r\n // Use resources\r\n const resources = options.customLogic.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 (error) {\r\n logWithStack(\r\n 2,\r\n error,\r\n `[export] The JS file ${file} cannot be loaded.`\r\n );\r\n }\r\n }\r\n }\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.customLogic.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\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 (element, scale) => ({\r\n chartHeight: element.height.baseVal.value * scale,\r\n chartWidth: element.width.baseVal.value * scale\r\n }),\r\n parseFloat(exportOptions.scale)\r\n )\r\n : await page.evaluate(() => {\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 // 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 let data;\r\n // RASTERIZATION\r\n if (exportOptions.type === 'svg') {\r\n // SVG\r\n data = await createSVG(page);\r\n } else if (['png', 'jpeg'].includes(exportOptions.type)) {\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 exportOptions.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 new ExportError(\r\n `[export] Unsupported output format ${exportOptions.type}.`\r\n );\r\n }\r\n\r\n // Destroy old charts after the export is done\r\n await page.evaluate(() => {\r\n // We are not guaranteed that Highcharts is loaded, e,g, when doing SVG\r\n // exports\r\n if (typeof Highcharts !== 'undefined') {\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 (Array.isArray(oldCharts) && 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\r\n await clearInjected(page);\r\n return data;\r\n } catch (error) {\r\n await clearInjected(page);\r\n return error;\r\n }\r\n};\r\n","/*******************************************************************************\r\n\r\nHighcharts Export Server\r\n\r\nCopyright (c) 2016-2024, 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-2024, 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 { Pool } from 'tarn';\r\nimport { v4 as uuid } from 'uuid';\r\n\r\nimport {\r\n close as browserClose,\r\n create as createBrowser,\r\n newPage as browserNewPage,\r\n clearPage\r\n} from './browser.js';\r\nimport puppeteerExport from './export.js';\r\nimport { log, logWithStack } from './logger.js';\r\nimport { measureTime } from './utils.js';\r\n\r\nimport ExportError from './errors/ExportError.js';\r\n\r\n// Pool statistics\r\nexport const stats = {\r\n performedExports: 0,\r\n exportAttempts: 0,\r\n exportFromSvgAttempts: 0,\r\n timeSpent: 0,\r\n droppedExports: 0,\r\n spentAverage: 0\r\n};\r\n\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 page for the export pool.\r\n *\r\n * @returns {Object} - An object containing the worker ID, a reference to the\r\n * browser page, and initial work count.\r\n *\r\n * @throws {ExportError} - If there's an error during the creation of the new\r\n * page.\r\n */\r\n create: async () => {\r\n let page = false;\r\n\r\n const id = uuid();\r\n const startDate = new Date().getTime();\r\n\r\n try {\r\n page = await browserNewPage();\r\n\r\n if (!page || page.isClosed()) {\r\n throw new ExportError('The page is invalid or closed.');\r\n }\r\n\r\n log(\r\n 3,\r\n `[pool] Successfully created a worker ${id} - took ${\r\n new Date().getTime() - startDate\r\n } ms.`\r\n );\r\n } catch (error) {\r\n throw new ExportError(\r\n 'Error encountered when creating a new page.'\r\n ).setError(error);\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 page in the export pool, checking if it has exceeded\r\n * the work limit.\r\n *\r\n * @param {Object} workerHandle - The handle to the worker, containing the\r\n * worker's ID, a reference to the browser page, and work count.\r\n *\r\n * @returns {boolean} - Returns true if the worker is valid and within\r\n * the work limit; otherwise, returns false.\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: 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, true);\r\n return true;\r\n },\r\n\r\n /**\r\n * Destroys a worker entry in the export pool, closing its associated page.\r\n *\r\n * @param {Object} workerHandle - The handle to the worker, containing\r\n * the worker's ID and a reference to the browser page.\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\r\n/**\r\n * Initializes the export pool with the provided configuration, creating\r\n * a browser instance and setting up worker resources.\r\n *\r\n * @param {Object} config - Configuration options for the export pool along\r\n * with custom puppeteer arguments for the puppeteer.launch function.\r\n */\r\nexport const initPool = async (config) => {\r\n // For the module scope usage\r\n poolConfig = config && config.pool ? { ...config.pool } : {};\r\n\r\n // Attach process' exit listeners\r\n if (poolConfig.listenToProcessExits) {\r\n attachProcessExitListeners();\r\n }\r\n\r\n // The newest puppeteer arguments for the browser creation\r\n puppeteerArgs = config.puppeteerArgs;\r\n\r\n // Create a browser instance\r\n await createBrowser(puppeteerArgs);\r\n\r\n log(\r\n 3,\r\n `[pool] Initializing pool with workers: 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 if (parseInt(poolConfig.minWorkers) > parseInt(poolConfig.maxWorkers)) {\r\n poolConfig.minWorkers = poolConfig.maxWorkers;\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: parseInt(poolConfig.minWorkers),\r\n max: parseInt(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('release', async (resource) => {\r\n // Clear page\r\n await clearPage(resource.page, false);\r\n log(4, `[pool] Releasing a worker with ID ${resource.id}.`);\r\n });\r\n\r\n pool.on('destroySuccess', (eventId, resource) => {\r\n log(4, `[pool] Destroyed a worker with 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 logWithStack(2, error, '[pool] Could not create an initial resource.');\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${initialResources.length ? ` with ${initialResources.length} initial resources waiting.` : '.'}`\r\n );\r\n } catch (error) {\r\n // Close browser if for some reason cannot establish the pool\r\n await browserClose();\r\n throw new ExportError(\r\n '[pool] Could not create the pool of workers.'\r\n ).setError(error);\r\n }\r\n};\r\n\r\n/**\r\n * Attaches exit listeners to the process, ensuring proper cleanup of resources\r\n * and termination on exit signals. Handles 'exit', 'SIGINT', 'SIGTERM', and\r\n * 'uncaughtException' events.\r\n */\r\nexport function attachProcessExitListeners() {\r\n log(3, '[pool] Attaching exit listeners to the process.');\r\n\r\n // Kill all pool resources on exit\r\n process.on('exit', async (code) => {\r\n log(4, `Process exited with code ${code}.`);\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 logWithStack(1, error, `The ${name} error.`);\r\n await killPool();\r\n process.exit(1);\r\n });\r\n}\r\n\r\n/**\r\n * Kills all workers in the pool, destroys the pool, and closes the browser\r\n * instance.\r\n *\r\n * @returns {Promise} A promise that resolves after the workers are\r\n * killed, the pool is destroyed, and the browser is closed.\r\n */\r\nexport async function killPool() {\r\n log(3, '[pool] Killing all pool workers and browser, if any exist.');\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 return browserClose();\r\n }\r\n\r\n // If still alive, destroy the pool of pages before closing a browser\r\n if (pool) {\r\n await pool.destroy();\r\n log(4, '[browser] Destroyed the pool of resources.');\r\n }\r\n\r\n // Close the browser instance\r\n return browserClose();\r\n}\r\n\r\n/**\r\n * Processes the export work using a worker from the pool. Acquires a worker\r\n * handle from the pool, performs the export using puppeteer, and releases\r\n * the worker handle back to the pool.\r\n *\r\n * @param {string} chart - The chart data or configuration to be exported.\r\n * @param {Object} options - Export options and configuration.\r\n *\r\n * @returns {Promise} A promise that resolves with the export resultand\r\n * options.\r\n *\r\n * @throws {ExportError} If an error occurs during the export process.\r\n */\r\nexport const postWork = async (chart, options) => {\r\n let workerHandle;\r\n\r\n try {\r\n log(4, '[pool] Work received, starting to process.');\r\n\r\n ++stats.exportAttempts;\r\n if (poolConfig.benchmarking) {\r\n getPoolInfo();\r\n }\r\n\r\n if (!pool) {\r\n throw new ExportError('Work received, but pool has not been started.');\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 a worker handle.');\r\n const acquireCounter = measureTime();\r\n workerHandle = await pool.acquire().promise;\r\n\r\n // Check the page acquire time\r\n if (options.server.benchmarking) {\r\n log(\r\n 5,\r\n options.payload?.requestId\r\n ? `[benchmark] Request with ID ${options.payload?.requestId} -`\r\n : '[benchmark]',\r\n `Acquired a worker handle: ${acquireCounter()}ms.`\r\n );\r\n }\r\n } catch (error) {\r\n throw new ExportError(\r\n 'Error encountered when acquiring an available entry.'\r\n ).setError(error);\r\n }\r\n log(4, '[pool] Acquired a worker handle.');\r\n\r\n if (!workerHandle.page) {\r\n throw new ExportError(\r\n 'Resolved worker page is invalid: the pool setup is wonky.'\r\n );\r\n }\r\n\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 with ID ${workerHandle.id}.`);\r\n\r\n // Perform an export on a puppeteer level\r\n const exportCounter = measureTime();\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 throw new ExportError('Error encountered during export.').setError(\r\n result\r\n );\r\n }\r\n\r\n // Check the Puppeteer export time\r\n if (options.server.benchmarking) {\r\n log(\r\n 5,\r\n options.payload?.requestId\r\n ? `[benchmark] Request with ID ${options.payload?.requestId} -`\r\n : '[benchmark]',\r\n `Exported a chart sucessfully: ${exportCounter()}ms.`\r\n );\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 stats.timeSpent += exportTime;\r\n stats.spentAverage = stats.timeSpent / ++stats.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 result,\r\n options\r\n };\r\n } catch (error) {\r\n ++stats.droppedExports;\r\n\r\n if (workerHandle) {\r\n pool.release(workerHandle);\r\n }\r\n\r\n throw new ExportError(`[pool] In pool.postWork: ${error.message}`).setError(\r\n error\r\n );\r\n }\r\n};\r\n\r\n/**\r\n * Retrieves the current pool instance.\r\n *\r\n * @returns {Object|null} The current pool instance if initialized, or null\r\n * if the pool has not been created.\r\n */\r\nexport function getPool() {\r\n return pool;\r\n}\r\n\r\n/**\r\n * Retrieves pool information in JSON format, including minimum and maximum\r\n * workers, available workers, workers in use, and pending acquire requests.\r\n *\r\n * @returns {Object} Pool information in JSON format.\r\n */\r\nexport const getPoolInfoJSON = () => ({\r\n min: pool.min,\r\n max: pool.max,\r\n available: pool.numFree(),\r\n inUse: pool.numUsed(),\r\n pendingAcquire: pool.numPendingAcquires()\r\n});\r\n\r\n/**\r\n * Logs information about the current state of the pool, including the minimum\r\n * and maximum workers, available workers, workers in use, and pending acquire\r\n * requests.\r\n */\r\nexport function getPoolInfo() {\r\n const { min, max } = pool;\r\n\r\n log(5, `[pool] The minimum number of resources allowed by pool: ${min}.`);\r\n log(5, `[pool] The maximum number of resources allowed by pool: ${max}.`);\r\n log(\r\n 5,\r\n `[pool] The number of resources that are currently available: ${pool.numFree()}.`\r\n );\r\n log(\r\n 5,\r\n `[pool] The number of resources that are currently acquired: ${pool.numUsed()}.`\r\n );\r\n log(\r\n 5,\r\n `[pool] The number of callers waiting to acquire a resource: ${pool.numPendingAcquires()}.`\r\n );\r\n}\r\n\r\nexport default {\r\n initPool,\r\n killPool,\r\n postWork,\r\n getPool,\r\n getPoolInfo,\r\n getPoolInfoJSON,\r\n getStats: () => stats\r\n};\r\n","/*******************************************************************************\r\n\r\nHighcharts Export Server\r\n\r\nCopyright (c) 2016-2024, 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, writeFileSync } from 'fs';\r\n\r\nimport { getOptions, initExportSettings } from './config.js';\r\nimport { log, logWithStack } from './logger.js';\r\nimport { killPool, postWork, stats } from './pool.js';\r\nimport {\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\n\r\nimport ExportError from './errors/ExportError.js';\r\n\r\nlet allowCodeExecution = false;\r\n\r\n/**\r\n * Starts an export process. The `settings` contains final options gathered\r\n * from all possible sources (config, env, cli, json). The `endCallback` is\r\n * called when the export is completed, with an error object as the first\r\n * argument and the second containing the base64 respresentation of a chart.\r\n *\r\n * @param {Object} settings - The settings object containing export\r\n * configuration.\r\n * @param {function} endCallback - The callback function to be invoked upon\r\n * finalizing work or upon error occurance of the exporting process.\r\n *\r\n * @returns {void} This function does not return a value directly; instead,\r\n * it communicates results via the endCallback.\r\n */\r\nexport const startExport = async (settings, endCallback) => {\r\n // Starting exporting process message\r\n log(4, '[chart] Starting the 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 try {\r\n log(4, '[chart] Attempting to export from a SVG input.');\r\n const result = exportAsString(\r\n options.payload.svg.trim(),\r\n options,\r\n endCallback\r\n );\r\n ++stats.exportFromSvgAttempts;\r\n return result;\r\n } catch (error) {\r\n return endCallback(\r\n new ExportError('[chart] Error loading SVG input.').setError(error)\r\n );\r\n }\r\n }\r\n\r\n // Export using options from the file\r\n if (exportOptions.infile && exportOptions.infile.length) {\r\n // Try to read the file to get the string representation\r\n try {\r\n log(4, '[chart] Attempting to export from an input file.');\r\n options.export.instr = readFileSync(exportOptions.infile, 'utf8');\r\n return exportAsString(options.export.instr.trim(), options, endCallback);\r\n } catch (error) {\r\n return endCallback(\r\n new ExportError('[chart] Error loading input file.').setError(error)\r\n );\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 try {\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.customLogic?.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 } catch (error) {\r\n return endCallback(\r\n new ExportError('[chart] Error loading raw input.').setError(error)\r\n );\r\n }\r\n }\r\n\r\n // No input specified, pass an error message to the callback\r\n return endCallback(\r\n new ExportError(\r\n `[chart] No valid input specified. Check if at least one of the following parameters is correctly set: 'infile', 'instr', 'options', or 'svg'.`\r\n )\r\n );\r\n};\r\n\r\n/**\r\n * Starts a batch export process for multiple charts based on the information\r\n * in the batch option. The batch is a string in the following format:\r\n * \"infile1.json=outfile1.png;infile2.json=outfile2.png;...\"\r\n *\r\n * @param {Object} options - The options object containing configuration for\r\n * a batch export.\r\n *\r\n * @returns {Promise} A Promise that resolves once the batch export\r\n * process is completed.\r\n *\r\n * @throws {ExportError} Throws an ExportError if an error occurs during\r\n * any of the batch export process.\r\n */\r\nexport const batchExport = async (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 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 (error, info) => {\r\n // Throw an error\r\n if (error) {\r\n throw 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.result, 'base64')\r\n );\r\n }\r\n )\r\n );\r\n }\r\n }\r\n\r\n try {\r\n // Await all exports are done\r\n await Promise.all(batchFunctions);\r\n\r\n // Kill pool and close browser after finishing batch export\r\n await killPool();\r\n } catch (error) {\r\n throw new ExportError(\r\n '[chart] Error encountered during batch export.'\r\n ).setError(error);\r\n }\r\n};\r\n\r\n/**\r\n * Starts a single export process based on the specified options.\r\n *\r\n * @param {Object} options - The options object containing configuration for\r\n * a single export.\r\n *\r\n * @returns {Promise} A Promise that resolves once the single export\r\n * process is completed.\r\n *\r\n * @throws {ExportError} Throws an ExportError if an error occurs during\r\n * the single export process.\r\n */\r\nexport const singleExport = async (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 await startExport(options, async (error, info) => {\r\n // Exit process when error\r\n if (error) {\r\n throw error;\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.result, 'base64') : info.result\r\n );\r\n\r\n // Kill the pool\r\n await killPool();\r\n });\r\n};\r\n\r\n/**\r\n * Determines the size and scale for chart export based on the provided options.\r\n *\r\n * @param {Object} options - The options object containing configuration for\r\n * chart export.\r\n *\r\n * @returns {Object} An object containing the calculated height, width,\r\n * and scale for the chart export.\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 const size = {\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 // Get rid of potential px and %\r\n for (let [param, value] of Object.entries(size)) {\r\n size[param] =\r\n typeof value === 'string' ? +value.replace(/px|%/gi, '') : value;\r\n }\r\n return size;\r\n};\r\n\r\n/**\r\n * Function for finalizing options before export.\r\n *\r\n * @param {Object} options - The options object containing configuration for\r\n * the export process.\r\n * @param {Object} chartJson - The JSON representation of the chart.\r\n * @param {Function} endCallback - The callback function to be called upon\r\n * completion or error.\r\n * @param {string} svg - The SVG representation of the chart.\r\n *\r\n * @returns {Promise} A Promise that resolves once the export process\r\n * is completed.\r\n */\r\nconst doExport = async (options, chartJson, endCallback, svg) => {\r\n let { export: exportOptions, customLogic: customLogicOptions } = options;\r\n\r\n const allowCodeExecutionScoped =\r\n typeof customLogicOptions.allowCodeExecution === 'boolean'\r\n ? customLogicOptions.allowCodeExecution\r\n : allowCodeExecution;\r\n\r\n if (!customLogicOptions) {\r\n customLogicOptions = options.customLogic = {};\r\n } else if (allowCodeExecutionScoped) {\r\n if (typeof options.customLogic.resources === 'string') {\r\n // Process resources\r\n options.customLogic.resources = handleResources(\r\n options.customLogic.resources,\r\n toBoolean(options.customLogic.allowFileResources)\r\n );\r\n } else if (!options.customLogic.resources) {\r\n try {\r\n const resources = readFileSync('resources.json', 'utf8');\r\n options.customLogic.resources = handleResources(\r\n resources,\r\n toBoolean(options.customLogic.allowFileResources)\r\n );\r\n } catch (error) {\r\n logWithStack(\r\n 2,\r\n error,\r\n `[chart] Unable to load the default resources.json file.`\r\n );\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 && customLogicOptions) {\r\n if (\r\n customLogicOptions.callback ||\r\n customLogicOptions.resources ||\r\n customLogicOptions.customCode\r\n ) {\r\n // Send back a friendly message saying that the exporter does not support\r\n // these settings.\r\n return endCallback(\r\n new ExportError(\r\n `[chart] The 'callback', 'resources' and 'customCode' options have been disabled for this server.`\r\n )\r\n );\r\n }\r\n\r\n // Reset all additional custom code\r\n customLogicOptions.callback = false;\r\n customLogicOptions.resources = false;\r\n customLogicOptions.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 logWithStack(2, error, `[chart] The '${optionsName}' cannot be loaded.`);\r\n }\r\n });\r\n\r\n // Prepare the customCode\r\n if (customLogicOptions.allowCodeExecution) {\r\n try {\r\n customLogicOptions.customCode = wrapAround(\r\n customLogicOptions.customCode,\r\n customLogicOptions.allowFileResources\r\n );\r\n } catch (error) {\r\n logWithStack(2, error, `[chart] The 'customCode' cannot be loaded.`);\r\n }\r\n }\r\n\r\n // Get the callback\r\n if (\r\n customLogicOptions &&\r\n customLogicOptions.callback &&\r\n customLogicOptions.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 (customLogicOptions.allowFileResources) {\r\n try {\r\n customLogicOptions.callback = readFileSync(\r\n customLogicOptions.callback,\r\n 'utf8'\r\n );\r\n } catch (error) {\r\n customLogicOptions.callback = false;\r\n logWithStack(2, error, `[chart] The 'callback' cannot be loaded.`);\r\n }\r\n } else {\r\n customLogicOptions.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 try {\r\n const result = await postWork(\r\n exportOptions.strInj || chartJson || svg,\r\n options\r\n );\r\n return endCallback(false, result);\r\n } catch (error) {\r\n return endCallback(error);\r\n }\r\n};\r\n\r\n/**\r\n * Performs a direct inject of options before export. The function attempts\r\n * to stringify the provided options and removes unnecessary characters,\r\n * ensuring a clean and formatted input. The resulting string is saved as\r\n * a \"stright inject\" string in the export options. It then invokes the\r\n * doExport function with the updated options.\r\n *\r\n * IMPORTANT: Dangerous and must be used deliberately by someone who sets up\r\n * a server (see the --allowCodeExecution option).\r\n *\r\n * @param {Object} options - The export options containing the input\r\n * to be injected.\r\n * @param {function} endCallback - The callback function to be invoked\r\n * at the end of the process.\r\n *\r\n * @returns {Promise} A Promise that resolves with the result of the export\r\n * operation or rejects with an error if any issues occur during the process.\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.customLogic?.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 return endCallback(\r\n new ExportError(\r\n `[chart] Malformed input detected for ${options.export?.requestId || '?'}. Please make sure that your JSON/JavaScript options are sent using the \"options\" attribute, and that if you're using SVG, it is unescaped.`\r\n ).setError(error)\r\n );\r\n }\r\n};\r\n\r\n/**\r\n * Exports a string based on the provided options and invokes an end callback.\r\n *\r\n * @param {string} stringToExport - The string content to be exported.\r\n * @param {Object} options - Export options, including customLogic with\r\n * allowCodeExecution flag.\r\n * @param {Function} endCallback - Callback function to be invoked at the end\r\n * of the export process.\r\n *\r\n * @returns {any} Result of the export process or an error if encountered.\r\n */\r\nconst exportAsString = (stringToExport, options, endCallback) => {\r\n const { allowCodeExecution } = options.customLogic;\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 endCallback(\r\n new ExportError(\r\n '[chart] Only JSON configurations and SVG are allowed for this server. If this is your server, JavaScript custom code can be enabled by starting the server with the --allowCodeExecution flag.'\r\n ).setError(error)\r\n );\r\n }\r\n }\r\n};\r\n\r\n/**\r\n * Retrieves and returns the current status of code execution permission.\r\n *\r\n * @returns {any} The value of allowCodeExecution.\r\n */\r\nexport const getAllowCodeExecution = () => allowCodeExecution;\r\n\r\n/**\r\n * Sets the code execution permission based on the provided boolean value.\r\n *\r\n * @param {any} value - The value to be converted and assigned\r\n * to allowCodeExecution.\r\n */\r\nexport const setAllowCodeExecution = (value) => {\r\n allowCodeExecution = toBoolean(value);\r\n};\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","import { logWithStack } from '../logger.js';\r\nimport { envs } from '../envs.js';\r\n\r\n/**\r\n * Middleware for logging errors with stack trace and handling error response.\r\n *\r\n * @param {Error} error - The error object.\r\n * @param {Express.Request} req - The Express request object.\r\n * @param {Express.Response} res - The Express response object.\r\n * @param {Function} next - The next middleware function.\r\n */\r\nconst logErrorMiddleware = (error, req, res, next) => {\r\n // Display the error with stack in a correct format\r\n logWithStack(1, error);\r\n\r\n // Delete the stack for the environment other than the development\r\n if (envs.NODE_ENV !== 'development') {\r\n delete error.stack;\r\n }\r\n\r\n // Call the returnErrorMiddleware\r\n next(error);\r\n};\r\n\r\n/**\r\n * Middleware for returning error response.\r\n *\r\n * @param {Error} error - The error object.\r\n * @param {Express.Request} req - The Express request object.\r\n * @param {Express.Response} res - The Express response object.\r\n * @param {Function} next - The next middleware function.\r\n */\r\nconst returnErrorMiddleware = (error, req, res, next) => {\r\n // Gather all requied information for the response\r\n const { statusCode: stCode, status, message, stack } = error;\r\n const statusCode = stCode || status || 500;\r\n\r\n // Set and return response\r\n res.status(statusCode).json({ statusCode, message, stack });\r\n};\r\n\r\nexport default (app) => {\r\n // Add log error middleware\r\n app.use(logErrorMiddleware);\r\n\r\n // Add set status and return error middleware\r\n app.use(returnErrorMiddleware);\r\n};\r\n","/*******************************************************************************\r\n\r\nHighcharts Export Server\r\n\r\nCopyright (c) 2016-2024, 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 { log } from '../logger.js';\r\n\r\n/**\r\n * Middleware for enabling rate limiting on the specified Express app.\r\n *\r\n * @param {Express} app - The Express app instance.\r\n * @param {Object} limitConfig - Configuration options for 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 `[rate limiting] Enabled rate limiting with ${rateOptions.max} requests per ${rateOptions.window} minute for each IP, trusting proxy: ${rateOptions.trustProxy}.`\r\n );\r\n};\r\n","import ExportError from './ExportError.js';\r\n\r\nclass HttpError extends ExportError {\r\n constructor(message, status) {\r\n super(message);\r\n this.status = this.statusCode = status;\r\n }\r\n\r\n setStatus(status) {\r\n this.status = status;\r\n return this;\r\n }\r\n}\r\n\r\nexport default HttpError;\r\n","/*******************************************************************************\r\n\r\nHighcharts Export Server\r\n\r\nCopyright (c) 2016-2024, 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 fixType,\r\n isCorrectJSON,\r\n isObjectEmpty,\r\n isPrivateRangeUrlFound,\r\n optionsStringify,\r\n measureTime\r\n} from '../../utils.js';\r\n\r\nimport HttpError from '../../errors/HttpError.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\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 * Invokes an array of callback functions with specified parameters, allowing\r\n * customization of request handling.\r\n *\r\n * @param {Function[]} callbacks - An array of callback functions\r\n * to be executed.\r\n * @param {Express.Request} request - The Express request object.\r\n * @param {Express.Response} response - The Express response object.\r\n * @param {Object} data - An object containing parameters like id, uniqueId,\r\n * type, and body.\r\n *\r\n * @returns {boolean} - Returns a boolean indicating the overall result\r\n * of the callback invocations.\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 the export requests from the client.\r\n *\r\n * @param {Express.Request} request - The Express request object.\r\n * @param {Express.Response} response - The Express response object.\r\n * @param {Function} next - The next middleware function.\r\n *\r\n * @returns {Promise} - A promise that resolves once the export process\r\n * is complete.\r\n */\r\nconst exportHandler = async (request, response, next) => {\r\n try {\r\n // Start counting time\r\n const stopCounter = measureTime();\r\n\r\n // Create a unique ID for a request\r\n const uniqueId = uuid().replace(/-/g, '');\r\n\r\n // Get the current server's general options\r\n const defaultOptions = getOptions();\r\n\r\n const body = request.body;\r\n const id = ++requestsCounter;\r\n\r\n let type = fixType(body.type);\r\n\r\n // Throw 'Bad Request' if there's no body\r\n if (!body || isObjectEmpty(body)) {\r\n throw new HttpError(\r\n 'The request body is required. Please ensure that your Content-Type header is correct (accepted types are application/json and multipart/form-data).',\r\n 400\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 // 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 `The request with ID ${uniqueId} from ${\r\n request.headers['x-forwarded-for'] || request.connection.remoteAddress\r\n } was incorrect. Payload received: ${JSON.stringify(body)}.`\r\n );\r\n\r\n throw new HttpError(\r\n \"No correct chart data found. Ensure that you are using either application/json or multipart/form-data headers. If sending JSON, make sure the chart data is in the 'infile', 'options', or 'data' attribute. If sending SVG, ensure it is in the 'svg' attribute.\",\r\n 400\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 // 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 with ID ${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 customLogic: {\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 if (instr) {\r\n // Stringify JSON with options\r\n requestOptions.export.instr = optionsStringify(\r\n instr,\r\n requestOptions.customLogic.allowCodeExecution\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 // 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 noDownload: body.noDownload || false,\r\n requestId: uniqueId\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 throw new HttpError(\r\n 'SVG potentially contain at least one forbidden URL in xlink:href element. Please review the SVG content and ensure that all referenced URLs comply with security policies.',\r\n 400\r\n );\r\n }\r\n\r\n // Start the export process\r\n await startExport(options, (error, info) => {\r\n // Remove the close event from the socket\r\n request.socket.removeAllListeners('close');\r\n\r\n // After the whole exporting process\r\n if (defaultOptions.server.benchmarking) {\r\n log(\r\n 5,\r\n `[benchmark] Request with ID ${uniqueId} - After the whole exporting process: ${stopCounter()}ms.`\r\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 `[export] The client closed the connection before the chart finished processing.`\r\n );\r\n }\r\n\r\n // If error, log it and send it to the error middleware\r\n if (error) {\r\n throw error;\r\n }\r\n\r\n // If data is missing, log the message and send it to the error middleware\r\n if (!info || !info.result) {\r\n throw new HttpError(\r\n `Unexpected return from chart generation. Please check your request data. For the request with ID ${uniqueId}, the result is ${info.result}.`,\r\n 400\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.result });\r\n\r\n if (info.result) {\r\n // If only base64 is required, return it\r\n if (body.b64) {\r\n // SVG Exception for the Highcharts 11.3.0 version\r\n if (type === 'pdf' || type == 'svg') {\r\n return response.send(\r\n Buffer.from(info.result, 'utf8').toString('base64')\r\n );\r\n }\r\n\r\n return response.send(info.result);\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.result)\r\n : response.send(Buffer.from(info.result, 'base64'));\r\n }\r\n });\r\n } catch (error) {\r\n next(error);\r\n }\r\n};\r\n\r\nexport default (app) => {\r\n /**\r\n * Adds the POST / a route for handling POST requests at the root endpoint.\r\n */\r\n app.post('/', exportHandler);\r\n\r\n /**\r\n * Adds the POST /:filename a route for handling POST requests with\r\n * a specified filename parameter.\r\n */\r\n app.post('/:filename', exportHandler);\r\n};\r\n","/*******************************************************************************\r\n\r\nHighcharts Export Server\r\n\r\nCopyright (c) 2016-2024, 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 { join as pather } from 'path';\r\nimport { log } from '../../logger.js';\r\n\r\nimport cache from '../../cache.js';\r\nimport pool from '../../pool.js';\r\nimport { __dirname } from '../../utils.js';\r\n\r\nconst pkgFile = JSON.parse(readFileSync(pather(__dirname, 'package.json')));\r\n\r\nconst serverStartTime = new Date();\r\n\r\nconst successRates = [];\r\nconst recordInterval = 60 * 1000; // record every minute\r\nconst windowSize = 30; // 30 minutes\r\n\r\nfunction recordSuccessRate() {\r\n const stats = pool.getStats();\r\n const successRatio =\r\n stats.exportAttempts === 0\r\n ? 1\r\n : (stats.performedExports / stats.exportAttempts) * 100;\r\n\r\n successRates.push(successRatio);\r\n if (successRates.length > windowSize) {\r\n successRates.shift();\r\n }\r\n}\r\n\r\nfunction calculateMovingAverage() {\r\n const sum = successRates.reduce((a, b) => a + b, 0);\r\n return sum / successRates.length;\r\n}\r\n\r\nsetInterval(recordSuccessRate, recordInterval);\r\n\r\n/**\r\n * Adds the /health and /success-moving-average routes\r\n * which output basic stats for the server.\r\n */\r\nexport default function addHealthRoutes(app) {\r\n if (!app) {\r\n return false;\r\n }\r\n\r\n app.get('/health', (_, res) => {\r\n const stats = pool.getStats();\r\n const period = successRates.length;\r\n const movingAverage = calculateMovingAverage();\r\n\r\n log(4, '[health.js] GET /health [200] - returning server health.');\r\n\r\n res.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: pkgFile.version,\r\n highchartsVersion: cache.version(),\r\n averageProcessingTime: stats.spentAverage,\r\n performedExports: stats.performedExports,\r\n failedExports: stats.droppedExports,\r\n exportAttempts: stats.exportAttempts,\r\n sucessRatio: (stats.performedExports / stats.exportAttempts) * 100,\r\n // eslint-disable-next-line import/no-named-as-default-member\r\n pool: pool.getPoolInfoJSON(),\r\n\r\n // Moving average\r\n period,\r\n movingAverage,\r\n message: `Last ${period} minutes had a success rate of ${movingAverage.toFixed(2)}%.`,\r\n\r\n // SVG/JSON attempts\r\n svgExportAttempts: stats.exportFromSvgAttempts,\r\n jsonExportAttempts: stats.performedExports - stats.exportFromSvgAttempts\r\n });\r\n });\r\n}\r\n","/*******************************************************************************\r\n\r\nHighcharts Export Server\r\n\r\nCopyright (c) 2016-2024, 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 cors from 'cors';\r\nimport express from 'express';\r\nimport http from 'http';\r\nimport https from 'https';\r\nimport multer from 'multer';\r\n\r\nimport errorHandler from './error.js';\r\nimport { log, logWithStack } from '../logger.js';\r\nimport rateLimit from './rate_limit.js';\r\nimport { __dirname } from '../utils.js';\r\n\r\nimport vSwitchRoute from './routes/change_hc_version.js';\r\nimport exportRoutes from './routes/export.js';\r\nimport healthRoute from './routes/health.js';\r\nimport uiRoute from './routes/ui.js';\r\n\r\nimport ExportError from '../errors/ExportError.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 fieldSize: 50 * 1024 * 1024\r\n }\r\n});\r\n\r\n// Enable body parser\r\napp.use(express.json({ limit: 50 * 1024 * 1024 }));\r\napp.use(express.urlencoded({ extended: true, limit: 50 * 1024 * 1024 }));\r\n\r\n// Use only non-file multipart form fields\r\napp.use(upload.none());\r\n\r\n/**\r\n * Attach error handlers to the server.\r\n *\r\n * @param {http.Server} server - The HTTP/HTTPS server instance.\r\n */\r\nconst attachErrorHandlers = (server) => {\r\n server.on('clientError', (error) => {\r\n logWithStack(1, error, `[server] Client error: ${error.message}`);\r\n });\r\n server.on('error', (error) => {\r\n logWithStack(1, error, `[server] Server error: ${error.message}`);\r\n });\r\n server.on('connection', (socket) => {\r\n socket.on('error', (error) => {\r\n logWithStack(1, error, `[server] Socket error: ${error.message}`);\r\n });\r\n });\r\n};\r\n\r\n/**\r\n * Starts an HTTP server based on the provided configuration. The `serverConfig`\r\n * object contains all server related properties (see the `server` section\r\n * in the `lib/schemas/config.js` file for a reference).\r\n *\r\n * @param {Object} serverConfig - The server configuration object.\r\n *\r\n * @throws {ExportError} - Throws an error if the server cannot be configured\r\n * and started.\r\n */\r\nexport const startServer = async (serverConfig) => {\r\n try {\r\n // Stop if not enabled\r\n if (!serverConfig.enable) {\r\n return false;\r\n }\r\n\r\n // Listen HTTP server\r\n if (!serverConfig.ssl.force) {\r\n // Main server instance (HTTP)\r\n const httpServer = http.createServer(app);\r\n\r\n // Attach error handlers and listen to the server\r\n attachErrorHandlers(httpServer);\r\n\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 2,\r\n `[server] Unable to load key/certificate from the '${serverConfig.ssl.certPath}' path. Could not run secured layer server.`\r\n );\r\n }\r\n\r\n if (key && cert) {\r\n // Main server instance (HTTPS)\r\n const httpsServer = https.createServer({ key, cert }, app);\r\n\r\n // Attach error handlers and listen to the server\r\n attachErrorHandlers(httpsServer);\r\n\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 // Set up centralized error handler\r\n errorHandler(app);\r\n } catch (error) {\r\n throw new ExportError(\r\n '[server] Could not configure and start the server.'\r\n ).setError(error);\r\n }\r\n};\r\n\r\n/**\r\n * Enable rate limiting for the server.\r\n *\r\n * @param {Object} limitConfig - Configuration object for rate limiting.\r\n */\r\nexport const enableRateLimiting = (limitConfig) => rateLimit(app, limitConfig);\r\n\r\n/**\r\n * Get the Express instance.\r\n *\r\n * @returns {Object} - The Express instance.\r\n */\r\nexport const getExpress = () => express;\r\n\r\n/**\r\n * Get the Express app instance.\r\n *\r\n * @returns {Object} - The Express app instance.\r\n */\r\nexport const getApp = () => app;\r\n\r\n/**\r\n * Apply middleware(s) to a specific path.\r\n *\r\n * @param {string} path - The path to which the middleware(s) should be applied.\r\n * @param {...Function} middlewares - The middleware functions to be applied.\r\n */\r\nexport const use = (path, ...middlewares) => {\r\n app.use(path, ...middlewares);\r\n};\r\n\r\n/**\r\n * Set up a route with GET method and apply middleware(s).\r\n *\r\n * @param {string} path - The route path.\r\n * @param {...Function} middlewares - The middleware functions to be applied.\r\n */\r\nexport const get = (path, ...middlewares) => {\r\n app.get(path, ...middlewares);\r\n};\r\n\r\n/**\r\n * Set up a route with POST method and apply middleware(s).\r\n *\r\n * @param {string} path - The route path.\r\n * @param {...Function} middlewares - The middleware functions to be applied.\r\n */\r\nexport const post = (path, ...middlewares) => {\r\n app.post(path, ...middlewares);\r\n};\r\n\r\nexport default {\r\n startServer,\r\n enableRateLimiting,\r\n getExpress,\r\n getApp,\r\n use,\r\n get,\r\n post\r\n};\r\n","/*******************************************************************************\r\n\r\nHighcharts Export Server\r\n\r\nCopyright (c) 2016-2024, 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/**\r\n * Adds the GET / route for a UI when enabled on 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-2024, 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 HttpError from '../../errors/HttpError.js';\r\nimport { envs } from '../../envs.js';\r\n\r\n/**\r\n * Adds the POST /change_hc_version/:newVersion route that can be utilized to modify\r\n * the Highcharts version on the server.\r\n *\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(\r\n '/version/change/:newVersion',\r\n async (request, response, next) => {\r\n try {\r\n const adminToken = envs.HIGHCHARTS_ADMIN_TOKEN;\r\n\r\n // Check the existence of the token\r\n if (!adminToken || !adminToken.length) {\r\n throw new HttpError(\r\n 'The server is not configured to perform run-time version changes: HIGHCHARTS_ADMIN_TOKEN is not set.',\r\n 401\r\n );\r\n }\r\n\r\n // Check if the hc-auth header contain a correct token\r\n const token = request.get('hc-auth');\r\n if (!token || token !== adminToken) {\r\n throw new HttpError(\r\n 'Invalid or missing token: Set the token in the hc-auth header.',\r\n 401\r\n );\r\n }\r\n\r\n // Compare versions\r\n const newVersion = request.params.newVersion;\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 (error) {\r\n throw new HttpError(\r\n `Version change: ${error.message}`,\r\n error.statusCode\r\n ).setError(error);\r\n }\r\n\r\n // Success\r\n response.status(200).send({\r\n statusCode: 200,\r\n version: cache.version(),\r\n message: `Successfully updated Highcharts to version: ${newVersion}.`\r\n });\r\n } else {\r\n // No version specified\r\n throw new HttpError('No new version supplied.', 400);\r\n }\r\n } catch (error) {\r\n next(error);\r\n }\r\n }\r\n );\r\n","/*******************************************************************************\r\n\r\nHighcharts Export Server\r\n\r\nCopyright (c) 2016-2024, 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 'colors';\r\nimport dotenv from 'dotenv';\r\n\r\nimport { checkAndUpdateCache } from './cache.js';\r\nimport {\r\n batchExport,\r\n setAllowCodeExecution,\r\n singleExport,\r\n startExport\r\n} from './chart.js';\r\nimport { mapToNewConfig, manualConfig, setOptions } from './config.js';\r\nimport {\r\n initLogging,\r\n log,\r\n logWithStack,\r\n setLogLevel,\r\n enableFileLogging\r\n} from './logger.js';\r\nimport { initPool, killPool } from './pool.js';\r\nimport server, { startServer } from './server/server.js';\r\nimport { printLogo, printUsage } from './utils.js';\r\n\r\n// Load .env into environment variables\r\ndotenv.config();\r\n\r\n/**\r\n * Initializes the export process. Tasks such as configuring logging, checking\r\n * cache and sources, and initializing the pool of resources happen during\r\n * this stage. Function that is required to be called before trying to export charts or setting a server. The `options` is an object that contains all options.\r\n *\r\n * @param {Object} options - All export options.\r\n *\r\n * @returns {Promise} Promise resolving to the updated export options.\r\n */\r\nconst initExport = async (options) => {\r\n // Set the allowCodeExecution per export module scope\r\n setAllowCodeExecution(\r\n options.customLogic && options.customLogic.allowCodeExecution\r\n );\r\n\r\n // Init the logging\r\n initLogging(options.logging);\r\n\r\n // Check if cache needs to be updated\r\n await checkAndUpdateCache(options.highcharts || { version: 'latest' });\r\n\r\n // Init the pool\r\n await initPool({\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\nexport default {\r\n // Server\r\n server,\r\n startServer,\r\n setOptions,\r\n\r\n // Exporting\r\n initExport,\r\n singleExport,\r\n batchExport,\r\n startExport,\r\n killPool,\r\n\r\n // Logs\r\n log,\r\n logWithStack,\r\n setLogLevel,\r\n enableFileLogging,\r\n\r\n // Utils\r\n mapToNewConfig,\r\n manualConfig,\r\n printLogo,\r\n printUsage\r\n};\r\n"],"names":["async","fetch","url","requestOptions","Promise","resolve","reject","protocol","startsWith","https","http","getProtocol","get","res","data","on","chunk","text","error","defaultConfig","puppeteer","args","value","type","description","highcharts","version","envLink","cdnURL","coreScripts","modules","indicators","scripts","forceFetch","cachePath","export","infile","instr","options","outfile","constr","defaultHeight","defaultWidth","defaultScale","height","width","scale","globalOptions","themeOptions","batch","rasterizationTimeout","customLogic","allowCodeExecution","allowFileResources","customCode","callback","resources","loadConfig","legacyName","createConfig","server","enable","cliName","host","port","benchmarking","ssl","force","certPath","rateLimiting","maxRequests","window","delay","trustProxy","skipKey","skipToken","pool","minWorkers","maxWorkers","workLimit","acquireTimeout","createTimeout","destroyTimeout","idleTimeout","createRetryInterval","reaperInterval","listenToProcessExits","logging","level","file","dest","ui","route","other","noLogo","promptsConfig","name","message","initial","join","separator","instructions","choices","hint","min","max","round","absoluteProps","nestedArgs","createNestedArgs","obj","propChain","Object","keys","forEach","k","includes","entry","substring","undefined","colors","toConsole","toFile","pathCreated","levelsDesc","title","color","listeners","key","option","entries","logToFile","texts","prefix","existsSync","mkdirSync","appendFile","concat","console","log","newLevel","length","Date","toString","split","trim","fn","apply","logWithStack","customMessage","mainMessage","stackMessage","stack","slice","setLogLevel","enableFileLogging","logDest","logFile","endsWith","__dirname","fileURLToPath","URL","fixType","formats","outType","pop","find","t","handleResources","allowedProps","handledResources","correctResources","isCorrectJSON","readFileSync","files","propName","map","item","parsedData","JSON","parse","stringify","deepCopy","copy","Array","isArray","prototype","hasOwnProperty","call","optionsStringify","allowFunctions","replaceAll","printUsage","bold","yellow","cycleCategories","descName","green","i","blue","category","toUpperCase","red","toBoolean","wrapAround","replace","measureTime","start","process","hrtime","bigint","Number","dotenv","config","v","z","enum","transform","optional","string","val","envs","object","HIGHCHARTS_VERSION","refine","test","HIGHCHARTS_CDN_URL","HIGHCHARTS_CORE_SCRIPTS","HIGHCHARTS_MODULES","HIGHCHARTS_INDICATORS","HIGHCHARTS_FORCE_FETCH","HIGHCHARTS_CACHE_PATH","HIGHCHARTS_ADMIN_TOKEN","EXPORT_TYPE","EXPORT_CONSTR","EXPORT_DEFAULT_HEIGHT","coerce","number","positive","EXPORT_DEFAULT_WIDTH","EXPORT_DEFAULT_SCALE","EXPORT_RASTERIZATION_TIMEOUT","CUSTOM_LOGIC_ALLOW_CODE_EXECUTION","CUSTOM_LOGIC_ALLOW_FILEL_RESOURCES","SERVER_ENABLE","SERVER_HOST","SERVER_PORT","SERVER_BENCHMARKING","SERVER_SSL_ENABLE","SERVER_SSL_FORCE","SERVER_SSL_PORT","SERVER_SSL_CERT_PATH","SERVER_RATE_LIMITING_ENABLE","SERVER_RATE_LIMITING_MAX_REQUESTS","SERVER_RATE_LIMITING_WINDOW","SERVER_RATE_LIMITING_DELAY","SERVER_RATE_LIMITING_TRUST_PROXY","SERVER_RATE_LIMITING_SKIP_KEY","SERVER_RATE_LIMITING_SKIP_TOKEN","POOL_MIN_WORKERS","POOL_MAX_WORKERS","POOL_WORK_LIMIT","POOL_ACQUIRE_TIMEOUT","POOL_CREATE_TIMEOUT","POOL_DESTROY_TIMEOUT","POOL_IDLE_TIMEOUT","POOL_CREATE_RETRY_INTERVAL","POOL_REAPER_INTERVAL","POOL_BENCHMARKING","POOL_LISTEN_TO_PROCESS_EXITS","LOGGING_LEVEL","LOGGING_FILE","LOGGING_DEST","UI_ENABLE","UI_ROUTE","OTHER_NO_LOGO","NODE_ENV","default","PROXY_SERVER_TIMEOUT","PROXY_SERVER_HOST","PROXY_SERVER_PORT","env","ExportError","Error","constructor","super","this","setError","statusCode","cache","activeManifest","sources","hcVersion","appliedConfig","extractVersion","indexOf","fetchAndProcessScript","script","proxyAgent","fetchedModules","shouldThrowError","agent","timeout","response","updateCache","sourcePath","customScripts","proxyHost","proxyPort","HttpsProxyAgent","moduleScripts","allFetchPromises","all","fetchScripts","c","m","writeFileSync","checkAndUpdateCache","manifestPath","requestUpdate","manifest","moduleMap","numberOfModules","some","moduleName","newManifest","saveConfigToManifest","getCachePath","cache$1","newVersion","assign","generalOptions","getOptions","mergeConfigOptions","newOptions","mergedOptions","updateDefaultConfig","configObj","customObj","customValue","initOptions","items","recursiveProps","objectToUpdate","nestedNames","shift","RANDOM_PID","randomBytes","PUPPETEER_DIR","path","minimalArgs","template","fs","browser","setPageContent","page","setContent","addScriptTag","evaluate","setupHighcharts","$eval","element","errorMessage","_displayErrors","innerHTML","clearPage","hardReset","goto","document","body","newPage","setCacheEnabled","close","isConnected","__basedir","setAsConfig","chart","triggerExport","puppeteerExport","injectedResources","clearInjected","dispose","scriptsToRemove","getElementsByTagName","stylesToRemove","linksToRemove","remove","exportOptions","requestAnimationFrame","displayErrors","debugger","isSVG","d","svgTemplate","strInj","js","push","content","isLocal","css","cssImports","match","cssImportPath","addStyleTag","size","chartHeight","baseVal","chartWidth","parseFloat","Highcharts","charts","viewportHeight","Math","ceil","viewportWidth","setViewport","deviceScaleFactor","zoomCallback","style","zoom","margin","x","y","getBoundingClientRect","trunc","getClipRegion","outerHTML","createSVG","encoding","clip","race","screenshot","omitBackground","_resolve","setTimeout","createImage","pdf","createPDF","oldCharts","oldChart","destroy","stats","performedExports","exportAttempts","exportFromSvgAttempts","timeSpent","droppedExports","spentAverage","puppeteerArgs","poolConfig","factory","create","id","uuid","startDate","getTime","browserNewPage","isClosed","workCount","random","validate","workerHandle","initPool","code","killPool","exit","allArgs","tryCount","open","launch","headless","userDataDir","createBrowser","parseInt","Pool","acquireTimeoutMillis","createTimeoutMillis","destroyTimeoutMillis","idleTimeoutMillis","createRetryIntervalMillis","reapIntervalMillis","propagateCreateError","resource","eventId","initialResources","acquire","promise","release","browserClose","destroyed","postWork","getPoolInfo","acquireCounter","payload","requestId","workStart","exportCounter","result","exportTime","numFree","numUsed","numPendingAcquires","pool$1","available","inUse","pendingAcquire","startExport","settings","endCallback","svg","initExportSettings","exportAsString","doStraightInject","doExport","findChartSize","exporting","precision","multiplier","pow","roundNumber","sourceHeight","sourceWidth","param","chartJson","customLogicOptions","allowCodeExecutionScoped","enabled","optionsName","stringToExport","chartJSON","logErrorMiddleware","req","next","returnErrorMiddleware","stCode","status","json","rateLimit","app","limitConfig","msg","rateOptions","limiter","windowMs","delayMs","handler","request","format","send","skip","query","access_token","use","HttpError","setStatus","reversedMime","png","jpeg","gif","requestsCounter","beforeRequest","afterRequest","doCallbacks","callbacks","uniqueId","callResponse","exportHandler","stopCounter","defaultOptions","headers","connection","remoteAddress","connectionAborted","socket","toLowerCase","substr","b64","noDownload","pattern","isPrivateRangeUrlFound","info","removeAllListeners","Buffer","from","header","attachment","params","filename","pkgFile","pather","serverStartTime","successRates","addHealthRoutes","_","period","movingAverage","reduce","a","b","bootTime","uptime","floor","highchartsVersion","averageProcessingTime","failedExports","sucessRatio","toFixed","svgExportAttempts","jsonExportAttempts","setInterval","successRatio","express","disable","cors","storage","multer","memoryStorage","upload","limits","fieldSize","limit","urlencoded","extended","none","attachErrorHandlers","startServer","serverConfig","httpServer","createServer","listen","cert","fsPromises","readFile","posix","httpsServer","NaN","static","healthRoute","post","exportRoutes","sendFile","uiRoute","adminToken","token","vSwitchRoute","errorHandler","enableRateLimiting","getExpress","getApp","middlewares","index","setOptions","userOptions","configIndex","findIndex","arg","fileName","loadConfigFile","showUsage","propertiesChain","argumentType","prop","pairArgumentValue","initExport","initLogging","singleExport","batchExport","batchFunctions","pair","mapToNewConfig","oldOptions","manualConfig","configFileName","configFile","choice","prompts","onSubmit","p","categories","questionsCounter","allQuestions","section","prompt","answer","module","writeFile","printLogo","packageVersion"],"mappings":"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"}
\ No newline at end of file