diff --git a/dist/index.cjs b/dist/index.cjs index 4ea67951..62ca7e1c 100644 --- a/dist/index.cjs +++ b/dist/index.cjs @@ -1,2 +1,2 @@ -"use strict";require("colors");var e=require("fs"),t=require("path"),r=require("https-proxy-agent"),o=require("prompts"),i=require("dotenv"),n=require("zod"),s=require("url"),a=require("http"),l=require("https"),c=require("tarn"),p=require("uuid"),u=require("puppeteer"),h=require("jsdom"),d=require("dompurify"),g=require("cors"),m=require("express"),f=require("multer"),v=require("express-rate-limit"),y="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 o=Object.getOwnPropertyDescriptor(e,r);Object.defineProperty(t,r,o.get?o:{enumerable:!0,get:function(){return e[r]}})}})),t.default=e,Object.freeze(t)}var w=b(s);const E={core:["highcharts","highcharts-more","highcharts-3d"],modules:["stock","map","gantt","exporting","export-data","parallel-coordinates","accessibility","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","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"],indicators:["indicators-all"]},T={puppeteer:{args:{value:["--allow-running-insecure-content","--ash-no-nudges","--autoplay-policy=user-gesture-required","--block-new-web-contents","--disable-accelerated-2d-canvas","--disable-background-networking","--disable-background-timer-throttling","--disable-backgrounding-occluded-windows","--disable-breakpad","--disable-checker-imaging","--disable-client-side-phishing-detection","--disable-component-extensions-with-background-pages","--disable-component-update","--disable-default-apps","--disable-dev-shm-usage","--disable-domain-reliability","--disable-extensions","--disable-features=CalculateNativeWinOcclusion,InterestFeedContentSuggestions,WebOTP","--disable-hang-monitor","--disable-ipc-flooding-protection","--disable-logging","--disable-notifications","--disable-offer-store-unmasked-wallet-cards","--disable-popup-blocking","--disable-print-preview","--disable-prompt-on-repost","--disable-renderer-backgrounding","--disable-search-engine-choice-screen","--disable-session-crashed-bubble","--disable-setuid-sandbox","--disable-site-isolation-trials","--disable-speech-api","--disable-sync","--enable-unsafe-webgpu","--hide-crash-restore-bubble","--hide-scrollbars","--metrics-recording-only","--mute-audio","--no-default-browser-check","--no-first-run","--no-pings","--no-sandbox","--no-startup-window","--no-zygote","--password-store=basic","--process-per-tab","--use-mock-keychain"],type:"string[]",description:"Arguments array to send to Puppeteer."}},highcharts:{version:{value:"latest",type:"string",envLink:"HIGHCHARTS_VERSION",description:"The Highcharts version to be used."},cdnURL:{value:"https://code.highcharts.com/",type:"string",envLink:"HIGHCHARTS_CDN_URL",description:"The CDN URL for Highcharts scripts to be used."},coreScripts:{value:E.core,type:"string[]",envLink:"HIGHCHARTS_CORE_SCRIPTS",description:"The core Highcharts scripts to fetch."},moduleScripts:{value:E.modules,type:"string[]",envLink:"HIGHCHARTS_MODULE_SCRIPTS",description:"The modules of Highcharts to fetch."},indicatorScripts:{value:E.indicators,type:"string[]",envLink:"HIGHCHARTS_INDICATOR_SCRIPTS",description:"The indicators of Highcharts to fetch."},customScripts:{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 custom scripts or dependencies to fetch."},forceFetch:{value:!1,type:"boolean",envLink:"HIGHCHARTS_FORCE_FETCH",description:"The flag to determine whether to refetch all scripts after each server rerun."},cachePath:{value:".cache",type:"string",envLink:"HIGHCHARTS_CACHE_PATH",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:{value:"png",type:"string",envLink:"EXPORT_TYPE",description:"The file export format. It can be jpeg, png, pdf, or svg."},constr:{value:"chart",type:"string",envLink:"EXPORT_CONSTR",description:"The constructor to use. Can be chart, stockChart, mapChart, or ganttChart."},defaultHeight:{value:400,type:"number",envLink:"EXPORT_DEFAULT_HEIGHT",description:"the default height of the exported chart. Used when no value is set."},defaultWidth:{value:600,type:"number",envLink:"EXPORT_DEFAULT_WIDTH",description:"The default width of the exported chart. Used when no value is set."},defaultScale:{value:1,type:"number",envLink:"EXPORT_DEFAULT_SCALE",description:"The default scale of the exported chart. Used when no value is set."},height:{value:!1,type:"number",description:"The height of the exported chart, overriding the option in the chart settings."},width:{value:!1,type:"number",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:{value:1500,type:"number",envLink:"EXPORT_RASTERIZATION_TIMEOUT",description:"The duration in milliseconds to wait for rendering a webpage."}},customLogic:{allowCodeExecution:{value:!1,type:"boolean",envLink:"CUSTOM_LOGIC_ALLOW_CODE_EXECUTION",description:"Controls whether the execution of arbitrary code is allowed during the exporting process."},allowFileResources:{value:!1,type:"boolean",envLink:"CUSTOM_LOGIC_ALLOW_FILE_RESOURCES",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:{value:!1,type:"boolean",envLink:"SERVER_ENABLE",cliName:"enableServer",description:"When set to true, the server starts on the local IP address 0.0.0.0."},host:{value:"0.0.0.0",type:"string",envLink:"SERVER_HOST",description:"The hostname of the server. Additionally, it starts a server on the provided hostname."},port:{value:7801,type:"number",envLink:"SERVER_PORT",description:"The server port when enabled."},benchmarking:{value:!1,type:"boolean",envLink:"SERVER_BENCHMARKING",cliName:"serverBenchmarking",description:"Indicates whether to display the duration, in milliseconds, of specific actions that occur on the server while serving a request."},proxy:{host:{value:!1,type:"string",envLink:"SERVER_PROXY_HOST",cliName:"proxyHost",description:"The host of the proxy server to use, if it exists."},port:{value:8080,type:"number",envLink:"SERVER_PROXY_PORT",cliName:"proxyPort",description:"The port of the proxy server to use, if it exists."},timeout:{value:5e3,type:"number",envLink:"SERVER_PROXY_TIMEOUT",cliName:"proxyTimeout",description:"The timeout for the proxy server to use, if it exists."}},rateLimiting:{enable:{value:!1,type:"boolean",envLink:"SERVER_RATE_LIMITING_ENABLE",cliName:"enableRateLimiting",description:"Enables rate limiting for the server."},maxRequests:{value:10,type:"number",envLink:"SERVER_RATE_LIMITING_MAX_REQUESTS",legacyName:"rateLimit",description:"The maximum number of requests allowed in one minute."},window:{value:1,type:"number",envLink:"SERVER_RATE_LIMITING_WINDOW",description:"The time window, in minutes, for the rate limiting."},delay:{value:0,type:"number",envLink:"SERVER_RATE_LIMITING_DELAY",description:"The delay duration for each successive request before reaching the maximum limit."},trustProxy:{value:!1,type:"boolean",envLink:"SERVER_RATE_LIMITING_TRUST_PROXY",description:"Set this to true if the server is behind a load balancer."},skipKey:{value:!1,type:"string",envLink:"SERVER_RATE_LIMITING_SKIP_KEY",description:"Allows bypassing the rate limiter and should be provided with the skipToken argument."},skipToken:{value:!1,type:"string",envLink:"SERVER_RATE_LIMITING_SKIP_TOKEN",description:"Allows bypassing the rate limiter and should be provided with the skipKey argument."}},ssl:{enable:{value:!1,type:"boolean",envLink:"SERVER_SSL_ENABLE",cliName:"enableSsl",description:"Enables or disables the SSL protocol."},force:{value:!1,type:"boolean",envLink:"SERVER_SSL_FORCE",cliName:"sslForce",legacyName:"sslOnly",description:"When set to true, the server is forced to serve only over HTTPS."},port:{value:443,type:"number",envLink:"SERVER_SSL_PORT",cliName:"sslPort",description:"The port on which to run the SSL server."},certPath:{value:!1,type:"string",envLink:"SERVER_SSL_CERT_PATH",legacyName:"sslPath",description:"The path to the SSL certificate/key file."}}},pool:{minWorkers:{value:4,type:"number",envLink:"POOL_MIN_WORKERS",description:"The number of minimum and initial pool workers to spawn."},maxWorkers:{value:8,type:"number",envLink:"POOL_MAX_WORKERS",legacyName:"workers",description:"The number of maximum pool workers to spawn."},workLimit:{value:40,type:"number",envLink:"POOL_WORK_LIMIT",description:"The number of work pieces that can be performed before restarting the worker process."},acquireTimeout:{value:5e3,type:"number",envLink:"POOL_ACQUIRE_TIMEOUT",description:"The duration, in milliseconds, to wait for acquiring a resource."},createTimeout:{value:5e3,type:"number",envLink:"POOL_CREATE_TIMEOUT",description:"The duration, in milliseconds, to wait for creating a resource."},destroyTimeout:{value:5e3,type:"number",envLink:"POOL_DESTROY_TIMEOUT",description:"The duration, in milliseconds, to wait for destroying a resource."},idleTimeout:{value:3e4,type:"number",envLink:"POOL_IDLE_TIMEOUT",description:"The duration, in milliseconds, after which an idle resource is destroyed."},createRetryInterval:{value:200,type:"number",envLink:"POOL_CREATE_RETRY_INTERVAL",description:"The duration, in milliseconds, to wait before retrying the create process in case of a failure."},reaperInterval:{value:1e3,type:"number",envLink:"POOL_REAPER_INTERVAL",description:"The duration, in milliseconds, after which the check for idle resources to destroy is triggered."},benchmarking:{value:!1,type:"boolean",envLink:"POOL_BENCHMARKING",cliName:"poolBenchmarking",description:"Indicate whether to show statistics for the pool of resources or not."}},logging:{level:{value:4,type:"number",envLink:"LOGGING_LEVEL",cliName:"logLevel",description:"The logging level to be used."},file:{value:"highcharts-export-server.log",type:"string",envLink:"LOGGING_FILE",cliName:"logFile",description:"The name of a log file. The logDest option also needs to be set to enable file logging."},dest:{value:"log/",type:"string",envLink:"LOGGING_DEST",cliName:"logDest",description:"The path to store log files. This also enables file logging."}},ui:{enable:{value:!1,type:"boolean",envLink:"UI_ENABLE",cliName:"enableUi",description:"Enables or disables the user interface (UI) for the export server."},route:{value:"/",type:"string",envLink:"UI_ROUTE",cliName:"uiRoute",description:"The endpoint route to which the user interface (UI) should be attached."}},other:{nodeEnv:{value:"production",type:"string",envLink:"OTHER_NODE_ENV",description:"The type of Node.js environment."},listenToProcessExits:{value:!0,type:"boolean",envLink:"OTHER_LISTEN_TO_PROCESS_EXITS",description:"Decides whether or not to attach process.exit handlers."},noLogo:{value:!1,type:"boolean",envLink:"OTHER_NO_LOGO",description:"Skip printing the logo on a startup. Will be replaced by a simple text."},hardResetPage:{value:!1,type:"boolean",envLink:"OTHER_HARD_RESET_PAGE",description:"Decides if the page content should be reset entirely."}},debug:{enable:{value:!1,type:"boolean",envLink:"DEBUG_ENABLE",cliName:"enableDebug",description:"Enables or disables debug mode for the underlying browser."},headless:{value:!0,type:"boolean",envLink:"DEBUG_HEADLESS",description:"Controls the mode in which the browser is launched when in the debug mode."},devtools:{value:!1,type:"boolean",envLink:"DEBUG_DEVTOOLS",description:"Decides whether to enable DevTools when the browser is in a headful state."},listenToConsole:{value:!1,type:"boolean",envLink:"DEBUG_LISTEN_TO_CONSOLE",description:"Decides whether to enable a listener for console messages sent from the browser."},dumpio:{value:!1,type:"boolean",envLink:"DEBUG_DUMPIO",description:"Redirects browser process stdout and stderr to process.stdout and process.stderr."},slowMo:{value:0,type:"number",envLink:"DEBUG_SLOW_MO",description:"Slows down Puppeteer operations by the specified number of milliseconds."},debuggingPort:{value:9222,type:"number",envLink:"DEBUG_DEBUGGING_PORT",description:"Specifies the debugging port."}}},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:"coreScripts",message:"Available core scripts",instructions:"Space: Select specific, A: Select all, Enter: Confirm.",choices:T.highcharts.coreScripts.value},{type:"multiselect",name:"moduleScripts",message:"Available module scripts",instructions:"Space: Select specific, A: Select all, Enter: Confirm.",choices:T.highcharts.moduleScripts.value},{type:"multiselect",name:"indicatorScripts",message:"Available indicator scripts",instructions:"Space: Select specific, A: Select all, Enter: Confirm.",choices:T.highcharts.indicatorScripts.value},{type:"list",name:"customScripts",message:"Custom scripts",initial:T.highcharts.customScripts.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:"text",name:"proxy.host",message:"The host of the proxy server to use",initial:T.server.proxy.host.value},{type:"number",name:"proxy.port",message:"The port of the proxy server to use",initial:T.server.proxy.port.value},{type:"number",name:"proxy.timeout",message:"The timeout for the proxy server to use",initial:T.server.proxy.timeout.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},{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}],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}],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:"text",name:"nodeEnv",message:"The type of Node.js environment",initial:T.other.nodeEnv.value},{type:"toggle",name:"listenToProcessExits",message:"Set to false to skip attaching process.exit handlers",initial:T.other.listenToProcessExits.value},{type:"toggle",name:"noLogo",message:"Skip printing the logo on startup. Replaced by simple text",initial:T.other.noLogo.value},{type:"toggle",name:"hardResetPage",message:"Decides if the page content should be reset entirely",initial:T.other.hardResetPage.value}],debug:[{type:"toggle",name:"enable",message:"Enables debug mode for the browser instance",initial:T.debug.enable.value},{type:"toggle",name:"headless",message:"The mode setting for the browser",initial:T.debug.headless.value},{type:"toggle",name:"devtools",message:"The DevTools for the headful browser",initial:T.debug.devtools.value},{type:"toggle",name:"listenToConsole",message:"The event listener for console messages from the browser",initial:T.debug.listenToConsole.value},{type:"toggle",name:"dumpio",message:"Redirects the browser stdout and stderr to NodeJS process",initial:T.debug.dumpio.value},{type:"number",name:"slowMo",message:"Puppeteer operations slow down in milliseconds",initial:T.debug.slowMo.value},{type:"number",name:"debuggingPort",message:"The port number for debugging",initial:T.debug.debuggingPort.value}]},x=["options","globalOptions","themeOptions","resources","payload"],R={},L=(e,t="")=>{Object.keys(e).forEach((r=>{if(!["puppeteer","highcharts"].includes(r)){const o=e[r];void 0===o.value?L(o,`${t}.${r}`):(R[o.cliName||r]=`${t}.${r}`.substring(1),void 0!==o.legacyName&&(R[o.legacyName]=`${t}.${r}`.substring(1)))}}))};L(T),i.config();const O=e=>n.z.string().transform((t=>t.split(",").map((e=>e.trim())).filter((t=>e.includes(t))))).transform((e=>e.length?e:void 0)),_=()=>n.z.enum(["true","false",""]).transform((e=>""!==e?"true"===e:void 0)),k=e=>n.z.enum([...e,""]).transform((e=>""!==e?e:void 0)),I=()=>n.z.string().trim().refine((e=>!["false","undefined","null","NaN"].includes(e)||""===e),(e=>({message:`The string contains forbidden values, received '${e}'`}))).transform((e=>""!==e?e:void 0)),C=()=>n.z.string().trim().refine((e=>""===e||!isNaN(parseFloat(e))&&parseFloat(e)>0),(e=>({message:`The value must be numeric and positive, received '${e}'`}))).transform((e=>""!==e?parseFloat(e):void 0)),A=()=>n.z.string().trim().refine((e=>""===e||!isNaN(parseFloat(e))&&parseFloat(e)>=0),(e=>({message:`The value must be numeric and non-negative, received '${e}'`}))).transform((e=>""!==e?parseFloat(e):void 0)),N=n.z.object({HIGHCHARTS_VERSION:n.z.string().trim().refine((e=>/^(latest|\d+(\.\d+){0,2})$/.test(e)||""===e),(e=>({message:`HIGHCHARTS_VERSION must be 'latest', a major version, or in the form XX.YY.ZZ, received '${e}'`}))).transform((e=>""!==e?e:void 0)),HIGHCHARTS_CDN_URL:n.z.string().trim().refine((e=>e.startsWith("https://")||e.startsWith("http://")||""===e),(e=>({message:`Invalid value for HIGHCHARTS_CDN_URL. It should start with http:// or https://, received '${e}'`}))).transform((e=>""!==e?e:void 0)),HIGHCHARTS_CORE_SCRIPTS:O(E.core),HIGHCHARTS_MODULE_SCRIPTS:O(E.modules),HIGHCHARTS_INDICATOR_SCRIPTS:O(E.indicators),HIGHCHARTS_FORCE_FETCH:_(),HIGHCHARTS_CACHE_PATH:I(),HIGHCHARTS_ADMIN_TOKEN:I(),EXPORT_TYPE:k(["jpeg","png","pdf","svg"]),EXPORT_CONSTR:k(["chart","stockChart","mapChart","ganttChart"]),EXPORT_DEFAULT_HEIGHT:C(),EXPORT_DEFAULT_WIDTH:C(),EXPORT_DEFAULT_SCALE:C(),EXPORT_RASTERIZATION_TIMEOUT:A(),CUSTOM_LOGIC_ALLOW_CODE_EXECUTION:_(),CUSTOM_LOGIC_ALLOW_FILE_RESOURCES:_(),SERVER_ENABLE:_(),SERVER_HOST:I(),SERVER_PORT:C(),SERVER_BENCHMARKING:_(),SERVER_PROXY_HOST:I(),SERVER_PROXY_PORT:C(),SERVER_PROXY_TIMEOUT:A(),SERVER_RATE_LIMITING_ENABLE:_(),SERVER_RATE_LIMITING_MAX_REQUESTS:A(),SERVER_RATE_LIMITING_WINDOW:A(),SERVER_RATE_LIMITING_DELAY:A(),SERVER_RATE_LIMITING_TRUST_PROXY:_(),SERVER_RATE_LIMITING_SKIP_KEY:I(),SERVER_RATE_LIMITING_SKIP_TOKEN:I(),SERVER_SSL_ENABLE:_(),SERVER_SSL_FORCE:_(),SERVER_SSL_PORT:C(),SERVER_SSL_CERT_PATH:I(),POOL_MIN_WORKERS:A(),POOL_MAX_WORKERS:A(),POOL_WORK_LIMIT:C(),POOL_ACQUIRE_TIMEOUT:A(),POOL_CREATE_TIMEOUT:A(),POOL_DESTROY_TIMEOUT:A(),POOL_IDLE_TIMEOUT:A(),POOL_CREATE_RETRY_INTERVAL:A(),POOL_REAPER_INTERVAL:A(),POOL_BENCHMARKING:_(),LOGGING_LEVEL:n.z.string().trim().refine((e=>""===e||!isNaN(parseFloat(e))&&parseFloat(e)>=0&&parseFloat(e)<=5),(e=>({message:`Invalid value for LOGGING_LEVEL. We only accept values from 0 to 5 as logging levels, received '${e}'`}))).transform((e=>""!==e?parseFloat(e):void 0)),LOGGING_FILE:I(),LOGGING_DEST:I(),UI_ENABLE:_(),UI_ROUTE:I(),OTHER_NODE_ENV:k(["development","production","test"]),OTHER_LISTEN_TO_PROCESS_EXITS:_(),OTHER_NO_LOGO:_(),OTHER_HARD_RESET_PAGE:_(),DEBUG_ENABLE:_(),DEBUG_HEADLESS:_(),DEBUG_DEVTOOLS:_(),DEBUG_LISTEN_TO_CONSOLE:_(),DEBUG_DUMPIO:_(),DEBUG_SLOW_MO:A(),DEBUG_DEBUGGING_PORT:C()}).partial().parse(process.env),P=["red","yellow","blue","gray","green"];let H={toConsole:!0,toFile:!1,pathCreated:!1,levelsDesc:[{title:"error",color:P[0]},{title:"warning",color:P[1]},{title:"notice",color:P[2]},{title:"verbose",color:P[3]},{title:"benchmark",color:P[4]}],listeners:[]};for(const[e,t]of Object.entries(T.logging))H[e]=t.value;const $=(t,r)=>{H.toFile&&(H.pathCreated||(!e.existsSync(H.dest)&&e.mkdirSync(H.dest),H.pathCreated=!0),e.appendFile(`${H.dest}${H.file}`,[r].concat(t).join(" ")+"\n",(e=>{e&&(console.log(`[logger] Unable to write to log file: ${e}`),H.toFile=!1)})))},U=(...e)=>{const[t,...r]=e,{level:o,levelsDesc:i}=H;if(5!==t&&(0===t||t>o||o>i.length))return;const n=`${(new Date).toString().split("(")[0].trim()} [${i[t-1].title}] -`;H.listeners.forEach((e=>{e(n,r.join(" "))})),H.toConsole&&console.log.apply(void 0,[n.toString()[H.levelsDesc[t-1].color]].concat(r)),$(r,n)},j=(e,t,r)=>{const o=r||t.message,{level:i,levelsDesc:n}=H;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];H.toConsole&&console.log.apply(void 0,[s.toString()[H.levelsDesc[e-1].color]].concat([o[P[e-1]],"\n",a])),H.listeners.forEach((e=>{e(s,l.join(" "))})),$(l,s)},D=e=>{e>=0&&e<=H.levelsDesc.length&&(H.level=e)},G=(e,t)=>{if(H={...H,dest:e||H.dest,file:t||H.file,toFile:!0},0===H.dest.length)return U(1,"[logger] File logging initialization: no path supplied.");H.dest.endsWith("/")||(H.dest+="/")},F=s.fileURLToPath(new URL("../.","undefined"==typeof document?require("url").pathToFileURL(__filename).href:y&&y.src||new URL("index.cjs",document.baseURI).href)),M=(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"},q=(t=!1,r)=>{const o=["js","css","files"];let i=t,n=!1;if(r&&t.endsWith(".json"))try{i=W(e.readFileSync(t,"utf8"))}catch(e){return j(2,e,"[cli] No resources found.")}else i=W(t),i&&!r&&delete i.files;for(const e in i)o.includes(e)?n||(n=!0):delete i[e];return n?(i.files&&(i.files=i.files.map((e=>e.trim())),(!i.files||i.files.length<=0)&&delete i.files),i):U(3,"[cli] No resources found.")};function W(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 V=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]=V(e[r]));return t},B=(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(T).forEach((t=>{["puppeteer","highcharts"].includes(t)||(console.log(`\n${t.toUpperCase()}`.red),e(T[t]))})),console.log("\n")}const z=e=>!["false","undefined","null","NaN","0",""].includes(e)&&!!e,K=(t,r)=>{if(t&&"string"==typeof t)return(t=t.trim()).endsWith(".js")?!!r&&K(e.readFileSync(t,"utf8")):t.startsWith("function()")||t.startsWith("function ()")||t.startsWith("()=>")||t.startsWith("() =>")?`(${t})()`:t.replace(/;$/,"")},J=()=>{const e=process.hrtime.bigint();return()=>Number(process.hrtime.bigint()-e)/1e6};let Y={};const Q=()=>Y,Z=(e,t,r=[])=>{const o=V(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]:Z(o[e],n,r);var i;return o};function ee(e,t={},r=""){Object.keys(e).forEach((o=>{const i=e[o],n=t&&t[o];void 0===i.value?ee(i,n,`${r}.${o}`):(void 0!==n&&(i.value=n),i.envLink in N&&void 0!==N[i.envLink]&&(i.value=N[i.envLink]))}))}function te(e){let t={};for(const[r,o]of Object.entries(e))t[r]=Object.prototype.hasOwnProperty.call(o,"value")?o.value:te(o);return t}function re(e,t,r){for(;t.length>1;){const o=t.shift();return Object.prototype.hasOwnProperty.call(e,o)||(e[o]={}),e[o]=re(Object.assign({},e[o]),t,r),e}return e[t[0]]=r,e}async function oe(e,t={}){return new Promise(((r,o)=>{const i=(e=>e.startsWith("https")?l:a)(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)}))}))}class ie 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 ne={cdnURL:"https://code.highcharts.com/",activeManifest:{},sources:"",hcVersion:""},se=e=>e.sources.substring(0,e.sources.indexOf("*/")).replace("/*","").replace("*/","").replace(/\n/g,"").trim(),ae=async(e,t,r,o=!1)=>{e.endsWith(".js")&&(e=e.substring(0,e.length-3)),U(4,`[cache] Fetching script - ${e}.js`);const i=await oe(`${e}.js`,t);if(200===i.statusCode&&"string"==typeof i.text){if(r){r[e.replace(/(.*)\/|(.*)modules\/|stock\/(.*)indicators\/|maps\/(.*)modules\//gi,"")]=1}return i.text}if(o)throw new ie(`Could not fetch the ${e}.js. The script might not exist in the requested version (status code: ${i.statusCode}).`).setError(i);return U(2,`[cache] Could not fetch the ${e}.js. The script might not exist in the requested version.`),""},le=async(t,o,i)=>{const n=t.version,s="latest"!==n&&n?`${n}/`:"",a=t.cdnURL||ne.cdnURL;U(3,`[cache] Updating cache version to Highcharts: ${s||"latest"}.`);const l={};try{return ne.sources=await(async(e,t,o,i,n)=>{let s;const a=i.host,l=i.port;if(a&&l)try{s=new r.HttpsProxyAgent({host:a,port:l})}catch(e){throw new ie("[cache] Could not create a Proxy Agent.").setError(e)}const c=s?{agent:s,timeout:N.SERVER_PROXY_TIMEOUT}:{},p=[...e.map((e=>ae(`${e}`,c,n,!0))),...t.map((e=>ae(`${e}`,c,n))),...o.map((e=>ae(`${e}`,c)))];return(await Promise.all(p)).join(";\n")})([...t.coreScripts.map((e=>`${a}${s}${e}`))],[...t.moduleScripts.map((e=>"map"===e?`${a}maps/${s}modules/${e}`:`${a}${s}modules/${e}`)),...t.indicatorScripts.map((e=>`${a}stock/${s}indicators/${e}`))],t.customScripts,o,l),ne.hcVersion=se(ne),e.writeFileSync(i,ne.sources),l}catch(e){throw new ie("[cache] Unable to update the local Highcharts cache.").setError(e)}},ce=async r=>{const{highcharts:o,server:i}=r,n=t.join(F,o.cachePath);let s;const a=t.join(n,"manifest.json"),l=t.join(n,"sources.js");if(!e.existsSync(n)&&e.mkdirSync(n),!e.existsSync(a)||o.forceFetch)U(3,"[cache] Fetching and caching Highcharts dependencies."),s=await le(o,i.proxy,l);else{let t=!1;const r=JSON.parse(e.readFileSync(a));if(r.modules&&Array.isArray(r.modules)){const e={};r.modules.forEach((t=>e[t]=1)),r.modules=e}const{coreScripts:n,moduleScripts:c,indicatorScripts:p}=o,u=n.length+c.length+p.length;r.version!==o.version?(U(2,"[cache] A Highcharts version mismatch in the cache, need to re-fetch."),t=!0):Object.keys(r.modules||{}).length!==u?(U(2,"[cache] The cache and the requested modules do not match, need to re-fetch."),t=!0):t=(c||[]).some((e=>{if(!r.modules[e])return U(2,`[cache] The ${e} is missing in the cache, need to re-fetch.`),!0})),t?s=await le(o,i.proxy,l):(U(3,"[cache] Dependency cache is up to date, proceeding."),ne.sources=e.readFileSync(l,"utf8"),s=r.modules,ne.hcVersion=se(ne))}await(async(r,o)=>{const i={version:r.version,modules:o||{}};ne.activeManifest=i,U(3,"[cache] Writing a new manifest.");try{e.writeFileSync(t.join(F,r.cachePath,"manifest.json"),JSON.stringify(i),"utf8")}catch(e){throw new ie("[cache] Error writing the cache manifest.").setError(e)}})(o,s)},pe=()=>t.join(F,Q().highcharts.cachePath);var ue=async e=>{const t=Q();t?.highcharts&&(t.highcharts.version=e),await ce(t)},he=()=>ne,de=()=>ne.hcVersion;function ge(){Highcharts.animObject=function(){return{duration:0}}}function me(e,t,r){window._displayErrors=r;const{getOptions:o,merge:i,setOptions:n,wrap:s}=Highcharts;Highcharts.setOptionsObj=i(!1,{},o()),t.customLogic.customCode&&new Function(t.customLogic.customCode)();const a={animation:!1};t.export.strInj&&(a.height=e.chart.height,a.width=e.chart.width),window.isRenderComplete=!1,s(Highcharts.Chart.prototype,"init",(function(e,t,r){((t=i(t,{exporting:{enabled:!1},plotOptions:{series:{label:{enabled:!1}}},tooltip:{}})).series||[]).forEach((function(e){e.animation=!1})),window.onHighchartsRender||(window.onHighchartsRender=Highcharts.addEvent(this,"render",(()=>{window.isRenderComplete=!0}))),e.apply(this,[t,r])})),s(Highcharts.Series.prototype,"init",(function(e,t,r){e.apply(this,[t,r])}));const l=t.export.strInj?new Function(`return ${t.export.strInj}`)():e,c=i(!1,JSON.parse(t.export.themeOptions),l,{chart:a}),p=t.customLogic.callback?new Function(`return ${t.customLogic.callback}`)():void 0,u=JSON.parse(t.export.globalOptions);u&&n(u),Highcharts[t.export.constr||"chart"]("container",c,p);const h=o();for(const e in h)"function"!=typeof h[e]&&delete h[e];n(Highcharts.setOptionsObj),Highcharts.setOptionsObj={}}const fe=w.fileURLToPath(new URL(".","undefined"==typeof document?require("url").pathToFileURL(__filename).href:y&&y.src||new URL("index.cjs",document.baseURI).href)),ve=e.readFileSync(fe+"/../templates/template.html","utf8");let ye;async function be(){if(!ye)return!1;const e=await ye.newPage();return await e.setCacheEnabled(!1),await we(e),e}async function we(e){await e.setContent(ve,{waitUntil:"domcontentloaded"}),await e.addScriptTag({path:`${pe()}/sources.js`}),await e.evaluate(ge)}const Ee=w.fileURLToPath(new URL(".","undefined"==typeof document?require("url").pathToFileURL(__filename).href:y&&y.src||new URL("index.cjs",document.baseURI).href)),Te=(e,t,r,o)=>e.evaluate(me,t,r,o);var Se=async(r,o,i)=>{const n=[],s=async e=>{for(const e of n)await e.dispose();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()}const[...e]=document.getElementsByTagName("script"),[,...t]=document.getElementsByTagName("style"),[...r]=document.getElementsByTagName("link");for(const o of[...e,...t,...r])o.remove()}))};try{U(4,"[export] Determining export path.");const a=i.export,l=a?.options?.chart?.displayErrors&&he().activeManifest.modules.debugger;let c;if(o.indexOf&&(o.indexOf("=0||o.indexOf("=0)){if(U(4,"[export] Treating as SVG."),"svg"===a.type)return o;c=!0,await r.setContent((e=>`\n\n\n \n \n Highcharts Export\n \n \n \n
\n ${e}\n
\n \n\n\n`)(o),{waitUntil:"domcontentloaded"})}else U(4,"[export] Treating as config."),a.strInj?await Te(r,{chart:{height:a.height,width:a.width}},i,l):(o.chart.height=a.height,o.chart.width=a.width,await Te(r,o,i,l));const p=i.customLogic.resources;if(p){const o=[];if(p.js&&o.push({content:p.js}),p.files)for(const t of p.files){const r=!t.startsWith("http");o.push(r?{content:e.readFileSync(t,"utf8")}:{url:t})}for(const e of o)try{n.push(await r.addScriptTag(e))}catch(e){j(2,e,"[export] The JS resource cannot be loaded.")}o.length=0;const s=[];if(p.css){let e=p.css.match(/@import\s*([^;]*);/g);if(e)for(let r of e)r&&(r=r.replace("url(","").replace("@import","").replace(/"/g,"").replace(/'/g,"").replace(/;/,"").replace(/\)/g,"").trim(),r.startsWith("http")?s.push({url:r}):i.customLogic.allowFileResources&&s.push({path:t.join(Ee,r)}));s.push({content:p.css.replace(/@import\s*([^;]*);/g,"")||" "});for(const e of s)try{n.push(await r.addStyleTag(e))}catch(e){j(2,e,"[export] The CSS resource cannot be loaded.")}s.length=0}}const u=c?await r.evaluate((e=>{const t=document.querySelector("#chart-container svg:first-of-type"),r=t.height.baseVal.value*e,o=t.width.baseVal.value*e;return document.body.style.zoom=e,document.body.style.margin="0px",{chartHeight:r,chartWidth:o}}),parseFloat(a.scale)):await r.evaluate((()=>{const{chartHeight:e,chartWidth:t}=window.Highcharts.charts[0];return document.body.style.zoom=1,{chartHeight:e,chartWidth:t}})),h=Math.ceil(u.chartHeight||a.height),d=Math.ceil(u.chartWidth||a.width),{x:g,y:m}=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)}})))(r);let f;if(await r.setViewport({height:h,width:d,deviceScaleFactor:c?1:parseFloat(a.scale)}),"svg"===a.type)f=await(e=>e.$eval("#container svg:first-of-type",(e=>e.outerHTML)))(r);else if(["png","jpeg"].includes(a.type))f=await((e,t,r,o,i)=>Promise.race([e.screenshot({type:t,encoding:r,clip:o,captureBeyondViewport:!0,fullPage:!1,optimizeForSpeed:!0,..."png"!==t?{quality:80}:{},omitBackground:"png"==t}),new Promise(((e,t)=>setTimeout((()=>t(new ie("Rasterization timeout"))),i||1500)))]))(r,a.type,"base64",{width:d,height:h,x:g,y:m},a.rasterizationTimeout);else{if("pdf"!==a.type)throw new ie(`[export] Unsupported output format ${a.type}.`);f=await(async(e,t,r,o,i)=>(await e.emulateMediaType("screen"),Promise.race([e.pdf({height:t+1,width:r,encoding:o}),new Promise(((e,t)=>setTimeout((()=>t(new ie("Rasterization timeout"))),i||1500)))])))(r,h,d,"base64",a.rasterizationTimeout)}return await s(r),f}catch(e){return await s(r),e}};let xe=!1;const Re={performedExports:0,exportAttempts:0,exportFromSvgAttempts:0,timeSpent:0,droppedExports:0,spentAverage:0};let Le={};const Oe={create:async()=>{let e=!1;const t=p.v4(),r=(new Date).getTime();try{if(e=await be(),!e||e.isClosed())throw new ie("The page is invalid or closed.");U(3,`[pool] Successfully created a worker ${t} - took ${(new Date).getTime()-r} ms.`)}catch(e){throw new ie("Error encountered when creating a new page.").setError(e)}const{debug:o}=Q();return o.enable&&o.listenToConsole&&e.on("console",(e=>{console.log(`[debug] ${e.text()}`)})),e.on("pageerror",(async t=>{await e.$eval("#container",((e,t)=>{window._displayErrors&&(e.innerHTML=t)}),`

Chart input data error:

${t.toString()}`)})),{id:t,page:e,workCount:Math.round(Math.random()*(Le.workLimit/2))}},validate:async e=>!(Le.workLimit&&++e.workCount>Le.workLimit)||(U(3,`[pool] Worker failed validation: exceeded work limit (limit is ${Le.workLimit}).`),!1),destroy:async e=>{U(3,`[pool] Destroying pool entry ${e.id}.`),e.page&&await e.page.close()}},_e=async e=>{if(Le=e&&e.pool?{...e.pool}:{},await async function(e){const{enable:t,...r}=Q().debug,o={headless:"shell",userDataDir:"./tmp/",args:e,handleSIGINT:!1,handleSIGTERM:!1,handleSIGHUP:!1,waitForInitialPage:!1,defaultViewport:null,...t&&r};if(!ye){let e=0;const r=async()=>{try{U(3,`[browser] Attempting to get a browser instance (try ${++e}).`),ye=await u.launch(o)}catch(t){if(j(1,t,"[browser] Failed to launch a browser instance."),!(e<25))throw t;U(3,`[browser] Retry to open a browser (${e} out of 25).`),await new Promise((e=>setTimeout(e,4e3))),await r()}};try{await r(),t&&U(3,"[browser] Launched browser in debug mode.")}catch(e){throw new ie("[browser] Maximum retries to open a browser instance reached.").setError(e)}if(!ye)throw new ie("[browser] Cannot find a browser to open.")}return ye}(e.puppeteerArgs),U(3,`[pool] Initializing pool with workers: min ${Le.minWorkers}, max ${Le.maxWorkers}.`),xe)return U(4,"[pool] Already initialized, please kill it before creating a new one.");parseInt(Le.minWorkers)>parseInt(Le.maxWorkers)&&(Le.minWorkers=Le.maxWorkers);try{xe=new c.Pool({...Oe,min:parseInt(Le.minWorkers),max:parseInt(Le.maxWorkers),acquireTimeoutMillis:Le.acquireTimeout,createTimeoutMillis:Le.createTimeout,destroyTimeoutMillis:Le.destroyTimeout,idleTimeoutMillis:Le.idleTimeout,createRetryIntervalMillis:Le.createRetryInterval,reapIntervalMillis:Le.reaperInterval,propagateCreateError:!1}),xe.on("release",(async e=>{await async function(e,t=!1){try{e.isClosed()||(t?(await e.goto("about:blank",{waitUntil:"domcontentloaded"}),await we(e)):await e.evaluate((()=>{document.body.innerHTML='
'})))}catch(e){j(2,e,"[browser] Could not clear the content of the page.")}}(e.page,!1),U(4,`[pool] Releasing a worker with ID ${e.id}.`)})),xe.on("destroySuccess",((e,t)=>{U(4,`[pool] Destroyed a worker with ID ${t.id}.`)}));const e=[];for(let t=0;t{xe.release(e)})),U(3,"[pool] The pool is ready"+(e.length?` with ${e.length} initial resources waiting.`:"."))}catch(e){throw new ie("[pool] Could not create the pool of workers.").setError(e)}};async function ke(){if(U(3,"[pool] Killing pool with all workers and closing browser."),xe){for(const e of xe.used)xe.release(e.resource);xe.destroyed||(await xe.destroy(),U(4,"[browser] Destroyed the pool of resources."))}await async function(){ye?.connected&&await ye.close(),U(4,"[browser] Closed the browser.")}()}const Ie=async(e,t)=>{let r;try{if(U(4,"[pool] Work received, starting to process."),++Re.exportAttempts,Le.benchmarking&&Ae(),!xe)throw new ie("Work received, but pool has not been started.");const o=J();try{U(4,"[pool] Acquiring a worker handle."),r=await xe.acquire().promise,t.server.benchmarking&&U(5,t.payload?.requestId?`[benchmark] Request with ID ${t.payload?.requestId} -`:"[benchmark]",`Acquired a worker handle: ${o()}ms.`)}catch(e){throw new ie((t.payload?.requestId?`For request with ID ${t.payload?.requestId} - `:"")+`Error encountered when acquiring an available entry: ${o()}ms.`).setError(e)}if(U(4,"[pool] Acquired a worker handle."),!r.page)throw new ie("Resolved worker page is invalid: the pool setup is wonky.");let i=(new Date).getTime();U(4,`[pool] Starting work on pool entry with ID ${r.id}.`);const n=J(),s=await Se(r.page,e,t);if(s instanceof Error)throw"Rasterization timeout"===s.message&&(r.page.close(),r.page=await be()),new ie((t.payload?.requestId?`For request with ID ${t.payload?.requestId} - `:"")+`Error encountered during export: ${n()}ms.`).setError(s);t.server.benchmarking&&U(5,t.payload?.requestId?`[benchmark] Request with ID ${t.payload?.requestId} -`:"[benchmark]",`Exported a chart sucessfully: ${n()}ms.`),xe.release(r);const a=(new Date).getTime()-i;return Re.timeSpent+=a,Re.spentAverage=Re.timeSpent/++Re.performedExports,U(4,`[pool] Work completed in ${a} ms.`),{result:s,options:t}}catch(e){throw++Re.droppedExports,r&&xe.release(r),new ie(`[pool] In pool.postWork: ${e.message}`).setError(e)}},Ce=()=>({min:xe.min,max:xe.max,all:xe.numFree()+xe.numUsed(),available:xe.numFree(),used:xe.numUsed(),pending:xe.numPendingAcquires()});function Ae(){const{min:e,max:t,all:r,available:o,used:i,pending:n}=Ce();U(5,`[pool] The minimum number of resources allowed by pool: ${e}.`),U(5,`[pool] The maximum number of resources allowed by pool: ${t}.`),U(5,`[pool] The number of all created resources: ${r}.`),U(5,`[pool] The number of available resources: ${o}.`),U(5,`[pool] The number of acquired resources: ${i}.`),U(5,`[pool] The number of resources waiting to be acquired: ${n}.`)}var Ne=Ce,Pe=()=>Re;let He=!1;const $e=async(t,r)=>{U(4,"[chart] Starting the exporting process.");const o=((e,t={})=>{let r={};return e.svg?(r=V(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=Z(t,e,x),r.export.outfile=r.export?.outfile||`chart.${r.export?.type||"png"}`,r})(t,Q()),i=o.export;if(o.payload?.svg&&""!==o.payload.svg)try{U(4,"[chart] Attempting to export from a SVG input.");const e=Ge(function(e){const t=new h.JSDOM("").window;return d(t).sanitize(e,{ADD_TAGS:["foreignObject"]})}(o.payload.svg),o,r);return++Re.exportFromSvgAttempts,e}catch(e){return r(new ie("[chart] Error loading SVG input.").setError(e))}if(i.infile&&i.infile.length)try{return U(4,"[chart] Attempting to export from an input file."),o.export.instr=e.readFileSync(i.infile,"utf8"),Ge(o.export.instr.trim(),o,r)}catch(e){return r(new ie("[chart] Error loading input file.").setError(e))}if(i.instr&&""!==i.instr||i.options&&""!==i.options)try{return U(4,"[chart] Attempting to export from a raw input."),z(o.customLogic?.allowCodeExecution)?De(o,r):"string"==typeof i.instr?Ge(i.instr.trim(),o,r):je(o,i.instr||i.options,r)}catch(e){return r(new ie("[chart] Error loading raw input.").setError(e))}return r(new ie("[chart] No valid input specified. Check if at least one of the following parameters is correctly set: 'infile', 'instr', 'options', or 'svg'."))},Ue=e=>{const{chart:t,exporting:r}=e.export?.options||W(e.export?.instr),o=W(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},je=async(t,r,o,i)=>{let{export:n,customLogic:s}=t;const a="boolean"==typeof s.allowCodeExecution?s.allowCodeExecution:He;if(s){if(a)if("string"==typeof t.customLogic.resources)t.customLogic.resources=q(t.customLogic.resources,z(t.customLogic.allowFileResources));else if(!t.customLogic.resources)try{const r=e.readFileSync("resources.json","utf8");t.customLogic.resources=q(r,z(t.customLogic.allowFileResources))}catch(e){j(2,e,"[chart] Unable to load the default resources.json file.")}}else s=t.customLogic={};if(!a&&s){if(s.callback||s.resources||s.customCode)return o(new ie("[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=M(n.type,n.outfile),"svg"===n.type&&(n.width=!1),["globalOptions","themeOptions"].forEach((t=>{try{n&&n[t]&&("string"==typeof n[t]&&n[t].endsWith(".json")?n[t]=W(e.readFileSync(n[t],"utf8"),!0):n[t]=W(n[t],!0))}catch(e){n[t]={},j(2,e,`[chart] The '${t}' cannot be loaded.`)}})),s.allowCodeExecution)try{s.customCode=K(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=e.readFileSync(s.callback,"utf8")}catch(e){s.callback=!1,j(2,e,"[chart] The 'callback' cannot be loaded.")}else s.callback=!1;t.export={...t.export,...Ue(t)};try{return o(!1,await Ie(n.strInj||r||i,t))}catch(e){return o(e)}},De=(e,t)=>{try{let r,o=e.export.instr||e.export.options;return"string"!=typeof o&&(r=o=B(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,je(e,!1,t)}catch(r){return t(new ie(`[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))}},Ge=(e,t,r)=>{const{allowCodeExecution:o}=t.customLogic;if(e.indexOf("=0||e.indexOf("=0)return U(4,"[chart] Parsing input as SVG."),je(t,!1,r,e);try{const o=JSON.parse(e.replaceAll(/\t|\n|\r/g," "));return je(t,o,r)}catch(e){return z(o)?De(t,r):r(new ie("[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))}},Fe=[],Me=()=>{U(4,"[server] Clearing all registered intervals.");for(const e of Fe)clearInterval(e)},qe=(e,t,r,o)=>{j(1,e),"development"!==N.OTHER_NODE_ENV&&delete e.stack,o(e)},We=(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 Ve=(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=v({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&&(U(4,"[rate limiting] Skipping rate limiter."),!0)});e.use(i),U(3,`[rate limiting] Enabled rate limiting with ${o.max} requests per ${o.window} minute for each IP, trusting proxy: ${o.trustProxy}.`)};class Be extends ie{constructor(e,t){super(e),this.status=this.statusCode=t}setStatus(e){return this.status=e,this}}const Xe={png:"image/png",jpeg:"image/jpeg",gif:"image/gif",pdf:"application/pdf",svg:"image/svg+xml"};let ze=0;const Ke=[],Je=[],Ye=(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},Qe=async(e,t,r)=>{try{const r=J(),i=p.v4().replace(/-/g,""),n=Q(),s=e.body,a=++ze;let l=M(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=W(s.infile||s.options||s.data);if(!c&&!s.svg)throw U(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 u=!1;if(u=Ye(Ke,e,t,{id:a,uniqueId:i,type:l,body:s}),!0!==u)return t.send(u);let h=!1;e.socket.on("close",(()=>{h=!0})),U(4,`[export] Got an incoming HTTP request with ID ${i}.`),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:W(s.globalOptions,!0),themeOptions:W(s.themeOptions,!0)},customLogic:{allowCodeExecution:He,allowFileResources:!1,resources:W(s.resources,!0),callback:s.callback,customCode:s.customCode}};c&&(d.export.instr=B(c,d.customLogic.allowCodeExecution));const g=Z(n,d);if(g.export.options=c,g.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))))(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 $e(g,((o,c)=>{if(e.socket.removeAllListeners("close"),n.server.benchmarking&&U(5,`[benchmark] Request with ID ${i} - After the whole exporting process: ${r()}ms.`),h)return U(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,Ye(Je,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",Xe[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 Ze=JSON.parse(e.readFileSync(t.join(F,"package.json"))),et=new Date,tt=[];function rt(e){if(!e)return!1;var t;t=setInterval((()=>{const e=Pe(),t=0===e.exportAttempts?1:e.performedExports/e.exportAttempts*100;tt.push(t),tt.length>30&&tt.shift()}),6e4),Fe.push(t),e.get("/health",((e,t)=>{const r=Pe(),o=tt.length,i=tt.reduce(((e,t)=>e+t),0)/tt.length;U(4,"[health.js] GET /health [200] - returning server health."),t.send({status:"OK",bootTime:et,uptime:Math.floor(((new Date).getTime()-et.getTime())/1e3/60)+" minutes",version:Ze.version,highchartsVersion:de(),averageProcessingTime:r.spentAverage,performedExports:r.performedExports,failedExports:r.droppedExports,exportAttempts:r.exportAttempts,sucessRatio:r.performedExports/r.exportAttempts*100,pool:Ne(),period:o,movingAverage:i,message:`Last ${o} minutes had a success rate of ${i.toFixed(2)}%.`,svgExportAttempts:r.exportFromSvgAttempts,jsonExportAttempts:r.performedExports-r.exportFromSvgAttempts})}))}const ot=new Map,it=m();it.disable("x-powered-by"),it.use(g());const nt=f.memoryStorage(),st=f({storage:nt,limits:{fieldSize:52428800}});it.use(m.json({limit:52428800})),it.use(m.urlencoded({extended:!0,limit:52428800})),it.use(st.none());const at=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}`)}))}))},lt=async r=>{try{if(!r.enable)return!1;if(!r.ssl.force){const e=a.createServer(it);at(e),e.listen(r.port,r.host),ot.set(r.port,e),U(3,`[server] Started HTTP server on ${r.host}:${r.port}.`)}if(r.ssl.enable){let o,i;try{o=await e.promises.readFile(t.posix.join(r.ssl.certPath,"server.key"),"utf8"),i=await e.promises.readFile(t.posix.join(r.ssl.certPath,"server.crt"),"utf8")}catch(e){U(2,`[server] Unable to load key/certificate from the '${r.ssl.certPath}' path. Could not run secured layer server.`)}if(o&&i){const e=l.createServer({key:o,cert:i},it);at(e),e.listen(r.ssl.port,r.host),ot.set(r.ssl.port,e),U(3,`[server] Started HTTPS server on ${r.host}:${r.ssl.port}.`)}}r.rateLimiting&&r.rateLimiting.enable&&![0,NaN].includes(r.rateLimiting.maxRequests)&&Ve(it,r.rateLimiting),it.use(m.static(t.posix.join(F,"public"))),rt(it),(e=>{e.post("/",Qe),e.post("/:filename",Qe)})(it),(e=>{!!e&&e.get("/",((e,r)=>{r.sendFile(t.join(F,"public","index.html"))}))})(it),(e=>{!!e&&e.post("/version/change/:newVersion",(async(e,t,r)=>{try{const r=N.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 ue(i)}catch(e){throw new Be(`Version change: ${e.message}`,e.statusCode).setError(e)}t.status(200).send({statusCode:200,version:de(),message:`Successfully updated Highcharts to version: ${i}.`})}catch(e){r(e)}}))})(it),(e=>{e.use(qe),e.use(We)})(it)}catch(e){throw new ie("[server] Could not configure and start the server.").setError(e)}},ct=()=>{U(4,"[server] Closing all servers.");for(const[e,t]of ot)t.close((()=>{ot.delete(e),U(4,`[server] Closed server on port: ${e}.`)}))};var pt={startServer:lt,closeServers:ct,getServers:()=>ot,enableRateLimiting:e=>Ve(it,e),getExpress:()=>m,getApp:()=>it,use:(e,...t)=>{it.use(e,...t)},get:(e,...t)=>{it.get(e,...t)},post:(e,...t)=>{it.post(e,...t)}};const ut=async e=>{await Promise.allSettled([Me(),ct(),ke()]),process.exit(e)};var ht={server:pt,startServer:lt,initExport:async e=>{var t;return t=e.customLogic&&e.customLogic.allowCodeExecution,He=z(t),(e=>{D(e&&parseInt(e.level)),e&&e.dest&&G(e.dest,e.file||"highcharts-export-server.log")})(e.logging),e.other.listenToProcessExits&&(U(3,"[process] Attaching exit listeners to the process."),process.on("exit",(e=>{U(4,`Process exited with code ${e}.`)})),process.on("SIGINT",(async(e,t)=>{U(4,`The ${e} event with code: ${t}.`),await ut(0)})),process.on("SIGTERM",(async(e,t)=>{U(4,`The ${e} event with code: ${t}.`),await ut(0)})),process.on("SIGHUP",(async(e,t)=>{U(4,`The ${e} event with code: ${t}.`),await ut(0)})),process.on("uncaughtException",(async(e,t)=>{j(1,e,`The ${t} error.`),await ut(1)}))),await ce(e),await _e({pool:e.pool||{minWorkers:1,maxWorkers:1},puppeteerArgs:e.puppeteer.args||[]}),e},singleExport:async t=>{t.export.instr=t.export.instr||t.export.options,await $e(t,(async(t,r)=>{if(t)throw t;const{outfile:o,type:i}=r.options.export;e.writeFileSync(o||`chart.${i}`,"svg"!==i?Buffer.from(r.result,"base64"):r.result),await ke()}))},batchExport:async t=>{const r=[];for(let o of t.export.batch.split(";"))o=o.split("="),2===o.length&&r.push($e({...t,export:{...t.export,infile:o[0],outfile:o[1]}},((t,r)=>{if(t)throw t;e.writeFileSync(r.options.export.outfile,"svg"!==r.options.export.type?Buffer.from(r.result,"base64"):r.result)})));try{await Promise.all(r),await ke()}catch(e){throw new ie("[chart] Error encountered during batch export.").setError(e)}},startExport:$e,initPool:_e,killPool:ke,setOptions:(t,r)=>(r?.length&&(Y=function(t){const r=t.findIndex((e=>"loadConfig"===e.replace(/-/g,"")));if(r>-1&&t[r+1]){const o=t[r+1];try{if(o&&o.endsWith(".json"))return JSON.parse(e.readFileSync(o))}catch(e){j(2,e,`[config] Unable to load the configuration from the ${o} file.`)}}return{}}(r)),ee(T,Y),Y=te(T),t&&(Y=Z(Y,t,x)),r?.length&&(Y=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]=z(t[i]):"number"===a?e[r]=+t[i]:a.indexOf("]")>=0?e[r]=t[i].split(","):e[r]=t[i]:(U(2,`[config] Missing value for the '${n}' argument. Using the default value.`),o=!0)),e[r])),e)}o&&X();return e}(Y,r,T)),Y),shutdownCleanUp:ut,log:U,logWithStack:j,setLogLevel:D,enableFileLogging:G,mapToNewConfig:e=>{const t={};for(const[r,o]of Object.entries(e)){const e=R[r]?R[r].split("."):[];e.reduce(((t,r,i)=>t[r]=e.length-1===i?o:t[r]||{}),t)}return t},manualConfig:async t=>{let r={};e.existsSync(t)&&(r=JSON.parse(e.readFileSync(t,"utf8")));const i=Object.keys(S).map((e=>({title:`${e} options`,value:e})));return o({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,n)=>{let s=0,a=[];for(const e of n)S[e]=S[e].map((t=>({...t,section:e}))),a=[...a,...S[e]];return await o(a,{onSubmit:async(o,i)=>{if("moduleScripts"===o.name?(i=i.length?i.map((e=>o.choices[e])):o.choices,r[o.section][o.name]=i):r[o.section]=re(Object.assign({},r[o.section]||{}),o.name.split("."),o.choices?o.choices[i]:i),++s===a.length){try{await e.promises.writeFile(t,JSON.stringify(r,null,2),"utf8")}catch(e){j(1,e,`[config] An error occurred while creating the ${t} file.`)}return!0}}}),!0}})},printLogo:r=>{const o=JSON.parse(e.readFileSync(t.join(F,"package.json"))).version;r?console.log(`Starting Highcharts Export Server v${o}...`):console.log(e.readFileSync(F+"/msg/startup.msg").toString().bold.yellow,`v${o}\n`.bold)},printUsage:X};module.exports=ht; -//# sourceMappingURL=data:application/json;charset=utf-8;base64,eyJ2ZXJzaW9uIjozLCJmaWxlIjoiaW5kZXguY2pzIiwic291cmNlcyI6WyIuLi9saWIvc2NoZW1hcy9jb25maWcuanMiLCIuLi9saWIvZW52cy5qcyIsIi4uL2xpYi9sb2dnZXIuanMiLCIuLi9saWIvdXRpbHMuanMiLCIuLi9saWIvY29uZmlnLmpzIiwiLi4vbGliL2ZldGNoLmpzIiwiLi4vbGliL2Vycm9ycy9FeHBvcnRFcnJvci5qcyIsIi4uL2xpYi9jYWNoZS5qcyIsIi4uL2xpYi9oaWdoY2hhcnRzLmpzIiwiLi4vbGliL2Jyb3dzZXIuanMiLCIuLi9saWIvZXhwb3J0LmpzIiwiLi4vdGVtcGxhdGVzL3N2Z19leHBvcnQvc3ZnX2V4cG9ydC5qcyIsIi4uL2xpYi9wb29sLmpzIiwiLi4vbGliL2NoYXJ0LmpzIiwiLi4vbGliL3Nhbml0aXplLmpzIiwiLi4vbGliL2ludGVydmFscy5qcyIsIi4uL2xpYi9zZXJ2ZXIvZXJyb3IuanMiLCIuLi9saWIvc2VydmVyL3JhdGVfbGltaXQuanMiLCIuLi9saWIvZXJyb3JzL0h0dHBFcnJvci5qcyIsIi4uL2xpYi9zZXJ2ZXIvcm91dGVzL2V4cG9ydC5qcyIsIi4uL2xpYi9zZXJ2ZXIvcm91dGVzL2hlYWx0aC5qcyIsIi4uL2xpYi9zZXJ2ZXIvc2VydmVyLmpzIiwiLi4vbGliL3NlcnZlci9yb3V0ZXMvdWkuanMiLCIuLi9saWIvc2VydmVyL3JvdXRlcy9jaGFuZ2VfaGNfdmVyc2lvbi5qcyIsIi4uL2xpYi9yZXNvdXJjZV9yZWxlYXNlLmpzIiwiLi4vbGliL2luZGV4LmpzIl0sInNvdXJjZXNDb250ZW50IjpbIi8qKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqXHJcblxyXG5IaWdoY2hhcnRzIEV4cG9ydCBTZXJ2ZXJcclxuXHJcbkNvcHlyaWdodCAoYykgMjAxNi0yMDI0LCBIaWdoc29mdFxyXG5cclxuTGljZW5jZWQgdW5kZXIgdGhlIE1JVCBsaWNlbmNlLlxyXG5cclxuQWRkaXRpb25hbGx5IGEgdmFsaWQgSGlnaGNoYXJ0cyBsaWNlbnNlIGlzIHJlcXVpcmVkIGZvciB1c2UuXHJcblxyXG5TZWUgTElDRU5TRSBmaWxlIGluIHJvb3QgZm9yIGRldGFpbHMuXHJcblxyXG4qKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqL1xyXG5cclxuLy8gUG9zc2libGUgbmFtZXMgZm9yIEhpZ2hjaGFydHMgc2NyaXB0c1xyXG5leHBvcnQgY29uc3Qgc2NyaXB0c05hbWVzID0ge1xyXG4gIGNvcmU6IFsnaGlnaGNoYXJ0cycsICdoaWdoY2hhcnRzLW1vcmUnLCAnaGlnaGNoYXJ0cy0zZCddLFxyXG4gIG1vZHVsZXM6IFtcclxuICAgICdzdG9jaycsXHJcbiAgICAnbWFwJyxcclxuICAgICdnYW50dCcsXHJcbiAgICAnZXhwb3J0aW5nJyxcclxuICAgICdleHBvcnQtZGF0YScsXHJcbiAgICAncGFyYWxsZWwtY29vcmRpbmF0ZXMnLFxyXG4gICAgJ2FjY2Vzc2liaWxpdHknLFxyXG4gICAgLy8gJ2Fubm90YXRpb25zLWFkdmFuY2VkJyxcclxuICAgICdib29zdC1jYW52YXMnLFxyXG4gICAgJ2Jvb3N0JyxcclxuICAgICdkYXRhJyxcclxuICAgICdkYXRhLXRvb2xzJyxcclxuICAgICdkcmFnZ2FibGUtcG9pbnRzJyxcclxuICAgICdzdGF0aWMtc2NhbGUnLFxyXG4gICAgJ2Jyb2tlbi1heGlzJyxcclxuICAgICdoZWF0bWFwJyxcclxuICAgICd0aWxlbWFwJyxcclxuICAgICd0aWxlZHdlYm1hcCcsXHJcbiAgICAndGltZWxpbmUnLFxyXG4gICAgJ3RyZWVtYXAnLFxyXG4gICAgJ3RyZWVncmFwaCcsXHJcbiAgICAnaXRlbS1zZXJpZXMnLFxyXG4gICAgJ2RyaWxsZG93bicsXHJcbiAgICAnaGlzdG9ncmFtLWJlbGxjdXJ2ZScsXHJcbiAgICAnYnVsbGV0JyxcclxuICAgICdmdW5uZWwnLFxyXG4gICAgJ2Z1bm5lbDNkJyxcclxuICAgICdnZW9oZWF0bWFwJyxcclxuICAgICdweXJhbWlkM2QnLFxyXG4gICAgJ25ldHdvcmtncmFwaCcsXHJcbiAgICAnb3ZlcmxhcHBpbmctZGF0YWxhYmVscycsXHJcbiAgICAncGFyZXRvJyxcclxuICAgICdwYXR0ZXJuLWZpbGwnLFxyXG4gICAgJ3BpY3RvcmlhbCcsXHJcbiAgICAncHJpY2UtaW5kaWNhdG9yJyxcclxuICAgICdzYW5rZXknLFxyXG4gICAgJ2FyYy1kaWFncmFtJyxcclxuICAgICdkZXBlbmRlbmN5LXdoZWVsJyxcclxuICAgICdzZXJpZXMtbGFiZWwnLFxyXG4gICAgJ3NvbGlkLWdhdWdlJyxcclxuICAgICdzb25pZmljYXRpb24nLFxyXG4gICAgLy8gJ3N0b2NrLXRvb2xzJyxcclxuICAgICdzdHJlYW1ncmFwaCcsXHJcbiAgICAnc3VuYnVyc3QnLFxyXG4gICAgJ3ZhcmlhYmxlLXBpZScsXHJcbiAgICAndmFyaXdpZGUnLFxyXG4gICAgJ3ZlY3RvcicsXHJcbiAgICAndmVubicsXHJcbiAgICAnd2luZGJhcmInLFxyXG4gICAgJ3dvcmRjbG91ZCcsXHJcbiAgICAneHJhbmdlJyxcclxuICAgICduby1kYXRhLXRvLWRpc3BsYXknLFxyXG4gICAgJ2RyYWctcGFuZXMnLFxyXG4gICAgJ2RlYnVnZ2VyJyxcclxuICAgICdkdW1iYmVsbCcsXHJcbiAgICAnbG9sbGlwb3AnLFxyXG4gICAgJ2N5bGluZGVyJyxcclxuICAgICdvcmdhbml6YXRpb24nLFxyXG4gICAgJ2RvdHBsb3QnLFxyXG4gICAgJ21hcmtlci1jbHVzdGVycycsXHJcbiAgICAnaG9sbG93Y2FuZGxlc3RpY2snLFxyXG4gICAgJ2hlaWtpbmFzaGknLFxyXG4gICAgJ2Zsb3dtYXAnXHJcbiAgXSxcclxuICBpbmRpY2F0b3JzOiBbJ2luZGljYXRvcnMtYWxsJ11cclxufTtcclxuXHJcbi8vIFRoaXMgaXMgdGhlIGNvbmZpZ3VyYXRpb24gb2JqZWN0IHdpdGggYWxsIG9wdGlvbnMgYW5kIHRoZWlyIGRlZmF1bHQgdmFsdWVzLFxyXG4vLyBhbHNvIGZyb20gdGhlIC5lbnYgZmlsZSBpZiBvbmUgZXhpc3RzXHJcbmV4cG9ydCBjb25zdCBkZWZhdWx0Q29uZmlnID0ge1xyXG4gIHB1cHBldGVlcjoge1xyXG4gICAgYXJnczoge1xyXG4gICAgICB2YWx1ZTogW1xyXG4gICAgICAgICctLWFsbG93LXJ1bm5pbmctaW5zZWN1cmUtY29udGVudCcsXHJcbiAgICAgICAgJy0tYXNoLW5vLW51ZGdlcycsXHJcbiAgICAgICAgJy0tYXV0b3BsYXktcG9saWN5PXVzZXItZ2VzdHVyZS1yZXF1aXJlZCcsXHJcbiAgICAgICAgJy0tYmxvY2stbmV3LXdlYi1jb250ZW50cycsXHJcbiAgICAgICAgJy0tZGlzYWJsZS1hY2NlbGVyYXRlZC0yZC1jYW52YXMnLFxyXG4gICAgICAgICctLWRpc2FibGUtYmFja2dyb3VuZC1uZXR3b3JraW5nJyxcclxuICAgICAgICAnLS1kaXNhYmxlLWJhY2tncm91bmQtdGltZXItdGhyb3R0bGluZycsXHJcbiAgICAgICAgJy0tZGlzYWJsZS1iYWNrZ3JvdW5kaW5nLW9jY2x1ZGVkLXdpbmRvd3MnLFxyXG4gICAgICAgICctLWRpc2FibGUtYnJlYWtwYWQnLFxyXG4gICAgICAgICctLWRpc2FibGUtY2hlY2tlci1pbWFnaW5nJyxcclxuICAgICAgICAnLS1kaXNhYmxlLWNsaWVudC1zaWRlLXBoaXNoaW5nLWRldGVjdGlvbicsXHJcbiAgICAgICAgJy0tZGlzYWJsZS1jb21wb25lbnQtZXh0ZW5zaW9ucy13aXRoLWJhY2tncm91bmQtcGFnZXMnLFxyXG4gICAgICAgICctLWRpc2FibGUtY29tcG9uZW50LXVwZGF0ZScsXHJcbiAgICAgICAgJy0tZGlzYWJsZS1kZWZhdWx0LWFwcHMnLFxyXG4gICAgICAgICctLWRpc2FibGUtZGV2LXNobS11c2FnZScsXHJcbiAgICAgICAgJy0tZGlzYWJsZS1kb21haW4tcmVsaWFiaWxpdHknLFxyXG4gICAgICAgICctLWRpc2FibGUtZXh0ZW5zaW9ucycsXHJcbiAgICAgICAgJy0tZGlzYWJsZS1mZWF0dXJlcz1DYWxjdWxhdGVOYXRpdmVXaW5PY2NsdXNpb24sSW50ZXJlc3RGZWVkQ29udGVudFN1Z2dlc3Rpb25zLFdlYk9UUCcsXHJcbiAgICAgICAgJy0tZGlzYWJsZS1oYW5nLW1vbml0b3InLFxyXG4gICAgICAgICctLWRpc2FibGUtaXBjLWZsb29kaW5nLXByb3RlY3Rpb24nLFxyXG4gICAgICAgICctLWRpc2FibGUtbG9nZ2luZycsXHJcbiAgICAgICAgJy0tZGlzYWJsZS1ub3RpZmljYXRpb25zJyxcclxuICAgICAgICAnLS1kaXNhYmxlLW9mZmVyLXN0b3JlLXVubWFza2VkLXdhbGxldC1jYXJkcycsXHJcbiAgICAgICAgJy0tZGlzYWJsZS1wb3B1cC1ibG9ja2luZycsXHJcbiAgICAgICAgJy0tZGlzYWJsZS1wcmludC1wcmV2aWV3JyxcclxuICAgICAgICAnLS1kaXNhYmxlLXByb21wdC1vbi1yZXBvc3QnLFxyXG4gICAgICAgICctLWRpc2FibGUtcmVuZGVyZXItYmFja2dyb3VuZGluZycsXHJcbiAgICAgICAgJy0tZGlzYWJsZS1zZWFyY2gtZW5naW5lLWNob2ljZS1zY3JlZW4nLFxyXG4gICAgICAgICctLWRpc2FibGUtc2Vzc2lvbi1jcmFzaGVkLWJ1YmJsZScsXHJcbiAgICAgICAgJy0tZGlzYWJsZS1zZXR1aWQtc2FuZGJveCcsXHJcbiAgICAgICAgJy0tZGlzYWJsZS1zaXRlLWlzb2xhdGlvbi10cmlhbHMnLFxyXG4gICAgICAgICctLWRpc2FibGUtc3BlZWNoLWFwaScsXHJcbiAgICAgICAgJy0tZGlzYWJsZS1zeW5jJyxcclxuICAgICAgICAnLS1lbmFibGUtdW5zYWZlLXdlYmdwdScsXHJcbiAgICAgICAgJy0taGlkZS1jcmFzaC1yZXN0b3JlLWJ1YmJsZScsXHJcbiAgICAgICAgJy0taGlkZS1zY3JvbGxiYXJzJyxcclxuICAgICAgICAnLS1tZXRyaWNzLXJlY29yZGluZy1vbmx5JyxcclxuICAgICAgICAnLS1tdXRlLWF1ZGlvJyxcclxuICAgICAgICAnLS1uby1kZWZhdWx0LWJyb3dzZXItY2hlY2snLFxyXG4gICAgICAgICctLW5vLWZpcnN0LXJ1bicsXHJcbiAgICAgICAgJy0tbm8tcGluZ3MnLFxyXG4gICAgICAgICctLW5vLXNhbmRib3gnLFxyXG4gICAgICAgICctLW5vLXN0YXJ0dXAtd2luZG93JyxcclxuICAgICAgICAnLS1uby16eWdvdGUnLFxyXG4gICAgICAgICctLXBhc3N3b3JkLXN0b3JlPWJhc2ljJyxcclxuICAgICAgICAnLS1wcm9jZXNzLXBlci10YWInLFxyXG4gICAgICAgICctLXVzZS1tb2NrLWtleWNoYWluJ1xyXG4gICAgICBdLFxyXG4gICAgICB0eXBlOiAnc3RyaW5nW10nLFxyXG4gICAgICBkZXNjcmlwdGlvbjogJ0FyZ3VtZW50cyBhcnJheSB0byBzZW5kIHRvIFB1cHBldGVlci4nXHJcbiAgICB9XHJcbiAgfSxcclxuICBoaWdoY2hhcnRzOiB7XHJcbiAgICB2ZXJzaW9uOiB7XHJcbiAgICAgIHZhbHVlOiAnbGF0ZXN0JyxcclxuICAgICAgdHlwZTogJ3N0cmluZycsXHJcbiAgICAgIGVudkxpbms6ICdISUdIQ0hBUlRTX1ZFUlNJT04nLFxyXG4gICAgICBkZXNjcmlwdGlvbjogJ1RoZSBIaWdoY2hhcnRzIHZlcnNpb24gdG8gYmUgdXNlZC4nXHJcbiAgICB9LFxyXG4gICAgY2RuVVJMOiB7XHJcbiAgICAgIHZhbHVlOiAnaHR0cHM6Ly9jb2RlLmhpZ2hjaGFydHMuY29tLycsXHJcbiAgICAgIHR5cGU6ICdzdHJpbmcnLFxyXG4gICAgICBlbnZMaW5rOiAnSElHSENIQVJUU19DRE5fVVJMJyxcclxuICAgICAgZGVzY3JpcHRpb246ICdUaGUgQ0ROIFVSTCBmb3IgSGlnaGNoYXJ0cyBzY3JpcHRzIHRvIGJlIHVzZWQuJ1xyXG4gICAgfSxcclxuICAgIGNvcmVTY3JpcHRzOiB7XHJcbiAgICAgIHZhbHVlOiBzY3JpcHRzTmFtZXMuY29yZSxcclxuICAgICAgdHlwZTogJ3N0cmluZ1tdJyxcclxuICAgICAgZW52TGluazogJ0hJR0hDSEFSVFNfQ09SRV9TQ1JJUFRTJyxcclxuICAgICAgZGVzY3JpcHRpb246ICdUaGUgY29yZSBIaWdoY2hhcnRzIHNjcmlwdHMgdG8gZmV0Y2guJ1xyXG4gICAgfSxcclxuICAgIG1vZHVsZVNjcmlwdHM6IHtcclxuICAgICAgdmFsdWU6IHNjcmlwdHNOYW1lcy5tb2R1bGVzLFxyXG4gICAgICB0eXBlOiAnc3RyaW5nW10nLFxyXG4gICAgICBlbnZMaW5rOiAnSElHSENIQVJUU19NT0RVTEVfU0NSSVBUUycsXHJcbiAgICAgIGRlc2NyaXB0aW9uOiAnVGhlIG1vZHVsZXMgb2YgSGlnaGNoYXJ0cyB0byBmZXRjaC4nXHJcbiAgICB9LFxyXG4gICAgaW5kaWNhdG9yU2NyaXB0czoge1xyXG4gICAgICB2YWx1ZTogc2NyaXB0c05hbWVzLmluZGljYXRvcnMsXHJcbiAgICAgIHR5cGU6ICdzdHJpbmdbXScsXHJcbiAgICAgIGVudkxpbms6ICdISUdIQ0hBUlRTX0lORElDQVRPUl9TQ1JJUFRTJyxcclxuICAgICAgZGVzY3JpcHRpb246ICdUaGUgaW5kaWNhdG9ycyBvZiBIaWdoY2hhcnRzIHRvIGZldGNoLidcclxuICAgIH0sXHJcbiAgICBjdXN0b21TY3JpcHRzOiB7XHJcbiAgICAgIHZhbHVlOiBbXHJcbiAgICAgICAgJ2h0dHBzOi8vY2RuanMuY2xvdWRmbGFyZS5jb20vYWpheC9saWJzL21vbWVudC5qcy8yLjI5LjQvbW9tZW50Lm1pbi5qcycsXHJcbiAgICAgICAgJ2h0dHBzOi8vY2RuanMuY2xvdWRmbGFyZS5jb20vYWpheC9saWJzL21vbWVudC10aW1lem9uZS8wLjUuMzQvbW9tZW50LXRpbWV6b25lLXdpdGgtZGF0YS5taW4uanMnXHJcbiAgICAgIF0sXHJcbiAgICAgIHR5cGU6ICdzdHJpbmdbXScsXHJcbiAgICAgIGRlc2NyaXB0aW9uOiAnQWRkaXRpb25hbCBjdXN0b20gc2NyaXB0cyBvciBkZXBlbmRlbmNpZXMgdG8gZmV0Y2guJ1xyXG4gICAgfSxcclxuICAgIGZvcmNlRmV0Y2g6IHtcclxuICAgICAgdmFsdWU6IGZhbHNlLFxyXG4gICAgICB0eXBlOiAnYm9vbGVhbicsXHJcbiAgICAgIGVudkxpbms6ICdISUdIQ0hBUlRTX0ZPUkNFX0ZFVENIJyxcclxuICAgICAgZGVzY3JpcHRpb246XHJcbiAgICAgICAgJ1RoZSBmbGFnIHRvIGRldGVybWluZSB3aGV0aGVyIHRvIHJlZmV0Y2ggYWxsIHNjcmlwdHMgYWZ0ZXIgZWFjaCBzZXJ2ZXIgcmVydW4uJ1xyXG4gICAgfSxcclxuICAgIGNhY2hlUGF0aDoge1xyXG4gICAgICB2YWx1ZTogJy5jYWNoZScsXHJcbiAgICAgIHR5cGU6ICdzdHJpbmcnLFxyXG4gICAgICBlbnZMaW5rOiAnSElHSENIQVJUU19DQUNIRV9QQVRIJyxcclxuICAgICAgZGVzY3JpcHRpb246XHJcbiAgICAgICAgJ1RoZSBwYXRoIHRvIHRoZSBjYWNoZSBkaXJlY3RvcnkuIEl0IGlzIHVzZWQgdG8gc3RvcmUgdGhlIEhpZ2hjaGFydHMgc2NyaXB0cyBhbmQgY3VzdG9tIHNjcmlwdHMuJ1xyXG4gICAgfVxyXG4gIH0sXHJcbiAgZXhwb3J0OiB7XHJcbiAgICBpbmZpbGU6IHtcclxuICAgICAgdmFsdWU6IGZhbHNlLFxyXG4gICAgICB0eXBlOiAnc3RyaW5nJyxcclxuICAgICAgZGVzY3JpcHRpb246XHJcbiAgICAgICAgJ1RoZSBpbnB1dCBmaWxlIHNob3VsZCBpbmNsdWRlIGEgbmFtZSBhbmQgYSB0eXBlIChqc29uIG9yIHN2ZykuIEl0IG11c3QgYmUgY29ycmVjdGx5IGZvcm1hdHRlZCBhcyBhIEpTT04gb3IgU1ZHIGZpbGUuJ1xyXG4gICAgfSxcclxuICAgIGluc3RyOiB7XHJcbiAgICAgIHZhbHVlOiBmYWxzZSxcclxuICAgICAgdHlwZTogJ3N0cmluZycsXHJcbiAgICAgIGRlc2NyaXB0aW9uOlxyXG4gICAgICAgICdJbnB1dCwgcHJvdmlkZWQgaW4gdGhlIGZvcm0gb2YgYSBzdHJpbmdpZmllZCBKU09OIG9yIFNWRyBmaWxlLCB3aWxsIG92ZXJyaWRlIHRoZSAtLWluZmlsZSBvcHRpb24uJ1xyXG4gICAgfSxcclxuICAgIG9wdGlvbnM6IHtcclxuICAgICAgdmFsdWU6IGZhbHNlLFxyXG4gICAgICB0eXBlOiAnc3RyaW5nJyxcclxuICAgICAgZGVzY3JpcHRpb246ICdBbiBhbGlhcyBmb3IgdGhlIC0taW5zdHIgb3B0aW9uLidcclxuICAgIH0sXHJcbiAgICBvdXRmaWxlOiB7XHJcbiAgICAgIHZhbHVlOiBmYWxzZSxcclxuICAgICAgdHlwZTogJ3N0cmluZycsXHJcbiAgICAgIGRlc2NyaXB0aW9uOlxyXG4gICAgICAgICdUaGUgb3V0cHV0IGZpbGVuYW1lIGFsb25nIHdpdGggYSB0eXBlIChqcGVnLCBwbmcsIHBkZiwgb3Igc3ZnKS4gVGhpcyB3aWxsIGlnbm9yZSB0aGUgLS10eXBlIGZsYWcuJ1xyXG4gICAgfSxcclxuICAgIHR5cGU6IHtcclxuICAgICAgdmFsdWU6ICdwbmcnLFxyXG4gICAgICB0eXBlOiAnc3RyaW5nJyxcclxuICAgICAgZW52TGluazogJ0VYUE9SVF9UWVBFJyxcclxuICAgICAgZGVzY3JpcHRpb246ICdUaGUgZmlsZSBleHBvcnQgZm9ybWF0LiBJdCBjYW4gYmUganBlZywgcG5nLCBwZGYsIG9yIHN2Zy4nXHJcbiAgICB9LFxyXG4gICAgY29uc3RyOiB7XHJcbiAgICAgIHZhbHVlOiAnY2hhcnQnLFxyXG4gICAgICB0eXBlOiAnc3RyaW5nJyxcclxuICAgICAgZW52TGluazogJ0VYUE9SVF9DT05TVFInLFxyXG4gICAgICBkZXNjcmlwdGlvbjpcclxuICAgICAgICAnVGhlIGNvbnN0cnVjdG9yIHRvIHVzZS4gQ2FuIGJlIGNoYXJ0LCBzdG9ja0NoYXJ0LCBtYXBDaGFydCwgb3IgZ2FudHRDaGFydC4nXHJcbiAgICB9LFxyXG4gICAgZGVmYXVsdEhlaWdodDoge1xyXG4gICAgICB2YWx1ZTogNDAwLFxyXG4gICAgICB0eXBlOiAnbnVtYmVyJyxcclxuICAgICAgZW52TGluazogJ0VYUE9SVF9ERUZBVUxUX0hFSUdIVCcsXHJcbiAgICAgIGRlc2NyaXB0aW9uOlxyXG4gICAgICAgICd0aGUgZGVmYXVsdCBoZWlnaHQgb2YgdGhlIGV4cG9ydGVkIGNoYXJ0LiBVc2VkIHdoZW4gbm8gdmFsdWUgaXMgc2V0LidcclxuICAgIH0sXHJcbiAgICBkZWZhdWx0V2lkdGg6IHtcclxuICAgICAgdmFsdWU6IDYwMCxcclxuICAgICAgdHlwZTogJ251bWJlcicsXHJcbiAgICAgIGVudkxpbms6ICdFWFBPUlRfREVGQVVMVF9XSURUSCcsXHJcbiAgICAgIGRlc2NyaXB0aW9uOlxyXG4gICAgICAgICdUaGUgZGVmYXVsdCB3aWR0aCBvZiB0aGUgZXhwb3J0ZWQgY2hhcnQuIFVzZWQgd2hlbiBubyB2YWx1ZSBpcyBzZXQuJ1xyXG4gICAgfSxcclxuICAgIGRlZmF1bHRTY2FsZToge1xyXG4gICAgICB2YWx1ZTogMSxcclxuICAgICAgdHlwZTogJ251bWJlcicsXHJcbiAgICAgIGVudkxpbms6ICdFWFBPUlRfREVGQVVMVF9TQ0FMRScsXHJcbiAgICAgIGRlc2NyaXB0aW9uOlxyXG4gICAgICAgICdUaGUgZGVmYXVsdCBzY2FsZSBvZiB0aGUgZXhwb3J0ZWQgY2hhcnQuIFVzZWQgd2hlbiBubyB2YWx1ZSBpcyBzZXQuJ1xyXG4gICAgfSxcclxuICAgIGhlaWdodDoge1xyXG4gICAgICB2YWx1ZTogZmFsc2UsXHJcbiAgICAgIHR5cGU6ICdudW1iZXInLFxyXG4gICAgICBkZXNjcmlwdGlvbjpcclxuICAgICAgICAnVGhlIGhlaWdodCBvZiB0aGUgZXhwb3J0ZWQgY2hhcnQsIG92ZXJyaWRpbmcgdGhlIG9wdGlvbiBpbiB0aGUgY2hhcnQgc2V0dGluZ3MuJ1xyXG4gICAgfSxcclxuICAgIHdpZHRoOiB7XHJcbiAgICAgIHZhbHVlOiBmYWxzZSxcclxuICAgICAgdHlwZTogJ251bWJlcicsXHJcbiAgICAgIGRlc2NyaXB0aW9uOlxyXG4gICAgICAgICdUaGUgd2lkdGggb2YgdGhlIGV4cG9ydGVkIGNoYXJ0LCBvdmVycmlkaW5nIHRoZSBvcHRpb24gaW4gdGhlIGNoYXJ0IHNldHRpbmdzLidcclxuICAgIH0sXHJcbiAgICBzY2FsZToge1xyXG4gICAgICB2YWx1ZTogZmFsc2UsXHJcbiAgICAgIHR5cGU6ICdudW1iZXInLFxyXG4gICAgICBkZXNjcmlwdGlvbjpcclxuICAgICAgICAnVGhlIHNjYWxlIG9mIHRoZSBleHBvcnRlZCBjaGFydCwgb3ZlcnJpZGluZyB0aGUgb3B0aW9uIGluIHRoZSBjaGFydCBzZXR0aW5ncy4gUmFuZ2VzIGJldHdlZW4gMC4xIGFuZCA1LjAuJ1xyXG4gICAgfSxcclxuICAgIGdsb2JhbE9wdGlvbnM6IHtcclxuICAgICAgdmFsdWU6IGZhbHNlLFxyXG4gICAgICB0eXBlOiAnc3RyaW5nJyxcclxuICAgICAgZGVzY3JpcHRpb246XHJcbiAgICAgICAgJ0VpdGhlciBhIHN0cmluZ2lmaWVkIEpTT04gb3IgYSBmaWxlbmFtZSBjb250YWluaW5nIG9wdGlvbnMgdG8gYmUgcGFzc2VkIGludG8gdGhlIEhpZ2hjaGFydHMuc2V0T3B0aW9ucy4nXHJcbiAgICB9LFxyXG4gICAgdGhlbWVPcHRpb25zOiB7XHJcbiAgICAgIHZhbHVlOiBmYWxzZSxcclxuICAgICAgdHlwZTogJ3N0cmluZycsXHJcbiAgICAgIGRlc2NyaXB0aW9uOlxyXG4gICAgICAgICdFaXRoZXIgYSBzdHJpbmdpZmllZCBKU09OIG9yIGEgZmlsZW5hbWUgY29udGFpbmluZyB0aGVtZSBvcHRpb25zIHRvIGJlIHBhc3NlZCBpbnRvIHRoZSBIaWdoY2hhcnRzLnNldE9wdGlvbnMuJ1xyXG4gICAgfSxcclxuICAgIGJhdGNoOiB7XHJcbiAgICAgIHZhbHVlOiBmYWxzZSxcclxuICAgICAgdHlwZTogJ3N0cmluZycsXHJcbiAgICAgIGRlc2NyaXB0aW9uOlxyXG4gICAgICAgICdJbml0aWF0ZXMgYSBiYXRjaCBqb2Igd2l0aCBhIHN0cmluZyBjb250YWluaW5nIGlucHV0L291dHB1dCBwYWlyczogXCJpbj1vdXQ7aW49b3V0Oy4uLlwiLidcclxuICAgIH0sXHJcbiAgICByYXN0ZXJpemF0aW9uVGltZW91dDoge1xyXG4gICAgICB2YWx1ZTogMTUwMCxcclxuICAgICAgdHlwZTogJ251bWJlcicsXHJcbiAgICAgIGVudkxpbms6ICdFWFBPUlRfUkFTVEVSSVpBVElPTl9USU1FT1VUJyxcclxuICAgICAgZGVzY3JpcHRpb246XHJcbiAgICAgICAgJ1RoZSBkdXJhdGlvbiBpbiBtaWxsaXNlY29uZHMgdG8gd2FpdCBmb3IgcmVuZGVyaW5nIGEgd2VicGFnZS4nXHJcbiAgICB9XHJcbiAgfSxcclxuICBjdXN0b21Mb2dpYzoge1xyXG4gICAgYWxsb3dDb2RlRXhlY3V0aW9uOiB7XHJcbiAgICAgIHZhbHVlOiBmYWxzZSxcclxuICAgICAgdHlwZTogJ2Jvb2xlYW4nLFxyXG4gICAgICBlbnZMaW5rOiAnQ1VTVE9NX0xPR0lDX0FMTE9XX0NPREVfRVhFQ1VUSU9OJyxcclxuICAgICAgZGVzY3JpcHRpb246XHJcbiAgICAgICAgJ0NvbnRyb2xzIHdoZXRoZXIgdGhlIGV4ZWN1dGlvbiBvZiBhcmJpdHJhcnkgY29kZSBpcyBhbGxvd2VkIGR1cmluZyB0aGUgZXhwb3J0aW5nIHByb2Nlc3MuJ1xyXG4gICAgfSxcclxuICAgIGFsbG93RmlsZVJlc291cmNlczoge1xyXG4gICAgICB2YWx1ZTogZmFsc2UsXHJcbiAgICAgIHR5cGU6ICdib29sZWFuJyxcclxuICAgICAgZW52TGluazogJ0NVU1RPTV9MT0dJQ19BTExPV19GSUxFX1JFU09VUkNFUycsXHJcbiAgICAgIGRlc2NyaXB0aW9uOlxyXG4gICAgICAgICdDb250cm9scyB0aGUgYWJpbGl0eSB0byBpbmplY3QgcmVzb3VyY2VzIGZyb20gdGhlIGZpbGVzeXN0ZW0uIFRoaXMgc2V0dGluZyBoYXMgbm8gZWZmZWN0IHdoZW4gcnVubmluZyBhcyBhIHNlcnZlci4nXHJcbiAgICB9LFxyXG4gICAgY3VzdG9tQ29kZToge1xyXG4gICAgICB2YWx1ZTogZmFsc2UsXHJcbiAgICAgIHR5cGU6ICdzdHJpbmcnLFxyXG4gICAgICBkZXNjcmlwdGlvbjpcclxuICAgICAgICAnQ3VzdG9tIGNvZGUgdG8gZXhlY3V0ZSBiZWZvcmUgY2hhcnQgaW5pdGlhbGl6YXRpb24uIEl0IGNhbiBiZSBhIGZ1bmN0aW9uLCBjb2RlIHdyYXBwZWQgd2l0aGluIGEgZnVuY3Rpb24sIG9yIGEgZmlsZW5hbWUgd2l0aCB0aGUgLmpzIGV4dGVuc2lvbi4nXHJcbiAgICB9LFxyXG4gICAgY2FsbGJhY2s6IHtcclxuICAgICAgdmFsdWU6IGZhbHNlLFxyXG4gICAgICB0eXBlOiAnc3RyaW5nJyxcclxuICAgICAgZGVzY3JpcHRpb246XHJcbiAgICAgICAgJ0phdmFTY3JpcHQgY29kZSB0byBydW4gZHVyaW5nIGNvbnN0cnVjdGlvbi4gSXQgY2FuIGJlIGEgZnVuY3Rpb24gb3IgYSBmaWxlbmFtZSB3aXRoIHRoZSAuanMgZXh0ZW5zaW9uLidcclxuICAgIH0sXHJcbiAgICByZXNvdXJjZXM6IHtcclxuICAgICAgdmFsdWU6IGZhbHNlLFxyXG4gICAgICB0eXBlOiAnc3RyaW5nJyxcclxuICAgICAgZGVzY3JpcHRpb246XHJcbiAgICAgICAgJ0FkZGl0aW9uYWwgcmVzb3VyY2UgaW4gdGhlIGZvcm0gb2YgYSBzdHJpbmdpZmllZCBKU09OLCB3aGljaCBtYXkgY29udGFpbiBmaWxlcywganMsIGFuZCBjc3Mgc2VjdGlvbnMuJ1xyXG4gICAgfSxcclxuICAgIGxvYWRDb25maWc6IHtcclxuICAgICAgdmFsdWU6IGZhbHNlLFxyXG4gICAgICB0eXBlOiAnc3RyaW5nJyxcclxuICAgICAgbGVnYWN5TmFtZTogJ2Zyb21GaWxlJyxcclxuICAgICAgZGVzY3JpcHRpb246ICdBIGZpbGUgY29udGFpbmluZyBhIHByZS1kZWZpbmVkIGNvbmZpZ3VyYXRpb24gdG8gdXNlLidcclxuICAgIH0sXHJcbiAgICBjcmVhdGVDb25maWc6IHtcclxuICAgICAgdmFsdWU6IGZhbHNlLFxyXG4gICAgICB0eXBlOiAnc3RyaW5nJyxcclxuICAgICAgZGVzY3JpcHRpb246XHJcbiAgICAgICAgJ0VuYWJsZXMgc2V0dGluZyBvcHRpb25zIHRocm91Z2ggYSBwcm9tcHQgYW5kIHNhdmluZyB0aGVtIGluIGEgcHJvdmlkZWQgY29uZmlnIGZpbGUuJ1xyXG4gICAgfVxyXG4gIH0sXHJcbiAgc2VydmVyOiB7XHJcbiAgICBlbmFibGU6IHtcclxuICAgICAgdmFsdWU6IGZhbHNlLFxyXG4gICAgICB0eXBlOiAnYm9vbGVhbicsXHJcbiAgICAgIGVudkxpbms6ICdTRVJWRVJfRU5BQkxFJyxcclxuICAgICAgY2xpTmFtZTogJ2VuYWJsZVNlcnZlcicsXHJcbiAgICAgIGRlc2NyaXB0aW9uOlxyXG4gICAgICAgICdXaGVuIHNldCB0byB0cnVlLCB0aGUgc2VydmVyIHN0YXJ0cyBvbiB0aGUgbG9jYWwgSVAgYWRkcmVzcyAwLjAuMC4wLidcclxuICAgIH0sXHJcbiAgICBob3N0OiB7XHJcbiAgICAgIHZhbHVlOiAnMC4wLjAuMCcsXHJcbiAgICAgIHR5cGU6ICdzdHJpbmcnLFxyXG4gICAgICBlbnZMaW5rOiAnU0VSVkVSX0hPU1QnLFxyXG4gICAgICBkZXNjcmlwdGlvbjpcclxuICAgICAgICAnVGhlIGhvc3RuYW1lIG9mIHRoZSBzZXJ2ZXIuIEFkZGl0aW9uYWxseSwgaXQgc3RhcnRzIGEgc2VydmVyIG9uIHRoZSBwcm92aWRlZCBob3N0bmFtZS4nXHJcbiAgICB9LFxyXG4gICAgcG9ydDoge1xyXG4gICAgICB2YWx1ZTogNzgwMSxcclxuICAgICAgdHlwZTogJ251bWJlcicsXHJcbiAgICAgIGVudkxpbms6ICdTRVJWRVJfUE9SVCcsXHJcbiAgICAgIGRlc2NyaXB0aW9uOiAnVGhlIHNlcnZlciBwb3J0IHdoZW4gZW5hYmxlZC4nXHJcbiAgICB9LFxyXG4gICAgYmVuY2htYXJraW5nOiB7XHJcbiAgICAgIHZhbHVlOiBmYWxzZSxcclxuICAgICAgdHlwZTogJ2Jvb2xlYW4nLFxyXG4gICAgICBlbnZMaW5rOiAnU0VSVkVSX0JFTkNITUFSS0lORycsXHJcbiAgICAgIGNsaU5hbWU6ICdzZXJ2ZXJCZW5jaG1hcmtpbmcnLFxyXG4gICAgICBkZXNjcmlwdGlvbjpcclxuICAgICAgICAnSW5kaWNhdGVzIHdoZXRoZXIgdG8gZGlzcGxheSB0aGUgZHVyYXRpb24sIGluIG1pbGxpc2Vjb25kcywgb2Ygc3BlY2lmaWMgYWN0aW9ucyB0aGF0IG9jY3VyIG9uIHRoZSBzZXJ2ZXIgd2hpbGUgc2VydmluZyBhIHJlcXVlc3QuJ1xyXG4gICAgfSxcclxuICAgIHByb3h5OiB7XHJcbiAgICAgIGhvc3Q6IHtcclxuICAgICAgICB2YWx1ZTogZmFsc2UsXHJcbiAgICAgICAgdHlwZTogJ3N0cmluZycsXHJcbiAgICAgICAgZW52TGluazogJ1NFUlZFUl9QUk9YWV9IT1NUJyxcclxuICAgICAgICBjbGlOYW1lOiAncHJveHlIb3N0JyxcclxuICAgICAgICBkZXNjcmlwdGlvbjogJ1RoZSBob3N0IG9mIHRoZSBwcm94eSBzZXJ2ZXIgdG8gdXNlLCBpZiBpdCBleGlzdHMuJ1xyXG4gICAgICB9LFxyXG4gICAgICBwb3J0OiB7XHJcbiAgICAgICAgdmFsdWU6IDgwODAsXHJcbiAgICAgICAgdHlwZTogJ251bWJlcicsXHJcbiAgICAgICAgZW52TGluazogJ1NFUlZFUl9QUk9YWV9QT1JUJyxcclxuICAgICAgICBjbGlOYW1lOiAncHJveHlQb3J0JyxcclxuICAgICAgICBkZXNjcmlwdGlvbjogJ1RoZSBwb3J0IG9mIHRoZSBwcm94eSBzZXJ2ZXIgdG8gdXNlLCBpZiBpdCBleGlzdHMuJ1xyXG4gICAgICB9LFxyXG4gICAgICB0aW1lb3V0OiB7XHJcbiAgICAgICAgdmFsdWU6IDUwMDAsXHJcbiAgICAgICAgdHlwZTogJ251bWJlcicsXHJcbiAgICAgICAgZW52TGluazogJ1NFUlZFUl9QUk9YWV9USU1FT1VUJyxcclxuICAgICAgICBjbGlOYW1lOiAncHJveHlUaW1lb3V0JyxcclxuICAgICAgICBkZXNjcmlwdGlvbjogJ1RoZSB0aW1lb3V0IGZvciB0aGUgcHJveHkgc2VydmVyIHRvIHVzZSwgaWYgaXQgZXhpc3RzLidcclxuICAgICAgfVxyXG4gICAgfSxcclxuICAgIHJhdGVMaW1pdGluZzoge1xyXG4gICAgICBlbmFibGU6IHtcclxuICAgICAgICB2YWx1ZTogZmFsc2UsXHJcbiAgICAgICAgdHlwZTogJ2Jvb2xlYW4nLFxyXG4gICAgICAgIGVudkxpbms6ICdTRVJWRVJfUkFURV9MSU1JVElOR19FTkFCTEUnLFxyXG4gICAgICAgIGNsaU5hbWU6ICdlbmFibGVSYXRlTGltaXRpbmcnLFxyXG4gICAgICAgIGRlc2NyaXB0aW9uOiAnRW5hYmxlcyByYXRlIGxpbWl0aW5nIGZvciB0aGUgc2VydmVyLidcclxuICAgICAgfSxcclxuICAgICAgbWF4UmVxdWVzdHM6IHtcclxuICAgICAgICB2YWx1ZTogMTAsXHJcbiAgICAgICAgdHlwZTogJ251bWJlcicsXHJcbiAgICAgICAgZW52TGluazogJ1NFUlZFUl9SQVRFX0xJTUlUSU5HX01BWF9SRVFVRVNUUycsXHJcbiAgICAgICAgbGVnYWN5TmFtZTogJ3JhdGVMaW1pdCcsXHJcbiAgICAgICAgZGVzY3JpcHRpb246ICdUaGUgbWF4aW11bSBudW1iZXIgb2YgcmVxdWVzdHMgYWxsb3dlZCBpbiBvbmUgbWludXRlLidcclxuICAgICAgfSxcclxuICAgICAgd2luZG93OiB7XHJcbiAgICAgICAgdmFsdWU6IDEsXHJcbiAgICAgICAgdHlwZTogJ251bWJlcicsXHJcbiAgICAgICAgZW52TGluazogJ1NFUlZFUl9SQVRFX0xJTUlUSU5HX1dJTkRPVycsXHJcbiAgICAgICAgZGVzY3JpcHRpb246ICdUaGUgdGltZSB3aW5kb3csIGluIG1pbnV0ZXMsIGZvciB0aGUgcmF0ZSBsaW1pdGluZy4nXHJcbiAgICAgIH0sXHJcbiAgICAgIGRlbGF5OiB7XHJcbiAgICAgICAgdmFsdWU6IDAsXHJcbiAgICAgICAgdHlwZTogJ251bWJlcicsXHJcbiAgICAgICAgZW52TGluazogJ1NFUlZFUl9SQVRFX0xJTUlUSU5HX0RFTEFZJyxcclxuICAgICAgICBkZXNjcmlwdGlvbjpcclxuICAgICAgICAgICdUaGUgZGVsYXkgZHVyYXRpb24gZm9yIGVhY2ggc3VjY2Vzc2l2ZSByZXF1ZXN0IGJlZm9yZSByZWFjaGluZyB0aGUgbWF4aW11bSBsaW1pdC4nXHJcbiAgICAgIH0sXHJcbiAgICAgIHRydXN0UHJveHk6IHtcclxuICAgICAgICB2YWx1ZTogZmFsc2UsXHJcbiAgICAgICAgdHlwZTogJ2Jvb2xlYW4nLFxyXG4gICAgICAgIGVudkxpbms6ICdTRVJWRVJfUkFURV9MSU1JVElOR19UUlVTVF9QUk9YWScsXHJcbiAgICAgICAgZGVzY3JpcHRpb246ICdTZXQgdGhpcyB0byB0cnVlIGlmIHRoZSBzZXJ2ZXIgaXMgYmVoaW5kIGEgbG9hZCBiYWxhbmNlci4nXHJcbiAgICAgIH0sXHJcbiAgICAgIHNraXBLZXk6IHtcclxuICAgICAgICB2YWx1ZTogZmFsc2UsXHJcbiAgICAgICAgdHlwZTogJ3N0cmluZycsXHJcbiAgICAgICAgZW52TGluazogJ1NFUlZFUl9SQVRFX0xJTUlUSU5HX1NLSVBfS0VZJyxcclxuICAgICAgICBkZXNjcmlwdGlvbjpcclxuICAgICAgICAgICdBbGxvd3MgYnlwYXNzaW5nIHRoZSByYXRlIGxpbWl0ZXIgYW5kIHNob3VsZCBiZSBwcm92aWRlZCB3aXRoIHRoZSBza2lwVG9rZW4gYXJndW1lbnQuJ1xyXG4gICAgICB9LFxyXG4gICAgICBza2lwVG9rZW46IHtcclxuICAgICAgICB2YWx1ZTogZmFsc2UsXHJcbiAgICAgICAgdHlwZTogJ3N0cmluZycsXHJcbiAgICAgICAgZW52TGluazogJ1NFUlZFUl9SQVRFX0xJTUlUSU5HX1NLSVBfVE9LRU4nLFxyXG4gICAgICAgIGRlc2NyaXB0aW9uOlxyXG4gICAgICAgICAgJ0FsbG93cyBieXBhc3NpbmcgdGhlIHJhdGUgbGltaXRlciBhbmQgc2hvdWxkIGJlIHByb3ZpZGVkIHdpdGggdGhlIHNraXBLZXkgYXJndW1lbnQuJ1xyXG4gICAgICB9XHJcbiAgICB9LFxyXG4gICAgc3NsOiB7XHJcbiAgICAgIGVuYWJsZToge1xyXG4gICAgICAgIHZhbHVlOiBmYWxzZSxcclxuICAgICAgICB0eXBlOiAnYm9vbGVhbicsXHJcbiAgICAgICAgZW52TGluazogJ1NFUlZFUl9TU0xfRU5BQkxFJyxcclxuICAgICAgICBjbGlOYW1lOiAnZW5hYmxlU3NsJyxcclxuICAgICAgICBkZXNjcmlwdGlvbjogJ0VuYWJsZXMgb3IgZGlzYWJsZXMgdGhlIFNTTCBwcm90b2NvbC4nXHJcbiAgICAgIH0sXHJcbiAgICAgIGZvcmNlOiB7XHJcbiAgICAgICAgdmFsdWU6IGZhbHNlLFxyXG4gICAgICAgIHR5cGU6ICdib29sZWFuJyxcclxuICAgICAgICBlbnZMaW5rOiAnU0VSVkVSX1NTTF9GT1JDRScsXHJcbiAgICAgICAgY2xpTmFtZTogJ3NzbEZvcmNlJyxcclxuICAgICAgICBsZWdhY3lOYW1lOiAnc3NsT25seScsXHJcbiAgICAgICAgZGVzY3JpcHRpb246XHJcbiAgICAgICAgICAnV2hlbiBzZXQgdG8gdHJ1ZSwgdGhlIHNlcnZlciBpcyBmb3JjZWQgdG8gc2VydmUgb25seSBvdmVyIEhUVFBTLidcclxuICAgICAgfSxcclxuICAgICAgcG9ydDoge1xyXG4gICAgICAgIHZhbHVlOiA0NDMsXHJcbiAgICAgICAgdHlwZTogJ251bWJlcicsXHJcbiAgICAgICAgZW52TGluazogJ1NFUlZFUl9TU0xfUE9SVCcsXHJcbiAgICAgICAgY2xpTmFtZTogJ3NzbFBvcnQnLFxyXG4gICAgICAgIGRlc2NyaXB0aW9uOiAnVGhlIHBvcnQgb24gd2hpY2ggdG8gcnVuIHRoZSBTU0wgc2VydmVyLidcclxuICAgICAgfSxcclxuICAgICAgY2VydFBhdGg6IHtcclxuICAgICAgICB2YWx1ZTogZmFsc2UsXHJcbiAgICAgICAgdHlwZTogJ3N0cmluZycsXHJcbiAgICAgICAgZW52TGluazogJ1NFUlZFUl9TU0xfQ0VSVF9QQVRIJyxcclxuICAgICAgICBsZWdhY3lOYW1lOiAnc3NsUGF0aCcsXHJcbiAgICAgICAgZGVzY3JpcHRpb246ICdUaGUgcGF0aCB0byB0aGUgU1NMIGNlcnRpZmljYXRlL2tleSBmaWxlLidcclxuICAgICAgfVxyXG4gICAgfVxyXG4gIH0sXHJcbiAgcG9vbDoge1xyXG4gICAgbWluV29ya2Vyczoge1xyXG4gICAgICB2YWx1ZTogNCxcclxuICAgICAgdHlwZTogJ251bWJlcicsXHJcbiAgICAgIGVudkxpbms6ICdQT09MX01JTl9XT1JLRVJTJyxcclxuICAgICAgZGVzY3JpcHRpb246ICdUaGUgbnVtYmVyIG9mIG1pbmltdW0gYW5kIGluaXRpYWwgcG9vbCB3b3JrZXJzIHRvIHNwYXduLidcclxuICAgIH0sXHJcbiAgICBtYXhXb3JrZXJzOiB7XHJcbiAgICAgIHZhbHVlOiA4LFxyXG4gICAgICB0eXBlOiAnbnVtYmVyJyxcclxuICAgICAgZW52TGluazogJ1BPT0xfTUFYX1dPUktFUlMnLFxyXG4gICAgICBsZWdhY3lOYW1lOiAnd29ya2VycycsXHJcbiAgICAgIGRlc2NyaXB0aW9uOiAnVGhlIG51bWJlciBvZiBtYXhpbXVtIHBvb2wgd29ya2VycyB0byBzcGF3bi4nXHJcbiAgICB9LFxyXG4gICAgd29ya0xpbWl0OiB7XHJcbiAgICAgIHZhbHVlOiA0MCxcclxuICAgICAgdHlwZTogJ251bWJlcicsXHJcbiAgICAgIGVudkxpbms6ICdQT09MX1dPUktfTElNSVQnLFxyXG4gICAgICBkZXNjcmlwdGlvbjpcclxuICAgICAgICAnVGhlIG51bWJlciBvZiB3b3JrIHBpZWNlcyB0aGF0IGNhbiBiZSBwZXJmb3JtZWQgYmVmb3JlIHJlc3RhcnRpbmcgdGhlIHdvcmtlciBwcm9jZXNzLidcclxuICAgIH0sXHJcbiAgICBhY3F1aXJlVGltZW91dDoge1xyXG4gICAgICB2YWx1ZTogNTAwMCxcclxuICAgICAgdHlwZTogJ251bWJlcicsXHJcbiAgICAgIGVudkxpbms6ICdQT09MX0FDUVVJUkVfVElNRU9VVCcsXHJcbiAgICAgIGRlc2NyaXB0aW9uOlxyXG4gICAgICAgICdUaGUgZHVyYXRpb24sIGluIG1pbGxpc2Vjb25kcywgdG8gd2FpdCBmb3IgYWNxdWlyaW5nIGEgcmVzb3VyY2UuJ1xyXG4gICAgfSxcclxuICAgIGNyZWF0ZVRpbWVvdXQ6IHtcclxuICAgICAgdmFsdWU6IDUwMDAsXHJcbiAgICAgIHR5cGU6ICdudW1iZXInLFxyXG4gICAgICBlbnZMaW5rOiAnUE9PTF9DUkVBVEVfVElNRU9VVCcsXHJcbiAgICAgIGRlc2NyaXB0aW9uOlxyXG4gICAgICAgICdUaGUgZHVyYXRpb24sIGluIG1pbGxpc2Vjb25kcywgdG8gd2FpdCBmb3IgY3JlYXRpbmcgYSByZXNvdXJjZS4nXHJcbiAgICB9LFxyXG4gICAgZGVzdHJveVRpbWVvdXQ6IHtcclxuICAgICAgdmFsdWU6IDUwMDAsXHJcbiAgICAgIHR5cGU6ICdudW1iZXInLFxyXG4gICAgICBlbnZMaW5rOiAnUE9PTF9ERVNUUk9ZX1RJTUVPVVQnLFxyXG4gICAgICBkZXNjcmlwdGlvbjpcclxuICAgICAgICAnVGhlIGR1cmF0aW9uLCBpbiBtaWxsaXNlY29uZHMsIHRvIHdhaXQgZm9yIGRlc3Ryb3lpbmcgYSByZXNvdXJjZS4nXHJcbiAgICB9LFxyXG4gICAgaWRsZVRpbWVvdXQ6IHtcclxuICAgICAgdmFsdWU6IDMwMDAwLFxyXG4gICAgICB0eXBlOiAnbnVtYmVyJyxcclxuICAgICAgZW52TGluazogJ1BPT0xfSURMRV9USU1FT1VUJyxcclxuICAgICAgZGVzY3JpcHRpb246XHJcbiAgICAgICAgJ1RoZSBkdXJhdGlvbiwgaW4gbWlsbGlzZWNvbmRzLCBhZnRlciB3aGljaCBhbiBpZGxlIHJlc291cmNlIGlzIGRlc3Ryb3llZC4nXHJcbiAgICB9LFxyXG4gICAgY3JlYXRlUmV0cnlJbnRlcnZhbDoge1xyXG4gICAgICB2YWx1ZTogMjAwLFxyXG4gICAgICB0eXBlOiAnbnVtYmVyJyxcclxuICAgICAgZW52TGluazogJ1BPT0xfQ1JFQVRFX1JFVFJZX0lOVEVSVkFMJyxcclxuICAgICAgZGVzY3JpcHRpb246XHJcbiAgICAgICAgJ1RoZSBkdXJhdGlvbiwgaW4gbWlsbGlzZWNvbmRzLCB0byB3YWl0IGJlZm9yZSByZXRyeWluZyB0aGUgY3JlYXRlIHByb2Nlc3MgaW4gY2FzZSBvZiBhIGZhaWx1cmUuJ1xyXG4gICAgfSxcclxuICAgIHJlYXBlckludGVydmFsOiB7XHJcbiAgICAgIHZhbHVlOiAxMDAwLFxyXG4gICAgICB0eXBlOiAnbnVtYmVyJyxcclxuICAgICAgZW52TGluazogJ1BPT0xfUkVBUEVSX0lOVEVSVkFMJyxcclxuICAgICAgZGVzY3JpcHRpb246XHJcbiAgICAgICAgJ1RoZSBkdXJhdGlvbiwgaW4gbWlsbGlzZWNvbmRzLCBhZnRlciB3aGljaCB0aGUgY2hlY2sgZm9yIGlkbGUgcmVzb3VyY2VzIHRvIGRlc3Ryb3kgaXMgdHJpZ2dlcmVkLidcclxuICAgIH0sXHJcbiAgICBiZW5jaG1hcmtpbmc6IHtcclxuICAgICAgdmFsdWU6IGZhbHNlLFxyXG4gICAgICB0eXBlOiAnYm9vbGVhbicsXHJcbiAgICAgIGVudkxpbms6ICdQT09MX0JFTkNITUFSS0lORycsXHJcbiAgICAgIGNsaU5hbWU6ICdwb29sQmVuY2htYXJraW5nJyxcclxuICAgICAgZGVzY3JpcHRpb246XHJcbiAgICAgICAgJ0luZGljYXRlIHdoZXRoZXIgdG8gc2hvdyBzdGF0aXN0aWNzIGZvciB0aGUgcG9vbCBvZiByZXNvdXJjZXMgb3Igbm90LidcclxuICAgIH1cclxuICB9LFxyXG4gIGxvZ2dpbmc6IHtcclxuICAgIGxldmVsOiB7XHJcbiAgICAgIHZhbHVlOiA0LFxyXG4gICAgICB0eXBlOiAnbnVtYmVyJyxcclxuICAgICAgZW52TGluazogJ0xPR0dJTkdfTEVWRUwnLFxyXG4gICAgICBjbGlOYW1lOiAnbG9nTGV2ZWwnLFxyXG4gICAgICBkZXNjcmlwdGlvbjogJ1RoZSBsb2dnaW5nIGxldmVsIHRvIGJlIHVzZWQuJ1xyXG4gICAgfSxcclxuICAgIGZpbGU6IHtcclxuICAgICAgdmFsdWU6ICdoaWdoY2hhcnRzLWV4cG9ydC1zZXJ2ZXIubG9nJyxcclxuICAgICAgdHlwZTogJ3N0cmluZycsXHJcbiAgICAgIGVudkxpbms6ICdMT0dHSU5HX0ZJTEUnLFxyXG4gICAgICBjbGlOYW1lOiAnbG9nRmlsZScsXHJcbiAgICAgIGRlc2NyaXB0aW9uOlxyXG4gICAgICAgICdUaGUgbmFtZSBvZiBhIGxvZyBmaWxlLiBUaGUgbG9nRGVzdCBvcHRpb24gYWxzbyBuZWVkcyB0byBiZSBzZXQgdG8gZW5hYmxlIGZpbGUgbG9nZ2luZy4nXHJcbiAgICB9LFxyXG4gICAgZGVzdDoge1xyXG4gICAgICB2YWx1ZTogJ2xvZy8nLFxyXG4gICAgICB0eXBlOiAnc3RyaW5nJyxcclxuICAgICAgZW52TGluazogJ0xPR0dJTkdfREVTVCcsXHJcbiAgICAgIGNsaU5hbWU6ICdsb2dEZXN0JyxcclxuICAgICAgZGVzY3JpcHRpb246XHJcbiAgICAgICAgJ1RoZSBwYXRoIHRvIHN0b3JlIGxvZyBmaWxlcy4gVGhpcyBhbHNvIGVuYWJsZXMgZmlsZSBsb2dnaW5nLidcclxuICAgIH1cclxuICB9LFxyXG4gIHVpOiB7XHJcbiAgICBlbmFibGU6IHtcclxuICAgICAgdmFsdWU6IGZhbHNlLFxyXG4gICAgICB0eXBlOiAnYm9vbGVhbicsXHJcbiAgICAgIGVudkxpbms6ICdVSV9FTkFCTEUnLFxyXG4gICAgICBjbGlOYW1lOiAnZW5hYmxlVWknLFxyXG4gICAgICBkZXNjcmlwdGlvbjpcclxuICAgICAgICAnRW5hYmxlcyBvciBkaXNhYmxlcyB0aGUgdXNlciBpbnRlcmZhY2UgKFVJKSBmb3IgdGhlIGV4cG9ydCBzZXJ2ZXIuJ1xyXG4gICAgfSxcclxuICAgIHJvdXRlOiB7XHJcbiAgICAgIHZhbHVlOiAnLycsXHJcbiAgICAgIHR5cGU6ICdzdHJpbmcnLFxyXG4gICAgICBlbnZMaW5rOiAnVUlfUk9VVEUnLFxyXG4gICAgICBjbGlOYW1lOiAndWlSb3V0ZScsXHJcbiAgICAgIGRlc2NyaXB0aW9uOlxyXG4gICAgICAgICdUaGUgZW5kcG9pbnQgcm91dGUgdG8gd2hpY2ggdGhlIHVzZXIgaW50ZXJmYWNlIChVSSkgc2hvdWxkIGJlIGF0dGFjaGVkLidcclxuICAgIH1cclxuICB9LFxyXG4gIG90aGVyOiB7XHJcbiAgICBub2RlRW52OiB7XHJcbiAgICAgIHZhbHVlOiAncHJvZHVjdGlvbicsXHJcbiAgICAgIHR5cGU6ICdzdHJpbmcnLFxyXG4gICAgICBlbnZMaW5rOiAnT1RIRVJfTk9ERV9FTlYnLFxyXG4gICAgICBkZXNjcmlwdGlvbjogJ1RoZSB0eXBlIG9mIE5vZGUuanMgZW52aXJvbm1lbnQuJ1xyXG4gICAgfSxcclxuICAgIGxpc3RlblRvUHJvY2Vzc0V4aXRzOiB7XHJcbiAgICAgIHZhbHVlOiB0cnVlLFxyXG4gICAgICB0eXBlOiAnYm9vbGVhbicsXHJcbiAgICAgIGVudkxpbms6ICdPVEhFUl9MSVNURU5fVE9fUFJPQ0VTU19FWElUUycsXHJcbiAgICAgIGRlc2NyaXB0aW9uOiAnRGVjaWRlcyB3aGV0aGVyIG9yIG5vdCB0byBhdHRhY2ggcHJvY2Vzcy5leGl0IGhhbmRsZXJzLidcclxuICAgIH0sXHJcbiAgICBub0xvZ286IHtcclxuICAgICAgdmFsdWU6IGZhbHNlLFxyXG4gICAgICB0eXBlOiAnYm9vbGVhbicsXHJcbiAgICAgIGVudkxpbms6ICdPVEhFUl9OT19MT0dPJyxcclxuICAgICAgZGVzY3JpcHRpb246XHJcbiAgICAgICAgJ1NraXAgcHJpbnRpbmcgdGhlIGxvZ28gb24gYSBzdGFydHVwLiBXaWxsIGJlIHJlcGxhY2VkIGJ5IGEgc2ltcGxlIHRleHQuJ1xyXG4gICAgfSxcclxuICAgIGhhcmRSZXNldFBhZ2U6IHtcclxuICAgICAgdmFsdWU6IGZhbHNlLFxyXG4gICAgICB0eXBlOiAnYm9vbGVhbicsXHJcbiAgICAgIGVudkxpbms6ICdPVEhFUl9IQVJEX1JFU0VUX1BBR0UnLFxyXG4gICAgICBkZXNjcmlwdGlvbjogJ0RlY2lkZXMgaWYgdGhlIHBhZ2UgY29udGVudCBzaG91bGQgYmUgcmVzZXQgZW50aXJlbHkuJ1xyXG4gICAgfVxyXG4gIH0sXHJcbiAgZGVidWc6IHtcclxuICAgIGVuYWJsZToge1xyXG4gICAgICB2YWx1ZTogZmFsc2UsXHJcbiAgICAgIHR5cGU6ICdib29sZWFuJyxcclxuICAgICAgZW52TGluazogJ0RFQlVHX0VOQUJMRScsXHJcbiAgICAgIGNsaU5hbWU6ICdlbmFibGVEZWJ1ZycsXHJcbiAgICAgIGRlc2NyaXB0aW9uOiAnRW5hYmxlcyBvciBkaXNhYmxlcyBkZWJ1ZyBtb2RlIGZvciB0aGUgdW5kZXJseWluZyBicm93c2VyLidcclxuICAgIH0sXHJcbiAgICBoZWFkbGVzczoge1xyXG4gICAgICB2YWx1ZTogdHJ1ZSxcclxuICAgICAgdHlwZTogJ2Jvb2xlYW4nLFxyXG4gICAgICBlbnZMaW5rOiAnREVCVUdfSEVBRExFU1MnLFxyXG4gICAgICBkZXNjcmlwdGlvbjpcclxuICAgICAgICAnQ29udHJvbHMgdGhlIG1vZGUgaW4gd2hpY2ggdGhlIGJyb3dzZXIgaXMgbGF1bmNoZWQgd2hlbiBpbiB0aGUgZGVidWcgbW9kZS4nXHJcbiAgICB9LFxyXG4gICAgZGV2dG9vbHM6IHtcclxuICAgICAgdmFsdWU6IGZhbHNlLFxyXG4gICAgICB0eXBlOiAnYm9vbGVhbicsXHJcbiAgICAgIGVudkxpbms6ICdERUJVR19ERVZUT09MUycsXHJcbiAgICAgIGRlc2NyaXB0aW9uOlxyXG4gICAgICAgICdEZWNpZGVzIHdoZXRoZXIgdG8gZW5hYmxlIERldlRvb2xzIHdoZW4gdGhlIGJyb3dzZXIgaXMgaW4gYSBoZWFkZnVsIHN0YXRlLidcclxuICAgIH0sXHJcbiAgICBsaXN0ZW5Ub0NvbnNvbGU6IHtcclxuICAgICAgdmFsdWU6IGZhbHNlLFxyXG4gICAgICB0eXBlOiAnYm9vbGVhbicsXHJcbiAgICAgIGVudkxpbms6ICdERUJVR19MSVNURU5fVE9fQ09OU09MRScsXHJcbiAgICAgIGRlc2NyaXB0aW9uOlxyXG4gICAgICAgICdEZWNpZGVzIHdoZXRoZXIgdG8gZW5hYmxlIGEgbGlzdGVuZXIgZm9yIGNvbnNvbGUgbWVzc2FnZXMgc2VudCBmcm9tIHRoZSBicm93c2VyLidcclxuICAgIH0sXHJcbiAgICBkdW1waW86IHtcclxuICAgICAgdmFsdWU6IGZhbHNlLFxyXG4gICAgICB0eXBlOiAnYm9vbGVhbicsXHJcbiAgICAgIGVudkxpbms6ICdERUJVR19EVU1QSU8nLFxyXG4gICAgICBkZXNjcmlwdGlvbjpcclxuICAgICAgICAnUmVkaXJlY3RzIGJyb3dzZXIgcHJvY2VzcyBzdGRvdXQgYW5kIHN0ZGVyciB0byBwcm9jZXNzLnN0ZG91dCBhbmQgcHJvY2Vzcy5zdGRlcnIuJ1xyXG4gICAgfSxcclxuICAgIHNsb3dNbzoge1xyXG4gICAgICB2YWx1ZTogMCxcclxuICAgICAgdHlwZTogJ251bWJlcicsXHJcbiAgICAgIGVudkxpbms6ICdERUJVR19TTE9XX01PJyxcclxuICAgICAgZGVzY3JpcHRpb246XHJcbiAgICAgICAgJ1Nsb3dzIGRvd24gUHVwcGV0ZWVyIG9wZXJhdGlvbnMgYnkgdGhlIHNwZWNpZmllZCBudW1iZXIgb2YgbWlsbGlzZWNvbmRzLidcclxuICAgIH0sXHJcbiAgICBkZWJ1Z2dpbmdQb3J0OiB7XHJcbiAgICAgIHZhbHVlOiA5MjIyLFxyXG4gICAgICB0eXBlOiAnbnVtYmVyJyxcclxuICAgICAgZW52TGluazogJ0RFQlVHX0RFQlVHR0lOR19QT1JUJyxcclxuICAgICAgZGVzY3JpcHRpb246ICdTcGVjaWZpZXMgdGhlIGRlYnVnZ2luZyBwb3J0LidcclxuICAgIH1cclxuICB9XHJcbn07XHJcblxyXG4vLyBUaGUgY29uZmlnIGRlc2NyaXB0aW9ucyBvYmplY3QgZm9yIHRoZSBwcm9tcHRzIGZ1bmN0aW9uYWxpdHkuIEl0IGNvbnRhaW5zXHJcbi8vIGluZm9ybWF0aW9uIGxpa2U6XHJcbi8vICogVHlwZSBvZiBhIHByb21wdFxyXG4vLyAqIE5hbWUgb2YgYW4gb3B0aW9uXHJcbi8vICogU2hvcnQgZGVzY3JpcHRpb24gb2YgYSBjaG9zZW4gb3B0aW9uXHJcbi8vICogSW5pdGlhbCB2YWx1ZVxyXG5leHBvcnQgY29uc3QgcHJvbXB0c0NvbmZpZyA9IHtcclxuICBwdXBwZXRlZXI6IFtcclxuICAgIHtcclxuICAgICAgdHlwZTogJ2xpc3QnLFxyXG4gICAgICBuYW1lOiAnYXJncycsXHJcbiAgICAgIG1lc3NhZ2U6ICdQdXBwZXRlZXIgYXJndW1lbnRzJyxcclxuICAgICAgaW5pdGlhbDogZGVmYXVsdENvbmZpZy5wdXBwZXRlZXIuYXJncy52YWx1ZS5qb2luKCcsJyksXHJcbiAgICAgIHNlcGFyYXRvcjogJywnXHJcbiAgICB9XHJcbiAgXSxcclxuICBoaWdoY2hhcnRzOiBbXHJcbiAgICB7XHJcbiAgICAgIHR5cGU6ICd0ZXh0JyxcclxuICAgICAgbmFtZTogJ3ZlcnNpb24nLFxyXG4gICAgICBtZXNzYWdlOiAnSGlnaGNoYXJ0cyB2ZXJzaW9uJyxcclxuICAgICAgaW5pdGlhbDogZGVmYXVsdENvbmZpZy5oaWdoY2hhcnRzLnZlcnNpb24udmFsdWVcclxuICAgIH0sXHJcbiAgICB7XHJcbiAgICAgIHR5cGU6ICd0ZXh0JyxcclxuICAgICAgbmFtZTogJ2NkblVSTCcsXHJcbiAgICAgIG1lc3NhZ2U6ICdUaGUgVVJMIG9mIENETicsXHJcbiAgICAgIGluaXRpYWw6IGRlZmF1bHRDb25maWcuaGlnaGNoYXJ0cy5jZG5VUkwudmFsdWVcclxuICAgIH0sXHJcbiAgICB7XHJcbiAgICAgIHR5cGU6ICdtdWx0aXNlbGVjdCcsXHJcbiAgICAgIG5hbWU6ICdjb3JlU2NyaXB0cycsXHJcbiAgICAgIG1lc3NhZ2U6ICdBdmFpbGFibGUgY29yZSBzY3JpcHRzJyxcclxuICAgICAgaW5zdHJ1Y3Rpb25zOiAnU3BhY2U6IFNlbGVjdCBzcGVjaWZpYywgQTogU2VsZWN0IGFsbCwgRW50ZXI6IENvbmZpcm0uJyxcclxuICAgICAgY2hvaWNlczogZGVmYXVsdENvbmZpZy5oaWdoY2hhcnRzLmNvcmVTY3JpcHRzLnZhbHVlXHJcbiAgICB9LFxyXG4gICAge1xyXG4gICAgICB0eXBlOiAnbXVsdGlzZWxlY3QnLFxyXG4gICAgICBuYW1lOiAnbW9kdWxlU2NyaXB0cycsXHJcbiAgICAgIG1lc3NhZ2U6ICdBdmFpbGFibGUgbW9kdWxlIHNjcmlwdHMnLFxyXG4gICAgICBpbnN0cnVjdGlvbnM6ICdTcGFjZTogU2VsZWN0IHNwZWNpZmljLCBBOiBTZWxlY3QgYWxsLCBFbnRlcjogQ29uZmlybS4nLFxyXG4gICAgICBjaG9pY2VzOiBkZWZhdWx0Q29uZmlnLmhpZ2hjaGFydHMubW9kdWxlU2NyaXB0cy52YWx1ZVxyXG4gICAgfSxcclxuICAgIHtcclxuICAgICAgdHlwZTogJ211bHRpc2VsZWN0JyxcclxuICAgICAgbmFtZTogJ2luZGljYXRvclNjcmlwdHMnLFxyXG4gICAgICBtZXNzYWdlOiAnQXZhaWxhYmxlIGluZGljYXRvciBzY3JpcHRzJyxcclxuICAgICAgaW5zdHJ1Y3Rpb25zOiAnU3BhY2U6IFNlbGVjdCBzcGVjaWZpYywgQTogU2VsZWN0IGFsbCwgRW50ZXI6IENvbmZpcm0uJyxcclxuICAgICAgY2hvaWNlczogZGVmYXVsdENvbmZpZy5oaWdoY2hhcnRzLmluZGljYXRvclNjcmlwdHMudmFsdWVcclxuICAgIH0sXHJcbiAgICB7XHJcbiAgICAgIHR5cGU6ICdsaXN0JyxcclxuICAgICAgbmFtZTogJ2N1c3RvbVNjcmlwdHMnLFxyXG4gICAgICBtZXNzYWdlOiAnQ3VzdG9tIHNjcmlwdHMnLFxyXG4gICAgICBpbml0aWFsOiBkZWZhdWx0Q29uZmlnLmhpZ2hjaGFydHMuY3VzdG9tU2NyaXB0cy52YWx1ZS5qb2luKCcsJyksXHJcbiAgICAgIHNlcGFyYXRvcjogJywnXHJcbiAgICB9LFxyXG4gICAge1xyXG4gICAgICB0eXBlOiAndG9nZ2xlJyxcclxuICAgICAgbmFtZTogJ2ZvcmNlRmV0Y2gnLFxyXG4gICAgICBtZXNzYWdlOiAnRm9yY2UgcmUtZmV0Y2ggdGhlIHNjcmlwdHMnLFxyXG4gICAgICBpbml0aWFsOiBkZWZhdWx0Q29uZmlnLmhpZ2hjaGFydHMuZm9yY2VGZXRjaC52YWx1ZVxyXG4gICAgfSxcclxuICAgIHtcclxuICAgICAgdHlwZTogJ3RleHQnLFxyXG4gICAgICBuYW1lOiAnY2FjaGVQYXRoJyxcclxuICAgICAgbWVzc2FnZTogJ1RoZSBwYXRoIHRvIHRoZSBjYWNoZSBkaXJlY3RvcnknLFxyXG4gICAgICBpbml0aWFsOiBkZWZhdWx0Q29uZmlnLmhpZ2hjaGFydHMuY2FjaGVQYXRoLnZhbHVlXHJcbiAgICB9XHJcbiAgXSxcclxuICBleHBvcnQ6IFtcclxuICAgIHtcclxuICAgICAgdHlwZTogJ3NlbGVjdCcsXHJcbiAgICAgIG5hbWU6ICd0eXBlJyxcclxuICAgICAgbWVzc2FnZTogJ1RoZSBkZWZhdWx0IGV4cG9ydCBmaWxlIHR5cGUnLFxyXG4gICAgICBoaW50OiBgRGVmYXVsdDogJHtkZWZhdWx0Q29uZmlnLmV4cG9ydC50eXBlLnZhbHVlfWAsXHJcbiAgICAgIGluaXRpYWw6IDAsXHJcbiAgICAgIGNob2ljZXM6IFsncG5nJywgJ2pwZWcnLCAncGRmJywgJ3N2ZyddXHJcbiAgICB9LFxyXG4gICAge1xyXG4gICAgICB0eXBlOiAnc2VsZWN0JyxcclxuICAgICAgbmFtZTogJ2NvbnN0cicsXHJcbiAgICAgIG1lc3NhZ2U6ICdUaGUgZGVmYXVsdCBjb25zdHJ1Y3RvciBmb3IgSGlnaGNoYXJ0cycsXHJcbiAgICAgIGhpbnQ6IGBEZWZhdWx0OiAke2RlZmF1bHRDb25maWcuZXhwb3J0LmNvbnN0ci52YWx1ZX1gLFxyXG4gICAgICBpbml0aWFsOiAwLFxyXG4gICAgICBjaG9pY2VzOiBbJ2NoYXJ0JywgJ3N0b2NrQ2hhcnQnLCAnbWFwQ2hhcnQnLCAnZ2FudHRDaGFydCddXHJcbiAgICB9LFxyXG4gICAge1xyXG4gICAgICB0eXBlOiAnbnVtYmVyJyxcclxuICAgICAgbmFtZTogJ2RlZmF1bHRIZWlnaHQnLFxyXG4gICAgICBtZXNzYWdlOiAnVGhlIGRlZmF1bHQgZmFsbGJhY2sgaGVpZ2h0IG9mIHRoZSBleHBvcnRlZCBjaGFydCcsXHJcbiAgICAgIGluaXRpYWw6IGRlZmF1bHRDb25maWcuZXhwb3J0LmRlZmF1bHRIZWlnaHQudmFsdWVcclxuICAgIH0sXHJcbiAgICB7XHJcbiAgICAgIHR5cGU6ICdudW1iZXInLFxyXG4gICAgICBuYW1lOiAnZGVmYXVsdFdpZHRoJyxcclxuICAgICAgbWVzc2FnZTogJ1RoZSBkZWZhdWx0IGZhbGxiYWNrIHdpZHRoIG9mIHRoZSBleHBvcnRlZCBjaGFydCcsXHJcbiAgICAgIGluaXRpYWw6IGRlZmF1bHRDb25maWcuZXhwb3J0LmRlZmF1bHRXaWR0aC52YWx1ZVxyXG4gICAgfSxcclxuICAgIHtcclxuICAgICAgdHlwZTogJ251bWJlcicsXHJcbiAgICAgIG5hbWU6ICdkZWZhdWx0U2NhbGUnLFxyXG4gICAgICBtZXNzYWdlOiAnVGhlIGRlZmF1bHQgZmFsbGJhY2sgc2NhbGUgb2YgdGhlIGV4cG9ydGVkIGNoYXJ0JyxcclxuICAgICAgaW5pdGlhbDogZGVmYXVsdENvbmZpZy5leHBvcnQuZGVmYXVsdFNjYWxlLnZhbHVlLFxyXG4gICAgICBtaW46IDAuMSxcclxuICAgICAgbWF4OiA1XHJcbiAgICB9LFxyXG4gICAge1xyXG4gICAgICB0eXBlOiAnbnVtYmVyJyxcclxuICAgICAgbmFtZTogJ3Jhc3Rlcml6YXRpb25UaW1lb3V0JyxcclxuICAgICAgbWVzc2FnZTogJ1RoZSByZW5kZXJpbmcgd2VicGFnZSB0aW1lb3V0IGluIG1pbGxpc2Vjb25kcycsXHJcbiAgICAgIGluaXRpYWw6IGRlZmF1bHRDb25maWcuZXhwb3J0LnJhc3Rlcml6YXRpb25UaW1lb3V0LnZhbHVlXHJcbiAgICB9XHJcbiAgXSxcclxuICBjdXN0b21Mb2dpYzogW1xyXG4gICAge1xyXG4gICAgICB0eXBlOiAndG9nZ2xlJyxcclxuICAgICAgbmFtZTogJ2FsbG93Q29kZUV4ZWN1dGlvbicsXHJcbiAgICAgIG1lc3NhZ2U6ICdFbmFibGUgZXhlY3V0aW9uIG9mIGN1c3RvbSBjb2RlJyxcclxuICAgICAgaW5pdGlhbDogZGVmYXVsdENvbmZpZy5jdXN0b21Mb2dpYy5hbGxvd0NvZGVFeGVjdXRpb24udmFsdWVcclxuICAgIH0sXHJcbiAgICB7XHJcbiAgICAgIHR5cGU6ICd0b2dnbGUnLFxyXG4gICAgICBuYW1lOiAnYWxsb3dGaWxlUmVzb3VyY2VzJyxcclxuICAgICAgbWVzc2FnZTogJ0VuYWJsZSBmaWxlIHJlc291cmNlcycsXHJcbiAgICAgIGluaXRpYWw6IGRlZmF1bHRDb25maWcuY3VzdG9tTG9naWMuYWxsb3dGaWxlUmVzb3VyY2VzLnZhbHVlXHJcbiAgICB9XHJcbiAgXSxcclxuICBzZXJ2ZXI6IFtcclxuICAgIHtcclxuICAgICAgdHlwZTogJ3RvZ2dsZScsXHJcbiAgICAgIG5hbWU6ICdlbmFibGUnLFxyXG4gICAgICBtZXNzYWdlOiAnU3RhcnRzIHRoZSBzZXJ2ZXIgb24gMC4wLjAuMCcsXHJcbiAgICAgIGluaXRpYWw6IGRlZmF1bHRDb25maWcuc2VydmVyLmVuYWJsZS52YWx1ZVxyXG4gICAgfSxcclxuICAgIHtcclxuICAgICAgdHlwZTogJ3RleHQnLFxyXG4gICAgICBuYW1lOiAnaG9zdCcsXHJcbiAgICAgIG1lc3NhZ2U6ICdTZXJ2ZXIgaG9zdG5hbWUnLFxyXG4gICAgICBpbml0aWFsOiBkZWZhdWx0Q29uZmlnLnNlcnZlci5ob3N0LnZhbHVlXHJcbiAgICB9LFxyXG4gICAge1xyXG4gICAgICB0eXBlOiAnbnVtYmVyJyxcclxuICAgICAgbmFtZTogJ3BvcnQnLFxyXG4gICAgICBtZXNzYWdlOiAnU2VydmVyIHBvcnQnLFxyXG4gICAgICBpbml0aWFsOiBkZWZhdWx0Q29uZmlnLnNlcnZlci5wb3J0LnZhbHVlXHJcbiAgICB9LFxyXG4gICAge1xyXG4gICAgICB0eXBlOiAndG9nZ2xlJyxcclxuICAgICAgbmFtZTogJ2JlbmNobWFya2luZycsXHJcbiAgICAgIG1lc3NhZ2U6ICdFbmFibGUgc2VydmVyIGJlbmNobWFya2luZycsXHJcbiAgICAgIGluaXRpYWw6IGRlZmF1bHRDb25maWcuc2VydmVyLmJlbmNobWFya2luZy52YWx1ZVxyXG4gICAgfSxcclxuICAgIHtcclxuICAgICAgdHlwZTogJ3RleHQnLFxyXG4gICAgICBuYW1lOiAncHJveHkuaG9zdCcsXHJcbiAgICAgIG1lc3NhZ2U6ICdUaGUgaG9zdCBvZiB0aGUgcHJveHkgc2VydmVyIHRvIHVzZScsXHJcbiAgICAgIGluaXRpYWw6IGRlZmF1bHRDb25maWcuc2VydmVyLnByb3h5Lmhvc3QudmFsdWVcclxuICAgIH0sXHJcbiAgICB7XHJcbiAgICAgIHR5cGU6ICdudW1iZXInLFxyXG4gICAgICBuYW1lOiAncHJveHkucG9ydCcsXHJcbiAgICAgIG1lc3NhZ2U6ICdUaGUgcG9ydCBvZiB0aGUgcHJveHkgc2VydmVyIHRvIHVzZScsXHJcbiAgICAgIGluaXRpYWw6IGRlZmF1bHRDb25maWcuc2VydmVyLnByb3h5LnBvcnQudmFsdWVcclxuICAgIH0sXHJcbiAgICB7XHJcbiAgICAgIHR5cGU6ICdudW1iZXInLFxyXG4gICAgICBuYW1lOiAncHJveHkudGltZW91dCcsXHJcbiAgICAgIG1lc3NhZ2U6ICdUaGUgdGltZW91dCBmb3IgdGhlIHByb3h5IHNlcnZlciB0byB1c2UnLFxyXG4gICAgICBpbml0aWFsOiBkZWZhdWx0Q29uZmlnLnNlcnZlci5wcm94eS50aW1lb3V0LnZhbHVlXHJcbiAgICB9LFxyXG4gICAge1xyXG4gICAgICB0eXBlOiAndG9nZ2xlJyxcclxuICAgICAgbmFtZTogJ3JhdGVMaW1pdGluZy5lbmFibGUnLFxyXG4gICAgICBtZXNzYWdlOiAnRW5hYmxlIHJhdGUgbGltaXRpbmcnLFxyXG4gICAgICBpbml0aWFsOiBkZWZhdWx0Q29uZmlnLnNlcnZlci5yYXRlTGltaXRpbmcuZW5hYmxlLnZhbHVlXHJcbiAgICB9LFxyXG4gICAge1xyXG4gICAgICB0eXBlOiAnbnVtYmVyJyxcclxuICAgICAgbmFtZTogJ3JhdGVMaW1pdGluZy5tYXhSZXF1ZXN0cycsXHJcbiAgICAgIG1lc3NhZ2U6ICdUaGUgbWF4aW11bSByZXF1ZXN0cyBhbGxvd2VkIHBlciBtaW51dGUnLFxyXG4gICAgICBpbml0aWFsOiBkZWZhdWx0Q29uZmlnLnNlcnZlci5yYXRlTGltaXRpbmcubWF4UmVxdWVzdHMudmFsdWVcclxuICAgIH0sXHJcbiAgICB7XHJcbiAgICAgIHR5cGU6ICdudW1iZXInLFxyXG4gICAgICBuYW1lOiAncmF0ZUxpbWl0aW5nLndpbmRvdycsXHJcbiAgICAgIG1lc3NhZ2U6ICdUaGUgcmF0ZS1saW1pdGluZyB0aW1lIHdpbmRvdyBpbiBtaW51dGVzJyxcclxuICAgICAgaW5pdGlhbDogZGVmYXVsdENvbmZpZy5zZXJ2ZXIucmF0ZUxpbWl0aW5nLndpbmRvdy52YWx1ZVxyXG4gICAgfSxcclxuICAgIHtcclxuICAgICAgdHlwZTogJ251bWJlcicsXHJcbiAgICAgIG5hbWU6ICdyYXRlTGltaXRpbmcuZGVsYXknLFxyXG4gICAgICBtZXNzYWdlOlxyXG4gICAgICAgICdUaGUgZGVsYXkgZm9yIGVhY2ggc3VjY2Vzc2l2ZSByZXF1ZXN0IGJlZm9yZSByZWFjaGluZyB0aGUgbWF4aW11bScsXHJcbiAgICAgIGluaXRpYWw6IGRlZmF1bHRDb25maWcuc2VydmVyLnJhdGVMaW1pdGluZy5kZWxheS52YWx1ZVxyXG4gICAgfSxcclxuICAgIHtcclxuICAgICAgdHlwZTogJ3RvZ2dsZScsXHJcbiAgICAgIG5hbWU6ICdyYXRlTGltaXRpbmcudHJ1c3RQcm94eScsXHJcbiAgICAgIG1lc3NhZ2U6ICdTZXQgdG8gdHJ1ZSBpZiBiZWhpbmQgYSBsb2FkIGJhbGFuY2VyJyxcclxuICAgICAgaW5pdGlhbDogZGVmYXVsdENvbmZpZy5zZXJ2ZXIucmF0ZUxpbWl0aW5nLnRydXN0UHJveHkudmFsdWVcclxuICAgIH0sXHJcbiAgICB7XHJcbiAgICAgIHR5cGU6ICd0ZXh0JyxcclxuICAgICAgbmFtZTogJ3JhdGVMaW1pdGluZy5za2lwS2V5JyxcclxuICAgICAgbWVzc2FnZTpcclxuICAgICAgICAnQWxsb3dzIGJ5cGFzc2luZyB0aGUgcmF0ZSBsaW1pdGVyIHdoZW4gcHJvdmlkZWQgd2l0aCB0aGUgc2tpcFRva2VuIGFyZ3VtZW50JyxcclxuICAgICAgaW5pdGlhbDogZGVmYXVsdENvbmZpZy5zZXJ2ZXIucmF0ZUxpbWl0aW5nLnNraXBLZXkudmFsdWVcclxuICAgIH0sXHJcbiAgICB7XHJcbiAgICAgIHR5cGU6ICd0ZXh0JyxcclxuICAgICAgbmFtZTogJ3JhdGVMaW1pdGluZy5za2lwVG9rZW4nLFxyXG4gICAgICBtZXNzYWdlOlxyXG4gICAgICAgICdBbGxvd3MgYnlwYXNzaW5nIHRoZSByYXRlIGxpbWl0ZXIgd2hlbiBwcm92aWRlZCB3aXRoIHRoZSBza2lwS2V5IGFyZ3VtZW50JyxcclxuICAgICAgaW5pdGlhbDogZGVmYXVsdENvbmZpZy5zZXJ2ZXIucmF0ZUxpbWl0aW5nLnNraXBUb2tlbi52YWx1ZVxyXG4gICAgfSxcclxuICAgIHtcclxuICAgICAgdHlwZTogJ3RvZ2dsZScsXHJcbiAgICAgIG5hbWU6ICdzc2wuZW5hYmxlJyxcclxuICAgICAgbWVzc2FnZTogJ0VuYWJsZSBTU0wgcHJvdG9jb2wnLFxyXG4gICAgICBpbml0aWFsOiBkZWZhdWx0Q29uZmlnLnNlcnZlci5zc2wuZW5hYmxlLnZhbHVlXHJcbiAgICB9LFxyXG4gICAge1xyXG4gICAgICB0eXBlOiAndG9nZ2xlJyxcclxuICAgICAgbmFtZTogJ3NzbC5mb3JjZScsXHJcbiAgICAgIG1lc3NhZ2U6ICdGb3JjZSBzZXJ2aW5nIG9ubHkgb3ZlciBIVFRQUycsXHJcbiAgICAgIGluaXRpYWw6IGRlZmF1bHRDb25maWcuc2VydmVyLnNzbC5mb3JjZS52YWx1ZVxyXG4gICAgfSxcclxuICAgIHtcclxuICAgICAgdHlwZTogJ251bWJlcicsXHJcbiAgICAgIG5hbWU6ICdzc2wucG9ydCcsXHJcbiAgICAgIG1lc3NhZ2U6ICdTU0wgc2VydmVyIHBvcnQnLFxyXG4gICAgICBpbml0aWFsOiBkZWZhdWx0Q29uZmlnLnNlcnZlci5zc2wucG9ydC52YWx1ZVxyXG4gICAgfSxcclxuICAgIHtcclxuICAgICAgdHlwZTogJ3RleHQnLFxyXG4gICAgICBuYW1lOiAnc3NsLmNlcnRQYXRoJyxcclxuICAgICAgbWVzc2FnZTogJ1RoZSBwYXRoIHRvIGZpbmQgdGhlIFNTTCBjZXJ0aWZpY2F0ZS9rZXknLFxyXG4gICAgICBpbml0aWFsOiBkZWZhdWx0Q29uZmlnLnNlcnZlci5zc2wuY2VydFBhdGgudmFsdWVcclxuICAgIH1cclxuICBdLFxyXG4gIHBvb2w6IFtcclxuICAgIHtcclxuICAgICAgdHlwZTogJ251bWJlcicsXHJcbiAgICAgIG5hbWU6ICdtaW5Xb3JrZXJzJyxcclxuICAgICAgbWVzc2FnZTogJ1RoZSBpbml0aWFsIG51bWJlciBvZiB3b3JrZXJzIHRvIHNwYXduJyxcclxuICAgICAgaW5pdGlhbDogZGVmYXVsdENvbmZpZy5wb29sLm1pbldvcmtlcnMudmFsdWVcclxuICAgIH0sXHJcbiAgICB7XHJcbiAgICAgIHR5cGU6ICdudW1iZXInLFxyXG4gICAgICBuYW1lOiAnbWF4V29ya2VycycsXHJcbiAgICAgIG1lc3NhZ2U6ICdUaGUgbWF4aW11bSBudW1iZXIgb2Ygd29ya2VycyB0byBzcGF3bicsXHJcbiAgICAgIGluaXRpYWw6IGRlZmF1bHRDb25maWcucG9vbC5tYXhXb3JrZXJzLnZhbHVlXHJcbiAgICB9LFxyXG4gICAge1xyXG4gICAgICB0eXBlOiAnbnVtYmVyJyxcclxuICAgICAgbmFtZTogJ3dvcmtMaW1pdCcsXHJcbiAgICAgIG1lc3NhZ2U6XHJcbiAgICAgICAgJ1RoZSBwaWVjZXMgb2Ygd29yayB0aGF0IGNhbiBiZSBwZXJmb3JtZWQgYmVmb3JlIHJlc3RhcnRpbmcgYSBQdXBwZXRlZXIgcHJvY2VzcycsXHJcbiAgICAgIGluaXRpYWw6IGRlZmF1bHRDb25maWcucG9vbC53b3JrTGltaXQudmFsdWVcclxuICAgIH0sXHJcbiAgICB7XHJcbiAgICAgIHR5cGU6ICdudW1iZXInLFxyXG4gICAgICBuYW1lOiAnYWNxdWlyZVRpbWVvdXQnLFxyXG4gICAgICBtZXNzYWdlOiAnVGhlIG51bWJlciBvZiBtaWxsaXNlY29uZHMgdG8gd2FpdCBmb3IgYWNxdWlyaW5nIGEgcmVzb3VyY2UnLFxyXG4gICAgICBpbml0aWFsOiBkZWZhdWx0Q29uZmlnLnBvb2wuYWNxdWlyZVRpbWVvdXQudmFsdWVcclxuICAgIH0sXHJcbiAgICB7XHJcbiAgICAgIHR5cGU6ICdudW1iZXInLFxyXG4gICAgICBuYW1lOiAnY3JlYXRlVGltZW91dCcsXHJcbiAgICAgIG1lc3NhZ2U6ICdUaGUgbnVtYmVyIG9mIG1pbGxpc2Vjb25kcyB0byB3YWl0IGZvciBjcmVhdGluZyBhIHJlc291cmNlJyxcclxuICAgICAgaW5pdGlhbDogZGVmYXVsdENvbmZpZy5wb29sLmNyZWF0ZVRpbWVvdXQudmFsdWVcclxuICAgIH0sXHJcbiAgICB7XHJcbiAgICAgIHR5cGU6ICdudW1iZXInLFxyXG4gICAgICBuYW1lOiAnZGVzdHJveVRpbWVvdXQnLFxyXG4gICAgICBtZXNzYWdlOiAnVGhlIG51bWJlciBvZiBtaWxsaXNlY29uZHMgdG8gd2FpdCBmb3IgZGVzdHJveWluZyBhIHJlc291cmNlJyxcclxuICAgICAgaW5pdGlhbDogZGVmYXVsdENvbmZpZy5wb29sLmRlc3Ryb3lUaW1lb3V0LnZhbHVlXHJcbiAgICB9LFxyXG4gICAge1xyXG4gICAgICB0eXBlOiAnbnVtYmVyJyxcclxuICAgICAgbmFtZTogJ2lkbGVUaW1lb3V0JyxcclxuICAgICAgbWVzc2FnZTogJ1RoZSBudW1iZXIgb2YgbWlsbGlzZWNvbmRzIGFmdGVyIGFuIGlkbGUgcmVzb3VyY2UgaXMgZGVzdHJveWVkJyxcclxuICAgICAgaW5pdGlhbDogZGVmYXVsdENvbmZpZy5wb29sLmlkbGVUaW1lb3V0LnZhbHVlXHJcbiAgICB9LFxyXG4gICAge1xyXG4gICAgICB0eXBlOiAnbnVtYmVyJyxcclxuICAgICAgbmFtZTogJ2NyZWF0ZVJldHJ5SW50ZXJ2YWwnLFxyXG4gICAgICBtZXNzYWdlOlxyXG4gICAgICAgICdUaGUgcmV0cnkgaW50ZXJ2YWwgaW4gbWlsbGlzZWNvbmRzIGFmdGVyIGEgY3JlYXRlIHByb2Nlc3MgZmFpbHMnLFxyXG4gICAgICBpbml0aWFsOiBkZWZhdWx0Q29uZmlnLnBvb2wuY3JlYXRlUmV0cnlJbnRlcnZhbC52YWx1ZVxyXG4gICAgfSxcclxuICAgIHtcclxuICAgICAgdHlwZTogJ251bWJlcicsXHJcbiAgICAgIG5hbWU6ICdyZWFwZXJJbnRlcnZhbCcsXHJcbiAgICAgIG1lc3NhZ2U6XHJcbiAgICAgICAgJ1RoZSByZWFwZXIgaW50ZXJ2YWwgaW4gbWlsbGlzZWNvbmRzIGFmdGVyIHRyaWdnZXJpbmcgdGhlIGNoZWNrIGZvciBpZGxlIHJlc291cmNlcyB0byBkZXN0cm95JyxcclxuICAgICAgaW5pdGlhbDogZGVmYXVsdENvbmZpZy5wb29sLnJlYXBlckludGVydmFsLnZhbHVlXHJcbiAgICB9LFxyXG4gICAge1xyXG4gICAgICB0eXBlOiAndG9nZ2xlJyxcclxuICAgICAgbmFtZTogJ2JlbmNobWFya2luZycsXHJcbiAgICAgIG1lc3NhZ2U6ICdFbmFibGUgYmVuY2htYXJraW5nIGZvciBhIHJlc291cmNlIHBvb2wnLFxyXG4gICAgICBpbml0aWFsOiBkZWZhdWx0Q29uZmlnLnBvb2wuYmVuY2htYXJraW5nLnZhbHVlXHJcbiAgICB9XHJcbiAgXSxcclxuICBsb2dnaW5nOiBbXHJcbiAgICB7XHJcbiAgICAgIHR5cGU6ICdudW1iZXInLFxyXG4gICAgICBuYW1lOiAnbGV2ZWwnLFxyXG4gICAgICBtZXNzYWdlOlxyXG4gICAgICAgICdUaGUgbG9nIGxldmVsICgwOiBzaWxlbnQsIDE6IGVycm9yLCAyOiB3YXJuaW5nLCAzOiBub3RpY2UsIDQ6IHZlcmJvc2UsIDU6IGJlbmNobWFyayknLFxyXG4gICAgICBpbml0aWFsOiBkZWZhdWx0Q29uZmlnLmxvZ2dpbmcubGV2ZWwudmFsdWUsXHJcbiAgICAgIHJvdW5kOiAwLFxyXG4gICAgICBtaW46IDAsXHJcbiAgICAgIG1heDogNVxyXG4gICAgfSxcclxuICAgIHtcclxuICAgICAgdHlwZTogJ3RleHQnLFxyXG4gICAgICBuYW1lOiAnZmlsZScsXHJcbiAgICAgIG1lc3NhZ2U6ICdBIGxvZyBmaWxlIG5hbWUuIFNldCB3aXRoIHRoZSAtLWxvZ0Rlc3QgdG8gZW5hYmxlIGZpbGUgbG9nZ2luZycsXHJcbiAgICAgIGluaXRpYWw6IGRlZmF1bHRDb25maWcubG9nZ2luZy5maWxlLnZhbHVlXHJcbiAgICB9LFxyXG4gICAge1xyXG4gICAgICB0eXBlOiAndGV4dCcsXHJcbiAgICAgIG5hbWU6ICdkZXN0JyxcclxuICAgICAgbWVzc2FnZTogJ1RoZSBwYXRoIHRvIGxvZyBmaWxlcy4gRW5hYmxlcyBmaWxlIGxvZ2dpbmcnLFxyXG4gICAgICBpbml0aWFsOiBkZWZhdWx0Q29uZmlnLmxvZ2dpbmcuZGVzdC52YWx1ZVxyXG4gICAgfVxyXG4gIF0sXHJcbiAgdWk6IFtcclxuICAgIHtcclxuICAgICAgdHlwZTogJ3RvZ2dsZScsXHJcbiAgICAgIG5hbWU6ICdlbmFibGUnLFxyXG4gICAgICBtZXNzYWdlOiAnRW5hYmxlIFVJIGZvciB0aGUgZXhwb3J0IHNlcnZlcicsXHJcbiAgICAgIGluaXRpYWw6IGRlZmF1bHRDb25maWcudWkuZW5hYmxlLnZhbHVlXHJcbiAgICB9LFxyXG4gICAge1xyXG4gICAgICB0eXBlOiAndGV4dCcsXHJcbiAgICAgIG5hbWU6ICdyb3V0ZScsXHJcbiAgICAgIG1lc3NhZ2U6ICdBIHJvdXRlIHRvIGF0dGFjaCB0aGUgVUknLFxyXG4gICAgICBpbml0aWFsOiBkZWZhdWx0Q29uZmlnLnVpLnJvdXRlLnZhbHVlXHJcbiAgICB9XHJcbiAgXSxcclxuICBvdGhlcjogW1xyXG4gICAge1xyXG4gICAgICB0eXBlOiAndGV4dCcsXHJcbiAgICAgIG5hbWU6ICdub2RlRW52JyxcclxuICAgICAgbWVzc2FnZTogJ1RoZSB0eXBlIG9mIE5vZGUuanMgZW52aXJvbm1lbnQnLFxyXG4gICAgICBpbml0aWFsOiBkZWZhdWx0Q29uZmlnLm90aGVyLm5vZGVFbnYudmFsdWVcclxuICAgIH0sXHJcbiAgICB7XHJcbiAgICAgIHR5cGU6ICd0b2dnbGUnLFxyXG4gICAgICBuYW1lOiAnbGlzdGVuVG9Qcm9jZXNzRXhpdHMnLFxyXG4gICAgICBtZXNzYWdlOiAnU2V0IHRvIGZhbHNlIHRvIHNraXAgYXR0YWNoaW5nIHByb2Nlc3MuZXhpdCBoYW5kbGVycycsXHJcbiAgICAgIGluaXRpYWw6IGRlZmF1bHRDb25maWcub3RoZXIubGlzdGVuVG9Qcm9jZXNzRXhpdHMudmFsdWVcclxuICAgIH0sXHJcbiAgICB7XHJcbiAgICAgIHR5cGU6ICd0b2dnbGUnLFxyXG4gICAgICBuYW1lOiAnbm9Mb2dvJyxcclxuICAgICAgbWVzc2FnZTogJ1NraXAgcHJpbnRpbmcgdGhlIGxvZ28gb24gc3RhcnR1cC4gUmVwbGFjZWQgYnkgc2ltcGxlIHRleHQnLFxyXG4gICAgICBpbml0aWFsOiBkZWZhdWx0Q29uZmlnLm90aGVyLm5vTG9nby52YWx1ZVxyXG4gICAgfSxcclxuICAgIHtcclxuICAgICAgdHlwZTogJ3RvZ2dsZScsXHJcbiAgICAgIG5hbWU6ICdoYXJkUmVzZXRQYWdlJyxcclxuICAgICAgbWVzc2FnZTogJ0RlY2lkZXMgaWYgdGhlIHBhZ2UgY29udGVudCBzaG91bGQgYmUgcmVzZXQgZW50aXJlbHknLFxyXG4gICAgICBpbml0aWFsOiBkZWZhdWx0Q29uZmlnLm90aGVyLmhhcmRSZXNldFBhZ2UudmFsdWVcclxuICAgIH1cclxuICBdLFxyXG4gIGRlYnVnOiBbXHJcbiAgICB7XHJcbiAgICAgIHR5cGU6ICd0b2dnbGUnLFxyXG4gICAgICBuYW1lOiAnZW5hYmxlJyxcclxuICAgICAgbWVzc2FnZTogJ0VuYWJsZXMgZGVidWcgbW9kZSBmb3IgdGhlIGJyb3dzZXIgaW5zdGFuY2UnLFxyXG4gICAgICBpbml0aWFsOiBkZWZhdWx0Q29uZmlnLmRlYnVnLmVuYWJsZS52YWx1ZVxyXG4gICAgfSxcclxuICAgIHtcclxuICAgICAgdHlwZTogJ3RvZ2dsZScsXHJcbiAgICAgIG5hbWU6ICdoZWFkbGVzcycsXHJcbiAgICAgIG1lc3NhZ2U6ICdUaGUgbW9kZSBzZXR0aW5nIGZvciB0aGUgYnJvd3NlcicsXHJcbiAgICAgIGluaXRpYWw6IGRlZmF1bHRDb25maWcuZGVidWcuaGVhZGxlc3MudmFsdWVcclxuICAgIH0sXHJcbiAgICB7XHJcbiAgICAgIHR5cGU6ICd0b2dnbGUnLFxyXG4gICAgICBuYW1lOiAnZGV2dG9vbHMnLFxyXG4gICAgICBtZXNzYWdlOiAnVGhlIERldlRvb2xzIGZvciB0aGUgaGVhZGZ1bCBicm93c2VyJyxcclxuICAgICAgaW5pdGlhbDogZGVmYXVsdENvbmZpZy5kZWJ1Zy5kZXZ0b29scy52YWx1ZVxyXG4gICAgfSxcclxuICAgIHtcclxuICAgICAgdHlwZTogJ3RvZ2dsZScsXHJcbiAgICAgIG5hbWU6ICdsaXN0ZW5Ub0NvbnNvbGUnLFxyXG4gICAgICBtZXNzYWdlOiAnVGhlIGV2ZW50IGxpc3RlbmVyIGZvciBjb25zb2xlIG1lc3NhZ2VzIGZyb20gdGhlIGJyb3dzZXInLFxyXG4gICAgICBpbml0aWFsOiBkZWZhdWx0Q29uZmlnLmRlYnVnLmxpc3RlblRvQ29uc29sZS52YWx1ZVxyXG4gICAgfSxcclxuICAgIHtcclxuICAgICAgdHlwZTogJ3RvZ2dsZScsXHJcbiAgICAgIG5hbWU6ICdkdW1waW8nLFxyXG4gICAgICBtZXNzYWdlOiAnUmVkaXJlY3RzIHRoZSBicm93c2VyIHN0ZG91dCBhbmQgc3RkZXJyIHRvIE5vZGVKUyBwcm9jZXNzJyxcclxuICAgICAgaW5pdGlhbDogZGVmYXVsdENvbmZpZy5kZWJ1Zy5kdW1waW8udmFsdWVcclxuICAgIH0sXHJcbiAgICB7XHJcbiAgICAgIHR5cGU6ICdudW1iZXInLFxyXG4gICAgICBuYW1lOiAnc2xvd01vJyxcclxuICAgICAgbWVzc2FnZTogJ1B1cHBldGVlciBvcGVyYXRpb25zIHNsb3cgZG93biBpbiBtaWxsaXNlY29uZHMnLFxyXG4gICAgICBpbml0aWFsOiBkZWZhdWx0Q29uZmlnLmRlYnVnLnNsb3dNby52YWx1ZVxyXG4gICAgfSxcclxuICAgIHtcclxuICAgICAgdHlwZTogJ251bWJlcicsXHJcbiAgICAgIG5hbWU6ICdkZWJ1Z2dpbmdQb3J0JyxcclxuICAgICAgbWVzc2FnZTogJ1RoZSBwb3J0IG51bWJlciBmb3IgZGVidWdnaW5nJyxcclxuICAgICAgaW5pdGlhbDogZGVmYXVsdENvbmZpZy5kZWJ1Zy5kZWJ1Z2dpbmdQb3J0LnZhbHVlXHJcbiAgICB9XHJcbiAgXVxyXG59O1xyXG5cclxuLy8gQWJzb2x1dGUgcHJvcHMgdGhhdCwgaW4gY2FzZSBvZiBtZXJnaW5nIHJlY3Vyc2l2ZWx5LCBuZWVkIHRvIGJlIGZvcmNlIG1lcmdlZFxyXG5leHBvcnQgY29uc3QgYWJzb2x1dGVQcm9wcyA9IFtcclxuICAnb3B0aW9ucycsXHJcbiAgJ2dsb2JhbE9wdGlvbnMnLFxyXG4gICd0aGVtZU9wdGlvbnMnLFxyXG4gICdyZXNvdXJjZXMnLFxyXG4gICdwYXlsb2FkJ1xyXG5dO1xyXG5cclxuLy8gQXJndW1lbnQgbmVzdGluZyBsZXZlbCBvZiBhbGwgZXhwb3J0IHNlcnZlciBvcHRpb25zXHJcbmV4cG9ydCBjb25zdCBuZXN0ZWRBcmdzID0ge307XHJcblxyXG4vKipcclxuICogUmVjdXJzaXZlbHkgY3JlYXRlcyBhIGNoYWluIG9mIG5lc3RlZCBhcmd1bWVudHMgZnJvbSBhbiBvYmplY3QuXHJcbiAqXHJcbiAqIEBwYXJhbSB7T2JqZWN0fSBvYmogLSBUaGUgb2JqZWN0IGNvbnRhaW5pbmcgbmVzdGVkIGFyZ3VtZW50cy5cclxuICogQHBhcmFtIHtzdHJpbmd9IHByb3BDaGFpbiAtIFRoZSBjdXJyZW50IGNoYWluIG9mIG5lc3RlZCBwcm9wZXJ0aWVzXHJcbiAqICh1c2VkIGludGVybmFsbHkgZHVyaW5nIHJlY3Vyc2lvbikuXHJcbiAqL1xyXG5jb25zdCBjcmVhdGVOZXN0ZWRBcmdzID0gKG9iaiwgcHJvcENoYWluID0gJycpID0+IHtcclxuICBPYmplY3Qua2V5cyhvYmopLmZvckVhY2goKGspID0+IHtcclxuICAgIGlmICghWydwdXBwZXRlZXInLCAnaGlnaGNoYXJ0cyddLmluY2x1ZGVzKGspKSB7XHJcbiAgICAgIGNvbnN0IGVudHJ5ID0gb2JqW2tdO1xyXG4gICAgICBpZiAodHlwZW9mIGVudHJ5LnZhbHVlID09PSAndW5kZWZpbmVkJykge1xyXG4gICAgICAgIC8vIEdvIGRlZXBlciBpbiB0aGUgbmVzdGVkIGFyZ3VtZW50c1xyXG4gICAgICAgIGNyZWF0ZU5lc3RlZEFyZ3MoZW50cnksIGAke3Byb3BDaGFpbn0uJHtrfWApO1xyXG4gICAgICB9IGVsc2Uge1xyXG4gICAgICAgIC8vIENyZWF0ZSB0aGUgY2hhaW4gb2YgbmVzdGVkIGFyZ3VtZW50c1xyXG4gICAgICAgIG5lc3RlZEFyZ3NbZW50cnkuY2xpTmFtZSB8fCBrXSA9IGAke3Byb3BDaGFpbn0uJHtrfWAuc3Vic3RyaW5nKDEpO1xyXG5cclxuICAgICAgICAvLyBTdXBwb3J0IGZvciB0aGUgbGVnYWN5LCBQaGFudG9tSlMgcHJvcGVydGllcyBuYW1lc1xyXG4gICAgICAgIGlmIChlbnRyeS5sZWdhY3lOYW1lICE9PSB1bmRlZmluZWQpIHtcclxuICAgICAgICAgIG5lc3RlZEFyZ3NbZW50cnkubGVnYWN5TmFtZV0gPSBgJHtwcm9wQ2hhaW59LiR7a31gLnN1YnN0cmluZygxKTtcclxuICAgICAgICB9XHJcbiAgICAgIH1cclxuICAgIH1cclxuICB9KTtcclxufTtcclxuXHJcbmNyZWF0ZU5lc3RlZEFyZ3MoZGVmYXVsdENvbmZpZyk7XHJcbiIsIi8qKlxyXG4gKiBAZmlsZW92ZXJ2aWV3XHJcbiAqIFRoaXMgZmlsZSBpcyByZXNwb25zaWJsZSBmb3IgcGFyc2luZyB0aGUgZW52aXJvbm1lbnQgdmFyaWFibGVzIHdpdGggdGhlICd6b2QnXHJcbiAqIGxpYnJhcnkuIFRoZSBwYXJzZWQgZW52aXJvbm1lbnQgdmFyaWFibGVzIGFyZSB0aGVuIGV4cG9ydGVkIHRvIGJlIHVzZWRcclxuICogaW4gdGhlIGFwcGxpY2F0aW9uIGFzIFwiZW52c1wiLiBXZSBzaG91bGQgbm90IHVzZSBwcm9jZXNzLmVudiBkaXJlY3RseVxyXG4gKiBpbiB0aGUgYXBwbGljYXRpb24gYXMgdGhlc2Ugd291bGQgbm90IGJlIHBhcnNlZCBwcm9wZXJseS5cclxuICpcclxuICogVGhlIGVudmlyb25tZW50IHZhcmlhYmxlcyBhcmUgcGFyc2VkIGFuZCB2YWxpZGF0ZWQgb25seSBvbmNlIHdoZW5cclxuICogdGhlIGFwcGxpY2F0aW9uIHN0YXJ0cy4gV2Ugc2hvdWxkIHdyaXRlIGEgY3VzdG9tIHZhbGlkYXRvciBvciBhIHRyYW5zZm9ybWVyXHJcbiAqIGZvciBlYWNoIG9mIHRoZSBvcHRpb25zLlxyXG4gKi9cclxuXHJcbmltcG9ydCBkb3RlbnYgZnJvbSAnZG90ZW52JztcclxuaW1wb3J0IHsgeiB9IGZyb20gJ3pvZCc7XHJcblxyXG5pbXBvcnQgeyBzY3JpcHRzTmFtZXMgfSBmcm9tICcuL3NjaGVtYXMvY29uZmlnLmpzJztcclxuXHJcbi8vIExvYWQgLmVudiBpbnRvIGVudmlyb25tZW50IHZhcmlhYmxlc1xyXG5kb3RlbnYuY29uZmlnKCk7XHJcblxyXG4vLyBPYmplY3Qgd2l0aCBjdXN0b20gdmFsaWRhdG9ycyBhbmQgdHJhbnNmb3JtZXJzLCB0byBhdm9pZCByZXBldGl0aW9uXHJcbi8vIGluIHRoZSBDb25maWcgb2JqZWN0XHJcbmNvbnN0IHYgPSB7XHJcbiAgLy8gU3BsaXRzIHN0cmluZyB2YWx1ZSBpbnRvIGVsZW1lbnRzIGluIGFuIGFycmF5LCB0cmltcyBldmVyeSBlbGVtZW50LCBjaGVja3NcclxuICAvLyBpZiBhbiBhcnJheSBpcyBjb3JyZWN0LCBpZiBpdCBpcyBlbXB0eSwgYW5kIGlmIGl0IGlzLCByZXR1cm5zIHVuZGVmaW5lZFxyXG4gIGFycmF5OiAoZmlsdGVyQXJyYXkpID0+XHJcbiAgICB6XHJcbiAgICAgIC5zdHJpbmcoKVxyXG4gICAgICAudHJhbnNmb3JtKCh2YWx1ZSkgPT5cclxuICAgICAgICB2YWx1ZVxyXG4gICAgICAgICAgLnNwbGl0KCcsJylcclxuICAgICAgICAgIC5tYXAoKHZhbHVlKSA9PiB2YWx1ZS50cmltKCkpXHJcbiAgICAgICAgICAuZmlsdGVyKCh2YWx1ZSkgPT4gZmlsdGVyQXJyYXkuaW5jbHVkZXModmFsdWUpKVxyXG4gICAgICApXHJcbiAgICAgIC50cmFuc2Zvcm0oKHZhbHVlKSA9PiAodmFsdWUubGVuZ3RoID8gdmFsdWUgOiB1bmRlZmluZWQpKSxcclxuXHJcbiAgLy8gQWxsb3dzIG9ubHkgdHJ1ZSwgZmFsc2UgYW5kIGNvcnJlY3RseSBwYXJzZSB0aGUgdmFsdWUgdG8gYm9vbGVhblxyXG4gIC8vIG9yIG5vIHZhbHVlIGluIHdoaWNoIGNhc2UgdGhlIHJldHVybmVkIHZhbHVlIHdpbGwgYmUgdW5kZWZpbmVkXHJcbiAgYm9vbGVhbjogKCkgPT5cclxuICAgIHpcclxuICAgICAgLmVudW0oWyd0cnVlJywgJ2ZhbHNlJywgJyddKVxyXG4gICAgICAudHJhbnNmb3JtKCh2YWx1ZSkgPT4gKHZhbHVlICE9PSAnJyA/IHZhbHVlID09PSAndHJ1ZScgOiB1bmRlZmluZWQpKSxcclxuXHJcbiAgLy8gQWxsb3dzIHBhc3NlZCB2YWx1ZXMgb3Igbm8gdmFsdWUgaW4gd2hpY2ggY2FzZSB0aGUgcmV0dXJuZWQgdmFsdWUgd2lsbFxyXG4gIC8vIGJlIHVuZGVmaW5lZFxyXG4gIGVudW06ICh2YWx1ZXMpID0+XHJcbiAgICB6XHJcbiAgICAgIC5lbnVtKFsuLi52YWx1ZXMsICcnXSlcclxuICAgICAgLnRyYW5zZm9ybSgodmFsdWUpID0+ICh2YWx1ZSAhPT0gJycgPyB2YWx1ZSA6IHVuZGVmaW5lZCkpLFxyXG5cclxuICAvLyBUcmltcyB0aGUgc3RyaW5nIHZhbHVlIGFuZCBjaGVja3MgaWYgaXQgaXMgZW1wdHkgb3IgY29udGFpbnMgc3RyaW5naWZpZWRcclxuICAvLyB2YWx1ZXMgc3VjaCBhcyBmYWxzZSwgdW5kZWZpbmVkLCBudWxsLCBOYU4sIGlmIGl0IGRvZXMsIHJldHVybnMgdW5kZWZpbmVkXHJcbiAgc3RyaW5nOiAoKSA9PlxyXG4gICAgelxyXG4gICAgICAuc3RyaW5nKClcclxuICAgICAgLnRyaW0oKVxyXG4gICAgICAucmVmaW5lKFxyXG4gICAgICAgICh2YWx1ZSkgPT5cclxuICAgICAgICAgICFbJ2ZhbHNlJywgJ3VuZGVmaW5lZCcsICdudWxsJywgJ05hTiddLmluY2x1ZGVzKHZhbHVlKSB8fFxyXG4gICAgICAgICAgdmFsdWUgPT09ICcnLFxyXG4gICAgICAgICh2YWx1ZSkgPT4gKHtcclxuICAgICAgICAgIG1lc3NhZ2U6IGBUaGUgc3RyaW5nIGNvbnRhaW5zIGZvcmJpZGRlbiB2YWx1ZXMsIHJlY2VpdmVkICcke3ZhbHVlfSdgXHJcbiAgICAgICAgfSlcclxuICAgICAgKVxyXG4gICAgICAudHJhbnNmb3JtKCh2YWx1ZSkgPT4gKHZhbHVlICE9PSAnJyA/IHZhbHVlIDogdW5kZWZpbmVkKSksXHJcblxyXG4gIC8vIEFsbG93cyBwb3NpdGl2ZSBudW1iZXJzIG9yIG5vIHZhbHVlIGluIHdoaWNoIGNhc2UgdGhlIHJldHVybmVkIHZhbHVlIHdpbGxcclxuICAvLyBiZSB1bmRlZmluZWRcclxuICBwb3NpdGl2ZU51bTogKCkgPT5cclxuICAgIHpcclxuICAgICAgLnN0cmluZygpXHJcbiAgICAgIC50cmltKClcclxuICAgICAgLnJlZmluZShcclxuICAgICAgICAodmFsdWUpID0+XHJcbiAgICAgICAgICB2YWx1ZSA9PT0gJycgfHwgKCFpc05hTihwYXJzZUZsb2F0KHZhbHVlKSkgJiYgcGFyc2VGbG9hdCh2YWx1ZSkgPiAwKSxcclxuICAgICAgICAodmFsdWUpID0+ICh7XHJcbiAgICAgICAgICBtZXNzYWdlOiBgVGhlIHZhbHVlIG11c3QgYmUgbnVtZXJpYyBhbmQgcG9zaXRpdmUsIHJlY2VpdmVkICcke3ZhbHVlfSdgXHJcbiAgICAgICAgfSlcclxuICAgICAgKVxyXG4gICAgICAudHJhbnNmb3JtKCh2YWx1ZSkgPT4gKHZhbHVlICE9PSAnJyA/IHBhcnNlRmxvYXQodmFsdWUpIDogdW5kZWZpbmVkKSksXHJcblxyXG4gIC8vIEFsbG93cyBub24tbmVnYXRpdmUgbnVtYmVycyBvciBubyB2YWx1ZSBpbiB3aGljaCBjYXNlIHRoZSByZXR1cm5lZCB2YWx1ZVxyXG4gIC8vIHdpbGwgYmUgdW5kZWZpbmVkXHJcbiAgbm9uTmVnYXRpdmVOdW06ICgpID0+XHJcbiAgICB6XHJcbiAgICAgIC5zdHJpbmcoKVxyXG4gICAgICAudHJpbSgpXHJcbiAgICAgIC5yZWZpbmUoXHJcbiAgICAgICAgKHZhbHVlKSA9PlxyXG4gICAgICAgICAgdmFsdWUgPT09ICcnIHx8ICghaXNOYU4ocGFyc2VGbG9hdCh2YWx1ZSkpICYmIHBhcnNlRmxvYXQodmFsdWUpID49IDApLFxyXG4gICAgICAgICh2YWx1ZSkgPT4gKHtcclxuICAgICAgICAgIG1lc3NhZ2U6IGBUaGUgdmFsdWUgbXVzdCBiZSBudW1lcmljIGFuZCBub24tbmVnYXRpdmUsIHJlY2VpdmVkICcke3ZhbHVlfSdgXHJcbiAgICAgICAgfSlcclxuICAgICAgKVxyXG4gICAgICAudHJhbnNmb3JtKCh2YWx1ZSkgPT4gKHZhbHVlICE9PSAnJyA/IHBhcnNlRmxvYXQodmFsdWUpIDogdW5kZWZpbmVkKSlcclxufTtcclxuXHJcbmV4cG9ydCBjb25zdCBDb25maWcgPSB6Lm9iamVjdCh7XHJcbiAgLy8gaGlnaGNoYXJ0c1xyXG4gIEhJR0hDSEFSVFNfVkVSU0lPTjogelxyXG4gICAgLnN0cmluZygpXHJcbiAgICAudHJpbSgpXHJcbiAgICAucmVmaW5lKFxyXG4gICAgICAodmFsdWUpID0+IC9eKGxhdGVzdHxcXGQrKFxcLlxcZCspezAsMn0pJC8udGVzdCh2YWx1ZSkgfHwgdmFsdWUgPT09ICcnLFxyXG4gICAgICAodmFsdWUpID0+ICh7XHJcbiAgICAgICAgbWVzc2FnZTogYEhJR0hDSEFSVFNfVkVSU0lPTiBtdXN0IGJlICdsYXRlc3QnLCBhIG1ham9yIHZlcnNpb24sIG9yIGluIHRoZSBmb3JtIFhYLllZLlpaLCByZWNlaXZlZCAnJHt2YWx1ZX0nYFxyXG4gICAgICB9KVxyXG4gICAgKVxyXG4gICAgLnRyYW5zZm9ybSgodmFsdWUpID0+ICh2YWx1ZSAhPT0gJycgPyB2YWx1ZSA6IHVuZGVmaW5lZCkpLFxyXG4gIEhJR0hDSEFSVFNfQ0ROX1VSTDogelxyXG4gICAgLnN0cmluZygpXHJcbiAgICAudHJpbSgpXHJcbiAgICAucmVmaW5lKFxyXG4gICAgICAodmFsdWUpID0+XHJcbiAgICAgICAgdmFsdWUuc3RhcnRzV2l0aCgnaHR0cHM6Ly8nKSB8fFxyXG4gICAgICAgIHZhbHVlLnN0YXJ0c1dpdGgoJ2h0dHA6Ly8nKSB8fFxyXG4gICAgICAgIHZhbHVlID09PSAnJyxcclxuICAgICAgKHZhbHVlKSA9PiAoe1xyXG4gICAgICAgIG1lc3NhZ2U6IGBJbnZhbGlkIHZhbHVlIGZvciBISUdIQ0hBUlRTX0NETl9VUkwuIEl0IHNob3VsZCBzdGFydCB3aXRoIGh0dHA6Ly8gb3IgaHR0cHM6Ly8sIHJlY2VpdmVkICcke3ZhbHVlfSdgXHJcbiAgICAgIH0pXHJcbiAgICApXHJcbiAgICAudHJhbnNmb3JtKCh2YWx1ZSkgPT4gKHZhbHVlICE9PSAnJyA/IHZhbHVlIDogdW5kZWZpbmVkKSksXHJcbiAgSElHSENIQVJUU19DT1JFX1NDUklQVFM6IHYuYXJyYXkoc2NyaXB0c05hbWVzLmNvcmUpLFxyXG4gIEhJR0hDSEFSVFNfTU9EVUxFX1NDUklQVFM6IHYuYXJyYXkoc2NyaXB0c05hbWVzLm1vZHVsZXMpLFxyXG4gIEhJR0hDSEFSVFNfSU5ESUNBVE9SX1NDUklQVFM6IHYuYXJyYXkoc2NyaXB0c05hbWVzLmluZGljYXRvcnMpLFxyXG4gIEhJR0hDSEFSVFNfRk9SQ0VfRkVUQ0g6IHYuYm9vbGVhbigpLFxyXG4gIEhJR0hDSEFSVFNfQ0FDSEVfUEFUSDogdi5zdHJpbmcoKSxcclxuICBISUdIQ0hBUlRTX0FETUlOX1RPS0VOOiB2LnN0cmluZygpLFxyXG5cclxuICAvLyBleHBvcnRcclxuICBFWFBPUlRfVFlQRTogdi5lbnVtKFsnanBlZycsICdwbmcnLCAncGRmJywgJ3N2ZyddKSxcclxuICBFWFBPUlRfQ09OU1RSOiB2LmVudW0oWydjaGFydCcsICdzdG9ja0NoYXJ0JywgJ21hcENoYXJ0JywgJ2dhbnR0Q2hhcnQnXSksXHJcbiAgRVhQT1JUX0RFRkFVTFRfSEVJR0hUOiB2LnBvc2l0aXZlTnVtKCksXHJcbiAgRVhQT1JUX0RFRkFVTFRfV0lEVEg6IHYucG9zaXRpdmVOdW0oKSxcclxuICBFWFBPUlRfREVGQVVMVF9TQ0FMRTogdi5wb3NpdGl2ZU51bSgpLFxyXG4gIEVYUE9SVF9SQVNURVJJWkFUSU9OX1RJTUVPVVQ6IHYubm9uTmVnYXRpdmVOdW0oKSxcclxuXHJcbiAgLy8gY3VzdG9tXHJcbiAgQ1VTVE9NX0xPR0lDX0FMTE9XX0NPREVfRVhFQ1VUSU9OOiB2LmJvb2xlYW4oKSxcclxuICBDVVNUT01fTE9HSUNfQUxMT1dfRklMRV9SRVNPVVJDRVM6IHYuYm9vbGVhbigpLFxyXG5cclxuICAvLyBzZXJ2ZXJcclxuICBTRVJWRVJfRU5BQkxFOiB2LmJvb2xlYW4oKSxcclxuICBTRVJWRVJfSE9TVDogdi5zdHJpbmcoKSxcclxuICBTRVJWRVJfUE9SVDogdi5wb3NpdGl2ZU51bSgpLFxyXG4gIFNFUlZFUl9CRU5DSE1BUktJTkc6IHYuYm9vbGVhbigpLFxyXG5cclxuICAvLyBzZXJ2ZXIgcHJveHlcclxuICBTRVJWRVJfUFJPWFlfSE9TVDogdi5zdHJpbmcoKSxcclxuICBTRVJWRVJfUFJPWFlfUE9SVDogdi5wb3NpdGl2ZU51bSgpLFxyXG4gIFNFUlZFUl9QUk9YWV9USU1FT1VUOiB2Lm5vbk5lZ2F0aXZlTnVtKCksXHJcblxyXG4gIC8vIHNlcnZlciByYXRlIGxpbWl0aW5nXHJcbiAgU0VSVkVSX1JBVEVfTElNSVRJTkdfRU5BQkxFOiB2LmJvb2xlYW4oKSxcclxuICBTRVJWRVJfUkFURV9MSU1JVElOR19NQVhfUkVRVUVTVFM6IHYubm9uTmVnYXRpdmVOdW0oKSxcclxuICBTRVJWRVJfUkFURV9MSU1JVElOR19XSU5ET1c6IHYubm9uTmVnYXRpdmVOdW0oKSxcclxuICBTRVJWRVJfUkFURV9MSU1JVElOR19ERUxBWTogdi5ub25OZWdhdGl2ZU51bSgpLFxyXG4gIFNFUlZFUl9SQVRFX0xJTUlUSU5HX1RSVVNUX1BST1hZOiB2LmJvb2xlYW4oKSxcclxuICBTRVJWRVJfUkFURV9MSU1JVElOR19TS0lQX0tFWTogdi5zdHJpbmcoKSxcclxuICBTRVJWRVJfUkFURV9MSU1JVElOR19TS0lQX1RPS0VOOiB2LnN0cmluZygpLFxyXG5cclxuICAvLyBzZXJ2ZXIgc3NsXHJcbiAgU0VSVkVSX1NTTF9FTkFCTEU6IHYuYm9vbGVhbigpLFxyXG4gIFNFUlZFUl9TU0xfRk9SQ0U6IHYuYm9vbGVhbigpLFxyXG4gIFNFUlZFUl9TU0xfUE9SVDogdi5wb3NpdGl2ZU51bSgpLFxyXG4gIFNFUlZFUl9TU0xfQ0VSVF9QQVRIOiB2LnN0cmluZygpLFxyXG5cclxuICAvLyBwb29sXHJcbiAgUE9PTF9NSU5fV09SS0VSUzogdi5ub25OZWdhdGl2ZU51bSgpLFxyXG4gIFBPT0xfTUFYX1dPUktFUlM6IHYubm9uTmVnYXRpdmVOdW0oKSxcclxuICBQT09MX1dPUktfTElNSVQ6IHYucG9zaXRpdmVOdW0oKSxcclxuICBQT09MX0FDUVVJUkVfVElNRU9VVDogdi5ub25OZWdhdGl2ZU51bSgpLFxyXG4gIFBPT0xfQ1JFQVRFX1RJTUVPVVQ6IHYubm9uTmVnYXRpdmVOdW0oKSxcclxuICBQT09MX0RFU1RST1lfVElNRU9VVDogdi5ub25OZWdhdGl2ZU51bSgpLFxyXG4gIFBPT0xfSURMRV9USU1FT1VUOiB2Lm5vbk5lZ2F0aXZlTnVtKCksXHJcbiAgUE9PTF9DUkVBVEVfUkVUUllfSU5URVJWQUw6IHYubm9uTmVnYXRpdmVOdW0oKSxcclxuICBQT09MX1JFQVBFUl9JTlRFUlZBTDogdi5ub25OZWdhdGl2ZU51bSgpLFxyXG4gIFBPT0xfQkVOQ0hNQVJLSU5HOiB2LmJvb2xlYW4oKSxcclxuXHJcbiAgLy8gbG9nZ2VyXHJcbiAgTE9HR0lOR19MRVZFTDogelxyXG4gICAgLnN0cmluZygpXHJcbiAgICAudHJpbSgpXHJcbiAgICAucmVmaW5lKFxyXG4gICAgICAodmFsdWUpID0+XHJcbiAgICAgICAgdmFsdWUgPT09ICcnIHx8XHJcbiAgICAgICAgKCFpc05hTihwYXJzZUZsb2F0KHZhbHVlKSkgJiZcclxuICAgICAgICAgIHBhcnNlRmxvYXQodmFsdWUpID49IDAgJiZcclxuICAgICAgICAgIHBhcnNlRmxvYXQodmFsdWUpIDw9IDUpLFxyXG4gICAgICAodmFsdWUpID0+ICh7XHJcbiAgICAgICAgbWVzc2FnZTogYEludmFsaWQgdmFsdWUgZm9yIExPR0dJTkdfTEVWRUwuIFdlIG9ubHkgYWNjZXB0IHZhbHVlcyBmcm9tIDAgdG8gNSBhcyBsb2dnaW5nIGxldmVscywgcmVjZWl2ZWQgJyR7dmFsdWV9J2BcclxuICAgICAgfSlcclxuICAgIClcclxuICAgIC50cmFuc2Zvcm0oKHZhbHVlKSA9PiAodmFsdWUgIT09ICcnID8gcGFyc2VGbG9hdCh2YWx1ZSkgOiB1bmRlZmluZWQpKSxcclxuICBMT0dHSU5HX0ZJTEU6IHYuc3RyaW5nKCksXHJcbiAgTE9HR0lOR19ERVNUOiB2LnN0cmluZygpLFxyXG5cclxuICAvLyB1aVxyXG4gIFVJX0VOQUJMRTogdi5ib29sZWFuKCksXHJcbiAgVUlfUk9VVEU6IHYuc3RyaW5nKCksXHJcblxyXG4gIC8vIG90aGVyXHJcbiAgT1RIRVJfTk9ERV9FTlY6IHYuZW51bShbJ2RldmVsb3BtZW50JywgJ3Byb2R1Y3Rpb24nLCAndGVzdCddKSxcclxuICBPVEhFUl9MSVNURU5fVE9fUFJPQ0VTU19FWElUUzogdi5ib29sZWFuKCksXHJcbiAgT1RIRVJfTk9fTE9HTzogdi5ib29sZWFuKCksXHJcbiAgT1RIRVJfSEFSRF9SRVNFVF9QQUdFOiB2LmJvb2xlYW4oKSxcclxuXHJcbiAgLy8gZGVidWdnZXJcclxuICBERUJVR19FTkFCTEU6IHYuYm9vbGVhbigpLFxyXG4gIERFQlVHX0hFQURMRVNTOiB2LmJvb2xlYW4oKSxcclxuICBERUJVR19ERVZUT09MUzogdi5ib29sZWFuKCksXHJcbiAgREVCVUdfTElTVEVOX1RPX0NPTlNPTEU6IHYuYm9vbGVhbigpLFxyXG4gIERFQlVHX0RVTVBJTzogdi5ib29sZWFuKCksXHJcbiAgREVCVUdfU0xPV19NTzogdi5ub25OZWdhdGl2ZU51bSgpLFxyXG4gIERFQlVHX0RFQlVHR0lOR19QT1JUOiB2LnBvc2l0aXZlTnVtKClcclxufSk7XHJcblxyXG5leHBvcnQgY29uc3QgZW52cyA9IENvbmZpZy5wYXJ0aWFsKCkucGFyc2UocHJvY2Vzcy5lbnYpO1xyXG4iLCIvKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKlxyXG5cclxuSGlnaGNoYXJ0cyBFeHBvcnQgU2VydmVyXHJcblxyXG5Db3B5cmlnaHQgKGMpIDIwMTYtMjAyNCwgSGlnaHNvZnRcclxuXHJcbkxpY2VuY2VkIHVuZGVyIHRoZSBNSVQgbGljZW5jZS5cclxuXHJcbkFkZGl0aW9uYWxseSBhIHZhbGlkIEhpZ2hjaGFydHMgbGljZW5zZSBpcyByZXF1aXJlZCBmb3IgdXNlLlxyXG5cclxuU2VlIExJQ0VOU0UgZmlsZSBpbiByb290IGZvciBkZXRhaWxzLlxyXG5cclxuKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKi9cclxuXHJcbmltcG9ydCB7IGFwcGVuZEZpbGUsIGV4aXN0c1N5bmMsIG1rZGlyU3luYyB9IGZyb20gJ2ZzJztcclxuXHJcbmltcG9ydCB7IGRlZmF1bHRDb25maWcgfSBmcm9tICcuL3NjaGVtYXMvY29uZmlnLmpzJztcclxuXHJcbi8vIFRoZSBhdmFpbGFibGUgY29sb3JzXHJcbmNvbnN0IGNvbG9ycyA9IFsncmVkJywgJ3llbGxvdycsICdibHVlJywgJ2dyYXknLCAnZ3JlZW4nXTtcclxuXHJcbi8vIFRoZSBkZWZhdWx0IGxvZ2dpbmcgY29uZmlnXHJcbmxldCBsb2dnaW5nID0ge1xyXG4gIC8vIEZsYWdzIGZvciBsb2dnaW5nIHN0YXR1c1xyXG4gIHRvQ29uc29sZTogdHJ1ZSxcclxuICB0b0ZpbGU6IGZhbHNlLFxyXG4gIHBhdGhDcmVhdGVkOiBmYWxzZSxcclxuICAvLyBMb2cgbGV2ZWxzXHJcbiAgbGV2ZWxzRGVzYzogW1xyXG4gICAge1xyXG4gICAgICB0aXRsZTogJ2Vycm9yJyxcclxuICAgICAgY29sb3I6IGNvbG9yc1swXVxyXG4gICAgfSxcclxuICAgIHtcclxuICAgICAgdGl0bGU6ICd3YXJuaW5nJyxcclxuICAgICAgY29sb3I6IGNvbG9yc1sxXVxyXG4gICAgfSxcclxuICAgIHtcclxuICAgICAgdGl0bGU6ICdub3RpY2UnLFxyXG4gICAgICBjb2xvcjogY29sb3JzWzJdXHJcbiAgICB9LFxyXG4gICAge1xyXG4gICAgICB0aXRsZTogJ3ZlcmJvc2UnLFxyXG4gICAgICBjb2xvcjogY29sb3JzWzNdXHJcbiAgICB9LFxyXG4gICAge1xyXG4gICAgICB0aXRsZTogJ2JlbmNobWFyaycsXHJcbiAgICAgIGNvbG9yOiBjb2xvcnNbNF1cclxuICAgIH1cclxuICBdLFxyXG4gIC8vIExvZyBsaXN0ZW5lcnNcclxuICBsaXN0ZW5lcnM6IFtdXHJcbn07XHJcblxyXG4vLyBHYXRoZXIgaW5pdCBsb2dnaW5nIG9wdGlvbnNcclxuZm9yIChjb25zdCBba2V5LCBvcHRpb25dIG9mIE9iamVjdC5lbnRyaWVzKGRlZmF1bHRDb25maWcubG9nZ2luZykpIHtcclxuICBsb2dnaW5nW2tleV0gPSBvcHRpb24udmFsdWU7XHJcbn1cclxuXHJcbi8qKlxyXG4gKiBMb2dzIHRoZSBwcm92aWRlZCB0ZXh0cyB0byBhIGZpbGUsIGlmIGZpbGUgbG9nZ2luZyBpcyBlbmFibGVkLiBJdCBjcmVhdGVzXHJcbiAqIHRoZSBuZWNlc3NhcnkgZGlyZWN0b3J5IHN0cnVjdHVyZSBpZiBub3QgYWxyZWFkeSBjcmVhdGVkIGFuZCBhcHBlbmRzIHRoZVxyXG4gKiBjb250ZW50LCBpbmNsdWRpbmcgYW4gb3B0aW9uYWwgcHJlZml4LCB0byB0aGUgc3BlY2lmaWVkIGxvZyBmaWxlLlxyXG4gKlxyXG4gKiBAcGFyYW0ge3N0cmluZ1tdfSB0ZXh0cyAtIEFuIGFycmF5IG9mIHRleHRzIHRvIGJlIGxvZ2dlZC5cclxuICogQHBhcmFtIHtzdHJpbmd9IHByZWZpeCAtIEFuIG9wdGlvbmFsIHByZWZpeCB0byBiZSBhZGRlZCB0byBlYWNoIGxvZyBlbnRyeS5cclxuICovXHJcbmNvbnN0IGxvZ1RvRmlsZSA9ICh0ZXh0cywgcHJlZml4KSA9PiB7XHJcbiAgaWYgKGxvZ2dpbmcudG9GaWxlKSB7XHJcbiAgICBpZiAoIWxvZ2dpbmcucGF0aENyZWF0ZWQpIHtcclxuICAgICAgLy8gQ3JlYXRlIGlmIGRvZXMgbm90IGV4aXN0XHJcbiAgICAgICFleGlzdHNTeW5jKGxvZ2dpbmcuZGVzdCkgJiYgbWtkaXJTeW5jKGxvZ2dpbmcuZGVzdCk7XHJcblxyXG4gICAgICAvLyBXZSBub3cgYXNzdW1lIHRoZSBwYXRoIGlzIGF2YWlsYWJsZSwgZS5nLiBpdCdzIHRoZSByZXNwb25zaWJpbGl0eVxyXG4gICAgICAvLyBvZiB0aGUgdXNlciB0byBjcmVhdGUgdGhlIHBhdGggd2l0aCB0aGUgY29ycmVjdCBhY2Nlc3MgcmlnaHRzLlxyXG4gICAgICBsb2dnaW5nLnBhdGhDcmVhdGVkID0gdHJ1ZTtcclxuICAgIH1cclxuXHJcbiAgICAvLyBBZGQgdGhlIGNvbnRlbnQgdG8gYSBmaWxlXHJcbiAgICBhcHBlbmRGaWxlKFxyXG4gICAgICBgJHtsb2dnaW5nLmRlc3R9JHtsb2dnaW5nLmZpbGV9YCxcclxuICAgICAgW3ByZWZpeF0uY29uY2F0KHRleHRzKS5qb2luKCcgJykgKyAnXFxuJyxcclxuICAgICAgKGVycm9yKSA9PiB7XHJcbiAgICAgICAgaWYgKGVycm9yKSB7XHJcbiAgICAgICAgICBjb25zb2xlLmxvZyhgW2xvZ2dlcl0gVW5hYmxlIHRvIHdyaXRlIHRvIGxvZyBmaWxlOiAke2Vycm9yfWApO1xyXG4gICAgICAgICAgbG9nZ2luZy50b0ZpbGUgPSBmYWxzZTtcclxuICAgICAgICB9XHJcbiAgICAgIH1cclxuICAgICk7XHJcbiAgfVxyXG59O1xyXG5cclxuLyoqXHJcbiAqIExvZ3MgYSBtZXNzYWdlLiBBY2NlcHRzIGEgdmFyaWFibGUgYW1vdW50IG9mIGFyZ3VtZW50cy4gQXJndW1lbnRzIGFmdGVyXHJcbiAqIGBsZXZlbGAgd2lsbCBiZSBwYXNzZWQgZGlyZWN0bHkgdG8gY29uc29sZS5sb2csIGFuZC9vciB3aWxsIGJlIGpvaW5lZFxyXG4gKiBhbmQgYXBwZW5kZWQgdG8gdGhlIGxvZyBmaWxlLlxyXG4gKlxyXG4gKiBAcGFyYW0ge2FueX0gYXJncyAtIEFuIGFycmF5IG9mIGFyZ3VtZW50cyB3aGVyZSB0aGUgZmlyc3QgaXMgdGhlIGxvZyBsZXZlbFxyXG4gKiBhbmQgdGhlIHJlc3QgYXJlIHN0cmluZ3MgdG8gYnVpbGQgYSBtZXNzYWdlIHdpdGguXHJcbiAqL1xyXG5leHBvcnQgY29uc3QgbG9nID0gKC4uLmFyZ3MpID0+IHtcclxuICBjb25zdCBbbmV3TGV2ZWwsIC4uLnRleHRzXSA9IGFyZ3M7XHJcblxyXG4gIC8vIEN1cnJlbnQgbG9nZ2luZyBvcHRpb25zXHJcbiAgY29uc3QgeyBsZXZlbCwgbGV2ZWxzRGVzYyB9ID0gbG9nZ2luZztcclxuXHJcbiAgLy8gQ2hlY2sgaWYgbG9nIGxldmVsIGlzIHdpdGhpbiBhIGNvcnJlY3QgcmFuZ2Ugb3IgaXMgYSBiZW5jaG1hcmsgbG9nXHJcbiAgaWYgKFxyXG4gICAgbmV3TGV2ZWwgIT09IDUgJiZcclxuICAgIChuZXdMZXZlbCA9PT0gMCB8fCBuZXdMZXZlbCA+IGxldmVsIHx8IGxldmVsID4gbGV2ZWxzRGVzYy5sZW5ndGgpXHJcbiAgKSB7XHJcbiAgICByZXR1cm47XHJcbiAgfVxyXG5cclxuICAvLyBHZXQgcmlkIG9mIHRoZSBHTVQgdGV4dCBpbmZvcm1hdGlvblxyXG4gIGNvbnN0IG5ld0RhdGUgPSBuZXcgRGF0ZSgpLnRvU3RyaW5nKCkuc3BsaXQoJygnKVswXS50cmltKCk7XHJcblxyXG4gIC8vIENyZWF0ZSBhIG1lc3NhZ2UncyBwcmVmaXhcclxuICBjb25zdCBwcmVmaXggPSBgJHtuZXdEYXRlfSBbJHtsZXZlbHNEZXNjW25ld0xldmVsIC0gMV0udGl0bGV9XSAtYDtcclxuXHJcbiAgLy8gQ2FsbCBhdmFpbGFibGUgbG9nIGxpc3RlbmVyc1xyXG4gIGxvZ2dpbmcubGlzdGVuZXJzLmZvckVhY2goKGZuKSA9PiB7XHJcbiAgICBmbihwcmVmaXgsIHRleHRzLmpvaW4oJyAnKSk7XHJcbiAgfSk7XHJcblxyXG4gIC8vIExvZyB0byBjb25zb2xlXHJcbiAgaWYgKGxvZ2dpbmcudG9Db25zb2xlKSB7XHJcbiAgICBjb25zb2xlLmxvZy5hcHBseShcclxuICAgICAgdW5kZWZpbmVkLFxyXG4gICAgICBbcHJlZml4LnRvU3RyaW5nKClbbG9nZ2luZy5sZXZlbHNEZXNjW25ld0xldmVsIC0gMV0uY29sb3JdXS5jb25jYXQodGV4dHMpXHJcbiAgICApO1xyXG4gIH1cclxuXHJcbiAgLy8gTG9nIHRvIGZpbGVcclxuICBsb2dUb0ZpbGUodGV4dHMsIHByZWZpeCk7XHJcbn07XHJcblxyXG4vKipcclxuICogTG9ncyBhbiBlcnJvciBtZXNzYWdlIHdpdGggaXRzIHN0YWNrIHRyYWNlLiBPcHRpb25hbGx5LCBhIGN1c3RvbSBtZXNzYWdlXHJcbiAqIGNhbiBiZSBwcm92aWRlZC5cclxuICpcclxuICogQHBhcmFtIHtudW1iZXJ9IGxldmVsIC0gVGhlIGxvZyBsZXZlbC5cclxuICogQHBhcmFtIHtFcnJvcn0gZXJyb3IgLSBUaGUgZXJyb3Igb2JqZWN0LlxyXG4gKiBAcGFyYW0ge3N0cmluZ30gY3VzdG9tTWVzc2FnZSAtIEFuIG9wdGlvbmFsIGN1c3RvbSBtZXNzYWdlIHRvIGJlIGxvZ2dlZCBhbG9uZ1xyXG4gKiB3aXRoIHRoZSBlcnJvci5cclxuICovXHJcbmV4cG9ydCBjb25zdCBsb2dXaXRoU3RhY2sgPSAobmV3TGV2ZWwsIGVycm9yLCBjdXN0b21NZXNzYWdlKSA9PiB7XHJcbiAgLy8gR2V0IHRoZSBtYWluIG1lc3NhZ2VcclxuICBjb25zdCBtYWluTWVzc2FnZSA9IGN1c3RvbU1lc3NhZ2UgfHwgZXJyb3IubWVzc2FnZTtcclxuXHJcbiAgLy8gQ3VycmVudCBsb2dnaW5nIG9wdGlvbnNcclxuICBjb25zdCB7IGxldmVsLCBsZXZlbHNEZXNjIH0gPSBsb2dnaW5nO1xyXG5cclxuICAvLyBDaGVjayBpZiBsb2cgbGV2ZWwgaXMgd2l0aGluIGEgY29ycmVjdCByYW5nZVxyXG4gIGlmIChuZXdMZXZlbCA9PT0gMCB8fCBuZXdMZXZlbCA+IGxldmVsIHx8IGxldmVsID4gbGV2ZWxzRGVzYy5sZW5ndGgpIHtcclxuICAgIHJldHVybjtcclxuICB9XHJcblxyXG4gIC8vIEdldCByaWQgb2YgdGhlIEdNVCB0ZXh0IGluZm9ybWF0aW9uXHJcbiAgY29uc3QgbmV3RGF0ZSA9IG5ldyBEYXRlKCkudG9TdHJpbmcoKS5zcGxpdCgnKCcpWzBdLnRyaW0oKTtcclxuXHJcbiAgLy8gQ3JlYXRlIGEgbWVzc2FnZSdzIHByZWZpeFxyXG4gIGNvbnN0IHByZWZpeCA9IGAke25ld0RhdGV9IFske2xldmVsc0Rlc2NbbmV3TGV2ZWwgLSAxXS50aXRsZX1dIC1gO1xyXG5cclxuICAvLyBJZiB0aGUgY3VzdG9tTWVzc2FnZSBleGlzdHMsIHdlIHdhbnQgdG8gZGlzcGxheSB0aGUgd2hvbGUgc3RhY2sgbWVzc2FnZVxyXG4gIGNvbnN0IHN0YWNrTWVzc2FnZSA9XHJcbiAgICBlcnJvci5tZXNzYWdlICE9PSBlcnJvci5zdGFja01lc3NhZ2UgfHwgZXJyb3Iuc3RhY2tNZXNzYWdlID09PSB1bmRlZmluZWRcclxuICAgICAgPyBlcnJvci5zdGFja1xyXG4gICAgICA6IGVycm9yLnN0YWNrLnNwbGl0KCdcXG4nKS5zbGljZSgxKS5qb2luKCdcXG4nKTtcclxuXHJcbiAgLy8gQ29tYmluZSBjdXN0b20gbWVzc2FnZSBvciBlcnJvciBtZXNzYWdlIHdpdGggZXJyb3Igc3RhY2sgbWVzc2FnZVxyXG4gIGNvbnN0IHRleHRzID0gW21haW5NZXNzYWdlLCAnXFxuJywgc3RhY2tNZXNzYWdlXTtcclxuXHJcbiAgLy8gTG9nIHRvIGNvbnNvbGVcclxuICBpZiAobG9nZ2luZy50b0NvbnNvbGUpIHtcclxuICAgIGNvbnNvbGUubG9nLmFwcGx5KFxyXG4gICAgICB1bmRlZmluZWQsXHJcbiAgICAgIFtwcmVmaXgudG9TdHJpbmcoKVtsb2dnaW5nLmxldmVsc0Rlc2NbbmV3TGV2ZWwgLSAxXS5jb2xvcl1dLmNvbmNhdChbXHJcbiAgICAgICAgbWFpbk1lc3NhZ2VbY29sb3JzW25ld0xldmVsIC0gMV1dLFxyXG4gICAgICAgICdcXG4nLFxyXG4gICAgICAgIHN0YWNrTWVzc2FnZVxyXG4gICAgICBdKVxyXG4gICAgKTtcclxuICB9XHJcblxyXG4gIC8vIENhbGwgYXZhaWxhYmxlIGxvZyBsaXN0ZW5lcnNcclxuICBsb2dnaW5nLmxpc3RlbmVycy5mb3JFYWNoKChmbikgPT4ge1xyXG4gICAgZm4ocHJlZml4LCB0ZXh0cy5qb2luKCcgJykpO1xyXG4gIH0pO1xyXG5cclxuICAvLyBMb2cgdG8gZmlsZVxyXG4gIGxvZ1RvRmlsZSh0ZXh0cywgcHJlZml4KTtcclxufTtcclxuXHJcbi8qKlxyXG4gKiBTZXRzIHRoZSBsb2cgbGV2ZWwgdG8gdGhlIHNwZWNpZmllZCB2YWx1ZS4gTG9nIGxldmVscyBhcmUgKDAgPSBubyBsb2dnaW5nLFxyXG4gKiAxID0gZXJyb3IsIDIgPSB3YXJuaW5nLCAzID0gbm90aWNlLCA0ID0gdmVyYm9zZSBvciA1ID0gYmVuY2htYXJrKVxyXG4gKlxyXG4gKiBAcGFyYW0ge251bWJlcn0gbmV3TGV2ZWwgLSBUaGUgbmV3IGxvZyBsZXZlbCB0byBiZSBzZXQuXHJcbiAqL1xyXG5leHBvcnQgY29uc3Qgc2V0TG9nTGV2ZWwgPSAobmV3TGV2ZWwpID0+IHtcclxuICBpZiAobmV3TGV2ZWwgPj0gMCAmJiBuZXdMZXZlbCA8PSBsb2dnaW5nLmxldmVsc0Rlc2MubGVuZ3RoKSB7XHJcbiAgICBsb2dnaW5nLmxldmVsID0gbmV3TGV2ZWw7XHJcbiAgfVxyXG59O1xyXG5cclxuLyoqXHJcbiAqIEVuYWJsZXMgZmlsZSBsb2dnaW5nIHdpdGggdGhlIHNwZWNpZmllZCBkZXN0aW5hdGlvbiBhbmQgbG9nIGZpbGUuXHJcbiAqXHJcbiAqIEBwYXJhbSB7c3RyaW5nfSBsb2dEZXN0IC0gVGhlIGRlc3RpbmF0aW9uIHBhdGggZm9yIGxvZyBmaWxlcy5cclxuICogQHBhcmFtIHtzdHJpbmd9IGxvZ0ZpbGUgLSBUaGUgbG9nIGZpbGUgbmFtZS5cclxuICovXHJcbmV4cG9ydCBjb25zdCBlbmFibGVGaWxlTG9nZ2luZyA9IChsb2dEZXN0LCBsb2dGaWxlKSA9PiB7XHJcbiAgLy8gVXBkYXRlIGxvZ2dpbmcgb3B0aW9uc1xyXG4gIGxvZ2dpbmcgPSB7XHJcbiAgICAuLi5sb2dnaW5nLFxyXG4gICAgZGVzdDogbG9nRGVzdCB8fCBsb2dnaW5nLmRlc3QsXHJcbiAgICBmaWxlOiBsb2dGaWxlIHx8IGxvZ2dpbmcuZmlsZSxcclxuICAgIHRvRmlsZTogdHJ1ZVxyXG4gIH07XHJcblxyXG4gIGlmIChsb2dnaW5nLmRlc3QubGVuZ3RoID09PSAwKSB7XHJcbiAgICByZXR1cm4gbG9nKDEsICdbbG9nZ2VyXSBGaWxlIGxvZ2dpbmcgaW5pdGlhbGl6YXRpb246IG5vIHBhdGggc3VwcGxpZWQuJyk7XHJcbiAgfVxyXG5cclxuICBpZiAoIWxvZ2dpbmcuZGVzdC5lbmRzV2l0aCgnLycpKSB7XHJcbiAgICBsb2dnaW5nLmRlc3QgKz0gJy8nO1xyXG4gIH1cclxufTtcclxuXHJcbi8qKlxyXG4gKiBJbml0aWFsaXplcyBsb2dnaW5nIHdpdGggdGhlIHNwZWNpZmllZCBsb2dnaW5nIGNvbmZpZ3VyYXRpb24uXHJcbiAqXHJcbiAqIEBwYXJhbSB7T2JqZWN0fSBsb2dnaW5nIC0gVGhlIGxvZ2dpbmcgY29uZmlndXJhdGlvbiBvYmplY3QuXHJcbiAqL1xyXG5leHBvcnQgY29uc3QgaW5pdExvZ2dpbmcgPSAobG9nZ2luZykgPT4ge1xyXG4gIC8vIFNldCB0aGUgbG9nIGxldmVsXHJcbiAgc2V0TG9nTGV2ZWwobG9nZ2luZyAmJiBwYXJzZUludChsb2dnaW5nLmxldmVsKSk7XHJcblxyXG4gIC8vIFNldCB0aGUgbG9nIGZpbGUgcGF0aCBhbmQgbmFtZVxyXG4gIGlmIChsb2dnaW5nICYmIGxvZ2dpbmcuZGVzdCkge1xyXG4gICAgZW5hYmxlRmlsZUxvZ2dpbmcoXHJcbiAgICAgIGxvZ2dpbmcuZGVzdCxcclxuICAgICAgbG9nZ2luZy5maWxlIHx8ICdoaWdoY2hhcnRzLWV4cG9ydC1zZXJ2ZXIubG9nJ1xyXG4gICAgKTtcclxuICB9XHJcbn07XHJcblxyXG4vKipcclxuICogQWRkcyBhIGxpc3RlbmVyIGZ1bmN0aW9uIHRvIHRoZSBsb2dnaW5nIHN5c3RlbS5cclxuICpcclxuICogQHBhcmFtIHtmdW5jdGlvbn0gZm4gLSBUaGUgbGlzdGVuZXIgZnVuY3Rpb24gdG8gYmUgYWRkZWQuXHJcbiAqL1xyXG5leHBvcnQgY29uc3QgbGlzdGVuID0gKGZuKSA9PiB7XHJcbiAgbG9nZ2luZy5saXN0ZW5lcnMucHVzaChmbik7XHJcbn07XHJcblxyXG4vKipcclxuICogVG9nZ2xlcyB0aGUgc3RhbmRhcmQgb3V0cHV0IChjb25zb2xlKSBsb2dnaW5nLlxyXG4gKlxyXG4gKiBAcGFyYW0ge2Jvb2xlYW59IGVuYWJsZWQgLSBJZiB0cnVlLCBlbmFibGVzIGNvbnNvbGUgbG9nZ2luZzsgaWYgZmFsc2UsXHJcbiAqIGRpc2FibGVzIGl0LlxyXG4gKi9cclxuZXhwb3J0IGNvbnN0IHRvZ2dsZVNURE91dCA9IChlbmFibGVkKSA9PiB7XHJcbiAgbG9nZ2luZy50b0NvbnNvbGUgPSBlbmFibGVkO1xyXG59O1xyXG5cclxuZXhwb3J0IGRlZmF1bHQge1xyXG4gIGxvZyxcclxuICBsb2dXaXRoU3RhY2ssXHJcbiAgc2V0TG9nTGV2ZWwsXHJcbiAgZW5hYmxlRmlsZUxvZ2dpbmcsXHJcbiAgaW5pdExvZ2dpbmcsXHJcbiAgbGlzdGVuLFxyXG4gIHRvZ2dsZVNURE91dFxyXG59O1xyXG4iLCIvKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKlxyXG5cclxuSGlnaGNoYXJ0cyBFeHBvcnQgU2VydmVyXHJcblxyXG5Db3B5cmlnaHQgKGMpIDIwMTYtMjAyNCwgSGlnaHNvZnRcclxuXHJcbkxpY2VuY2VkIHVuZGVyIHRoZSBNSVQgbGljZW5jZS5cclxuXHJcbkFkZGl0aW9uYWxseSBhIHZhbGlkIEhpZ2hjaGFydHMgbGljZW5zZSBpcyByZXF1aXJlZCBmb3IgdXNlLlxyXG5cclxuU2VlIExJQ0VOU0UgZmlsZSBpbiByb290IGZvciBkZXRhaWxzLlxyXG5cclxuKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKi9cclxuXHJcbmltcG9ydCB7IHJlYWRGaWxlU3luYyB9IGZyb20gJ2ZzJztcclxuaW1wb3J0IHsgam9pbiB9IGZyb20gJ3BhdGgnO1xyXG5pbXBvcnQgeyBmaWxlVVJMVG9QYXRoIH0gZnJvbSAndXJsJztcclxuXHJcbmltcG9ydCB7IGRlZmF1bHRDb25maWcgfSBmcm9tICcuLi9saWIvc2NoZW1hcy9jb25maWcuanMnO1xyXG5pbXBvcnQgeyBsb2csIGxvZ1dpdGhTdGFjayB9IGZyb20gJy4vbG9nZ2VyLmpzJztcclxuXHJcbmNvbnN0IE1BWF9CQUNLT0ZGX0FUVEVNUFRTID0gNjtcclxuXHJcbmV4cG9ydCBjb25zdCBfX2Rpcm5hbWUgPSBmaWxlVVJMVG9QYXRoKG5ldyBVUkwoJy4uLy4nLCBpbXBvcnQubWV0YS51cmwpKTtcclxuXHJcbi8qKlxyXG4gKiBDbGVhcnMgYW5kIHN0YW5kYXJkaXplcyB0ZXh0IGJ5IHJlcGxhY2luZyBtdWx0aXBsZSBjb25zZWN1dGl2ZSB3aGl0ZXNwYWNlXHJcbiAqIGNoYXJhY3RlcnMgd2l0aCBhIHNpbmdsZSBzcGFjZSBhbmQgdHJpbW1pbmcgYW55IGxlYWRpbmcgb3IgdHJhaWxpbmdcclxuICogd2hpdGVzcGFjZS5cclxuICpcclxuICogQHBhcmFtIHtzdHJpbmd9IHRleHQgLSBUaGUgaW5wdXQgdGV4dCB0byBiZSBjbGVhcmVkLlxyXG4gKiBAcGFyYW0ge1JlZ0V4cH0gW3J1bGU9L1xcc1xccysvZ10gLSBUaGUgcmVndWxhciBleHByZXNzaW9uIHJ1bGUgdG8gbWF0Y2hcclxuICogbXVsdGlwbGUgY29uc2VjdXRpdmUgd2hpdGVzcGFjZSBjaGFyYWN0ZXJzLlxyXG4gKiBAcGFyYW0ge3N0cmluZ30gW3JlcGxhY2VyPScgJ10gLSBUaGUgc3RyaW5nIHVzZWQgdG8gcmVwbGFjZSBtdWx0aXBsZVxyXG4gKiBjb25zZWN1dGl2ZSB3aGl0ZXNwYWNlIGNoYXJhY3RlcnMuXHJcbiAqXHJcbiAqIEByZXR1cm5zIHtzdHJpbmd9IC0gVGhlIGNsZWFyZWQgYW5kIHN0YW5kYXJkaXplZCB0ZXh0LlxyXG4gKi9cclxuZXhwb3J0IGNvbnN0IGNsZWFyVGV4dCA9ICh0ZXh0LCBydWxlID0gL1xcc1xccysvZywgcmVwbGFjZXIgPSAnICcpID0+XHJcbiAgdGV4dC5yZXBsYWNlQWxsKHJ1bGUsIHJlcGxhY2VyKS50cmltKCk7XHJcblxyXG4vKipcclxuICogSW1wbGVtZW50cyBhbiBleHBvbmVudGlhbCBiYWNrb2ZmIHN0cmF0ZWd5IGZvciByZXRyeWluZyBhIGZ1bmN0aW9uIHVudGlsXHJcbiAqIGEgY2VydGFpbiBudW1iZXIgb2YgYXR0ZW1wdHMgYXJlIHJlYWNoZWQuXHJcbiAqXHJcbiAqIEBwYXJhbSB7RnVuY3Rpb259IGZuIC0gVGhlIGZ1bmN0aW9uIHRvIGJlIHJldHJpZWQuXHJcbiAqIEBwYXJhbSB7bnVtYmVyfSBbYXR0ZW1wdD0wXSAtIFRoZSBjdXJyZW50IGF0dGVtcHQgbnVtYmVyLlxyXG4gKiBAcGFyYW0gey4uLmFueX0gYXJncyAtIEFyZ3VtZW50cyB0byBiZSBwYXNzZWQgdG8gdGhlIGZ1bmN0aW9uLlxyXG4gKlxyXG4gKiBAcmV0dXJucyB7UHJvbWlzZX0gLSBBIHByb21pc2UgdGhhdCByZXNvbHZlcyB0byB0aGUgcmVzdWx0IG9mIHRoZSBmdW5jdGlvblxyXG4gKiBpZiBzdWNjZXNzZnVsLlxyXG4gKlxyXG4gKiBAdGhyb3dzIHtFcnJvcn0gLSBUaHJvd3MgYW4gZXJyb3IgaWYgdGhlIG1heGltdW0gbnVtYmVyIG9mIGF0dGVtcHRzXHJcbiAqIGlzIHJlYWNoZWQuXHJcbiAqL1xyXG5leHBvcnQgY29uc3QgZXhwQmFja29mZiA9IGFzeW5jIChmbiwgYXR0ZW1wdCA9IDAsIC4uLmFyZ3MpID0+IHtcclxuICB0cnkge1xyXG4gICAgLy8gVHJ5IHRvIGNhbGwgdGhlIGZ1bmN0aW9uXHJcbiAgICByZXR1cm4gYXdhaXQgZm4oLi4uYXJncyk7XHJcbiAgfSBjYXRjaCAoZXJyb3IpIHtcclxuICAgIC8vIENhbGN1bGF0ZSBkZWxheSBpbiBtc1xyXG4gICAgY29uc3QgZGVsYXlJbk1zID0gMiAqKiBhdHRlbXB0ICogMTAwMDtcclxuXHJcbiAgICAvLyBJZiB0aGUgYXR0ZW1wdCBleGNlZWRzIHRoZSBtYXhpbXVtIGF0dGVtcHRzIG9mIHJlYXBlYXQsIHRocm93IGFuIGVycm9yXHJcbiAgICBpZiAoKythdHRlbXB0ID49IE1BWF9CQUNLT0ZGX0FUVEVNUFRTKSB7XHJcbiAgICAgIHRocm93IGVycm9yO1xyXG4gICAgfVxyXG5cclxuICAgIC8vIFdhaXQgZ2l2ZW4gYW1vdW50IG9mIHRpbWVcclxuICAgIGF3YWl0IG5ldyBQcm9taXNlKChyZXNwb25zZSkgPT4gc2V0VGltZW91dChyZXNwb25zZSwgZGVsYXlJbk1zKSk7XHJcbiAgICBsb2coXHJcbiAgICAgIDMsXHJcbiAgICAgIGBbcG9vbF0gV2FpdGVkICR7ZGVsYXlJbk1zfW1zIHVudGlsIG5leHQgY2FsbCBmb3IgdGhlIHJlc291cmNlIGlkOiAke2FyZ3NbMF19LmBcclxuICAgICk7XHJcblxyXG4gICAgLy8gVHJ5IGFnYWluXHJcbiAgICByZXR1cm4gZXhwQmFja29mZihmbiwgYXR0ZW1wdCwgLi4uYXJncyk7XHJcbiAgfVxyXG59O1xyXG5cclxuLyoqXHJcbiAqIEZpeGVzIHRoZSBleHBvcnQgdHlwZSBiYXNlZCBvbiBNSU1FIHR5cGVzIGFuZCBmaWxlIGV4dGVuc2lvbnMuXHJcbiAqXHJcbiAqIEBwYXJhbSB7c3RyaW5nfSB0eXBlIC0gVGhlIG9yaWdpbmFsIGV4cG9ydCB0eXBlLlxyXG4gKiBAcGFyYW0ge3N0cmluZ30gb3V0ZmlsZSAtIFRoZSBmaWxlIHBhdGggb3IgbmFtZS5cclxuICpcclxuICogQHJldHVybnMge3N0cmluZ30gLSBUaGUgY29ycmVjdGVkIGV4cG9ydCB0eXBlLlxyXG4gKi9cclxuZXhwb3J0IGNvbnN0IGZpeFR5cGUgPSAodHlwZSwgb3V0ZmlsZSkgPT4ge1xyXG4gIC8vIE1JTUUgdHlwZXNcclxuICBjb25zdCBtaW1lVHlwZXMgPSB7XHJcbiAgICAnaW1hZ2UvcG5nJzogJ3BuZycsXHJcbiAgICAnaW1hZ2UvanBlZyc6ICdqcGVnJyxcclxuICAgICdhcHBsaWNhdGlvbi9wZGYnOiAncGRmJyxcclxuICAgICdpbWFnZS9zdmcreG1sJzogJ3N2ZydcclxuICB9O1xyXG5cclxuICAvLyBGb3JtYXRzXHJcbiAgY29uc3QgZm9ybWF0cyA9IFsncG5nJywgJ2pwZWcnLCAncGRmJywgJ3N2ZyddO1xyXG5cclxuICAvLyBDaGVjayBpZiB0eXBlIGFuZCBvdXRmaWxlJ3MgZXh0ZW5zaW9ucyBhcmUgdGhlIHNhbWVcclxuICBpZiAob3V0ZmlsZSkge1xyXG4gICAgY29uc3Qgb3V0VHlwZSA9IG91dGZpbGUuc3BsaXQoJy4nKS5wb3AoKTtcclxuXHJcbiAgICBpZiAob3V0VHlwZSA9PT0gJ2pwZycpIHtcclxuICAgICAgdHlwZSA9ICdqcGVnJztcclxuICAgIH0gZWxzZSBpZiAoZm9ybWF0cy5pbmNsdWRlcyhvdXRUeXBlKSAmJiB0eXBlICE9PSBvdXRUeXBlKSB7XHJcbiAgICAgIHR5cGUgPSBvdXRUeXBlO1xyXG4gICAgfVxyXG4gIH1cclxuXHJcbiAgLy8gUmV0dXJuIGEgY29ycmVjdCB0eXBlXHJcbiAgcmV0dXJuIG1pbWVUeXBlc1t0eXBlXSB8fCBmb3JtYXRzLmZpbmQoKHQpID0+IHQgPT09IHR5cGUpIHx8ICdwbmcnO1xyXG59O1xyXG5cclxuLyoqXHJcbiAqIEhhbmRsZXMgYW5kIHZhbGlkYXRlcyByZXNvdXJjZXMgZm9yIGV4cG9ydC5cclxuICpcclxuICogQHBhcmFtIHtPYmplY3R8c3RyaW5nfSByZXNvdXJjZXMgLSBUaGUgcmVzb3VyY2VzIHRvIGJlIGhhbmRsZWQuIENhbiBiZSBlaXRoZXJcclxuICogYSBKU09OIG9iamVjdCwgc3RyaW5naWZpZWQgSlNPTiBvciBhIHBhdGggdG8gYSBKU09OIGZpbGUuXHJcbiAqIEBwYXJhbSB7Ym9vbGVhbn0gYWxsb3dGaWxlUmVzb3VyY2VzIC0gV2hldGhlciB0byBhbGxvdyBsb2FkaW5nIHJlc291cmNlcyBmcm9tXHJcbiAqIGZpbGVzLlxyXG4gKlxyXG4gKiBAcmV0dXJucyB7T2JqZWN0fHVuZGVmaW5lZH0gLSBUaGUgaGFuZGxlZCByZXNvdXJjZXMgb3IgdW5kZWZpbmVkIGlmIG5vIHZhbGlkXHJcbiAqIHJlc291cmNlcyBhcmUgZm91bmQuXHJcbiAqL1xyXG5leHBvcnQgY29uc3QgaGFuZGxlUmVzb3VyY2VzID0gKHJlc291cmNlcyA9IGZhbHNlLCBhbGxvd0ZpbGVSZXNvdXJjZXMpID0+IHtcclxuICBjb25zdCBhbGxvd2VkUHJvcHMgPSBbJ2pzJywgJ2NzcycsICdmaWxlcyddO1xyXG5cclxuICBsZXQgaGFuZGxlZFJlc291cmNlcyA9IHJlc291cmNlcztcclxuICBsZXQgY29ycmVjdFJlc291cmNlcyA9IGZhbHNlO1xyXG5cclxuICAvLyBUcnkgdG8gbG9hZCByZXNvdXJjZXMgZnJvbSBhIGZpbGVcclxuICBpZiAoYWxsb3dGaWxlUmVzb3VyY2VzICYmIHJlc291cmNlcy5lbmRzV2l0aCgnLmpzb24nKSkge1xyXG4gICAgdHJ5IHtcclxuICAgICAgaGFuZGxlZFJlc291cmNlcyA9IGlzQ29ycmVjdEpTT04ocmVhZEZpbGVTeW5jKHJlc291cmNlcywgJ3V0ZjgnKSk7XHJcbiAgICB9IGNhdGNoIChlcnJvcikge1xyXG4gICAgICByZXR1cm4gbG9nV2l0aFN0YWNrKDIsIGVycm9yLCBgW2NsaV0gTm8gcmVzb3VyY2VzIGZvdW5kLmApO1xyXG4gICAgfVxyXG4gIH0gZWxzZSB7XHJcbiAgICAvLyBUcnkgdG8gZ2V0IEpTT05cclxuICAgIGhhbmRsZWRSZXNvdXJjZXMgPSBpc0NvcnJlY3RKU09OKHJlc291cmNlcyk7XHJcblxyXG4gICAgLy8gR2V0IHJpZCBvZiB0aGUgZmlsZXMgc2VjdGlvblxyXG4gICAgaWYgKGhhbmRsZWRSZXNvdXJjZXMgJiYgIWFsbG93RmlsZVJlc291cmNlcykge1xyXG4gICAgICBkZWxldGUgaGFuZGxlZFJlc291cmNlcy5maWxlcztcclxuICAgIH1cclxuICB9XHJcblxyXG4gIC8vIEZpbHRlciBmcm9tIHVubmVjZXNzYXJ5IHByb3BlcnRpZXNcclxuICBmb3IgKGNvbnN0IHByb3BOYW1lIGluIGhhbmRsZWRSZXNvdXJjZXMpIHtcclxuICAgIGlmICghYWxsb3dlZFByb3BzLmluY2x1ZGVzKHByb3BOYW1lKSkge1xyXG4gICAgICBkZWxldGUgaGFuZGxlZFJlc291cmNlc1twcm9wTmFtZV07XHJcbiAgICB9IGVsc2UgaWYgKCFjb3JyZWN0UmVzb3VyY2VzKSB7XHJcbiAgICAgIGNvcnJlY3RSZXNvdXJjZXMgPSB0cnVlO1xyXG4gICAgfVxyXG4gIH1cclxuXHJcbiAgLy8gQ2hlY2sgaWYgYXQgbGVhc3Qgb25lIG9mIGFsbG93ZWQgcHJvcGVydGllcyBpcyBwcmVzZW50XHJcbiAgaWYgKCFjb3JyZWN0UmVzb3VyY2VzKSB7XHJcbiAgICByZXR1cm4gbG9nKDMsIGBbY2xpXSBObyByZXNvdXJjZXMgZm91bmQuYCk7XHJcbiAgfVxyXG5cclxuICAvLyBIYW5kbGUgZmlsZXMgc2VjdGlvblxyXG4gIGlmIChoYW5kbGVkUmVzb3VyY2VzLmZpbGVzKSB7XHJcbiAgICBoYW5kbGVkUmVzb3VyY2VzLmZpbGVzID0gaGFuZGxlZFJlc291cmNlcy5maWxlcy5tYXAoKGl0ZW0pID0+IGl0ZW0udHJpbSgpKTtcclxuICAgIGlmICghaGFuZGxlZFJlc291cmNlcy5maWxlcyB8fCBoYW5kbGVkUmVzb3VyY2VzLmZpbGVzLmxlbmd0aCA8PSAwKSB7XHJcbiAgICAgIGRlbGV0ZSBoYW5kbGVkUmVzb3VyY2VzLmZpbGVzO1xyXG4gICAgfVxyXG4gIH1cclxuXHJcbiAgLy8gUmV0dXJuIHJlc291cmNlc1xyXG4gIHJldHVybiBoYW5kbGVkUmVzb3VyY2VzO1xyXG59O1xyXG5cclxuLyoqXHJcbiAqIFZhbGlkYXRlcyBhbmQgcGFyc2VzIEpTT04gZGF0YS4gQ2hlY2tzIGlmIHByb3ZpZGVkIGRhdGEgaXMgb3IgY2FuXHJcbiAqIGJlIGEgY29ycmVjdCBKU09OLiBJZiBhIHByaW1pdGl2ZSBpcyBwcm92aWRlZCwgaXQgaXMgc3RyaW5naWZpZWQgYW5kIHJldHVybmVkLlxyXG4gKlxyXG4gKiBAcGFyYW0ge09iamVjdHxzdHJpbmd9IGRhdGEgLSBUaGUgSlNPTiBkYXRhIHRvIGJlIHZhbGlkYXRlZCBhbmQgcGFyc2VkLlxyXG4gKiBAcGFyYW0ge2Jvb2xlYW59IHRvU3RyaW5nIC0gV2hldGhlciB0byByZXR1cm4gYSBzdHJpbmdpZmllZCByZXByZXNlbnRhdGlvblxyXG4gKiBvZiB0aGUgcGFyc2VkIEpTT04uXHJcbiAqXHJcbiAqIEByZXR1cm5zIHtPYmplY3R8c3RyaW5nfGJvb2xlYW59IC0gVGhlIHBhcnNlZCBKU09OIG9iamVjdCwgc3RyaW5naWZpZWQgSlNPTixcclxuICogb3IgZmFsc2UgaWYgdmFsaWRhdGlvbiBmYWlscy5cclxuICovXHJcbmV4cG9ydCBmdW5jdGlvbiBpc0NvcnJlY3RKU09OKGRhdGEsIHRvU3RyaW5nKSB7XHJcbiAgdHJ5IHtcclxuICAgIC8vIEdldCB0aGUgc3RyaW5nIHJlcHJlc2VudGF0aW9uIGlmIG5vdCBhbHJlYWR5IGJlZm9yZSBwYXJzaW5nXHJcbiAgICBjb25zdCBwYXJzZWREYXRhID0gSlNPTi5wYXJzZShcclxuICAgICAgdHlwZW9mIGRhdGEgIT09ICdzdHJpbmcnID8gSlNPTi5zdHJpbmdpZnkoZGF0YSkgOiBkYXRhXHJcbiAgICApO1xyXG5cclxuICAgIC8vIFJldHVybiBhIHN0cmluZ2lmaWVkIHJlcHJlc2VudGF0aW9uIG9mIGEgSlNPTiBpZiByZXF1aXJlZFxyXG4gICAgaWYgKHR5cGVvZiBwYXJzZWREYXRhICE9PSAnc3RyaW5nJyAmJiB0b1N0cmluZykge1xyXG4gICAgICByZXR1cm4gSlNPTi5zdHJpbmdpZnkocGFyc2VkRGF0YSk7XHJcbiAgICB9XHJcblxyXG4gICAgLy8gUmV0dXJuIGEgSlNPTlxyXG4gICAgcmV0dXJuIHBhcnNlZERhdGE7XHJcbiAgfSBjYXRjaCB7XHJcbiAgICByZXR1cm4gZmFsc2U7XHJcbiAgfVxyXG59XHJcblxyXG4vKipcclxuICogQ2hlY2tzIGlmIHRoZSBnaXZlbiBpdGVtIGlzIGFuIG9iamVjdC5cclxuICpcclxuICogQHBhcmFtIHthbnl9IGl0ZW0gLSBUaGUgaXRlbSB0byBiZSBjaGVja2VkLlxyXG4gKlxyXG4gKiBAcmV0dXJucyB7Ym9vbGVhbn0gLSBUcnVlIGlmIHRoZSBpdGVtIGlzIGFuIG9iamVjdCwgZmFsc2Ugb3RoZXJ3aXNlLlxyXG4gKi9cclxuZXhwb3J0IGNvbnN0IGlzT2JqZWN0ID0gKGl0ZW0pID0+XHJcbiAgdHlwZW9mIGl0ZW0gPT09ICdvYmplY3QnICYmICFBcnJheS5pc0FycmF5KGl0ZW0pICYmIGl0ZW0gIT09IG51bGw7XHJcblxyXG4vKipcclxuICogQ2hlY2tzIGlmIHRoZSBnaXZlbiBvYmplY3QgaXMgZW1wdHkuXHJcbiAqXHJcbiAqIEBwYXJhbSB7T2JqZWN0fSBpdGVtIC0gVGhlIG9iamVjdCB0byBiZSBjaGVja2VkLlxyXG4gKlxyXG4gKiBAcmV0dXJucyB7Ym9vbGVhbn0gLSBUcnVlIGlmIHRoZSBvYmplY3QgaXMgZW1wdHksIGZhbHNlIG90aGVyd2lzZS5cclxuICovXHJcbmV4cG9ydCBjb25zdCBpc09iamVjdEVtcHR5ID0gKGl0ZW0pID0+XHJcbiAgdHlwZW9mIGl0ZW0gPT09ICdvYmplY3QnICYmXHJcbiAgIUFycmF5LmlzQXJyYXkoaXRlbSkgJiZcclxuICBpdGVtICE9PSBudWxsICYmXHJcbiAgT2JqZWN0LmtleXMoaXRlbSkubGVuZ3RoID09PSAwO1xyXG5cclxuLyoqXHJcbiAqIENoZWNrcyBpZiBhIHByaXZhdGUgSVAgcmFuZ2UgVVJMIGlzIGZvdW5kIGluIHRoZSBnaXZlbiBzdHJpbmcuXHJcbiAqXHJcbiAqIEBwYXJhbSB7c3RyaW5nfSBpdGVtIC0gVGhlIHN0cmluZyB0byBiZSBjaGVja2VkIGZvciBhIHByaXZhdGUgSVAgcmFuZ2UgVVJMLlxyXG4gKlxyXG4gKiBAcmV0dXJucyB7Ym9vbGVhbn0gLSBUcnVlIGlmIGEgcHJpdmF0ZSBJUCByYW5nZSBVUkwgaXMgZm91bmQsIGZhbHNlXHJcbiAqIG90aGVyd2lzZS5cclxuICovXHJcbmV4cG9ydCBjb25zdCBpc1ByaXZhdGVSYW5nZVVybEZvdW5kID0gKGl0ZW0pID0+IHtcclxuICBjb25zdCByZWdleFBhdHRlcm5zID0gW1xyXG4gICAgL3hsaW5rOmhyZWY9XCIoPzpodHRwOlxcL1xcL3xodHRwczpcXC9cXC8pP2xvY2FsaG9zdFxcYi8sXHJcbiAgICAveGxpbms6aHJlZj1cIig/Omh0dHA6XFwvXFwvfGh0dHBzOlxcL1xcLyk/MTBcXC5cXGR7MSwzfVxcLlxcZHsxLDN9XFwuXFxkezEsM31cXGIvLFxyXG4gICAgL3hsaW5rOmhyZWY9XCIoPzpodHRwOlxcL1xcL3xodHRwczpcXC9cXC8pPzEyN1xcLlxcZHsxLDN9XFwuXFxkezEsM31cXC5cXGR7MSwzfVxcYi8sXHJcbiAgICAveGxpbms6aHJlZj1cIig/Omh0dHA6XFwvXFwvfGh0dHBzOlxcL1xcLyk/MTcyXFwuKDFbNi05XXwyWzAtOV18M1swLTFdKVxcLlxcZHsxLDN9XFwuXFxkezEsM31cXGIvLFxyXG4gICAgL3hsaW5rOmhyZWY9XCIoPzpodHRwOlxcL1xcL3xodHRwczpcXC9cXC8pPzE5MlxcLjE2OFxcLlxcZHsxLDN9XFwuXFxkezEsM31cXGIvXHJcbiAgXTtcclxuXHJcbiAgcmV0dXJuIHJlZ2V4UGF0dGVybnMuc29tZSgocGF0dGVybikgPT4gcGF0dGVybi50ZXN0KGl0ZW0pKTtcclxufTtcclxuXHJcbi8qKlxyXG4gKiBDcmVhdGVzIGEgZGVlcCBjb3B5IG9mIHRoZSBnaXZlbiBvYmplY3Qgb3IgYXJyYXkuXHJcbiAqXHJcbiAqIEBwYXJhbSB7T2JqZWN0fEFycmF5fSBvYmogLSBUaGUgb2JqZWN0IG9yIGFycmF5IHRvIGJlIGRlZXBseSBjb3BpZWQuXHJcbiAqXHJcbiAqIEByZXR1cm5zIHtPYmplY3R8QXJyYXl9IC0gVGhlIGRlZXAgY29weSBvZiB0aGUgcHJvdmlkZWQgb2JqZWN0IG9yIGFycmF5LlxyXG4gKi9cclxuZXhwb3J0IGNvbnN0IGRlZXBDb3B5ID0gKG9iaikgPT4ge1xyXG4gIGlmIChvYmogPT09IG51bGwgfHwgdHlwZW9mIG9iaiAhPT0gJ29iamVjdCcpIHtcclxuICAgIHJldHVybiBvYmo7XHJcbiAgfVxyXG5cclxuICBjb25zdCBjb3B5ID0gQXJyYXkuaXNBcnJheShvYmopID8gW10gOiB7fTtcclxuXHJcbiAgZm9yIChjb25zdCBrZXkgaW4gb2JqKSB7XHJcbiAgICBpZiAoT2JqZWN0LnByb3RvdHlwZS5oYXNPd25Qcm9wZXJ0eS5jYWxsKG9iaiwga2V5KSkge1xyXG4gICAgICBjb3B5W2tleV0gPSBkZWVwQ29weShvYmpba2V5XSk7XHJcbiAgICB9XHJcbiAgfVxyXG5cclxuICByZXR1cm4gY29weTtcclxufTtcclxuXHJcbi8qKlxyXG4gKiBDb252ZXJ0cyB0aGUgcHJvdmlkZWQgb3B0aW9ucyBvYmplY3QgdG8gYSBKU09OLWZvcm1hdHRlZCBzdHJpbmcgd2l0aCB0aGVcclxuICogb3B0aW9uIHRvIHByZXNlcnZlIGZ1bmN0aW9ucy5cclxuICpcclxuICogQHBhcmFtIHtPYmplY3R9IG9wdGlvbnMgLSBUaGUgb3B0aW9ucyBvYmplY3QgdG8gYmUgY29udmVydGVkIHRvIGEgc3RyaW5nLlxyXG4gKiBAcGFyYW0ge2Jvb2xlYW59IGFsbG93RnVuY3Rpb25zIC0gSWYgc2V0IHRvIHRydWUsIGZ1bmN0aW9ucyBhcmUgcHJlc2VydmVkXHJcbiAqIGluIHRoZSBvdXRwdXQuXHJcbiAqXHJcbiAqIEByZXR1cm5zIHtzdHJpbmd9IC0gVGhlIEpTT04tZm9ybWF0dGVkIHN0cmluZyByZXByZXNlbnRpbmcgdGhlIG9wdGlvbnMuXHJcbiAqL1xyXG5leHBvcnQgY29uc3Qgb3B0aW9uc1N0cmluZ2lmeSA9IChvcHRpb25zLCBhbGxvd0Z1bmN0aW9ucykgPT4ge1xyXG4gIGNvbnN0IHJlcGxhY2VyQ2FsbGJhY2sgPSAobmFtZSwgdmFsdWUpID0+IHtcclxuICAgIGlmICh0eXBlb2YgdmFsdWUgPT09ICdzdHJpbmcnKSB7XHJcbiAgICAgIHZhbHVlID0gdmFsdWUudHJpbSgpO1xyXG5cclxuICAgICAgLy8gSWYgYWxsb3dGdW5jdGlvbnMgaXMgc2V0IHRvIHRydWUsIHByZXNlcnZlIGZ1bmN0aW9uc1xyXG4gICAgICBpZiAoXHJcbiAgICAgICAgKHZhbHVlLnN0YXJ0c1dpdGgoJ2Z1bmN0aW9uKCcpIHx8IHZhbHVlLnN0YXJ0c1dpdGgoJ2Z1bmN0aW9uICgnKSkgJiZcclxuICAgICAgICB2YWx1ZS5lbmRzV2l0aCgnfScpXHJcbiAgICAgICkge1xyXG4gICAgICAgIHZhbHVlID0gYWxsb3dGdW5jdGlvbnNcclxuICAgICAgICAgID8gYEVYUF9GVU4keyh2YWx1ZSArICcnKS5yZXBsYWNlQWxsKC9cXG58XFx0fFxcci9nLCAnICcpfUVYUF9GVU5gXHJcbiAgICAgICAgICA6IHVuZGVmaW5lZDtcclxuICAgICAgfVxyXG4gICAgfVxyXG5cclxuICAgIHJldHVybiB0eXBlb2YgdmFsdWUgPT09ICdmdW5jdGlvbidcclxuICAgICAgPyBgRVhQX0ZVTiR7KHZhbHVlICsgJycpLnJlcGxhY2VBbGwoL1xcbnxcXHR8XFxyL2csICcgJyl9RVhQX0ZVTmBcclxuICAgICAgOiB2YWx1ZTtcclxuICB9O1xyXG5cclxuICAvLyBTdHJpbmdpZnkgb3B0aW9ucyBhbmQgaWYgcmVxdWlyZWQsIHJlcGxhY2Ugc3BlY2lhbCBmdW5jdGlvbnMgbWFya3NcclxuICByZXR1cm4gSlNPTi5zdHJpbmdpZnkob3B0aW9ucywgcmVwbGFjZXJDYWxsYmFjaykucmVwbGFjZUFsbChcclxuICAgIC9cIkVYUF9GVU58RVhQX0ZVTlwiL2csXHJcbiAgICAnJ1xyXG4gICk7XHJcbn07XHJcblxyXG4vKipcclxuICogUHJpbnRzIHRoZSBIaWdoY2hhcnRzIEV4cG9ydCBTZXJ2ZXIgbG9nbyBhbmQgdmVyc2lvbiBpbmZvcm1hdGlvbi5cclxuICpcclxuICogQHBhcmFtIHtib29sZWFufSBub0xvZ28gLSBJZiB0cnVlLCBvbmx5IHByaW50cyB2ZXJzaW9uIGluZm9ybWF0aW9uIHdpdGhvdXRcclxuICogdGhlIGxvZ28uXHJcbiAqL1xyXG5leHBvcnQgY29uc3QgcHJpbnRMb2dvID0gKG5vTG9nbykgPT4ge1xyXG4gIC8vIEdldCBwYWNrYWdlIHZlcnNpb24gZWl0aGVyIGZyb20gZW52IG9yIGZyb20gcGFja2FnZS5qc29uXHJcbiAgY29uc3QgcGFja2FnZVZlcnNpb24gPSBKU09OLnBhcnNlKFxyXG4gICAgcmVhZEZpbGVTeW5jKGpvaW4oX19kaXJuYW1lLCAncGFja2FnZS5qc29uJykpXHJcbiAgKS52ZXJzaW9uO1xyXG5cclxuICAvLyBQcmludCB0ZXh0IG9ubHlcclxuICBpZiAobm9Mb2dvKSB7XHJcbiAgICBjb25zb2xlLmxvZyhgU3RhcnRpbmcgSGlnaGNoYXJ0cyBFeHBvcnQgU2VydmVyIHYke3BhY2thZ2VWZXJzaW9ufS4uLmApO1xyXG4gICAgcmV0dXJuO1xyXG4gIH1cclxuXHJcbiAgLy8gUHJpbnQgdGhlIGxvZ29cclxuICBjb25zb2xlLmxvZyhcclxuICAgIHJlYWRGaWxlU3luYyhfX2Rpcm5hbWUgKyAnL21zZy9zdGFydHVwLm1zZycpLnRvU3RyaW5nKCkuYm9sZC55ZWxsb3csXHJcbiAgICBgdiR7cGFja2FnZVZlcnNpb259XFxuYC5ib2xkXHJcbiAgKTtcclxufTtcclxuXHJcbi8qKlxyXG4gKiBQcmludHMgdGhlIHVzYWdlIGluZm9ybWF0aW9uIGZvciBDTEkgYXJndW1lbnRzLiBJZiByZXF1aXJlZCwgaXQgY2FuIGxpc3RcclxuICogcHJvcGVydGllcyByZWN1cnNpdmVseVxyXG4gKi9cclxuZXhwb3J0IGZ1bmN0aW9uIHByaW50VXNhZ2UoKSB7XHJcbiAgY29uc3QgcGFkID0gNDg7XHJcbiAgY29uc3QgcmVhZG1lID0gJ2h0dHBzOi8vZ2l0aHViLmNvbS9oaWdoY2hhcnRzL25vZGUtZXhwb3J0LXNlcnZlciNyZWFkbWUnO1xyXG5cclxuICAvLyBEaXNwbGF5IHJlYWRtZSBpbmZvcm1hdGlvblxyXG4gIGNvbnNvbGUubG9nKFxyXG4gICAgJ1xcblVzYWdlIG9mIENMSSBhcmd1bWVudHM6Jy5ib2xkLFxyXG4gICAgJ1xcbi0tLS0tLScsXHJcbiAgICBgXFxuRm9yIG1vcmUgZGV0YWlsZWQgaW5mb3JtYXRpb24sIHZpc2l0IHRoZSByZWFkbWUgYXQ6ICR7cmVhZG1lLmJvbGQueWVsbG93fS5gXHJcbiAgKTtcclxuXHJcbiAgY29uc3QgY3ljbGVDYXRlZ29yaWVzID0gKG9wdGlvbnMpID0+IHtcclxuICAgIGZvciAoY29uc3QgW25hbWUsIG9wdGlvbl0gb2YgT2JqZWN0LmVudHJpZXMob3B0aW9ucykpIHtcclxuICAgICAgLy8gSWYgY2F0ZWdvcnkgaGFzIG1vcmUgbGV2ZWxzLCBnbyBmdXJ0aGVyXHJcbiAgICAgIGlmICghT2JqZWN0LnByb3RvdHlwZS5oYXNPd25Qcm9wZXJ0eS5jYWxsKG9wdGlvbiwgJ3ZhbHVlJykpIHtcclxuICAgICAgICBjeWNsZUNhdGVnb3JpZXMob3B0aW9uKTtcclxuICAgICAgfSBlbHNlIHtcclxuICAgICAgICBsZXQgZGVzY05hbWUgPSBgICAtLSR7b3B0aW9uLmNsaU5hbWUgfHwgbmFtZX0gJHtcclxuICAgICAgICAgICgnPCcgKyBvcHRpb24udHlwZSArICc+JykuZ3JlZW5cclxuICAgICAgICB9IGA7XHJcbiAgICAgICAgaWYgKGRlc2NOYW1lLmxlbmd0aCA8IHBhZCkge1xyXG4gICAgICAgICAgZm9yIChsZXQgaSA9IGRlc2NOYW1lLmxlbmd0aDsgaSA8IHBhZDsgaSsrKSB7XHJcbiAgICAgICAgICAgIGRlc2NOYW1lICs9ICcuJztcclxuICAgICAgICAgIH1cclxuICAgICAgICB9XHJcblxyXG4gICAgICAgIC8vIERpc3BsYXkgY29ycmVjdGx5IGFsaWduZWQgbWVzc2FnZXNcclxuICAgICAgICBjb25zb2xlLmxvZyhcclxuICAgICAgICAgIGRlc2NOYW1lLFxyXG4gICAgICAgICAgb3B0aW9uLmRlc2NyaXB0aW9uLFxyXG4gICAgICAgICAgYFtEZWZhdWx0OiAke29wdGlvbi52YWx1ZS50b1N0cmluZygpLmJvbGR9XWAuYmx1ZVxyXG4gICAgICAgICk7XHJcbiAgICAgIH1cclxuICAgIH1cclxuICB9O1xyXG5cclxuICAvLyBDeWNsZSB0aHJvdWdoIG9wdGlvbnMgb2YgZWFjaCBjYXRlZ29yaWVzIGFuZCBkaXNwbGF5IHRoZSB1c2FnZSBpbmZvXHJcbiAgT2JqZWN0LmtleXMoZGVmYXVsdENvbmZpZykuZm9yRWFjaCgoY2F0ZWdvcnkpID0+IHtcclxuICAgIC8vIE9ubHkgcHVwcGV0ZWVyIGFuZCBoaWdoY2hhcnRzIGNhdGVnb3JpZXMgY2Fubm90IGJlIGNvbmZpZ3VyZWQgdGhyb3VnaCBDTElcclxuICAgIGlmICghWydwdXBwZXRlZXInLCAnaGlnaGNoYXJ0cyddLmluY2x1ZGVzKGNhdGVnb3J5KSkge1xyXG4gICAgICBjb25zb2xlLmxvZyhgXFxuJHtjYXRlZ29yeS50b1VwcGVyQ2FzZSgpfWAucmVkKTtcclxuICAgICAgY3ljbGVDYXRlZ29yaWVzKGRlZmF1bHRDb25maWdbY2F0ZWdvcnldKTtcclxuICAgIH1cclxuICB9KTtcclxuICBjb25zb2xlLmxvZygnXFxuJyk7XHJcbn1cclxuXHJcbi8qKlxyXG4gKiBSb3VuZHMgYSBudW1iZXIgdG8gdGhlIHNwZWNpZmllZCBwcmVjaXNpb24uXHJcbiAqXHJcbiAqIEBwYXJhbSB7bnVtYmVyfSB2YWx1ZSAtIFRoZSBudW1iZXIgdG8gYmUgcm91bmRlZC5cclxuICogQHBhcmFtIHtudW1iZXJ9IHByZWNpc2lvbiAtIFRoZSBudW1iZXIgb2YgZGVjaW1hbCBwbGFjZXMgdG8gcm91bmQgdG8uXHJcbiAqXHJcbiAqIEByZXR1cm5zIHtudW1iZXJ9IC0gVGhlIHJvdW5kZWQgbnVtYmVyLlxyXG4gKi9cclxuZXhwb3J0IGNvbnN0IHJvdW5kTnVtYmVyID0gKHZhbHVlLCBwcmVjaXNpb24gPSAxKSA9PiB7XHJcbiAgY29uc3QgbXVsdGlwbGllciA9IE1hdGgucG93KDEwLCBwcmVjaXNpb24gfHwgMCk7XHJcbiAgcmV0dXJuIE1hdGgucm91bmQoK3ZhbHVlICogbXVsdGlwbGllcikgLyBtdWx0aXBsaWVyO1xyXG59O1xyXG5cclxuLyoqXHJcbiAqIENvbnZlcnRzIGEgdmFsdWUgdG8gYSBib29sZWFuLlxyXG4gKlxyXG4gKiBAcGFyYW0ge2FueX0gaXRlbSAtIFRoZSB2YWx1ZSB0byBiZSBjb252ZXJ0ZWQgdG8gYSBib29sZWFuLlxyXG4gKlxyXG4gKiBAcmV0dXJucyB7Ym9vbGVhbn0gLSBUaGUgYm9vbGVhbiByZXByZXNlbnRhdGlvbiBvZiB0aGUgaW5wdXQgdmFsdWUuXHJcbiAqL1xyXG5leHBvcnQgY29uc3QgdG9Cb29sZWFuID0gKGl0ZW0pID0+XHJcbiAgWydmYWxzZScsICd1bmRlZmluZWQnLCAnbnVsbCcsICdOYU4nLCAnMCcsICcnXS5pbmNsdWRlcyhpdGVtKVxyXG4gICAgPyBmYWxzZVxyXG4gICAgOiAhIWl0ZW07XHJcblxyXG4vKipcclxuICogV3JhcHMgY3VzdG9tIGNvZGUgdG8gZXhlY3V0ZSBpdCBzYWZlbHkuXHJcbiAqXHJcbiAqIEBwYXJhbSB7c3RyaW5nfSBjdXN0b21Db2RlIC0gVGhlIGN1c3RvbSBjb2RlIHRvIGJlIHdyYXBwZWQuXHJcbiAqIEBwYXJhbSB7Ym9vbGVhbn0gYWxsb3dGaWxlUmVzb3VyY2VzIC0gRmxhZyB0byBhbGxvdyBsb2FkaW5nIGNvZGUgZnJvbSBhIGZpbGUuXHJcbiAqXHJcbiAqIEByZXR1cm5zIHtzdHJpbmd8Ym9vbGVhbn0gLSBUaGUgd3JhcHBlZCBjdXN0b20gY29kZSBvciBmYWxzZSBpZiB3cmFwcGluZ1xyXG4gKiBmYWlscy5cclxuICovXHJcbmV4cG9ydCBjb25zdCB3cmFwQXJvdW5kID0gKGN1c3RvbUNvZGUsIGFsbG93RmlsZVJlc291cmNlcykgPT4ge1xyXG4gIGlmIChjdXN0b21Db2RlICYmIHR5cGVvZiBjdXN0b21Db2RlID09PSAnc3RyaW5nJykge1xyXG4gICAgY3VzdG9tQ29kZSA9IGN1c3RvbUNvZGUudHJpbSgpO1xyXG5cclxuICAgIGlmIChjdXN0b21Db2RlLmVuZHNXaXRoKCcuanMnKSkge1xyXG4gICAgICByZXR1cm4gYWxsb3dGaWxlUmVzb3VyY2VzXHJcbiAgICAgICAgPyB3cmFwQXJvdW5kKHJlYWRGaWxlU3luYyhjdXN0b21Db2RlLCAndXRmOCcpKVxyXG4gICAgICAgIDogZmFsc2U7XHJcbiAgICB9IGVsc2UgaWYgKFxyXG4gICAgICBjdXN0b21Db2RlLnN0YXJ0c1dpdGgoJ2Z1bmN0aW9uKCknKSB8fFxyXG4gICAgICBjdXN0b21Db2RlLnN0YXJ0c1dpdGgoJ2Z1bmN0aW9uICgpJykgfHxcclxuICAgICAgY3VzdG9tQ29kZS5zdGFydHNXaXRoKCcoKT0+JykgfHxcclxuICAgICAgY3VzdG9tQ29kZS5zdGFydHNXaXRoKCcoKSA9PicpXHJcbiAgICApIHtcclxuICAgICAgcmV0dXJuIGAoJHtjdXN0b21Db2RlfSkoKWA7XHJcbiAgICB9XHJcbiAgICByZXR1cm4gY3VzdG9tQ29kZS5yZXBsYWNlKC87JC8sICcnKTtcclxuICB9XHJcbn07XHJcblxyXG4vKipcclxuICogVXRpbGl0eSB0byBtZWFzdXJlIGVsYXBzZWQgdGltZSB1c2luZyB0aGUgTm9kZS5qcyBwcm9jZXNzLmhydGltZSgpIG1ldGhvZC5cclxuICpcclxuICogQHJldHVybnMge2Z1bmN0aW9uKCk6IG51bWJlcn0gLSBBIGZ1bmN0aW9uIHRvIGNhbGN1bGF0ZSB0aGUgZWxhcHNlZCB0aW1lXHJcbiAqIGluIG1pbGxpc2Vjb25kcy5cclxuICovXHJcbmV4cG9ydCBjb25zdCBtZWFzdXJlVGltZSA9ICgpID0+IHtcclxuICBjb25zdCBzdGFydCA9IHByb2Nlc3MuaHJ0aW1lLmJpZ2ludCgpO1xyXG4gIHJldHVybiAoKSA9PiBOdW1iZXIocHJvY2Vzcy5ocnRpbWUuYmlnaW50KCkgLSBzdGFydCkgLyAxMDAwMDAwO1xyXG59O1xyXG5cclxuZXhwb3J0IGRlZmF1bHQge1xyXG4gIF9fZGlybmFtZSxcclxuICBjbGVhclRleHQsXHJcbiAgZXhwQmFja29mZixcclxuICBmaXhUeXBlLFxyXG4gIGhhbmRsZVJlc291cmNlcyxcclxuICBpc0NvcnJlY3RKU09OLFxyXG4gIGlzT2JqZWN0LFxyXG4gIGlzT2JqZWN0RW1wdHksXHJcbiAgaXNQcml2YXRlUmFuZ2VVcmxGb3VuZCxcclxuICBvcHRpb25zU3RyaW5naWZ5LFxyXG4gIHByaW50TG9nbyxcclxuICBwcmludFVzYWdlLFxyXG4gIHJvdW5kTnVtYmVyLFxyXG4gIHRvQm9vbGVhbixcclxuICB3cmFwQXJvdW5kLFxyXG4gIG1lYXN1cmVUaW1lXHJcbn07XHJcbiIsIi8qKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqXHJcblxyXG5IaWdoY2hhcnRzIEV4cG9ydCBTZXJ2ZXJcclxuXHJcbkNvcHlyaWdodCAoYykgMjAxNi0yMDI0LCBIaWdoc29mdFxyXG5cclxuTGljZW5jZWQgdW5kZXIgdGhlIE1JVCBsaWNlbmNlLlxyXG5cclxuQWRkaXRpb25hbGx5IGEgdmFsaWQgSGlnaGNoYXJ0cyBsaWNlbnNlIGlzIHJlcXVpcmVkIGZvciB1c2UuXHJcblxyXG5TZWUgTElDRU5TRSBmaWxlIGluIHJvb3QgZm9yIGRldGFpbHMuXHJcblxyXG4qKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqL1xyXG5cclxuaW1wb3J0IHsgZXhpc3RzU3luYywgcmVhZEZpbGVTeW5jLCBwcm9taXNlcyBhcyBmc1Byb21pc2VzIH0gZnJvbSAnZnMnO1xyXG5cclxuaW1wb3J0IHByb21wdHMgZnJvbSAncHJvbXB0cyc7XHJcblxyXG5pbXBvcnQge1xyXG4gIGFic29sdXRlUHJvcHMsXHJcbiAgZGVmYXVsdENvbmZpZyxcclxuICBuZXN0ZWRBcmdzLFxyXG4gIHByb21wdHNDb25maWdcclxufSBmcm9tICcuL3NjaGVtYXMvY29uZmlnLmpzJztcclxuaW1wb3J0IHsgZW52cyB9IGZyb20gJy4vZW52cy5qcyc7XHJcbmltcG9ydCB7IGxvZywgbG9nV2l0aFN0YWNrIH0gZnJvbSAnLi9sb2dnZXIuanMnO1xyXG5pbXBvcnQgeyBkZWVwQ29weSwgaXNPYmplY3QsIHByaW50VXNhZ2UsIHRvQm9vbGVhbiB9IGZyb20gJy4vdXRpbHMuanMnO1xyXG5cclxubGV0IGdlbmVyYWxPcHRpb25zID0ge307XHJcblxyXG4vKipcclxuICogUmV0cmlldmVzIGFuZCByZXR1cm5zIHRoZSBnZW5lcmFsIG9wdGlvbnMgZm9yIHRoZSBleHBvcnQgcHJvY2Vzcy5cclxuICpcclxuICogQHJldHVybnMge09iamVjdH0gVGhlIGdlbmVyYWwgb3B0aW9ucyBvYmplY3QuXHJcbiAqL1xyXG5leHBvcnQgY29uc3QgZ2V0T3B0aW9ucyA9ICgpID0+IGdlbmVyYWxPcHRpb25zO1xyXG5cclxuLyoqXHJcbiAqIEluaXRpYWxpemVzIGFuZCBzZXRzIHRoZSBnZW5lcmFsIG9wdGlvbnMgZm9yIHRoZSBzZXJ2ZXIgaW5zdGFjZSwga2VlcGluZ1xyXG4gKiB0aGUgcHJpbmNpcGxlIG9mIHRoZSBvcHRpb25zIGxvYWQgcHJpb3JpdHkuIEl0IGFjY2VwdHMgb3B0aW9uYWwgdXNlck9wdGlvbnNcclxuICogYW5kIGFyZ3MgZnJvbSB0aGUgQ0xJLlxyXG4gKlxyXG4gKiBAcGFyYW0ge09iamVjdH0gdXNlck9wdGlvbnMgLSBVc2VyLXByb3ZpZGVkIG9wdGlvbnMgZm9yIGN1c3RvbWl6YXRpb24uXHJcbiAqIEBwYXJhbSB7QXJyYXl9IGFyZ3MgLSBDb21tYW5kLWxpbmUgYXJndW1lbnRzIGZvciBhZGRpdGlvbmFsIGNvbmZpZ3VyYXRpb25cclxuICogKENMSSB1c2FnZSkuXHJcbiAqXHJcbiAqIEByZXR1cm5zIHtPYmplY3R9IFRoZSB1cGRhdGVkIGdlbmVyYWwgb3B0aW9ucyBvYmplY3QuXHJcbiAqL1xyXG5leHBvcnQgY29uc3Qgc2V0T3B0aW9ucyA9ICh1c2VyT3B0aW9ucywgYXJncykgPT4ge1xyXG4gIC8vIE9ubHkgZm9yIHRoZSBDTEkgdXNhZ2VcclxuICBpZiAoYXJncz8ubGVuZ3RoKSB7XHJcbiAgICAvLyBHZXQgdGhlIGFkZGl0aW9uYWwgb3B0aW9ucyBmcm9tIHRoZSBjdXN0b20gSlNPTiBmaWxlXHJcbiAgICBnZW5lcmFsT3B0aW9ucyA9IGxvYWRDb25maWdGaWxlKGFyZ3MpO1xyXG4gIH1cclxuXHJcbiAgLy8gVXBkYXRlIHRoZSBkZWZhdWx0IGNvbmZpZyB3aXRoIGEgY29ycmVjdCBvcHRpb24gdmFsdWVzXHJcbiAgdXBkYXRlRGVmYXVsdENvbmZpZyhkZWZhdWx0Q29uZmlnLCBnZW5lcmFsT3B0aW9ucyk7XHJcblxyXG4gIC8vIFNldCB2YWx1ZXMgZm9yIHNlcnZlcidzIG9wdGlvbnMgYW5kIHJldHVybnMgdGhlbVxyXG4gIGdlbmVyYWxPcHRpb25zID0gaW5pdE9wdGlvbnMoZGVmYXVsdENvbmZpZyk7XHJcblxyXG4gIC8vIEFwcGx5IHVzZXIgb3B0aW9ucyBpZiB0aGVyZSBhcmUgYW55XHJcbiAgaWYgKHVzZXJPcHRpb25zKSB7XHJcbiAgICAvLyBNZXJnZSB1c2VyIG9wdGlvbnNcclxuICAgIGdlbmVyYWxPcHRpb25zID0gbWVyZ2VDb25maWdPcHRpb25zKFxyXG4gICAgICBnZW5lcmFsT3B0aW9ucyxcclxuICAgICAgdXNlck9wdGlvbnMsXHJcbiAgICAgIGFic29sdXRlUHJvcHNcclxuICAgICk7XHJcbiAgfVxyXG5cclxuICAvLyBPbmx5IGZvciB0aGUgQ0xJIHVzYWdlXHJcbiAgaWYgKGFyZ3M/Lmxlbmd0aCkge1xyXG4gICAgLy8gUGFpciBwcm92aWRlZCBhcmd1bWVudHNcclxuICAgIGdlbmVyYWxPcHRpb25zID0gcGFpckFyZ3VtZW50VmFsdWUoZ2VuZXJhbE9wdGlvbnMsIGFyZ3MsIGRlZmF1bHRDb25maWcpO1xyXG4gIH1cclxuXHJcbiAgLy8gUmV0dXJuIGZpbmFsIGdlbmVyYWwgb3B0aW9uc1xyXG4gIHJldHVybiBnZW5lcmFsT3B0aW9ucztcclxufTtcclxuXHJcbi8qKlxyXG4gKiBBbGxvd3MgbWFudWFsIGNvbmZpZ3VyYXRpb24gYmFzZWQgb24gc3BlY2lmaWVkIHByb21wdHMgYW5kIHNhdmVzXHJcbiAqIHRoZSBjb25maWd1cmF0aW9uIHRvIGEgZmlsZS5cclxuICpcclxuICogQHBhcmFtIHtzdHJpbmd9IGNvbmZpZ0ZpbGVOYW1lIC0gVGhlIG5hbWUgb2YgdGhlIGNvbmZpZ3VyYXRpb24gZmlsZS5cclxuICpcclxuICogQHJldHVybnMge1Byb21pc2U8Ym9vbGVhbj59IEEgUHJvbWlzZSB0aGF0IHJlc29sdmVzIHRvIHRydWUgb25jZSB0aGUgbWFudWFsXHJcbiAqIGNvbmZpZ3VyYXRpb24gaXMgY29tcGxldGVkIGFuZCBzYXZlZC5cclxuICovXHJcbmV4cG9ydCBjb25zdCBtYW51YWxDb25maWcgPSBhc3luYyAoY29uZmlnRmlsZU5hbWUpID0+IHtcclxuICAvLyBQcmVwYXJlIGEgY29uZmlnIG9iamVjdFxyXG4gIGxldCBjb25maWdGaWxlID0ge307XHJcblxyXG4gIC8vIENoZWNrIGlmIHByb3ZpZGVkIGNvbmZpZyBmaWxlIGV4aXN0c1xyXG4gIGlmIChleGlzdHNTeW5jKGNvbmZpZ0ZpbGVOYW1lKSkge1xyXG4gICAgY29uZmlnRmlsZSA9IEpTT04ucGFyc2UocmVhZEZpbGVTeW5jKGNvbmZpZ0ZpbGVOYW1lLCAndXRmOCcpKTtcclxuICB9XHJcblxyXG4gIC8vIFF1ZXN0aW9uIGFib3V0IGEgY29uZmlndXJhdGlvbiBjYXRlZ29yeVxyXG4gIGNvbnN0IG9uU3VibWl0ID0gYXN5bmMgKHAsIGNhdGVnb3JpZXMpID0+IHtcclxuICAgIGxldCBxdWVzdGlvbnNDb3VudGVyID0gMDtcclxuICAgIGxldCBhbGxRdWVzdGlvbnMgPSBbXTtcclxuXHJcbiAgICAvLyBDcmVhdGUgYSBjb3JyZXNwb25kaW5nIHByb3BlcnR5IGluIHRoZSBtYW51YWxDb25maWcgb2JqZWN0XHJcbiAgICBmb3IgKGNvbnN0IHNlY3Rpb24gb2YgY2F0ZWdvcmllcykge1xyXG4gICAgICAvLyBNYXJrIGVhY2ggb3B0aW9uIHdpdGggYSBzZWN0aW9uXHJcbiAgICAgIHByb21wdHNDb25maWdbc2VjdGlvbl0gPSBwcm9tcHRzQ29uZmlnW3NlY3Rpb25dLm1hcCgob3B0aW9uKSA9PiAoe1xyXG4gICAgICAgIC4uLm9wdGlvbixcclxuICAgICAgICBzZWN0aW9uXHJcbiAgICAgIH0pKTtcclxuXHJcbiAgICAgIC8vIENvbGxlY3QgdGhlIHF1ZXN0aW9uc1xyXG4gICAgICBhbGxRdWVzdGlvbnMgPSBbLi4uYWxsUXVlc3Rpb25zLCAuLi5wcm9tcHRzQ29uZmlnW3NlY3Rpb25dXTtcclxuICAgIH1cclxuXHJcbiAgICBhd2FpdCBwcm9tcHRzKGFsbFF1ZXN0aW9ucywge1xyXG4gICAgICBvblN1Ym1pdDogYXN5bmMgKHByb21wdCwgYW5zd2VyKSA9PiB7XHJcbiAgICAgICAgLy8gR2V0IHRoZSBkZWZhdWx0IG1vZHVsZSBzY3JpcHRzXHJcbiAgICAgICAgaWYgKHByb21wdC5uYW1lID09PSAnbW9kdWxlU2NyaXB0cycpIHtcclxuICAgICAgICAgIGFuc3dlciA9IGFuc3dlci5sZW5ndGhcclxuICAgICAgICAgICAgPyBhbnN3ZXIubWFwKChtb2R1bGUpID0+IHByb21wdC5jaG9pY2VzW21vZHVsZV0pXHJcbiAgICAgICAgICAgIDogcHJvbXB0LmNob2ljZXM7XHJcblxyXG4gICAgICAgICAgY29uZmlnRmlsZVtwcm9tcHQuc2VjdGlvbl1bcHJvbXB0Lm5hbWVdID0gYW5zd2VyO1xyXG4gICAgICAgIH0gZWxzZSB7XHJcbiAgICAgICAgICBjb25maWdGaWxlW3Byb21wdC5zZWN0aW9uXSA9IHJlY3Vyc2l2ZVByb3BzKFxyXG4gICAgICAgICAgICBPYmplY3QuYXNzaWduKHt9LCBjb25maWdGaWxlW3Byb21wdC5zZWN0aW9uXSB8fCB7fSksXHJcbiAgICAgICAgICAgIHByb21wdC5uYW1lLnNwbGl0KCcuJyksXHJcbiAgICAgICAgICAgIHByb21wdC5jaG9pY2VzID8gcHJvbXB0LmNob2ljZXNbYW5zd2VyXSA6IGFuc3dlclxyXG4gICAgICAgICAgKTtcclxuICAgICAgICB9XHJcblxyXG4gICAgICAgIGlmICgrK3F1ZXN0aW9uc0NvdW50ZXIgPT09IGFsbFF1ZXN0aW9ucy5sZW5ndGgpIHtcclxuICAgICAgICAgIHRyeSB7XHJcbiAgICAgICAgICAgIGF3YWl0IGZzUHJvbWlzZXMud3JpdGVGaWxlKFxyXG4gICAgICAgICAgICAgIGNvbmZpZ0ZpbGVOYW1lLFxyXG4gICAgICAgICAgICAgIEpTT04uc3RyaW5naWZ5KGNvbmZpZ0ZpbGUsIG51bGwsIDIpLFxyXG4gICAgICAgICAgICAgICd1dGY4J1xyXG4gICAgICAgICAgICApO1xyXG4gICAgICAgICAgfSBjYXRjaCAoZXJyb3IpIHtcclxuICAgICAgICAgICAgbG9nV2l0aFN0YWNrKFxyXG4gICAgICAgICAgICAgIDEsXHJcbiAgICAgICAgICAgICAgZXJyb3IsXHJcbiAgICAgICAgICAgICAgYFtjb25maWddIEFuIGVycm9yIG9jY3VycmVkIHdoaWxlIGNyZWF0aW5nIHRoZSAke2NvbmZpZ0ZpbGVOYW1lfSBmaWxlLmBcclxuICAgICAgICAgICAgKTtcclxuICAgICAgICAgIH1cclxuICAgICAgICAgIHJldHVybiB0cnVlO1xyXG4gICAgICAgIH1cclxuICAgICAgfVxyXG4gICAgfSk7XHJcblxyXG4gICAgcmV0dXJuIHRydWU7XHJcbiAgfTtcclxuXHJcbiAgLy8gRmluZCB0aGUgY2F0ZWdvcmllc1xyXG4gIGNvbnN0IGNob2ljZXMgPSBPYmplY3Qua2V5cyhwcm9tcHRzQ29uZmlnKS5tYXAoKGNob2ljZSkgPT4gKHtcclxuICAgIHRpdGxlOiBgJHtjaG9pY2V9IG9wdGlvbnNgLFxyXG4gICAgdmFsdWU6IGNob2ljZVxyXG4gIH0pKTtcclxuXHJcbiAgLy8gQ2F0ZWdvcnkgcHJvbXB0XHJcbiAgcmV0dXJuIHByb21wdHMoXHJcbiAgICB7XHJcbiAgICAgIHR5cGU6ICdtdWx0aXNlbGVjdCcsXHJcbiAgICAgIG5hbWU6ICdjYXRlZ29yeScsXHJcbiAgICAgIG1lc3NhZ2U6ICdXaGljaCBjYXRlZ29yeSBkbyB5b3Ugd2FudCB0byBjb25maWd1cmU/JyxcclxuICAgICAgaGludDogJ1NwYWNlOiBTZWxlY3Qgc3BlY2lmaWMsIEE6IFNlbGVjdCBhbGwsIEVudGVyOiBDb25maXJtLicsXHJcbiAgICAgIGluc3RydWN0aW9uczogJycsXHJcbiAgICAgIGNob2ljZXNcclxuICAgIH0sXHJcbiAgICB7IG9uU3VibWl0IH1cclxuICApO1xyXG59O1xyXG5cclxuLyoqXHJcbiAqIE1hcHMgb2xkLXN0cnVjdHVyZWQgKFBoYW50b21KUykgb3B0aW9ucyB0byBhIG5ldyBjb25maWd1cmF0aW9uIGZvcm1hdFxyXG4gKiAoUHVwcGV0ZWVyKS5cclxuICpcclxuICogQHBhcmFtIHtPYmplY3R9IG9sZE9wdGlvbnMgLSBPbGQtc3RydWN0dXJlZCBvcHRpb25zIHRvIGJlIG1hcHBlZC5cclxuICpcclxuICogQHJldHVybnMge09iamVjdH0gTmV3IG9wdGlvbnMgc3RydWN0dXJlZCBiYXNlZCBvbiB0aGUgZGVmaW5lZCBuZXN0ZWRBcmdzXHJcbiAqIG1hcHBpbmcuXHJcbiAqL1xyXG5leHBvcnQgY29uc3QgbWFwVG9OZXdDb25maWcgPSAob2xkT3B0aW9ucykgPT4ge1xyXG4gIGNvbnN0IG5ld09wdGlvbnMgPSB7fTtcclxuICAvLyBDeWNsZSB0aHJvdWdoIG9sZC1zdHJ1Y3R1cmVkIG9wdGlvbnNcclxuICBmb3IgKGNvbnN0IFtrZXksIHZhbHVlXSBvZiBPYmplY3QuZW50cmllcyhvbGRPcHRpb25zKSkge1xyXG4gICAgY29uc3QgcHJvcGVydGllc0NoYWluID0gbmVzdGVkQXJnc1trZXldID8gbmVzdGVkQXJnc1trZXldLnNwbGl0KCcuJykgOiBbXTtcclxuXHJcbiAgICAvLyBQb3B1bGF0ZSBvYmplY3QgaW4gY29ycmVjdCBwcm9wZXJ0aWVzIGxldmVsc1xyXG4gICAgcHJvcGVydGllc0NoYWluLnJlZHVjZShcclxuICAgICAgKG9iaiwgcHJvcCwgaW5kZXgpID0+XHJcbiAgICAgICAgKG9ialtwcm9wXSA9XHJcbiAgICAgICAgICBwcm9wZXJ0aWVzQ2hhaW4ubGVuZ3RoIC0gMSA9PT0gaW5kZXggPyB2YWx1ZSA6IG9ialtwcm9wXSB8fCB7fSksXHJcbiAgICAgIG5ld09wdGlvbnNcclxuICAgICk7XHJcbiAgfVxyXG4gIHJldHVybiBuZXdPcHRpb25zO1xyXG59O1xyXG5cclxuLyoqXHJcbiAqIE1lcmdlcyB0d28gc2V0cyBvZiBjb25maWd1cmF0aW9uIG9wdGlvbnMsIGNvbnNpZGVyaW5nIGFic29sdXRlIHByb3BlcnRpZXMuXHJcbiAqXHJcbiAqIEBwYXJhbSB7T2JqZWN0fSBvcHRpb25zIC0gT3JpZ2luYWwgY29uZmlndXJhdGlvbiBvcHRpb25zLlxyXG4gKiBAcGFyYW0ge09iamVjdH0gbmV3T3B0aW9ucyAtIE5ldyBjb25maWd1cmF0aW9uIG9wdGlvbnMgdG8gYmUgbWVyZ2VkLlxyXG4gKiBAcGFyYW0ge0FycmF5fSBhYnNvbHV0ZVByb3BzIC0gTGlzdCBvZiBwcm9wZXJ0aWVzIHRoYXQgc2hvdWxkXHJcbiAqIG5vdCBiZSByZWN1cnNpdmVseSBtZXJnZWQuXHJcbiAqXHJcbiAqIEByZXR1cm5zIHtPYmplY3R9IE1lcmdlZCBjb25maWd1cmF0aW9uIG9wdGlvbnMuXHJcbiAqL1xyXG5leHBvcnQgY29uc3QgbWVyZ2VDb25maWdPcHRpb25zID0gKG9wdGlvbnMsIG5ld09wdGlvbnMsIGFic29sdXRlUHJvcHMgPSBbXSkgPT4ge1xyXG4gIGNvbnN0IG1lcmdlZE9wdGlvbnMgPSBkZWVwQ29weShvcHRpb25zKTtcclxuXHJcbiAgZm9yIChjb25zdCBba2V5LCB2YWx1ZV0gb2YgT2JqZWN0LmVudHJpZXMobmV3T3B0aW9ucykpIHtcclxuICAgIG1lcmdlZE9wdGlvbnNba2V5XSA9XHJcbiAgICAgIGlzT2JqZWN0KHZhbHVlKSAmJlxyXG4gICAgICAhYWJzb2x1dGVQcm9wcy5pbmNsdWRlcyhrZXkpICYmXHJcbiAgICAgIG1lcmdlZE9wdGlvbnNba2V5XSAhPT0gdW5kZWZpbmVkXHJcbiAgICAgICAgPyBtZXJnZUNvbmZpZ09wdGlvbnMobWVyZ2VkT3B0aW9uc1trZXldLCB2YWx1ZSwgYWJzb2x1dGVQcm9wcylcclxuICAgICAgICA6IHZhbHVlICE9PSB1bmRlZmluZWRcclxuICAgICAgICAgID8gdmFsdWVcclxuICAgICAgICAgIDogbWVyZ2VkT3B0aW9uc1trZXldO1xyXG4gIH1cclxuXHJcbiAgcmV0dXJuIG1lcmdlZE9wdGlvbnM7XHJcbn07XHJcblxyXG4vKipcclxuICogSW5pdGlhbGl6ZXMgZXhwb3J0IHNldHRpbmdzIGJhc2VkIG9uIHByb3ZpZGVkIGV4cG9ydE9wdGlvbnNcclxuICogYW5kIGdlbmVyYWxPcHRpb25zLlxyXG4gKlxyXG4gKiBAcGFyYW0ge09iamVjdH0gZXhwb3J0T3B0aW9ucyAtIE9wdGlvbnMgc3BlY2lmaWMgdG8gdGhlIGV4cG9ydCBwcm9jZXNzLlxyXG4gKiBAcGFyYW0ge09iamVjdH0gZ2VuZXJhbE9wdGlvbnMgLSBHZW5lcmFsIGNvbmZpZ3VyYXRpb24gb3B0aW9ucy5cclxuICpcclxuICogQHJldHVybnMge09iamVjdH0gSW5pdGlhbGl6ZWQgZXhwb3J0IHNldHRpbmdzLlxyXG4gKi9cclxuZXhwb3J0IGNvbnN0IGluaXRFeHBvcnRTZXR0aW5ncyA9IChleHBvcnRPcHRpb25zLCBnZW5lcmFsT3B0aW9ucyA9IHt9KSA9PiB7XHJcbiAgbGV0IG9wdGlvbnMgPSB7fTtcclxuXHJcbiAgaWYgKGV4cG9ydE9wdGlvbnMuc3ZnKSB7XHJcbiAgICBvcHRpb25zID0gZGVlcENvcHkoZ2VuZXJhbE9wdGlvbnMpO1xyXG4gICAgb3B0aW9ucy5leHBvcnQudHlwZSA9IGV4cG9ydE9wdGlvbnMudHlwZSB8fCBleHBvcnRPcHRpb25zLmV4cG9ydC50eXBlO1xyXG4gICAgb3B0aW9ucy5leHBvcnQuc2NhbGUgPSBleHBvcnRPcHRpb25zLnNjYWxlIHx8IGV4cG9ydE9wdGlvbnMuZXhwb3J0LnNjYWxlO1xyXG4gICAgb3B0aW9ucy5leHBvcnQub3V0ZmlsZSA9XHJcbiAgICAgIGV4cG9ydE9wdGlvbnMub3V0ZmlsZSB8fCBleHBvcnRPcHRpb25zLmV4cG9ydC5vdXRmaWxlO1xyXG4gICAgb3B0aW9ucy5wYXlsb2FkID0ge1xyXG4gICAgICBzdmc6IGV4cG9ydE9wdGlvbnMuc3ZnXHJcbiAgICB9O1xyXG4gIH0gZWxzZSB7XHJcbiAgICBvcHRpb25zID0gbWVyZ2VDb25maWdPcHRpb25zKFxyXG4gICAgICBnZW5lcmFsT3B0aW9ucyxcclxuICAgICAgZXhwb3J0T3B0aW9ucyxcclxuICAgICAgLy8gT21pdCBnb2luZyBkb3duIHJlY3Vyc2l2ZWx5IHdpdGggdGhlIGJlbG93c1xyXG4gICAgICBhYnNvbHV0ZVByb3BzXHJcbiAgICApO1xyXG4gIH1cclxuXHJcbiAgb3B0aW9ucy5leHBvcnQub3V0ZmlsZSA9XHJcbiAgICBvcHRpb25zLmV4cG9ydD8ub3V0ZmlsZSB8fCBgY2hhcnQuJHtvcHRpb25zLmV4cG9ydD8udHlwZSB8fCAncG5nJ31gO1xyXG4gIHJldHVybiBvcHRpb25zO1xyXG59O1xyXG5cclxuLyoqXHJcbiAqIExvYWRzIGFkZGl0aW9uYWwgY29uZmlndXJhdGlvbiBmcm9tIGEgc3BlY2lmaWVkIGZpbGUgdXNpbmdcclxuICogdGhlIC0tbG9hZENvbmZpZyBvcHRpb24uXHJcbiAqXHJcbiAqIEBwYXJhbSB7QXJyYXl9IGFyZ3MgLSBDb21tYW5kLWxpbmUgYXJndW1lbnRzIHRvIGNoZWNrIGZvclxyXG4gKiB0aGUgLS1sb2FkQ29uZmlnIG9wdGlvbi5cclxuICpcclxuICogQHJldHVybnMge09iamVjdH0gQWRkaXRpb25hbCBjb25maWd1cmF0aW9uIGxvYWRlZCBmcm9tIHRoZSBzcGVjaWZpZWQgZmlsZSxcclxuICogb3IgYW4gZW1wdHkgb2JqZWN0IGlmIG5vdCBmb3VuZCBvciBpbnZhbGlkLlxyXG4gKi9cclxuZnVuY3Rpb24gbG9hZENvbmZpZ0ZpbGUoYXJncykge1xyXG4gIC8vIENoZWNrIGlmIHRoZSAtLWxvYWRDb25maWcgb3B0aW9uIHdhcyB1c2VkXHJcbiAgY29uc3QgY29uZmlnSW5kZXggPSBhcmdzLmZpbmRJbmRleChcclxuICAgIChhcmcpID0+IGFyZy5yZXBsYWNlKC8tL2csICcnKSA9PT0gJ2xvYWRDb25maWcnXHJcbiAgKTtcclxuXHJcbiAgLy8gQ2hlY2sgaWYgdGhlIC0tbG9hZENvbmZpZyBoYXMgYSB2YWx1ZVxyXG4gIGlmIChjb25maWdJbmRleCA+IC0xICYmIGFyZ3NbY29uZmlnSW5kZXggKyAxXSkge1xyXG4gICAgY29uc3QgZmlsZU5hbWUgPSBhcmdzW2NvbmZpZ0luZGV4ICsgMV07XHJcbiAgICB0cnkge1xyXG4gICAgICAvLyBDaGVjayBpZiBhbiBhZGRpdGlvbmFsIGNvbmZpZyBmaWxlIGlzIGEgY29ycmVjdCBKU09OIGZpbGVcclxuICAgICAgaWYgKGZpbGVOYW1lICYmIGZpbGVOYW1lLmVuZHNXaXRoKCcuanNvbicpKSB7XHJcbiAgICAgICAgLy8gTG9hZCBhbiBvcHRpb25hbCBjdXN0b20gSlNPTiBjb25maWcgZmlsZVxyXG4gICAgICAgIHJldHVybiBKU09OLnBhcnNlKHJlYWRGaWxlU3luYyhmaWxlTmFtZSkpO1xyXG4gICAgICB9XHJcbiAgICB9IGNhdGNoIChlcnJvcikge1xyXG4gICAgICBsb2dXaXRoU3RhY2soXHJcbiAgICAgICAgMixcclxuICAgICAgICBlcnJvcixcclxuICAgICAgICBgW2NvbmZpZ10gVW5hYmxlIHRvIGxvYWQgdGhlIGNvbmZpZ3VyYXRpb24gZnJvbSB0aGUgJHtmaWxlTmFtZX0gZmlsZS5gXHJcbiAgICAgICk7XHJcbiAgICB9XHJcbiAgfVxyXG5cclxuICAvLyBObyBhZGRpdGlvbmFsIG9wdGlvbnMgdG8gcmV0dXJuXHJcbiAgcmV0dXJuIHt9O1xyXG59XHJcblxyXG4vKipcclxuICogVXBkYXRlcyB0aGUgZGVmYXVsdCBjb25maWd1cmF0aW9uIG9iamVjdCB3aXRoIHZhbHVlcyBmcm9tIGEgY3VzdG9tIG9iamVjdFxyXG4gKiBhbmQgZW52aXJvbm1lbnQgdmFyaWFibGVzLlxyXG4gKlxyXG4gKiBAcGFyYW0ge09iamVjdH0gY29uZmlnT2JqIC0gVGhlIGRlZmF1bHQgY29uZmlndXJhdGlvbiBvYmplY3QuXHJcbiAqIEBwYXJhbSB7T2JqZWN0fSBjdXN0b21PYmogLSBDdXN0b20gY29uZmlndXJhdGlvbiBvYmplY3QgdG8gb3ZlcnJpZGUgZGVmYXVsdHMuXHJcbiAqIEBwYXJhbSB7c3RyaW5nfSBwcm9wQ2hhaW4gLSBQcm9wZXJ0eSBjaGFpbiBmb3IgdHJhY2tpbmcgbmVzdGVkIHByb3BlcnRpZXNcclxuICogZHVyaW5nIHJlY3Vyc2lvbi5cclxuICovXHJcbmZ1bmN0aW9uIHVwZGF0ZURlZmF1bHRDb25maWcoY29uZmlnT2JqLCBjdXN0b21PYmogPSB7fSwgcHJvcENoYWluID0gJycpIHtcclxuICBPYmplY3Qua2V5cyhjb25maWdPYmopLmZvckVhY2goKGtleSkgPT4ge1xyXG4gICAgY29uc3QgZW50cnkgPSBjb25maWdPYmpba2V5XTtcclxuICAgIGNvbnN0IGN1c3RvbVZhbHVlID0gY3VzdG9tT2JqICYmIGN1c3RvbU9ialtrZXldO1xyXG5cclxuICAgIGlmICh0eXBlb2YgZW50cnkudmFsdWUgPT09ICd1bmRlZmluZWQnKSB7XHJcbiAgICAgIHVwZGF0ZURlZmF1bHRDb25maWcoZW50cnksIGN1c3RvbVZhbHVlLCBgJHtwcm9wQ2hhaW59LiR7a2V5fWApO1xyXG4gICAgfSBlbHNlIHtcclxuICAgICAgLy8gSWYgYSB2YWx1ZSBmcm9tIGEgY3VzdG9tIEpTT04gZXhpc3RzLCBpdCB0YWtlIHByZWNlZGVuY2VcclxuICAgICAgaWYgKGN1c3RvbVZhbHVlICE9PSB1bmRlZmluZWQpIHtcclxuICAgICAgICBlbnRyeS52YWx1ZSA9IGN1c3RvbVZhbHVlO1xyXG4gICAgICB9XHJcblxyXG4gICAgICAvLyBJZiBhIHZhbHVlIGZyb20gYW4gZW52IHZhcmlhYmxlIGV4aXN0cywgaXQgdGFrZSBwcmVjZWRlbmNlXHJcbiAgICAgIGlmIChlbnRyeS5lbnZMaW5rIGluIGVudnMgJiYgZW52c1tlbnRyeS5lbnZMaW5rXSAhPT0gdW5kZWZpbmVkKSB7XHJcbiAgICAgICAgZW50cnkudmFsdWUgPSBlbnZzW2VudHJ5LmVudkxpbmtdO1xyXG4gICAgICB9XHJcbiAgICB9XHJcbiAgfSk7XHJcbn1cclxuXHJcbi8qKlxyXG4gKiBJbml0aWFsaXplcyBvcHRpb25zIG9iamVjdCBiYXNlZCBvbiBwcm92aWRlZCBpdGVtcywgc2V0dGluZyB2YWx1ZXMgZnJvbVxyXG4gKiBuZXN0ZWQgcHJvcGVydGllcyByZWN1cnNpdmVseS5cclxuICpcclxuICogQHBhcmFtIHtPYmplY3R9IGl0ZW1zIC0gQ29uZmlndXJhdGlvbiBpdGVtcyB0byBiZSB1c2VkIGZvciBpbml0aWFsaXppbmdcclxuICogb3B0aW9ucy5cclxuICpcclxuICogQHJldHVybnMge09iamVjdH0gSW5pdGlhbGl6ZWQgb3B0aW9ucyBvYmplY3QuXHJcbiAqL1xyXG5mdW5jdGlvbiBpbml0T3B0aW9ucyhpdGVtcykge1xyXG4gIGxldCBvcHRpb25zID0ge307XHJcbiAgZm9yIChjb25zdCBbbmFtZSwgaXRlbV0gb2YgT2JqZWN0LmVudHJpZXMoaXRlbXMpKSB7XHJcbiAgICBvcHRpb25zW25hbWVdID0gT2JqZWN0LnByb3RvdHlwZS5oYXNPd25Qcm9wZXJ0eS5jYWxsKGl0ZW0sICd2YWx1ZScpXHJcbiAgICAgID8gaXRlbS52YWx1ZVxyXG4gICAgICA6IGluaXRPcHRpb25zKGl0ZW0pO1xyXG4gIH1cclxuICByZXR1cm4gb3B0aW9ucztcclxufVxyXG5cclxuLyoqXHJcbiAqIFBhaXJzIGFyZ3VtZW50IHZhbHVlcyB3aXRoIGNvcnJlc3BvbmRpbmcgb3B0aW9ucyBpbiB0aGUgY29uZmlndXJhdGlvbixcclxuICogdXBkYXRpbmcgdGhlIG9wdGlvbnMgb2JqZWN0LlxyXG4gKlxyXG4gKiBAcGFyYW0ge09iamVjdH0gb3B0aW9ucyAtIENvbmZpZ3VyYXRpb24gb3B0aW9ucyBvYmplY3QgdG8gYmUgdXBkYXRlZC5cclxuICogQHBhcmFtIHtBcnJheX0gYXJncyAtIENvbW1hbmQtbGluZSBhcmd1bWVudHMgY29udGFpbmluZyB2YWx1ZXMgZm9yIHNwZWNpZmljXHJcbiAqIG9wdGlvbnMuXHJcbiAqIEBwYXJhbSB7T2JqZWN0fSBkZWZhdWx0Q29uZmlnIC0gRGVmYXVsdCBjb25maWd1cmF0aW9uIG9iamVjdCBmb3IgcmVmZXJlbmNlLlxyXG4gKlxyXG4gKiBAcmV0dXJucyB7T2JqZWN0fSBVcGRhdGVkIG9wdGlvbnMgb2JqZWN0LlxyXG4gKi9cclxuZnVuY3Rpb24gcGFpckFyZ3VtZW50VmFsdWUob3B0aW9ucywgYXJncywgZGVmYXVsdENvbmZpZykge1xyXG4gIGxldCBzaG93VXNhZ2UgPSBmYWxzZTtcclxuICBmb3IgKGxldCBpID0gMDsgaSA8IGFyZ3MubGVuZ3RoOyBpKyspIHtcclxuICAgIGNvbnN0IG9wdGlvbiA9IGFyZ3NbaV0ucmVwbGFjZSgvLS9nLCAnJyk7XHJcblxyXG4gICAgLy8gRmluZCB0aGUgcmlnaHQgcGxhY2UgZm9yIHByb3BlcnR5J3MgdmFsdWVcclxuICAgIGNvbnN0IHByb3BlcnRpZXNDaGFpbiA9IG5lc3RlZEFyZ3Nbb3B0aW9uXVxyXG4gICAgICA/IG5lc3RlZEFyZ3Nbb3B0aW9uXS5zcGxpdCgnLicpXHJcbiAgICAgIDogW107XHJcblxyXG4gICAgLy8gR2V0IHRoZSBjb3JyZWN0IHR5cGUgZm9yIENMSSBhcmdzIHdoaWNoIGFyZSBwYXNzZWQgYXMgc3RyaW5nc1xyXG4gICAgbGV0IGFyZ3VtZW50VHlwZTtcclxuICAgIHByb3BlcnRpZXNDaGFpbi5yZWR1Y2UoKG9iaiwgcHJvcCwgaW5kZXgpID0+IHtcclxuICAgICAgaWYgKHByb3BlcnRpZXNDaGFpbi5sZW5ndGggLSAxID09PSBpbmRleCkge1xyXG4gICAgICAgIGFyZ3VtZW50VHlwZSA9IG9ialtwcm9wXS50eXBlO1xyXG4gICAgICB9XHJcbiAgICAgIHJldHVybiBvYmpbcHJvcF07XHJcbiAgICB9LCBkZWZhdWx0Q29uZmlnKTtcclxuXHJcbiAgICBwcm9wZXJ0aWVzQ2hhaW4ucmVkdWNlKChvYmosIHByb3AsIGluZGV4KSA9PiB7XHJcbiAgICAgIGlmIChwcm9wZXJ0aWVzQ2hhaW4ubGVuZ3RoIC0gMSA9PT0gaW5kZXgpIHtcclxuICAgICAgICAvLyBGaW5kcyBhbiBvcHRpb24gYW5kIHNldCBhIGNvcnJlc3BvbmRpbmcgdmFsdWVcclxuICAgICAgICBpZiAodHlwZW9mIG9ialtwcm9wXSAhPT0gJ3VuZGVmaW5lZCcpIHtcclxuICAgICAgICAgIGlmIChhcmdzWysraV0pIHtcclxuICAgICAgICAgICAgaWYgKGFyZ3VtZW50VHlwZSA9PT0gJ2Jvb2xlYW4nKSB7XHJcbiAgICAgICAgICAgICAgb2JqW3Byb3BdID0gdG9Cb29sZWFuKGFyZ3NbaV0pO1xyXG4gICAgICAgICAgICB9IGVsc2UgaWYgKGFyZ3VtZW50VHlwZSA9PT0gJ251bWJlcicpIHtcclxuICAgICAgICAgICAgICBvYmpbcHJvcF0gPSArYXJnc1tpXTtcclxuICAgICAgICAgICAgfSBlbHNlIGlmIChhcmd1bWVudFR5cGUuaW5kZXhPZignXScpID49IDApIHtcclxuICAgICAgICAgICAgICBvYmpbcHJvcF0gPSBhcmdzW2ldLnNwbGl0KCcsJyk7XHJcbiAgICAgICAgICAgIH0gZWxzZSB7XHJcbiAgICAgICAgICAgICAgb2JqW3Byb3BdID0gYXJnc1tpXTtcclxuICAgICAgICAgICAgfVxyXG4gICAgICAgICAgfSBlbHNlIHtcclxuICAgICAgICAgICAgbG9nKFxyXG4gICAgICAgICAgICAgIDIsXHJcbiAgICAgICAgICAgICAgYFtjb25maWddIE1pc3NpbmcgdmFsdWUgZm9yIHRoZSAnJHtvcHRpb259JyBhcmd1bWVudC4gVXNpbmcgdGhlIGRlZmF1bHQgdmFsdWUuYFxyXG4gICAgICAgICAgICApO1xyXG4gICAgICAgICAgICBzaG93VXNhZ2UgPSB0cnVlO1xyXG4gICAgICAgICAgfVxyXG4gICAgICAgIH1cclxuICAgICAgfVxyXG4gICAgICByZXR1cm4gb2JqW3Byb3BdO1xyXG4gICAgfSwgb3B0aW9ucyk7XHJcbiAgfVxyXG5cclxuICAvLyBEaXNwbGF5IHRoZSB1c2FnZSBmb3IgdGhlIHJlZmVyZW5jZSBpZiBuZWVkZWRcclxuICBpZiAoc2hvd1VzYWdlKSB7XHJcbiAgICBwcmludFVzYWdlKGRlZmF1bHRDb25maWcpO1xyXG4gIH1cclxuXHJcbiAgcmV0dXJuIG9wdGlvbnM7XHJcbn1cclxuXHJcbi8qKlxyXG4gKiBSZWN1cnNpdmVseSB1cGRhdGVzIHByb3BlcnRpZXMgaW4gYW4gb2JqZWN0IGJhc2VkIG9uIG5lc3RlZCBuYW1lcyBhbmQgYXNzaWduc1xyXG4gKiB0aGUgZmluYWwgdmFsdWUuXHJcbiAqXHJcbiAqIEBwYXJhbSB7T2JqZWN0fSBvYmplY3RUb1VwZGF0ZSAtIFRoZSBvYmplY3QgdG8gYmUgdXBkYXRlZC5cclxuICogQHBhcmFtIHtBcnJheX0gbmVzdGVkTmFtZXMgLSBBcnJheSBvZiBuZXN0ZWQgcHJvcGVydHkgbmFtZXMuXHJcbiAqIEBwYXJhbSB7YW55fSB2YWx1ZSAtIFRoZSBmaW5hbCB2YWx1ZSB0byBiZSBhc3NpZ25lZC5cclxuICpcclxuICogQHJldHVybnMge09iamVjdH0gVXBkYXRlZCBvYmplY3Qgd2l0aCBhc3NpZ25lZCB2YWx1ZXMuXHJcbiAqL1xyXG5mdW5jdGlvbiByZWN1cnNpdmVQcm9wcyhvYmplY3RUb1VwZGF0ZSwgbmVzdGVkTmFtZXMsIHZhbHVlKSB7XHJcbiAgd2hpbGUgKG5lc3RlZE5hbWVzLmxlbmd0aCA+IDEpIHtcclxuICAgIGNvbnN0IHByb3BOYW1lID0gbmVzdGVkTmFtZXMuc2hpZnQoKTtcclxuXHJcbiAgICAvLyBDcmVhdGUgYSBwcm9wZXJ0eSBpbiBvYmplY3QgaWYgaXQgZG9lc24ndCBleGlzdFxyXG4gICAgaWYgKCFPYmplY3QucHJvdG90eXBlLmhhc093blByb3BlcnR5LmNhbGwob2JqZWN0VG9VcGRhdGUsIHByb3BOYW1lKSkge1xyXG4gICAgICBvYmplY3RUb1VwZGF0ZVtwcm9wTmFtZV0gPSB7fTtcclxuICAgIH1cclxuXHJcbiAgICAvLyBDYWxsIGZ1bmN0aW9uIGFnYWluIGlmIHRoZXJlIHN0aWxsIG5hbWVzIHRvIGdvXHJcbiAgICBvYmplY3RUb1VwZGF0ZVtwcm9wTmFtZV0gPSByZWN1cnNpdmVQcm9wcyhcclxuICAgICAgT2JqZWN0LmFzc2lnbih7fSwgb2JqZWN0VG9VcGRhdGVbcHJvcE5hbWVdKSxcclxuICAgICAgbmVzdGVkTmFtZXMsXHJcbiAgICAgIHZhbHVlXHJcbiAgICApO1xyXG5cclxuICAgIHJldHVybiBvYmplY3RUb1VwZGF0ZTtcclxuICB9XHJcblxyXG4gIC8vIEFzc2lnbiB0aGUgZmluYWwgdmFsdWVcclxuICBvYmplY3RUb1VwZGF0ZVtuZXN0ZWROYW1lc1swXV0gPSB2YWx1ZTtcclxuICByZXR1cm4gb2JqZWN0VG9VcGRhdGU7XHJcbn1cclxuXHJcbmV4cG9ydCBkZWZhdWx0IHtcclxuICBnZXRPcHRpb25zLFxyXG4gIHNldE9wdGlvbnMsXHJcbiAgbWFudWFsQ29uZmlnLFxyXG4gIG1hcFRvTmV3Q29uZmlnLFxyXG4gIG1lcmdlQ29uZmlnT3B0aW9ucyxcclxuICBpbml0RXhwb3J0U2V0dGluZ3NcclxufTtcclxuIiwiLyoqXHJcbiAqIFRoaXMgbW9kdWxlIGV4cG9ydHMgdHdvIGZ1bmN0aW9uczogZmV0Y2ggKGZvciBHRVQgcmVxdWVzdHMpIGFuZCBwb3N0IChmb3IgUE9TVCByZXF1ZXN0cykuXHJcbiAqL1xyXG5cclxuaW1wb3J0IGh0dHAgZnJvbSAnaHR0cCc7XHJcbmltcG9ydCBodHRwcyBmcm9tICdodHRwcyc7XHJcblxyXG4vKipcclxuICogUmV0dXJucyB0aGUgSFRUUCBvciBIVFRQUyBwcm90b2NvbCBtb2R1bGUgYmFzZWQgb24gdGhlIHByb3ZpZGVkIFVSTC5cclxuICpcclxuICogQHBhcmFtIHtzdHJpbmd9IHVybCAtIFRoZSBVUkwgdG8gZGV0ZXJtaW5lIHRoZSBwcm90b2NvbC5cclxuICpcclxuICogQHJldHVybnMge09iamVjdH0gVGhlIEhUVFAgb3IgSFRUUFMgcHJvdG9jb2wgbW9kdWxlIChodHRwIG9yIGh0dHBzKS5cclxuICovXHJcbmNvbnN0IGdldFByb3RvY29sID0gKHVybCkgPT4gKHVybC5zdGFydHNXaXRoKCdodHRwcycpID8gaHR0cHMgOiBodHRwKTtcclxuXHJcbi8qKlxyXG4gKiBGZXRjaGVzIGRhdGEgZnJvbSB0aGUgc3BlY2lmaWVkIFVSTCB1c2luZyBlaXRoZXIgSFRUUCBvciBIVFRQUyBwcm90b2NvbC5cclxuICpcclxuICogQHBhcmFtIHtzdHJpbmd9IHVybCAtIFRoZSBVUkwgdG8gZmV0Y2ggZGF0YSBmcm9tLlxyXG4gKiBAcGFyYW0ge09iamVjdH0gcmVxdWVzdE9wdGlvbnMgLSBPcHRpb25zIGZvciB0aGUgSFRUUCByZXF1ZXN0IChvcHRpb25hbCkuXHJcbiAqXHJcbiAqIEByZXR1cm5zIHtQcm9taXNlPE9iamVjdD59IFByb21pc2UgcmVzb2x2aW5nIHRvIHRoZSBIVFRQIHJlc3BvbnNlIG9iamVjdFxyXG4gKiB3aXRoIGFkZGVkICd0ZXh0JyBwcm9wZXJ0eSBvciByZWplY3Rpbmcgd2l0aCBhbiBlcnJvci5cclxuICovXHJcbmFzeW5jIGZ1bmN0aW9uIGZldGNoKHVybCwgcmVxdWVzdE9wdGlvbnMgPSB7fSkge1xyXG4gIHJldHVybiBuZXcgUHJvbWlzZSgocmVzb2x2ZSwgcmVqZWN0KSA9PiB7XHJcbiAgICBjb25zdCBwcm90b2NvbCA9IGdldFByb3RvY29sKHVybCk7XHJcblxyXG4gICAgcHJvdG9jb2xcclxuICAgICAgLmdldCh1cmwsIHJlcXVlc3RPcHRpb25zLCAocmVzKSA9PiB7XHJcbiAgICAgICAgbGV0IGRhdGEgPSAnJztcclxuXHJcbiAgICAgICAgLy8gQSBjaHVuayBvZiBkYXRhIGhhcyBiZWVuIHJlY2VpdmVkLlxyXG4gICAgICAgIHJlcy5vbignZGF0YScsIChjaHVuaykgPT4ge1xyXG4gICAgICAgICAgZGF0YSArPSBjaHVuaztcclxuICAgICAgICB9KTtcclxuXHJcbiAgICAgICAgLy8gVGhlIHdob2xlIHJlc3BvbnNlIGhhcyBiZWVuIHJlY2VpdmVkLlxyXG4gICAgICAgIHJlcy5vbignZW5kJywgKCkgPT4ge1xyXG4gICAgICAgICAgaWYgKCFkYXRhKSB7XHJcbiAgICAgICAgICAgIHJlamVjdCgnTm90aGluZyB3YXMgZmV0Y2hlZCBmcm9tIHRoZSBVUkwuJyk7XHJcbiAgICAgICAgICB9XHJcblxyXG4gICAgICAgICAgcmVzLnRleHQgPSBkYXRhO1xyXG4gICAgICAgICAgcmVzb2x2ZShyZXMpO1xyXG4gICAgICAgIH0pO1xyXG4gICAgICB9KVxyXG4gICAgICAub24oJ2Vycm9yJywgKGVycm9yKSA9PiB7XHJcbiAgICAgICAgcmVqZWN0KGVycm9yKTtcclxuICAgICAgfSk7XHJcbiAgfSk7XHJcbn1cclxuXHJcbi8qKlxyXG4gKiBTZW5kcyBhIFBPU1QgcmVxdWVzdCB0byB0aGUgc3BlY2lmaWVkIFVSTCB3aXRoIHRoZSBwcm92aWRlZCBKU09OIGJvZHkgdXNpbmdcclxuICogZWl0aGVyIEhUVFAgb3IgSFRUUFMgcHJvdG9jb2wuXHJcbiAqXHJcbiAqIEBwYXJhbSB7c3RyaW5nfSB1cmwgLSBUaGUgVVJMIHRvIHNlbmQgdGhlIFBPU1QgcmVxdWVzdCB0by5cclxuICogQHBhcmFtIHtPYmplY3R9IGJvZHkgLSBUaGUgSlNPTiBib2R5IHRvIGluY2x1ZGUgaW4gdGhlIFBPU1QgcmVxdWVzdFxyXG4gKiAob3B0aW9uYWwsIGRlZmF1bHQgaXMgYW4gZW1wdHkgb2JqZWN0KS5cclxuICogQHBhcmFtIHtPYmplY3R9IHJlcXVlc3RPcHRpb25zIC0gT3B0aW9ucyBmb3IgdGhlIEhUVFAgcmVxdWVzdCAob3B0aW9uYWwpLlxyXG4gKlxyXG4gKiBAcmV0dXJucyB7UHJvbWlzZTxPYmplY3Q+fSBQcm9taXNlIHJlc29sdmluZyB0byB0aGUgSFRUUCByZXNwb25zZSBvYmplY3Qgd2l0aFxyXG4gKiBhZGRlZCAndGV4dCcgcHJvcGVydHkgb3IgcmVqZWN0aW5nIHdpdGggYW4gZXJyb3IuXHJcbiAqL1xyXG5hc3luYyBmdW5jdGlvbiBwb3N0KHVybCwgYm9keSA9IHt9LCByZXF1ZXN0T3B0aW9ucyA9IHt9KSB7XHJcbiAgcmV0dXJuIG5ldyBQcm9taXNlKChyZXNvbHZlLCByZWplY3QpID0+IHtcclxuICAgIGNvbnN0IHByb3RvY29sID0gZ2V0UHJvdG9jb2wodXJsKTtcclxuICAgIGNvbnN0IGRhdGEgPSBKU09OLnN0cmluZ2lmeShib2R5KTtcclxuXHJcbiAgICAvLyBTZXQgZGVmYXVsdCBoZWFkZXJzIGFuZCBtZXJnZSB3aXRoIHJlcXVlc3RPcHRpb25zXHJcbiAgICBjb25zdCBvcHRpb25zID0gT2JqZWN0LmFzc2lnbihcclxuICAgICAge1xyXG4gICAgICAgIG1ldGhvZDogJ1BPU1QnLFxyXG4gICAgICAgIGhlYWRlcnM6IHtcclxuICAgICAgICAgICdDb250ZW50LVR5cGUnOiAnYXBwbGljYXRpb24vanNvbicsXHJcbiAgICAgICAgICAnQ29udGVudC1MZW5ndGgnOiBkYXRhLmxlbmd0aFxyXG4gICAgICAgIH1cclxuICAgICAgfSxcclxuICAgICAgcmVxdWVzdE9wdGlvbnNcclxuICAgICk7XHJcblxyXG4gICAgY29uc3QgcmVxID0gcHJvdG9jb2xcclxuICAgICAgLnJlcXVlc3QodXJsLCBvcHRpb25zLCAocmVzKSA9PiB7XHJcbiAgICAgICAgbGV0IHJlc3BvbnNlRGF0YSA9ICcnO1xyXG5cclxuICAgICAgICAvLyBBIGNodW5rIG9mIGRhdGEgaGFzIGJlZW4gcmVjZWl2ZWQuXHJcbiAgICAgICAgcmVzLm9uKCdkYXRhJywgKGNodW5rKSA9PiB7XHJcbiAgICAgICAgICByZXNwb25zZURhdGEgKz0gY2h1bms7XHJcbiAgICAgICAgfSk7XHJcblxyXG4gICAgICAgIC8vIFRoZSB3aG9sZSByZXNwb25zZSBoYXMgYmVlbiByZWNlaXZlZC5cclxuICAgICAgICByZXMub24oJ2VuZCcsICgpID0+IHtcclxuICAgICAgICAgIHRyeSB7XHJcbiAgICAgICAgICAgIHJlcy50ZXh0ID0gcmVzcG9uc2VEYXRhO1xyXG4gICAgICAgICAgICByZXNvbHZlKHJlcyk7XHJcbiAgICAgICAgICB9IGNhdGNoIChlcnJvcikge1xyXG4gICAgICAgICAgICByZWplY3QoZXJyb3IpO1xyXG4gICAgICAgICAgfVxyXG4gICAgICAgIH0pO1xyXG4gICAgICB9KVxyXG4gICAgICAub24oJ2Vycm9yJywgKGVycm9yKSA9PiB7XHJcbiAgICAgICAgcmVqZWN0KGVycm9yKTtcclxuICAgICAgfSk7XHJcblxyXG4gICAgLy8gV3JpdGUgdGhlIHJlcXVlc3QgYm9keSBhbmQgZW5kIHRoZSByZXF1ZXN0LlxyXG4gICAgcmVxLndyaXRlKGRhdGEpO1xyXG4gICAgcmVxLmVuZCgpO1xyXG4gIH0pO1xyXG59XHJcblxyXG5leHBvcnQgZGVmYXVsdCBmZXRjaDtcclxuZXhwb3J0IHsgZmV0Y2gsIHBvc3QgfTtcclxuIiwiY2xhc3MgRXhwb3J0RXJyb3IgZXh0ZW5kcyBFcnJvciB7XHJcbiAgY29uc3RydWN0b3IobWVzc2FnZSkge1xyXG4gICAgc3VwZXIoKTtcclxuICAgIHRoaXMubWVzc2FnZSA9IG1lc3NhZ2U7XHJcbiAgICB0aGlzLnN0YWNrTWVzc2FnZSA9IG1lc3NhZ2U7XHJcbiAgfVxyXG5cclxuICBzZXRFcnJvcihlcnJvcikge1xyXG4gICAgdGhpcy5lcnJvciA9IGVycm9yO1xyXG4gICAgaWYgKGVycm9yLm5hbWUpIHtcclxuICAgICAgdGhpcy5uYW1lID0gZXJyb3IubmFtZTtcclxuICAgIH1cclxuICAgIGlmIChlcnJvci5zdGF0dXNDb2RlKSB7XHJcbiAgICAgIHRoaXMuc3RhdHVzQ29kZSA9IGVycm9yLnN0YXR1c0NvZGU7XHJcbiAgICB9XHJcbiAgICBpZiAoZXJyb3Iuc3RhY2spIHtcclxuICAgICAgdGhpcy5zdGFja01lc3NhZ2UgPSBlcnJvci5tZXNzYWdlO1xyXG4gICAgICB0aGlzLnN0YWNrID0gZXJyb3Iuc3RhY2s7XHJcbiAgICB9XHJcbiAgICByZXR1cm4gdGhpcztcclxuICB9XHJcbn1cclxuXHJcbmV4cG9ydCBkZWZhdWx0IEV4cG9ydEVycm9yO1xyXG4iLCIvKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKlxyXG5cclxuSGlnaGNoYXJ0cyBFeHBvcnQgU2VydmVyXHJcblxyXG5Db3B5cmlnaHQgKGMpIDIwMTYtMjAyNCwgSGlnaHNvZnRcclxuXHJcbkxpY2VuY2VkIHVuZGVyIHRoZSBNSVQgbGljZW5jZS5cclxuXHJcbkFkZGl0aW9uYWxseSBhIHZhbGlkIEhpZ2hjaGFydHMgbGljZW5zZSBpcyByZXF1aXJlZCBmb3IgdXNlLlxyXG5cclxuU2VlIExJQ0VOU0UgZmlsZSBpbiByb290IGZvciBkZXRhaWxzLlxyXG5cclxuKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKi9cclxuXHJcbi8vIFRoZSBjYWNoZSBtYW5hZ2VyIG1hbmFnZXMgdGhlIEhpZ2hjaGFydHMgbGlicmFyeSBhbmQgaXRzIGRlcGVuZGVuY2llcy5cclxuLy8gVGhlIGNhY2hlIGl0c2VsZiBpcyBzdG9yZWQgaW4gLmNhY2hlLCBhbmQgaXMgY2hlY2tlZCBieSB0aGUgY29uZmlnIHN5c3RlbVxyXG4vLyBiZWZvcmUgc3RhcnRpbmcgdGhlIHNlcnZpY2VcclxuXHJcbmltcG9ydCB7IGV4aXN0c1N5bmMsIG1rZGlyU3luYywgcmVhZEZpbGVTeW5jLCB3cml0ZUZpbGVTeW5jIH0gZnJvbSAnZnMnO1xyXG5pbXBvcnQgeyBqb2luIH0gZnJvbSAncGF0aCc7XHJcblxyXG5pbXBvcnQgeyBIdHRwc1Byb3h5QWdlbnQgfSBmcm9tICdodHRwcy1wcm94eS1hZ2VudCc7XHJcblxyXG5pbXBvcnQgeyBnZXRPcHRpb25zIH0gZnJvbSAnLi9jb25maWcuanMnO1xyXG5pbXBvcnQgeyBlbnZzIH0gZnJvbSAnLi9lbnZzLmpzJztcclxuaW1wb3J0IHsgZmV0Y2ggfSBmcm9tICcuL2ZldGNoLmpzJztcclxuaW1wb3J0IHsgbG9nIH0gZnJvbSAnLi9sb2dnZXIuanMnO1xyXG5pbXBvcnQgeyBfX2Rpcm5hbWUgfSBmcm9tICcuL3V0aWxzLmpzJztcclxuXHJcbmltcG9ydCBFeHBvcnRFcnJvciBmcm9tICcuL2Vycm9ycy9FeHBvcnRFcnJvci5qcyc7XHJcblxyXG5jb25zdCBjYWNoZSA9IHtcclxuICBjZG5VUkw6ICdodHRwczovL2NvZGUuaGlnaGNoYXJ0cy5jb20vJyxcclxuICBhY3RpdmVNYW5pZmVzdDoge30sXHJcbiAgc291cmNlczogJycsXHJcbiAgaGNWZXJzaW9uOiAnJ1xyXG59O1xyXG5cclxuLyoqXHJcbiAqIEV4dHJhY3RzIGFuZCBjYWNoZXMgdGhlIEhpZ2hjaGFydHMgdmVyc2lvbiBmcm9tIHRoZSBzb3VyY2VzIHN0cmluZy5cclxuICpcclxuICogQHJldHVybnMge3N0cmluZ30gVGhlIGV4dHJhY3RlZCBIaWdoY2hhcnRzIHZlcnNpb24uXHJcbiAqL1xyXG5leHBvcnQgY29uc3QgZXh0cmFjdFZlcnNpb24gPSAoY2FjaGUpID0+IHtcclxuICByZXR1cm4gY2FjaGUuc291cmNlc1xyXG4gICAgLnN1YnN0cmluZygwLCBjYWNoZS5zb3VyY2VzLmluZGV4T2YoJyovJykpXHJcbiAgICAucmVwbGFjZSgnLyonLCAnJylcclxuICAgIC5yZXBsYWNlKCcqLycsICcnKVxyXG4gICAgLnJlcGxhY2UoL1xcbi9nLCAnJylcclxuICAgIC50cmltKCk7XHJcbn07XHJcblxyXG4vKipcclxuICogRXh0cmFjdHMgdGhlIEhpZ2hjaGFydHMgbW9kdWxlIG5hbWUgYmFzZWQgb24gdGhlIHNjcmlwdFBhdGguXHJcbiAqL1xyXG5leHBvcnQgY29uc3QgZXh0cmFjdE1vZHVsZU5hbWUgPSAoc2NyaXB0UGF0aCkgPT4ge1xyXG4gIHJldHVybiBzY3JpcHRQYXRoLnJlcGxhY2UoXHJcbiAgICAvKC4qKVxcL3woLiopbW9kdWxlc1xcL3xzdG9ja1xcLyguKilpbmRpY2F0b3JzXFwvfG1hcHNcXC8oLiopbW9kdWxlc1xcLy9naSxcclxuICAgICcnXHJcbiAgKTtcclxufTtcclxuXHJcbi8qKlxyXG4gKiBTYXZlcyB0aGUgcHJvdmlkZWQgY29uZmlndXJhdGlvbiBhbmQgZmV0Y2hlZCBtb2R1bGVzIHRvIHRoZSBjYWNoZSBtYW5pZmVzdFxyXG4gKiBmaWxlLlxyXG4gKlxyXG4gKiBAcGFyYW0ge29iamVjdH0gY29uZmlnIC0gSGlnaGNoYXJ0cy1yZWxhdGVkIGNvbmZpZ3VyYXRpb24gb2JqZWN0LlxyXG4gKiBAcGFyYW0ge29iamVjdH0gZmV0Y2hlZE1vZHVsZXMgLSBBbiBvYmplY3QgdGhhdCBjb250YWlucyBtYXBwZWQgbmFtZXMgb2ZcclxuICogZmV0Y2hlZCBIaWdoY2hhcnRzIG1vZHVsZXMgdG8gdXNlLlxyXG4gKlxyXG4gKiBAdGhyb3dzIHtFeHBvcnRFcnJvcn0gVGhyb3dzIGFuIEV4cG9ydEVycm9yIGlmIGFuIGVycm9yIG9jY3VycyB3aGlsZSB3cml0aW5nXHJcbiAqIHRoZSBjYWNoZSBtYW5pZmVzdC5cclxuICovXHJcbmV4cG9ydCBjb25zdCBzYXZlQ29uZmlnVG9NYW5pZmVzdCA9IGFzeW5jIChjb25maWcsIGZldGNoZWRNb2R1bGVzKSA9PiB7XHJcbiAgY29uc3QgbmV3TWFuaWZlc3QgPSB7XHJcbiAgICB2ZXJzaW9uOiBjb25maWcudmVyc2lvbixcclxuICAgIG1vZHVsZXM6IGZldGNoZWRNb2R1bGVzIHx8IHt9XHJcbiAgfTtcclxuXHJcbiAgLy8gVXBkYXRlIGNhY2hlIG9iamVjdCB3aXRoIHRoZSBjdXJyZW50IG1vZHVsZXNcclxuICBjYWNoZS5hY3RpdmVNYW5pZmVzdCA9IG5ld01hbmlmZXN0O1xyXG5cclxuICBsb2coMywgJ1tjYWNoZV0gV3JpdGluZyBhIG5ldyBtYW5pZmVzdC4nKTtcclxuICB0cnkge1xyXG4gICAgd3JpdGVGaWxlU3luYyhcclxuICAgICAgam9pbihfX2Rpcm5hbWUsIGNvbmZpZy5jYWNoZVBhdGgsICdtYW5pZmVzdC5qc29uJyksXHJcbiAgICAgIEpTT04uc3RyaW5naWZ5KG5ld01hbmlmZXN0KSxcclxuICAgICAgJ3V0ZjgnXHJcbiAgICApO1xyXG4gIH0gY2F0Y2ggKGVycm9yKSB7XHJcbiAgICB0aHJvdyBuZXcgRXhwb3J0RXJyb3IoJ1tjYWNoZV0gRXJyb3Igd3JpdGluZyB0aGUgY2FjaGUgbWFuaWZlc3QuJykuc2V0RXJyb3IoXHJcbiAgICAgIGVycm9yXHJcbiAgICApO1xyXG4gIH1cclxufTtcclxuXHJcbi8qKlxyXG4gKiBGZXRjaGVzIGEgc2luZ2xlIHNjcmlwdCBhbmQgdXBkYXRlcyB0aGUgZmV0Y2hlZE1vZHVsZXMgYWNjb3JkaW5nbHkuXHJcbiAqXHJcbiAqIEBwYXJhbSB7c3RyaW5nfSBzY3JpcHQgLSBBIHBhdGggdG8gc2NyaXB0IHRvIGdldC5cclxuICogQHBhcmFtIHtPYmplY3R9IHJlcXVlc3RPcHRpb25zIC0gQWRkaXRpb25hbCBvcHRpb25zIGZvciB0aGUgcHJveHkgYWdlbnRcclxuICogdG8gdXNlIGZvciBhIHJlcXVlc3QuXHJcbiAqIEBwYXJhbSB7T2JqZWN0fSBmZXRjaGVkTW9kdWxlcyAtIEFuIG9iamVjdCB3aGljaCB0cmFja3Mgd2hpY2ggSGlnaGNoYXJ0c1xyXG4gKiBtb2R1bGVzIGhhdmUgYmVlbiBmZXRjaGVkLlxyXG4gKiBAcGFyYW0ge2Jvb2xlYW59IHNob3VsZFRocm93RXJyb3IgLSBBIGZsYWcgdG8gaW5kaWNhdGUgaWYgdGhlIGVycm9yIHNob3VsZCBiZVxyXG4gKiB0aHJvd24uIFRoaXMgc2hvdWxkIGJlIHVzZWQgb25seSBmb3IgdGhlIGNvcmUgc2NyaXB0cy5cclxuICpcclxuICogQHJldHVybnMge1Byb21pc2U8c3RyaW5nPn0gQSBQcm9taXNlIHJlc29sdmluZyB0byB0aGUgdGV4dCByZXByZXNlbnRhdGlvblxyXG4gKiBvZiB0aGUgZmV0Y2hlZCBzY3JpcHQuXHJcbiAqXHJcbiAqIEB0aHJvd3Mge0V4cG9ydEVycm9yfSBUaHJvd3MgYW4gRXhwb3J0RXJyb3IgaWYgdGhlcmUgaXMgYSBwcm9ibGVtIHdpdGhcclxuICogZmV0Y2hpbmcgdGhlIHNjcmlwdC5cclxuICovXHJcbmV4cG9ydCBjb25zdCBmZXRjaEFuZFByb2Nlc3NTY3JpcHQgPSBhc3luYyAoXHJcbiAgc2NyaXB0LFxyXG4gIHJlcXVlc3RPcHRpb25zLFxyXG4gIGZldGNoZWRNb2R1bGVzLFxyXG4gIHNob3VsZFRocm93RXJyb3IgPSBmYWxzZVxyXG4pID0+IHtcclxuICAvLyBHZXQgcmlkIG9mIHRoZSAuanMgZnJvbSB0aGUgY3VzdG9tIHN0cmluZ3NcclxuICBpZiAoc2NyaXB0LmVuZHNXaXRoKCcuanMnKSkge1xyXG4gICAgc2NyaXB0ID0gc2NyaXB0LnN1YnN0cmluZygwLCBzY3JpcHQubGVuZ3RoIC0gMyk7XHJcbiAgfVxyXG5cclxuICBsb2coNCwgYFtjYWNoZV0gRmV0Y2hpbmcgc2NyaXB0IC0gJHtzY3JpcHR9LmpzYCk7XHJcblxyXG4gIC8vIEZldGNoIHRoZSBzY3JpcHRcclxuICBjb25zdCByZXNwb25zZSA9IGF3YWl0IGZldGNoKGAke3NjcmlwdH0uanNgLCByZXF1ZXN0T3B0aW9ucyk7XHJcblxyXG4gIC8vIElmIE9LLCByZXR1cm4gaXRzIHRleHQgcmVwcmVzZW50YXRpb25cclxuICBpZiAocmVzcG9uc2Uuc3RhdHVzQ29kZSA9PT0gMjAwICYmIHR5cGVvZiByZXNwb25zZS50ZXh0ID09ICdzdHJpbmcnKSB7XHJcbiAgICBpZiAoZmV0Y2hlZE1vZHVsZXMpIHtcclxuICAgICAgY29uc3QgbW9kdWxlTmFtZSA9IGV4dHJhY3RNb2R1bGVOYW1lKHNjcmlwdCk7XHJcbiAgICAgIGZldGNoZWRNb2R1bGVzW21vZHVsZU5hbWVdID0gMTtcclxuICAgIH1cclxuXHJcbiAgICByZXR1cm4gcmVzcG9uc2UudGV4dDtcclxuICB9XHJcblxyXG4gIGlmIChzaG91bGRUaHJvd0Vycm9yKSB7XHJcbiAgICB0aHJvdyBuZXcgRXhwb3J0RXJyb3IoXHJcbiAgICAgIGBDb3VsZCBub3QgZmV0Y2ggdGhlICR7c2NyaXB0fS5qcy4gVGhlIHNjcmlwdCBtaWdodCBub3QgZXhpc3QgaW4gdGhlIHJlcXVlc3RlZCB2ZXJzaW9uIChzdGF0dXMgY29kZTogJHtyZXNwb25zZS5zdGF0dXNDb2RlfSkuYFxyXG4gICAgKS5zZXRFcnJvcihyZXNwb25zZSk7XHJcbiAgfSBlbHNlIHtcclxuICAgIGxvZyhcclxuICAgICAgMixcclxuICAgICAgYFtjYWNoZV0gQ291bGQgbm90IGZldGNoIHRoZSAke3NjcmlwdH0uanMuIFRoZSBzY3JpcHQgbWlnaHQgbm90IGV4aXN0IGluIHRoZSByZXF1ZXN0ZWQgdmVyc2lvbi5gXHJcbiAgICApO1xyXG4gIH1cclxuXHJcbiAgcmV0dXJuICcnO1xyXG59O1xyXG5cclxuLyoqXHJcbiAqIEZldGNoZXMgSGlnaGNoYXJ0cyBzY3JpcHRzIGFuZCBjdXN0b21TY3JpcHRzIGZyb20gdGhlIGdpdmVuIENETnMuXHJcbiAqXHJcbiAqIEBwYXJhbSB7c3RyaW5nfSBjb3JlU2NyaXB0cyAtIEFycmF5IG9mIEhpZ2hjaGFydHMgY29yZSBzY3JpcHRzIHRvIGZldGNoLlxyXG4gKiBAcGFyYW0ge3N0cmluZ30gbW9kdWxlU2NyaXB0cyAtIEFycmF5IG9mIEhpZ2hjaGFydHMgbW9kdWxlcyB0byBmZXRjaC5cclxuICogQHBhcmFtIHtzdHJpbmd9IGN1c3RvbVNjcmlwdHMgLSBBcnJheSBvZiBjdXN0b20gc2NyaXB0IHBhdGhzIHRvIGZldGNoXHJcbiAqIChmdWxsIFVSTHMpLlxyXG4gKiBAcGFyYW0ge29iamVjdH0gcHJveHlPcHRpb25zIC0gT3B0aW9ucyBmb3IgdGhlIHByb3h5IGFnZW50IHRvIHVzZSBmb3JcclxuICogYSByZXF1ZXN0LlxyXG4gKiBAcGFyYW0ge29iamVjdH0gZmV0Y2hlZE1vZHVsZXMgLSBBbiBvYmplY3Qgd2hpY2ggdHJhY2tzIHdoaWNoIEhpZ2hjaGFydHNcclxuICogbW9kdWxlcyBoYXZlIGJlZW4gZmV0Y2hlZC5cclxuICpcclxuICogQHJldHVybnMge1Byb21pc2U8c3RyaW5nPn0gVGhlIGZldGNoZWQgc2NyaXB0cyBjb250ZW50IGpvaW5lZC5cclxuICovXHJcbmV4cG9ydCBjb25zdCBmZXRjaFNjcmlwdHMgPSBhc3luYyAoXHJcbiAgY29yZVNjcmlwdHMsXHJcbiAgbW9kdWxlU2NyaXB0cyxcclxuICBjdXN0b21TY3JpcHRzLFxyXG4gIHByb3h5T3B0aW9ucyxcclxuICBmZXRjaGVkTW9kdWxlc1xyXG4pID0+IHtcclxuICAvLyBDb25maWd1cmUgcHJveHkgaWYgZXhpc3RzXHJcbiAgbGV0IHByb3h5QWdlbnQ7XHJcbiAgY29uc3QgcHJveHlIb3N0ID0gcHJveHlPcHRpb25zLmhvc3Q7XHJcbiAgY29uc3QgcHJveHlQb3J0ID0gcHJveHlPcHRpb25zLnBvcnQ7XHJcblxyXG4gIC8vIFRyeSB0byBjcmVhdGUgYSBQcm94eSBBZ2VudFxyXG4gIGlmIChwcm94eUhvc3QgJiYgcHJveHlQb3J0KSB7XHJcbiAgICB0cnkge1xyXG4gICAgICBwcm94eUFnZW50ID0gbmV3IEh0dHBzUHJveHlBZ2VudCh7XHJcbiAgICAgICAgaG9zdDogcHJveHlIb3N0LFxyXG4gICAgICAgIHBvcnQ6IHByb3h5UG9ydFxyXG4gICAgICB9KTtcclxuICAgIH0gY2F0Y2ggKGVycm9yKSB7XHJcbiAgICAgIHRocm93IG5ldyBFeHBvcnRFcnJvcignW2NhY2hlXSBDb3VsZCBub3QgY3JlYXRlIGEgUHJveHkgQWdlbnQuJykuc2V0RXJyb3IoXHJcbiAgICAgICAgZXJyb3JcclxuICAgICAgKTtcclxuICAgIH1cclxuICB9XHJcblxyXG4gIC8vIElmIGV4aXN0cywgYWRkIHByb3h5IGFnZW50IHRvIHJlcXVlc3Qgb3B0aW9uc1xyXG4gIGNvbnN0IHJlcXVlc3RPcHRpb25zID0gcHJveHlBZ2VudFxyXG4gICAgPyB7XHJcbiAgICAgICAgYWdlbnQ6IHByb3h5QWdlbnQsXHJcbiAgICAgICAgdGltZW91dDogZW52cy5TRVJWRVJfUFJPWFlfVElNRU9VVFxyXG4gICAgICB9XHJcbiAgICA6IHt9O1xyXG5cclxuICBjb25zdCBhbGxGZXRjaFByb21pc2VzID0gW1xyXG4gICAgLi4uY29yZVNjcmlwdHMubWFwKChzY3JpcHQpID0+XHJcbiAgICAgIGZldGNoQW5kUHJvY2Vzc1NjcmlwdChgJHtzY3JpcHR9YCwgcmVxdWVzdE9wdGlvbnMsIGZldGNoZWRNb2R1bGVzLCB0cnVlKVxyXG4gICAgKSxcclxuICAgIC4uLm1vZHVsZVNjcmlwdHMubWFwKChzY3JpcHQpID0+XHJcbiAgICAgIGZldGNoQW5kUHJvY2Vzc1NjcmlwdChgJHtzY3JpcHR9YCwgcmVxdWVzdE9wdGlvbnMsIGZldGNoZWRNb2R1bGVzKVxyXG4gICAgKSxcclxuICAgIC4uLmN1c3RvbVNjcmlwdHMubWFwKChzY3JpcHQpID0+XHJcbiAgICAgIGZldGNoQW5kUHJvY2Vzc1NjcmlwdChgJHtzY3JpcHR9YCwgcmVxdWVzdE9wdGlvbnMpXHJcbiAgICApXHJcbiAgXTtcclxuXHJcbiAgY29uc3QgZmV0Y2hlZFNjcmlwdHMgPSBhd2FpdCBQcm9taXNlLmFsbChhbGxGZXRjaFByb21pc2VzKTtcclxuICByZXR1cm4gZmV0Y2hlZFNjcmlwdHMuam9pbignO1xcbicpO1xyXG59O1xyXG5cclxuLyoqXHJcbiAqIFVwZGF0ZXMgdGhlIGxvY2FsIGNhY2hlIHdpdGggSGlnaGNoYXJ0cyBzY3JpcHRzIGFuZCB0aGVpciB2ZXJzaW9ucy5cclxuICpcclxuICogQHBhcmFtIHtPYmplY3R9IG9wdGlvbnMgLSBPYmplY3QgY29udGFpbmluZyBhbGwgb3B0aW9ucy5cclxuICogQHBhcmFtIHtzdHJpbmd9IHNvdXJjZVBhdGggLSBUaGUgcGF0aCB0byB0aGUgc291cmNlIGZpbGUgaW4gdGhlIGNhY2hlLlxyXG4gKlxyXG4gKiBAcmV0dXJucyB7UHJvbWlzZTxvYmplY3Q+fSBBIFByb21pc2UgcmVzb2x2aW5nIHRvIGFuIG9iamVjdCByZXByZXNlbnRpbmdcclxuICogdGhlIGZldGNoZWQgbW9kdWxlcy5cclxuICpcclxuICogQHRocm93cyB7RXhwb3J0RXJyb3J9IFRocm93cyBhbiBFeHBvcnRFcnJvciBpZiB0aGVyZSBpcyBhbiBpc3N1ZSB1cGRhdGluZ1xyXG4gKiB0aGUgbG9jYWwgSGlnaGNoYXJ0cyBjYWNoZS5cclxuICovXHJcbmV4cG9ydCBjb25zdCB1cGRhdGVDYWNoZSA9IGFzeW5jIChcclxuICBoaWdoY2hhcnRzT3B0aW9ucyxcclxuICBwcm94eU9wdGlvbnMsXHJcbiAgc291cmNlUGF0aFxyXG4pID0+IHtcclxuICBjb25zdCB2ZXJzaW9uID0gaGlnaGNoYXJ0c09wdGlvbnMudmVyc2lvbjtcclxuICBjb25zdCBoY1ZlcnNpb24gPSB2ZXJzaW9uID09PSAnbGF0ZXN0JyB8fCAhdmVyc2lvbiA/ICcnIDogYCR7dmVyc2lvbn0vYDtcclxuICBjb25zdCBjZG5VUkwgPSBoaWdoY2hhcnRzT3B0aW9ucy5jZG5VUkwgfHwgY2FjaGUuY2RuVVJMO1xyXG5cclxuICBsb2coXHJcbiAgICAzLFxyXG4gICAgYFtjYWNoZV0gVXBkYXRpbmcgY2FjaGUgdmVyc2lvbiB0byBIaWdoY2hhcnRzOiAke2hjVmVyc2lvbiB8fCAnbGF0ZXN0J30uYFxyXG4gICk7XHJcblxyXG4gIGNvbnN0IGZldGNoZWRNb2R1bGVzID0ge307XHJcbiAgdHJ5IHtcclxuICAgIGNhY2hlLnNvdXJjZXMgPSBhd2FpdCBmZXRjaFNjcmlwdHMoXHJcbiAgICAgIFtcclxuICAgICAgICAuLi5oaWdoY2hhcnRzT3B0aW9ucy5jb3JlU2NyaXB0cy5tYXAoKGMpID0+IGAke2NkblVSTH0ke2hjVmVyc2lvbn0ke2N9YClcclxuICAgICAgXSxcclxuICAgICAgW1xyXG4gICAgICAgIC4uLmhpZ2hjaGFydHNPcHRpb25zLm1vZHVsZVNjcmlwdHMubWFwKChtKSA9PlxyXG4gICAgICAgICAgbSA9PT0gJ21hcCdcclxuICAgICAgICAgICAgPyBgJHtjZG5VUkx9bWFwcy8ke2hjVmVyc2lvbn1tb2R1bGVzLyR7bX1gXHJcbiAgICAgICAgICAgIDogYCR7Y2RuVVJMfSR7aGNWZXJzaW9ufW1vZHVsZXMvJHttfWBcclxuICAgICAgICApLFxyXG4gICAgICAgIC4uLmhpZ2hjaGFydHNPcHRpb25zLmluZGljYXRvclNjcmlwdHMubWFwKFxyXG4gICAgICAgICAgKGkpID0+IGAke2NkblVSTH1zdG9jay8ke2hjVmVyc2lvbn1pbmRpY2F0b3JzLyR7aX1gXHJcbiAgICAgICAgKVxyXG4gICAgICBdLFxyXG4gICAgICBoaWdoY2hhcnRzT3B0aW9ucy5jdXN0b21TY3JpcHRzLFxyXG4gICAgICBwcm94eU9wdGlvbnMsXHJcbiAgICAgIGZldGNoZWRNb2R1bGVzXHJcbiAgICApO1xyXG5cclxuICAgIGNhY2hlLmhjVmVyc2lvbiA9IGV4dHJhY3RWZXJzaW9uKGNhY2hlKTtcclxuXHJcbiAgICAvLyBTYXZlIHRoZSBmZXRjaGVkIG1vZHVsZXMgaW50byBjYWNoZXMnIHNvdXJjZSBKU09OXHJcbiAgICB3cml0ZUZpbGVTeW5jKHNvdXJjZVBhdGgsIGNhY2hlLnNvdXJjZXMpO1xyXG4gICAgcmV0dXJuIGZldGNoZWRNb2R1bGVzO1xyXG4gIH0gY2F0Y2ggKGVycm9yKSB7XHJcbiAgICB0aHJvdyBuZXcgRXhwb3J0RXJyb3IoXHJcbiAgICAgICdbY2FjaGVdIFVuYWJsZSB0byB1cGRhdGUgdGhlIGxvY2FsIEhpZ2hjaGFydHMgY2FjaGUuJ1xyXG4gICAgKS5zZXRFcnJvcihlcnJvcik7XHJcbiAgfVxyXG59O1xyXG5cclxuLyoqXHJcbiAqIFVwZGF0ZXMgdGhlIEhpZ2hjaGFydHMgdmVyc2lvbiBpbiB0aGUgYXBwbGllZCBjb25maWd1cmF0aW9uIGFuZCBjaGVja3NcclxuICogdGhlIGNhY2hlIGZvciB0aGUgbmV3IHZlcnNpb24uXHJcbiAqXHJcbiAqIEBwYXJhbSB7c3RyaW5nfSBuZXdWZXJzaW9uIC0gVGhlIG5ldyBIaWdoY2hhcnRzIHZlcnNpb24gdG8gYmUgYXBwbGllZC5cclxuICpcclxuICogQHJldHVybnMge1Byb21pc2U8KG9iamVjdHxib29sZWFuKT59IEEgUHJvbWlzZSByZXNvbHZpbmcgdG8gdGhlIHVwZGF0ZWRcclxuICogY29uZmlndXJhdGlvbiB3aXRoIHRoZSBuZXcgdmVyc2lvbiwgb3IgZmFsc2UgaWYgbm8gYXBwbGllZCBjb25maWd1cmF0aW9uXHJcbiAqIGV4aXN0cy5cclxuICovXHJcbmV4cG9ydCBjb25zdCB1cGRhdGVWZXJzaW9uID0gYXN5bmMgKG5ld1ZlcnNpb24pID0+IHtcclxuICBjb25zdCBvcHRpb25zID0gZ2V0T3B0aW9ucygpO1xyXG4gIGlmIChvcHRpb25zPy5oaWdoY2hhcnRzKSB7XHJcbiAgICBvcHRpb25zLmhpZ2hjaGFydHMudmVyc2lvbiA9IG5ld1ZlcnNpb247XHJcbiAgfVxyXG4gIGF3YWl0IGNoZWNrQW5kVXBkYXRlQ2FjaGUob3B0aW9ucyk7XHJcbn07XHJcblxyXG4vKipcclxuICogQ2hlY2tzIHRoZSBjYWNoZSBmb3IgSGlnaGNoYXJ0cyBkZXBlbmRlbmNpZXMsIHVwZGF0ZXMgdGhlIGNhY2hlIGlmIG5lZWRlZCxcclxuICogYW5kIGxvYWRzIHRoZSBzb3VyY2VzLlxyXG4gKlxyXG4gKiBAcGFyYW0ge09iamVjdH0gb3B0aW9ucyAtIE9iamVjdCBjb250YWluaW5nIGFsbCBvcHRpb25zLlxyXG4gKlxyXG4gKiBAcmV0dXJucyB7UHJvbWlzZTx2b2lkPn0gQSBQcm9taXNlIHRoYXQgcmVzb2x2ZXMgb25jZSB0aGUgY2FjaGUgaXMgY2hlY2tlZFxyXG4gKiBhbmQgdXBkYXRlZC5cclxuICpcclxuICogQHRocm93cyB7RXhwb3J0RXJyb3J9IFRocm93cyBhbiBFeHBvcnRFcnJvciBpZiB0aGVyZSBpcyBhbiBpc3N1ZSB1cGRhdGluZ1xyXG4gKiBvciByZWFkaW5nIHRoZSBjYWNoZS5cclxuICovXHJcbmV4cG9ydCBjb25zdCBjaGVja0FuZFVwZGF0ZUNhY2hlID0gYXN5bmMgKG9wdGlvbnMpID0+IHtcclxuICBjb25zdCB7IGhpZ2hjaGFydHMsIHNlcnZlciB9ID0gb3B0aW9ucztcclxuICBjb25zdCBjYWNoZVBhdGggPSBqb2luKF9fZGlybmFtZSwgaGlnaGNoYXJ0cy5jYWNoZVBhdGgpO1xyXG5cclxuICBsZXQgZmV0Y2hlZE1vZHVsZXM7XHJcbiAgLy8gUHJlcGFyZSBwYXRocyB0byBtYW5pZmVzdCBhbmQgc291cmNlcyBmcm9tIHRoZSAuY2FjaGUgZm9sZGVyXHJcbiAgY29uc3QgbWFuaWZlc3RQYXRoID0gam9pbihjYWNoZVBhdGgsICdtYW5pZmVzdC5qc29uJyk7XHJcbiAgY29uc3Qgc291cmNlUGF0aCA9IGpvaW4oY2FjaGVQYXRoLCAnc291cmNlcy5qcycpO1xyXG5cclxuICAvLyBDcmVhdGUgdGhlIGNhY2hlIGRlc3RpbmF0aW9uIGlmIGl0IGRvZXNuJ3QgZXhpc3QgYWxyZWFkeVxyXG4gICFleGlzdHNTeW5jKGNhY2hlUGF0aCkgJiYgbWtkaXJTeW5jKGNhY2hlUGF0aCk7XHJcblxyXG4gIC8vIEZldGNoIGFsbCB0aGUgc2NyaXB0cyBlaXRoZXIgaWYgbWFuaWZlc3QuanNvbiBkb2VzIG5vdCBleGlzdFxyXG4gIC8vIG9yIGlmIHRoZSBmb3JjZUZldGNoIG9wdGlvbiBpcyBlbmFibGVkXHJcbiAgaWYgKCFleGlzdHNTeW5jKG1hbmlmZXN0UGF0aCkgfHwgaGlnaGNoYXJ0cy5mb3JjZUZldGNoKSB7XHJcbiAgICBsb2coMywgJ1tjYWNoZV0gRmV0Y2hpbmcgYW5kIGNhY2hpbmcgSGlnaGNoYXJ0cyBkZXBlbmRlbmNpZXMuJyk7XHJcbiAgICBmZXRjaGVkTW9kdWxlcyA9IGF3YWl0IHVwZGF0ZUNhY2hlKGhpZ2hjaGFydHMsIHNlcnZlci5wcm94eSwgc291cmNlUGF0aCk7XHJcbiAgfSBlbHNlIHtcclxuICAgIGxldCByZXF1ZXN0VXBkYXRlID0gZmFsc2U7XHJcblxyXG4gICAgLy8gUmVhZCB0aGUgbWFuaWZlc3QgSlNPTlxyXG4gICAgY29uc3QgbWFuaWZlc3QgPSBKU09OLnBhcnNlKHJlYWRGaWxlU3luYyhtYW5pZmVzdFBhdGgpKTtcclxuXHJcbiAgICAvLyBDaGVjayBpZiB0aGUgbW9kdWxlcyBpcyBhbiBhcnJheSwgaWYgc28sIHdlIHJld3JpdGUgaXQgdG8gYSBtYXAgdG8gbWFrZVxyXG4gICAgLy8gaXQgZWFzaWVyIHRvIHJlc29sdmUgbW9kdWxlcy5cclxuICAgIGlmIChtYW5pZmVzdC5tb2R1bGVzICYmIEFycmF5LmlzQXJyYXkobWFuaWZlc3QubW9kdWxlcykpIHtcclxuICAgICAgY29uc3QgbW9kdWxlTWFwID0ge307XHJcbiAgICAgIG1hbmlmZXN0Lm1vZHVsZXMuZm9yRWFjaCgobSkgPT4gKG1vZHVsZU1hcFttXSA9IDEpKTtcclxuICAgICAgbWFuaWZlc3QubW9kdWxlcyA9IG1vZHVsZU1hcDtcclxuICAgIH1cclxuXHJcbiAgICBjb25zdCB7IGNvcmVTY3JpcHRzLCBtb2R1bGVTY3JpcHRzLCBpbmRpY2F0b3JTY3JpcHRzIH0gPSBoaWdoY2hhcnRzO1xyXG4gICAgY29uc3QgbnVtYmVyT2ZNb2R1bGVzID1cclxuICAgICAgY29yZVNjcmlwdHMubGVuZ3RoICsgbW9kdWxlU2NyaXB0cy5sZW5ndGggKyBpbmRpY2F0b3JTY3JpcHRzLmxlbmd0aDtcclxuXHJcbiAgICAvLyBDb21wYXJlIHRoZSBsb2FkZWQgaGlnaGNoYXJ0cyBjb25maWcgd2l0aCB0aGUgY29udGVudHMgaW4gY2FjaGUuXHJcbiAgICAvLyBJZiB0aGVyZSBhcmUgY2hhbmdlcywgZmV0Y2ggcmVxdWVzdGVkIG1vZHVsZXMgYW5kIHByb2R1Y3RzLFxyXG4gICAgLy8gYW5kIGJha2UgdGhlbSBpbnRvIGEgZ2lhbnQgYmxvYi4gU2F2ZSB0aGUgYmxvYi5cclxuICAgIGlmIChtYW5pZmVzdC52ZXJzaW9uICE9PSBoaWdoY2hhcnRzLnZlcnNpb24pIHtcclxuICAgICAgbG9nKFxyXG4gICAgICAgIDIsXHJcbiAgICAgICAgJ1tjYWNoZV0gQSBIaWdoY2hhcnRzIHZlcnNpb24gbWlzbWF0Y2ggaW4gdGhlIGNhY2hlLCBuZWVkIHRvIHJlLWZldGNoLidcclxuICAgICAgKTtcclxuICAgICAgcmVxdWVzdFVwZGF0ZSA9IHRydWU7XHJcbiAgICB9IGVsc2UgaWYgKE9iamVjdC5rZXlzKG1hbmlmZXN0Lm1vZHVsZXMgfHwge30pLmxlbmd0aCAhPT0gbnVtYmVyT2ZNb2R1bGVzKSB7XHJcbiAgICAgIGxvZyhcclxuICAgICAgICAyLFxyXG4gICAgICAgICdbY2FjaGVdIFRoZSBjYWNoZSBhbmQgdGhlIHJlcXVlc3RlZCBtb2R1bGVzIGRvIG5vdCBtYXRjaCwgbmVlZCB0byByZS1mZXRjaC4nXHJcbiAgICAgICk7XHJcbiAgICAgIHJlcXVlc3RVcGRhdGUgPSB0cnVlO1xyXG4gICAgfSBlbHNlIHtcclxuICAgICAgLy8gQ2hlY2sgZWFjaCBtb2R1bGUsIGlmIGFueXRoaW5nIGlzIG1pc3NpbmcgcmVmZXRjaCBldmVyeXRoaW5nXHJcbiAgICAgIHJlcXVlc3RVcGRhdGUgPSAobW9kdWxlU2NyaXB0cyB8fCBbXSkuc29tZSgobW9kdWxlTmFtZSkgPT4ge1xyXG4gICAgICAgIGlmICghbWFuaWZlc3QubW9kdWxlc1ttb2R1bGVOYW1lXSkge1xyXG4gICAgICAgICAgbG9nKFxyXG4gICAgICAgICAgICAyLFxyXG4gICAgICAgICAgICBgW2NhY2hlXSBUaGUgJHttb2R1bGVOYW1lfSBpcyBtaXNzaW5nIGluIHRoZSBjYWNoZSwgbmVlZCB0byByZS1mZXRjaC5gXHJcbiAgICAgICAgICApO1xyXG4gICAgICAgICAgcmV0dXJuIHRydWU7XHJcbiAgICAgICAgfVxyXG4gICAgICB9KTtcclxuICAgIH1cclxuXHJcbiAgICBpZiAocmVxdWVzdFVwZGF0ZSkge1xyXG4gICAgICBmZXRjaGVkTW9kdWxlcyA9IGF3YWl0IHVwZGF0ZUNhY2hlKGhpZ2hjaGFydHMsIHNlcnZlci5wcm94eSwgc291cmNlUGF0aCk7XHJcbiAgICB9IGVsc2Uge1xyXG4gICAgICBsb2coMywgJ1tjYWNoZV0gRGVwZW5kZW5jeSBjYWNoZSBpcyB1cCB0byBkYXRlLCBwcm9jZWVkaW5nLicpO1xyXG5cclxuICAgICAgLy8gTG9hZCB0aGUgc291cmNlc1xyXG4gICAgICBjYWNoZS5zb3VyY2VzID0gcmVhZEZpbGVTeW5jKHNvdXJjZVBhdGgsICd1dGY4Jyk7XHJcblxyXG4gICAgICAvLyBHZXQgY3VycmVudCBtb2R1bGVzIG1hcFxyXG4gICAgICBmZXRjaGVkTW9kdWxlcyA9IG1hbmlmZXN0Lm1vZHVsZXM7XHJcblxyXG4gICAgICBjYWNoZS5oY1ZlcnNpb24gPSBleHRyYWN0VmVyc2lvbihjYWNoZSk7XHJcbiAgICB9XHJcbiAgfVxyXG5cclxuICAvLyBGaW5hbGx5LCBzYXZlIHRoZSBuZXcgbWFuaWZlc3QsIHdoaWNoIGlzIGJhc2ljYWxseSBvdXIgY3VycmVudCBjb25maWdcclxuICAvLyBpbiBhIHNsaWdodGx5IGRpZmZlcmVudCBmb3JtYXRcclxuICBhd2FpdCBzYXZlQ29uZmlnVG9NYW5pZmVzdChoaWdoY2hhcnRzLCBmZXRjaGVkTW9kdWxlcyk7XHJcbn07XHJcblxyXG5leHBvcnQgY29uc3QgZ2V0Q2FjaGVQYXRoID0gKCkgPT5cclxuICBqb2luKF9fZGlybmFtZSwgZ2V0T3B0aW9ucygpLmhpZ2hjaGFydHMuY2FjaGVQYXRoKTtcclxuXHJcbmV4cG9ydCBkZWZhdWx0IHtcclxuICBjaGVja0FuZFVwZGF0ZUNhY2hlLFxyXG4gIGdldENhY2hlUGF0aCxcclxuICB1cGRhdGVWZXJzaW9uLFxyXG4gIGdldENhY2hlOiAoKSA9PiBjYWNoZSxcclxuICBoaWdoY2hhcnRzOiAoKSA9PiBjYWNoZS5zb3VyY2VzLFxyXG4gIHZlcnNpb246ICgpID0+IGNhY2hlLmhjVmVyc2lvblxyXG59O1xyXG4iLCIvKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKlxyXG5cclxuSGlnaGNoYXJ0cyBFeHBvcnQgU2VydmVyXHJcblxyXG5Db3B5cmlnaHQgKGMpIDIwMTYtMjAyNCwgSGlnaHNvZnRcclxuXHJcbkxpY2VuY2VkIHVuZGVyIHRoZSBNSVQgbGljZW5jZS5cclxuXHJcbkFkZGl0aW9uYWxseSBhIHZhbGlkIEhpZ2hjaGFydHMgbGljZW5zZSBpcyByZXF1aXJlZCBmb3IgdXNlLlxyXG5cclxuU2VlIExJQ0VOU0UgZmlsZSBpbiByb290IGZvciBkZXRhaWxzLlxyXG5cclxuKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKi9cclxuXHJcbi8qIGVzbGludC1kaXNhYmxlIG5vLXVuZGVmICovXHJcblxyXG4vKipcclxuICogU2V0dGluZyB0aGUgYW5pbU9iamVjdC4gQ2FsbGVkIHdoZW4gaW5pdGluZyB0aGUgcGFnZS5cclxuICovXHJcbmV4cG9ydCBmdW5jdGlvbiBzZXR1cEhpZ2hjaGFydHMoKSB7XHJcbiAgSGlnaGNoYXJ0cy5hbmltT2JqZWN0ID0gZnVuY3Rpb24gKCkge1xyXG4gICAgcmV0dXJuIHsgZHVyYXRpb246IDAgfTtcclxuICB9O1xyXG59XHJcblxyXG4vKipcclxuICogQ3JlYXRlcyB0aGUgYWN0dWFsIGNoYXJ0LlxyXG4gKlxyXG4gKiBAcGFyYW0ge29iamVjdH0gY2hhcnRPcHRpb25zIC0gVGhlIG9wdGlvbnMgZm9yIHRoZSBIaWdoY2hhcnRzIGNoYXJ0LlxyXG4gKiBAcGFyYW0ge29iamVjdH0gb3B0aW9ucyAtIFRoZSBleHBvcnQgb3B0aW9ucy5cclxuICogQHBhcmFtIHtib29sZWFufSBkaXNwbGF5RXJyb3JzIC0gQSBmbGFnIGluZGljYXRpbmcgd2hldGhlciB0byBkaXNwbGF5IGVycm9ycy5cclxuICovXHJcbmV4cG9ydCBmdW5jdGlvbiB0cmlnZ2VyRXhwb3J0KGNoYXJ0T3B0aW9ucywgb3B0aW9ucywgZGlzcGxheUVycm9ycykge1xyXG4gIC8vIERpc3BsYXkgZXJyb3JzIGZsYWcgdGFrZW4gZnJvbSBjaGFydCBvcHRpb25zIG5hZCBkZWJ1Z2dlciBtb2R1bGVcclxuICB3aW5kb3cuX2Rpc3BsYXlFcnJvcnMgPSBkaXNwbGF5RXJyb3JzO1xyXG5cclxuICAvLyBHZXQgcmVxdWlyZWQgZnVuY3Rpb25zXHJcbiAgY29uc3QgeyBnZXRPcHRpb25zLCBtZXJnZSwgc2V0T3B0aW9ucywgd3JhcCB9ID0gSGlnaGNoYXJ0cztcclxuXHJcbiAgLy8gQ3JlYXRlIGEgc2VwYXJhdGUgb2JqZWN0IGZvciBhIHBvdGVudGlhbCBzZXRPcHRpb25zIHVzYWdlcyBpbiBvcmRlciB0b1xyXG4gIC8vIHByZXZlbnQgZnJvbSBwb2xsdXRpbmcgb3RoZXIgZXhwb3J0cyB0aGF0IGNhbiBoYXBwZW4gb24gdGhlIHNhbWUgcGFnZVxyXG4gIEhpZ2hjaGFydHMuc2V0T3B0aW9uc09iaiA9IG1lcmdlKGZhbHNlLCB7fSwgZ2V0T3B0aW9ucygpKTtcclxuXHJcbiAgLy8gVHJpZ2dlciBjdXN0b20gY29kZVxyXG4gIGlmIChvcHRpb25zLmN1c3RvbUxvZ2ljLmN1c3RvbUNvZGUpIHtcclxuICAgIG5ldyBGdW5jdGlvbihvcHRpb25zLmN1c3RvbUxvZ2ljLmN1c3RvbUNvZGUpKCk7XHJcbiAgfVxyXG5cclxuICAvLyBCeSBkZWZhdWx0IGFuaW1hdGlvbiBpcyBkaXNhYmxlZFxyXG4gIGNvbnN0IGNoYXJ0ID0ge1xyXG4gICAgYW5pbWF0aW9uOiBmYWxzZVxyXG4gIH07XHJcblxyXG4gIC8vIFdoZW4gc3RyYWlnaHQgaW5qZWN0LCB0aGUgc2l6ZSBpcyBzZXQgdGhyb3VnaCBDU1Mgb25seVxyXG4gIGlmIChvcHRpb25zLmV4cG9ydC5zdHJJbmopIHtcclxuICAgIGNoYXJ0LmhlaWdodCA9IGNoYXJ0T3B0aW9ucy5jaGFydC5oZWlnaHQ7XHJcbiAgICBjaGFydC53aWR0aCA9IGNoYXJ0T3B0aW9ucy5jaGFydC53aWR0aDtcclxuICB9XHJcblxyXG4gIC8vIE5PVEU6IElzIHRoaXMgdXNlZCBmb3IgYW55dGhpbmcgdXNlZnVsP1xyXG4gIHdpbmRvdy5pc1JlbmRlckNvbXBsZXRlID0gZmFsc2U7XHJcbiAgd3JhcChIaWdoY2hhcnRzLkNoYXJ0LnByb3RvdHlwZSwgJ2luaXQnLCBmdW5jdGlvbiAocHJvY2VlZCwgdXNlck9wdGlvbnMsIGNiKSB7XHJcbiAgICAvLyBPdmVycmlkZSB1c2VyT3B0aW9ucyB3aXRoIGltYWdlIGZyaWVuZGx5IG9wdGlvbnNcclxuICAgIHVzZXJPcHRpb25zID0gbWVyZ2UodXNlck9wdGlvbnMsIHtcclxuICAgICAgZXhwb3J0aW5nOiB7XHJcbiAgICAgICAgZW5hYmxlZDogZmFsc2VcclxuICAgICAgfSxcclxuICAgICAgcGxvdE9wdGlvbnM6IHtcclxuICAgICAgICBzZXJpZXM6IHtcclxuICAgICAgICAgIGxhYmVsOiB7XHJcbiAgICAgICAgICAgIGVuYWJsZWQ6IGZhbHNlXHJcbiAgICAgICAgICB9XHJcbiAgICAgICAgfVxyXG4gICAgICB9LFxyXG4gICAgICAvKiBFeHBlY3RzIHRvb2x0aXAgaW4gdXNlck9wdGlvbnMgd2hlbiBmb3JFeHBvcnQgaXMgdHJ1ZS5cclxuICAgICAgICBodHRwczovL2dpdGh1Yi5jb20vaGlnaGNoYXJ0cy9oaWdoY2hhcnRzL2Jsb2IvM2FkNDMwYTM1M2I4MDU2YjllNzY0YWE0ZTVjZDY4MjhhYTQ3OWRiMi9qcy9wYXJ0cy9DaGFydC5qcyNMMjQxXHJcbiAgICAgICAgKi9cclxuICAgICAgdG9vbHRpcDoge31cclxuICAgIH0pO1xyXG5cclxuICAgICh1c2VyT3B0aW9ucy5zZXJpZXMgfHwgW10pLmZvckVhY2goZnVuY3Rpb24gKHNlcmllcykge1xyXG4gICAgICBzZXJpZXMuYW5pbWF0aW9uID0gZmFsc2U7XHJcbiAgICB9KTtcclxuXHJcbiAgICAvLyBBZGQgZmxhZyB0byBrbm93IGlmIGNoYXJ0IHJlbmRlciBoYXMgYmVlbiBjYWxsZWQuXHJcbiAgICBpZiAoIXdpbmRvdy5vbkhpZ2hjaGFydHNSZW5kZXIpIHtcclxuICAgICAgd2luZG93Lm9uSGlnaGNoYXJ0c1JlbmRlciA9IEhpZ2hjaGFydHMuYWRkRXZlbnQodGhpcywgJ3JlbmRlcicsICgpID0+IHtcclxuICAgICAgICB3aW5kb3cuaXNSZW5kZXJDb21wbGV0ZSA9IHRydWU7XHJcbiAgICAgIH0pO1xyXG4gICAgfVxyXG5cclxuICAgIHByb2NlZWQuYXBwbHkodGhpcywgW3VzZXJPcHRpb25zLCBjYl0pO1xyXG4gIH0pO1xyXG5cclxuICB3cmFwKEhpZ2hjaGFydHMuU2VyaWVzLnByb3RvdHlwZSwgJ2luaXQnLCBmdW5jdGlvbiAocHJvY2VlZCwgY2hhcnQsIG9wdGlvbnMpIHtcclxuICAgIHByb2NlZWQuYXBwbHkodGhpcywgW2NoYXJ0LCBvcHRpb25zXSk7XHJcbiAgfSk7XHJcblxyXG4gIC8vIEdldCB0aGUgdXNlciBvcHRpb25zXHJcbiAgY29uc3QgdXNlck9wdGlvbnMgPSBvcHRpb25zLmV4cG9ydC5zdHJJbmpcclxuICAgID8gbmV3IEZ1bmN0aW9uKGByZXR1cm4gJHtvcHRpb25zLmV4cG9ydC5zdHJJbmp9YCkoKVxyXG4gICAgOiBjaGFydE9wdGlvbnM7XHJcblxyXG4gIC8vIE1lcmdlIHRoZSBnbG9iYWxPcHRpb25zLCB0aGVtZU9wdGlvbnMsIG9wdGlvbnMgZnJvbSB0aGUgd3JhcHBlZFxyXG4gIC8vIHNldE9wdGlvbnMgZnVuY3Rpb24gYW5kIHVzZXIgb3B0aW9ucyB0byBjcmVhdGUgdGhlIGZpbmFsIG9wdGlvbnMgb2JqZWN0XHJcbiAgY29uc3QgZmluYWxPcHRpb25zID0gbWVyZ2UoXHJcbiAgICBmYWxzZSxcclxuICAgIEpTT04ucGFyc2Uob3B0aW9ucy5leHBvcnQudGhlbWVPcHRpb25zKSxcclxuICAgIHVzZXJPcHRpb25zLFxyXG4gICAgLy8gUGxhY2VkIGl0IGhlcmUgaW5zdGVhZCBpbiB0aGUgaW5pdCBiZWNhdXNlIG9mIHRoZSBzaXplIGlzc3Vlc1xyXG4gICAgeyBjaGFydCB9XHJcbiAgKTtcclxuXHJcbiAgY29uc3QgZmluYWxDYWxsYmFjayA9IG9wdGlvbnMuY3VzdG9tTG9naWMuY2FsbGJhY2tcclxuICAgID8gbmV3IEZ1bmN0aW9uKGByZXR1cm4gJHtvcHRpb25zLmN1c3RvbUxvZ2ljLmNhbGxiYWNrfWApKClcclxuICAgIDogdW5kZWZpbmVkO1xyXG5cclxuICAvLyBTZXQgdGhlIGdsb2JhbCBvcHRpb25zIGlmIGV4aXN0XHJcbiAgY29uc3QgZ2xvYmFsT3B0aW9ucyA9IEpTT04ucGFyc2Uob3B0aW9ucy5leHBvcnQuZ2xvYmFsT3B0aW9ucyk7XHJcbiAgaWYgKGdsb2JhbE9wdGlvbnMpIHtcclxuICAgIHNldE9wdGlvbnMoZ2xvYmFsT3B0aW9ucyk7XHJcbiAgfVxyXG5cclxuICBIaWdoY2hhcnRzW29wdGlvbnMuZXhwb3J0LmNvbnN0ciB8fCAnY2hhcnQnXShcclxuICAgICdjb250YWluZXInLFxyXG4gICAgZmluYWxPcHRpb25zLFxyXG4gICAgZmluYWxDYWxsYmFja1xyXG4gICk7XHJcblxyXG4gIC8vIEdldCB0aGUgY3VycmVudCBnbG9iYWwgb3B0aW9uc1xyXG4gIGNvbnN0IGRlZmF1bHRPcHRpb25zID0gZ2V0T3B0aW9ucygpO1xyXG5cclxuICAvLyBDbGVhciBpdCBqdXN0IGluIGNhc2UgKGUuZy4gdGhlIHNldE9wdGlvbnMgd2FzIHVzZWQgaW4gdGhlIGN1c3RvbUNvZGUpXHJcbiAgZm9yIChjb25zdCBwcm9wIGluIGRlZmF1bHRPcHRpb25zKSB7XHJcbiAgICBpZiAodHlwZW9mIGRlZmF1bHRPcHRpb25zW3Byb3BdICE9PSAnZnVuY3Rpb24nKSB7XHJcbiAgICAgIGRlbGV0ZSBkZWZhdWx0T3B0aW9uc1twcm9wXTtcclxuICAgIH1cclxuICB9XHJcblxyXG4gIC8vIFNldCB0aGUgZGVmYXVsdCBvcHRpb25zIGJhY2tcclxuICBzZXRPcHRpb25zKEhpZ2hjaGFydHMuc2V0T3B0aW9uc09iaik7XHJcblxyXG4gIC8vIEVtcHR5IHRoZSBjdXN0b20gZ2xvYmFsIG9wdGlvbnMgb2JqZWN0XHJcbiAgSGlnaGNoYXJ0cy5zZXRPcHRpb25zT2JqID0ge307XHJcbn1cclxuIiwiLyoqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKipcclxuXHJcbkhpZ2hjaGFydHMgRXhwb3J0IFNlcnZlclxyXG5cclxuQ29weXJpZ2h0IChjKSAyMDE2LTIwMjQsIEhpZ2hzb2Z0XHJcblxyXG5MaWNlbmNlZCB1bmRlciB0aGUgTUlUIGxpY2VuY2UuXHJcblxyXG5BZGRpdGlvbmFsbHkgYSB2YWxpZCBIaWdoY2hhcnRzIGxpY2Vuc2UgaXMgcmVxdWlyZWQgZm9yIHVzZS5cclxuXHJcblNlZSBMSUNFTlNFIGZpbGUgaW4gcm9vdCBmb3IgZGV0YWlscy5cclxuXHJcbioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKiovXHJcblxyXG5pbXBvcnQgZnMgZnJvbSAnZnMnO1xyXG5pbXBvcnQgKiBhcyB1cmwgZnJvbSAndXJsJztcclxuXHJcbmltcG9ydCBwdXBwZXRlZXIgZnJvbSAncHVwcGV0ZWVyJztcclxuXHJcbmltcG9ydCB7IGdldENhY2hlUGF0aCB9IGZyb20gJy4vY2FjaGUuanMnO1xyXG5pbXBvcnQgeyBnZXRPcHRpb25zIH0gZnJvbSAnLi9jb25maWcuanMnO1xyXG5pbXBvcnQgeyBzZXR1cEhpZ2hjaGFydHMgfSBmcm9tICcuL2hpZ2hjaGFydHMuanMnO1xyXG5pbXBvcnQgeyBsb2csIGxvZ1dpdGhTdGFjayB9IGZyb20gJy4vbG9nZ2VyLmpzJztcclxuXHJcbmltcG9ydCBFeHBvcnRFcnJvciBmcm9tICcuL2Vycm9ycy9FeHBvcnRFcnJvci5qcyc7XHJcblxyXG4vLyBHZXQgdGhlIHRlbXBsYXRlIGZvciB0aGUgcGFnZVxyXG5jb25zdCBfX2Rpcm5hbWUgPSB1cmwuZmlsZVVSTFRvUGF0aChuZXcgVVJMKCcuJywgaW1wb3J0Lm1ldGEudXJsKSk7XHJcbmNvbnN0IHRlbXBsYXRlID0gZnMucmVhZEZpbGVTeW5jKFxyXG4gIF9fZGlybmFtZSArICcvLi4vdGVtcGxhdGVzL3RlbXBsYXRlLmh0bWwnLFxyXG4gICd1dGY4J1xyXG4pO1xyXG5cclxubGV0IGJyb3dzZXI7XHJcblxyXG4vKipcclxuICogUmV0cmlldmVzIHRoZSBleGlzdGluZyBQdXBwZXRlZXIgYnJvd3NlciBpbnN0YW5jZS5cclxuICpcclxuICogQHJldHVybnMge1Byb21pc2U8b2JqZWN0Pn0gQSBQcm9taXNlIHJlc29sdmluZyB0byB0aGUgUHVwcGV0ZWVyIGJyb3dzZXJcclxuICogaW5zdGFuY2UuXHJcbiAqXHJcbiAqIEB0aHJvd3Mge0V4cG9ydEVycm9yfSBUaHJvd3MgYW4gRXhwb3J0RXJyb3IgaWYgbm8gdmFsaWQgYnJvd3NlciBoYXMgYmVlblxyXG4gKiBjcmVhdGVkLlxyXG4gKi9cclxuZXhwb3J0IGZ1bmN0aW9uIGdldCgpIHtcclxuICBpZiAoIWJyb3dzZXIpIHtcclxuICAgIHRocm93IG5ldyBFeHBvcnRFcnJvcignW2Jyb3dzZXJdIE5vIHZhbGlkIGJyb3dzZXIgaGFzIGJlZW4gY3JlYXRlZC4nKTtcclxuICB9XHJcbiAgcmV0dXJuIGJyb3dzZXI7XHJcbn1cclxuXHJcbi8qKlxyXG4gKiBDcmVhdGVzIGEgUHVwcGV0ZWVyIGJyb3dzZXIgaW5zdGFuY2Ugd2l0aCB0aGUgc3BlY2lmaWVkIGFyZ3VtZW50cy5cclxuICpcclxuICogQHBhcmFtIHtBcnJheX0gcHVwcGV0ZWVyQXJncyAtIEFkZGl0aW9uYWwgYXJndW1lbnRzIGZvciBQdXBwZXRlZXIgbGF1bmNoLlxyXG4gKlxyXG4gKiBAcmV0dXJucyB7UHJvbWlzZTxvYmplY3Q+fSBBIFByb21pc2UgcmVzb2x2aW5nIHRvIHRoZSBQdXBwZXRlZXIgYnJvd3NlclxyXG4gKiBpbnN0YW5jZS5cclxuICpcclxuICogQHRocm93cyB7RXhwb3J0RXJyb3J9IFRocm93cyBhbiBFeHBvcnRFcnJvciBpZiBtYXggcmV0cmllcyB0byBvcGVuIGEgYnJvd3NlclxyXG4gKiBpbnN0YW5jZSBhcmUgcmVhY2hlZCwgb3IgaWYgbm8gYnJvd3NlciBpbnN0YW5jZSBpcyBmb3VuZCBhZnRlciByZXRyaWVzLlxyXG4gKi9cclxuZXhwb3J0IGFzeW5jIGZ1bmN0aW9uIGNyZWF0ZShwdXBwZXRlZXJBcmdzKSB7XHJcbiAgLy8gR2V0IHRoZSBkZWJ1ZyBvcHRpb25zXHJcbiAgY29uc3QgeyBlbmFibGU6IGVuYWJsZWREZWJ1ZywgLi4uZGVidWcgfSA9IGdldE9wdGlvbnMoKS5kZWJ1ZztcclxuICBjb25zdCBsYXVuY2hPcHRpb25zID0ge1xyXG4gICAgaGVhZGxlc3M6ICdzaGVsbCcsXHJcbiAgICB1c2VyRGF0YURpcjogJy4vdG1wLycsXHJcbiAgICBhcmdzOiBwdXBwZXRlZXJBcmdzLFxyXG4gICAgaGFuZGxlU0lHSU5UOiBmYWxzZSxcclxuICAgIGhhbmRsZVNJR1RFUk06IGZhbHNlLFxyXG4gICAgaGFuZGxlU0lHSFVQOiBmYWxzZSxcclxuICAgIHdhaXRGb3JJbml0aWFsUGFnZTogZmFsc2UsXHJcbiAgICBkZWZhdWx0Vmlld3BvcnQ6IG51bGwsXHJcbiAgICAuLi4oZW5hYmxlZERlYnVnICYmIGRlYnVnKVxyXG4gIH07XHJcblxyXG4gIC8vIENyZWF0ZSBhIGJyb3dzZXJcclxuICBpZiAoIWJyb3dzZXIpIHtcclxuICAgIGxldCB0cnlDb3VudCA9IDA7XHJcblxyXG4gICAgY29uc3Qgb3BlbiA9IGFzeW5jICgpID0+IHtcclxuICAgICAgdHJ5IHtcclxuICAgICAgICBsb2coXHJcbiAgICAgICAgICAzLFxyXG4gICAgICAgICAgYFticm93c2VyXSBBdHRlbXB0aW5nIHRvIGdldCBhIGJyb3dzZXIgaW5zdGFuY2UgKHRyeSAkeysrdHJ5Q291bnR9KS5gXHJcbiAgICAgICAgKTtcclxuICAgICAgICBicm93c2VyID0gYXdhaXQgcHVwcGV0ZWVyLmxhdW5jaChsYXVuY2hPcHRpb25zKTtcclxuICAgICAgfSBjYXRjaCAoZXJyb3IpIHtcclxuICAgICAgICBsb2dXaXRoU3RhY2soXHJcbiAgICAgICAgICAxLFxyXG4gICAgICAgICAgZXJyb3IsXHJcbiAgICAgICAgICAnW2Jyb3dzZXJdIEZhaWxlZCB0byBsYXVuY2ggYSBicm93c2VyIGluc3RhbmNlLidcclxuICAgICAgICApO1xyXG5cclxuICAgICAgICAvLyBSZXRyeSB0byBsYXVuY2ggYnJvd3NlciB1bnRpbCByZWFjaGluZyBtYXggYXR0ZW1wdHNcclxuICAgICAgICBpZiAodHJ5Q291bnQgPCAyNSkge1xyXG4gICAgICAgICAgbG9nKDMsIGBbYnJvd3Nlcl0gUmV0cnkgdG8gb3BlbiBhIGJyb3dzZXIgKCR7dHJ5Q291bnR9IG91dCBvZiAyNSkuYCk7XHJcbiAgICAgICAgICBhd2FpdCBuZXcgUHJvbWlzZSgocmVzcG9uc2UpID0+IHNldFRpbWVvdXQocmVzcG9uc2UsIDQwMDApKTtcclxuICAgICAgICAgIGF3YWl0IG9wZW4oKTtcclxuICAgICAgICB9IGVsc2Uge1xyXG4gICAgICAgICAgdGhyb3cgZXJyb3I7XHJcbiAgICAgICAgfVxyXG4gICAgICB9XHJcbiAgICB9O1xyXG5cclxuICAgIHRyeSB7XHJcbiAgICAgIGF3YWl0IG9wZW4oKTtcclxuICAgICAgLy8gRGVidWcgbW9kZSBpbmZvcm1cclxuICAgICAgaWYgKGVuYWJsZWREZWJ1Zykge1xyXG4gICAgICAgIGxvZygzLCBgW2Jyb3dzZXJdIExhdW5jaGVkIGJyb3dzZXIgaW4gZGVidWcgbW9kZS5gKTtcclxuICAgICAgfVxyXG4gICAgfSBjYXRjaCAoZXJyb3IpIHtcclxuICAgICAgdGhyb3cgbmV3IEV4cG9ydEVycm9yKFxyXG4gICAgICAgICdbYnJvd3Nlcl0gTWF4aW11bSByZXRyaWVzIHRvIG9wZW4gYSBicm93c2VyIGluc3RhbmNlIHJlYWNoZWQuJ1xyXG4gICAgICApLnNldEVycm9yKGVycm9yKTtcclxuICAgIH1cclxuXHJcbiAgICBpZiAoIWJyb3dzZXIpIHtcclxuICAgICAgdGhyb3cgbmV3IEV4cG9ydEVycm9yKCdbYnJvd3Nlcl0gQ2Fubm90IGZpbmQgYSBicm93c2VyIHRvIG9wZW4uJyk7XHJcbiAgICB9XHJcbiAgfVxyXG5cclxuICAvLyBSZXR1cm4gYSBicm93c2VyIHByb21pc2VcclxuICByZXR1cm4gYnJvd3NlcjtcclxufVxyXG5cclxuLyoqXHJcbiAqIENsb3NlcyB0aGUgUHVwcGV0ZWVyIGJyb3dzZXIgaW5zdGFuY2UgaWYgaXQgaXMgY29ubmVjdGVkLlxyXG4gKlxyXG4gKiBAcmV0dXJucyB7UHJvbWlzZTxib29sZWFuPn0gQSBQcm9taXNlIHJlc29sdmluZyB0byB0cnVlIGFmdGVyIHRoZSBicm93c2VyXHJcbiAqIGlzIGNsb3NlZC5cclxuICovXHJcbmV4cG9ydCBhc3luYyBmdW5jdGlvbiBjbG9zZSgpIHtcclxuICAvLyBDbG9zZSB0aGUgYnJvd3NlciB3aGVuIGNvbm5uZWN0ZWRcclxuICBpZiAoYnJvd3Nlcj8uY29ubmVjdGVkKSB7XHJcbiAgICBhd2FpdCBicm93c2VyLmNsb3NlKCk7XHJcbiAgfVxyXG4gIGxvZyg0LCAnW2Jyb3dzZXJdIENsb3NlZCB0aGUgYnJvd3Nlci4nKTtcclxufVxyXG5cclxuLyoqXHJcbiAqIENyZWF0ZXMgYSBuZXcgUHVwcGV0ZWVyIFBhZ2Ugd2l0aGluIGFuIGV4aXN0aW5nIGJyb3dzZXIgaW5zdGFuY2UuXHJcbiAqXHJcbiAqIElmIHRoZSBicm93c2VyIGluc3RhbmNlIGlzIG5vdCBhdmFpbGFibGUsIHJldHVybnMgZmFsc2UuXHJcbiAqXHJcbiAqIFRoZSBmdW5jdGlvbiBjcmVhdGVzIGEgbmV3IHBhZ2UsIGRpc2FibGVzIGNhY2hpbmcsIHNldHMgY29udGVudCB1c2luZ1xyXG4gKiBzZXRQYWdlQ29udGVudCgpLCBhbmQgcmV0dXJucyB0aGUgY3JlYXRlZCBQdXBwZXRlZXIgUGFnZS5cclxuICpcclxuICogQHJldHVybnMgeyhib29sZWFufG9iamVjdCl9IFJldHVybnMgZmFsc2UgaWYgdGhlIGJyb3dzZXIgaW5zdGFuY2UgaXMgbm90XHJcbiAqIGF2YWlsYWJsZSwgb3IgYSBQdXBwZXRlZXIgUGFnZSBvYmplY3QgcmVwcmVzZW50aW5nIHRoZSBuZXdseSBjcmVhdGVkIHBhZ2UuXHJcbiAqL1xyXG5leHBvcnQgYXN5bmMgZnVuY3Rpb24gbmV3UGFnZSgpIHtcclxuICBpZiAoIWJyb3dzZXIpIHtcclxuICAgIHJldHVybiBmYWxzZTtcclxuICB9XHJcblxyXG4gIC8vIENyZWF0ZSBhIHBhZ2VcclxuICBjb25zdCBwYWdlID0gYXdhaXQgYnJvd3Nlci5uZXdQYWdlKCk7XHJcblxyXG4gIC8vIERpc2FibGUgY2FjaGVcclxuICBhd2FpdCBwYWdlLnNldENhY2hlRW5hYmxlZChmYWxzZSk7XHJcblxyXG4gIC8vIFNldCB0aGUgY29udGVudFxyXG4gIGF3YWl0IHNldFBhZ2VDb250ZW50KHBhZ2UpO1xyXG5cclxuICByZXR1cm4gcGFnZTtcclxufVxyXG5cclxuLyoqXHJcbiAqIENsZWFycyB0aGUgY29udGVudCBvZiBhIFB1cHBldGVlciBQYWdlIGJhc2VkIG9uIHRoZSBzcGVjaWZpZWQgbW9kZS5cclxuICpcclxuICogQHBhcmFtIHtPYmplY3R9IHBhZ2UgLSBUaGUgUHVwcGV0ZWVyIFBhZ2Ugb2JqZWN0IHRvIGJlIGNsZWFyZWQuXHJcbiAqIEBwYXJhbSB7Ym9vbGVhbn0gaGFyZFJlc2V0IC0gQSBmbGFnIGluZGljYXRpbmcgdGhlIHR5cGUgb2YgY2xlYXJpbmdcclxuICogdG8gYmUgcGVyZm9ybWVkLiBJZiB0cnVlLCBuYXZpZ2F0ZXMgdG8gJ2Fib3V0OmJsYW5rJyBhbmQgcmVzZXRzIGNvbnRlbnRcclxuICogYW5kIHNjcmlwdHMuIElmIGZhbHNlLCBjbGVhcnMgdGhlIGJvZHkgY29udGVudCBieSBzZXR0aW5nIGEgcHJlZGVmaW5lZCBIVE1MXHJcbiAqIHN0cnVjdHVyZS5cclxuICpcclxuICogQHRocm93cyB7RXJyb3J9IExvZ3MgdGhyb3duIGVycm9yIGlmIGNsZWFyaW5nIHRoZSBwYWdlIGNvbnRlbnQgZmFpbHMuXHJcbiAqL1xyXG5leHBvcnQgYXN5bmMgZnVuY3Rpb24gY2xlYXJQYWdlKHBhZ2UsIGhhcmRSZXNldCA9IGZhbHNlKSB7XHJcbiAgdHJ5IHtcclxuICAgIGlmICghcGFnZS5pc0Nsb3NlZCgpKSB7XHJcbiAgICAgIGlmIChoYXJkUmVzZXQpIHtcclxuICAgICAgICAvLyBOYXZpZ2F0ZSB0byBhYm91dDpibGFua1xyXG4gICAgICAgIGF3YWl0IHBhZ2UuZ290bygnYWJvdXQ6YmxhbmsnLCB7IHdhaXRVbnRpbDogJ2RvbWNvbnRlbnRsb2FkZWQnIH0pO1xyXG5cclxuICAgICAgICAvLyBTZXQgdGhlIGNvbnRlbnQgYW5kIGFuZCBzY3JpcHRzIGFnYWluXHJcbiAgICAgICAgYXdhaXQgc2V0UGFnZUNvbnRlbnQocGFnZSk7XHJcbiAgICAgIH0gZWxzZSB7XHJcbiAgICAgICAgLy8gQ2xlYXIgYm9keSBjb250ZW50XHJcbiAgICAgICAgYXdhaXQgcGFnZS5ldmFsdWF0ZSgoKSA9PiB7XHJcbiAgICAgICAgICBkb2N1bWVudC5ib2R5LmlubmVySFRNTCA9XHJcbiAgICAgICAgICAgICc8ZGl2IGlkPVwiY2hhcnQtY29udGFpbmVyXCI+PGRpdiBpZD1cImNvbnRhaW5lclwiPjwvZGl2PjwvZGl2Pic7XHJcbiAgICAgICAgfSk7XHJcbiAgICAgIH1cclxuICAgIH1cclxuICB9IGNhdGNoIChlcnJvcikge1xyXG4gICAgbG9nV2l0aFN0YWNrKFxyXG4gICAgICAyLFxyXG4gICAgICBlcnJvcixcclxuICAgICAgJ1ticm93c2VyXSBDb3VsZCBub3QgY2xlYXIgdGhlIGNvbnRlbnQgb2YgdGhlIHBhZ2UuJ1xyXG4gICAgKTtcclxuICB9XHJcbn1cclxuXHJcbi8qKlxyXG4gKiBTZXRzIHRoZSBjb250ZW50IGZvciBhIFB1cHBldGVlciBQYWdlIHVzaW5nIGEgcHJlZGVmaW5lZCB0ZW1wbGF0ZVxyXG4gKiBhbmQgYWRkaXRpb25hbCBzY3JpcHRzLiBBbHNvLCBzZXRzIHRoZSBwYWdlZXJyb3IgaW4gb3JkZXIgdG8gY2F0Y2hcclxuICogYW5kIGRpc3BsYXkgZXJyb3JzIGZyb20gdGhlIHdpbmRvdyBjb250ZXh0LlxyXG4gKlxyXG4gKiBAcGFyYW0ge09iamVjdH0gcGFnZSAtIFRoZSBQdXBwZXRlZXIgUGFnZSBvYmplY3QgZm9yIHdoaWNoIHRoZSBjb250ZW50XHJcbiAqIGlzIGJlaW5nIHNldC5cclxuICovXHJcbmFzeW5jIGZ1bmN0aW9uIHNldFBhZ2VDb250ZW50KHBhZ2UpIHtcclxuICBhd2FpdCBwYWdlLnNldENvbnRlbnQodGVtcGxhdGUsIHsgd2FpdFVudGlsOiAnZG9tY29udGVudGxvYWRlZCcgfSk7XHJcblxyXG4gIC8vIEFkZCBhbGwgcmVnaXN0ZXJlZCBIaWdjaGFydHMgc2NyaXB0cywgcXVpdGUgZGVtYW5kaW5nXHJcbiAgYXdhaXQgcGFnZS5hZGRTY3JpcHRUYWcoeyBwYXRoOiBgJHtnZXRDYWNoZVBhdGgoKX0vc291cmNlcy5qc2AgfSk7XHJcblxyXG4gIC8vIFNldCB0aGUgaW5pdGlhbCBhbmltT2JqZWN0XHJcbiAgYXdhaXQgcGFnZS5ldmFsdWF0ZShzZXR1cEhpZ2hjaGFydHMpO1xyXG59XHJcblxyXG5leHBvcnQgZGVmYXVsdCB7XHJcbiAgZ2V0LFxyXG4gIGNyZWF0ZSxcclxuICBjbG9zZSxcclxuICBuZXdQYWdlLFxyXG4gIGNsZWFyUGFnZVxyXG59O1xyXG4iLCIvKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKlxyXG5cclxuSGlnaGNoYXJ0cyBFeHBvcnQgU2VydmVyXHJcblxyXG5Db3B5cmlnaHQgKGMpIDIwMTYtMjAyNCwgSGlnaHNvZnRcclxuXHJcbkxpY2VuY2VkIHVuZGVyIHRoZSBNSVQgbGljZW5jZS5cclxuXHJcbkFkZGl0aW9uYWxseSBhIHZhbGlkIEhpZ2hjaGFydHMgbGljZW5zZSBpcyByZXF1aXJlZCBmb3IgdXNlLlxyXG5cclxuU2VlIExJQ0VOU0UgZmlsZSBpbiByb290IGZvciBkZXRhaWxzLlxyXG5cclxuKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKi9cclxuXHJcbmltcG9ydCB7IHJlYWRGaWxlU3luYyB9IGZyb20gJ2ZzJztcclxuaW1wb3J0IHBhdGggZnJvbSAncGF0aCc7XHJcbmltcG9ydCAqIGFzIHVybCBmcm9tICd1cmwnO1xyXG5cclxuaW1wb3J0IGNhY2hlIGZyb20gJy4vY2FjaGUuanMnO1xyXG5pbXBvcnQgeyB0cmlnZ2VyRXhwb3J0IH0gZnJvbSAnLi9oaWdoY2hhcnRzLmpzJztcclxuaW1wb3J0IHsgbG9nLCBsb2dXaXRoU3RhY2sgfSBmcm9tICcuL2xvZ2dlci5qcyc7XHJcbmltcG9ydCBzdmdUZW1wbGF0ZSBmcm9tICcuLy4uL3RlbXBsYXRlcy9zdmdfZXhwb3J0L3N2Z19leHBvcnQuanMnO1xyXG5cclxuaW1wb3J0IEV4cG9ydEVycm9yIGZyb20gJy4vZXJyb3JzL0V4cG9ydEVycm9yLmpzJztcclxuXHJcbmNvbnN0IF9fYmFzZWRpciA9IHVybC5maWxlVVJMVG9QYXRoKG5ldyBVUkwoJy4nLCBpbXBvcnQubWV0YS51cmwpKTtcclxuXHJcbi8qKlxyXG4gKiBSZXRyaWV2ZXMgdGhlIGNsaXBwaW5nIHJlZ2lvbiBjb29yZGluYXRlcyBvZiB0aGUgc3BlY2lmaWVkIHBhZ2UgZWxlbWVudCB3aXRoXHJcbiAqIHRoZSBpZCAnY2hhcnQtY29udGFpbmVyJy5cclxuICpcclxuICogQHBhcmFtIHtPYmplY3R9IHBhZ2UgLSBQdXBwZXRlZXIgcGFnZSBvYmplY3QuXHJcbiAqXHJcbiAqIEByZXR1cm5zIHtQcm9taXNlPE9iamVjdD59IFByb21pc2UgcmVzb2x2aW5nIHRvIGFuIG9iamVjdCBjb250YWluaW5nXHJcbiAqIHgsIHksIHdpZHRoLCBhbmQgaGVpZ2h0IHByb3BlcnRpZXMuXHJcbiAqL1xyXG5jb25zdCBnZXRDbGlwUmVnaW9uID0gKHBhZ2UpID0+XHJcbiAgcGFnZS4kZXZhbCgnI2NoYXJ0LWNvbnRhaW5lcicsIChlbGVtZW50KSA9PiB7XHJcbiAgICBjb25zdCB7IHgsIHksIHdpZHRoLCBoZWlnaHQgfSA9IGVsZW1lbnQuZ2V0Qm91bmRpbmdDbGllbnRSZWN0KCk7XHJcbiAgICByZXR1cm4ge1xyXG4gICAgICB4LFxyXG4gICAgICB5LFxyXG4gICAgICB3aWR0aCxcclxuICAgICAgaGVpZ2h0OiBNYXRoLnRydW5jKGhlaWdodCA+IDEgPyBoZWlnaHQgOiA1MDApXHJcbiAgICB9O1xyXG4gIH0pO1xyXG5cclxuLyoqXHJcbiAqIENyZWF0ZXMgYW4gaW1hZ2UgdXNpbmcgUHVwcGV0ZWVyJ3MgcGFnZSBzY3JlZW5zaG90IGZ1bmN0aW9uYWxpdHkgd2l0aFxyXG4gKiBzcGVjaWZpZWQgb3B0aW9ucy5cclxuICpcclxuICogQHBhcmFtIHtPYmplY3R9IHBhZ2UgLSBQdXBwZXRlZXIgcGFnZSBvYmplY3QuXHJcbiAqIEBwYXJhbSB7c3RyaW5nfSB0eXBlIC0gSW1hZ2UgdHlwZS5cclxuICogQHBhcmFtIHtzdHJpbmd9IGVuY29kaW5nIC0gSW1hZ2UgZW5jb2RpbmcuXHJcbiAqIEBwYXJhbSB7T2JqZWN0fSBjbGlwIC0gQ2xpcHBpbmcgcmVnaW9uIGNvb3JkaW5hdGVzLlxyXG4gKiBAcGFyYW0ge251bWJlcn0gcmFzdGVyaXphdGlvblRpbWVvdXQgLSBUaW1lb3V0IGZvciByYXN0ZXJpemF0aW9uXHJcbiAqIGluIG1pbGxpc2Vjb25kcy5cclxuICpcclxuICogQHJldHVybnMge1Byb21pc2U8QnVmZmVyPn0gUHJvbWlzZSByZXNvbHZpbmcgdG8gdGhlIGltYWdlIGJ1ZmZlciBvciByZWplY3RpbmdcclxuICogd2l0aCBhbiBFeHBvcnRFcnJvciBmb3IgdGltZW91dC5cclxuICovXHJcbmNvbnN0IGNyZWF0ZUltYWdlID0gKHBhZ2UsIHR5cGUsIGVuY29kaW5nLCBjbGlwLCByYXN0ZXJpemF0aW9uVGltZW91dCkgPT5cclxuICBQcm9taXNlLnJhY2UoW1xyXG4gICAgcGFnZS5zY3JlZW5zaG90KHtcclxuICAgICAgdHlwZSxcclxuICAgICAgZW5jb2RpbmcsXHJcbiAgICAgIGNsaXAsXHJcbiAgICAgIGNhcHR1cmVCZXlvbmRWaWV3cG9ydDogdHJ1ZSxcclxuICAgICAgZnVsbFBhZ2U6IGZhbHNlLFxyXG4gICAgICBvcHRpbWl6ZUZvclNwZWVkOiB0cnVlLFxyXG4gICAgICAuLi4odHlwZSAhPT0gJ3BuZycgPyB7IHF1YWxpdHk6IDgwIH0gOiB7fSksXHJcblxyXG4gICAgICAvLyAjNDQ3LCAjNDYzIC0gYWx3YXlzIHJlbmRlciBvbiBhIHRyYW5zcGFyZW50IHBhZ2UgaWYgdGhlIGV4cGVjdGVkIHR5cGVcclxuICAgICAgLy8gZm9ybWF0IGlzIFBOR1xyXG4gICAgICBvbWl0QmFja2dyb3VuZDogdHlwZSA9PSAncG5nJ1xyXG4gICAgfSksXHJcbiAgICBuZXcgUHJvbWlzZSgoX3Jlc29sdmUsIHJlamVjdCkgPT5cclxuICAgICAgc2V0VGltZW91dChcclxuICAgICAgICAoKSA9PiByZWplY3QobmV3IEV4cG9ydEVycm9yKCdSYXN0ZXJpemF0aW9uIHRpbWVvdXQnKSksXHJcbiAgICAgICAgcmFzdGVyaXphdGlvblRpbWVvdXQgfHwgMTUwMFxyXG4gICAgICApXHJcbiAgICApXHJcbiAgXSk7XHJcblxyXG4vKipcclxuICogQ3JlYXRlcyBhIFBERiB1c2luZyBQdXBwZXRlZXIncyBwYWdlIHBkZiBmdW5jdGlvbmFsaXR5IHdpdGggc3BlY2lmaWVkXHJcbiAqIG9wdGlvbnMuXHJcbiAqXHJcbiAqIEBwYXJhbSB7T2JqZWN0fSBwYWdlIC0gUHVwcGV0ZWVyIHBhZ2Ugb2JqZWN0LlxyXG4gKiBAcGFyYW0ge251bWJlcn0gaGVpZ2h0IC0gUERGIGhlaWdodC5cclxuICogQHBhcmFtIHtudW1iZXJ9IHdpZHRoIC0gUERGIHdpZHRoLlxyXG4gKiBAcGFyYW0ge3N0cmluZ30gZW5jb2RpbmcgLSBQREYgZW5jb2RpbmcuXHJcbiAqXHJcbiAqIEByZXR1cm5zIHtQcm9taXNlPEJ1ZmZlcj59IFByb21pc2UgcmVzb2x2aW5nIHRvIHRoZSBQREYgYnVmZmVyLlxyXG4gKi9cclxuY29uc3QgY3JlYXRlUERGID0gYXN5bmMgKFxyXG4gIHBhZ2UsXHJcbiAgaGVpZ2h0LFxyXG4gIHdpZHRoLFxyXG4gIGVuY29kaW5nLFxyXG4gIHJhc3Rlcml6YXRpb25UaW1lb3V0XHJcbikgPT4ge1xyXG4gIGF3YWl0IHBhZ2UuZW11bGF0ZU1lZGlhVHlwZSgnc2NyZWVuJyk7XHJcbiAgcmV0dXJuIFByb21pc2UucmFjZShbXHJcbiAgICBwYWdlLnBkZih7XHJcbiAgICAgIC8vIFRoaXMgd2lsbCByZW1vdmUgYW4gZXh0cmEgZW1wdHkgcGFnZSBpbiBQREYgZXhwb3J0c1xyXG4gICAgICBoZWlnaHQ6IGhlaWdodCArIDEsXHJcbiAgICAgIHdpZHRoLFxyXG4gICAgICBlbmNvZGluZ1xyXG4gICAgfSksXHJcbiAgICBuZXcgUHJvbWlzZSgoX3Jlc29sdmUsIHJlamVjdCkgPT5cclxuICAgICAgc2V0VGltZW91dChcclxuICAgICAgICAoKSA9PiByZWplY3QobmV3IEV4cG9ydEVycm9yKCdSYXN0ZXJpemF0aW9uIHRpbWVvdXQnKSksXHJcbiAgICAgICAgcmFzdGVyaXphdGlvblRpbWVvdXQgfHwgMTUwMFxyXG4gICAgICApXHJcbiAgICApXHJcbiAgXSk7XHJcbn07XHJcblxyXG4vKipcclxuICogQ3JlYXRlcyBhbiBTVkcgc3RyaW5nIGJ5IGV2YWx1YXRpbmcgdGhlIG91dGVySFRNTCBvZiB0aGUgZmlyc3QgJ3N2ZycgZWxlbWVudFxyXG4gKiBpbnNpZGUgYW4gZWxlbWVudCB3aXRoIHRoZSBpZCAnY29udGFpbmVyJy5cclxuICpcclxuICogQHBhcmFtIHtPYmplY3R9IHBhZ2UgLSBQdXBwZXRlZXIgcGFnZSBvYmplY3QuXHJcbiAqXHJcbiAqIEByZXR1cm5zIHtQcm9taXNlPHN0cmluZz59IFByb21pc2UgcmVzb2x2aW5nIHRvIHRoZSBTVkcgc3RyaW5nLlxyXG4gKi9cclxuY29uc3QgY3JlYXRlU1ZHID0gKHBhZ2UpID0+XHJcbiAgcGFnZS4kZXZhbCgnI2NvbnRhaW5lciBzdmc6Zmlyc3Qtb2YtdHlwZScsIChlbGVtZW50KSA9PiBlbGVtZW50Lm91dGVySFRNTCk7XHJcblxyXG4vKipcclxuICogU2V0cyB0aGUgc3BlY2lmaWVkIGNoYXJ0IGFuZCBvcHRpb25zIGFzIGNvbmZpZ3VyYXRpb24gaW50byB0aGUgdHJpZ2dlckV4cG9ydFxyXG4gKiBmdW5jdGlvbiB3aXRoaW4gdGhlIHdpbmRvdyBjb250ZXh0IHVzaW5nIHBhZ2UuZXZhbHVhdGUuXHJcbiAqXHJcbiAqIEBwYXJhbSB7T2JqZWN0fSBwYWdlIC0gUHVwcGV0ZWVyIHBhZ2Ugb2JqZWN0LlxyXG4gKiBAcGFyYW0ge2FueX0gY2hhcnQgLSBUaGUgY2hhcnQgb2JqZWN0IHRvIGJlIGNvbmZpZ3VyZWQuXHJcbiAqIEBwYXJhbSB7T2JqZWN0fSBvcHRpb25zIC0gQ29uZmlndXJhdGlvbiBvcHRpb25zIGZvciB0aGUgY2hhcnQuXHJcbiAqXHJcbiAqIEByZXR1cm5zIHtQcm9taXNlPHZvaWQ+fSBQcm9taXNlIHJlc29sdmluZyBhZnRlciB0aGUgY29uZmlndXJhdGlvbiBpcyBzZXQuXHJcbiAqL1xyXG5jb25zdCBzZXRBc0NvbmZpZyA9IChwYWdlLCBjaGFydCwgb3B0aW9ucywgZGlzcGxheUVycm9ycykgPT5cclxuICBwYWdlLmV2YWx1YXRlKHRyaWdnZXJFeHBvcnQsIGNoYXJ0LCBvcHRpb25zLCBkaXNwbGF5RXJyb3JzKTtcclxuXHJcbi8qKlxyXG4gKiBFeHBvcnRzIHRvIGEgY2hhcnQgZnJvbSBhIHBhZ2UgdXNpbmcgUHVwcGV0ZWVyLlxyXG4gKlxyXG4gKiBAcGFyYW0ge09iamVjdH0gcGFnZSAtIFB1cHBldGVlciBwYWdlIG9iamVjdC5cclxuICogQHBhcmFtIHthbnl9IGNoYXJ0IC0gVGhlIGNoYXJ0IG9iamVjdCBvciBTVkcgY29uZmlndXJhdGlvbiB0byBiZSBleHBvcnRlZC5cclxuICogQHBhcmFtIHtPYmplY3R9IG9wdGlvbnMgLSBFeHBvcnQgb3B0aW9ucyBhbmQgY29uZmlndXJhdGlvbi5cclxuICpcclxuICogQHJldHVybnMge1Byb21pc2U8c3RyaW5nIHwgQnVmZmVyIHwgRXhwb3J0RXJyb3I+fSBQcm9taXNlIHJlc29sdmluZyB0b1xyXG4gKiB0aGUgZXhwb3J0ZWQgZGF0YSBvciByZWplY3Rpbmcgd2l0aCBhbiBFeHBvcnRFcnJvci5cclxuICovXHJcbmV4cG9ydCBkZWZhdWx0IGFzeW5jIChwYWdlLCBjaGFydCwgb3B0aW9ucykgPT4ge1xyXG4gIC8qKlxyXG4gICAqIEtlZXBzIHRyYWNrIG9mIGFsbCByZXNvdXJjZXMgYWRkZWQgb24gdGhlIHBhZ2Ugd2l0aCBhZGRYWFhUYWcuIGV0Y1xyXG4gICAqIEl0J3MgVklUQUwgdGhhdCBhbGwgYWRkZWQgcmVzb3VyY2VzIGVuZHMgdXAgaGVyZSBzbyB3ZSBjYW4gY2xlYXIgdGhpbmdzXHJcbiAgICogb3V0IHdoZW4gZG9pbmcgYSBuZXcgZXhwb3J0IGluIHRoZSBzYW1lIHBhZ2UhXHJcbiAgICovXHJcbiAgY29uc3QgaW5qZWN0ZWRSZXNvdXJjZXMgPSBbXTtcclxuXHJcbiAgLyoqIENsZWFyIG91dCBhbGwgc3RhdGUgc2V0IG9uIHRoZSBwYWdlIHdpdGggYWRkU2NyaXB0VGFnL2FkZFN0eWxlVGFnLiAqL1xyXG4gIGNvbnN0IGNsZWFySW5qZWN0ZWQgPSBhc3luYyAocGFnZSkgPT4ge1xyXG4gICAgZm9yIChjb25zdCByZXNvdXJjZSBvZiBpbmplY3RlZFJlc291cmNlcykge1xyXG4gICAgICBhd2FpdCByZXNvdXJjZS5kaXNwb3NlKCk7XHJcbiAgICB9XHJcblxyXG4gICAgLy8gRGVzdHJveSBvbGQgY2hhcnRzIGFmdGVyIGV4cG9ydCBpcyBkb25lIGFuZCByZXNldCBhbGwgQ1NTIGFuZCBzY3JpcHQgdGFnc1xyXG4gICAgYXdhaXQgcGFnZS5ldmFsdWF0ZSgoKSA9PiB7XHJcbiAgICAgIC8vIFdlIGFyZSBub3QgZ3VhcmFudGVlZCB0aGF0IEhpZ2hjaGFydHMgaXMgbG9hZGVkLCBlLGcsIHdoZW4gZG9pbmcgU1ZHXHJcbiAgICAgIC8vIGV4cG9ydHNcclxuICAgICAgaWYgKHR5cGVvZiBIaWdoY2hhcnRzICE9PSAndW5kZWZpbmVkJykge1xyXG4gICAgICAgIC8vIGVzbGludC1kaXNhYmxlLW5leHQtbGluZSBuby11bmRlZlxyXG4gICAgICAgIGNvbnN0IG9sZENoYXJ0cyA9IEhpZ2hjaGFydHMuY2hhcnRzO1xyXG5cclxuICAgICAgICAvLyBDaGVjayBpbiBhbnkgYWxyZWFkeSBleGlzdGluZyBjaGFydHNcclxuICAgICAgICBpZiAoQXJyYXkuaXNBcnJheShvbGRDaGFydHMpICYmIG9sZENoYXJ0cy5sZW5ndGgpIHtcclxuICAgICAgICAgIC8vIERlc3Ryb3kgb2xkIGNoYXJ0c1xyXG4gICAgICAgICAgZm9yIChjb25zdCBvbGRDaGFydCBvZiBvbGRDaGFydHMpIHtcclxuICAgICAgICAgICAgb2xkQ2hhcnQgJiYgb2xkQ2hhcnQuZGVzdHJveSgpO1xyXG4gICAgICAgICAgICAvLyBlc2xpbnQtZGlzYWJsZS1uZXh0LWxpbmUgbm8tdW5kZWZcclxuICAgICAgICAgICAgSGlnaGNoYXJ0cy5jaGFydHMuc2hpZnQoKTtcclxuICAgICAgICAgIH1cclxuICAgICAgICB9XHJcbiAgICAgIH1cclxuXHJcbiAgICAgIC8vIGVzbGludC1kaXNhYmxlLW5leHQtbGluZSBuby11bmRlZlxyXG4gICAgICBjb25zdCBbLi4uc2NyaXB0c1RvUmVtb3ZlXSA9IGRvY3VtZW50LmdldEVsZW1lbnRzQnlUYWdOYW1lKCdzY3JpcHQnKTtcclxuICAgICAgLy8gZXNsaW50LWRpc2FibGUtbmV4dC1saW5lIG5vLXVuZGVmXHJcbiAgICAgIGNvbnN0IFssIC4uLnN0eWxlc1RvUmVtb3ZlXSA9IGRvY3VtZW50LmdldEVsZW1lbnRzQnlUYWdOYW1lKCdzdHlsZScpO1xyXG4gICAgICAvLyBlc2xpbnQtZGlzYWJsZS1uZXh0LWxpbmUgbm8tdW5kZWZcclxuICAgICAgY29uc3QgWy4uLmxpbmtzVG9SZW1vdmVdID0gZG9jdW1lbnQuZ2V0RWxlbWVudHNCeVRhZ05hbWUoJ2xpbmsnKTtcclxuXHJcbiAgICAgIC8vIFJlbW92ZSB0YWdzXHJcbiAgICAgIGZvciAoY29uc3QgZWxlbWVudCBvZiBbXHJcbiAgICAgICAgLi4uc2NyaXB0c1RvUmVtb3ZlLFxyXG4gICAgICAgIC4uLnN0eWxlc1RvUmVtb3ZlLFxyXG4gICAgICAgIC4uLmxpbmtzVG9SZW1vdmVcclxuICAgICAgXSkge1xyXG4gICAgICAgIGVsZW1lbnQucmVtb3ZlKCk7XHJcbiAgICAgIH1cclxuICAgIH0pO1xyXG4gIH07XHJcblxyXG4gIHRyeSB7XHJcbiAgICBsb2coNCwgJ1tleHBvcnRdIERldGVybWluaW5nIGV4cG9ydCBwYXRoLicpO1xyXG5cclxuICAgIGNvbnN0IGV4cG9ydE9wdGlvbnMgPSBvcHRpb25zLmV4cG9ydDtcclxuXHJcbiAgICAvLyBEZWNpZGUgd2hldGhlciBkaXNwbGF5IGVycm9yIG9yIGRlYmJ1Z2VyIHdyYXBwZXIgYXJvdW5kIGl0XHJcbiAgICBjb25zdCBkaXNwbGF5RXJyb3JzID1cclxuICAgICAgZXhwb3J0T3B0aW9ucz8ub3B0aW9ucz8uY2hhcnQ/LmRpc3BsYXlFcnJvcnMgJiZcclxuICAgICAgY2FjaGUuZ2V0Q2FjaGUoKS5hY3RpdmVNYW5pZmVzdC5tb2R1bGVzLmRlYnVnZ2VyO1xyXG5cclxuICAgIGxldCBpc1NWRztcclxuICAgIGlmIChcclxuICAgICAgY2hhcnQuaW5kZXhPZiAmJlxyXG4gICAgICAoY2hhcnQuaW5kZXhPZignPHN2ZycpID49IDAgfHwgY2hhcnQuaW5kZXhPZignPD94bWwnKSA+PSAwKVxyXG4gICAgKSB7XHJcbiAgICAgIC8vIFNWRyBpbnB1dCBoYW5kbGluZ1xyXG4gICAgICBsb2coNCwgJ1tleHBvcnRdIFRyZWF0aW5nIGFzIFNWRy4nKTtcclxuXHJcbiAgICAgIC8vIElmIGlucHV0IGlzIGFsc28gU1ZHLCBqdXN0IHJldHVybiBpdFxyXG4gICAgICBpZiAoZXhwb3J0T3B0aW9ucy50eXBlID09PSAnc3ZnJykge1xyXG4gICAgICAgIHJldHVybiBjaGFydDtcclxuICAgICAgfVxyXG5cclxuICAgICAgaXNTVkcgPSB0cnVlO1xyXG4gICAgICBhd2FpdCBwYWdlLnNldENvbnRlbnQoc3ZnVGVtcGxhdGUoY2hhcnQpLCB7XHJcbiAgICAgICAgd2FpdFVudGlsOiAnZG9tY29udGVudGxvYWRlZCdcclxuICAgICAgfSk7XHJcbiAgICB9IGVsc2Uge1xyXG4gICAgICAvLyBKU09OIGNvbmZpZyBoYW5kbGluZ1xyXG4gICAgICBsb2coNCwgJ1tleHBvcnRdIFRyZWF0aW5nIGFzIGNvbmZpZy4nKTtcclxuXHJcbiAgICAgIC8vIE5lZWQgdG8gcGVyZm9ybSBzdHJhaWdodCBpbmplY3RcclxuICAgICAgaWYgKGV4cG9ydE9wdGlvbnMuc3RySW5qKSB7XHJcbiAgICAgICAgLy8gSW5qZWN0aW9uIGJhc2VkIGNvbmZpZ3VyYXRpb24gZXhwb3J0XHJcbiAgICAgICAgYXdhaXQgc2V0QXNDb25maWcoXHJcbiAgICAgICAgICBwYWdlLFxyXG4gICAgICAgICAge1xyXG4gICAgICAgICAgICBjaGFydDoge1xyXG4gICAgICAgICAgICAgIGhlaWdodDogZXhwb3J0T3B0aW9ucy5oZWlnaHQsXHJcbiAgICAgICAgICAgICAgd2lkdGg6IGV4cG9ydE9wdGlvbnMud2lkdGhcclxuICAgICAgICAgICAgfVxyXG4gICAgICAgICAgfSxcclxuICAgICAgICAgIG9wdGlvbnMsXHJcbiAgICAgICAgICBkaXNwbGF5RXJyb3JzXHJcbiAgICAgICAgKTtcclxuICAgICAgfSBlbHNlIHtcclxuICAgICAgICAvLyBCYXNpYyBjb25maWd1cmF0aW9uIGV4cG9ydFxyXG4gICAgICAgIGNoYXJ0LmNoYXJ0LmhlaWdodCA9IGV4cG9ydE9wdGlvbnMuaGVpZ2h0O1xyXG4gICAgICAgIGNoYXJ0LmNoYXJ0LndpZHRoID0gZXhwb3J0T3B0aW9ucy53aWR0aDtcclxuXHJcbiAgICAgICAgYXdhaXQgc2V0QXNDb25maWcocGFnZSwgY2hhcnQsIG9wdGlvbnMsIGRpc3BsYXlFcnJvcnMpO1xyXG4gICAgICB9XHJcbiAgICB9XHJcblxyXG4gICAgLy8gVXNlIHJlc291cmNlc1xyXG4gICAgY29uc3QgcmVzb3VyY2VzID0gb3B0aW9ucy5jdXN0b21Mb2dpYy5yZXNvdXJjZXM7XHJcbiAgICBpZiAocmVzb3VyY2VzKSB7XHJcbiAgICAgIGNvbnN0IGluamVjdGVkSnMgPSBbXTtcclxuXHJcbiAgICAgIC8vIExvYWQgY3VzdG9tIEpTIGNvZGVcclxuICAgICAgaWYgKHJlc291cmNlcy5qcykge1xyXG4gICAgICAgIGluamVjdGVkSnMucHVzaCh7XHJcbiAgICAgICAgICBjb250ZW50OiByZXNvdXJjZXMuanNcclxuICAgICAgICB9KTtcclxuICAgICAgfVxyXG5cclxuICAgICAgLy8gTG9hZCBzY3JpcHRzIGZyb20gYWxsIGN1c3RvbSBmaWxlc1xyXG4gICAgICBpZiAocmVzb3VyY2VzLmZpbGVzKSB7XHJcbiAgICAgICAgZm9yIChjb25zdCBmaWxlIG9mIHJlc291cmNlcy5maWxlcykge1xyXG4gICAgICAgICAgY29uc3QgaXNMb2NhbCA9ICFmaWxlLnN0YXJ0c1dpdGgoJ2h0dHAnKSA/IHRydWUgOiBmYWxzZTtcclxuXHJcbiAgICAgICAgICAvLyBBZGQgZWFjaCBjdXN0b20gc2NyaXB0IGZyb20gcmVzb3VyY2VzJyBmaWxlc1xyXG4gICAgICAgICAgaW5qZWN0ZWRKcy5wdXNoKFxyXG4gICAgICAgICAgICBpc0xvY2FsXHJcbiAgICAgICAgICAgICAgPyB7XHJcbiAgICAgICAgICAgICAgICAgIGNvbnRlbnQ6IHJlYWRGaWxlU3luYyhmaWxlLCAndXRmOCcpXHJcbiAgICAgICAgICAgICAgICB9XHJcbiAgICAgICAgICAgICAgOiB7XHJcbiAgICAgICAgICAgICAgICAgIHVybDogZmlsZVxyXG4gICAgICAgICAgICAgICAgfVxyXG4gICAgICAgICAgKTtcclxuICAgICAgICB9XHJcbiAgICAgIH1cclxuXHJcbiAgICAgIGZvciAoY29uc3QganNSZXNvdXJjZSBvZiBpbmplY3RlZEpzKSB7XHJcbiAgICAgICAgdHJ5IHtcclxuICAgICAgICAgIGluamVjdGVkUmVzb3VyY2VzLnB1c2goYXdhaXQgcGFnZS5hZGRTY3JpcHRUYWcoanNSZXNvdXJjZSkpO1xyXG4gICAgICAgIH0gY2F0Y2ggKGVycm9yKSB7XHJcbiAgICAgICAgICBsb2dXaXRoU3RhY2soMiwgZXJyb3IsIGBbZXhwb3J0XSBUaGUgSlMgcmVzb3VyY2UgY2Fubm90IGJlIGxvYWRlZC5gKTtcclxuICAgICAgICB9XHJcbiAgICAgIH1cclxuICAgICAgaW5qZWN0ZWRKcy5sZW5ndGggPSAwO1xyXG5cclxuICAgICAgLy8gTG9hZCBDU1NcclxuICAgICAgY29uc3QgaW5qZWN0ZWRDc3MgPSBbXTtcclxuICAgICAgaWYgKHJlc291cmNlcy5jc3MpIHtcclxuICAgICAgICBsZXQgY3NzSW1wb3J0cyA9IHJlc291cmNlcy5jc3MubWF0Y2goL0BpbXBvcnRcXHMqKFteO10qKTsvZyk7XHJcbiAgICAgICAgaWYgKGNzc0ltcG9ydHMpIHtcclxuICAgICAgICAgIC8vIEhhbmRsZSBjc3Mgc2VjdGlvblxyXG4gICAgICAgICAgZm9yIChsZXQgY3NzSW1wb3J0UGF0aCBvZiBjc3NJbXBvcnRzKSB7XHJcbiAgICAgICAgICAgIGlmIChjc3NJbXBvcnRQYXRoKSB7XHJcbiAgICAgICAgICAgICAgY3NzSW1wb3J0UGF0aCA9IGNzc0ltcG9ydFBhdGhcclxuICAgICAgICAgICAgICAgIC5yZXBsYWNlKCd1cmwoJywgJycpXHJcbiAgICAgICAgICAgICAgICAucmVwbGFjZSgnQGltcG9ydCcsICcnKVxyXG4gICAgICAgICAgICAgICAgLnJlcGxhY2UoL1wiL2csICcnKVxyXG4gICAgICAgICAgICAgICAgLnJlcGxhY2UoLycvZywgJycpXHJcbiAgICAgICAgICAgICAgICAucmVwbGFjZSgvOy8sICcnKVxyXG4gICAgICAgICAgICAgICAgLnJlcGxhY2UoL1xcKS9nLCAnJylcclxuICAgICAgICAgICAgICAgIC50cmltKCk7XHJcblxyXG4gICAgICAgICAgICAgIC8vIEFkZCBlYWNoIGN1c3RvbSBjc3MgZnJvbSByZXNvdXJjZXNcclxuICAgICAgICAgICAgICBpZiAoY3NzSW1wb3J0UGF0aC5zdGFydHNXaXRoKCdodHRwJykpIHtcclxuICAgICAgICAgICAgICAgIGluamVjdGVkQ3NzLnB1c2goe1xyXG4gICAgICAgICAgICAgICAgICB1cmw6IGNzc0ltcG9ydFBhdGhcclxuICAgICAgICAgICAgICAgIH0pO1xyXG4gICAgICAgICAgICAgIH0gZWxzZSBpZiAob3B0aW9ucy5jdXN0b21Mb2dpYy5hbGxvd0ZpbGVSZXNvdXJjZXMpIHtcclxuICAgICAgICAgICAgICAgIGluamVjdGVkQ3NzLnB1c2goe1xyXG4gICAgICAgICAgICAgICAgICBwYXRoOiBwYXRoLmpvaW4oX19iYXNlZGlyLCBjc3NJbXBvcnRQYXRoKVxyXG4gICAgICAgICAgICAgICAgfSk7XHJcbiAgICAgICAgICAgICAgfVxyXG4gICAgICAgICAgICB9XHJcbiAgICAgICAgICB9XHJcbiAgICAgICAgfVxyXG5cclxuICAgICAgICAvLyBUaGUgcmVzdCBvZiB0aGUgQ1NTIHNlY3Rpb24gd2lsbCBiZSBjb250ZW50IGJ5IG5vd1xyXG4gICAgICAgIGluamVjdGVkQ3NzLnB1c2goe1xyXG4gICAgICAgICAgY29udGVudDogcmVzb3VyY2VzLmNzcy5yZXBsYWNlKC9AaW1wb3J0XFxzKihbXjtdKik7L2csICcnKSB8fCAnICdcclxuICAgICAgICB9KTtcclxuXHJcbiAgICAgICAgZm9yIChjb25zdCBjc3NSZXNvdXJjZSBvZiBpbmplY3RlZENzcykge1xyXG4gICAgICAgICAgdHJ5IHtcclxuICAgICAgICAgICAgaW5qZWN0ZWRSZXNvdXJjZXMucHVzaChhd2FpdCBwYWdlLmFkZFN0eWxlVGFnKGNzc1Jlc291cmNlKSk7XHJcbiAgICAgICAgICB9IGNhdGNoIChlcnJvcikge1xyXG4gICAgICAgICAgICBsb2dXaXRoU3RhY2soXHJcbiAgICAgICAgICAgICAgMixcclxuICAgICAgICAgICAgICBlcnJvcixcclxuICAgICAgICAgICAgICBgW2V4cG9ydF0gVGhlIENTUyByZXNvdXJjZSBjYW5ub3QgYmUgbG9hZGVkLmBcclxuICAgICAgICAgICAgKTtcclxuICAgICAgICAgIH1cclxuICAgICAgICB9XHJcbiAgICAgICAgaW5qZWN0ZWRDc3MubGVuZ3RoID0gMDtcclxuICAgICAgfVxyXG4gICAgfVxyXG5cclxuICAgIC8vIEdldCB0aGUgcmVhbCBjaGFydCBzaXplIGFuZCBzZXQgdGhlIHpvb20gYWNjb3JkaW5nbHlcclxuICAgIGNvbnN0IHNpemUgPSBpc1NWR1xyXG4gICAgICA/IGF3YWl0IHBhZ2UuZXZhbHVhdGUoKHNjYWxlKSA9PiB7XHJcbiAgICAgICAgICBjb25zdCBzdmdFbGVtZW50ID0gZG9jdW1lbnQucXVlcnlTZWxlY3RvcihcclxuICAgICAgICAgICAgJyNjaGFydC1jb250YWluZXIgc3ZnOmZpcnN0LW9mLXR5cGUnXHJcbiAgICAgICAgICApO1xyXG5cclxuICAgICAgICAgIC8vIEdldCB0aGUgdmFsdWVzIGNvcnJlY3RseSBzY2FsZWRcclxuICAgICAgICAgIGNvbnN0IGNoYXJ0SGVpZ2h0ID0gc3ZnRWxlbWVudC5oZWlnaHQuYmFzZVZhbC52YWx1ZSAqIHNjYWxlO1xyXG4gICAgICAgICAgY29uc3QgY2hhcnRXaWR0aCA9IHN2Z0VsZW1lbnQud2lkdGguYmFzZVZhbC52YWx1ZSAqIHNjYWxlO1xyXG5cclxuICAgICAgICAgIC8vIEluIGNhc2Ugb2YgU1ZHIHRoZSB6b29tIG11c3QgYmUgc2V0IGRpcmVjdGx5IGZvciBib2R5XHJcbiAgICAgICAgICAvLyBTZXQgdGhlIHpvb20gYXMgc2NhbGVcclxuICAgICAgICAgIC8vIGVzbGludC1kaXNhYmxlLW5leHQtbGluZSBuby11bmRlZlxyXG4gICAgICAgICAgZG9jdW1lbnQuYm9keS5zdHlsZS56b29tID0gc2NhbGU7XHJcblxyXG4gICAgICAgICAgLy8gU2V0IHRoZSBtYXJnaW4gdG8gMHB4XHJcbiAgICAgICAgICAvLyBlc2xpbnQtZGlzYWJsZS1uZXh0LWxpbmUgbm8tdW5kZWZcclxuICAgICAgICAgIGRvY3VtZW50LmJvZHkuc3R5bGUubWFyZ2luID0gJzBweCc7XHJcblxyXG4gICAgICAgICAgcmV0dXJuIHtcclxuICAgICAgICAgICAgY2hhcnRIZWlnaHQsXHJcbiAgICAgICAgICAgIGNoYXJ0V2lkdGhcclxuICAgICAgICAgIH07XHJcbiAgICAgICAgfSwgcGFyc2VGbG9hdChleHBvcnRPcHRpb25zLnNjYWxlKSlcclxuICAgICAgOiBhd2FpdCBwYWdlLmV2YWx1YXRlKCgpID0+IHtcclxuICAgICAgICAgIC8vIGVzbGludC1kaXNhYmxlLW5leHQtbGluZSBuby11bmRlZlxyXG4gICAgICAgICAgY29uc3QgeyBjaGFydEhlaWdodCwgY2hhcnRXaWR0aCB9ID0gd2luZG93LkhpZ2hjaGFydHMuY2hhcnRzWzBdO1xyXG5cclxuICAgICAgICAgIC8vIE5vIG5lZWQgZm9yIHN1Y2ggc2NhbGUgbWFuaXB1bGF0aW9uIGluIGNhc2Ugb2Ygb3RoZXIgdHlwZXMgb2YgZXhwb3J0c1xyXG4gICAgICAgICAgLy8gUmVzZXQgdGhlIHpvb20gZm9yIG90aGVyIGV4cG9ydHMgdGhhbiB0byBTVkdzXHJcbiAgICAgICAgICAvLyBlc2xpbnQtZGlzYWJsZS1uZXh0LWxpbmUgbm8tdW5kZWZcclxuICAgICAgICAgIGRvY3VtZW50LmJvZHkuc3R5bGUuem9vbSA9IDE7XHJcblxyXG4gICAgICAgICAgcmV0dXJuIHtcclxuICAgICAgICAgICAgY2hhcnRIZWlnaHQsXHJcbiAgICAgICAgICAgIGNoYXJ0V2lkdGhcclxuICAgICAgICAgIH07XHJcbiAgICAgICAgfSk7XHJcblxyXG4gICAgLy8gU2V0IGZpbmFsIGhlaWdodCBhbmQgd2lkdGggZm9yIHZpZXdwb3J0XHJcbiAgICBjb25zdCB2aWV3cG9ydEhlaWdodCA9IE1hdGguY2VpbChzaXplLmNoYXJ0SGVpZ2h0IHx8IGV4cG9ydE9wdGlvbnMuaGVpZ2h0KTtcclxuICAgIGNvbnN0IHZpZXdwb3J0V2lkdGggPSBNYXRoLmNlaWwoc2l6ZS5jaGFydFdpZHRoIHx8IGV4cG9ydE9wdGlvbnMud2lkdGgpO1xyXG5cclxuICAgIC8vIEdldCB0aGUgY2xpcCByZWdpb24gZm9yIHRoZSBwYWdlXHJcbiAgICBjb25zdCB7IHgsIHkgfSA9IGF3YWl0IGdldENsaXBSZWdpb24ocGFnZSk7XHJcblxyXG4gICAgLy8gU2V0IHRoZSBmaW5hbCB2aWV3cG9ydCBub3cgdGhhdCB3ZSBoYXZlIHRoZSByZWFsIGhlaWdodFxyXG4gICAgYXdhaXQgcGFnZS5zZXRWaWV3cG9ydCh7XHJcbiAgICAgIGhlaWdodDogdmlld3BvcnRIZWlnaHQsXHJcbiAgICAgIHdpZHRoOiB2aWV3cG9ydFdpZHRoLFxyXG4gICAgICBkZXZpY2VTY2FsZUZhY3RvcjogaXNTVkcgPyAxIDogcGFyc2VGbG9hdChleHBvcnRPcHRpb25zLnNjYWxlKVxyXG4gICAgfSk7XHJcblxyXG4gICAgbGV0IGRhdGE7XHJcbiAgICAvLyBSQVNURVJJWkFUSU9OXHJcbiAgICBpZiAoZXhwb3J0T3B0aW9ucy50eXBlID09PSAnc3ZnJykge1xyXG4gICAgICAvLyBTVkdcclxuICAgICAgZGF0YSA9IGF3YWl0IGNyZWF0ZVNWRyhwYWdlKTtcclxuICAgIH0gZWxzZSBpZiAoWydwbmcnLCAnanBlZyddLmluY2x1ZGVzKGV4cG9ydE9wdGlvbnMudHlwZSkpIHtcclxuICAgICAgLy8gUE5HIG9yIEpQRUdcclxuICAgICAgZGF0YSA9IGF3YWl0IGNyZWF0ZUltYWdlKFxyXG4gICAgICAgIHBhZ2UsXHJcbiAgICAgICAgZXhwb3J0T3B0aW9ucy50eXBlLFxyXG4gICAgICAgICdiYXNlNjQnLFxyXG4gICAgICAgIHtcclxuICAgICAgICAgIHdpZHRoOiB2aWV3cG9ydFdpZHRoLFxyXG4gICAgICAgICAgaGVpZ2h0OiB2aWV3cG9ydEhlaWdodCxcclxuICAgICAgICAgIHgsXHJcbiAgICAgICAgICB5XHJcbiAgICAgICAgfSxcclxuICAgICAgICBleHBvcnRPcHRpb25zLnJhc3Rlcml6YXRpb25UaW1lb3V0XHJcbiAgICAgICk7XHJcbiAgICB9IGVsc2UgaWYgKGV4cG9ydE9wdGlvbnMudHlwZSA9PT0gJ3BkZicpIHtcclxuICAgICAgLy8gUERGXHJcbiAgICAgIGRhdGEgPSBhd2FpdCBjcmVhdGVQREYoXHJcbiAgICAgICAgcGFnZSxcclxuICAgICAgICB2aWV3cG9ydEhlaWdodCxcclxuICAgICAgICB2aWV3cG9ydFdpZHRoLFxyXG4gICAgICAgICdiYXNlNjQnLFxyXG4gICAgICAgIGV4cG9ydE9wdGlvbnMucmFzdGVyaXphdGlvblRpbWVvdXRcclxuICAgICAgKTtcclxuICAgIH0gZWxzZSB7XHJcbiAgICAgIHRocm93IG5ldyBFeHBvcnRFcnJvcihcclxuICAgICAgICBgW2V4cG9ydF0gVW5zdXBwb3J0ZWQgb3V0cHV0IGZvcm1hdCAke2V4cG9ydE9wdGlvbnMudHlwZX0uYFxyXG4gICAgICApO1xyXG4gICAgfVxyXG5cclxuICAgIGF3YWl0IGNsZWFySW5qZWN0ZWQocGFnZSk7XHJcbiAgICByZXR1cm4gZGF0YTtcclxuICB9IGNhdGNoIChlcnJvcikge1xyXG4gICAgYXdhaXQgY2xlYXJJbmplY3RlZChwYWdlKTtcclxuICAgIHJldHVybiBlcnJvcjtcclxuICB9XHJcbn07XHJcbiIsIi8qKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqXHJcblxyXG5IaWdoY2hhcnRzIEV4cG9ydCBTZXJ2ZXJcclxuXHJcbkNvcHlyaWdodCAoYykgMjAxNi0yMDI0LCBIaWdoc29mdFxyXG5cclxuTGljZW5jZWQgdW5kZXIgdGhlIE1JVCBsaWNlbmNlLlxyXG5cclxuQWRkaXRpb25hbGx5IGEgdmFsaWQgSGlnaGNoYXJ0cyBsaWNlbnNlIGlzIHJlcXVpcmVkIGZvciB1c2UuXHJcblxyXG5TZWUgTElDRU5TRSBmaWxlIGluIHJvb3QgZm9yIGRldGFpbHMuXHJcblxyXG4qKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqL1xyXG5cclxuaW1wb3J0IGNzc1RlbXBsYXRlIGZyb20gJy4vY3NzLmpzJztcclxuXHJcbmV4cG9ydCBkZWZhdWx0IChjaGFydCkgPT4gYFxyXG48IURPQ1RZUEUgaHRtbD5cclxuPGh0bWwgbGFuZz0nZW4tVVMnPlxyXG4gIDxoZWFkPlxyXG4gICAgPG1ldGEgaHR0cC1lcXVpdj1cIkNvbnRlbnQtVHlwZVwiIGNvbnRlbnQ9XCJ0ZXh0L2h0bWw7IGNoYXJzZXQ9dXRmLThcIj5cclxuICAgIDx0aXRsZT5IaWdoY2hhcnRzIEV4cG9ydDwvdGl0bGU+XHJcbiAgPC9oZWFkPlxyXG4gIDxzdHlsZT5cclxuICAgICR7Y3NzVGVtcGxhdGUoKX1cclxuICA8L3N0eWxlPlxyXG4gIDxib2R5PlxyXG4gICAgPGRpdiBpZD1cImNoYXJ0LWNvbnRhaW5lclwiPlxyXG4gICAgICAke2NoYXJ0fVxyXG4gICAgPC9kaXY+XHJcbiAgPC9ib2R5PlxyXG48L2h0bWw+XHJcblxyXG5gO1xyXG4iLCIvKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKlxyXG5cclxuSGlnaGNoYXJ0cyBFeHBvcnQgU2VydmVyXHJcblxyXG5Db3B5cmlnaHQgKGMpIDIwMTYtMjAyNCwgSGlnaHNvZnRcclxuXHJcbkxpY2VuY2VkIHVuZGVyIHRoZSBNSVQgbGljZW5jZS5cclxuXHJcbkFkZGl0aW9uYWxseSBhIHZhbGlkIEhpZ2hjaGFydHMgbGljZW5zZSBpcyByZXF1aXJlZCBmb3IgdXNlLlxyXG5cclxuU2VlIExJQ0VOU0UgZmlsZSBpbiByb290IGZvciBkZXRhaWxzLlxyXG5cclxuKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKi9cclxuXHJcbmltcG9ydCB7IFBvb2wgfSBmcm9tICd0YXJuJztcclxuaW1wb3J0IHsgdjQgYXMgdXVpZCB9IGZyb20gJ3V1aWQnO1xyXG5cclxuaW1wb3J0IHtcclxuICBjbG9zZSBhcyBicm93c2VyQ2xvc2UsXHJcbiAgY3JlYXRlIGFzIGNyZWF0ZUJyb3dzZXIsXHJcbiAgbmV3UGFnZSBhcyBicm93c2VyTmV3UGFnZSxcclxuICBjbGVhclBhZ2VcclxufSBmcm9tICcuL2Jyb3dzZXIuanMnO1xyXG5pbXBvcnQgeyBnZXRPcHRpb25zIH0gZnJvbSAnLi9jb25maWcuanMnO1xyXG5pbXBvcnQgcHVwcGV0ZWVyRXhwb3J0IGZyb20gJy4vZXhwb3J0LmpzJztcclxuaW1wb3J0IHsgbG9nLCBsb2dXaXRoU3RhY2sgfSBmcm9tICcuL2xvZ2dlci5qcyc7XHJcbmltcG9ydCB7IG1lYXN1cmVUaW1lIH0gZnJvbSAnLi91dGlscy5qcyc7XHJcblxyXG5pbXBvcnQgRXhwb3J0RXJyb3IgZnJvbSAnLi9lcnJvcnMvRXhwb3J0RXJyb3IuanMnO1xyXG5cclxuLy8gVGhlIHBvb2wgaW5zdGFuY2VcclxubGV0IHBvb2wgPSBmYWxzZTtcclxuXHJcbi8vIFBvb2wgc3RhdGlzdGljc1xyXG5leHBvcnQgY29uc3Qgc3RhdHMgPSB7XHJcbiAgcGVyZm9ybWVkRXhwb3J0czogMCxcclxuICBleHBvcnRBdHRlbXB0czogMCxcclxuICBleHBvcnRGcm9tU3ZnQXR0ZW1wdHM6IDAsXHJcbiAgdGltZVNwZW50OiAwLFxyXG4gIGRyb3BwZWRFeHBvcnRzOiAwLFxyXG4gIHNwZW50QXZlcmFnZTogMFxyXG59O1xyXG5cclxubGV0IHBvb2xDb25maWcgPSB7fTtcclxuXHJcbmNvbnN0IGZhY3RvcnkgPSB7XHJcbiAgLyoqXHJcbiAgICogQ3JlYXRlcyBhIG5ldyB3b3JrZXIgcGFnZSBmb3IgdGhlIGV4cG9ydCBwb29sLlxyXG4gICAqXHJcbiAgICogQHJldHVybnMge09iamVjdH0gLSBBbiBvYmplY3QgY29udGFpbmluZyB0aGUgd29ya2VyIElELCBhIHJlZmVyZW5jZSB0byB0aGVcclxuICAgKiBicm93c2VyIHBhZ2UsIGFuZCBpbml0aWFsIHdvcmsgY291bnQuXHJcbiAgICpcclxuICAgKiBAdGhyb3dzIHtFeHBvcnRFcnJvcn0gLSBJZiB0aGVyZSdzIGFuIGVycm9yIGR1cmluZyB0aGUgY3JlYXRpb24gb2YgdGhlIG5ld1xyXG4gICAqIHBhZ2UuXHJcbiAgICovXHJcbiAgY3JlYXRlOiBhc3luYyAoKSA9PiB7XHJcbiAgICBsZXQgcGFnZSA9IGZhbHNlO1xyXG5cclxuICAgIGNvbnN0IGlkID0gdXVpZCgpO1xyXG4gICAgY29uc3Qgc3RhcnREYXRlID0gbmV3IERhdGUoKS5nZXRUaW1lKCk7XHJcblxyXG4gICAgdHJ5IHtcclxuICAgICAgcGFnZSA9IGF3YWl0IGJyb3dzZXJOZXdQYWdlKCk7XHJcblxyXG4gICAgICBpZiAoIXBhZ2UgfHwgcGFnZS5pc0Nsb3NlZCgpKSB7XHJcbiAgICAgICAgdGhyb3cgbmV3IEV4cG9ydEVycm9yKCdUaGUgcGFnZSBpcyBpbnZhbGlkIG9yIGNsb3NlZC4nKTtcclxuICAgICAgfVxyXG5cclxuICAgICAgbG9nKFxyXG4gICAgICAgIDMsXHJcbiAgICAgICAgYFtwb29sXSBTdWNjZXNzZnVsbHkgY3JlYXRlZCBhIHdvcmtlciAke2lkfSAtIHRvb2sgJHtcclxuICAgICAgICAgIG5ldyBEYXRlKCkuZ2V0VGltZSgpIC0gc3RhcnREYXRlXHJcbiAgICAgICAgfSBtcy5gXHJcbiAgICAgICk7XHJcbiAgICB9IGNhdGNoIChlcnJvcikge1xyXG4gICAgICB0aHJvdyBuZXcgRXhwb3J0RXJyb3IoXHJcbiAgICAgICAgJ0Vycm9yIGVuY291bnRlcmVkIHdoZW4gY3JlYXRpbmcgYSBuZXcgcGFnZS4nXHJcbiAgICAgICkuc2V0RXJyb3IoZXJyb3IpO1xyXG4gICAgfVxyXG5cclxuICAgIGNvbnN0IHsgZGVidWcgfSA9IGdldE9wdGlvbnMoKTtcclxuICAgIC8vIFNldCB0aGUgY29uc29sZSBsaXN0ZW5lciwgaWYgbmVlZGVkXHJcbiAgICBpZiAoZGVidWcuZW5hYmxlICYmIGRlYnVnLmxpc3RlblRvQ29uc29sZSkge1xyXG4gICAgICBwYWdlLm9uKCdjb25zb2xlJywgKG1lc3NhZ2UpID0+IHtcclxuICAgICAgICBjb25zb2xlLmxvZyhgW2RlYnVnXSAke21lc3NhZ2UudGV4dCgpfWApO1xyXG4gICAgICB9KTtcclxuICAgIH1cclxuXHJcbiAgICAvLyBTZXQgdGhlIHBhZ2VlcnJvciBsaXN0ZW5lclxyXG4gICAgcGFnZS5vbigncGFnZWVycm9yJywgYXN5bmMgKGVycm9yKSA9PiB7XHJcbiAgICAgIC8vIFRPRE86IENvbnNpZGVyIGFkZGluZyBhIHN3aXRjaCBoZXJlIHRoYXQgdHVybnMgb24gbG9nKDApIGxvZ2dpbmdcclxuICAgICAgLy8gb24gcGFnZSBlcnJvcnMuXHJcbiAgICAgIGF3YWl0IHBhZ2UuJGV2YWwoXHJcbiAgICAgICAgJyNjb250YWluZXInLFxyXG4gICAgICAgIChlbGVtZW50LCBlcnJvck1lc3NhZ2UpID0+IHtcclxuICAgICAgICAgIC8vIGVzbGludC1kaXNhYmxlLW5leHQtbGluZSBuby11bmRlZlxyXG4gICAgICAgICAgaWYgKHdpbmRvdy5fZGlzcGxheUVycm9ycykge1xyXG4gICAgICAgICAgICBlbGVtZW50LmlubmVySFRNTCA9IGVycm9yTWVzc2FnZTtcclxuICAgICAgICAgIH1cclxuICAgICAgICB9LFxyXG4gICAgICAgIGA8aDE+Q2hhcnQgaW5wdXQgZGF0YSBlcnJvcjogPC9oMT4ke2Vycm9yLnRvU3RyaW5nKCl9YFxyXG4gICAgICApO1xyXG4gICAgfSk7XHJcblxyXG4gICAgcmV0dXJuIHtcclxuICAgICAgaWQsXHJcbiAgICAgIHBhZ2UsXHJcbiAgICAgIC8vIFRyeSB0byBkaXN0cmlidXRlIHRoZSBpbml0aWFsIHdvcmsgY291bnRcclxuICAgICAgd29ya0NvdW50OiBNYXRoLnJvdW5kKE1hdGgucmFuZG9tKCkgKiAocG9vbENvbmZpZy53b3JrTGltaXQgLyAyKSlcclxuICAgIH07XHJcbiAgfSxcclxuXHJcbiAgLyoqXHJcbiAgICogVmFsaWRhdGVzIGEgd29ya2VyIHBhZ2UgaW4gdGhlIGV4cG9ydCBwb29sLCBjaGVja2luZyBpZiBpdCBoYXMgZXhjZWVkZWRcclxuICAgKiB0aGUgd29yayBsaW1pdC5cclxuICAgKlxyXG4gICAqIEBwYXJhbSB7T2JqZWN0fSB3b3JrZXJIYW5kbGUgLSBUaGUgaGFuZGxlIHRvIHRoZSB3b3JrZXIsIGNvbnRhaW5pbmcgdGhlXHJcbiAgICogd29ya2VyJ3MgSUQsIGEgcmVmZXJlbmNlIHRvIHRoZSBicm93c2VyIHBhZ2UsIGFuZCB3b3JrIGNvdW50LlxyXG4gICAqXHJcbiAgICogQHJldHVybnMge2Jvb2xlYW59IC0gUmV0dXJucyB0cnVlIGlmIHRoZSB3b3JrZXIgaXMgdmFsaWQgYW5kIHdpdGhpblxyXG4gICAqIHRoZSB3b3JrIGxpbWl0OyBvdGhlcndpc2UsIHJldHVybnMgZmFsc2UuXHJcbiAgICovXHJcbiAgdmFsaWRhdGU6IGFzeW5jICh3b3JrZXJIYW5kbGUpID0+IHtcclxuICAgIGlmIChcclxuICAgICAgcG9vbENvbmZpZy53b3JrTGltaXQgJiZcclxuICAgICAgKyt3b3JrZXJIYW5kbGUud29ya0NvdW50ID4gcG9vbENvbmZpZy53b3JrTGltaXRcclxuICAgICkge1xyXG4gICAgICBsb2coXHJcbiAgICAgICAgMyxcclxuICAgICAgICBgW3Bvb2xdIFdvcmtlciBmYWlsZWQgdmFsaWRhdGlvbjogZXhjZWVkZWQgd29yayBsaW1pdCAobGltaXQgaXMgJHtwb29sQ29uZmlnLndvcmtMaW1pdH0pLmBcclxuICAgICAgKTtcclxuICAgICAgcmV0dXJuIGZhbHNlO1xyXG4gICAgfVxyXG4gICAgcmV0dXJuIHRydWU7XHJcbiAgfSxcclxuXHJcbiAgLyoqXHJcbiAgICogRGVzdHJveXMgYSB3b3JrZXIgZW50cnkgaW4gdGhlIGV4cG9ydCBwb29sLCBjbG9zaW5nIGl0cyBhc3NvY2lhdGVkIHBhZ2UuXHJcbiAgICpcclxuICAgKiBAcGFyYW0ge09iamVjdH0gd29ya2VySGFuZGxlIC0gVGhlIGhhbmRsZSB0byB0aGUgd29ya2VyLCBjb250YWluaW5nXHJcbiAgICogdGhlIHdvcmtlcidzIElEIGFuZCBhIHJlZmVyZW5jZSB0byB0aGUgYnJvd3NlciBwYWdlLlxyXG4gICAqL1xyXG4gIGRlc3Ryb3k6IGFzeW5jICh3b3JrZXJIYW5kbGUpID0+IHtcclxuICAgIGxvZygzLCBgW3Bvb2xdIERlc3Ryb3lpbmcgcG9vbCBlbnRyeSAke3dvcmtlckhhbmRsZS5pZH0uYCk7XHJcblxyXG4gICAgaWYgKHdvcmtlckhhbmRsZS5wYWdlKSB7XHJcbiAgICAgIC8vIFdlIGRvbid0IHJlYWxseSBuZWVkIHRvIHdhaXQgYXJvdW5kIGZvciB0aGlzXHJcbiAgICAgIGF3YWl0IHdvcmtlckhhbmRsZS5wYWdlLmNsb3NlKCk7XHJcbiAgICB9XHJcbiAgfVxyXG59O1xyXG5cclxuLyoqXHJcbiAqIEluaXRpYWxpemVzIHRoZSBleHBvcnQgcG9vbCB3aXRoIHRoZSBwcm92aWRlZCBjb25maWd1cmF0aW9uLCBjcmVhdGluZ1xyXG4gKiBhIGJyb3dzZXIgaW5zdGFuY2UgYW5kIHNldHRpbmcgdXAgd29ya2VyIHJlc291cmNlcy5cclxuICpcclxuICogQHBhcmFtIHtPYmplY3R9IGNvbmZpZyAtIENvbmZpZ3VyYXRpb24gb3B0aW9ucyBmb3IgdGhlIGV4cG9ydCBwb29sIGFsb25nXHJcbiAqIHdpdGggY3VzdG9tIHB1cHBldGVlciBhcmd1bWVudHMgZm9yIHRoZSBwdXBwZXRlZXIubGF1bmNoIGZ1bmN0aW9uLlxyXG4gKi9cclxuZXhwb3J0IGNvbnN0IGluaXRQb29sID0gYXN5bmMgKGNvbmZpZykgPT4ge1xyXG4gIC8vIEZvciB0aGUgbW9kdWxlIHNjb3BlIHVzYWdlXHJcbiAgcG9vbENvbmZpZyA9IGNvbmZpZyAmJiBjb25maWcucG9vbCA/IHsgLi4uY29uZmlnLnBvb2wgfSA6IHt9O1xyXG5cclxuICAvLyBDcmVhdGUgYSBicm93c2VyIGluc3RhbmNlIHdpdGggdGhlIHB1cHBldGVlciBhcmd1bWVudHNcclxuICBhd2FpdCBjcmVhdGVCcm93c2VyKGNvbmZpZy5wdXBwZXRlZXJBcmdzKTtcclxuXHJcbiAgbG9nKFxyXG4gICAgMyxcclxuICAgIGBbcG9vbF0gSW5pdGlhbGl6aW5nIHBvb2wgd2l0aCB3b3JrZXJzOiBtaW4gJHtwb29sQ29uZmlnLm1pbldvcmtlcnN9LCBtYXggJHtwb29sQ29uZmlnLm1heFdvcmtlcnN9LmBcclxuICApO1xyXG5cclxuICBpZiAocG9vbCkge1xyXG4gICAgcmV0dXJuIGxvZyhcclxuICAgICAgNCxcclxuICAgICAgJ1twb29sXSBBbHJlYWR5IGluaXRpYWxpemVkLCBwbGVhc2Uga2lsbCBpdCBiZWZvcmUgY3JlYXRpbmcgYSBuZXcgb25lLidcclxuICAgICk7XHJcbiAgfVxyXG5cclxuICBpZiAocGFyc2VJbnQocG9vbENvbmZpZy5taW5Xb3JrZXJzKSA+IHBhcnNlSW50KHBvb2xDb25maWcubWF4V29ya2VycykpIHtcclxuICAgIHBvb2xDb25maWcubWluV29ya2VycyA9IHBvb2xDb25maWcubWF4V29ya2VycztcclxuICB9XHJcblxyXG4gIHRyeSB7XHJcbiAgICAvLyBDcmVhdGUgYSBwb29sIGFsb25nIHdpdGggYSBtaW5pbWFsIG51bWJlciBvZiByZXNvdXJjZXNcclxuICAgIHBvb2wgPSBuZXcgUG9vbCh7XHJcbiAgICAgIC8vIEdldCB0aGUgY3JlYXRlL3ZhbGlkYXRlL2Rlc3Ryb3kvbG9nIGZ1bmN0aW9uc1xyXG4gICAgICAuLi5mYWN0b3J5LFxyXG4gICAgICBtaW46IHBhcnNlSW50KHBvb2xDb25maWcubWluV29ya2VycyksXHJcbiAgICAgIG1heDogcGFyc2VJbnQocG9vbENvbmZpZy5tYXhXb3JrZXJzKSxcclxuICAgICAgYWNxdWlyZVRpbWVvdXRNaWxsaXM6IHBvb2xDb25maWcuYWNxdWlyZVRpbWVvdXQsXHJcbiAgICAgIGNyZWF0ZVRpbWVvdXRNaWxsaXM6IHBvb2xDb25maWcuY3JlYXRlVGltZW91dCxcclxuICAgICAgZGVzdHJveVRpbWVvdXRNaWxsaXM6IHBvb2xDb25maWcuZGVzdHJveVRpbWVvdXQsXHJcbiAgICAgIGlkbGVUaW1lb3V0TWlsbGlzOiBwb29sQ29uZmlnLmlkbGVUaW1lb3V0LFxyXG4gICAgICBjcmVhdGVSZXRyeUludGVydmFsTWlsbGlzOiBwb29sQ29uZmlnLmNyZWF0ZVJldHJ5SW50ZXJ2YWwsXHJcbiAgICAgIHJlYXBJbnRlcnZhbE1pbGxpczogcG9vbENvbmZpZy5yZWFwZXJJbnRlcnZhbCxcclxuICAgICAgcHJvcGFnYXRlQ3JlYXRlRXJyb3I6IGZhbHNlXHJcbiAgICB9KTtcclxuXHJcbiAgICAvLyBTZXQgZXZlbnRzXHJcbiAgICBwb29sLm9uKCdyZWxlYXNlJywgYXN5bmMgKHJlc291cmNlKSA9PiB7XHJcbiAgICAgIC8vIENsZWFyIHBhZ2VcclxuICAgICAgYXdhaXQgY2xlYXJQYWdlKHJlc291cmNlLnBhZ2UsIGZhbHNlKTtcclxuICAgICAgbG9nKDQsIGBbcG9vbF0gUmVsZWFzaW5nIGEgd29ya2VyIHdpdGggSUQgJHtyZXNvdXJjZS5pZH0uYCk7XHJcbiAgICB9KTtcclxuXHJcbiAgICBwb29sLm9uKCdkZXN0cm95U3VjY2VzcycsIChldmVudElkLCByZXNvdXJjZSkgPT4ge1xyXG4gICAgICBsb2coNCwgYFtwb29sXSBEZXN0cm95ZWQgYSB3b3JrZXIgd2l0aCBJRCAke3Jlc291cmNlLmlkfS5gKTtcclxuICAgIH0pO1xyXG5cclxuICAgIGNvbnN0IGluaXRpYWxSZXNvdXJjZXMgPSBbXTtcclxuICAgIC8vIENyZWF0ZSBhbiBpbml0aWFsIG51bWJlciBvZiByZXNvdXJjZXNcclxuICAgIGZvciAobGV0IGkgPSAwOyBpIDwgcG9vbENvbmZpZy5taW5Xb3JrZXJzOyBpKyspIHtcclxuICAgICAgdHJ5IHtcclxuICAgICAgICBjb25zdCByZXNvdXJjZSA9IGF3YWl0IHBvb2wuYWNxdWlyZSgpLnByb21pc2U7XHJcbiAgICAgICAgaW5pdGlhbFJlc291cmNlcy5wdXNoKHJlc291cmNlKTtcclxuICAgICAgfSBjYXRjaCAoZXJyb3IpIHtcclxuICAgICAgICBsb2dXaXRoU3RhY2soMiwgZXJyb3IsICdbcG9vbF0gQ291bGQgbm90IGNyZWF0ZSBhbiBpbml0aWFsIHJlc291cmNlLicpO1xyXG4gICAgICB9XHJcbiAgICB9XHJcblxyXG4gICAgLy8gUmVsZWFzZSB0aGUgaW5pdGlhbCBudW1iZXIgb2YgcmVzb3VyY2VzIGJhY2sgdG8gdGhlIHBvb2xcclxuICAgIGluaXRpYWxSZXNvdXJjZXMuZm9yRWFjaCgocmVzb3VyY2UpID0+IHtcclxuICAgICAgcG9vbC5yZWxlYXNlKHJlc291cmNlKTtcclxuICAgIH0pO1xyXG5cclxuICAgIGxvZyhcclxuICAgICAgMyxcclxuICAgICAgYFtwb29sXSBUaGUgcG9vbCBpcyByZWFkeSR7aW5pdGlhbFJlc291cmNlcy5sZW5ndGggPyBgIHdpdGggJHtpbml0aWFsUmVzb3VyY2VzLmxlbmd0aH0gaW5pdGlhbCByZXNvdXJjZXMgd2FpdGluZy5gIDogJy4nfWBcclxuICAgICk7XHJcbiAgfSBjYXRjaCAoZXJyb3IpIHtcclxuICAgIHRocm93IG5ldyBFeHBvcnRFcnJvcihcclxuICAgICAgJ1twb29sXSBDb3VsZCBub3QgY3JlYXRlIHRoZSBwb29sIG9mIHdvcmtlcnMuJ1xyXG4gICAgKS5zZXRFcnJvcihlcnJvcik7XHJcbiAgfVxyXG59O1xyXG5cclxuLyoqXHJcbiAqIEtpbGxzIGFsbCB3b3JrZXJzIGluIHRoZSBwb29sLCBkZXN0cm95cyB0aGUgcG9vbCwgYW5kIGNsb3NlcyB0aGUgYnJvd3NlclxyXG4gKiBpbnN0YW5jZS5cclxuICpcclxuICogQHJldHVybnMge1Byb21pc2U8dm9pZD59IEEgcHJvbWlzZSB0aGF0IHJlc29sdmVzIGFmdGVyIHRoZSB3b3JrZXJzIGFyZVxyXG4gKiBraWxsZWQsIHRoZSBwb29sIGlzIGRlc3Ryb3llZCwgYW5kIHRoZSBicm93c2VyIGlzIGNsb3NlZC5cclxuICovXHJcbmV4cG9ydCBhc3luYyBmdW5jdGlvbiBraWxsUG9vbCgpIHtcclxuICBsb2coMywgJ1twb29sXSBLaWxsaW5nIHBvb2wgd2l0aCBhbGwgd29ya2VycyBhbmQgY2xvc2luZyBicm93c2VyLicpO1xyXG5cclxuICAvLyBJZiBzdGlsbCBhbGl2ZSwgZGVzdHJveSB0aGUgcG9vbCBvZiBwYWdlcyBiZWZvcmUgY2xvc2luZyBhIGJyb3dzZXJcclxuICBpZiAocG9vbCkge1xyXG4gICAgLy8gRnJlZSB1cCBub3QgcmVsZWFzZWQgd29ya2Vyc1xyXG4gICAgZm9yIChjb25zdCB3b3JrZXIgb2YgcG9vbC51c2VkKSB7XHJcbiAgICAgIHBvb2wucmVsZWFzZSh3b3JrZXIucmVzb3VyY2UpO1xyXG4gICAgfVxyXG5cclxuICAgIC8vIERlc3Ryb3kgdGhlIHBvb2wgaWYgaXQgaXMgc3RpbGwgYXZhaWxhYmxlXHJcbiAgICBpZiAoIXBvb2wuZGVzdHJveWVkKSB7XHJcbiAgICAgIGF3YWl0IHBvb2wuZGVzdHJveSgpO1xyXG4gICAgICBsb2coNCwgJ1ticm93c2VyXSBEZXN0cm95ZWQgdGhlIHBvb2wgb2YgcmVzb3VyY2VzLicpO1xyXG4gICAgfVxyXG4gIH1cclxuXHJcbiAgLy8gQ2xvc2UgdGhlIGJyb3dzZXIgaW5zdGFuY2VcclxuICBhd2FpdCBicm93c2VyQ2xvc2UoKTtcclxufVxyXG5cclxuLyoqXHJcbiAqIFByb2Nlc3NlcyB0aGUgZXhwb3J0IHdvcmsgdXNpbmcgYSB3b3JrZXIgZnJvbSB0aGUgcG9vbC4gQWNxdWlyZXMgYSB3b3JrZXJcclxuICogaGFuZGxlIGZyb20gdGhlIHBvb2wsIHBlcmZvcm1zIHRoZSBleHBvcnQgdXNpbmcgcHVwcGV0ZWVyLCBhbmQgcmVsZWFzZXNcclxuICogdGhlIHdvcmtlciBoYW5kbGUgYmFjayB0byB0aGUgcG9vbC5cclxuICpcclxuICogQHBhcmFtIHtzdHJpbmd9IGNoYXJ0IC0gVGhlIGNoYXJ0IGRhdGEgb3IgY29uZmlndXJhdGlvbiB0byBiZSBleHBvcnRlZC5cclxuICogQHBhcmFtIHtPYmplY3R9IG9wdGlvbnMgLSBFeHBvcnQgb3B0aW9ucyBhbmQgY29uZmlndXJhdGlvbi5cclxuICpcclxuICogQHJldHVybnMge1Byb21pc2U8T2JqZWN0Pn0gQSBwcm9taXNlIHRoYXQgcmVzb2x2ZXMgd2l0aCB0aGUgZXhwb3J0IHJlc3VsdGFuZFxyXG4gKiBvcHRpb25zLlxyXG4gKlxyXG4gKiBAdGhyb3dzIHtFeHBvcnRFcnJvcn0gSWYgYW4gZXJyb3Igb2NjdXJzIGR1cmluZyB0aGUgZXhwb3J0IHByb2Nlc3MuXHJcbiAqL1xyXG5leHBvcnQgY29uc3QgcG9zdFdvcmsgPSBhc3luYyAoY2hhcnQsIG9wdGlvbnMpID0+IHtcclxuICBsZXQgd29ya2VySGFuZGxlO1xyXG5cclxuICB0cnkge1xyXG4gICAgbG9nKDQsICdbcG9vbF0gV29yayByZWNlaXZlZCwgc3RhcnRpbmcgdG8gcHJvY2Vzcy4nKTtcclxuXHJcbiAgICArK3N0YXRzLmV4cG9ydEF0dGVtcHRzO1xyXG4gICAgaWYgKHBvb2xDb25maWcuYmVuY2htYXJraW5nKSB7XHJcbiAgICAgIGdldFBvb2xJbmZvKCk7XHJcbiAgICB9XHJcblxyXG4gICAgaWYgKCFwb29sKSB7XHJcbiAgICAgIHRocm93IG5ldyBFeHBvcnRFcnJvcignV29yayByZWNlaXZlZCwgYnV0IHBvb2wgaGFzIG5vdCBiZWVuIHN0YXJ0ZWQuJyk7XHJcbiAgICB9XHJcblxyXG4gICAgLy8gQWNxdWlyZSB0aGUgd29ya2VyIGFsb25nIHdpdGggdGhlIGlkIG9mIHJlc291cmNlIGFuZCB3b3JrIGNvdW50XHJcbiAgICBjb25zdCBhY3F1aXJlQ291bnRlciA9IG1lYXN1cmVUaW1lKCk7XHJcbiAgICB0cnkge1xyXG4gICAgICBsb2coNCwgJ1twb29sXSBBY3F1aXJpbmcgYSB3b3JrZXIgaGFuZGxlLicpO1xyXG4gICAgICB3b3JrZXJIYW5kbGUgPSBhd2FpdCBwb29sLmFjcXVpcmUoKS5wcm9taXNlO1xyXG5cclxuICAgICAgLy8gQ2hlY2sgdGhlIHBhZ2UgYWNxdWlyZSB0aW1lXHJcbiAgICAgIGlmIChvcHRpb25zLnNlcnZlci5iZW5jaG1hcmtpbmcpIHtcclxuICAgICAgICBsb2coXHJcbiAgICAgICAgICA1LFxyXG4gICAgICAgICAgb3B0aW9ucy5wYXlsb2FkPy5yZXF1ZXN0SWRcclxuICAgICAgICAgICAgPyBgW2JlbmNobWFya10gUmVxdWVzdCB3aXRoIElEICR7b3B0aW9ucy5wYXlsb2FkPy5yZXF1ZXN0SWR9IC1gXHJcbiAgICAgICAgICAgIDogJ1tiZW5jaG1hcmtdJyxcclxuICAgICAgICAgIGBBY3F1aXJlZCBhIHdvcmtlciBoYW5kbGU6ICR7YWNxdWlyZUNvdW50ZXIoKX1tcy5gXHJcbiAgICAgICAgKTtcclxuICAgICAgfVxyXG4gICAgfSBjYXRjaCAoZXJyb3IpIHtcclxuICAgICAgdGhyb3cgbmV3IEV4cG9ydEVycm9yKFxyXG4gICAgICAgIChvcHRpb25zLnBheWxvYWQ/LnJlcXVlc3RJZFxyXG4gICAgICAgICAgPyBgRm9yIHJlcXVlc3Qgd2l0aCBJRCAke29wdGlvbnMucGF5bG9hZD8ucmVxdWVzdElkfSAtIGBcclxuICAgICAgICAgIDogJycpICtcclxuICAgICAgICAgIGBFcnJvciBlbmNvdW50ZXJlZCB3aGVuIGFjcXVpcmluZyBhbiBhdmFpbGFibGUgZW50cnk6ICR7YWNxdWlyZUNvdW50ZXIoKX1tcy5gXHJcbiAgICAgICkuc2V0RXJyb3IoZXJyb3IpO1xyXG4gICAgfVxyXG4gICAgbG9nKDQsICdbcG9vbF0gQWNxdWlyZWQgYSB3b3JrZXIgaGFuZGxlLicpO1xyXG5cclxuICAgIGlmICghd29ya2VySGFuZGxlLnBhZ2UpIHtcclxuICAgICAgdGhyb3cgbmV3IEV4cG9ydEVycm9yKFxyXG4gICAgICAgICdSZXNvbHZlZCB3b3JrZXIgcGFnZSBpcyBpbnZhbGlkOiB0aGUgcG9vbCBzZXR1cCBpcyB3b25reS4nXHJcbiAgICAgICk7XHJcbiAgICB9XHJcblxyXG4gICAgLy8gU2F2ZSB0aGUgc3RhcnQgdGltZVxyXG4gICAgbGV0IHdvcmtTdGFydCA9IG5ldyBEYXRlKCkuZ2V0VGltZSgpO1xyXG5cclxuICAgIGxvZyg0LCBgW3Bvb2xdIFN0YXJ0aW5nIHdvcmsgb24gcG9vbCBlbnRyeSB3aXRoIElEICR7d29ya2VySGFuZGxlLmlkfS5gKTtcclxuXHJcbiAgICAvLyBQZXJmb3JtIGFuIGV4cG9ydCBvbiBhIHB1cHBldGVlciBsZXZlbFxyXG4gICAgY29uc3QgZXhwb3J0Q291bnRlciA9IG1lYXN1cmVUaW1lKCk7XHJcbiAgICBjb25zdCByZXN1bHQgPSBhd2FpdCBwdXBwZXRlZXJFeHBvcnQod29ya2VySGFuZGxlLnBhZ2UsIGNoYXJ0LCBvcHRpb25zKTtcclxuXHJcbiAgICAvLyBDaGVjayBpZiBpdCdzIGFuIGVycm9yXHJcbiAgICBpZiAocmVzdWx0IGluc3RhbmNlb2YgRXJyb3IpIHtcclxuICAgICAgLy8gVE9ETzogSWYgdGhlIGV4cG9ydCBmYWlsZWQgYmVjYXVzZSBwdXBwZXRlZXIgdGltZWQgb3V0LCB3ZSBuZWVkIHRvIGZvcmNlIGtpbGwgdGhlIHdvcmtlciBzbyB3ZSBnZXQgYSBuZXcgcGFnZS4gVGhhdCBuZWVkcyB0byBiZSBoYW5kbGVkIGJldHRlciB0aGFuIHRoaXMgaGFjay5cclxuICAgICAgaWYgKHJlc3VsdC5tZXNzYWdlID09PSAnUmFzdGVyaXphdGlvbiB0aW1lb3V0Jykge1xyXG4gICAgICAgIHdvcmtlckhhbmRsZS5wYWdlLmNsb3NlKCk7XHJcbiAgICAgICAgd29ya2VySGFuZGxlLnBhZ2UgPSBhd2FpdCBicm93c2VyTmV3UGFnZSgpO1xyXG4gICAgICB9XHJcblxyXG4gICAgICB0aHJvdyBuZXcgRXhwb3J0RXJyb3IoXHJcbiAgICAgICAgKG9wdGlvbnMucGF5bG9hZD8ucmVxdWVzdElkXHJcbiAgICAgICAgICA/IGBGb3IgcmVxdWVzdCB3aXRoIElEICR7b3B0aW9ucy5wYXlsb2FkPy5yZXF1ZXN0SWR9IC0gYFxyXG4gICAgICAgICAgOiAnJykgKyBgRXJyb3IgZW5jb3VudGVyZWQgZHVyaW5nIGV4cG9ydDogJHtleHBvcnRDb3VudGVyKCl9bXMuYFxyXG4gICAgICApLnNldEVycm9yKHJlc3VsdCk7XHJcbiAgICB9XHJcblxyXG4gICAgLy8gQ2hlY2sgdGhlIFB1cHBldGVlciBleHBvcnQgdGltZVxyXG4gICAgaWYgKG9wdGlvbnMuc2VydmVyLmJlbmNobWFya2luZykge1xyXG4gICAgICBsb2coXHJcbiAgICAgICAgNSxcclxuICAgICAgICBvcHRpb25zLnBheWxvYWQ/LnJlcXVlc3RJZFxyXG4gICAgICAgICAgPyBgW2JlbmNobWFya10gUmVxdWVzdCB3aXRoIElEICR7b3B0aW9ucy5wYXlsb2FkPy5yZXF1ZXN0SWR9IC1gXHJcbiAgICAgICAgICA6ICdbYmVuY2htYXJrXScsXHJcbiAgICAgICAgYEV4cG9ydGVkIGEgY2hhcnQgc3VjZXNzZnVsbHk6ICR7ZXhwb3J0Q291bnRlcigpfW1zLmBcclxuICAgICAgKTtcclxuICAgIH1cclxuXHJcbiAgICAvLyBSZWxlYXNlIHRoZSByZXNvdXJjZSBiYWNrIHRvIHRoZSBwb29sXHJcbiAgICBwb29sLnJlbGVhc2Uod29ya2VySGFuZGxlKTtcclxuXHJcbiAgICAvLyBVc2VkIGZvciBzdGF0aXN0aWNzIGluIGF2ZXJhZ2VUaW1lIGFuZCBwcm9jZXNzZWRXb3JrQ291bnQsIHdoaWNoXHJcbiAgICAvLyBpbiB0dXJuIGlzIHVzZWQgYnkgdGhlIC9oZWFsdGggcm91dGUuXHJcbiAgICBjb25zdCB3b3JrRW5kID0gbmV3IERhdGUoKS5nZXRUaW1lKCk7XHJcbiAgICBjb25zdCBleHBvcnRUaW1lID0gd29ya0VuZCAtIHdvcmtTdGFydDtcclxuICAgIHN0YXRzLnRpbWVTcGVudCArPSBleHBvcnRUaW1lO1xyXG4gICAgc3RhdHMuc3BlbnRBdmVyYWdlID0gc3RhdHMudGltZVNwZW50IC8gKytzdGF0cy5wZXJmb3JtZWRFeHBvcnRzO1xyXG5cclxuICAgIGxvZyg0LCBgW3Bvb2xdIFdvcmsgY29tcGxldGVkIGluICR7ZXhwb3J0VGltZX0gbXMuYCk7XHJcblxyXG4gICAgLy8gT3RoZXJ3aXNlIHJldHVybiB0aGUgcmVzdWx0XHJcbiAgICByZXR1cm4ge1xyXG4gICAgICByZXN1bHQsXHJcbiAgICAgIG9wdGlvbnNcclxuICAgIH07XHJcbiAgfSBjYXRjaCAoZXJyb3IpIHtcclxuICAgICsrc3RhdHMuZHJvcHBlZEV4cG9ydHM7XHJcblxyXG4gICAgaWYgKHdvcmtlckhhbmRsZSkge1xyXG4gICAgICBwb29sLnJlbGVhc2Uod29ya2VySGFuZGxlKTtcclxuICAgIH1cclxuXHJcbiAgICB0aHJvdyBuZXcgRXhwb3J0RXJyb3IoYFtwb29sXSBJbiBwb29sLnBvc3RXb3JrOiAke2Vycm9yLm1lc3NhZ2V9YCkuc2V0RXJyb3IoXHJcbiAgICAgIGVycm9yXHJcbiAgICApO1xyXG4gIH1cclxufTtcclxuXHJcbi8qKlxyXG4gKiBSZXRyaWV2ZXMgdGhlIGN1cnJlbnQgcG9vbCBpbnN0YW5jZS5cclxuICpcclxuICogQHJldHVybnMge09iamVjdHxudWxsfSBUaGUgY3VycmVudCBwb29sIGluc3RhbmNlIGlmIGluaXRpYWxpemVkLCBvciBudWxsXHJcbiAqIGlmIHRoZSBwb29sIGhhcyBub3QgYmVlbiBjcmVhdGVkLlxyXG4gKi9cclxuZXhwb3J0IGNvbnN0IGdldFBvb2wgPSAoKSA9PiBwb29sO1xyXG5cclxuLyoqXHJcbiAqIFJldHJpZXZlcyBwb29sIGluZm9ybWF0aW9uIGluIEpTT04gZm9ybWF0LCBpbmNsdWRpbmcgbWluaW11bSBhbmQgbWF4aW11bVxyXG4gKiB3b3JrZXJzLCBhdmFpbGFibGUgd29ya2Vycywgd29ya2VycyBpbiB1c2UsIGFuZCBwZW5kaW5nIGFjcXVpcmUgcmVxdWVzdHMuXHJcbiAqXHJcbiAqIEByZXR1cm5zIHtPYmplY3R9IFBvb2wgaW5mb3JtYXRpb24gaW4gSlNPTiBmb3JtYXQuXHJcbiAqL1xyXG5leHBvcnQgY29uc3QgZ2V0UG9vbEluZm9KU09OID0gKCkgPT4gKHtcclxuICBtaW46IHBvb2wubWluLFxyXG4gIG1heDogcG9vbC5tYXgsXHJcbiAgYWxsOiBwb29sLm51bUZyZWUoKSArIHBvb2wubnVtVXNlZCgpLFxyXG4gIGF2YWlsYWJsZTogcG9vbC5udW1GcmVlKCksXHJcbiAgdXNlZDogcG9vbC5udW1Vc2VkKCksXHJcbiAgcGVuZGluZzogcG9vbC5udW1QZW5kaW5nQWNxdWlyZXMoKVxyXG59KTtcclxuXHJcbi8qKlxyXG4gKiBMb2dzIGluZm9ybWF0aW9uIGFib3V0IHRoZSBjdXJyZW50IHN0YXRlIG9mIHRoZSBwb29sLCBpbmNsdWRpbmcgdGhlIG1pbmltdW1cclxuICogYW5kIG1heGltdW0gd29ya2VycywgYXZhaWxhYmxlIHdvcmtlcnMsIHdvcmtlcnMgaW4gdXNlLCBhbmQgcGVuZGluZyBhY3F1aXJlXHJcbiAqIHJlcXVlc3RzLlxyXG4gKi9cclxuZXhwb3J0IGZ1bmN0aW9uIGdldFBvb2xJbmZvKCkge1xyXG4gIGNvbnN0IHsgbWluLCBtYXgsIGFsbCwgYXZhaWxhYmxlLCB1c2VkLCBwZW5kaW5nIH0gPSBnZXRQb29sSW5mb0pTT04oKTtcclxuXHJcbiAgbG9nKDUsIGBbcG9vbF0gVGhlIG1pbmltdW0gbnVtYmVyIG9mIHJlc291cmNlcyBhbGxvd2VkIGJ5IHBvb2w6ICR7bWlufS5gKTtcclxuICBsb2coNSwgYFtwb29sXSBUaGUgbWF4aW11bSBudW1iZXIgb2YgcmVzb3VyY2VzIGFsbG93ZWQgYnkgcG9vbDogJHttYXh9LmApO1xyXG4gIGxvZyg1LCBgW3Bvb2xdIFRoZSBudW1iZXIgb2YgYWxsIGNyZWF0ZWQgcmVzb3VyY2VzOiAke2FsbH0uYCk7XHJcbiAgbG9nKDUsIGBbcG9vbF0gVGhlIG51bWJlciBvZiBhdmFpbGFibGUgcmVzb3VyY2VzOiAke2F2YWlsYWJsZX0uYCk7XHJcbiAgbG9nKDUsIGBbcG9vbF0gVGhlIG51bWJlciBvZiBhY3F1aXJlZCByZXNvdXJjZXM6ICR7dXNlZH0uYCk7XHJcbiAgbG9nKDUsIGBbcG9vbF0gVGhlIG51bWJlciBvZiByZXNvdXJjZXMgd2FpdGluZyB0byBiZSBhY3F1aXJlZDogJHtwZW5kaW5nfS5gKTtcclxufVxyXG5cclxuZXhwb3J0IGRlZmF1bHQge1xyXG4gIGluaXRQb29sLFxyXG4gIGtpbGxQb29sLFxyXG4gIHBvc3RXb3JrLFxyXG4gIGdldFBvb2wsXHJcbiAgZ2V0UG9vbEluZm8sXHJcbiAgZ2V0UG9vbEluZm9KU09OLFxyXG4gIGdldFN0YXRzOiAoKSA9PiBzdGF0c1xyXG59O1xyXG4iLCIvKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKlxyXG5cclxuSGlnaGNoYXJ0cyBFeHBvcnQgU2VydmVyXHJcblxyXG5Db3B5cmlnaHQgKGMpIDIwMTYtMjAyNCwgSGlnaHNvZnRcclxuXHJcbkxpY2VuY2VkIHVuZGVyIHRoZSBNSVQgbGljZW5jZS5cclxuXHJcbkFkZGl0aW9uYWxseSBhIHZhbGlkIEhpZ2hjaGFydHMgbGljZW5zZSBpcyByZXF1aXJlZCBmb3IgdXNlLlxyXG5cclxuU2VlIExJQ0VOU0UgZmlsZSBpbiByb290IGZvciBkZXRhaWxzLlxyXG5cclxuKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKi9cclxuXHJcbmltcG9ydCB7IHJlYWRGaWxlU3luYywgd3JpdGVGaWxlU3luYyB9IGZyb20gJ2ZzJztcclxuXHJcbmltcG9ydCB7IGdldE9wdGlvbnMsIGluaXRFeHBvcnRTZXR0aW5ncyB9IGZyb20gJy4vY29uZmlnLmpzJztcclxuaW1wb3J0IHsgbG9nLCBsb2dXaXRoU3RhY2sgfSBmcm9tICcuL2xvZ2dlci5qcyc7XHJcbmltcG9ydCB7IGtpbGxQb29sLCBwb3N0V29yaywgc3RhdHMgfSBmcm9tICcuL3Bvb2wuanMnO1xyXG5pbXBvcnQge1xyXG4gIGZpeFR5cGUsXHJcbiAgaGFuZGxlUmVzb3VyY2VzLFxyXG4gIGlzQ29ycmVjdEpTT04sXHJcbiAgb3B0aW9uc1N0cmluZ2lmeSxcclxuICByb3VuZE51bWJlcixcclxuICB0b0Jvb2xlYW4sXHJcbiAgd3JhcEFyb3VuZFxyXG59IGZyb20gJy4vdXRpbHMuanMnO1xyXG5pbXBvcnQgeyBzYW5pdGl6ZSB9IGZyb20gJy4vc2FuaXRpemUuanMnO1xyXG5pbXBvcnQgRXhwb3J0RXJyb3IgZnJvbSAnLi9lcnJvcnMvRXhwb3J0RXJyb3IuanMnO1xyXG5cclxubGV0IGFsbG93Q29kZUV4ZWN1dGlvbiA9IGZhbHNlO1xyXG5cclxuLyoqXHJcbiAqIFN0YXJ0cyBhbiBleHBvcnQgcHJvY2Vzcy4gVGhlIGBzZXR0aW5nc2AgY29udGFpbnMgZmluYWwgb3B0aW9ucyBnYXRoZXJlZFxyXG4gKiBmcm9tIGFsbCBwb3NzaWJsZSBzb3VyY2VzIChjb25maWcsIGVudiwgY2xpLCBqc29uKS4gVGhlIGBlbmRDYWxsYmFja2AgaXNcclxuICogY2FsbGVkIHdoZW4gdGhlIGV4cG9ydCBpcyBjb21wbGV0ZWQsIHdpdGggYW4gZXJyb3Igb2JqZWN0IGFzIHRoZSBmaXJzdFxyXG4gKiBhcmd1bWVudCBhbmQgdGhlIHNlY29uZCBjb250YWluaW5nIHRoZSBiYXNlNjQgcmVzcHJlc2VudGF0aW9uIG9mIGEgY2hhcnQuXHJcbiAqXHJcbiAqIEBwYXJhbSB7T2JqZWN0fSBzZXR0aW5ncyAtIFRoZSBzZXR0aW5ncyBvYmplY3QgY29udGFpbmluZyBleHBvcnRcclxuICogY29uZmlndXJhdGlvbi5cclxuICogQHBhcmFtIHtmdW5jdGlvbn0gZW5kQ2FsbGJhY2sgLSBUaGUgY2FsbGJhY2sgZnVuY3Rpb24gdG8gYmUgaW52b2tlZCB1cG9uXHJcbiAqIGZpbmFsaXppbmcgd29yayBvciB1cG9uIGVycm9yIG9jY3VyYW5jZSBvZiB0aGUgZXhwb3J0aW5nIHByb2Nlc3MuXHJcbiAqXHJcbiAqIEByZXR1cm5zIHt2b2lkfSBUaGlzIGZ1bmN0aW9uIGRvZXMgbm90IHJldHVybiBhIHZhbHVlIGRpcmVjdGx5OyBpbnN0ZWFkLFxyXG4gKiBpdCBjb21tdW5pY2F0ZXMgcmVzdWx0cyB2aWEgdGhlIGVuZENhbGxiYWNrLlxyXG4gKi9cclxuZXhwb3J0IGNvbnN0IHN0YXJ0RXhwb3J0ID0gYXN5bmMgKHNldHRpbmdzLCBlbmRDYWxsYmFjaykgPT4ge1xyXG4gIC8vIFN0YXJ0aW5nIGV4cG9ydGluZyBwcm9jZXNzIG1lc3NhZ2VcclxuICBsb2coNCwgJ1tjaGFydF0gU3RhcnRpbmcgdGhlIGV4cG9ydGluZyBwcm9jZXNzLicpO1xyXG5cclxuICAvLyBJbml0aWFsaXplIG9wdGlvbnNcclxuICBjb25zdCBvcHRpb25zID0gaW5pdEV4cG9ydFNldHRpbmdzKHNldHRpbmdzLCBnZXRPcHRpb25zKCkpO1xyXG5cclxuICAvLyBHZXQgdGhlIGV4cG9ydCBvcHRpb25zXHJcbiAgY29uc3QgZXhwb3J0T3B0aW9ucyA9IG9wdGlvbnMuZXhwb3J0O1xyXG5cclxuICAvLyBJZiBTVkcgaXMgYW4gaW5wdXQgKGFyZ3VtZW50IGNhbiBiZSBzZW50IG9ubHkgYnkgdGhlIHJlcXVlc3QpXHJcbiAgaWYgKG9wdGlvbnMucGF5bG9hZD8uc3ZnICYmIG9wdGlvbnMucGF5bG9hZC5zdmcgIT09ICcnKSB7XHJcbiAgICB0cnkge1xyXG4gICAgICBsb2coNCwgJ1tjaGFydF0gQXR0ZW1wdGluZyB0byBleHBvcnQgZnJvbSBhIFNWRyBpbnB1dC4nKTtcclxuXHJcbiAgICAgIGNvbnN0IHJlc3VsdCA9IGV4cG9ydEFzU3RyaW5nKFxyXG4gICAgICAgIHNhbml0aXplKG9wdGlvbnMucGF5bG9hZC5zdmcpLCAvLyAjMjA5XHJcbiAgICAgICAgb3B0aW9ucyxcclxuICAgICAgICBlbmRDYWxsYmFja1xyXG4gICAgICApO1xyXG5cclxuICAgICAgKytzdGF0cy5leHBvcnRGcm9tU3ZnQXR0ZW1wdHM7XHJcbiAgICAgIHJldHVybiByZXN1bHQ7XHJcbiAgICB9IGNhdGNoIChlcnJvcikge1xyXG4gICAgICByZXR1cm4gZW5kQ2FsbGJhY2soXHJcbiAgICAgICAgbmV3IEV4cG9ydEVycm9yKCdbY2hhcnRdIEVycm9yIGxvYWRpbmcgU1ZHIGlucHV0LicpLnNldEVycm9yKGVycm9yKVxyXG4gICAgICApO1xyXG4gICAgfVxyXG4gIH1cclxuXHJcbiAgLy8gRXhwb3J0IHVzaW5nIG9wdGlvbnMgZnJvbSB0aGUgZmlsZVxyXG4gIGlmIChleHBvcnRPcHRpb25zLmluZmlsZSAmJiBleHBvcnRPcHRpb25zLmluZmlsZS5sZW5ndGgpIHtcclxuICAgIC8vIFRyeSB0byByZWFkIHRoZSBmaWxlIHRvIGdldCB0aGUgc3RyaW5nIHJlcHJlc2VudGF0aW9uXHJcbiAgICB0cnkge1xyXG4gICAgICBsb2coNCwgJ1tjaGFydF0gQXR0ZW1wdGluZyB0byBleHBvcnQgZnJvbSBhbiBpbnB1dCBmaWxlLicpO1xyXG4gICAgICBvcHRpb25zLmV4cG9ydC5pbnN0ciA9IHJlYWRGaWxlU3luYyhleHBvcnRPcHRpb25zLmluZmlsZSwgJ3V0ZjgnKTtcclxuICAgICAgcmV0dXJuIGV4cG9ydEFzU3RyaW5nKG9wdGlvbnMuZXhwb3J0Lmluc3RyLnRyaW0oKSwgb3B0aW9ucywgZW5kQ2FsbGJhY2spO1xyXG4gICAgfSBjYXRjaCAoZXJyb3IpIHtcclxuICAgICAgcmV0dXJuIGVuZENhbGxiYWNrKFxyXG4gICAgICAgIG5ldyBFeHBvcnRFcnJvcignW2NoYXJ0XSBFcnJvciBsb2FkaW5nIGlucHV0IGZpbGUuJykuc2V0RXJyb3IoZXJyb3IpXHJcbiAgICAgICk7XHJcbiAgICB9XHJcbiAgfVxyXG5cclxuICAvLyBFeHBvcnQgd2l0aCBvcHRpb25zIGZyb20gdGhlIHJhdyByZXByZXNlbnRhdGlvblxyXG4gIGlmIChcclxuICAgIChleHBvcnRPcHRpb25zLmluc3RyICYmIGV4cG9ydE9wdGlvbnMuaW5zdHIgIT09ICcnKSB8fFxyXG4gICAgKGV4cG9ydE9wdGlvbnMub3B0aW9ucyAmJiBleHBvcnRPcHRpb25zLm9wdGlvbnMgIT09ICcnKVxyXG4gICkge1xyXG4gICAgdHJ5IHtcclxuICAgICAgbG9nKDQsICdbY2hhcnRdIEF0dGVtcHRpbmcgdG8gZXhwb3J0IGZyb20gYSByYXcgaW5wdXQuJyk7XHJcblxyXG4gICAgICAvLyBQZXJmb3JtIGEgZGlyZWN0IGluamVjdCB3aGVuIGZvcmNlZFxyXG4gICAgICBpZiAodG9Cb29sZWFuKG9wdGlvbnMuY3VzdG9tTG9naWM/LmFsbG93Q29kZUV4ZWN1dGlvbikpIHtcclxuICAgICAgICByZXR1cm4gZG9TdHJhaWdodEluamVjdChvcHRpb25zLCBlbmRDYWxsYmFjayk7XHJcbiAgICAgIH1cclxuXHJcbiAgICAgIC8vIEVpdGhlciB0cnkgdG8gcGFyc2UgdG8gSlNPTiBmaXJzdCBvciBkbyB0aGUgZGlyZWN0IGV4cG9ydFxyXG4gICAgICByZXR1cm4gdHlwZW9mIGV4cG9ydE9wdGlvbnMuaW5zdHIgPT09ICdzdHJpbmcnXHJcbiAgICAgICAgPyBleHBvcnRBc1N0cmluZyhleHBvcnRPcHRpb25zLmluc3RyLnRyaW0oKSwgb3B0aW9ucywgZW5kQ2FsbGJhY2spXHJcbiAgICAgICAgOiBkb0V4cG9ydChcclxuICAgICAgICAgICAgb3B0aW9ucyxcclxuICAgICAgICAgICAgZXhwb3J0T3B0aW9ucy5pbnN0ciB8fCBleHBvcnRPcHRpb25zLm9wdGlvbnMsXHJcbiAgICAgICAgICAgIGVuZENhbGxiYWNrXHJcbiAgICAgICAgICApO1xyXG4gICAgfSBjYXRjaCAoZXJyb3IpIHtcclxuICAgICAgcmV0dXJuIGVuZENhbGxiYWNrKFxyXG4gICAgICAgIG5ldyBFeHBvcnRFcnJvcignW2NoYXJ0XSBFcnJvciBsb2FkaW5nIHJhdyBpbnB1dC4nKS5zZXRFcnJvcihlcnJvcilcclxuICAgICAgKTtcclxuICAgIH1cclxuICB9XHJcblxyXG4gIC8vIE5vIGlucHV0IHNwZWNpZmllZCwgcGFzcyBhbiBlcnJvciBtZXNzYWdlIHRvIHRoZSBjYWxsYmFja1xyXG4gIHJldHVybiBlbmRDYWxsYmFjayhcclxuICAgIG5ldyBFeHBvcnRFcnJvcihcclxuICAgICAgYFtjaGFydF0gTm8gdmFsaWQgaW5wdXQgc3BlY2lmaWVkLiBDaGVjayBpZiBhdCBsZWFzdCBvbmUgb2YgdGhlIGZvbGxvd2luZyBwYXJhbWV0ZXJzIGlzIGNvcnJlY3RseSBzZXQ6ICdpbmZpbGUnLCAnaW5zdHInLCAnb3B0aW9ucycsIG9yICdzdmcnLmBcclxuICAgIClcclxuICApO1xyXG59O1xyXG5cclxuLyoqXHJcbiAqIFN0YXJ0cyBhIGJhdGNoIGV4cG9ydCBwcm9jZXNzIGZvciBtdWx0aXBsZSBjaGFydHMgYmFzZWQgb24gdGhlIGluZm9ybWF0aW9uXHJcbiAqIGluIHRoZSBiYXRjaCBvcHRpb24uIFRoZSBiYXRjaCBpcyBhIHN0cmluZyBpbiB0aGUgZm9sbG93aW5nIGZvcm1hdDpcclxuICogXCJpbmZpbGUxLmpzb249b3V0ZmlsZTEucG5nO2luZmlsZTIuanNvbj1vdXRmaWxlMi5wbmc7Li4uXCJcclxuICpcclxuICogQHBhcmFtIHtPYmplY3R9IG9wdGlvbnMgLSBUaGUgb3B0aW9ucyBvYmplY3QgY29udGFpbmluZyBjb25maWd1cmF0aW9uIGZvclxyXG4gKiBhIGJhdGNoIGV4cG9ydC5cclxuICpcclxuICogQHJldHVybnMge1Byb21pc2U8dm9pZD59IEEgUHJvbWlzZSB0aGF0IHJlc29sdmVzIG9uY2UgdGhlIGJhdGNoIGV4cG9ydFxyXG4gKiBwcm9jZXNzIGlzIGNvbXBsZXRlZC5cclxuICpcclxuICogQHRocm93cyB7RXhwb3J0RXJyb3J9IFRocm93cyBhbiBFeHBvcnRFcnJvciBpZiBhbiBlcnJvciBvY2N1cnMgZHVyaW5nXHJcbiAqIGFueSBvZiB0aGUgYmF0Y2ggZXhwb3J0IHByb2Nlc3MuXHJcbiAqL1xyXG5leHBvcnQgY29uc3QgYmF0Y2hFeHBvcnQgPSBhc3luYyAob3B0aW9ucykgPT4ge1xyXG4gIGNvbnN0IGJhdGNoRnVuY3Rpb25zID0gW107XHJcblxyXG4gIC8vIFNwbGl0IGFuZCBwYWlyIHRoZSAtLWJhdGNoIGFyZ3VtZW50c1xyXG4gIGZvciAobGV0IHBhaXIgb2Ygb3B0aW9ucy5leHBvcnQuYmF0Y2guc3BsaXQoJzsnKSkge1xyXG4gICAgcGFpciA9IHBhaXIuc3BsaXQoJz0nKTtcclxuICAgIGlmIChwYWlyLmxlbmd0aCA9PT0gMikge1xyXG4gICAgICBiYXRjaEZ1bmN0aW9ucy5wdXNoKFxyXG4gICAgICAgIHN0YXJ0RXhwb3J0KFxyXG4gICAgICAgICAge1xyXG4gICAgICAgICAgICAuLi5vcHRpb25zLFxyXG4gICAgICAgICAgICBleHBvcnQ6IHtcclxuICAgICAgICAgICAgICAuLi5vcHRpb25zLmV4cG9ydCxcclxuICAgICAgICAgICAgICBpbmZpbGU6IHBhaXJbMF0sXHJcbiAgICAgICAgICAgICAgb3V0ZmlsZTogcGFpclsxXVxyXG4gICAgICAgICAgICB9XHJcbiAgICAgICAgICB9LFxyXG4gICAgICAgICAgKGVycm9yLCBpbmZvKSA9PiB7XHJcbiAgICAgICAgICAgIC8vIFRocm93IGFuIGVycm9yXHJcbiAgICAgICAgICAgIGlmIChlcnJvcikge1xyXG4gICAgICAgICAgICAgIHRocm93IGVycm9yO1xyXG4gICAgICAgICAgICB9XHJcblxyXG4gICAgICAgICAgICAvLyBTYXZlIHRoZSBiYXNlNjQgZnJvbSBhIGJ1ZmZlciB0byBhIGNvcnJlY3QgaW1hZ2UgZmlsZVxyXG4gICAgICAgICAgICB3cml0ZUZpbGVTeW5jKFxyXG4gICAgICAgICAgICAgIGluZm8ub3B0aW9ucy5leHBvcnQub3V0ZmlsZSxcclxuICAgICAgICAgICAgICBpbmZvLm9wdGlvbnMuZXhwb3J0LnR5cGUgIT09ICdzdmcnXHJcbiAgICAgICAgICAgICAgICA/IEJ1ZmZlci5mcm9tKGluZm8ucmVzdWx0LCAnYmFzZTY0JylcclxuICAgICAgICAgICAgICAgIDogaW5mby5yZXN1bHRcclxuICAgICAgICAgICAgKTtcclxuICAgICAgICAgIH1cclxuICAgICAgICApXHJcbiAgICAgICk7XHJcbiAgICB9XHJcbiAgfVxyXG5cclxuICB0cnkge1xyXG4gICAgLy8gQXdhaXQgYWxsIGV4cG9ydHMgYXJlIGRvbmVcclxuICAgIGF3YWl0IFByb21pc2UuYWxsKGJhdGNoRnVuY3Rpb25zKTtcclxuXHJcbiAgICAvLyBLaWxsIHBvb2wgYW5kIGNsb3NlIGJyb3dzZXIgYWZ0ZXIgZmluaXNoaW5nIGJhdGNoIGV4cG9ydFxyXG4gICAgYXdhaXQga2lsbFBvb2woKTtcclxuICB9IGNhdGNoIChlcnJvcikge1xyXG4gICAgdGhyb3cgbmV3IEV4cG9ydEVycm9yKFxyXG4gICAgICAnW2NoYXJ0XSBFcnJvciBlbmNvdW50ZXJlZCBkdXJpbmcgYmF0Y2ggZXhwb3J0LidcclxuICAgICkuc2V0RXJyb3IoZXJyb3IpO1xyXG4gIH1cclxufTtcclxuXHJcbi8qKlxyXG4gKiBTdGFydHMgYSBzaW5nbGUgZXhwb3J0IHByb2Nlc3MgYmFzZWQgb24gdGhlIHNwZWNpZmllZCBvcHRpb25zLlxyXG4gKlxyXG4gKiBAcGFyYW0ge09iamVjdH0gb3B0aW9ucyAtIFRoZSBvcHRpb25zIG9iamVjdCBjb250YWluaW5nIGNvbmZpZ3VyYXRpb24gZm9yXHJcbiAqIGEgc2luZ2xlIGV4cG9ydC5cclxuICpcclxuICogQHJldHVybnMge1Byb21pc2U8dm9pZD59IEEgUHJvbWlzZSB0aGF0IHJlc29sdmVzIG9uY2UgdGhlIHNpbmdsZSBleHBvcnRcclxuICogcHJvY2VzcyBpcyBjb21wbGV0ZWQuXHJcbiAqXHJcbiAqIEB0aHJvd3Mge0V4cG9ydEVycm9yfSBUaHJvd3MgYW4gRXhwb3J0RXJyb3IgaWYgYW4gZXJyb3Igb2NjdXJzIGR1cmluZ1xyXG4gKiB0aGUgc2luZ2xlIGV4cG9ydCBwcm9jZXNzLlxyXG4gKi9cclxuZXhwb3J0IGNvbnN0IHNpbmdsZUV4cG9ydCA9IGFzeW5jIChvcHRpb25zKSA9PiB7XHJcbiAgLy8gVXNlIGluc3RyIG9yIGl0cyBhbGlhcywgb3B0aW9uc1xyXG4gIG9wdGlvbnMuZXhwb3J0Lmluc3RyID0gb3B0aW9ucy5leHBvcnQuaW5zdHIgfHwgb3B0aW9ucy5leHBvcnQub3B0aW9ucztcclxuXHJcbiAgLy8gUGVyZm9ybSBhbiBleHBvcnRcclxuICBhd2FpdCBzdGFydEV4cG9ydChvcHRpb25zLCBhc3luYyAoZXJyb3IsIGluZm8pID0+IHtcclxuICAgIC8vIEV4aXQgcHJvY2VzcyB3aGVuIGVycm9yXHJcbiAgICBpZiAoZXJyb3IpIHtcclxuICAgICAgdGhyb3cgZXJyb3I7XHJcbiAgICB9XHJcblxyXG4gICAgY29uc3QgeyBvdXRmaWxlLCB0eXBlIH0gPSBpbmZvLm9wdGlvbnMuZXhwb3J0O1xyXG5cclxuICAgIC8vIFNhdmUgdGhlIGJhc2U2NCBmcm9tIGEgYnVmZmVyIHRvIGEgY29ycmVjdCBpbWFnZSBmaWxlXHJcbiAgICB3cml0ZUZpbGVTeW5jKFxyXG4gICAgICBvdXRmaWxlIHx8IGBjaGFydC4ke3R5cGV9YCxcclxuICAgICAgdHlwZSAhPT0gJ3N2ZycgPyBCdWZmZXIuZnJvbShpbmZvLnJlc3VsdCwgJ2Jhc2U2NCcpIDogaW5mby5yZXN1bHRcclxuICAgICk7XHJcblxyXG4gICAgLy8gS2lsbCBwb29sIGFuZCBjbG9zZSBicm93c2VyIGFmdGVyIGZpbmlzaGluZyBzaW5nbGUgZXhwb3J0XHJcbiAgICBhd2FpdCBraWxsUG9vbCgpO1xyXG4gIH0pO1xyXG59O1xyXG5cclxuLyoqXHJcbiAqIERldGVybWluZXMgdGhlIHNpemUgYW5kIHNjYWxlIGZvciBjaGFydCBleHBvcnQgYmFzZWQgb24gdGhlIHByb3ZpZGVkIG9wdGlvbnMuXHJcbiAqXHJcbiAqIEBwYXJhbSB7T2JqZWN0fSBvcHRpb25zIC0gVGhlIG9wdGlvbnMgb2JqZWN0IGNvbnRhaW5pbmcgY29uZmlndXJhdGlvbiBmb3JcclxuICogY2hhcnQgZXhwb3J0LlxyXG4gKlxyXG4gKiBAcmV0dXJucyB7T2JqZWN0fSBBbiBvYmplY3QgY29udGFpbmluZyB0aGUgY2FsY3VsYXRlZCBoZWlnaHQsIHdpZHRoLFxyXG4gKiBhbmQgc2NhbGUgZm9yIHRoZSBjaGFydCBleHBvcnQuXHJcbiAqL1xyXG5leHBvcnQgY29uc3QgZmluZENoYXJ0U2l6ZSA9IChvcHRpb25zKSA9PiB7XHJcbiAgY29uc3QgeyBjaGFydCwgZXhwb3J0aW5nIH0gPVxyXG4gICAgb3B0aW9ucy5leHBvcnQ/Lm9wdGlvbnMgfHwgaXNDb3JyZWN0SlNPTihvcHRpb25zLmV4cG9ydD8uaW5zdHIpO1xyXG5cclxuICAvLyBTZWUgaWYgZ2xvYmFsT3B0aW9ucyBob2xkcyBjaGFydCBvciBleHBvcnRpbmcgc2l6ZVxyXG4gIGNvbnN0IGdsb2JhbE9wdGlvbnMgPSBpc0NvcnJlY3RKU09OKG9wdGlvbnMuZXhwb3J0Py5nbG9iYWxPcHRpb25zKTtcclxuXHJcbiAgLy8gU2VjdXJlIHNjYWxlIHZhbHVlXHJcbiAgbGV0IHNjYWxlID1cclxuICAgIG9wdGlvbnMuZXhwb3J0Py5zY2FsZSB8fFxyXG4gICAgZXhwb3J0aW5nPy5zY2FsZSB8fFxyXG4gICAgZ2xvYmFsT3B0aW9ucz8uZXhwb3J0aW5nPy5zY2FsZSB8fFxyXG4gICAgb3B0aW9ucy5leHBvcnQ/LmRlZmF1bHRTY2FsZSB8fFxyXG4gICAgMTtcclxuXHJcbiAgLy8gdGhlIHNjYWxlIGNhbm5vdCBiZSBsb3dlciB0aGFuIDAuMSBhbmQgY2Fubm90IGJlIGhpZ2hlciB0aGFuIDUuMFxyXG4gIHNjYWxlID0gTWF0aC5tYXgoMC4xLCBNYXRoLm1pbihzY2FsZSwgNS4wKSk7XHJcblxyXG4gIC8vIHdlIHdhbnQgdG8gcm91bmQgdGhlIG51bWJlcnMgbGlrZSAwLjIzMjM0IC0+IDAuMjNcclxuICBzY2FsZSA9IHJvdW5kTnVtYmVyKHNjYWxlLCAyKTtcclxuXHJcbiAgLy8gRmluZCBjaGFydCBzaXplIGFuZCBzY2FsZVxyXG4gIGNvbnN0IHNpemUgPSB7XHJcbiAgICBoZWlnaHQ6XHJcbiAgICAgIG9wdGlvbnMuZXhwb3J0Py5oZWlnaHQgfHxcclxuICAgICAgZXhwb3J0aW5nPy5zb3VyY2VIZWlnaHQgfHxcclxuICAgICAgY2hhcnQ/LmhlaWdodCB8fFxyXG4gICAgICBnbG9iYWxPcHRpb25zPy5leHBvcnRpbmc/LnNvdXJjZUhlaWdodCB8fFxyXG4gICAgICBnbG9iYWxPcHRpb25zPy5jaGFydD8uaGVpZ2h0IHx8XHJcbiAgICAgIG9wdGlvbnMuZXhwb3J0Py5kZWZhdWx0SGVpZ2h0IHx8XHJcbiAgICAgIDQwMCxcclxuICAgIHdpZHRoOlxyXG4gICAgICBvcHRpb25zLmV4cG9ydD8ud2lkdGggfHxcclxuICAgICAgZXhwb3J0aW5nPy5zb3VyY2VXaWR0aCB8fFxyXG4gICAgICBjaGFydD8ud2lkdGggfHxcclxuICAgICAgZ2xvYmFsT3B0aW9ucz8uZXhwb3J0aW5nPy5zb3VyY2VXaWR0aCB8fFxyXG4gICAgICBnbG9iYWxPcHRpb25zPy5jaGFydD8ud2lkdGggfHxcclxuICAgICAgb3B0aW9ucy5leHBvcnQ/LmRlZmF1bHRXaWR0aCB8fFxyXG4gICAgICA2MDAsXHJcbiAgICBzY2FsZVxyXG4gIH07XHJcblxyXG4gIC8vIEdldCByaWQgb2YgcG90ZW50aWFsIHB4IGFuZCAlXHJcbiAgZm9yIChsZXQgW3BhcmFtLCB2YWx1ZV0gb2YgT2JqZWN0LmVudHJpZXMoc2l6ZSkpIHtcclxuICAgIHNpemVbcGFyYW1dID1cclxuICAgICAgdHlwZW9mIHZhbHVlID09PSAnc3RyaW5nJyA/ICt2YWx1ZS5yZXBsYWNlKC9weHwlL2dpLCAnJykgOiB2YWx1ZTtcclxuICB9XHJcbiAgcmV0dXJuIHNpemU7XHJcbn07XHJcblxyXG4vKipcclxuICogRnVuY3Rpb24gZm9yIGZpbmFsaXppbmcgb3B0aW9ucyBiZWZvcmUgZXhwb3J0LlxyXG4gKlxyXG4gKiBAcGFyYW0ge09iamVjdH0gb3B0aW9ucyAtIFRoZSBvcHRpb25zIG9iamVjdCBjb250YWluaW5nIGNvbmZpZ3VyYXRpb24gZm9yXHJcbiAqIHRoZSBleHBvcnQgcHJvY2Vzcy5cclxuICogQHBhcmFtIHtPYmplY3R9IGNoYXJ0SnNvbiAtIFRoZSBKU09OIHJlcHJlc2VudGF0aW9uIG9mIHRoZSBjaGFydC5cclxuICogQHBhcmFtIHtGdW5jdGlvbn0gZW5kQ2FsbGJhY2sgLSBUaGUgY2FsbGJhY2sgZnVuY3Rpb24gdG8gYmUgY2FsbGVkIHVwb25cclxuICogY29tcGxldGlvbiBvciBlcnJvci5cclxuICogQHBhcmFtIHtzdHJpbmd9IHN2ZyAtIFRoZSBTVkcgcmVwcmVzZW50YXRpb24gb2YgdGhlIGNoYXJ0LlxyXG4gKlxyXG4gKiBAcmV0dXJucyB7UHJvbWlzZTx2b2lkPn0gQSBQcm9taXNlIHRoYXQgcmVzb2x2ZXMgb25jZSB0aGUgZXhwb3J0IHByb2Nlc3NcclxuICogaXMgY29tcGxldGVkLlxyXG4gKi9cclxuY29uc3QgZG9FeHBvcnQgPSBhc3luYyAob3B0aW9ucywgY2hhcnRKc29uLCBlbmRDYWxsYmFjaywgc3ZnKSA9PiB7XHJcbiAgbGV0IHsgZXhwb3J0OiBleHBvcnRPcHRpb25zLCBjdXN0b21Mb2dpYzogY3VzdG9tTG9naWNPcHRpb25zIH0gPSBvcHRpb25zO1xyXG5cclxuICBjb25zdCBhbGxvd0NvZGVFeGVjdXRpb25TY29wZWQgPVxyXG4gICAgdHlwZW9mIGN1c3RvbUxvZ2ljT3B0aW9ucy5hbGxvd0NvZGVFeGVjdXRpb24gPT09ICdib29sZWFuJ1xyXG4gICAgICA/IGN1c3RvbUxvZ2ljT3B0aW9ucy5hbGxvd0NvZGVFeGVjdXRpb25cclxuICAgICAgOiBhbGxvd0NvZGVFeGVjdXRpb247XHJcblxyXG4gIGlmICghY3VzdG9tTG9naWNPcHRpb25zKSB7XHJcbiAgICBjdXN0b21Mb2dpY09wdGlvbnMgPSBvcHRpb25zLmN1c3RvbUxvZ2ljID0ge307XHJcbiAgfSBlbHNlIGlmIChhbGxvd0NvZGVFeGVjdXRpb25TY29wZWQpIHtcclxuICAgIGlmICh0eXBlb2Ygb3B0aW9ucy5jdXN0b21Mb2dpYy5yZXNvdXJjZXMgPT09ICdzdHJpbmcnKSB7XHJcbiAgICAgIC8vIFByb2Nlc3MgcmVzb3VyY2VzXHJcbiAgICAgIG9wdGlvbnMuY3VzdG9tTG9naWMucmVzb3VyY2VzID0gaGFuZGxlUmVzb3VyY2VzKFxyXG4gICAgICAgIG9wdGlvbnMuY3VzdG9tTG9naWMucmVzb3VyY2VzLFxyXG4gICAgICAgIHRvQm9vbGVhbihvcHRpb25zLmN1c3RvbUxvZ2ljLmFsbG93RmlsZVJlc291cmNlcylcclxuICAgICAgKTtcclxuICAgIH0gZWxzZSBpZiAoIW9wdGlvbnMuY3VzdG9tTG9naWMucmVzb3VyY2VzKSB7XHJcbiAgICAgIHRyeSB7XHJcbiAgICAgICAgY29uc3QgcmVzb3VyY2VzID0gcmVhZEZpbGVTeW5jKCdyZXNvdXJjZXMuanNvbicsICd1dGY4Jyk7XHJcbiAgICAgICAgb3B0aW9ucy5jdXN0b21Mb2dpYy5yZXNvdXJjZXMgPSBoYW5kbGVSZXNvdXJjZXMoXHJcbiAgICAgICAgICByZXNvdXJjZXMsXHJcbiAgICAgICAgICB0b0Jvb2xlYW4ob3B0aW9ucy5jdXN0b21Mb2dpYy5hbGxvd0ZpbGVSZXNvdXJjZXMpXHJcbiAgICAgICAgKTtcclxuICAgICAgfSBjYXRjaCAoZXJyb3IpIHtcclxuICAgICAgICBsb2dXaXRoU3RhY2soXHJcbiAgICAgICAgICAyLFxyXG4gICAgICAgICAgZXJyb3IsXHJcbiAgICAgICAgICBgW2NoYXJ0XSBVbmFibGUgdG8gbG9hZCB0aGUgZGVmYXVsdCByZXNvdXJjZXMuanNvbiBmaWxlLmBcclxuICAgICAgICApO1xyXG4gICAgICB9XHJcbiAgICB9XHJcbiAgfVxyXG5cclxuICAvLyBJZiB0aGUgYWxsb3dDb2RlRXhlY3V0aW9uIGZsYWcgaXNuJ3Qgc2V0LCB3ZSBzaG91bGQgcmVmdXNlIHRoZSB1c2FnZVxyXG4gIC8vIG9mIGNhbGxiYWNrLCByZXNvdXJjZXMsIGFuZCBjdXN0b20gY29kZS4gQWRkaXRpb25hbGx5LCB0aGUgd29ya2VyIHdpbGxcclxuICAvLyByZWZ1c2UgdG8gcnVuIGFyYml0cmFyeSBKYXZhU2NyaXB0LiBQcmlvcml0aXplZCBzaG91bGQgYmUgdGhlIHNjb3BlZFxyXG4gIC8vIG9wdGlvbiwgdGhlbiB3ZSBzaG91bGQgdGFrZSBhIGxvb2sgYXQgdGhlIG92ZXJhbGwgcG9vbCBvcHRpb24uXHJcbiAgaWYgKCFhbGxvd0NvZGVFeGVjdXRpb25TY29wZWQgJiYgY3VzdG9tTG9naWNPcHRpb25zKSB7XHJcbiAgICBpZiAoXHJcbiAgICAgIGN1c3RvbUxvZ2ljT3B0aW9ucy5jYWxsYmFjayB8fFxyXG4gICAgICBjdXN0b21Mb2dpY09wdGlvbnMucmVzb3VyY2VzIHx8XHJcbiAgICAgIGN1c3RvbUxvZ2ljT3B0aW9ucy5jdXN0b21Db2RlXHJcbiAgICApIHtcclxuICAgICAgLy8gU2VuZCBiYWNrIGEgZnJpZW5kbHkgbWVzc2FnZSBzYXlpbmcgdGhhdCB0aGUgZXhwb3J0ZXIgZG9lcyBub3Qgc3VwcG9ydFxyXG4gICAgICAvLyB0aGVzZSBzZXR0aW5ncy5cclxuICAgICAgcmV0dXJuIGVuZENhbGxiYWNrKFxyXG4gICAgICAgIG5ldyBFeHBvcnRFcnJvcihcclxuICAgICAgICAgIGBbY2hhcnRdIFRoZSAnY2FsbGJhY2snLCAncmVzb3VyY2VzJyBhbmQgJ2N1c3RvbUNvZGUnIG9wdGlvbnMgaGF2ZSBiZWVuIGRpc2FibGVkIGZvciB0aGlzIHNlcnZlci5gXHJcbiAgICAgICAgKVxyXG4gICAgICApO1xyXG4gICAgfVxyXG5cclxuICAgIC8vIFJlc2V0IGFsbCBhZGRpdGlvbmFsIGN1c3RvbSBjb2RlXHJcbiAgICBjdXN0b21Mb2dpY09wdGlvbnMuY2FsbGJhY2sgPSBmYWxzZTtcclxuICAgIGN1c3RvbUxvZ2ljT3B0aW9ucy5yZXNvdXJjZXMgPSBmYWxzZTtcclxuICAgIGN1c3RvbUxvZ2ljT3B0aW9ucy5jdXN0b21Db2RlID0gZmFsc2U7XHJcbiAgfVxyXG5cclxuICAvLyBDbGVhbiBwcm9wZXJ0aWVzIHRvIGtlZXAgaXQgbGVhbiBhbmQgbWVhblxyXG4gIGlmIChjaGFydEpzb24pIHtcclxuICAgIGNoYXJ0SnNvbi5jaGFydCA9IGNoYXJ0SnNvbi5jaGFydCB8fCB7fTtcclxuICAgIGNoYXJ0SnNvbi5leHBvcnRpbmcgPSBjaGFydEpzb24uZXhwb3J0aW5nIHx8IHt9O1xyXG4gICAgY2hhcnRKc29uLmV4cG9ydGluZy5lbmFibGVkID0gZmFsc2U7XHJcbiAgfVxyXG5cclxuICBleHBvcnRPcHRpb25zLmNvbnN0ciA9IGV4cG9ydE9wdGlvbnMuY29uc3RyIHx8ICdjaGFydCc7XHJcbiAgZXhwb3J0T3B0aW9ucy50eXBlID0gZml4VHlwZShleHBvcnRPcHRpb25zLnR5cGUsIGV4cG9ydE9wdGlvbnMub3V0ZmlsZSk7XHJcbiAgaWYgKGV4cG9ydE9wdGlvbnMudHlwZSA9PT0gJ3N2ZycpIHtcclxuICAgIGV4cG9ydE9wdGlvbnMud2lkdGggPSBmYWxzZTtcclxuICB9XHJcblxyXG4gIC8vIFByZXBhcmUgZ2xvYmFsIGFuZCB0aGVtZSBvcHRpb25zXHJcbiAgWydnbG9iYWxPcHRpb25zJywgJ3RoZW1lT3B0aW9ucyddLmZvckVhY2goKG9wdGlvbnNOYW1lKSA9PiB7XHJcbiAgICB0cnkge1xyXG4gICAgICBpZiAoZXhwb3J0T3B0aW9ucyAmJiBleHBvcnRPcHRpb25zW29wdGlvbnNOYW1lXSkge1xyXG4gICAgICAgIGlmIChcclxuICAgICAgICAgIHR5cGVvZiBleHBvcnRPcHRpb25zW29wdGlvbnNOYW1lXSA9PT0gJ3N0cmluZycgJiZcclxuICAgICAgICAgIGV4cG9ydE9wdGlvbnNbb3B0aW9uc05hbWVdLmVuZHNXaXRoKCcuanNvbicpXHJcbiAgICAgICAgKSB7XHJcbiAgICAgICAgICBleHBvcnRPcHRpb25zW29wdGlvbnNOYW1lXSA9IGlzQ29ycmVjdEpTT04oXHJcbiAgICAgICAgICAgIHJlYWRGaWxlU3luYyhleHBvcnRPcHRpb25zW29wdGlvbnNOYW1lXSwgJ3V0ZjgnKSxcclxuICAgICAgICAgICAgdHJ1ZVxyXG4gICAgICAgICAgKTtcclxuICAgICAgICB9IGVsc2Uge1xyXG4gICAgICAgICAgZXhwb3J0T3B0aW9uc1tvcHRpb25zTmFtZV0gPSBpc0NvcnJlY3RKU09OKFxyXG4gICAgICAgICAgICBleHBvcnRPcHRpb25zW29wdGlvbnNOYW1lXSxcclxuICAgICAgICAgICAgdHJ1ZVxyXG4gICAgICAgICAgKTtcclxuICAgICAgICB9XHJcbiAgICAgIH1cclxuICAgIH0gY2F0Y2ggKGVycm9yKSB7XHJcbiAgICAgIGV4cG9ydE9wdGlvbnNbb3B0aW9uc05hbWVdID0ge307XHJcbiAgICAgIGxvZ1dpdGhTdGFjaygyLCBlcnJvciwgYFtjaGFydF0gVGhlICcke29wdGlvbnNOYW1lfScgY2Fubm90IGJlIGxvYWRlZC5gKTtcclxuICAgIH1cclxuICB9KTtcclxuXHJcbiAgLy8gUHJlcGFyZSB0aGUgY3VzdG9tQ29kZVxyXG4gIGlmIChjdXN0b21Mb2dpY09wdGlvbnMuYWxsb3dDb2RlRXhlY3V0aW9uKSB7XHJcbiAgICB0cnkge1xyXG4gICAgICBjdXN0b21Mb2dpY09wdGlvbnMuY3VzdG9tQ29kZSA9IHdyYXBBcm91bmQoXHJcbiAgICAgICAgY3VzdG9tTG9naWNPcHRpb25zLmN1c3RvbUNvZGUsXHJcbiAgICAgICAgY3VzdG9tTG9naWNPcHRpb25zLmFsbG93RmlsZVJlc291cmNlc1xyXG4gICAgICApO1xyXG4gICAgfSBjYXRjaCAoZXJyb3IpIHtcclxuICAgICAgbG9nV2l0aFN0YWNrKDIsIGVycm9yLCBgW2NoYXJ0XSBUaGUgJ2N1c3RvbUNvZGUnIGNhbm5vdCBiZSBsb2FkZWQuYCk7XHJcbiAgICB9XHJcbiAgfVxyXG5cclxuICAvLyBHZXQgdGhlIGNhbGxiYWNrXHJcbiAgaWYgKFxyXG4gICAgY3VzdG9tTG9naWNPcHRpb25zICYmXHJcbiAgICBjdXN0b21Mb2dpY09wdGlvbnMuY2FsbGJhY2sgJiZcclxuICAgIGN1c3RvbUxvZ2ljT3B0aW9ucy5jYWxsYmFjaz8uaW5kZXhPZigneycpIDwgMFxyXG4gICkge1xyXG4gICAgLy8gVGhlIGFsbG93RmlsZVJlc291cmNlcyBpcyBhbHdheXMgc2V0IHRvIGZhbHNlIGZvciBIVFRQIHJlcXVlc3RzIHRvIGF2b2lkXHJcbiAgICAvLyBpbmplY3RpbmcgYXJiaXRyYXJ5IGZpbGVzIGZyb20gdGhlIGZzXHJcbiAgICBpZiAoY3VzdG9tTG9naWNPcHRpb25zLmFsbG93RmlsZVJlc291cmNlcykge1xyXG4gICAgICB0cnkge1xyXG4gICAgICAgIGN1c3RvbUxvZ2ljT3B0aW9ucy5jYWxsYmFjayA9IHJlYWRGaWxlU3luYyhcclxuICAgICAgICAgIGN1c3RvbUxvZ2ljT3B0aW9ucy5jYWxsYmFjayxcclxuICAgICAgICAgICd1dGY4J1xyXG4gICAgICAgICk7XHJcbiAgICAgIH0gY2F0Y2ggKGVycm9yKSB7XHJcbiAgICAgICAgY3VzdG9tTG9naWNPcHRpb25zLmNhbGxiYWNrID0gZmFsc2U7XHJcbiAgICAgICAgbG9nV2l0aFN0YWNrKDIsIGVycm9yLCBgW2NoYXJ0XSBUaGUgJ2NhbGxiYWNrJyBjYW5ub3QgYmUgbG9hZGVkLmApO1xyXG4gICAgICB9XHJcbiAgICB9IGVsc2Uge1xyXG4gICAgICBjdXN0b21Mb2dpY09wdGlvbnMuY2FsbGJhY2sgPSBmYWxzZTtcclxuICAgIH1cclxuICB9XHJcblxyXG4gIC8vIFNpemUgc2VhcmNoXHJcbiAgb3B0aW9ucy5leHBvcnQgPSB7XHJcbiAgICAuLi5vcHRpb25zLmV4cG9ydCxcclxuICAgIC4uLmZpbmRDaGFydFNpemUob3B0aW9ucylcclxuICB9O1xyXG5cclxuICAvLyBQb3N0IHRoZSB3b3JrIHRvIHRoZSBwb29sXHJcbiAgdHJ5IHtcclxuICAgIGNvbnN0IHJlc3VsdCA9IGF3YWl0IHBvc3RXb3JrKFxyXG4gICAgICBleHBvcnRPcHRpb25zLnN0ckluaiB8fCBjaGFydEpzb24gfHwgc3ZnLFxyXG4gICAgICBvcHRpb25zXHJcbiAgICApO1xyXG4gICAgcmV0dXJuIGVuZENhbGxiYWNrKGZhbHNlLCByZXN1bHQpO1xyXG4gIH0gY2F0Y2ggKGVycm9yKSB7XHJcbiAgICByZXR1cm4gZW5kQ2FsbGJhY2soZXJyb3IpO1xyXG4gIH1cclxufTtcclxuXHJcbi8qKlxyXG4gKiBQZXJmb3JtcyBhIGRpcmVjdCBpbmplY3Qgb2Ygb3B0aW9ucyBiZWZvcmUgZXhwb3J0LiBUaGUgZnVuY3Rpb24gYXR0ZW1wdHNcclxuICogdG8gc3RyaW5naWZ5IHRoZSBwcm92aWRlZCBvcHRpb25zIGFuZCByZW1vdmVzIHVubmVjZXNzYXJ5IGNoYXJhY3RlcnMsXHJcbiAqIGVuc3VyaW5nIGEgY2xlYW4gYW5kIGZvcm1hdHRlZCBpbnB1dC4gVGhlIHJlc3VsdGluZyBzdHJpbmcgaXMgc2F2ZWQgYXNcclxuICogYSBcInN0cmlnaHQgaW5qZWN0XCIgc3RyaW5nIGluIHRoZSBleHBvcnQgb3B0aW9ucy4gSXQgdGhlbiBpbnZva2VzIHRoZVxyXG4gKiBkb0V4cG9ydCBmdW5jdGlvbiB3aXRoIHRoZSB1cGRhdGVkIG9wdGlvbnMuXHJcbiAqXHJcbiAqIElNUE9SVEFOVDogRGFuZ2Vyb3VzIGFuZCBtdXN0IGJlIHVzZWQgZGVsaWJlcmF0ZWx5IGJ5IHNvbWVvbmUgd2hvIHNldHMgdXBcclxuICogYSBzZXJ2ZXIgKHNlZSB0aGUgIC0tYWxsb3dDb2RlRXhlY3V0aW9uIG9wdGlvbikuXHJcbiAqXHJcbiAqIEBwYXJhbSB7T2JqZWN0fSBvcHRpb25zIC0gVGhlIGV4cG9ydCBvcHRpb25zIGNvbnRhaW5pbmcgdGhlIGlucHV0XHJcbiAqIHRvIGJlIGluamVjdGVkLlxyXG4gKiBAcGFyYW0ge2Z1bmN0aW9ufSBlbmRDYWxsYmFjayAtIFRoZSBjYWxsYmFjayBmdW5jdGlvbiB0byBiZSBpbnZva2VkXHJcbiAqIGF0IHRoZSBlbmQgb2YgdGhlIHByb2Nlc3MuXHJcbiAqXHJcbiAqIEByZXR1cm5zIHtQcm9taXNlfSBBIFByb21pc2UgdGhhdCByZXNvbHZlcyB3aXRoIHRoZSByZXN1bHQgb2YgdGhlIGV4cG9ydFxyXG4gKiBvcGVyYXRpb24gb3IgcmVqZWN0cyB3aXRoIGFuIGVycm9yIGlmIGFueSBpc3N1ZXMgb2NjdXIgZHVyaW5nIHRoZSBwcm9jZXNzLlxyXG4gKi9cclxuY29uc3QgZG9TdHJhaWdodEluamVjdCA9IChvcHRpb25zLCBlbmRDYWxsYmFjaykgPT4ge1xyXG4gIHRyeSB7XHJcbiAgICBsZXQgc3RySW5qO1xyXG4gICAgbGV0IGluc3RyID0gb3B0aW9ucy5leHBvcnQuaW5zdHIgfHwgb3B0aW9ucy5leHBvcnQub3B0aW9ucztcclxuXHJcbiAgICBpZiAodHlwZW9mIGluc3RyICE9PSAnc3RyaW5nJykge1xyXG4gICAgICAvLyBUcnkgdG8gc3RyaW5naWZ5IG9wdGlvbnNcclxuICAgICAgc3RySW5qID0gaW5zdHIgPSBvcHRpb25zU3RyaW5naWZ5KFxyXG4gICAgICAgIGluc3RyLFxyXG4gICAgICAgIG9wdGlvbnMuY3VzdG9tTG9naWM/LmFsbG93Q29kZUV4ZWN1dGlvblxyXG4gICAgICApO1xyXG4gICAgfVxyXG4gICAgc3RySW5qID0gaW5zdHIucmVwbGFjZUFsbCgvXFx0fFxcbnxcXHIvZywgJycpLnRyaW0oKTtcclxuXHJcbiAgICAvLyBHZXQgcmlkIG9mIHRoZSA7XHJcbiAgICBpZiAoc3RySW5qW3N0ckluai5sZW5ndGggLSAxXSA9PT0gJzsnKSB7XHJcbiAgICAgIHN0ckluaiA9IHN0ckluai5zdWJzdHJpbmcoMCwgc3RySW5qLmxlbmd0aCAtIDEpO1xyXG4gICAgfVxyXG5cclxuICAgIC8vIFNhdmUgYXMgc3RyaWdodCBpbmplY3Qgc3RyaW5nXHJcbiAgICBvcHRpb25zLmV4cG9ydC5zdHJJbmogPSBzdHJJbmo7XHJcbiAgICByZXR1cm4gZG9FeHBvcnQob3B0aW9ucywgZmFsc2UsIGVuZENhbGxiYWNrKTtcclxuICB9IGNhdGNoIChlcnJvcikge1xyXG4gICAgcmV0dXJuIGVuZENhbGxiYWNrKFxyXG4gICAgICBuZXcgRXhwb3J0RXJyb3IoXHJcbiAgICAgICAgYFtjaGFydF0gTWFsZm9ybWVkIGlucHV0IGRldGVjdGVkIGZvciAke29wdGlvbnMuZXhwb3J0Py5yZXF1ZXN0SWQgfHwgJz8nfS4gUGxlYXNlIG1ha2Ugc3VyZSB0aGF0IHlvdXIgSlNPTi9KYXZhU2NyaXB0IG9wdGlvbnMgYXJlIHNlbnQgdXNpbmcgdGhlIFwib3B0aW9uc1wiIGF0dHJpYnV0ZSwgYW5kIHRoYXQgaWYgeW91J3JlIHVzaW5nIFNWRywgaXQgaXMgdW5lc2NhcGVkLmBcclxuICAgICAgKS5zZXRFcnJvcihlcnJvcilcclxuICAgICk7XHJcbiAgfVxyXG59O1xyXG5cclxuLyoqXHJcbiAqIEV4cG9ydHMgYSBzdHJpbmcgYmFzZWQgb24gdGhlIHByb3ZpZGVkIG9wdGlvbnMgYW5kIGludm9rZXMgYW4gZW5kIGNhbGxiYWNrLlxyXG4gKlxyXG4gKiBAcGFyYW0ge3N0cmluZ30gc3RyaW5nVG9FeHBvcnQgLSBUaGUgc3RyaW5nIGNvbnRlbnQgdG8gYmUgZXhwb3J0ZWQuXHJcbiAqIEBwYXJhbSB7T2JqZWN0fSBvcHRpb25zIC0gRXhwb3J0IG9wdGlvbnMsIGluY2x1ZGluZyBjdXN0b21Mb2dpYyB3aXRoXHJcbiAqIGFsbG93Q29kZUV4ZWN1dGlvbiBmbGFnLlxyXG4gKiBAcGFyYW0ge0Z1bmN0aW9ufSBlbmRDYWxsYmFjayAtIENhbGxiYWNrIGZ1bmN0aW9uIHRvIGJlIGludm9rZWQgYXQgdGhlIGVuZFxyXG4gKiBvZiB0aGUgZXhwb3J0IHByb2Nlc3MuXHJcbiAqXHJcbiAqIEByZXR1cm5zIHthbnl9IFJlc3VsdCBvZiB0aGUgZXhwb3J0IHByb2Nlc3Mgb3IgYW4gZXJyb3IgaWYgZW5jb3VudGVyZWQuXHJcbiAqL1xyXG5jb25zdCBleHBvcnRBc1N0cmluZyA9IChzdHJpbmdUb0V4cG9ydCwgb3B0aW9ucywgZW5kQ2FsbGJhY2spID0+IHtcclxuICBjb25zdCB7IGFsbG93Q29kZUV4ZWN1dGlvbiB9ID0gb3B0aW9ucy5jdXN0b21Mb2dpYztcclxuXHJcbiAgLy8gQ2hlY2sgaWYgaXQgaXMgU1ZHXHJcbiAgaWYgKFxyXG4gICAgc3RyaW5nVG9FeHBvcnQuaW5kZXhPZignPHN2ZycpID49IDAgfHxcclxuICAgIHN0cmluZ1RvRXhwb3J0LmluZGV4T2YoJzw/eG1sJykgPj0gMFxyXG4gICkge1xyXG4gICAgbG9nKDQsICdbY2hhcnRdIFBhcnNpbmcgaW5wdXQgYXMgU1ZHLicpO1xyXG4gICAgcmV0dXJuIGRvRXhwb3J0KG9wdGlvbnMsIGZhbHNlLCBlbmRDYWxsYmFjaywgc3RyaW5nVG9FeHBvcnQpO1xyXG4gIH1cclxuXHJcbiAgdHJ5IHtcclxuICAgIC8vIFRyeSB0byBwYXJzZSB0byBKU09OIGFuZCBjYWxsIHRoZSBkb0V4cG9ydCBmdW5jdGlvblxyXG4gICAgY29uc3QgY2hhcnRKU09OID0gSlNPTi5wYXJzZShzdHJpbmdUb0V4cG9ydC5yZXBsYWNlQWxsKC9cXHR8XFxufFxcci9nLCAnICcpKTtcclxuXHJcbiAgICAvLyBJZiBhIGNvcnJlY3QgSlNPTiwgZG8gdGhlIGV4cG9ydFxyXG4gICAgcmV0dXJuIGRvRXhwb3J0KG9wdGlvbnMsIGNoYXJ0SlNPTiwgZW5kQ2FsbGJhY2spO1xyXG4gIH0gY2F0Y2ggKGVycm9yKSB7XHJcbiAgICAvLyBOb3QgYSB2YWxpZCBKU09OXHJcbiAgICBpZiAodG9Cb29sZWFuKGFsbG93Q29kZUV4ZWN1dGlvbikpIHtcclxuICAgICAgcmV0dXJuIGRvU3RyYWlnaHRJbmplY3Qob3B0aW9ucywgZW5kQ2FsbGJhY2spO1xyXG4gICAgfSBlbHNlIHtcclxuICAgICAgLy8gRG8gbm90IGFsbG93IHN0cmFpZ2h0IGluamVjdGlvbiB3aXRob3V0IHRoZSBhbGxvd0NvZGVFeGVjdXRpb24gZmxhZ1xyXG4gICAgICByZXR1cm4gZW5kQ2FsbGJhY2soXHJcbiAgICAgICAgbmV3IEV4cG9ydEVycm9yKFxyXG4gICAgICAgICAgJ1tjaGFydF0gT25seSBKU09OIGNvbmZpZ3VyYXRpb25zIGFuZCBTVkcgYXJlIGFsbG93ZWQgZm9yIHRoaXMgc2VydmVyLiBJZiB0aGlzIGlzIHlvdXIgc2VydmVyLCBKYXZhU2NyaXB0IGN1c3RvbSBjb2RlIGNhbiBiZSBlbmFibGVkIGJ5IHN0YXJ0aW5nIHRoZSBzZXJ2ZXIgd2l0aCB0aGUgLS1hbGxvd0NvZGVFeGVjdXRpb24gZmxhZy4nXHJcbiAgICAgICAgKS5zZXRFcnJvcihlcnJvcilcclxuICAgICAgKTtcclxuICAgIH1cclxuICB9XHJcbn07XHJcblxyXG4vKipcclxuICogUmV0cmlldmVzIGFuZCByZXR1cm5zIHRoZSBjdXJyZW50IHN0YXR1cyBvZiBjb2RlIGV4ZWN1dGlvbiBwZXJtaXNzaW9uLlxyXG4gKlxyXG4gKiBAcmV0dXJucyB7YW55fSBUaGUgdmFsdWUgb2YgYWxsb3dDb2RlRXhlY3V0aW9uLlxyXG4gKi9cclxuZXhwb3J0IGNvbnN0IGdldEFsbG93Q29kZUV4ZWN1dGlvbiA9ICgpID0+IGFsbG93Q29kZUV4ZWN1dGlvbjtcclxuXHJcbi8qKlxyXG4gKiBTZXRzIHRoZSBjb2RlIGV4ZWN1dGlvbiBwZXJtaXNzaW9uIGJhc2VkIG9uIHRoZSBwcm92aWRlZCBib29sZWFuIHZhbHVlLlxyXG4gKlxyXG4gKiBAcGFyYW0ge2FueX0gdmFsdWUgLSBUaGUgdmFsdWUgdG8gYmUgY29udmVydGVkIGFuZCBhc3NpZ25lZFxyXG4gKiB0byBhbGxvd0NvZGVFeGVjdXRpb24uXHJcbiAqL1xyXG5leHBvcnQgY29uc3Qgc2V0QWxsb3dDb2RlRXhlY3V0aW9uID0gKHZhbHVlKSA9PiB7XHJcbiAgYWxsb3dDb2RlRXhlY3V0aW9uID0gdG9Cb29sZWFuKHZhbHVlKTtcclxufTtcclxuXHJcbmV4cG9ydCBkZWZhdWx0IHtcclxuICBiYXRjaEV4cG9ydCxcclxuICBzaW5nbGVFeHBvcnQsXHJcbiAgZ2V0QWxsb3dDb2RlRXhlY3V0aW9uLFxyXG4gIHNldEFsbG93Q29kZUV4ZWN1dGlvbixcclxuICBzdGFydEV4cG9ydCxcclxuICBmaW5kQ2hhcnRTaXplXHJcbn07XHJcbiIsIi8qKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqXHJcblxyXG5IaWdoY2hhcnRzIEV4cG9ydCBTZXJ2ZXJcclxuXHJcbkNvcHlyaWdodCAoYykgMjAxNi0yMDI0LCBIaWdoc29mdFxyXG5cclxuTGljZW5jZWQgdW5kZXIgdGhlIE1JVCBsaWNlbmNlLlxyXG5cclxuQWRkaXRpb25hbGx5IGEgdmFsaWQgSGlnaGNoYXJ0cyBsaWNlbnNlIGlzIHJlcXVpcmVkIGZvciB1c2UuXHJcblxyXG5TZWUgTElDRU5TRSBmaWxlIGluIHJvb3QgZm9yIGRldGFpbHMuXHJcblxyXG4qKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqL1xyXG5cclxuLyoqXHJcbiAqIEBvdmVydmlldyBVc2VkIHRvIHNhbml0aXplIHRoZSBzdHJpbmdzIGNvbWluZyBmcm9tIHRoZSBleHBvcnRpbmcgbW9kdWxlXHJcbiAqIHRvIHByZXZlbnQgWFNTIGF0dGFja3MgKHdpdGggdGhlIERPTVB1cmlmeSBsaWJyYXJ5KS5cclxuICoqL1xyXG5cclxuaW1wb3J0IHsgSlNET00gfSBmcm9tICdqc2RvbSc7XHJcbmltcG9ydCBET01QdXJpZnkgZnJvbSAnZG9tcHVyaWZ5JztcclxuXHJcbi8qKlxyXG4gKiBTYW5pdGl6ZXMgYSBnaXZlbiBIVE1MIHN0cmluZyBieSByZW1vdmluZyA8c2NyaXB0PiB0YWdzLlxyXG4gKiBUaGlzIGZ1bmN0aW9uIHVzZXMgYSByZWd1bGFyIGV4cHJlc3Npb24gdG8gZmluZCBhbmQgcmVtb3ZlIGFsbFxyXG4gKiBvY2N1cnJlbmNlcyBvZiA8c2NyaXB0Pi4uLjwvc2NyaXB0PiB0YWdzIGFuZCBhbnkgY29udGVudCB3aXRoaW4gdGhlbS5cclxuICpcclxuICogQHBhcmFtIHtzdHJpbmd9IGlucHV0IFRoZSBIVE1MIHN0cmluZyB0byBiZSBzYW5pdGl6ZWQuXHJcbiAqIEByZXR1cm5zIHtzdHJpbmd9IFRoZSBzYW5pdGl6ZWQgSFRNTCBzdHJpbmcuXHJcbiAqL1xyXG5leHBvcnQgZnVuY3Rpb24gc2FuaXRpemUoaW5wdXQpIHtcclxuICBjb25zdCB3aW5kb3cgPSBuZXcgSlNET00oJycpLndpbmRvdztcclxuICBjb25zdCBwdXJpZnkgPSBET01QdXJpZnkod2luZG93KTtcclxuICByZXR1cm4gcHVyaWZ5LnNhbml0aXplKGlucHV0LCB7IEFERF9UQUdTOiBbJ2ZvcmVpZ25PYmplY3QnXSB9KTtcclxufVxyXG5cclxuZXhwb3J0IGRlZmF1bHQgc2FuaXRpemU7XHJcbiIsIi8qKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqXHJcblxyXG5IaWdoY2hhcnRzIEV4cG9ydCBTZXJ2ZXJcclxuXHJcbkNvcHlyaWdodCAoYykgMjAxNi0yMDI0LCBIaWdoc29mdFxyXG5cclxuTGljZW5jZWQgdW5kZXIgdGhlIE1JVCBsaWNlbmNlLlxyXG5cclxuQWRkaXRpb25hbGx5IGEgdmFsaWQgSGlnaGNoYXJ0cyBsaWNlbnNlIGlzIHJlcXVpcmVkIGZvciB1c2UuXHJcblxyXG5TZWUgTElDRU5TRSBmaWxlIGluIHJvb3QgZm9yIGRldGFpbHMuXHJcblxyXG4qKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqL1xyXG5cclxuaW1wb3J0IHsgbG9nIH0gZnJvbSAnLi9sb2dnZXIuanMnO1xyXG5cclxuLy8gQXJyYXkgdGhhdCBjb250YWlucyBpZHMgb2YgYWxsIG9uZ29pbmcgaW50ZXJ2YWxzXHJcbmNvbnN0IGludGVydmFsSWRzID0gW107XHJcblxyXG4vKipcclxuICogQWRkcyBpZCBvZiBhIHNldEludGVydmFsIHRvIHRoZSBpbnRlcnZhbElkcyBhcnJheS5cclxuICpcclxuICogQHBhcmFtIHtOb2RlSlMuVGltZW91dH0gaWQgLSBJZCBvZiBhbiBpbnRlcnZhbC5cclxuICovXHJcbmV4cG9ydCBjb25zdCBhZGRJbnRlcnZhbCA9IChpZCkgPT4ge1xyXG4gIGludGVydmFsSWRzLnB1c2goaWQpO1xyXG59O1xyXG5cclxuLyoqXHJcbiAqIENsZWFycyBhbGwgb2Ygb25nb2luZyBpbnRlcnZhbHMgYnkgaWRzIGdhdGhlcmVkIGluIHRoZSBpbnRlcnZhbElkcyBhcnJheS5cclxuICovXHJcbmV4cG9ydCBjb25zdCBjbGVhckFsbEludGVydmFscyA9ICgpID0+IHtcclxuICBsb2coNCwgYFtzZXJ2ZXJdIENsZWFyaW5nIGFsbCByZWdpc3RlcmVkIGludGVydmFscy5gKTtcclxuICBmb3IgKGNvbnN0IGlkIG9mIGludGVydmFsSWRzKSB7XHJcbiAgICBjbGVhckludGVydmFsKGlkKTtcclxuICB9XHJcbn07XHJcblxyXG5leHBvcnQgZGVmYXVsdCB7XHJcbiAgYWRkSW50ZXJ2YWwsXHJcbiAgY2xlYXJBbGxJbnRlcnZhbHNcclxufTtcclxuIiwiaW1wb3J0IHsgZW52cyB9IGZyb20gJy4uL2VudnMuanMnO1xyXG5pbXBvcnQgeyBsb2dXaXRoU3RhY2sgfSBmcm9tICcuLi9sb2dnZXIuanMnO1xyXG5cclxuLyoqXHJcbiAqIE1pZGRsZXdhcmUgZm9yIGxvZ2dpbmcgZXJyb3JzIHdpdGggc3RhY2sgdHJhY2UgYW5kIGhhbmRsaW5nIGVycm9yIHJlc3BvbnNlLlxyXG4gKlxyXG4gKiBAcGFyYW0ge0Vycm9yfSBlcnJvciAtIFRoZSBlcnJvciBvYmplY3QuXHJcbiAqIEBwYXJhbSB7RXhwcmVzcy5SZXF1ZXN0fSByZXEgLSBUaGUgRXhwcmVzcyByZXF1ZXN0IG9iamVjdC5cclxuICogQHBhcmFtIHtFeHByZXNzLlJlc3BvbnNlfSByZXMgLSBUaGUgRXhwcmVzcyByZXNwb25zZSBvYmplY3QuXHJcbiAqIEBwYXJhbSB7RnVuY3Rpb259IG5leHQgLSBUaGUgbmV4dCBtaWRkbGV3YXJlIGZ1bmN0aW9uLlxyXG4gKi9cclxuY29uc3QgbG9nRXJyb3JNaWRkbGV3YXJlID0gKGVycm9yLCByZXEsIHJlcywgbmV4dCkgPT4ge1xyXG4gIC8vIERpc3BsYXkgdGhlIGVycm9yIHdpdGggc3RhY2sgaW4gYSBjb3JyZWN0IGZvcm1hdFxyXG4gIGxvZ1dpdGhTdGFjaygxLCBlcnJvcik7XHJcblxyXG4gIC8vIERlbGV0ZSB0aGUgc3RhY2sgZm9yIHRoZSBlbnZpcm9ubWVudCBvdGhlciB0aGFuIHRoZSBkZXZlbG9wbWVudFxyXG4gIGlmIChlbnZzLk9USEVSX05PREVfRU5WICE9PSAnZGV2ZWxvcG1lbnQnKSB7XHJcbiAgICBkZWxldGUgZXJyb3Iuc3RhY2s7XHJcbiAgfVxyXG5cclxuICAvLyBDYWxsIHRoZSByZXR1cm5FcnJvck1pZGRsZXdhcmVcclxuICBuZXh0KGVycm9yKTtcclxufTtcclxuXHJcbi8qKlxyXG4gKiBNaWRkbGV3YXJlIGZvciByZXR1cm5pbmcgZXJyb3IgcmVzcG9uc2UuXHJcbiAqXHJcbiAqIEBwYXJhbSB7RXJyb3J9IGVycm9yIC0gVGhlIGVycm9yIG9iamVjdC5cclxuICogQHBhcmFtIHtFeHByZXNzLlJlcXVlc3R9IHJlcSAtIFRoZSBFeHByZXNzIHJlcXVlc3Qgb2JqZWN0LlxyXG4gKiBAcGFyYW0ge0V4cHJlc3MuUmVzcG9uc2V9IHJlcyAtIFRoZSBFeHByZXNzIHJlc3BvbnNlIG9iamVjdC5cclxuICogQHBhcmFtIHtGdW5jdGlvbn0gbmV4dCAtIFRoZSBuZXh0IG1pZGRsZXdhcmUgZnVuY3Rpb24uXHJcbiAqL1xyXG5jb25zdCByZXR1cm5FcnJvck1pZGRsZXdhcmUgPSAoZXJyb3IsIHJlcSwgcmVzLCBuZXh0KSA9PiB7XHJcbiAgLy8gR2F0aGVyIGFsbCByZXF1aWVkIGluZm9ybWF0aW9uIGZvciB0aGUgcmVzcG9uc2VcclxuICBjb25zdCB7IHN0YXR1c0NvZGU6IHN0Q29kZSwgc3RhdHVzLCBtZXNzYWdlLCBzdGFjayB9ID0gZXJyb3I7XHJcbiAgY29uc3Qgc3RhdHVzQ29kZSA9IHN0Q29kZSB8fCBzdGF0dXMgfHwgNTAwO1xyXG5cclxuICAvLyBTZXQgYW5kIHJldHVybiByZXNwb25zZVxyXG4gIHJlcy5zdGF0dXMoc3RhdHVzQ29kZSkuanNvbih7IHN0YXR1c0NvZGUsIG1lc3NhZ2UsIHN0YWNrIH0pO1xyXG59O1xyXG5cclxuZXhwb3J0IGRlZmF1bHQgKGFwcCkgPT4ge1xyXG4gIC8vIEFkZCBsb2cgZXJyb3IgbWlkZGxld2FyZVxyXG4gIGFwcC51c2UobG9nRXJyb3JNaWRkbGV3YXJlKTtcclxuXHJcbiAgLy8gQWRkIHNldCBzdGF0dXMgYW5kIHJldHVybiBlcnJvciBtaWRkbGV3YXJlXHJcbiAgYXBwLnVzZShyZXR1cm5FcnJvck1pZGRsZXdhcmUpO1xyXG59O1xyXG4iLCIvKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKlxyXG5cclxuSGlnaGNoYXJ0cyBFeHBvcnQgU2VydmVyXHJcblxyXG5Db3B5cmlnaHQgKGMpIDIwMTYtMjAyNCwgSGlnaHNvZnRcclxuXHJcbkxpY2VuY2VkIHVuZGVyIHRoZSBNSVQgbGljZW5jZS5cclxuXHJcbkFkZGl0aW9uYWxseSBhIHZhbGlkIEhpZ2hjaGFydHMgbGljZW5zZSBpcyByZXF1aXJlZCBmb3IgdXNlLlxyXG5cclxuU2VlIExJQ0VOU0UgZmlsZSBpbiByb290IGZvciBkZXRhaWxzLlxyXG5cclxuKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKi9cclxuXHJcbmltcG9ydCByYXRlTGltaXQgZnJvbSAnZXhwcmVzcy1yYXRlLWxpbWl0JztcclxuXHJcbmltcG9ydCB7IGxvZyB9IGZyb20gJy4uL2xvZ2dlci5qcyc7XHJcblxyXG4vKipcclxuICogTWlkZGxld2FyZSBmb3IgZW5hYmxpbmcgcmF0ZSBsaW1pdGluZyBvbiB0aGUgc3BlY2lmaWVkIEV4cHJlc3MgYXBwLlxyXG4gKlxyXG4gKiBAcGFyYW0ge0V4cHJlc3N9IGFwcCAtIFRoZSBFeHByZXNzIGFwcCBpbnN0YW5jZS5cclxuICogQHBhcmFtIHtPYmplY3R9IGxpbWl0Q29uZmlnIC0gQ29uZmlndXJhdGlvbiBvcHRpb25zIGZvciByYXRlIGxpbWl0aW5nLlxyXG4gKi9cclxuZXhwb3J0IGRlZmF1bHQgKGFwcCwgbGltaXRDb25maWcpID0+IHtcclxuICBjb25zdCBtc2cgPVxyXG4gICAgJ1RvbyBtYW55IHJlcXVlc3RzLCB5b3UgaGF2ZSBiZWVuIHJhdGUgbGltaXRlZC4gUGxlYXNlIHRyeSBhZ2FpbiBsYXRlci4nO1xyXG5cclxuICAvLyBPcHRpb25zIGZvciB0aGUgcmF0ZSBsaW1pdGVyXHJcbiAgY29uc3QgcmF0ZU9wdGlvbnMgPSB7XHJcbiAgICBtYXg6IGxpbWl0Q29uZmlnLm1heFJlcXVlc3RzIHx8IDMwLFxyXG4gICAgd2luZG93OiBsaW1pdENvbmZpZy53aW5kb3cgfHwgMSxcclxuICAgIGRlbGF5OiBsaW1pdENvbmZpZy5kZWxheSB8fCAwLFxyXG4gICAgdHJ1c3RQcm94eTogbGltaXRDb25maWcudHJ1c3RQcm94eSB8fCBmYWxzZSxcclxuICAgIHNraXBLZXk6IGxpbWl0Q29uZmlnLnNraXBLZXkgfHwgZmFsc2UsXHJcbiAgICBza2lwVG9rZW46IGxpbWl0Q29uZmlnLnNraXBUb2tlbiB8fCBmYWxzZVxyXG4gIH07XHJcblxyXG4gIC8vIFNldCBpZiBiZWhpbmQgYSBwcm94eVxyXG4gIGlmIChyYXRlT3B0aW9ucy50cnVzdFByb3h5KSB7XHJcbiAgICBhcHAuZW5hYmxlKCd0cnVzdCBwcm94eScpO1xyXG4gIH1cclxuXHJcbiAgLy8gQ3JlYXRlIGEgbGltaXRlclxyXG4gIGNvbnN0IGxpbWl0ZXIgPSByYXRlTGltaXQoe1xyXG4gICAgd2luZG93TXM6IHJhdGVPcHRpb25zLndpbmRvdyAqIDYwICogMTAwMCxcclxuICAgIC8vIExpbWl0IGVhY2ggSVAgdG8gMTAwIHJlcXVlc3RzIHBlciB3aW5kb3dNc1xyXG4gICAgbWF4OiByYXRlT3B0aW9ucy5tYXgsXHJcbiAgICAvLyBEaXNhYmxlIGRlbGF5aW5nLCBmdWxsIHNwZWVkIHVudGlsIHRoZSBtYXggbGltaXQgaXMgcmVhY2hlZFxyXG4gICAgZGVsYXlNczogcmF0ZU9wdGlvbnMuZGVsYXksXHJcbiAgICBoYW5kbGVyOiAocmVxdWVzdCwgcmVzcG9uc2UpID0+IHtcclxuICAgICAgcmVzcG9uc2UuZm9ybWF0KHtcclxuICAgICAgICBqc29uOiAoKSA9PiB7XHJcbiAgICAgICAgICByZXNwb25zZS5zdGF0dXMoNDI5KS5zZW5kKHsgbWVzc2FnZTogbXNnIH0pO1xyXG4gICAgICAgIH0sXHJcbiAgICAgICAgZGVmYXVsdDogKCkgPT4ge1xyXG4gICAgICAgICAgcmVzcG9uc2Uuc3RhdHVzKDQyOSkuc2VuZChtc2cpO1xyXG4gICAgICAgIH1cclxuICAgICAgfSk7XHJcbiAgICB9LFxyXG4gICAgc2tpcDogKHJlcXVlc3QpID0+IHtcclxuICAgICAgLy8gQWxsb3cgYnlwYXNzaW5nIHRoZSBsaW1pdGVyIGlmIGEgdmFsaWQga2V5L3Rva2VuIGhhcyBiZWVuIHNlbnRcclxuICAgICAgaWYgKFxyXG4gICAgICAgIHJhdGVPcHRpb25zLnNraXBLZXkgIT09IGZhbHNlICYmXHJcbiAgICAgICAgcmF0ZU9wdGlvbnMuc2tpcFRva2VuICE9PSBmYWxzZSAmJlxyXG4gICAgICAgIHJlcXVlc3QucXVlcnkua2V5ID09PSByYXRlT3B0aW9ucy5za2lwS2V5ICYmXHJcbiAgICAgICAgcmVxdWVzdC5xdWVyeS5hY2Nlc3NfdG9rZW4gPT09IHJhdGVPcHRpb25zLnNraXBUb2tlblxyXG4gICAgICApIHtcclxuICAgICAgICBsb2coNCwgJ1tyYXRlIGxpbWl0aW5nXSBTa2lwcGluZyByYXRlIGxpbWl0ZXIuJyk7XHJcbiAgICAgICAgcmV0dXJuIHRydWU7XHJcbiAgICAgIH1cclxuICAgICAgcmV0dXJuIGZhbHNlO1xyXG4gICAgfVxyXG4gIH0pO1xyXG5cclxuICAvLyBVc2UgYSBsaW1pdGVyIGFzIGEgbWlkZGxld2FyZVxyXG4gIGFwcC51c2UobGltaXRlcik7XHJcblxyXG4gIGxvZyhcclxuICAgIDMsXHJcbiAgICBgW3JhdGUgbGltaXRpbmddIEVuYWJsZWQgcmF0ZSBsaW1pdGluZyB3aXRoICR7cmF0ZU9wdGlvbnMubWF4fSByZXF1ZXN0cyBwZXIgJHtyYXRlT3B0aW9ucy53aW5kb3d9IG1pbnV0ZSBmb3IgZWFjaCBJUCwgdHJ1c3RpbmcgcHJveHk6ICR7cmF0ZU9wdGlvbnMudHJ1c3RQcm94eX0uYFxyXG4gICk7XHJcbn07XHJcbiIsImltcG9ydCBFeHBvcnRFcnJvciBmcm9tICcuL0V4cG9ydEVycm9yLmpzJztcclxuXHJcbmNsYXNzIEh0dHBFcnJvciBleHRlbmRzIEV4cG9ydEVycm9yIHtcclxuICBjb25zdHJ1Y3RvcihtZXNzYWdlLCBzdGF0dXMpIHtcclxuICAgIHN1cGVyKG1lc3NhZ2UpO1xyXG4gICAgdGhpcy5zdGF0dXMgPSB0aGlzLnN0YXR1c0NvZGUgPSBzdGF0dXM7XHJcbiAgfVxyXG5cclxuICBzZXRTdGF0dXMoc3RhdHVzKSB7XHJcbiAgICB0aGlzLnN0YXR1cyA9IHN0YXR1cztcclxuICAgIHJldHVybiB0aGlzO1xyXG4gIH1cclxufVxyXG5cclxuZXhwb3J0IGRlZmF1bHQgSHR0cEVycm9yO1xyXG4iLCIvKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKlxyXG5cclxuSGlnaGNoYXJ0cyBFeHBvcnQgU2VydmVyXHJcblxyXG5Db3B5cmlnaHQgKGMpIDIwMTYtMjAyNCwgSGlnaHNvZnRcclxuXHJcbkxpY2VuY2VkIHVuZGVyIHRoZSBNSVQgbGljZW5jZS5cclxuXHJcbkFkZGl0aW9uYWxseSBhIHZhbGlkIEhpZ2hjaGFydHMgbGljZW5zZSBpcyByZXF1aXJlZCBmb3IgdXNlLlxyXG5cclxuU2VlIExJQ0VOU0UgZmlsZSBpbiByb290IGZvciBkZXRhaWxzLlxyXG5cclxuKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKi9cclxuXHJcbmltcG9ydCB7IHY0IGFzIHV1aWQgfSBmcm9tICd1dWlkJztcclxuXHJcbmltcG9ydCB7IGdldEFsbG93Q29kZUV4ZWN1dGlvbiwgc3RhcnRFeHBvcnQgfSBmcm9tICcuLi8uLi9jaGFydC5qcyc7XHJcbmltcG9ydCB7IGdldE9wdGlvbnMsIG1lcmdlQ29uZmlnT3B0aW9ucyB9IGZyb20gJy4uLy4uL2NvbmZpZy5qcyc7XHJcbmltcG9ydCB7IGxvZyB9IGZyb20gJy4uLy4uL2xvZ2dlci5qcyc7XHJcbmltcG9ydCB7XHJcbiAgZml4VHlwZSxcclxuICBpc0NvcnJlY3RKU09OLFxyXG4gIGlzT2JqZWN0RW1wdHksXHJcbiAgaXNQcml2YXRlUmFuZ2VVcmxGb3VuZCxcclxuICBvcHRpb25zU3RyaW5naWZ5LFxyXG4gIG1lYXN1cmVUaW1lXHJcbn0gZnJvbSAnLi4vLi4vdXRpbHMuanMnO1xyXG5cclxuaW1wb3J0IEh0dHBFcnJvciBmcm9tICcuLi8uLi9lcnJvcnMvSHR0cEVycm9yLmpzJztcclxuXHJcbi8vIFJldmVyc2VkIE1JTUUgdHlwZXNcclxuY29uc3QgcmV2ZXJzZWRNaW1lID0ge1xyXG4gIHBuZzogJ2ltYWdlL3BuZycsXHJcbiAganBlZzogJ2ltYWdlL2pwZWcnLFxyXG4gIGdpZjogJ2ltYWdlL2dpZicsXHJcbiAgcGRmOiAnYXBwbGljYXRpb24vcGRmJyxcclxuICBzdmc6ICdpbWFnZS9zdmcreG1sJ1xyXG59O1xyXG5cclxuLy8gVGhlIHJlcXVlc3RzIGNvdW50ZXJcclxubGV0IHJlcXVlc3RzQ291bnRlciA9IDA7XHJcblxyXG4vLyBUaGUgYXJyYXkgb2YgY2FsbGJhY2tzIHRvIGNhbGwgYmVmb3JlIGEgcmVxdWVzdFxyXG5jb25zdCBiZWZvcmVSZXF1ZXN0ID0gW107XHJcblxyXG4vLyBUaGUgYXJyYXkgb2YgY2FsbGJhY2tzIHRvIGNhbGwgYWZ0ZXIgYSByZXF1ZXN0XHJcbmNvbnN0IGFmdGVyUmVxdWVzdCA9IFtdO1xyXG5cclxuLyoqXHJcbiAqIEludm9rZXMgYW4gYXJyYXkgb2YgY2FsbGJhY2sgZnVuY3Rpb25zIHdpdGggc3BlY2lmaWVkIHBhcmFtZXRlcnMsIGFsbG93aW5nXHJcbiAqIGN1c3RvbWl6YXRpb24gb2YgcmVxdWVzdCBoYW5kbGluZy5cclxuICpcclxuICogQHBhcmFtIHtGdW5jdGlvbltdfSBjYWxsYmFja3MgLSBBbiBhcnJheSBvZiBjYWxsYmFjayBmdW5jdGlvbnNcclxuICogdG8gYmUgZXhlY3V0ZWQuXHJcbiAqIEBwYXJhbSB7RXhwcmVzcy5SZXF1ZXN0fSByZXF1ZXN0IC0gVGhlIEV4cHJlc3MgcmVxdWVzdCBvYmplY3QuXHJcbiAqIEBwYXJhbSB7RXhwcmVzcy5SZXNwb25zZX0gcmVzcG9uc2UgLSBUaGUgRXhwcmVzcyByZXNwb25zZSBvYmplY3QuXHJcbiAqIEBwYXJhbSB7T2JqZWN0fSBkYXRhIC0gQW4gb2JqZWN0IGNvbnRhaW5pbmcgcGFyYW1ldGVycyBsaWtlIGlkLCB1bmlxdWVJZCxcclxuICogdHlwZSwgYW5kIGJvZHkuXHJcbiAqXHJcbiAqIEByZXR1cm5zIHtib29sZWFufSAtIFJldHVybnMgYSBib29sZWFuIGluZGljYXRpbmcgdGhlIG92ZXJhbGwgcmVzdWx0XHJcbiAqIG9mIHRoZSBjYWxsYmFjayBpbnZvY2F0aW9ucy5cclxuICovXHJcbmNvbnN0IGRvQ2FsbGJhY2tzID0gKGNhbGxiYWNrcywgcmVxdWVzdCwgcmVzcG9uc2UsIGRhdGEpID0+IHtcclxuICBsZXQgcmVzdWx0ID0gdHJ1ZTtcclxuICBjb25zdCB7IGlkLCB1bmlxdWVJZCwgdHlwZSwgYm9keSB9ID0gZGF0YTtcclxuXHJcbiAgY2FsbGJhY2tzLnNvbWUoKGNhbGxiYWNrKSA9PiB7XHJcbiAgICBpZiAoY2FsbGJhY2spIHtcclxuICAgICAgbGV0IGNhbGxSZXNwb25zZSA9IGNhbGxiYWNrKHJlcXVlc3QsIHJlc3BvbnNlLCBpZCwgdW5pcXVlSWQsIHR5cGUsIGJvZHkpO1xyXG5cclxuICAgICAgaWYgKGNhbGxSZXNwb25zZSAhPT0gdW5kZWZpbmVkICYmIGNhbGxSZXNwb25zZSAhPT0gdHJ1ZSkge1xyXG4gICAgICAgIHJlc3VsdCA9IGNhbGxSZXNwb25zZTtcclxuICAgICAgfVxyXG5cclxuICAgICAgcmV0dXJuIHRydWU7XHJcbiAgICB9XHJcbiAgfSk7XHJcblxyXG4gIHJldHVybiByZXN1bHQ7XHJcbn07XHJcblxyXG4vKipcclxuICogSGFuZGxlcyB0aGUgZXhwb3J0IHJlcXVlc3RzIGZyb20gdGhlIGNsaWVudC5cclxuICpcclxuICogQHBhcmFtIHtFeHByZXNzLlJlcXVlc3R9IHJlcXVlc3QgLSBUaGUgRXhwcmVzcyByZXF1ZXN0IG9iamVjdC5cclxuICogQHBhcmFtIHtFeHByZXNzLlJlc3BvbnNlfSByZXNwb25zZSAtIFRoZSBFeHByZXNzIHJlc3BvbnNlIG9iamVjdC5cclxuICogQHBhcmFtIHtGdW5jdGlvbn0gbmV4dCAtIFRoZSBuZXh0IG1pZGRsZXdhcmUgZnVuY3Rpb24uXHJcbiAqXHJcbiAqIEByZXR1cm5zIHtQcm9taXNlPHZvaWQ+fSAtIEEgcHJvbWlzZSB0aGF0IHJlc29sdmVzIG9uY2UgdGhlIGV4cG9ydCBwcm9jZXNzXHJcbiAqIGlzIGNvbXBsZXRlLlxyXG4gKi9cclxuY29uc3QgZXhwb3J0SGFuZGxlciA9IGFzeW5jIChyZXF1ZXN0LCByZXNwb25zZSwgbmV4dCkgPT4ge1xyXG4gIHRyeSB7XHJcbiAgICAvLyBTdGFydCBjb3VudGluZyB0aW1lXHJcbiAgICBjb25zdCBzdG9wQ291bnRlciA9IG1lYXN1cmVUaW1lKCk7XHJcblxyXG4gICAgLy8gQ3JlYXRlIGEgdW5pcXVlIElEIGZvciBhIHJlcXVlc3RcclxuICAgIGNvbnN0IHVuaXF1ZUlkID0gdXVpZCgpLnJlcGxhY2UoLy0vZywgJycpO1xyXG5cclxuICAgIC8vIEdldCB0aGUgY3VycmVudCBzZXJ2ZXIncyBnZW5lcmFsIG9wdGlvbnNcclxuICAgIGNvbnN0IGRlZmF1bHRPcHRpb25zID0gZ2V0T3B0aW9ucygpO1xyXG5cclxuICAgIGNvbnN0IGJvZHkgPSByZXF1ZXN0LmJvZHk7XHJcbiAgICBjb25zdCBpZCA9ICsrcmVxdWVzdHNDb3VudGVyO1xyXG5cclxuICAgIGxldCB0eXBlID0gZml4VHlwZShib2R5LnR5cGUpO1xyXG5cclxuICAgIC8vIFRocm93ICdCYWQgUmVxdWVzdCcgaWYgdGhlcmUncyBubyBib2R5XHJcbiAgICBpZiAoIWJvZHkgfHwgaXNPYmplY3RFbXB0eShib2R5KSkge1xyXG4gICAgICB0aHJvdyBuZXcgSHR0cEVycm9yKFxyXG4gICAgICAgICdUaGUgcmVxdWVzdCBib2R5IGlzIHJlcXVpcmVkLiBQbGVhc2UgZW5zdXJlIHRoYXQgeW91ciBDb250ZW50LVR5cGUgaGVhZGVyIGlzIGNvcnJlY3QgKGFjY2VwdGVkIHR5cGVzIGFyZSBhcHBsaWNhdGlvbi9qc29uIGFuZCBtdWx0aXBhcnQvZm9ybS1kYXRhKS4nLFxyXG4gICAgICAgIDQwMFxyXG4gICAgICApO1xyXG4gICAgfVxyXG5cclxuICAgIC8vIEFsbCBvZiB0aGUgYmVsb3cgY2FuIGJlIHVzZWRcclxuICAgIGxldCBpbnN0ciA9IGlzQ29ycmVjdEpTT04oYm9keS5pbmZpbGUgfHwgYm9keS5vcHRpb25zIHx8IGJvZHkuZGF0YSk7XHJcblxyXG4gICAgLy8gVGhyb3cgJ0JhZCBSZXF1ZXN0JyBpZiB0aGVyZSdzIG5vIEpTT04gb3IgU1ZHIHRvIGV4cG9ydFxyXG4gICAgaWYgKCFpbnN0ciAmJiAhYm9keS5zdmcpIHtcclxuICAgICAgbG9nKFxyXG4gICAgICAgIDIsXHJcbiAgICAgICAgYFRoZSByZXF1ZXN0IHdpdGggSUQgJHt1bmlxdWVJZH0gZnJvbSAke1xyXG4gICAgICAgICAgcmVxdWVzdC5oZWFkZXJzWyd4LWZvcndhcmRlZC1mb3InXSB8fCByZXF1ZXN0LmNvbm5lY3Rpb24ucmVtb3RlQWRkcmVzc1xyXG4gICAgICAgIH0gd2FzIGluY29ycmVjdC4gUGF5bG9hZCByZWNlaXZlZDogJHtKU09OLnN0cmluZ2lmeShib2R5KX0uYFxyXG4gICAgICApO1xyXG5cclxuICAgICAgdGhyb3cgbmV3IEh0dHBFcnJvcihcclxuICAgICAgICBcIk5vIGNvcnJlY3QgY2hhcnQgZGF0YSBmb3VuZC4gRW5zdXJlIHRoYXQgeW91IGFyZSB1c2luZyBlaXRoZXIgYXBwbGljYXRpb24vanNvbiBvciBtdWx0aXBhcnQvZm9ybS1kYXRhIGhlYWRlcnMuIElmIHNlbmRpbmcgSlNPTiwgbWFrZSBzdXJlIHRoZSBjaGFydCBkYXRhIGlzIGluIHRoZSAnaW5maWxlJywgJ29wdGlvbnMnLCBvciAnZGF0YScgYXR0cmlidXRlLiBJZiBzZW5kaW5nIFNWRywgZW5zdXJlIGl0IGlzIGluIHRoZSAnc3ZnJyBhdHRyaWJ1dGUuXCIsXHJcbiAgICAgICAgNDAwXHJcbiAgICAgICk7XHJcbiAgICB9XHJcblxyXG4gICAgbGV0IGNhbGxSZXNwb25zZSA9IGZhbHNlO1xyXG5cclxuICAgIC8vIENhbGwgdGhlIGJlZm9yZSByZXF1ZXN0IGZ1bmN0aW9uc1xyXG4gICAgY2FsbFJlc3BvbnNlID0gZG9DYWxsYmFja3MoYmVmb3JlUmVxdWVzdCwgcmVxdWVzdCwgcmVzcG9uc2UsIHtcclxuICAgICAgaWQsXHJcbiAgICAgIHVuaXF1ZUlkLFxyXG4gICAgICB0eXBlLFxyXG4gICAgICBib2R5XHJcbiAgICB9KTtcclxuXHJcbiAgICAvLyBCbG9jayB0aGUgcmVxdWVzdCBpZiBvbmUgb2YgYSBjYWxsYmFja3MgZmFpbGVkXHJcbiAgICBpZiAoY2FsbFJlc3BvbnNlICE9PSB0cnVlKSB7XHJcbiAgICAgIHJldHVybiByZXNwb25zZS5zZW5kKGNhbGxSZXNwb25zZSk7XHJcbiAgICB9XHJcblxyXG4gICAgbGV0IGNvbm5lY3Rpb25BYm9ydGVkID0gZmFsc2U7XHJcblxyXG4gICAgLy8gSW4gY2FzZSB0aGUgY29ubmVjdGlvbiBpcyBjbG9zZWQsIGZvcmNlIHRvIGFib3J0IGZ1cnRoZXIgYWN0aW9uc1xyXG4gICAgcmVxdWVzdC5zb2NrZXQub24oJ2Nsb3NlJywgKCkgPT4ge1xyXG4gICAgICBjb25uZWN0aW9uQWJvcnRlZCA9IHRydWU7XHJcbiAgICB9KTtcclxuXHJcbiAgICBsb2coNCwgYFtleHBvcnRdIEdvdCBhbiBpbmNvbWluZyBIVFRQIHJlcXVlc3Qgd2l0aCBJRCAke3VuaXF1ZUlkfS5gKTtcclxuXHJcbiAgICBib2R5LmNvbnN0ciA9ICh0eXBlb2YgYm9keS5jb25zdHIgPT09ICdzdHJpbmcnICYmIGJvZHkuY29uc3RyKSB8fCAnY2hhcnQnO1xyXG5cclxuICAgIC8vIEdhdGhlciBhbmQgb3JnYW5pemUgb3B0aW9ucyBmcm9tIHRoZSBwYXlsb2FkXHJcbiAgICBjb25zdCByZXF1ZXN0T3B0aW9ucyA9IHtcclxuICAgICAgZXhwb3J0OiB7XHJcbiAgICAgICAgaW5zdHIsXHJcbiAgICAgICAgdHlwZSxcclxuICAgICAgICBjb25zdHI6IGJvZHkuY29uc3RyWzBdLnRvTG93ZXJDYXNlKCkgKyBib2R5LmNvbnN0ci5zdWJzdHIoMSksXHJcbiAgICAgICAgaGVpZ2h0OiBib2R5LmhlaWdodCxcclxuICAgICAgICB3aWR0aDogYm9keS53aWR0aCxcclxuICAgICAgICBzY2FsZTogYm9keS5zY2FsZSB8fCBkZWZhdWx0T3B0aW9ucy5leHBvcnQuc2NhbGUsXHJcbiAgICAgICAgZ2xvYmFsT3B0aW9uczogaXNDb3JyZWN0SlNPTihib2R5Lmdsb2JhbE9wdGlvbnMsIHRydWUpLFxyXG4gICAgICAgIHRoZW1lT3B0aW9uczogaXNDb3JyZWN0SlNPTihib2R5LnRoZW1lT3B0aW9ucywgdHJ1ZSlcclxuICAgICAgfSxcclxuICAgICAgY3VzdG9tTG9naWM6IHtcclxuICAgICAgICBhbGxvd0NvZGVFeGVjdXRpb246IGdldEFsbG93Q29kZUV4ZWN1dGlvbigpLFxyXG4gICAgICAgIGFsbG93RmlsZVJlc291cmNlczogZmFsc2UsXHJcbiAgICAgICAgcmVzb3VyY2VzOiBpc0NvcnJlY3RKU09OKGJvZHkucmVzb3VyY2VzLCB0cnVlKSxcclxuICAgICAgICBjYWxsYmFjazogYm9keS5jYWxsYmFjayxcclxuICAgICAgICBjdXN0b21Db2RlOiBib2R5LmN1c3RvbUNvZGVcclxuICAgICAgfVxyXG4gICAgfTtcclxuXHJcbiAgICBpZiAoaW5zdHIpIHtcclxuICAgICAgLy8gU3RyaW5naWZ5IEpTT04gd2l0aCBvcHRpb25zXHJcbiAgICAgIHJlcXVlc3RPcHRpb25zLmV4cG9ydC5pbnN0ciA9IG9wdGlvbnNTdHJpbmdpZnkoXHJcbiAgICAgICAgaW5zdHIsXHJcbiAgICAgICAgcmVxdWVzdE9wdGlvbnMuY3VzdG9tTG9naWMuYWxsb3dDb2RlRXhlY3V0aW9uXHJcbiAgICAgICk7XHJcbiAgICB9XHJcblxyXG4gICAgLy8gTWVyZ2UgdGhlIHJlcXVlc3Qgb3B0aW9ucyBpbnRvIGRlZmF1bHQgb25lc1xyXG4gICAgY29uc3Qgb3B0aW9ucyA9IG1lcmdlQ29uZmlnT3B0aW9ucyhkZWZhdWx0T3B0aW9ucywgcmVxdWVzdE9wdGlvbnMpO1xyXG5cclxuICAgIC8vIFNhdmUgdGhlIEpTT04gaWYgZXhpc3RzXHJcbiAgICBvcHRpb25zLmV4cG9ydC5vcHRpb25zID0gaW5zdHI7XHJcblxyXG4gICAgLy8gTGFzdGx5LCBhZGQgdGhlIHNlcnZlciBzcGVjaWZpYyBhcmd1bWVudHMgaW50byBvcHRpb25zIGFzIHBheWxvYWRcclxuICAgIG9wdGlvbnMucGF5bG9hZCA9IHtcclxuICAgICAgc3ZnOiBib2R5LnN2ZyB8fCBmYWxzZSxcclxuICAgICAgYjY0OiBib2R5LmI2NCB8fCBmYWxzZSxcclxuICAgICAgbm9Eb3dubG9hZDogYm9keS5ub0Rvd25sb2FkIHx8IGZhbHNlLFxyXG4gICAgICByZXF1ZXN0SWQ6IHVuaXF1ZUlkXHJcbiAgICB9O1xyXG5cclxuICAgIC8vIFRlc3QgeGxpbms6aHJlZiBlbGVtZW50cyBmcm9tIHBheWxvYWQncyBTVkdcclxuICAgIGlmIChib2R5LnN2ZyAmJiBpc1ByaXZhdGVSYW5nZVVybEZvdW5kKG9wdGlvbnMucGF5bG9hZC5zdmcpKSB7XHJcbiAgICAgIHRocm93IG5ldyBIdHRwRXJyb3IoXHJcbiAgICAgICAgJ1NWRyBwb3RlbnRpYWxseSBjb250YWluIGF0IGxlYXN0IG9uZSBmb3JiaWRkZW4gVVJMIGluIHhsaW5rOmhyZWYgZWxlbWVudC4gUGxlYXNlIHJldmlldyB0aGUgU1ZHIGNvbnRlbnQgYW5kIGVuc3VyZSB0aGF0IGFsbCByZWZlcmVuY2VkIFVSTHMgY29tcGx5IHdpdGggc2VjdXJpdHkgcG9saWNpZXMuJyxcclxuICAgICAgICA0MDBcclxuICAgICAgKTtcclxuICAgIH1cclxuXHJcbiAgICAvLyBTdGFydCB0aGUgZXhwb3J0IHByb2Nlc3NcclxuICAgIGF3YWl0IHN0YXJ0RXhwb3J0KG9wdGlvbnMsIChlcnJvciwgaW5mbykgPT4ge1xyXG4gICAgICAvLyBSZW1vdmUgdGhlIGNsb3NlIGV2ZW50IGZyb20gdGhlIHNvY2tldFxyXG4gICAgICByZXF1ZXN0LnNvY2tldC5yZW1vdmVBbGxMaXN0ZW5lcnMoJ2Nsb3NlJyk7XHJcblxyXG4gICAgICAvLyBBZnRlciB0aGUgd2hvbGUgZXhwb3J0aW5nIHByb2Nlc3NcclxuICAgICAgaWYgKGRlZmF1bHRPcHRpb25zLnNlcnZlci5iZW5jaG1hcmtpbmcpIHtcclxuICAgICAgICBsb2coXHJcbiAgICAgICAgICA1LFxyXG4gICAgICAgICAgYFtiZW5jaG1hcmtdIFJlcXVlc3Qgd2l0aCBJRCAke3VuaXF1ZUlkfSAtIEFmdGVyIHRoZSB3aG9sZSBleHBvcnRpbmcgcHJvY2VzczogJHtzdG9wQ291bnRlcigpfW1zLmBcclxuICAgICAgICApO1xyXG4gICAgICB9XHJcblxyXG4gICAgICAvLyBJZiB0aGUgY29ubmVjdGlvbiB3YXMgY2xvc2VkLCBkbyBub3RoaW5nXHJcbiAgICAgIGlmIChjb25uZWN0aW9uQWJvcnRlZCkge1xyXG4gICAgICAgIHJldHVybiBsb2coXHJcbiAgICAgICAgICAzLFxyXG4gICAgICAgICAgYFtleHBvcnRdIFRoZSBjbGllbnQgY2xvc2VkIHRoZSBjb25uZWN0aW9uIGJlZm9yZSB0aGUgY2hhcnQgZmluaXNoZWQgcHJvY2Vzc2luZy5gXHJcbiAgICAgICAgKTtcclxuICAgICAgfVxyXG5cclxuICAgICAgLy8gSWYgZXJyb3IsIGxvZyBpdCBhbmQgc2VuZCBpdCB0byB0aGUgZXJyb3IgbWlkZGxld2FyZVxyXG4gICAgICBpZiAoZXJyb3IpIHtcclxuICAgICAgICB0aHJvdyBlcnJvcjtcclxuICAgICAgfVxyXG5cclxuICAgICAgLy8gSWYgZGF0YSBpcyBtaXNzaW5nLCBsb2cgdGhlIG1lc3NhZ2UgYW5kIHNlbmQgaXQgdG8gdGhlIGVycm9yIG1pZGRsZXdhcmVcclxuICAgICAgaWYgKCFpbmZvIHx8ICFpbmZvLnJlc3VsdCkge1xyXG4gICAgICAgIHRocm93IG5ldyBIdHRwRXJyb3IoXHJcbiAgICAgICAgICBgVW5leHBlY3RlZCByZXR1cm4gZnJvbSBjaGFydCBnZW5lcmF0aW9uLiBQbGVhc2UgY2hlY2sgeW91ciByZXF1ZXN0IGRhdGEuIEZvciB0aGUgcmVxdWVzdCB3aXRoIElEICR7dW5pcXVlSWR9LCB0aGUgcmVzdWx0IGlzICR7aW5mby5yZXN1bHR9LmAsXHJcbiAgICAgICAgICA0MDBcclxuICAgICAgICApO1xyXG4gICAgICB9XHJcblxyXG4gICAgICAvLyBHZXQgdGhlIHR5cGUgZnJvbSBvcHRpb25zXHJcbiAgICAgIHR5cGUgPSBpbmZvLm9wdGlvbnMuZXhwb3J0LnR5cGU7XHJcblxyXG4gICAgICAvLyBUaGUgYWZ0ZXIgcmVxdWVzdCBjYWxsYmFja3NcclxuICAgICAgZG9DYWxsYmFja3MoYWZ0ZXJSZXF1ZXN0LCByZXF1ZXN0LCByZXNwb25zZSwgeyBpZCwgYm9keTogaW5mby5yZXN1bHQgfSk7XHJcblxyXG4gICAgICBpZiAoaW5mby5yZXN1bHQpIHtcclxuICAgICAgICAvLyBJZiBvbmx5IGJhc2U2NCBpcyByZXF1aXJlZCwgcmV0dXJuIGl0XHJcbiAgICAgICAgaWYgKGJvZHkuYjY0KSB7XHJcbiAgICAgICAgICAvLyBTVkcgRXhjZXB0aW9uIGZvciB0aGUgSGlnaGNoYXJ0cyAxMS4zLjAgdmVyc2lvblxyXG4gICAgICAgICAgaWYgKHR5cGUgPT09ICdwZGYnIHx8IHR5cGUgPT0gJ3N2ZycpIHtcclxuICAgICAgICAgICAgcmV0dXJuIHJlc3BvbnNlLnNlbmQoXHJcbiAgICAgICAgICAgICAgQnVmZmVyLmZyb20oaW5mby5yZXN1bHQsICd1dGY4JykudG9TdHJpbmcoJ2Jhc2U2NCcpXHJcbiAgICAgICAgICAgICk7XHJcbiAgICAgICAgICB9XHJcblxyXG4gICAgICAgICAgcmV0dXJuIHJlc3BvbnNlLnNlbmQoaW5mby5yZXN1bHQpO1xyXG4gICAgICAgIH1cclxuXHJcbiAgICAgICAgLy8gU2V0IGNvcnJlY3QgY29udGVudCB0eXBlXHJcbiAgICAgICAgcmVzcG9uc2UuaGVhZGVyKCdDb250ZW50LVR5cGUnLCByZXZlcnNlZE1pbWVbdHlwZV0gfHwgJ2ltYWdlL3BuZycpO1xyXG5cclxuICAgICAgICAvLyBEZWNpZGUgd2hldGhlciB0byBkb3dubG9hZCBvciBub3QgY2hhcnQgZmlsZVxyXG4gICAgICAgIGlmICghYm9keS5ub0Rvd25sb2FkKSB7XHJcbiAgICAgICAgICByZXNwb25zZS5hdHRhY2htZW50KFxyXG4gICAgICAgICAgICBgJHtyZXF1ZXN0LnBhcmFtcy5maWxlbmFtZSB8fCByZXF1ZXN0LmJvZHkuZmlsZW5hbWUgfHwgJ2NoYXJ0J30uJHtcclxuICAgICAgICAgICAgICB0eXBlIHx8ICdwbmcnXHJcbiAgICAgICAgICAgIH1gXHJcbiAgICAgICAgICApO1xyXG4gICAgICAgIH1cclxuXHJcbiAgICAgICAgLy8gSWYgU1ZHLCByZXR1cm4gcGxhaW4gY29udGVudFxyXG4gICAgICAgIHJldHVybiB0eXBlID09PSAnc3ZnJ1xyXG4gICAgICAgICAgPyByZXNwb25zZS5zZW5kKGluZm8ucmVzdWx0KVxyXG4gICAgICAgICAgOiByZXNwb25zZS5zZW5kKEJ1ZmZlci5mcm9tKGluZm8ucmVzdWx0LCAnYmFzZTY0JykpO1xyXG4gICAgICB9XHJcbiAgICB9KTtcclxuICB9IGNhdGNoIChlcnJvcikge1xyXG4gICAgbmV4dChlcnJvcik7XHJcbiAgfVxyXG59O1xyXG5cclxuZXhwb3J0IGRlZmF1bHQgKGFwcCkgPT4ge1xyXG4gIC8qKlxyXG4gICAqIEFkZHMgdGhlIFBPU1QgLyBhIHJvdXRlIGZvciBoYW5kbGluZyBQT1NUIHJlcXVlc3RzIGF0IHRoZSByb290IGVuZHBvaW50LlxyXG4gICAqL1xyXG4gIGFwcC5wb3N0KCcvJywgZXhwb3J0SGFuZGxlcik7XHJcblxyXG4gIC8qKlxyXG4gICAqIEFkZHMgdGhlIFBPU1QgLzpmaWxlbmFtZSBhIHJvdXRlIGZvciBoYW5kbGluZyBQT1NUIHJlcXVlc3RzIHdpdGhcclxuICAgKiBhIHNwZWNpZmllZCBmaWxlbmFtZSBwYXJhbWV0ZXIuXHJcbiAgICovXHJcbiAgYXBwLnBvc3QoJy86ZmlsZW5hbWUnLCBleHBvcnRIYW5kbGVyKTtcclxufTtcclxuIiwiLyoqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKipcclxuXHJcbkhpZ2hjaGFydHMgRXhwb3J0IFNlcnZlclxyXG5cclxuQ29weXJpZ2h0IChjKSAyMDE2LTIwMjQsIEhpZ2hzb2Z0XHJcblxyXG5MaWNlbmNlZCB1bmRlciB0aGUgTUlUIGxpY2VuY2UuXHJcblxyXG5BZGRpdGlvbmFsbHkgYSB2YWxpZCBIaWdoY2hhcnRzIGxpY2Vuc2UgaXMgcmVxdWlyZWQgZm9yIHVzZS5cclxuXHJcblNlZSBMSUNFTlNFIGZpbGUgaW4gcm9vdCBmb3IgZGV0YWlscy5cclxuXHJcbioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKiovXHJcblxyXG5pbXBvcnQgeyByZWFkRmlsZVN5bmMgfSBmcm9tICdmcyc7XHJcbmltcG9ydCB7IGpvaW4gYXMgcGF0aGVyIH0gZnJvbSAncGF0aCc7XHJcbmltcG9ydCB7IGxvZyB9IGZyb20gJy4uLy4uL2xvZ2dlci5qcyc7XHJcblxyXG5pbXBvcnQgY2FjaGUgZnJvbSAnLi4vLi4vY2FjaGUuanMnO1xyXG5pbXBvcnQgeyBhZGRJbnRlcnZhbCB9IGZyb20gJy4uLy4uL2ludGVydmFscy5qcyc7XHJcbmltcG9ydCBwb29sIGZyb20gJy4uLy4uL3Bvb2wuanMnO1xyXG5pbXBvcnQgeyBfX2Rpcm5hbWUgfSBmcm9tICcuLi8uLi91dGlscy5qcyc7XHJcblxyXG5jb25zdCBwa2dGaWxlID0gSlNPTi5wYXJzZShyZWFkRmlsZVN5bmMocGF0aGVyKF9fZGlybmFtZSwgJ3BhY2thZ2UuanNvbicpKSk7XHJcblxyXG5jb25zdCBzZXJ2ZXJTdGFydFRpbWUgPSBuZXcgRGF0ZSgpO1xyXG5cclxuY29uc3Qgc3VjY2Vzc1JhdGVzID0gW107XHJcbmNvbnN0IHJlY29yZEludGVydmFsID0gNjAgKiAxMDAwOyAvLyByZWNvcmQgZXZlcnkgbWludXRlXHJcbmNvbnN0IHdpbmRvd1NpemUgPSAzMDsgLy8gMzAgbWludXRlc1xyXG5cclxuLyoqXHJcbiAqIENhbGN1bGF0ZXMgbW92aW5nIGF2ZXJhZ2UgaW5kaWNhdG9yIGJhc2VkIG9uIHRoZSBkYXRhIGZyb20gdGhlIHN1Y2Nlc3NSYXRlc1xyXG4gKiBhcnJheS5cclxuICpcclxuICogQHJldHVybnMge251bWJlcn0gLSBBIG1vdmluZyBhdmVyYWdlIGZvciBzdWNjZXNzIHJhdGlvIG9mIHRoZSBzZXJ2ZXIgZXhwb3J0cy5cclxuICovXHJcbmZ1bmN0aW9uIGNhbGN1bGF0ZU1vdmluZ0F2ZXJhZ2UoKSB7XHJcbiAgY29uc3Qgc3VtID0gc3VjY2Vzc1JhdGVzLnJlZHVjZSgoYSwgYikgPT4gYSArIGIsIDApO1xyXG4gIHJldHVybiBzdW0gLyBzdWNjZXNzUmF0ZXMubGVuZ3RoO1xyXG59XHJcblxyXG4vKipcclxuICogU3RhcnRzIHRoZSBpbnRlcnZhbCByZXNwb25zaWJsZSBmb3IgY2FsY3VsYXRpbmcgY3VycmVudCBzdWNjZXNzIHJhdGUgcmF0aW9cclxuICogYW5kIGdhdGhlcnNcclxuICpcclxuICogQHJldHVybnMge05vZGVKUy5UaW1lb3V0fSBpZCAtIElkIG9mIGFuIGludGVydmFsLlxyXG4gKi9cclxuZXhwb3J0IGNvbnN0IHN0YXJ0U3VjY2Vzc1JhdGUgPSAoKSA9PlxyXG4gIHNldEludGVydmFsKCgpID0+IHtcclxuICAgIGNvbnN0IHN0YXRzID0gcG9vbC5nZXRTdGF0cygpO1xyXG4gICAgY29uc3Qgc3VjY2Vzc1JhdGlvID1cclxuICAgICAgc3RhdHMuZXhwb3J0QXR0ZW1wdHMgPT09IDBcclxuICAgICAgICA/IDFcclxuICAgICAgICA6IChzdGF0cy5wZXJmb3JtZWRFeHBvcnRzIC8gc3RhdHMuZXhwb3J0QXR0ZW1wdHMpICogMTAwO1xyXG5cclxuICAgIHN1Y2Nlc3NSYXRlcy5wdXNoKHN1Y2Nlc3NSYXRpbyk7XHJcbiAgICBpZiAoc3VjY2Vzc1JhdGVzLmxlbmd0aCA+IHdpbmRvd1NpemUpIHtcclxuICAgICAgc3VjY2Vzc1JhdGVzLnNoaWZ0KCk7XHJcbiAgICB9XHJcbiAgfSwgcmVjb3JkSW50ZXJ2YWwpO1xyXG5cclxuLyoqXHJcbiAqIEFkZHMgdGhlIC9oZWFsdGggYW5kIC9zdWNjZXNzLW1vdmluZy1hdmVyYWdlIHJvdXRlc1xyXG4gKiB3aGljaCBvdXRwdXQgYmFzaWMgc3RhdHMgZm9yIHRoZSBzZXJ2ZXIuXHJcbiAqL1xyXG5leHBvcnQgZGVmYXVsdCBmdW5jdGlvbiBhZGRIZWFsdGhSb3V0ZXMoYXBwKSB7XHJcbiAgaWYgKCFhcHApIHtcclxuICAgIHJldHVybiBmYWxzZTtcclxuICB9XHJcblxyXG4gIC8vIFN0YXJ0IHByb2Nlc3Npbmcgc3VjY2VzcyByYXRlIHJhdGlvIGludGVydmFsIGFuZCBzYXZlIGl0cyBpZCB0byB0aGUgYXJyYXlcclxuICAvLyBmb3IgdGhlIGdyYWNlZnVsIGNsZWFyaW5nIG9uIHNodXRkb3duIHdpdGggaW5qZWN0ZWQgYWRkSW50ZXJ2YWwgZnVudGlvblxyXG4gIGFkZEludGVydmFsKHN0YXJ0U3VjY2Vzc1JhdGUoKSk7XHJcblxyXG4gIGFwcC5nZXQoJy9oZWFsdGgnLCAoXywgcmVzKSA9PiB7XHJcbiAgICBjb25zdCBzdGF0cyA9IHBvb2wuZ2V0U3RhdHMoKTtcclxuICAgIGNvbnN0IHBlcmlvZCA9IHN1Y2Nlc3NSYXRlcy5sZW5ndGg7XHJcbiAgICBjb25zdCBtb3ZpbmdBdmVyYWdlID0gY2FsY3VsYXRlTW92aW5nQXZlcmFnZSgpO1xyXG5cclxuICAgIGxvZyg0LCAnW2hlYWx0aC5qc10gR0VUIC9oZWFsdGggWzIwMF0gLSByZXR1cm5pbmcgc2VydmVyIGhlYWx0aC4nKTtcclxuXHJcbiAgICByZXMuc2VuZCh7XHJcbiAgICAgIHN0YXR1czogJ09LJyxcclxuICAgICAgYm9vdFRpbWU6IHNlcnZlclN0YXJ0VGltZSxcclxuICAgICAgdXB0aW1lOlxyXG4gICAgICAgIE1hdGguZmxvb3IoXHJcbiAgICAgICAgICAobmV3IERhdGUoKS5nZXRUaW1lKCkgLSBzZXJ2ZXJTdGFydFRpbWUuZ2V0VGltZSgpKSAvIDEwMDAgLyA2MFxyXG4gICAgICAgICkgKyAnIG1pbnV0ZXMnLFxyXG4gICAgICB2ZXJzaW9uOiBwa2dGaWxlLnZlcnNpb24sXHJcbiAgICAgIGhpZ2hjaGFydHNWZXJzaW9uOiBjYWNoZS52ZXJzaW9uKCksXHJcbiAgICAgIGF2ZXJhZ2VQcm9jZXNzaW5nVGltZTogc3RhdHMuc3BlbnRBdmVyYWdlLFxyXG4gICAgICBwZXJmb3JtZWRFeHBvcnRzOiBzdGF0cy5wZXJmb3JtZWRFeHBvcnRzLFxyXG4gICAgICBmYWlsZWRFeHBvcnRzOiBzdGF0cy5kcm9wcGVkRXhwb3J0cyxcclxuICAgICAgZXhwb3J0QXR0ZW1wdHM6IHN0YXRzLmV4cG9ydEF0dGVtcHRzLFxyXG4gICAgICBzdWNlc3NSYXRpbzogKHN0YXRzLnBlcmZvcm1lZEV4cG9ydHMgLyBzdGF0cy5leHBvcnRBdHRlbXB0cykgKiAxMDAsXHJcbiAgICAgIC8vIGVzbGludC1kaXNhYmxlLW5leHQtbGluZSBpbXBvcnQvbm8tbmFtZWQtYXMtZGVmYXVsdC1tZW1iZXJcclxuICAgICAgcG9vbDogcG9vbC5nZXRQb29sSW5mb0pTT04oKSxcclxuXHJcbiAgICAgIC8vIE1vdmluZyBhdmVyYWdlXHJcbiAgICAgIHBlcmlvZCxcclxuICAgICAgbW92aW5nQXZlcmFnZSxcclxuICAgICAgbWVzc2FnZTogYExhc3QgJHtwZXJpb2R9IG1pbnV0ZXMgaGFkIGEgc3VjY2VzcyByYXRlIG9mICR7bW92aW5nQXZlcmFnZS50b0ZpeGVkKDIpfSUuYCxcclxuXHJcbiAgICAgIC8vIFNWRy9KU09OIGF0dGVtcHRzXHJcbiAgICAgIHN2Z0V4cG9ydEF0dGVtcHRzOiBzdGF0cy5leHBvcnRGcm9tU3ZnQXR0ZW1wdHMsXHJcbiAgICAgIGpzb25FeHBvcnRBdHRlbXB0czogc3RhdHMucGVyZm9ybWVkRXhwb3J0cyAtIHN0YXRzLmV4cG9ydEZyb21TdmdBdHRlbXB0c1xyXG4gICAgfSk7XHJcbiAgfSk7XHJcbn1cclxuIiwiLyoqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKipcclxuXHJcbkhpZ2hjaGFydHMgRXhwb3J0IFNlcnZlclxyXG5cclxuQ29weXJpZ2h0IChjKSAyMDE2LTIwMjQsIEhpZ2hzb2Z0XHJcblxyXG5MaWNlbmNlZCB1bmRlciB0aGUgTUlUIGxpY2VuY2UuXHJcblxyXG5BZGRpdGlvbmFsbHkgYSB2YWxpZCBIaWdoY2hhcnRzIGxpY2Vuc2UgaXMgcmVxdWlyZWQgZm9yIHVzZS5cclxuXHJcblNlZSBMSUNFTlNFIGZpbGUgaW4gcm9vdCBmb3IgZGV0YWlscy5cclxuXHJcbioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKiovXHJcblxyXG5pbXBvcnQgeyBwcm9taXNlcyBhcyBmc1Byb21pc2VzIH0gZnJvbSAnZnMnO1xyXG5pbXBvcnQgeyBwb3NpeCB9IGZyb20gJ3BhdGgnO1xyXG5cclxuaW1wb3J0IGNvcnMgZnJvbSAnY29ycyc7XHJcbmltcG9ydCBleHByZXNzIGZyb20gJ2V4cHJlc3MnO1xyXG5pbXBvcnQgaHR0cCBmcm9tICdodHRwJztcclxuaW1wb3J0IGh0dHBzIGZyb20gJ2h0dHBzJztcclxuaW1wb3J0IG11bHRlciBmcm9tICdtdWx0ZXInO1xyXG5cclxuaW1wb3J0IGVycm9ySGFuZGxlciBmcm9tICcuL2Vycm9yLmpzJztcclxuaW1wb3J0IHJhdGVMaW1pdCBmcm9tICcuL3JhdGVfbGltaXQuanMnO1xyXG5pbXBvcnQgeyBsb2csIGxvZ1dpdGhTdGFjayB9IGZyb20gJy4uL2xvZ2dlci5qcyc7XHJcbmltcG9ydCB7IF9fZGlybmFtZSB9IGZyb20gJy4uL3V0aWxzLmpzJztcclxuXHJcbmltcG9ydCB2U3dpdGNoUm91dGUgZnJvbSAnLi9yb3V0ZXMvY2hhbmdlX2hjX3ZlcnNpb24uanMnO1xyXG5pbXBvcnQgZXhwb3J0Um91dGVzIGZyb20gJy4vcm91dGVzL2V4cG9ydC5qcyc7XHJcbmltcG9ydCBoZWFsdGhSb3V0ZSBmcm9tICcuL3JvdXRlcy9oZWFsdGguanMnO1xyXG5pbXBvcnQgdWlSb3V0ZSBmcm9tICcuL3JvdXRlcy91aS5qcyc7XHJcblxyXG5pbXBvcnQgRXhwb3J0RXJyb3IgZnJvbSAnLi4vZXJyb3JzL0V4cG9ydEVycm9yLmpzJztcclxuXHJcbi8vIEFycmF5IG9mIGFuIGFjdGl2ZSBzZXJ2ZXJzXHJcbmNvbnN0IGFjdGl2ZVNlcnZlcnMgPSBuZXcgTWFwKCk7XHJcblxyXG4vLyBDcmVhdGUgZXhwcmVzcyBhcHBcclxuY29uc3QgYXBwID0gZXhwcmVzcygpO1xyXG5cclxuLy8gRGlzYWJsZSB0aGUgWC1Qb3dlcmVkLUJ5IGhlYWRlclxyXG5hcHAuZGlzYWJsZSgneC1wb3dlcmVkLWJ5Jyk7XHJcblxyXG4vLyBFbmFibGUgQ09SUyBzdXBwb3J0XHJcbmFwcC51c2UoY29ycygpKTtcclxuXHJcbi8vIEVuYWJsZSBwYXJzaW5nIG9mIGZvcm0gZGF0YSAoZmlsZXMpIHdpdGggTXVsdGVyIHBhY2thZ2VcclxuY29uc3Qgc3RvcmFnZSA9IG11bHRlci5tZW1vcnlTdG9yYWdlKCk7XHJcbmNvbnN0IHVwbG9hZCA9IG11bHRlcih7XHJcbiAgc3RvcmFnZSxcclxuICBsaW1pdHM6IHtcclxuICAgIGZpZWxkU2l6ZTogNTAgKiAxMDI0ICogMTAyNFxyXG4gIH1cclxufSk7XHJcblxyXG4vLyBFbmFibGUgYm9keSBwYXJzZXJcclxuYXBwLnVzZShleHByZXNzLmpzb24oeyBsaW1pdDogNTAgKiAxMDI0ICogMTAyNCB9KSk7XHJcbmFwcC51c2UoZXhwcmVzcy51cmxlbmNvZGVkKHsgZXh0ZW5kZWQ6IHRydWUsIGxpbWl0OiA1MCAqIDEwMjQgKiAxMDI0IH0pKTtcclxuXHJcbi8vIFVzZSBvbmx5IG5vbi1maWxlIG11bHRpcGFydCBmb3JtIGZpZWxkc1xyXG5hcHAudXNlKHVwbG9hZC5ub25lKCkpO1xyXG5cclxuLyoqXHJcbiAqIEF0dGFjaCBlcnJvciBoYW5kbGVycyB0byB0aGUgc2VydmVyLlxyXG4gKlxyXG4gKiBAcGFyYW0ge2h0dHAuU2VydmVyfSBzZXJ2ZXIgLSBUaGUgSFRUUC9IVFRQUyBzZXJ2ZXIgaW5zdGFuY2UuXHJcbiAqL1xyXG5jb25zdCBhdHRhY2hTZXJ2ZXJFcnJvckhhbmRsZXJzID0gKHNlcnZlcikgPT4ge1xyXG4gIHNlcnZlci5vbignY2xpZW50RXJyb3InLCAoZXJyb3IpID0+IHtcclxuICAgIGxvZ1dpdGhTdGFjaygxLCBlcnJvciwgYFtzZXJ2ZXJdIENsaWVudCBlcnJvcjogJHtlcnJvci5tZXNzYWdlfWApO1xyXG4gIH0pO1xyXG5cclxuICBzZXJ2ZXIub24oJ2Vycm9yJywgKGVycm9yKSA9PiB7XHJcbiAgICBsb2dXaXRoU3RhY2soMSwgZXJyb3IsIGBbc2VydmVyXSBTZXJ2ZXIgZXJyb3I6ICR7ZXJyb3IubWVzc2FnZX1gKTtcclxuICB9KTtcclxuXHJcbiAgc2VydmVyLm9uKCdjb25uZWN0aW9uJywgKHNvY2tldCkgPT4ge1xyXG4gICAgc29ja2V0Lm9uKCdlcnJvcicsIChlcnJvcikgPT4ge1xyXG4gICAgICBsb2dXaXRoU3RhY2soMSwgZXJyb3IsIGBbc2VydmVyXSBTb2NrZXQgZXJyb3I6ICR7ZXJyb3IubWVzc2FnZX1gKTtcclxuICAgIH0pO1xyXG4gIH0pO1xyXG59O1xyXG5cclxuLyoqXHJcbiAqIFN0YXJ0cyBhbiBIVFRQIHNlcnZlciBiYXNlZCBvbiB0aGUgcHJvdmlkZWQgY29uZmlndXJhdGlvbi4gVGhlIGBzZXJ2ZXJDb25maWdgXHJcbiAqIG9iamVjdCBjb250YWlucyBhbGwgc2VydmVyIHJlbGF0ZWQgcHJvcGVydGllcyAoc2VlIHRoZSBgc2VydmVyYCBzZWN0aW9uXHJcbiAqIGluIHRoZSBgbGliL3NjaGVtYXMvY29uZmlnLmpzYCBmaWxlIGZvciBhIHJlZmVyZW5jZSkuXHJcbiAqXHJcbiAqIEBwYXJhbSB7T2JqZWN0fSBzZXJ2ZXJDb25maWcgLSBUaGUgc2VydmVyIGNvbmZpZ3VyYXRpb24gb2JqZWN0LlxyXG4gKlxyXG4gKiBAdGhyb3dzIHtFeHBvcnRFcnJvcn0gLSBUaHJvd3MgYW4gZXJyb3IgaWYgdGhlIHNlcnZlciBjYW5ub3QgYmUgY29uZmlndXJlZFxyXG4gKiBhbmQgc3RhcnRlZC5cclxuICovXHJcbmV4cG9ydCBjb25zdCBzdGFydFNlcnZlciA9IGFzeW5jIChzZXJ2ZXJDb25maWcpID0+IHtcclxuICB0cnkge1xyXG4gICAgLy8gU3RvcCBpZiBub3QgZW5hYmxlZFxyXG4gICAgaWYgKCFzZXJ2ZXJDb25maWcuZW5hYmxlKSB7XHJcbiAgICAgIHJldHVybiBmYWxzZTtcclxuICAgIH1cclxuXHJcbiAgICAvLyBMaXN0ZW4gSFRUUCBzZXJ2ZXJcclxuICAgIGlmICghc2VydmVyQ29uZmlnLnNzbC5mb3JjZSkge1xyXG4gICAgICAvLyBNYWluIHNlcnZlciBpbnN0YW5jZSAoSFRUUClcclxuICAgICAgY29uc3QgaHR0cFNlcnZlciA9IGh0dHAuY3JlYXRlU2VydmVyKGFwcCk7XHJcblxyXG4gICAgICAvLyBBdHRhY2ggZXJyb3IgaGFuZGxlcnMgYW5kIGxpc3RlbiB0byB0aGUgc2VydmVyXHJcbiAgICAgIGF0dGFjaFNlcnZlckVycm9ySGFuZGxlcnMoaHR0cFNlcnZlcik7XHJcblxyXG4gICAgICAvLyBMaXN0ZW5cclxuICAgICAgaHR0cFNlcnZlci5saXN0ZW4oc2VydmVyQ29uZmlnLnBvcnQsIHNlcnZlckNvbmZpZy5ob3N0KTtcclxuXHJcbiAgICAgIC8vIFNhdmUgdGhlIHJlZmVyZW5jZSB0byBIVFRQIHNlcnZlclxyXG4gICAgICBhY3RpdmVTZXJ2ZXJzLnNldChzZXJ2ZXJDb25maWcucG9ydCwgaHR0cFNlcnZlcik7XHJcblxyXG4gICAgICBsb2coXHJcbiAgICAgICAgMyxcclxuICAgICAgICBgW3NlcnZlcl0gU3RhcnRlZCBIVFRQIHNlcnZlciBvbiAke3NlcnZlckNvbmZpZy5ob3N0fToke3NlcnZlckNvbmZpZy5wb3J0fS5gXHJcbiAgICAgICk7XHJcbiAgICB9XHJcblxyXG4gICAgLy8gTGlzdGVuIEhUVFBTIHNlcnZlclxyXG4gICAgaWYgKHNlcnZlckNvbmZpZy5zc2wuZW5hYmxlKSB7XHJcbiAgICAgIC8vIFNldCB1cCBhbiBTU0wgc2VydmVyIGFsc29cclxuICAgICAgbGV0IGtleSwgY2VydDtcclxuXHJcbiAgICAgIHRyeSB7XHJcbiAgICAgICAgLy8gR2V0IHRoZSBTU0wga2V5XHJcbiAgICAgICAga2V5ID0gYXdhaXQgZnNQcm9taXNlcy5yZWFkRmlsZShcclxuICAgICAgICAgIHBvc2l4LmpvaW4oc2VydmVyQ29uZmlnLnNzbC5jZXJ0UGF0aCwgJ3NlcnZlci5rZXknKSxcclxuICAgICAgICAgICd1dGY4J1xyXG4gICAgICAgICk7XHJcblxyXG4gICAgICAgIC8vIEdldCB0aGUgU1NMIGNlcnRpZmljYXRlXHJcbiAgICAgICAgY2VydCA9IGF3YWl0IGZzUHJvbWlzZXMucmVhZEZpbGUoXHJcbiAgICAgICAgICBwb3NpeC5qb2luKHNlcnZlckNvbmZpZy5zc2wuY2VydFBhdGgsICdzZXJ2ZXIuY3J0JyksXHJcbiAgICAgICAgICAndXRmOCdcclxuICAgICAgICApO1xyXG4gICAgICB9IGNhdGNoIChlcnJvcikge1xyXG4gICAgICAgIGxvZyhcclxuICAgICAgICAgIDIsXHJcbiAgICAgICAgICBgW3NlcnZlcl0gVW5hYmxlIHRvIGxvYWQga2V5L2NlcnRpZmljYXRlIGZyb20gdGhlICcke3NlcnZlckNvbmZpZy5zc2wuY2VydFBhdGh9JyBwYXRoLiBDb3VsZCBub3QgcnVuIHNlY3VyZWQgbGF5ZXIgc2VydmVyLmBcclxuICAgICAgICApO1xyXG4gICAgICB9XHJcblxyXG4gICAgICBpZiAoa2V5ICYmIGNlcnQpIHtcclxuICAgICAgICAvLyBNYWluIHNlcnZlciBpbnN0YW5jZSAoSFRUUFMpXHJcbiAgICAgICAgY29uc3QgaHR0cHNTZXJ2ZXIgPSBodHRwcy5jcmVhdGVTZXJ2ZXIoeyBrZXksIGNlcnQgfSwgYXBwKTtcclxuXHJcbiAgICAgICAgLy8gQXR0YWNoIGVycm9yIGhhbmRsZXJzIGFuZCBsaXN0ZW4gdG8gdGhlIHNlcnZlclxyXG4gICAgICAgIGF0dGFjaFNlcnZlckVycm9ySGFuZGxlcnMoaHR0cHNTZXJ2ZXIpO1xyXG5cclxuICAgICAgICAvLyBMaXN0ZW5cclxuICAgICAgICBodHRwc1NlcnZlci5saXN0ZW4oc2VydmVyQ29uZmlnLnNzbC5wb3J0LCBzZXJ2ZXJDb25maWcuaG9zdCk7XHJcblxyXG4gICAgICAgIC8vIFNhdmUgdGhlIHJlZmVyZW5jZSB0byBIVFRQUyBzZXJ2ZXJcclxuICAgICAgICBhY3RpdmVTZXJ2ZXJzLnNldChzZXJ2ZXJDb25maWcuc3NsLnBvcnQsIGh0dHBzU2VydmVyKTtcclxuXHJcbiAgICAgICAgbG9nKFxyXG4gICAgICAgICAgMyxcclxuICAgICAgICAgIGBbc2VydmVyXSBTdGFydGVkIEhUVFBTIHNlcnZlciBvbiAke3NlcnZlckNvbmZpZy5ob3N0fToke3NlcnZlckNvbmZpZy5zc2wucG9ydH0uYFxyXG4gICAgICAgICk7XHJcbiAgICAgIH1cclxuICAgIH1cclxuXHJcbiAgICAvLyBFbmFibGUgdGhlIHJhdGUgbGltaXRlciBpZiBjb25maWcgc2F5cyBzb1xyXG4gICAgaWYgKFxyXG4gICAgICBzZXJ2ZXJDb25maWcucmF0ZUxpbWl0aW5nICYmXHJcbiAgICAgIHNlcnZlckNvbmZpZy5yYXRlTGltaXRpbmcuZW5hYmxlICYmXHJcbiAgICAgICFbMCwgTmFOXS5pbmNsdWRlcyhzZXJ2ZXJDb25maWcucmF0ZUxpbWl0aW5nLm1heFJlcXVlc3RzKVxyXG4gICAgKSB7XHJcbiAgICAgIHJhdGVMaW1pdChhcHAsIHNlcnZlckNvbmZpZy5yYXRlTGltaXRpbmcpO1xyXG4gICAgfVxyXG5cclxuICAgIC8vIFNldCB1cCBzdGF0aWMgZm9sZGVyJ3Mgcm91dGVcclxuICAgIGFwcC51c2UoZXhwcmVzcy5zdGF0aWMocG9zaXguam9pbihfX2Rpcm5hbWUsICdwdWJsaWMnKSkpO1xyXG5cclxuICAgIC8vIFNldCB1cCByb3V0ZXNcclxuICAgIGhlYWx0aFJvdXRlKGFwcCk7XHJcbiAgICBleHBvcnRSb3V0ZXMoYXBwKTtcclxuICAgIHVpUm91dGUoYXBwKTtcclxuICAgIHZTd2l0Y2hSb3V0ZShhcHApO1xyXG5cclxuICAgIC8vIFNldCB1cCBjZW50cmFsaXplZCBlcnJvciBoYW5kbGVyXHJcbiAgICBlcnJvckhhbmRsZXIoYXBwKTtcclxuICB9IGNhdGNoIChlcnJvcikge1xyXG4gICAgdGhyb3cgbmV3IEV4cG9ydEVycm9yKFxyXG4gICAgICAnW3NlcnZlcl0gQ291bGQgbm90IGNvbmZpZ3VyZSBhbmQgc3RhcnQgdGhlIHNlcnZlci4nXHJcbiAgICApLnNldEVycm9yKGVycm9yKTtcclxuICB9XHJcbn07XHJcblxyXG4vKipcclxuICogQ2xvc2VzIGFsbCBzZXJ2ZXJzIGFzc29jaWF0ZWQgd2l0aCBFeHByZXNzIGFwcCBpbnN0YW5jZS5cclxuICovXHJcbmV4cG9ydCBjb25zdCBjbG9zZVNlcnZlcnMgPSAoKSA9PiB7XHJcbiAgbG9nKDQsIGBbc2VydmVyXSBDbG9zaW5nIGFsbCBzZXJ2ZXJzLmApO1xyXG4gIGZvciAoY29uc3QgW3BvcnQsIHNlcnZlcl0gb2YgYWN0aXZlU2VydmVycykge1xyXG4gICAgc2VydmVyLmNsb3NlKCgpID0+IHtcclxuICAgICAgYWN0aXZlU2VydmVycy5kZWxldGUocG9ydCk7XHJcbiAgICAgIGxvZyg0LCBgW3NlcnZlcl0gQ2xvc2VkIHNlcnZlciBvbiBwb3J0OiAke3BvcnR9LmApO1xyXG4gICAgfSk7XHJcbiAgfVxyXG59O1xyXG5cclxuLyoqXHJcbiAqIEdldCBhbGwgc2VydmVycyBhc3NvY2lhdGVkIHdpdGggRXhwcmVzcyBhcHAgaW5zdGFuY2UuXHJcbiAqXHJcbiAqIEByZXR1cm5zIHtBcnJheX0gLSBTZXJ2ZXJzIGFzc29jaWF0ZWQgd2l0aCBFeHByZXNzIGFwcCBpbnN0YW5jZS5cclxuICovXHJcbmV4cG9ydCBjb25zdCBnZXRTZXJ2ZXJzID0gKCkgPT4gYWN0aXZlU2VydmVycztcclxuXHJcbi8qKlxyXG4gKiBFbmFibGUgcmF0ZSBsaW1pdGluZyBmb3IgdGhlIHNlcnZlci5cclxuICpcclxuICogQHBhcmFtIHtPYmplY3R9IGxpbWl0Q29uZmlnIC0gQ29uZmlndXJhdGlvbiBvYmplY3QgZm9yIHJhdGUgbGltaXRpbmcuXHJcbiAqL1xyXG5leHBvcnQgY29uc3QgZW5hYmxlUmF0ZUxpbWl0aW5nID0gKGxpbWl0Q29uZmlnKSA9PiByYXRlTGltaXQoYXBwLCBsaW1pdENvbmZpZyk7XHJcblxyXG4vKipcclxuICogR2V0IHRoZSBFeHByZXNzIGluc3RhbmNlLlxyXG4gKlxyXG4gKiBAcmV0dXJucyB7T2JqZWN0fSAtIFRoZSBFeHByZXNzIGluc3RhbmNlLlxyXG4gKi9cclxuZXhwb3J0IGNvbnN0IGdldEV4cHJlc3MgPSAoKSA9PiBleHByZXNzO1xyXG5cclxuLyoqXHJcbiAqIEdldCB0aGUgRXhwcmVzcyBhcHAgaW5zdGFuY2UuXHJcbiAqXHJcbiAqIEByZXR1cm5zIHtPYmplY3R9IC0gVGhlIEV4cHJlc3MgYXBwIGluc3RhbmNlLlxyXG4gKi9cclxuZXhwb3J0IGNvbnN0IGdldEFwcCA9ICgpID0+IGFwcDtcclxuXHJcbi8qKlxyXG4gKiBBcHBseSBtaWRkbGV3YXJlKHMpIHRvIGEgc3BlY2lmaWMgcGF0aC5cclxuICpcclxuICogQHBhcmFtIHtzdHJpbmd9IHBhdGggLSBUaGUgcGF0aCB0byB3aGljaCB0aGUgbWlkZGxld2FyZShzKSBzaG91bGQgYmUgYXBwbGllZC5cclxuICogQHBhcmFtIHsuLi5GdW5jdGlvbn0gbWlkZGxld2FyZXMgLSBUaGUgbWlkZGxld2FyZSBmdW5jdGlvbnMgdG8gYmUgYXBwbGllZC5cclxuICovXHJcbmV4cG9ydCBjb25zdCB1c2UgPSAocGF0aCwgLi4ubWlkZGxld2FyZXMpID0+IHtcclxuICBhcHAudXNlKHBhdGgsIC4uLm1pZGRsZXdhcmVzKTtcclxufTtcclxuXHJcbi8qKlxyXG4gKiBTZXQgdXAgYSByb3V0ZSB3aXRoIEdFVCBtZXRob2QgYW5kIGFwcGx5IG1pZGRsZXdhcmUocykuXHJcbiAqXHJcbiAqIEBwYXJhbSB7c3RyaW5nfSBwYXRoIC0gVGhlIHJvdXRlIHBhdGguXHJcbiAqIEBwYXJhbSB7Li4uRnVuY3Rpb259IG1pZGRsZXdhcmVzIC0gVGhlIG1pZGRsZXdhcmUgZnVuY3Rpb25zIHRvIGJlIGFwcGxpZWQuXHJcbiAqL1xyXG5leHBvcnQgY29uc3QgZ2V0ID0gKHBhdGgsIC4uLm1pZGRsZXdhcmVzKSA9PiB7XHJcbiAgYXBwLmdldChwYXRoLCAuLi5taWRkbGV3YXJlcyk7XHJcbn07XHJcblxyXG4vKipcclxuICogU2V0IHVwIGEgcm91dGUgd2l0aCBQT1NUIG1ldGhvZCBhbmQgYXBwbHkgbWlkZGxld2FyZShzKS5cclxuICpcclxuICogQHBhcmFtIHtzdHJpbmd9IHBhdGggLSBUaGUgcm91dGUgcGF0aC5cclxuICogQHBhcmFtIHsuLi5GdW5jdGlvbn0gbWlkZGxld2FyZXMgLSBUaGUgbWlkZGxld2FyZSBmdW5jdGlvbnMgdG8gYmUgYXBwbGllZC5cclxuICovXHJcbmV4cG9ydCBjb25zdCBwb3N0ID0gKHBhdGgsIC4uLm1pZGRsZXdhcmVzKSA9PiB7XHJcbiAgYXBwLnBvc3QocGF0aCwgLi4ubWlkZGxld2FyZXMpO1xyXG59O1xyXG5cclxuZXhwb3J0IGRlZmF1bHQge1xyXG4gIHN0YXJ0U2VydmVyLFxyXG4gIGNsb3NlU2VydmVycyxcclxuICBnZXRTZXJ2ZXJzLFxyXG4gIGVuYWJsZVJhdGVMaW1pdGluZyxcclxuICBnZXRFeHByZXNzLFxyXG4gIGdldEFwcCxcclxuICB1c2UsXHJcbiAgZ2V0LFxyXG4gIHBvc3RcclxufTtcclxuIiwiLyoqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKipcclxuXHJcbkhpZ2hjaGFydHMgRXhwb3J0IFNlcnZlclxyXG5cclxuQ29weXJpZ2h0IChjKSAyMDE2LTIwMjQsIEhpZ2hzb2Z0XHJcblxyXG5MaWNlbmNlZCB1bmRlciB0aGUgTUlUIGxpY2VuY2UuXHJcblxyXG5BZGRpdGlvbmFsbHkgYSB2YWxpZCBIaWdoY2hhcnRzIGxpY2Vuc2UgaXMgcmVxdWlyZWQgZm9yIHVzZS5cclxuXHJcblNlZSBMSUNFTlNFIGZpbGUgaW4gcm9vdCBmb3IgZGV0YWlscy5cclxuXHJcbioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKiovXHJcblxyXG5pbXBvcnQgeyBqb2luIH0gZnJvbSAncGF0aCc7XHJcblxyXG5pbXBvcnQgeyBfX2Rpcm5hbWUgfSBmcm9tICcuLi8uLi91dGlscy5qcyc7XHJcblxyXG4vKipcclxuICogQWRkcyB0aGUgR0VUIC8gcm91dGUgZm9yIGEgVUkgd2hlbiBlbmFibGVkIG9uIHRoZSBleHBvcnQgc2VydmVyLlxyXG4gKi9cclxuZXhwb3J0IGRlZmF1bHQgKGFwcCkgPT5cclxuICAhYXBwXHJcbiAgICA/IGZhbHNlXHJcbiAgICA6IGFwcC5nZXQoJy8nLCAocmVxdWVzdCwgcmVzcG9uc2UpID0+IHtcclxuICAgICAgICByZXNwb25zZS5zZW5kRmlsZShqb2luKF9fZGlybmFtZSwgJ3B1YmxpYycsICdpbmRleC5odG1sJykpO1xyXG4gICAgICB9KTtcclxuIiwiLyoqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKipcclxuXHJcbkhpZ2hjaGFydHMgRXhwb3J0IFNlcnZlclxyXG5cclxuQ29weXJpZ2h0IChjKSAyMDE2LTIwMjQsIEhpZ2hzb2Z0XHJcblxyXG5MaWNlbmNlZCB1bmRlciB0aGUgTUlUIGxpY2VuY2UuXHJcblxyXG5BZGRpdGlvbmFsbHkgYSB2YWxpZCBIaWdoY2hhcnRzIGxpY2Vuc2UgaXMgcmVxdWlyZWQgZm9yIHVzZS5cclxuXHJcblNlZSBMSUNFTlNFIGZpbGUgaW4gcm9vdCBmb3IgZGV0YWlscy5cclxuXHJcbioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKiovXHJcblxyXG5pbXBvcnQgY2FjaGUgZnJvbSAnLi4vLi4vY2FjaGUuanMnO1xyXG5pbXBvcnQgeyBlbnZzIH0gZnJvbSAnLi4vLi4vZW52cy5qcyc7XHJcblxyXG5pbXBvcnQgSHR0cEVycm9yIGZyb20gJy4uLy4uL2Vycm9ycy9IdHRwRXJyb3IuanMnO1xyXG5cclxuLyoqXHJcbiAqIEFkZHMgdGhlIFBPU1QgL2NoYW5nZV9oY192ZXJzaW9uLzpuZXdWZXJzaW9uIHJvdXRlIHRoYXQgY2FuIGJlIHV0aWxpemVkIHRvIG1vZGlmeVxyXG4gKiB0aGUgSGlnaGNoYXJ0cyB2ZXJzaW9uIG9uIHRoZSBzZXJ2ZXIuXHJcbiAqXHJcbiAqIFRPRE86IEFkZCBhdXRoIHRva2VuIGFuZCBjb25uZWN0IHRvIEFQSVxyXG4gKi9cclxuZXhwb3J0IGRlZmF1bHQgKGFwcCkgPT5cclxuICAhYXBwXHJcbiAgICA/IGZhbHNlXHJcbiAgICA6IGFwcC5wb3N0KFxyXG4gICAgICAgICcvdmVyc2lvbi9jaGFuZ2UvOm5ld1ZlcnNpb24nLFxyXG4gICAgICAgIGFzeW5jIChyZXF1ZXN0LCByZXNwb25zZSwgbmV4dCkgPT4ge1xyXG4gICAgICAgICAgdHJ5IHtcclxuICAgICAgICAgICAgY29uc3QgYWRtaW5Ub2tlbiA9IGVudnMuSElHSENIQVJUU19BRE1JTl9UT0tFTjtcclxuXHJcbiAgICAgICAgICAgIC8vIENoZWNrIHRoZSBleGlzdGVuY2Ugb2YgdGhlIHRva2VuXHJcbiAgICAgICAgICAgIGlmICghYWRtaW5Ub2tlbiB8fCAhYWRtaW5Ub2tlbi5sZW5ndGgpIHtcclxuICAgICAgICAgICAgICB0aHJvdyBuZXcgSHR0cEVycm9yKFxyXG4gICAgICAgICAgICAgICAgJ1RoZSBzZXJ2ZXIgaXMgbm90IGNvbmZpZ3VyZWQgdG8gcGVyZm9ybSBydW4tdGltZSB2ZXJzaW9uIGNoYW5nZXM6IEhJR0hDSEFSVFNfQURNSU5fVE9LRU4gaXMgbm90IHNldC4nLFxyXG4gICAgICAgICAgICAgICAgNDAxXHJcbiAgICAgICAgICAgICAgKTtcclxuICAgICAgICAgICAgfVxyXG5cclxuICAgICAgICAgICAgLy8gQ2hlY2sgaWYgdGhlIGhjLWF1dGggaGVhZGVyIGNvbnRhaW4gYSBjb3JyZWN0IHRva2VuXHJcbiAgICAgICAgICAgIGNvbnN0IHRva2VuID0gcmVxdWVzdC5nZXQoJ2hjLWF1dGgnKTtcclxuICAgICAgICAgICAgaWYgKCF0b2tlbiB8fCB0b2tlbiAhPT0gYWRtaW5Ub2tlbikge1xyXG4gICAgICAgICAgICAgIHRocm93IG5ldyBIdHRwRXJyb3IoXHJcbiAgICAgICAgICAgICAgICAnSW52YWxpZCBvciBtaXNzaW5nIHRva2VuOiBTZXQgdGhlIHRva2VuIGluIHRoZSBoYy1hdXRoIGhlYWRlci4nLFxyXG4gICAgICAgICAgICAgICAgNDAxXHJcbiAgICAgICAgICAgICAgKTtcclxuICAgICAgICAgICAgfVxyXG5cclxuICAgICAgICAgICAgLy8gQ29tcGFyZSB2ZXJzaW9uc1xyXG4gICAgICAgICAgICBjb25zdCBuZXdWZXJzaW9uID0gcmVxdWVzdC5wYXJhbXMubmV3VmVyc2lvbjtcclxuICAgICAgICAgICAgaWYgKG5ld1ZlcnNpb24pIHtcclxuICAgICAgICAgICAgICB0cnkge1xyXG4gICAgICAgICAgICAgICAgLy8gZXNsaW50LWRpc2FibGUtbmV4dC1saW5lIGltcG9ydC9uby1uYW1lZC1hcy1kZWZhdWx0LW1lbWJlclxyXG4gICAgICAgICAgICAgICAgYXdhaXQgY2FjaGUudXBkYXRlVmVyc2lvbihuZXdWZXJzaW9uKTtcclxuICAgICAgICAgICAgICB9IGNhdGNoIChlcnJvcikge1xyXG4gICAgICAgICAgICAgICAgdGhyb3cgbmV3IEh0dHBFcnJvcihcclxuICAgICAgICAgICAgICAgICAgYFZlcnNpb24gY2hhbmdlOiAke2Vycm9yLm1lc3NhZ2V9YCxcclxuICAgICAgICAgICAgICAgICAgZXJyb3Iuc3RhdHVzQ29kZVxyXG4gICAgICAgICAgICAgICAgKS5zZXRFcnJvcihlcnJvcik7XHJcbiAgICAgICAgICAgICAgfVxyXG5cclxuICAgICAgICAgICAgICAvLyBTdWNjZXNzXHJcbiAgICAgICAgICAgICAgcmVzcG9uc2Uuc3RhdHVzKDIwMCkuc2VuZCh7XHJcbiAgICAgICAgICAgICAgICBzdGF0dXNDb2RlOiAyMDAsXHJcbiAgICAgICAgICAgICAgICB2ZXJzaW9uOiBjYWNoZS52ZXJzaW9uKCksXHJcbiAgICAgICAgICAgICAgICBtZXNzYWdlOiBgU3VjY2Vzc2Z1bGx5IHVwZGF0ZWQgSGlnaGNoYXJ0cyB0byB2ZXJzaW9uOiAke25ld1ZlcnNpb259LmBcclxuICAgICAgICAgICAgICB9KTtcclxuICAgICAgICAgICAgfSBlbHNlIHtcclxuICAgICAgICAgICAgICAvLyBObyB2ZXJzaW9uIHNwZWNpZmllZFxyXG4gICAgICAgICAgICAgIHRocm93IG5ldyBIdHRwRXJyb3IoJ05vIG5ldyB2ZXJzaW9uIHN1cHBsaWVkLicsIDQwMCk7XHJcbiAgICAgICAgICAgIH1cclxuICAgICAgICAgIH0gY2F0Y2ggKGVycm9yKSB7XHJcbiAgICAgICAgICAgIG5leHQoZXJyb3IpO1xyXG4gICAgICAgICAgfVxyXG4gICAgICAgIH1cclxuICAgICAgKTtcclxuIiwiLyoqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKipcclxuXHJcbkhpZ2hjaGFydHMgRXhwb3J0IFNlcnZlclxyXG5cclxuQ29weXJpZ2h0IChjKSAyMDE2LTIwMjQsIEhpZ2hzb2Z0XHJcblxyXG5MaWNlbmNlZCB1bmRlciB0aGUgTUlUIGxpY2VuY2UuXHJcblxyXG5BZGRpdGlvbmFsbHkgYSB2YWxpZCBIaWdoY2hhcnRzIGxpY2Vuc2UgaXMgcmVxdWlyZWQgZm9yIHVzZS5cclxuXHJcblNlZSBMSUNFTlNFIGZpbGUgaW4gcm9vdCBmb3IgZGV0YWlscy5cclxuXHJcbioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKiovXHJcblxyXG5pbXBvcnQgeyBjbGVhckFsbEludGVydmFscyB9IGZyb20gJy4vaW50ZXJ2YWxzLmpzJztcclxuaW1wb3J0IHsga2lsbFBvb2wgfSBmcm9tICcuL3Bvb2wuanMnO1xyXG5pbXBvcnQgeyBjbG9zZVNlcnZlcnMgfSBmcm9tICcuL3NlcnZlci9zZXJ2ZXIuanMnO1xyXG5cclxuLyoqXHJcbiAqIENsZWFuIHVwIGZ1bmN0aW9uIHRvIHRyaWdnZXIgYmVmb3JlIGVuZGluZyBwcm9jZXNzIGZvciB0aGUgZ3JhY2VmdWwgc2h1dGRvd24uXHJcbiAqXHJcbiAqIEBwYXJhbSB7bnVtYmVyfSBleGl0Q29kZSAtIEFuIGV4aXQgY29kZSBmb3IgdGhlIHByb2Nlc3MuZXhpdCgpIGZ1bmN0aW9uLlxyXG4gKi9cclxuZXhwb3J0IGNvbnN0IHNodXRkb3duQ2xlYW5VcCA9IGFzeW5jIChleGl0Q29kZSkgPT4ge1xyXG4gIC8vIEF3YWl0IGZyZWVpbmcgYWxsIHJlc291cmNlc1xyXG4gIGF3YWl0IFByb21pc2UuYWxsU2V0dGxlZChbXHJcbiAgICAvLyBDbGVhciBhbGwgb25nb2luZyBpbnRlcnZhbHNcclxuICAgIGNsZWFyQWxsSW50ZXJ2YWxzKCksXHJcblxyXG4gICAgLy8gR2V0IGF2YWlsYWJsZSBzZXJ2ZXIgaW5zdGFuY2VzIChIVFRQL0hUVFBTKSBhbmQgY2xvc2UgdGhlbVxyXG4gICAgY2xvc2VTZXJ2ZXJzKCksXHJcblxyXG4gICAgLy8gQ2xvc2UgcG9vbCBhbG9uZyB3aXRoIGl0cyB3b3JrZXJzIGFuZCB0aGUgYnJvd3NlciBpbnN0YW5jZSwgaWYgZXhpc3RzXHJcbiAgICBraWxsUG9vbCgpXHJcbiAgXSk7XHJcblxyXG4gIC8vIEV4aXQgcHJvY2VzcyB3aXRoIGEgY29ycmVjdCBjb2RlXHJcbiAgcHJvY2Vzcy5leGl0KGV4aXRDb2RlKTtcclxufTtcclxuXHJcbmV4cG9ydCBkZWZhdWx0IHtcclxuICBzaHV0ZG93bkNsZWFuVXBcclxufTtcclxuIiwiLyoqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKipcclxuXHJcbkhpZ2hjaGFydHMgRXhwb3J0IFNlcnZlclxyXG5cclxuQ29weXJpZ2h0IChjKSAyMDE2LTIwMjQsIEhpZ2hzb2Z0XHJcblxyXG5MaWNlbmNlZCB1bmRlciB0aGUgTUlUIGxpY2VuY2UuXHJcblxyXG5BZGRpdGlvbmFsbHkgYSB2YWxpZCBIaWdoY2hhcnRzIGxpY2Vuc2UgaXMgcmVxdWlyZWQgZm9yIHVzZS5cclxuXHJcblNlZSBMSUNFTlNFIGZpbGUgaW4gcm9vdCBmb3IgZGV0YWlscy5cclxuXHJcbioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKiovXHJcblxyXG5pbXBvcnQgJ2NvbG9ycyc7XHJcblxyXG5pbXBvcnQgeyBjaGVja0FuZFVwZGF0ZUNhY2hlIH0gZnJvbSAnLi9jYWNoZS5qcyc7XHJcbmltcG9ydCB7XHJcbiAgYmF0Y2hFeHBvcnQsXHJcbiAgc2V0QWxsb3dDb2RlRXhlY3V0aW9uLFxyXG4gIHNpbmdsZUV4cG9ydCxcclxuICBzdGFydEV4cG9ydFxyXG59IGZyb20gJy4vY2hhcnQuanMnO1xyXG5pbXBvcnQgeyBtYXBUb05ld0NvbmZpZywgbWFudWFsQ29uZmlnLCBzZXRPcHRpb25zIH0gZnJvbSAnLi9jb25maWcuanMnO1xyXG5pbXBvcnQge1xyXG4gIGluaXRMb2dnaW5nLFxyXG4gIGxvZyxcclxuICBsb2dXaXRoU3RhY2ssXHJcbiAgc2V0TG9nTGV2ZWwsXHJcbiAgZW5hYmxlRmlsZUxvZ2dpbmdcclxufSBmcm9tICcuL2xvZ2dlci5qcyc7XHJcbmltcG9ydCB7IGluaXRQb29sLCBraWxsUG9vbCB9IGZyb20gJy4vcG9vbC5qcyc7XHJcbmltcG9ydCB7IHNodXRkb3duQ2xlYW5VcCB9IGZyb20gJy4vcmVzb3VyY2VfcmVsZWFzZS5qcyc7XHJcbmltcG9ydCBzZXJ2ZXIsIHsgc3RhcnRTZXJ2ZXIgfSBmcm9tICcuL3NlcnZlci9zZXJ2ZXIuanMnO1xyXG5pbXBvcnQgeyBwcmludExvZ28sIHByaW50VXNhZ2UgfSBmcm9tICcuL3V0aWxzLmpzJztcclxuXHJcbi8qKlxyXG4gKiBBdHRhY2hlcyBleGl0IGxpc3RlbmVycyB0byB0aGUgcHJvY2VzcywgZW5zdXJpbmcgcHJvcGVyIGNsZWFudXAgb2YgcmVzb3VyY2VzXHJcbiAqIGFuZCB0ZXJtaW5hdGlvbiBvbiBleGl0IHNpZ25hbHMuIEhhbmRsZXMgJ2V4aXQnLCAnU0lHSU5UJywgJ1NJR1RFUk0nLCBhbmRcclxuICogJ3VuY2F1Z2h0RXhjZXB0aW9uJyBldmVudHMuXHJcbiAqL1xyXG5jb25zdCBhdHRhY2hQcm9jZXNzRXhpdExpc3RlbmVycyA9ICgpID0+IHtcclxuICBsb2coMywgJ1twcm9jZXNzXSBBdHRhY2hpbmcgZXhpdCBsaXN0ZW5lcnMgdG8gdGhlIHByb2Nlc3MuJyk7XHJcblxyXG4gIC8vIEhhbmRsZXIgZm9yIHRoZSAnZXhpdCdcclxuICBwcm9jZXNzLm9uKCdleGl0JywgKGNvZGUpID0+IHtcclxuICAgIGxvZyg0LCBgUHJvY2VzcyBleGl0ZWQgd2l0aCBjb2RlICR7Y29kZX0uYCk7XHJcbiAgfSk7XHJcblxyXG4gIC8vIEhhbmRsZXIgZm9yIHRoZSAnU0lHSU5UJ1xyXG4gIHByb2Nlc3Mub24oJ1NJR0lOVCcsIGFzeW5jIChuYW1lLCBjb2RlKSA9PiB7XHJcbiAgICBsb2coNCwgYFRoZSAke25hbWV9IGV2ZW50IHdpdGggY29kZTogJHtjb2RlfS5gKTtcclxuICAgIGF3YWl0IHNodXRkb3duQ2xlYW5VcCgwKTtcclxuICB9KTtcclxuXHJcbiAgLy8gSGFuZGxlciBmb3IgdGhlICdTSUdURVJNJ1xyXG4gIHByb2Nlc3Mub24oJ1NJR1RFUk0nLCBhc3luYyAobmFtZSwgY29kZSkgPT4ge1xyXG4gICAgbG9nKDQsIGBUaGUgJHtuYW1lfSBldmVudCB3aXRoIGNvZGU6ICR7Y29kZX0uYCk7XHJcbiAgICBhd2FpdCBzaHV0ZG93bkNsZWFuVXAoMCk7XHJcbiAgfSk7XHJcblxyXG4gIC8vIEhhbmRsZXIgZm9yIHRoZSAnU0lHSFVQJ1xyXG4gIHByb2Nlc3Mub24oJ1NJR0hVUCcsIGFzeW5jIChuYW1lLCBjb2RlKSA9PiB7XHJcbiAgICBsb2coNCwgYFRoZSAke25hbWV9IGV2ZW50IHdpdGggY29kZTogJHtjb2RlfS5gKTtcclxuICAgIGF3YWl0IHNodXRkb3duQ2xlYW5VcCgwKTtcclxuICB9KTtcclxuXHJcbiAgLy8gSGFuZGxlciBmb3IgdGhlICd1bmNhdWdodEV4Y2VwdGlvbidcclxuICBwcm9jZXNzLm9uKCd1bmNhdWdodEV4Y2VwdGlvbicsIGFzeW5jIChlcnJvciwgbmFtZSkgPT4ge1xyXG4gICAgbG9nV2l0aFN0YWNrKDEsIGVycm9yLCBgVGhlICR7bmFtZX0gZXJyb3IuYCk7XHJcbiAgICBhd2FpdCBzaHV0ZG93bkNsZWFuVXAoMSk7XHJcbiAgfSk7XHJcbn07XHJcblxyXG4vKipcclxuICogSW5pdGlhbGl6ZXMgdGhlIGV4cG9ydCBwcm9jZXNzLiBUYXNrcyBzdWNoIGFzIGNvbmZpZ3VyaW5nIGxvZ2dpbmcsIGNoZWNraW5nXHJcbiAqIGNhY2hlIGFuZCBzb3VyY2VzLCBhbmQgaW5pdGlhbGl6aW5nIHRoZSBwb29sIG9mIHJlc291cmNlcyBoYXBwZW4gZHVyaW5nXHJcbiAqIHRoaXMgc3RhZ2UuIEZ1bmN0aW9uIHRoYXQgaXMgcmVxdWlyZWQgdG8gYmUgY2FsbGVkIGJlZm9yZSB0cnlpbmcgdG8gZXhwb3J0IGNoYXJ0cyBvciBzZXR0aW5nIGEgc2VydmVyLiBUaGUgYG9wdGlvbnNgIGlzIGFuIG9iamVjdCB0aGF0IGNvbnRhaW5zIGFsbCBvcHRpb25zLlxyXG4gKlxyXG4gKiBAcGFyYW0ge09iamVjdH0gb3B0aW9ucyAtIEFsbCBleHBvcnQgb3B0aW9ucy5cclxuICpcclxuICogQHJldHVybnMge1Byb21pc2U8T2JqZWN0Pn0gUHJvbWlzZSByZXNvbHZpbmcgdG8gdGhlIHVwZGF0ZWQgZXhwb3J0IG9wdGlvbnMuXHJcbiAqL1xyXG5jb25zdCBpbml0RXhwb3J0ID0gYXN5bmMgKG9wdGlvbnMpID0+IHtcclxuICAvLyBTZXQgdGhlIGFsbG93Q29kZUV4ZWN1dGlvbiBwZXIgZXhwb3J0IG1vZHVsZSBzY29wZVxyXG4gIHNldEFsbG93Q29kZUV4ZWN1dGlvbihcclxuICAgIG9wdGlvbnMuY3VzdG9tTG9naWMgJiYgb3B0aW9ucy5jdXN0b21Mb2dpYy5hbGxvd0NvZGVFeGVjdXRpb25cclxuICApO1xyXG5cclxuICAvLyBJbml0IHRoZSBsb2dnaW5nXHJcbiAgaW5pdExvZ2dpbmcob3B0aW9ucy5sb2dnaW5nKTtcclxuXHJcbiAgLy8gQXR0YWNoIHByb2Nlc3MnIGV4aXQgbGlzdGVuZXJzXHJcbiAgaWYgKG9wdGlvbnMub3RoZXIubGlzdGVuVG9Qcm9jZXNzRXhpdHMpIHtcclxuICAgIGF0dGFjaFByb2Nlc3NFeGl0TGlzdGVuZXJzKCk7XHJcbiAgfVxyXG5cclxuICAvLyBDaGVjayBpZiBjYWNoZSBuZWVkcyB0byBiZSB1cGRhdGVkXHJcbiAgYXdhaXQgY2hlY2tBbmRVcGRhdGVDYWNoZShvcHRpb25zKTtcclxuXHJcbiAgLy8gSW5pdCB0aGUgcG9vbFxyXG4gIGF3YWl0IGluaXRQb29sKHtcclxuICAgIHBvb2w6IG9wdGlvbnMucG9vbCB8fCB7XHJcbiAgICAgIG1pbldvcmtlcnM6IDEsXHJcbiAgICAgIG1heFdvcmtlcnM6IDFcclxuICAgIH0sXHJcbiAgICBwdXBwZXRlZXJBcmdzOiBvcHRpb25zLnB1cHBldGVlci5hcmdzIHx8IFtdXHJcbiAgfSk7XHJcblxyXG4gIC8vIFJldHVybiB1cGRhdGVkIG9wdGlvbnNcclxuICByZXR1cm4gb3B0aW9ucztcclxufTtcclxuXHJcbmV4cG9ydCBkZWZhdWx0IHtcclxuICAvLyBTZXJ2ZXJcclxuICBzZXJ2ZXIsXHJcbiAgc3RhcnRTZXJ2ZXIsXHJcblxyXG4gIC8vIEV4cG9ydGluZ1xyXG4gIGluaXRFeHBvcnQsXHJcbiAgc2luZ2xlRXhwb3J0LFxyXG4gIGJhdGNoRXhwb3J0LFxyXG4gIHN0YXJ0RXhwb3J0LFxyXG5cclxuICAvLyBQb29sXHJcbiAgaW5pdFBvb2wsXHJcbiAga2lsbFBvb2wsXHJcblxyXG4gIC8vIE90aGVyXHJcbiAgc2V0T3B0aW9ucyxcclxuICBzaHV0ZG93bkNsZWFuVXAsXHJcblxyXG4gIC8vIExvZ3NcclxuICBsb2csXHJcbiAgbG9nV2l0aFN0YWNrLFxyXG4gIHNldExvZ0xldmVsLFxyXG4gIGVuYWJsZUZpbGVMb2dnaW5nLFxyXG5cclxuICAvLyBVdGlsc1xyXG4gIG1hcFRvTmV3Q29uZmlnLFxyXG4gIG1hbnVhbENvbmZpZyxcclxuICBwcmludExvZ28sXHJcbiAgcHJpbnRVc2FnZVxyXG59O1xyXG4iXSwibmFtZXMiOlsic2NyaXB0c05hbWVzIiwiY29yZSIsIm1vZHVsZXMiLCJpbmRpY2F0b3JzIiwiZGVmYXVsdENvbmZpZyIsInB1cHBldGVlciIsImFyZ3MiLCJ2YWx1ZSIsInR5cGUiLCJkZXNjcmlwdGlvbiIsImhpZ2hjaGFydHMiLCJ2ZXJzaW9uIiwiZW52TGluayIsImNkblVSTCIsImNvcmVTY3JpcHRzIiwibW9kdWxlU2NyaXB0cyIsImluZGljYXRvclNjcmlwdHMiLCJjdXN0b21TY3JpcHRzIiwiZm9yY2VGZXRjaCIsImNhY2hlUGF0aCIsImV4cG9ydCIsImluZmlsZSIsImluc3RyIiwib3B0aW9ucyIsIm91dGZpbGUiLCJjb25zdHIiLCJkZWZhdWx0SGVpZ2h0IiwiZGVmYXVsdFdpZHRoIiwiZGVmYXVsdFNjYWxlIiwiaGVpZ2h0Iiwid2lkdGgiLCJzY2FsZSIsImdsb2JhbE9wdGlvbnMiLCJ0aGVtZU9wdGlvbnMiLCJiYXRjaCIsInJhc3Rlcml6YXRpb25UaW1lb3V0IiwiY3VzdG9tTG9naWMiLCJhbGxvd0NvZGVFeGVjdXRpb24iLCJhbGxvd0ZpbGVSZXNvdXJjZXMiLCJjdXN0b21Db2RlIiwiY2FsbGJhY2siLCJyZXNvdXJjZXMiLCJsb2FkQ29uZmlnIiwibGVnYWN5TmFtZSIsImNyZWF0ZUNvbmZpZyIsInNlcnZlciIsImVuYWJsZSIsImNsaU5hbWUiLCJob3N0IiwicG9ydCIsImJlbmNobWFya2luZyIsInByb3h5IiwidGltZW91dCIsInJhdGVMaW1pdGluZyIsIm1heFJlcXVlc3RzIiwid2luZG93IiwiZGVsYXkiLCJ0cnVzdFByb3h5Iiwic2tpcEtleSIsInNraXBUb2tlbiIsInNzbCIsImZvcmNlIiwiY2VydFBhdGgiLCJwb29sIiwibWluV29ya2VycyIsIm1heFdvcmtlcnMiLCJ3b3JrTGltaXQiLCJhY3F1aXJlVGltZW91dCIsImNyZWF0ZVRpbWVvdXQiLCJkZXN0cm95VGltZW91dCIsImlkbGVUaW1lb3V0IiwiY3JlYXRlUmV0cnlJbnRlcnZhbCIsInJlYXBlckludGVydmFsIiwibG9nZ2luZyIsImxldmVsIiwiZmlsZSIsImRlc3QiLCJ1aSIsInJvdXRlIiwib3RoZXIiLCJub2RlRW52IiwibGlzdGVuVG9Qcm9jZXNzRXhpdHMiLCJub0xvZ28iLCJoYXJkUmVzZXRQYWdlIiwiZGVidWciLCJoZWFkbGVzcyIsImRldnRvb2xzIiwibGlzdGVuVG9Db25zb2xlIiwiZHVtcGlvIiwic2xvd01vIiwiZGVidWdnaW5nUG9ydCIsInByb21wdHNDb25maWciLCJuYW1lIiwibWVzc2FnZSIsImluaXRpYWwiLCJqb2luIiwic2VwYXJhdG9yIiwiaW5zdHJ1Y3Rpb25zIiwiY2hvaWNlcyIsImhpbnQiLCJtaW4iLCJtYXgiLCJyb3VuZCIsImFic29sdXRlUHJvcHMiLCJuZXN0ZWRBcmdzIiwiY3JlYXRlTmVzdGVkQXJncyIsIm9iaiIsInByb3BDaGFpbiIsIk9iamVjdCIsImtleXMiLCJmb3JFYWNoIiwiayIsImluY2x1ZGVzIiwiZW50cnkiLCJzdWJzdHJpbmciLCJ1bmRlZmluZWQiLCJkb3RlbnYiLCJjb25maWciLCJ2IiwiZmlsdGVyQXJyYXkiLCJ6Iiwic3RyaW5nIiwidHJhbnNmb3JtIiwic3BsaXQiLCJtYXAiLCJ0cmltIiwiZmlsdGVyIiwibGVuZ3RoIiwiZW51bSIsInZhbHVlcyIsInJlZmluZSIsImlzTmFOIiwicGFyc2VGbG9hdCIsImVudnMiLCJvYmplY3QiLCJISUdIQ0hBUlRTX1ZFUlNJT04iLCJ0ZXN0IiwiSElHSENIQVJUU19DRE5fVVJMIiwic3RhcnRzV2l0aCIsIkhJR0hDSEFSVFNfQ09SRV9TQ1JJUFRTIiwiSElHSENIQVJUU19NT0RVTEVfU0NSSVBUUyIsIkhJR0hDSEFSVFNfSU5ESUNBVE9SX1NDUklQVFMiLCJISUdIQ0hBUlRTX0ZPUkNFX0ZFVENIIiwiSElHSENIQVJUU19DQUNIRV9QQVRIIiwiSElHSENIQVJUU19BRE1JTl9UT0tFTiIsIkVYUE9SVF9UWVBFIiwiRVhQT1JUX0NPTlNUUiIsIkVYUE9SVF9ERUZBVUxUX0hFSUdIVCIsIkVYUE9SVF9ERUZBVUxUX1dJRFRIIiwiRVhQT1JUX0RFRkFVTFRfU0NBTEUiLCJFWFBPUlRfUkFTVEVSSVpBVElPTl9USU1FT1VUIiwiQ1VTVE9NX0xPR0lDX0FMTE9XX0NPREVfRVhFQ1VUSU9OIiwiQ1VTVE9NX0xPR0lDX0FMTE9XX0ZJTEVfUkVTT1VSQ0VTIiwiU0VSVkVSX0VOQUJMRSIsIlNFUlZFUl9IT1NUIiwiU0VSVkVSX1BPUlQiLCJTRVJWRVJfQkVOQ0hNQVJLSU5HIiwiU0VSVkVSX1BST1hZX0hPU1QiLCJTRVJWRVJfUFJPWFlfUE9SVCIsIlNFUlZFUl9QUk9YWV9USU1FT1VUIiwiU0VSVkVSX1JBVEVfTElNSVRJTkdfRU5BQkxFIiwiU0VSVkVSX1JBVEVfTElNSVRJTkdfTUFYX1JFUVVFU1RTIiwiU0VSVkVSX1JBVEVfTElNSVRJTkdfV0lORE9XIiwiU0VSVkVSX1JBVEVfTElNSVRJTkdfREVMQVkiLCJTRVJWRVJfUkFURV9MSU1JVElOR19UUlVTVF9QUk9YWSIsIlNFUlZFUl9SQVRFX0xJTUlUSU5HX1NLSVBfS0VZIiwiU0VSVkVSX1JBVEVfTElNSVRJTkdfU0tJUF9UT0tFTiIsIlNFUlZFUl9TU0xfRU5BQkxFIiwiU0VSVkVSX1NTTF9GT1JDRSIsIlNFUlZFUl9TU0xfUE9SVCIsIlNFUlZFUl9TU0xfQ0VSVF9QQVRIIiwiUE9PTF9NSU5fV09SS0VSUyIsIlBPT0xfTUFYX1dPUktFUlMiLCJQT09MX1dPUktfTElNSVQiLCJQT09MX0FDUVVJUkVfVElNRU9VVCIsIlBPT0xfQ1JFQVRFX1RJTUVPVVQiLCJQT09MX0RFU1RST1lfVElNRU9VVCIsIlBPT0xfSURMRV9USU1FT1VUIiwiUE9PTF9DUkVBVEVfUkVUUllfSU5URVJWQUwiLCJQT09MX1JFQVBFUl9JTlRFUlZBTCIsIlBPT0xfQkVOQ0hNQVJLSU5HIiwiTE9HR0lOR19MRVZFTCIsIkxPR0dJTkdfRklMRSIsIkxPR0dJTkdfREVTVCIsIlVJX0VOQUJMRSIsIlVJX1JPVVRFIiwiT1RIRVJfTk9ERV9FTlYiLCJPVEhFUl9MSVNURU5fVE9fUFJPQ0VTU19FWElUUyIsIk9USEVSX05PX0xPR08iLCJPVEhFUl9IQVJEX1JFU0VUX1BBR0UiLCJERUJVR19FTkFCTEUiLCJERUJVR19IRUFETEVTUyIsIkRFQlVHX0RFVlRPT0xTIiwiREVCVUdfTElTVEVOX1RPX0NPTlNPTEUiLCJERUJVR19EVU1QSU8iLCJERUJVR19TTE9XX01PIiwiREVCVUdfREVCVUdHSU5HX1BPUlQiLCJwYXJ0aWFsIiwicGFyc2UiLCJwcm9jZXNzIiwiZW52IiwiY29sb3JzIiwidG9Db25zb2xlIiwidG9GaWxlIiwicGF0aENyZWF0ZWQiLCJsZXZlbHNEZXNjIiwidGl0bGUiLCJjb2xvciIsImxpc3RlbmVycyIsImtleSIsIm9wdGlvbiIsImVudHJpZXMiLCJsb2dUb0ZpbGUiLCJ0ZXh0cyIsInByZWZpeCIsImV4aXN0c1N5bmMiLCJta2RpclN5bmMiLCJhcHBlbmRGaWxlIiwiY29uY2F0IiwiZXJyb3IiLCJjb25zb2xlIiwibG9nIiwibmV3TGV2ZWwiLCJEYXRlIiwidG9TdHJpbmciLCJmbiIsImFwcGx5IiwibG9nV2l0aFN0YWNrIiwiY3VzdG9tTWVzc2FnZSIsIm1haW5NZXNzYWdlIiwic3RhY2tNZXNzYWdlIiwic3RhY2siLCJzbGljZSIsInNldExvZ0xldmVsIiwiZW5hYmxlRmlsZUxvZ2dpbmciLCJsb2dEZXN0IiwibG9nRmlsZSIsImVuZHNXaXRoIiwiX19kaXJuYW1lIiwiZmlsZVVSTFRvUGF0aCIsIlVSTCIsImRvY3VtZW50IiwicmVxdWlyZSIsInBhdGhUb0ZpbGVVUkwiLCJfX2ZpbGVuYW1lIiwiaHJlZiIsIl9kb2N1bWVudEN1cnJlbnRTY3JpcHQiLCJzcmMiLCJiYXNlVVJJIiwiZml4VHlwZSIsImZvcm1hdHMiLCJvdXRUeXBlIiwicG9wIiwiZmluZCIsInQiLCJoYW5kbGVSZXNvdXJjZXMiLCJhbGxvd2VkUHJvcHMiLCJoYW5kbGVkUmVzb3VyY2VzIiwiY29ycmVjdFJlc291cmNlcyIsImlzQ29ycmVjdEpTT04iLCJyZWFkRmlsZVN5bmMiLCJmaWxlcyIsInByb3BOYW1lIiwiaXRlbSIsImRhdGEiLCJwYXJzZWREYXRhIiwiSlNPTiIsInN0cmluZ2lmeSIsImRlZXBDb3B5IiwiY29weSIsIkFycmF5IiwiaXNBcnJheSIsInByb3RvdHlwZSIsImhhc093blByb3BlcnR5IiwiY2FsbCIsIm9wdGlvbnNTdHJpbmdpZnkiLCJhbGxvd0Z1bmN0aW9ucyIsInJlcGxhY2VBbGwiLCJwcmludFVzYWdlIiwiYm9sZCIsInllbGxvdyIsImN5Y2xlQ2F0ZWdvcmllcyIsImRlc2NOYW1lIiwiZ3JlZW4iLCJpIiwiYmx1ZSIsImNhdGVnb3J5IiwidG9VcHBlckNhc2UiLCJyZWQiLCJ0b0Jvb2xlYW4iLCJ3cmFwQXJvdW5kIiwicmVwbGFjZSIsIm1lYXN1cmVUaW1lIiwic3RhcnQiLCJocnRpbWUiLCJiaWdpbnQiLCJOdW1iZXIiLCJnZW5lcmFsT3B0aW9ucyIsImdldE9wdGlvbnMiLCJtZXJnZUNvbmZpZ09wdGlvbnMiLCJuZXdPcHRpb25zIiwibWVyZ2VkT3B0aW9ucyIsInVwZGF0ZURlZmF1bHRDb25maWciLCJjb25maWdPYmoiLCJjdXN0b21PYmoiLCJjdXN0b21WYWx1ZSIsImluaXRPcHRpb25zIiwiaXRlbXMiLCJyZWN1cnNpdmVQcm9wcyIsIm9iamVjdFRvVXBkYXRlIiwibmVzdGVkTmFtZXMiLCJzaGlmdCIsImFzc2lnbiIsImFzeW5jIiwiZmV0Y2giLCJ1cmwiLCJyZXF1ZXN0T3B0aW9ucyIsIlByb21pc2UiLCJyZXNvbHZlIiwicmVqZWN0IiwicHJvdG9jb2wiLCJodHRwcyIsImh0dHAiLCJnZXRQcm90b2NvbCIsImdldCIsInJlcyIsIm9uIiwiY2h1bmsiLCJ0ZXh0IiwiRXhwb3J0RXJyb3IiLCJFcnJvciIsImNvbnN0cnVjdG9yIiwic3VwZXIiLCJ0aGlzIiwic2V0RXJyb3IiLCJzdGF0dXNDb2RlIiwiY2FjaGUiLCJhY3RpdmVNYW5pZmVzdCIsInNvdXJjZXMiLCJoY1ZlcnNpb24iLCJleHRyYWN0VmVyc2lvbiIsImluZGV4T2YiLCJmZXRjaEFuZFByb2Nlc3NTY3JpcHQiLCJzY3JpcHQiLCJmZXRjaGVkTW9kdWxlcyIsInNob3VsZFRocm93RXJyb3IiLCJyZXNwb25zZSIsInVwZGF0ZUNhY2hlIiwiaGlnaGNoYXJ0c09wdGlvbnMiLCJwcm94eU9wdGlvbnMiLCJzb3VyY2VQYXRoIiwicHJveHlBZ2VudCIsInByb3h5SG9zdCIsInByb3h5UG9ydCIsIkh0dHBzUHJveHlBZ2VudCIsImFnZW50IiwiYWxsRmV0Y2hQcm9taXNlcyIsImFsbCIsImZldGNoU2NyaXB0cyIsImMiLCJtIiwid3JpdGVGaWxlU3luYyIsImNoZWNrQW5kVXBkYXRlQ2FjaGUiLCJtYW5pZmVzdFBhdGgiLCJyZXF1ZXN0VXBkYXRlIiwibWFuaWZlc3QiLCJtb2R1bGVNYXAiLCJudW1iZXJPZk1vZHVsZXMiLCJzb21lIiwibW9kdWxlTmFtZSIsIm5ld01hbmlmZXN0Iiwic2F2ZUNvbmZpZ1RvTWFuaWZlc3QiLCJnZXRDYWNoZVBhdGgiLCJjYWNoZSQxIiwibmV3VmVyc2lvbiIsInNldHVwSGlnaGNoYXJ0cyIsIkhpZ2hjaGFydHMiLCJhbmltT2JqZWN0IiwiZHVyYXRpb24iLCJ0cmlnZ2VyRXhwb3J0IiwiY2hhcnRPcHRpb25zIiwiZGlzcGxheUVycm9ycyIsIl9kaXNwbGF5RXJyb3JzIiwibWVyZ2UiLCJzZXRPcHRpb25zIiwid3JhcCIsInNldE9wdGlvbnNPYmoiLCJGdW5jdGlvbiIsImNoYXJ0IiwiYW5pbWF0aW9uIiwic3RySW5qIiwiaXNSZW5kZXJDb21wbGV0ZSIsIkNoYXJ0IiwicHJvY2VlZCIsInVzZXJPcHRpb25zIiwiY2IiLCJleHBvcnRpbmciLCJlbmFibGVkIiwicGxvdE9wdGlvbnMiLCJzZXJpZXMiLCJsYWJlbCIsInRvb2x0aXAiLCJvbkhpZ2hjaGFydHNSZW5kZXIiLCJhZGRFdmVudCIsIlNlcmllcyIsImZpbmFsT3B0aW9ucyIsImZpbmFsQ2FsbGJhY2siLCJkZWZhdWx0T3B0aW9ucyIsInByb3AiLCJ0ZW1wbGF0ZSIsImZzIiwiYnJvd3NlciIsIm5ld1BhZ2UiLCJwYWdlIiwic2V0Q2FjaGVFbmFibGVkIiwic2V0UGFnZUNvbnRlbnQiLCJzZXRDb250ZW50Iiwid2FpdFVudGlsIiwiYWRkU2NyaXB0VGFnIiwicGF0aCIsImV2YWx1YXRlIiwiX19iYXNlZGlyIiwic2V0QXNDb25maWciLCJwdXBwZXRlZXJFeHBvcnQiLCJpbmplY3RlZFJlc291cmNlcyIsImNsZWFySW5qZWN0ZWQiLCJyZXNvdXJjZSIsImRpc3Bvc2UiLCJvbGRDaGFydHMiLCJjaGFydHMiLCJvbGRDaGFydCIsImRlc3Ryb3kiLCJzY3JpcHRzVG9SZW1vdmUiLCJnZXRFbGVtZW50c0J5VGFnTmFtZSIsInN0eWxlc1RvUmVtb3ZlIiwibGlua3NUb1JlbW92ZSIsImVsZW1lbnQiLCJyZW1vdmUiLCJleHBvcnRPcHRpb25zIiwiZGVidWdnZXIiLCJpc1NWRyIsInN2Z1RlbXBsYXRlIiwiaW5qZWN0ZWRKcyIsImpzIiwicHVzaCIsImNvbnRlbnQiLCJpc0xvY2FsIiwianNSZXNvdXJjZSIsImluamVjdGVkQ3NzIiwiY3NzIiwiY3NzSW1wb3J0cyIsIm1hdGNoIiwiY3NzSW1wb3J0UGF0aCIsImNzc1Jlc291cmNlIiwiYWRkU3R5bGVUYWciLCJzaXplIiwic3ZnRWxlbWVudCIsInF1ZXJ5U2VsZWN0b3IiLCJjaGFydEhlaWdodCIsImJhc2VWYWwiLCJjaGFydFdpZHRoIiwiYm9keSIsInN0eWxlIiwiem9vbSIsIm1hcmdpbiIsInZpZXdwb3J0SGVpZ2h0IiwiTWF0aCIsImNlaWwiLCJ2aWV3cG9ydFdpZHRoIiwieCIsInkiLCIkZXZhbCIsImdldEJvdW5kaW5nQ2xpZW50UmVjdCIsInRydW5jIiwiZ2V0Q2xpcFJlZ2lvbiIsInNldFZpZXdwb3J0IiwiZGV2aWNlU2NhbGVGYWN0b3IiLCJvdXRlckhUTUwiLCJjcmVhdGVTVkciLCJlbmNvZGluZyIsImNsaXAiLCJyYWNlIiwic2NyZWVuc2hvdCIsImNhcHR1cmVCZXlvbmRWaWV3cG9ydCIsImZ1bGxQYWdlIiwib3B0aW1pemVGb3JTcGVlZCIsInF1YWxpdHkiLCJvbWl0QmFja2dyb3VuZCIsIl9yZXNvbHZlIiwic2V0VGltZW91dCIsImNyZWF0ZUltYWdlIiwiZW11bGF0ZU1lZGlhVHlwZSIsInBkZiIsImNyZWF0ZVBERiIsInN0YXRzIiwicGVyZm9ybWVkRXhwb3J0cyIsImV4cG9ydEF0dGVtcHRzIiwiZXhwb3J0RnJvbVN2Z0F0dGVtcHRzIiwidGltZVNwZW50IiwiZHJvcHBlZEV4cG9ydHMiLCJzcGVudEF2ZXJhZ2UiLCJwb29sQ29uZmlnIiwiZmFjdG9yeSIsImNyZWF0ZSIsImlkIiwidXVpZCIsInN0YXJ0RGF0ZSIsImdldFRpbWUiLCJicm93c2VyTmV3UGFnZSIsImlzQ2xvc2VkIiwiZXJyb3JNZXNzYWdlIiwiaW5uZXJIVE1MIiwid29ya0NvdW50IiwicmFuZG9tIiwidmFsaWRhdGUiLCJ3b3JrZXJIYW5kbGUiLCJjbG9zZSIsImluaXRQb29sIiwicHVwcGV0ZWVyQXJncyIsImVuYWJsZWREZWJ1ZyIsImxhdW5jaE9wdGlvbnMiLCJ1c2VyRGF0YURpciIsImhhbmRsZVNJR0lOVCIsImhhbmRsZVNJR1RFUk0iLCJoYW5kbGVTSUdIVVAiLCJ3YWl0Rm9ySW5pdGlhbFBhZ2UiLCJkZWZhdWx0Vmlld3BvcnQiLCJ0cnlDb3VudCIsIm9wZW4iLCJsYXVuY2giLCJjcmVhdGVCcm93c2VyIiwicGFyc2VJbnQiLCJQb29sIiwiYWNxdWlyZVRpbWVvdXRNaWxsaXMiLCJjcmVhdGVUaW1lb3V0TWlsbGlzIiwiZGVzdHJveVRpbWVvdXRNaWxsaXMiLCJpZGxlVGltZW91dE1pbGxpcyIsImNyZWF0ZVJldHJ5SW50ZXJ2YWxNaWxsaXMiLCJyZWFwSW50ZXJ2YWxNaWxsaXMiLCJwcm9wYWdhdGVDcmVhdGVFcnJvciIsImhhcmRSZXNldCIsImdvdG8iLCJjbGVhclBhZ2UiLCJldmVudElkIiwiaW5pdGlhbFJlc291cmNlcyIsImFjcXVpcmUiLCJwcm9taXNlIiwicmVsZWFzZSIsImtpbGxQb29sIiwid29ya2VyIiwidXNlZCIsImRlc3Ryb3llZCIsImNvbm5lY3RlZCIsImJyb3dzZXJDbG9zZSIsInBvc3RXb3JrIiwiZ2V0UG9vbEluZm8iLCJhY3F1aXJlQ291bnRlciIsInBheWxvYWQiLCJyZXF1ZXN0SWQiLCJ3b3JrU3RhcnQiLCJleHBvcnRDb3VudGVyIiwicmVzdWx0IiwiZXhwb3J0VGltZSIsImdldFBvb2xJbmZvSlNPTiIsIm51bUZyZWUiLCJudW1Vc2VkIiwiYXZhaWxhYmxlIiwicGVuZGluZyIsIm51bVBlbmRpbmdBY3F1aXJlcyIsInBvb2wkMSIsInN0YXJ0RXhwb3J0Iiwic2V0dGluZ3MiLCJlbmRDYWxsYmFjayIsInN2ZyIsImluaXRFeHBvcnRTZXR0aW5ncyIsImV4cG9ydEFzU3RyaW5nIiwiaW5wdXQiLCJKU0RPTSIsIkRPTVB1cmlmeSIsInNhbml0aXplIiwiQUREX1RBR1MiLCJkb1N0cmFpZ2h0SW5qZWN0IiwiZG9FeHBvcnQiLCJmaW5kQ2hhcnRTaXplIiwicHJlY2lzaW9uIiwibXVsdGlwbGllciIsInBvdyIsInJvdW5kTnVtYmVyIiwic291cmNlSGVpZ2h0Iiwic291cmNlV2lkdGgiLCJwYXJhbSIsImNoYXJ0SnNvbiIsImN1c3RvbUxvZ2ljT3B0aW9ucyIsImFsbG93Q29kZUV4ZWN1dGlvblNjb3BlZCIsIm9wdGlvbnNOYW1lIiwic3RyaW5nVG9FeHBvcnQiLCJjaGFydEpTT04iLCJpbnRlcnZhbElkcyIsImNsZWFyQWxsSW50ZXJ2YWxzIiwiY2xlYXJJbnRlcnZhbCIsImxvZ0Vycm9yTWlkZGxld2FyZSIsInJlcSIsIm5leHQiLCJyZXR1cm5FcnJvck1pZGRsZXdhcmUiLCJzdENvZGUiLCJzdGF0dXMiLCJqc29uIiwicmF0ZUxpbWl0IiwiYXBwIiwibGltaXRDb25maWciLCJtc2ciLCJyYXRlT3B0aW9ucyIsImxpbWl0ZXIiLCJ3aW5kb3dNcyIsImRlbGF5TXMiLCJoYW5kbGVyIiwicmVxdWVzdCIsImZvcm1hdCIsInNlbmQiLCJkZWZhdWx0Iiwic2tpcCIsInF1ZXJ5IiwiYWNjZXNzX3Rva2VuIiwidXNlIiwiSHR0cEVycm9yIiwic2V0U3RhdHVzIiwicmV2ZXJzZWRNaW1lIiwicG5nIiwianBlZyIsImdpZiIsInJlcXVlc3RzQ291bnRlciIsImJlZm9yZVJlcXVlc3QiLCJhZnRlclJlcXVlc3QiLCJkb0NhbGxiYWNrcyIsImNhbGxiYWNrcyIsInVuaXF1ZUlkIiwiY2FsbFJlc3BvbnNlIiwiZXhwb3J0SGFuZGxlciIsInN0b3BDb3VudGVyIiwiaGVhZGVycyIsImNvbm5lY3Rpb24iLCJyZW1vdGVBZGRyZXNzIiwiY29ubmVjdGlvbkFib3J0ZWQiLCJzb2NrZXQiLCJ0b0xvd2VyQ2FzZSIsInN1YnN0ciIsImI2NCIsIm5vRG93bmxvYWQiLCJwYXR0ZXJuIiwiaXNQcml2YXRlUmFuZ2VVcmxGb3VuZCIsImluZm8iLCJyZW1vdmVBbGxMaXN0ZW5lcnMiLCJCdWZmZXIiLCJmcm9tIiwiaGVhZGVyIiwiYXR0YWNobWVudCIsInBhcmFtcyIsImZpbGVuYW1lIiwicGtnRmlsZSIsInBhdGhlciIsInNlcnZlclN0YXJ0VGltZSIsInN1Y2Nlc3NSYXRlcyIsImFkZEhlYWx0aFJvdXRlcyIsInNldEludGVydmFsIiwic3VjY2Vzc1JhdGlvIiwiXyIsInBlcmlvZCIsIm1vdmluZ0F2ZXJhZ2UiLCJyZWR1Y2UiLCJhIiwiYiIsImJvb3RUaW1lIiwidXB0aW1lIiwiZmxvb3IiLCJoaWdoY2hhcnRzVmVyc2lvbiIsImF2ZXJhZ2VQcm9jZXNzaW5nVGltZSIsImZhaWxlZEV4cG9ydHMiLCJzdWNlc3NSYXRpbyIsInRvRml4ZWQiLCJzdmdFeHBvcnRBdHRlbXB0cyIsImpzb25FeHBvcnRBdHRlbXB0cyIsImFjdGl2ZVNlcnZlcnMiLCJNYXAiLCJleHByZXNzIiwiZGlzYWJsZSIsImNvcnMiLCJzdG9yYWdlIiwibXVsdGVyIiwibWVtb3J5U3RvcmFnZSIsInVwbG9hZCIsImxpbWl0cyIsImZpZWxkU2l6ZSIsImxpbWl0IiwidXJsZW5jb2RlZCIsImV4dGVuZGVkIiwibm9uZSIsImF0dGFjaFNlcnZlckVycm9ySGFuZGxlcnMiLCJzdGFydFNlcnZlciIsInNlcnZlckNvbmZpZyIsImh0dHBTZXJ2ZXIiLCJjcmVhdGVTZXJ2ZXIiLCJsaXN0ZW4iLCJzZXQiLCJjZXJ0IiwiZnNQcm9taXNlcyIsInJlYWRGaWxlIiwicG9zaXgiLCJodHRwc1NlcnZlciIsIk5hTiIsInN0YXRpYyIsImhlYWx0aFJvdXRlIiwicG9zdCIsImV4cG9ydFJvdXRlcyIsInNlbmRGaWxlIiwidWlSb3V0ZSIsImFkbWluVG9rZW4iLCJ0b2tlbiIsInZTd2l0Y2hSb3V0ZSIsImVycm9ySGFuZGxlciIsImNsb3NlU2VydmVycyIsImRlbGV0ZSIsImdldFNlcnZlcnMiLCJlbmFibGVSYXRlTGltaXRpbmciLCJnZXRFeHByZXNzIiwiZ2V0QXBwIiwibWlkZGxld2FyZXMiLCJzaHV0ZG93bkNsZWFuVXAiLCJleGl0Q29kZSIsImFsbFNldHRsZWQiLCJleGl0IiwiaW5kZXgiLCJpbml0RXhwb3J0IiwiaW5pdExvZ2dpbmciLCJjb2RlIiwic2luZ2xlRXhwb3J0IiwiYmF0Y2hFeHBvcnQiLCJiYXRjaEZ1bmN0aW9ucyIsInBhaXIiLCJjb25maWdJbmRleCIsImZpbmRJbmRleCIsImFyZyIsImZpbGVOYW1lIiwibG9hZENvbmZpZ0ZpbGUiLCJzaG93VXNhZ2UiLCJwcm9wZXJ0aWVzQ2hhaW4iLCJhcmd1bWVudFR5cGUiLCJwYWlyQXJndW1lbnRWYWx1ZSIsIm1hcFRvTmV3Q29uZmlnIiwib2xkT3B0aW9ucyIsIm1hbnVhbENvbmZpZyIsImNvbmZpZ0ZpbGVOYW1lIiwiY29uZmlnRmlsZSIsImNob2ljZSIsInByb21wdHMiLCJvblN1Ym1pdCIsInAiLCJjYXRlZ29yaWVzIiwicXVlc3Rpb25zQ291bnRlciIsImFsbFF1ZXN0aW9ucyIsInNlY3Rpb24iLCJwcm9tcHQiLCJhbnN3ZXIiLCJtb2R1bGUiLCJwcm9taXNlcyIsIndyaXRlRmlsZSIsInByaW50TG9nbyIsInBhY2thZ2VWZXJzaW9uIl0sIm1hcHBpbmdzIjoiNnRCQWVPLE1BQU1BLEVBQWUsQ0FDMUJDLEtBQU0sQ0FBQyxhQUFjLGtCQUFtQixpQkFDeENDLFFBQVMsQ0FDUCxRQUNBLE1BQ0EsUUFDQSxZQUNBLGNBQ0EsdUJBQ0EsZ0JBRUEsZUFDQSxRQUNBLE9BQ0EsYUFDQSxtQkFDQSxlQUNBLGNBQ0EsVUFDQSxVQUNBLGNBQ0EsV0FDQSxVQUNBLFlBQ0EsY0FDQSxZQUNBLHNCQUNBLFNBQ0EsU0FDQSxXQUNBLGFBQ0EsWUFDQSxlQUNBLHlCQUNBLFNBQ0EsZUFDQSxZQUNBLGtCQUNBLFNBQ0EsY0FD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 +"use strict";require("colors");var e=require("fs"),t=require("path"),r=require("https-proxy-agent"),o=require("prompts"),i=require("dotenv"),s=require("zod"),n=require("url"),a=require("http"),l=require("https"),c=require("tarn"),p=require("uuid"),u=require("puppeteer"),h=require("jsdom"),d=require("dompurify"),g=require("cors"),m=require("express"),f=require("multer"),v=require("express-rate-limit"),y="undefined"!=typeof document?document.currentScript:null;const b={core:["highcharts","highcharts-more","highcharts-3d"],modules:["stock","map","gantt","exporting","export-data","parallel-coordinates","accessibility","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","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"],indicators:["indicators-all"]},w={puppeteer:{args:{value:["--allow-running-insecure-content","--ash-no-nudges","--autoplay-policy=user-gesture-required","--block-new-web-contents","--disable-accelerated-2d-canvas","--disable-background-networking","--disable-background-timer-throttling","--disable-backgrounding-occluded-windows","--disable-breakpad","--disable-checker-imaging","--disable-client-side-phishing-detection","--disable-component-extensions-with-background-pages","--disable-component-update","--disable-default-apps","--disable-dev-shm-usage","--disable-domain-reliability","--disable-extensions","--disable-features=CalculateNativeWinOcclusion,InterestFeedContentSuggestions,WebOTP","--disable-hang-monitor","--disable-ipc-flooding-protection","--disable-logging","--disable-notifications","--disable-offer-store-unmasked-wallet-cards","--disable-popup-blocking","--disable-print-preview","--disable-prompt-on-repost","--disable-renderer-backgrounding","--disable-search-engine-choice-screen","--disable-session-crashed-bubble","--disable-setuid-sandbox","--disable-site-isolation-trials","--disable-speech-api","--disable-sync","--enable-unsafe-webgpu","--hide-crash-restore-bubble","--hide-scrollbars","--metrics-recording-only","--mute-audio","--no-default-browser-check","--no-first-run","--no-pings","--no-sandbox","--no-startup-window","--no-zygote","--password-store=basic","--process-per-tab","--use-mock-keychain"],type:"string[]",description:"Arguments array to send to Puppeteer."}},highcharts:{version:{value:"latest",type:"string",envLink:"HIGHCHARTS_VERSION",description:"The Highcharts version to be used."},cdnURL:{value:"https://code.highcharts.com/",type:"string",envLink:"HIGHCHARTS_CDN_URL",description:"The CDN URL for Highcharts scripts to be used."},coreScripts:{value:b.core,type:"string[]",envLink:"HIGHCHARTS_CORE_SCRIPTS",description:"The core Highcharts scripts to fetch."},moduleScripts:{value:b.modules,type:"string[]",envLink:"HIGHCHARTS_MODULE_SCRIPTS",description:"The modules of Highcharts to fetch."},indicatorScripts:{value:b.indicators,type:"string[]",envLink:"HIGHCHARTS_INDICATOR_SCRIPTS",description:"The indicators of Highcharts to fetch."},customScripts:{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 custom scripts or dependencies to fetch."},forceFetch:{value:!1,type:"boolean",envLink:"HIGHCHARTS_FORCE_FETCH",description:"The flag to determine whether to refetch all scripts after each server rerun."},cachePath:{value:".cache",type:"string",envLink:"HIGHCHARTS_CACHE_PATH",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:{value:"png",type:"string",envLink:"EXPORT_TYPE",description:"The file export format. It can be jpeg, png, pdf, or svg."},constr:{value:"chart",type:"string",envLink:"EXPORT_CONSTR",description:"The constructor to use. Can be chart, stockChart, mapChart, or ganttChart."},defaultHeight:{value:400,type:"number",envLink:"EXPORT_DEFAULT_HEIGHT",description:"the default height of the exported chart. Used when no value is set."},defaultWidth:{value:600,type:"number",envLink:"EXPORT_DEFAULT_WIDTH",description:"The default width of the exported chart. Used when no value is set."},defaultScale:{value:1,type:"number",envLink:"EXPORT_DEFAULT_SCALE",description:"The default scale of the exported chart. Used when no value is set."},height:{value:!1,type:"number",description:"The height of the exported chart, overriding the option in the chart settings."},width:{value:!1,type:"number",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:{value:1500,type:"number",envLink:"EXPORT_RASTERIZATION_TIMEOUT",description:"The duration in milliseconds to wait for rendering a webpage."}},customLogic:{allowCodeExecution:{value:!1,type:"boolean",envLink:"CUSTOM_LOGIC_ALLOW_CODE_EXECUTION",description:"Controls whether the execution of arbitrary code is allowed during the exporting process."},allowFileResources:{value:!1,type:"boolean",envLink:"CUSTOM_LOGIC_ALLOW_FILE_RESOURCES",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:{value:!1,type:"boolean",envLink:"SERVER_ENABLE",cliName:"enableServer",description:"When set to true, the server starts on the local IP address 0.0.0.0."},host:{value:"0.0.0.0",type:"string",envLink:"SERVER_HOST",description:"The hostname of the server. Additionally, it starts a server on the provided hostname."},port:{value:7801,type:"number",envLink:"SERVER_PORT",description:"The server port when enabled."},benchmarking:{value:!1,type:"boolean",envLink:"SERVER_BENCHMARKING",cliName:"serverBenchmarking",description:"Indicates whether to display the duration, in milliseconds, of specific actions that occur on the server while serving a request."},proxy:{host:{value:!1,type:"string",envLink:"SERVER_PROXY_HOST",cliName:"proxyHost",description:"The host of the proxy server to use, if it exists."},port:{value:8080,type:"number",envLink:"SERVER_PROXY_PORT",cliName:"proxyPort",description:"The port of the proxy server to use, if it exists."},timeout:{value:5e3,type:"number",envLink:"SERVER_PROXY_TIMEOUT",cliName:"proxyTimeout",description:"The timeout for the proxy server to use, if it exists."}},rateLimiting:{enable:{value:!1,type:"boolean",envLink:"SERVER_RATE_LIMITING_ENABLE",cliName:"enableRateLimiting",description:"Enables rate limiting for the server."},maxRequests:{value:10,type:"number",envLink:"SERVER_RATE_LIMITING_MAX_REQUESTS",legacyName:"rateLimit",description:"The maximum number of requests allowed in one minute."},window:{value:1,type:"number",envLink:"SERVER_RATE_LIMITING_WINDOW",description:"The time window, in minutes, for the rate limiting."},delay:{value:0,type:"number",envLink:"SERVER_RATE_LIMITING_DELAY",description:"The delay duration for each successive request before reaching the maximum limit."},trustProxy:{value:!1,type:"boolean",envLink:"SERVER_RATE_LIMITING_TRUST_PROXY",description:"Set this to true if the server is behind a load balancer."},skipKey:{value:!1,type:"string",envLink:"SERVER_RATE_LIMITING_SKIP_KEY",description:"Allows bypassing the rate limiter and should be provided with the skipToken argument."},skipToken:{value:!1,type:"string",envLink:"SERVER_RATE_LIMITING_SKIP_TOKEN",description:"Allows bypassing the rate limiter and should be provided with the skipKey argument."}},ssl:{enable:{value:!1,type:"boolean",envLink:"SERVER_SSL_ENABLE",cliName:"enableSsl",description:"Enables or disables the SSL protocol."},force:{value:!1,type:"boolean",envLink:"SERVER_SSL_FORCE",cliName:"sslForce",legacyName:"sslOnly",description:"When set to true, the server is forced to serve only over HTTPS."},port:{value:443,type:"number",envLink:"SERVER_SSL_PORT",cliName:"sslPort",description:"The port on which to run the SSL server."},certPath:{value:!1,type:"string",envLink:"SERVER_SSL_CERT_PATH",legacyName:"sslPath",description:"The path to the SSL certificate/key file."}}},pool:{minWorkers:{value:4,type:"number",envLink:"POOL_MIN_WORKERS",description:"The number of minimum and initial pool workers to spawn."},maxWorkers:{value:8,type:"number",envLink:"POOL_MAX_WORKERS",legacyName:"workers",description:"The number of maximum pool workers to spawn."},workLimit:{value:40,type:"number",envLink:"POOL_WORK_LIMIT",description:"The number of work pieces that can be performed before restarting the worker process."},acquireTimeout:{value:5e3,type:"number",envLink:"POOL_ACQUIRE_TIMEOUT",description:"The duration, in milliseconds, to wait for acquiring a resource."},createTimeout:{value:5e3,type:"number",envLink:"POOL_CREATE_TIMEOUT",description:"The duration, in milliseconds, to wait for creating a resource."},destroyTimeout:{value:5e3,type:"number",envLink:"POOL_DESTROY_TIMEOUT",description:"The duration, in milliseconds, to wait for destroying a resource."},idleTimeout:{value:3e4,type:"number",envLink:"POOL_IDLE_TIMEOUT",description:"The duration, in milliseconds, after which an idle resource is destroyed."},createRetryInterval:{value:200,type:"number",envLink:"POOL_CREATE_RETRY_INTERVAL",description:"The duration, in milliseconds, to wait before retrying the create process in case of a failure."},reaperInterval:{value:1e3,type:"number",envLink:"POOL_REAPER_INTERVAL",description:"The duration, in milliseconds, after which the check for idle resources to destroy is triggered."},benchmarking:{value:!1,type:"boolean",envLink:"POOL_BENCHMARKING",cliName:"poolBenchmarking",description:"Indicate whether to show statistics for the pool of resources or not."}},logging:{level:{value:4,type:"number",envLink:"LOGGING_LEVEL",cliName:"logLevel",description:"The logging level to be used."},file:{value:"highcharts-export-server.log",type:"string",envLink:"LOGGING_FILE",cliName:"logFile",description:"The name of a log file. The logDest option also needs to be set to enable file logging."},dest:{value:"log/",type:"string",envLink:"LOGGING_DEST",cliName:"logDest",description:"The path to store log files. This also enables file logging."}},ui:{enable:{value:!1,type:"boolean",envLink:"UI_ENABLE",cliName:"enableUi",description:"Enables or disables the user interface (UI) for the export server."},route:{value:"/",type:"string",envLink:"UI_ROUTE",cliName:"uiRoute",description:"The endpoint route to which the user interface (UI) should be attached."}},other:{nodeEnv:{value:"production",type:"string",envLink:"OTHER_NODE_ENV",description:"The type of Node.js environment."},listenToProcessExits:{value:!0,type:"boolean",envLink:"OTHER_LISTEN_TO_PROCESS_EXITS",description:"Decides whether or not to attach process.exit handlers."},noLogo:{value:!1,type:"boolean",envLink:"OTHER_NO_LOGO",description:"Skip printing the logo on a startup. Will be replaced by a simple text."},hardResetPage:{value:!1,type:"boolean",envLink:"OTHER_HARD_RESET_PAGE",description:"Decides if the page content should be reset entirely."}},debug:{enable:{value:!1,type:"boolean",envLink:"DEBUG_ENABLE",cliName:"enableDebug",description:"Enables or disables debug mode for the underlying browser."},headless:{value:!0,type:"boolean",envLink:"DEBUG_HEADLESS",description:"Controls the mode in which the browser is launched when in the debug mode."},devtools:{value:!1,type:"boolean",envLink:"DEBUG_DEVTOOLS",description:"Decides whether to enable DevTools when the browser is in a headful state."},listenToConsole:{value:!1,type:"boolean",envLink:"DEBUG_LISTEN_TO_CONSOLE",description:"Decides whether to enable a listener for console messages sent from the browser."},dumpio:{value:!1,type:"boolean",envLink:"DEBUG_DUMPIO",description:"Redirects browser process stdout and stderr to process.stdout and process.stderr."},slowMo:{value:0,type:"number",envLink:"DEBUG_SLOW_MO",description:"Slows down Puppeteer operations by the specified number of milliseconds."},debuggingPort:{value:9222,type:"number",envLink:"DEBUG_DEBUGGING_PORT",description:"Specifies the debugging port."}}},E={puppeteer:[{type:"list",name:"args",message:"Puppeteer arguments",initial:w.puppeteer.args.value.join(","),separator:","}],highcharts:[{type:"text",name:"version",message:"Highcharts version",initial:w.highcharts.version.value},{type:"text",name:"cdnURL",message:"The URL of CDN",initial:w.highcharts.cdnURL.value},{type:"multiselect",name:"coreScripts",message:"Available core scripts",instructions:"Space: Select specific, A: Select all, Enter: Confirm.",choices:w.highcharts.coreScripts.value},{type:"multiselect",name:"moduleScripts",message:"Available module scripts",instructions:"Space: Select specific, A: Select all, Enter: Confirm.",choices:w.highcharts.moduleScripts.value},{type:"multiselect",name:"indicatorScripts",message:"Available indicator scripts",instructions:"Space: Select specific, A: Select all, Enter: Confirm.",choices:w.highcharts.indicatorScripts.value},{type:"list",name:"customScripts",message:"Custom scripts",initial:w.highcharts.customScripts.value.join(","),separator:","},{type:"toggle",name:"forceFetch",message:"Force re-fetch the scripts",initial:w.highcharts.forceFetch.value},{type:"text",name:"cachePath",message:"The path to the cache directory",initial:w.highcharts.cachePath.value}],export:[{type:"select",name:"type",message:"The default export file type",hint:`Default: ${w.export.type.value}`,initial:0,choices:["png","jpeg","pdf","svg"]},{type:"select",name:"constr",message:"The default constructor for Highcharts",hint:`Default: ${w.export.constr.value}`,initial:0,choices:["chart","stockChart","mapChart","ganttChart"]},{type:"number",name:"defaultHeight",message:"The default fallback height of the exported chart",initial:w.export.defaultHeight.value},{type:"number",name:"defaultWidth",message:"The default fallback width of the exported chart",initial:w.export.defaultWidth.value},{type:"number",name:"defaultScale",message:"The default fallback scale of the exported chart",initial:w.export.defaultScale.value,min:.1,max:5},{type:"number",name:"rasterizationTimeout",message:"The rendering webpage timeout in milliseconds",initial:w.export.rasterizationTimeout.value}],customLogic:[{type:"toggle",name:"allowCodeExecution",message:"Enable execution of custom code",initial:w.customLogic.allowCodeExecution.value},{type:"toggle",name:"allowFileResources",message:"Enable file resources",initial:w.customLogic.allowFileResources.value}],server:[{type:"toggle",name:"enable",message:"Starts the server on 0.0.0.0",initial:w.server.enable.value},{type:"text",name:"host",message:"Server hostname",initial:w.server.host.value},{type:"number",name:"port",message:"Server port",initial:w.server.port.value},{type:"toggle",name:"benchmarking",message:"Enable server benchmarking",initial:w.server.benchmarking.value},{type:"text",name:"proxy.host",message:"The host of the proxy server to use",initial:w.server.proxy.host.value},{type:"number",name:"proxy.port",message:"The port of the proxy server to use",initial:w.server.proxy.port.value},{type:"number",name:"proxy.timeout",message:"The timeout for the proxy server to use",initial:w.server.proxy.timeout.value},{type:"toggle",name:"rateLimiting.enable",message:"Enable rate limiting",initial:w.server.rateLimiting.enable.value},{type:"number",name:"rateLimiting.maxRequests",message:"The maximum requests allowed per minute",initial:w.server.rateLimiting.maxRequests.value},{type:"number",name:"rateLimiting.window",message:"The rate-limiting time window in minutes",initial:w.server.rateLimiting.window.value},{type:"number",name:"rateLimiting.delay",message:"The delay for each successive request before reaching the maximum",initial:w.server.rateLimiting.delay.value},{type:"toggle",name:"rateLimiting.trustProxy",message:"Set to true if behind a load balancer",initial:w.server.rateLimiting.trustProxy.value},{type:"text",name:"rateLimiting.skipKey",message:"Allows bypassing the rate limiter when provided with the skipToken argument",initial:w.server.rateLimiting.skipKey.value},{type:"text",name:"rateLimiting.skipToken",message:"Allows bypassing the rate limiter when provided with the skipKey argument",initial:w.server.rateLimiting.skipToken.value},{type:"toggle",name:"ssl.enable",message:"Enable SSL protocol",initial:w.server.ssl.enable.value},{type:"toggle",name:"ssl.force",message:"Force serving only over HTTPS",initial:w.server.ssl.force.value},{type:"number",name:"ssl.port",message:"SSL server port",initial:w.server.ssl.port.value},{type:"text",name:"ssl.certPath",message:"The path to find the SSL certificate/key",initial:w.server.ssl.certPath.value}],pool:[{type:"number",name:"minWorkers",message:"The initial number of workers to spawn",initial:w.pool.minWorkers.value},{type:"number",name:"maxWorkers",message:"The maximum number of workers to spawn",initial:w.pool.maxWorkers.value},{type:"number",name:"workLimit",message:"The pieces of work that can be performed before restarting a Puppeteer process",initial:w.pool.workLimit.value},{type:"number",name:"acquireTimeout",message:"The number of milliseconds to wait for acquiring a resource",initial:w.pool.acquireTimeout.value},{type:"number",name:"createTimeout",message:"The number of milliseconds to wait for creating a resource",initial:w.pool.createTimeout.value},{type:"number",name:"destroyTimeout",message:"The number of milliseconds to wait for destroying a resource",initial:w.pool.destroyTimeout.value},{type:"number",name:"idleTimeout",message:"The number of milliseconds after an idle resource is destroyed",initial:w.pool.idleTimeout.value},{type:"number",name:"createRetryInterval",message:"The retry interval in milliseconds after a create process fails",initial:w.pool.createRetryInterval.value},{type:"number",name:"reaperInterval",message:"The reaper interval in milliseconds after triggering the check for idle resources to destroy",initial:w.pool.reaperInterval.value},{type:"toggle",name:"benchmarking",message:"Enable benchmarking for a resource pool",initial:w.pool.benchmarking.value}],logging:[{type:"number",name:"level",message:"The log level (0: silent, 1: error, 2: warning, 3: notice, 4: verbose, 5: benchmark)",initial:w.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:w.logging.file.value},{type:"text",name:"dest",message:"The path to log files. Enables file logging",initial:w.logging.dest.value}],ui:[{type:"toggle",name:"enable",message:"Enable UI for the export server",initial:w.ui.enable.value},{type:"text",name:"route",message:"A route to attach the UI",initial:w.ui.route.value}],other:[{type:"text",name:"nodeEnv",message:"The type of Node.js environment",initial:w.other.nodeEnv.value},{type:"toggle",name:"listenToProcessExits",message:"Set to false to skip attaching process.exit handlers",initial:w.other.listenToProcessExits.value},{type:"toggle",name:"noLogo",message:"Skip printing the logo on startup. Replaced by simple text",initial:w.other.noLogo.value},{type:"toggle",name:"hardResetPage",message:"Decides if the page content should be reset entirely",initial:w.other.hardResetPage.value}],debug:[{type:"toggle",name:"enable",message:"Enables debug mode for the browser instance",initial:w.debug.enable.value},{type:"toggle",name:"headless",message:"The mode setting for the browser",initial:w.debug.headless.value},{type:"toggle",name:"devtools",message:"The DevTools for the headful browser",initial:w.debug.devtools.value},{type:"toggle",name:"listenToConsole",message:"The event listener for console messages from the browser",initial:w.debug.listenToConsole.value},{type:"toggle",name:"dumpio",message:"Redirects the browser stdout and stderr to NodeJS process",initial:w.debug.dumpio.value},{type:"number",name:"slowMo",message:"Puppeteer operations slow down in milliseconds",initial:w.debug.slowMo.value},{type:"number",name:"debuggingPort",message:"The port number for debugging",initial:w.debug.debuggingPort.value}]},T=["options","globalOptions","themeOptions","resources","payload"],S={},x=(e,t="")=>{Object.keys(e).forEach((r=>{if(!["puppeteer","highcharts"].includes(r)){const o=e[r];void 0===o.value?x(o,`${t}.${r}`):(S[o.cliName||r]=`${t}.${r}`.substring(1),void 0!==o.legacyName&&(S[o.legacyName]=`${t}.${r}`.substring(1)))}}))};x(w),i.config();const R=e=>s.z.string().transform((t=>t.split(",").map((e=>e.trim())).filter((t=>e.includes(t))))).transform((e=>e.length?e:void 0)),L=()=>s.z.enum(["true","false",""]).transform((e=>""!==e?"true"===e:void 0)),O=e=>s.z.enum([...e,""]).transform((e=>""!==e?e:void 0)),_=()=>s.z.string().trim().refine((e=>!["false","undefined","null","NaN"].includes(e)||""===e),(e=>({message:`The string contains forbidden values, received '${e}'`}))).transform((e=>""!==e?e:void 0)),k=()=>s.z.string().trim().refine((e=>""===e||!isNaN(parseFloat(e))&&parseFloat(e)>0),(e=>({message:`The value must be numeric and positive, received '${e}'`}))).transform((e=>""!==e?parseFloat(e):void 0)),I=()=>s.z.string().trim().refine((e=>""===e||!isNaN(parseFloat(e))&&parseFloat(e)>=0),(e=>({message:`The value must be numeric and non-negative, received '${e}'`}))).transform((e=>""!==e?parseFloat(e):void 0)),C=s.z.object({HIGHCHARTS_VERSION:s.z.string().trim().refine((e=>/^(latest|\d+(\.\d+){0,2})$/.test(e)||""===e),(e=>({message:`HIGHCHARTS_VERSION must be 'latest', a major version, or in the form XX.YY.ZZ, received '${e}'`}))).transform((e=>""!==e?e:void 0)),HIGHCHARTS_CDN_URL:s.z.string().trim().refine((e=>e.startsWith("https://")||e.startsWith("http://")||""===e),(e=>({message:`Invalid value for HIGHCHARTS_CDN_URL. It should start with http:// or https://, received '${e}'`}))).transform((e=>""!==e?e:void 0)),HIGHCHARTS_CORE_SCRIPTS:R(b.core),HIGHCHARTS_MODULE_SCRIPTS:R(b.modules),HIGHCHARTS_INDICATOR_SCRIPTS:R(b.indicators),HIGHCHARTS_FORCE_FETCH:L(),HIGHCHARTS_CACHE_PATH:_(),HIGHCHARTS_ADMIN_TOKEN:_(),EXPORT_TYPE:O(["jpeg","png","pdf","svg"]),EXPORT_CONSTR:O(["chart","stockChart","mapChart","ganttChart"]),EXPORT_DEFAULT_HEIGHT:k(),EXPORT_DEFAULT_WIDTH:k(),EXPORT_DEFAULT_SCALE:k(),EXPORT_RASTERIZATION_TIMEOUT:I(),CUSTOM_LOGIC_ALLOW_CODE_EXECUTION:L(),CUSTOM_LOGIC_ALLOW_FILE_RESOURCES:L(),SERVER_ENABLE:L(),SERVER_HOST:_(),SERVER_PORT:k(),SERVER_BENCHMARKING:L(),SERVER_PROXY_HOST:_(),SERVER_PROXY_PORT:k(),SERVER_PROXY_TIMEOUT:I(),SERVER_RATE_LIMITING_ENABLE:L(),SERVER_RATE_LIMITING_MAX_REQUESTS:I(),SERVER_RATE_LIMITING_WINDOW:I(),SERVER_RATE_LIMITING_DELAY:I(),SERVER_RATE_LIMITING_TRUST_PROXY:L(),SERVER_RATE_LIMITING_SKIP_KEY:_(),SERVER_RATE_LIMITING_SKIP_TOKEN:_(),SERVER_SSL_ENABLE:L(),SERVER_SSL_FORCE:L(),SERVER_SSL_PORT:k(),SERVER_SSL_CERT_PATH:_(),POOL_MIN_WORKERS:I(),POOL_MAX_WORKERS:I(),POOL_WORK_LIMIT:k(),POOL_ACQUIRE_TIMEOUT:I(),POOL_CREATE_TIMEOUT:I(),POOL_DESTROY_TIMEOUT:I(),POOL_IDLE_TIMEOUT:I(),POOL_CREATE_RETRY_INTERVAL:I(),POOL_REAPER_INTERVAL:I(),POOL_BENCHMARKING:L(),LOGGING_LEVEL:s.z.string().trim().refine((e=>""===e||!isNaN(parseFloat(e))&&parseFloat(e)>=0&&parseFloat(e)<=5),(e=>({message:`Invalid value for LOGGING_LEVEL. We only accept values from 0 to 5 as logging levels, received '${e}'`}))).transform((e=>""!==e?parseFloat(e):void 0)),LOGGING_FILE:_(),LOGGING_DEST:_(),UI_ENABLE:L(),UI_ROUTE:_(),OTHER_NODE_ENV:O(["development","production","test"]),OTHER_LISTEN_TO_PROCESS_EXITS:L(),OTHER_NO_LOGO:L(),OTHER_HARD_RESET_PAGE:L(),DEBUG_ENABLE:L(),DEBUG_HEADLESS:L(),DEBUG_DEVTOOLS:L(),DEBUG_LISTEN_TO_CONSOLE:L(),DEBUG_DUMPIO:L(),DEBUG_SLOW_MO:I(),DEBUG_DEBUGGING_PORT:k()}).partial().parse(process.env),A=["red","yellow","blue","gray","green"];let N={toConsole:!0,toFile:!1,pathCreated:!1,levelsDesc:[{title:"error",color:A[0]},{title:"warning",color:A[1]},{title:"notice",color:A[2]},{title:"verbose",color:A[3]},{title:"benchmark",color:A[4]}],listeners:[]};for(const[e,t]of Object.entries(w.logging))N[e]=t.value;const P=(t,r)=>{N.toFile&&(N.pathCreated||(!e.existsSync(N.dest)&&e.mkdirSync(N.dest),N.pathCreated=!0),e.appendFile(`${N.dest}${N.file}`,[r].concat(t).join(" ")+"\n",(e=>{e&&(console.log(`[logger] Unable to write to log file: ${e}`),N.toFile=!1)})))},H=(...e)=>{const[t,...r]=e,{level:o,levelsDesc:i}=N;if(5!==t&&(0===t||t>o||o>i.length))return;const s=`${(new Date).toString().split("(")[0].trim()} [${i[t-1].title}] -`;N.listeners.forEach((e=>{e(s,r.join(" "))})),N.toConsole&&console.log.apply(void 0,[s.toString()[N.levelsDesc[t-1].color]].concat(r)),P(r,s)},$=(e,t,r)=>{const o=r||t.message,{level:i,levelsDesc:s}=N;if(0===e||e>i||i>s.length)return;const n=`${(new Date).toString().split("(")[0].trim()} [${s[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];N.toConsole&&console.log.apply(void 0,[n.toString()[N.levelsDesc[e-1].color]].concat([o[A[e-1]],"\n",a])),N.listeners.forEach((e=>{e(n,l.join(" "))})),P(l,n)},U=e=>{e>=0&&e<=N.levelsDesc.length&&(N.level=e)},D=(e,t)=>{if(N={...N,dest:e||N.dest,file:t||N.file,toFile:!0},0===N.dest.length)return H(1,"[logger] File logging initialization: no path supplied.");N.dest.endsWith("/")||(N.dest+="/")},j=n.fileURLToPath(new URL("../.","undefined"==typeof document?require("url").pathToFileURL(__filename).href:y&&y.src||new URL("index.cjs",document.baseURI).href)),G=(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"},F=(t=!1,r)=>{const o=["js","css","files"];let i=t,s=!1;if(r&&t.endsWith(".json"))try{i=M(e.readFileSync(t,"utf8"))}catch(e){return $(2,e,"[cli] No resources found.")}else i=M(t),i&&!r&&delete i.files;for(const e in i)o.includes(e)?s||(s=!0):delete i[e];return s?(i.files&&(i.files=i.files.map((e=>e.trim())),(!i.files||i.files.length<=0)&&delete i.files),i):H(3,"[cli] No resources found.")};function M(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 q=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]=q(e[r]));return t},W=(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 V(){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(w).forEach((t=>{["puppeteer","highcharts"].includes(t)||(console.log(`\n${t.toUpperCase()}`.red),e(w[t]))})),console.log("\n")}const B=e=>!["false","undefined","null","NaN","0",""].includes(e)&&!!e,X=(t,r)=>{if(t&&"string"==typeof t)return(t=t.trim()).endsWith(".js")?!!r&&X(e.readFileSync(t,"utf8")):t.startsWith("function()")||t.startsWith("function ()")||t.startsWith("()=>")||t.startsWith("() =>")?`(${t})()`:t.replace(/;$/,"")},z=()=>{const e=process.hrtime.bigint();return()=>Number(process.hrtime.bigint()-e)/1e6};let K={};const J=()=>K,Y=(e,t,r=[])=>{const o=q(e);for(const[e,s]of Object.entries(t))o[e]="object"!=typeof(i=s)||Array.isArray(i)||null===i||r.includes(e)||void 0===o[e]?void 0!==s?s:o[e]:Y(o[e],s,r);var i;return o};function Q(e,t={},r=""){Object.keys(e).forEach((o=>{const i=e[o],s=t&&t[o];void 0===i.value?Q(i,s,`${r}.${o}`):(void 0!==s&&(i.value=s),i.envLink in C&&void 0!==C[i.envLink]&&(i.value=C[i.envLink]))}))}function Z(e){let t={};for(const[r,o]of Object.entries(e))t[r]=Object.prototype.hasOwnProperty.call(o,"value")?o.value:Z(o);return t}function ee(e,t,r){for(;t.length>1;){const o=t.shift();return Object.prototype.hasOwnProperty.call(e,o)||(e[o]={}),e[o]=ee(Object.assign({},e[o]),t,r),e}return e[t[0]]=r,e}async function te(e,t={}){return new Promise(((r,o)=>{const i=(e=>e.startsWith("https")?l:a)(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)}))}))}class re 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 oe={cdnURL:"https://code.highcharts.com/",activeManifest:{},sources:"",hcVersion:""},ie=e=>e.sources.substring(0,e.sources.indexOf("*/")).replace("/*","").replace("*/","").replace(/\n/g,"").trim(),se=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=await te(`${e}.js`,t);if(200===i.statusCode&&"string"==typeof i.text){if(r){r[e.replace(/(.*)\/|(.*)modules\/|stock\/(.*)indicators\/|maps\/(.*)modules\//gi,"")]=1}return i.text}if(o)throw new re(`Could not fetch the ${e}.js. The script might not exist in the requested version (status code: ${i.statusCode}).`).setError(i);return H(2,`[cache] Could not fetch the ${e}.js. The script might not exist in the requested version.`),""},ne=async(t,o,i)=>{const s=t.version,n="latest"!==s&&s?`${s}/`:"",a=t.cdnURL||oe.cdnURL;H(3,`[cache] Updating cache version to Highcharts: ${n||"latest"}.`);const l={};try{return oe.sources=await(async(e,t,o,i,s)=>{let n;const a=i.host,l=i.port;if(a&&l)try{n=new r.HttpsProxyAgent({host:a,port:l})}catch(e){throw new re("[cache] Could not create a Proxy Agent.").setError(e)}const c=n?{agent:n,timeout:C.SERVER_PROXY_TIMEOUT}:{},p=[...e.map((e=>se(`${e}`,c,s,!0))),...t.map((e=>se(`${e}`,c,s))),...o.map((e=>se(`${e}`,c)))];return(await Promise.all(p)).join(";\n")})([...t.coreScripts.map((e=>`${a}${n}${e}`))],[...t.moduleScripts.map((e=>"map"===e?`${a}maps/${n}modules/${e}`:`${a}${n}modules/${e}`)),...t.indicatorScripts.map((e=>`${a}stock/${n}indicators/${e}`))],t.customScripts,o,l),oe.hcVersion=ie(oe),e.writeFileSync(i,oe.sources),l}catch(e){throw new re("[cache] Unable to update the local Highcharts cache.").setError(e)}},ae=async r=>{const{highcharts:o,server:i}=r,s=t.join(j,o.cachePath);let n;const a=t.join(s,"manifest.json"),l=t.join(s,"sources.js");if(!e.existsSync(s)&&e.mkdirSync(s),!e.existsSync(a)||o.forceFetch)H(3,"[cache] Fetching and caching Highcharts dependencies."),n=await ne(o,i.proxy,l);else{let t=!1;const r=JSON.parse(e.readFileSync(a));if(r.modules&&Array.isArray(r.modules)){const e={};r.modules.forEach((t=>e[t]=1)),r.modules=e}const{coreScripts:s,moduleScripts:c,indicatorScripts:p}=o,u=s.length+c.length+p.length;r.version!==o.version?(H(2,"[cache] A Highcharts version mismatch in the cache, need to re-fetch."),t=!0):Object.keys(r.modules||{}).length!==u?(H(2,"[cache] The cache and the requested modules do not match, need to re-fetch."),t=!0):t=(c||[]).some((e=>{if(!r.modules[e])return H(2,`[cache] The ${e} is missing in the cache, need to re-fetch.`),!0})),t?n=await ne(o,i.proxy,l):(H(3,"[cache] Dependency cache is up to date, proceeding."),oe.sources=e.readFileSync(l,"utf8"),n=r.modules,oe.hcVersion=ie(oe))}await(async(r,o)=>{const i={version:r.version,modules:o||{}};oe.activeManifest=i,H(3,"[cache] Writing a new manifest.");try{e.writeFileSync(t.join(j,r.cachePath,"manifest.json"),JSON.stringify(i),"utf8")}catch(e){throw new re("[cache] Error writing the cache manifest.").setError(e)}})(o,n)},le=()=>t.join(j,J().highcharts.cachePath),ce=()=>oe.hcVersion;function pe(){Highcharts.animObject=function(){return{duration:0}}}async function ue(e,t,r){window._displayErrors=r;const{getOptions:o,merge:i,setOptions:s,wrap:n}=Highcharts;Highcharts.setOptionsObj=i(!1,{},o()),t.customLogic.customCode&&new Function(t.customLogic.customCode)();const a={animation:!1};t.export.strInj&&(a.height=e.chart.height,a.width=e.chart.width),window.isRenderComplete=!1,n(Highcharts.Chart.prototype,"init",(function(e,t,r){((t=i(t,{exporting:{enabled:!1},plotOptions:{series:{label:{enabled:!1}}},tooltip:{}})).series||[]).forEach((function(e){e.animation=!1})),window.onHighchartsRender||(window.onHighchartsRender=Highcharts.addEvent(this,"render",(()=>{window.isRenderComplete=!0}))),e.apply(this,[t,r])})),n(Highcharts.Series.prototype,"init",(function(e,t,r){e.apply(this,[t,r])}));const l=t.export.strInj?new Function(`return ${t.export.strInj}`)():e,c=i(!1,JSON.parse(t.export.themeOptions),l,{chart:a}),p=t.customLogic.callback?new Function(`return ${t.customLogic.callback}`)():void 0,u=JSON.parse(t.export.globalOptions);u&&s(u),Highcharts[t.export.constr||"chart"]("container",c,p);const h=o();for(const e in h)"function"!=typeof h[e]&&delete h[e];s(Highcharts.setOptionsObj),Highcharts.setOptionsObj={}}const he=e.readFileSync(j+"/templates/template.html","utf8");let de;async function ge(){if(!de)return!1;const e=await de.newPage();return await e.setCacheEnabled(!1),await fe(e),function(e){const{debug:t}=J();t.enable&&t.listenToConsole&&e.on("console",(e=>{console.log(`[debug] ${e.text()}`)}));e.on("pageerror",(async t=>{await e.$eval("#container",((e,t)=>{window._displayErrors&&(e.innerHTML=t)}),`

Chart input data error:

${t.toString()}`)}))}(e),e}async function me(e,t){for(const e of t)await e.dispose();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()}const[...e]=document.getElementsByTagName("script"),[,...t]=document.getElementsByTagName("style"),[...r]=document.getElementsByTagName("link");for(const o of[...e,...t,...r])o.remove()}))}async function fe(e){await e.setContent(he,{waitUntil:"domcontentloaded"}),await e.addScriptTag({path:`${le()}/sources.js`}),await e.evaluate(pe)}const ve=async(e,t,r,o)=>e.evaluate(ue,t,r,o);var ye=async(r,o,i)=>{let s=[];try{H(4,"[export] Determining export path.");const n=i.export,a=n?.options?.chart?.displayErrors&&oe.activeManifest.modules.debugger;let l;if(o.indexOf&&(o.indexOf("=0||o.indexOf("=0)){if(H(4,"[export] Treating as SVG."),"svg"===n.type)return o;l=!0,await r.setContent((e=>`\n\n\n \n \n Highcharts Export\n \n \n \n
\n ${e}\n
\n \n\n\n`)(o),{waitUntil:"domcontentloaded"})}else H(4,"[export] Treating as config."),n.strInj?await ve(r,{chart:{height:n.height,width:n.width}},i,a):(o.chart.height=n.height,o.chart.width=n.width,await ve(r,o,i,a));s=await async function(r,o){const i=[],s=o.customLogic.resources;if(s){const n=[];if(s.js&&n.push({content:s.js}),s.files)for(const t of s.files){const r=!t.startsWith("http");n.push(r?{content:e.readFileSync(t,"utf8")}:{url:t})}for(const e of n)try{i.push(await r.addScriptTag(e))}catch(e){$(2,e,"[export] The JS resource cannot be loaded.")}n.length=0;const a=[];if(s.css){let e=s.css.match(/@import\s*([^;]*);/g);if(e)for(let r of e)r&&(r=r.replace("url(","").replace("@import","").replace(/"/g,"").replace(/'/g,"").replace(/;/,"").replace(/\)/g,"").trim(),r.startsWith("http")?a.push({url:r}):o.customLogic.allowFileResources&&a.push({path:t.join(j,r)}));a.push({content:s.css.replace(/@import\s*([^;]*);/g,"")||" "});for(const e of a)try{i.push(await r.addStyleTag(e))}catch(e){$(2,e,"[export] The CSS resource cannot be loaded.")}a.length=0}}return i}(r,i);const c=l?await r.evaluate((e=>{const t=document.querySelector("#chart-container svg:first-of-type"),r=t.height.baseVal.value*e,o=t.width.baseVal.value*e;return document.body.style.zoom=e,document.body.style.margin="0px",{chartHeight:r,chartWidth:o}}),parseFloat(n.scale)):await r.evaluate((()=>{const{chartHeight:e,chartWidth:t}=window.Highcharts.charts[0];return document.body.style.zoom=1,{chartHeight:e,chartWidth:t}})),p=Math.ceil(c.chartHeight||n.height),u=Math.ceil(c.chartWidth||n.width),{x:h,y:d}=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)}})))(r);let g;if(await r.setViewport({height:p,width:u,deviceScaleFactor:l?1:parseFloat(n.scale)}),"svg"===n.type)g=await(e=>e.$eval("#container svg:first-of-type",(e=>e.outerHTML)))(r);else if(["png","jpeg"].includes(n.type))g=await((e,t,r,o,i)=>Promise.race([e.screenshot({type:t,encoding:r,clip:o,captureBeyondViewport:!0,fullPage:!1,optimizeForSpeed:!0,..."png"!==t?{quality:80}:{},omitBackground:"png"==t}),new Promise(((e,t)=>setTimeout((()=>t(new re("Rasterization timeout"))),i||1500)))]))(r,n.type,"base64",{width:u,height:p,x:h,y:d},n.rasterizationTimeout);else{if("pdf"!==n.type)throw new re(`[export] Unsupported output format ${n.type}.`);g=await(async(e,t,r,o,i)=>(await e.emulateMediaType("screen"),Promise.race([e.pdf({height:t+1,width:r,encoding:o}),new Promise(((e,t)=>setTimeout((()=>t(new re("Rasterization timeout"))),i||1500)))])))(r,p,u,"base64",n.rasterizationTimeout)}return await me(r,s),g}catch(e){return await me(r,s),e}};let be=!1;const we={performedExports:0,exportAttempts:0,exportFromSvgAttempts:0,timeSpent:0,droppedExports:0,spentAverage:0};let Ee={};const Te={create:async()=>{let e=!1;const t=p.v4(),r=(new Date).getTime();try{if(e=await ge(),!e||e.isClosed())throw new re("The page is invalid or closed.");H(3,`[pool] Successfully created a worker ${t} - took ${(new Date).getTime()-r} ms.`)}catch(e){throw new re("Error encountered when creating a new page.").setError(e)}return{id:t,page:e,workCount:Math.round(Math.random()*(Ee.workLimit/2))}},validate:async e=>!(Ee.workLimit&&++e.workCount>Ee.workLimit)||(H(3,`[pool] Worker failed validation: exceeded work limit (limit is ${Ee.workLimit}).`),!1),destroy:async e=>{H(3,`[pool] Destroying pool entry ${e.id}.`),e.page&&await e.page.close()}},Se=async e=>{if(Ee=e&&e.pool?{...e.pool}:{},await async function(e){const{enable:t,...r}=J().debug,o={headless:"shell",userDataDir:"./tmp/",args:e,handleSIGINT:!1,handleSIGTERM:!1,handleSIGHUP:!1,waitForInitialPage:!1,defaultViewport:null,...t&&r};if(!de){let e=0;const r=async()=>{try{H(3,`[browser] Attempting to get a browser instance (try ${++e}).`),de=await u.launch(o)}catch(t){if($(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(),t&&H(3,"[browser] Launched browser in debug mode.")}catch(e){throw new re("[browser] Maximum retries to open a browser instance reached.").setError(e)}if(!de)throw new re("[browser] Cannot find a browser to open.")}return de}(e.puppeteerArgs),H(3,`[pool] Initializing pool with workers: min ${Ee.minWorkers}, max ${Ee.maxWorkers}.`),be)return H(4,"[pool] Already initialized, please kill it before creating a new one.");parseInt(Ee.minWorkers)>parseInt(Ee.maxWorkers)&&(Ee.minWorkers=Ee.maxWorkers);try{be=new c.Pool({...Te,min:parseInt(Ee.minWorkers),max:parseInt(Ee.maxWorkers),acquireTimeoutMillis:Ee.acquireTimeout,createTimeoutMillis:Ee.createTimeout,destroyTimeoutMillis:Ee.destroyTimeout,idleTimeoutMillis:Ee.idleTimeout,createRetryIntervalMillis:Ee.createRetryInterval,reapIntervalMillis:Ee.reaperInterval,propagateCreateError:!1}),be.on("release",(async e=>{await async function(e,t=!1){try{e.isClosed()||(t?(await e.goto("about:blank",{waitUntil:"domcontentloaded"}),await fe(e)):await e.evaluate((()=>{document.body.innerHTML='
'})))}catch(e){$(2,e,"[browser] Could not clear the content of the page.")}}(e.page,!1),H(4,`[pool] Releasing a worker with ID ${e.id}.`)})),be.on("destroySuccess",((e,t)=>{H(4,`[pool] Destroyed a worker with ID ${t.id}.`)}));const e=[];for(let t=0;t{be.release(e)})),H(3,"[pool] The pool is ready"+(e.length?` with ${e.length} initial resources waiting.`:"."))}catch(e){throw new re("[pool] Could not create the pool of workers.").setError(e)}};async function xe(){if(H(3,"[pool] Killing pool with all workers and closing browser."),be){for(const e of be.used)be.release(e.resource);be.destroyed||(await be.destroy(),H(4,"[browser] Destroyed the pool of resources."))}await async function(){de?.connected&&await de.close(),H(4,"[browser] Closed the browser.")}()}const Re=async(e,t)=>{let r;try{if(H(4,"[pool] Work received, starting to process."),++we.exportAttempts,Ee.benchmarking&&Oe(),!be)throw new re("Work received, but pool has not been started.");const o=z();try{H(4,"[pool] Acquiring a worker handle."),r=await be.acquire().promise,t.server.benchmarking&&H(5,t.payload?.requestId?`[benchmark] Request with ID ${t.payload?.requestId} -`:"[benchmark]",`Acquired a worker handle: ${o()}ms.`)}catch(e){throw new re((t.payload?.requestId?`For request with ID ${t.payload?.requestId} - `:"")+`Error encountered when acquiring an available entry: ${o()}ms.`).setError(e)}if(H(4,"[pool] Acquired a worker handle."),!r.page)throw new re("Resolved worker page is invalid: the pool setup is wonky.");let i=(new Date).getTime();H(4,`[pool] Starting work on pool entry with ID ${r.id}.`);const s=z(),n=await ye(r.page,e,t);if(n instanceof Error)throw"Rasterization timeout"===n.message&&(r.page.close(),r.page=await ge()),new re((t.payload?.requestId?`For request with ID ${t.payload?.requestId} - `:"")+`Error encountered during export: ${s()}ms.`).setError(n);t.server.benchmarking&&H(5,t.payload?.requestId?`[benchmark] Request with ID ${t.payload?.requestId} -`:"[benchmark]",`Exported a chart sucessfully: ${s()}ms.`),be.release(r);const a=(new Date).getTime()-i;return we.timeSpent+=a,we.spentAverage=we.timeSpent/++we.performedExports,H(4,`[pool] Work completed in ${a} ms.`),{result:n,options:t}}catch(e){throw++we.droppedExports,r&&be.release(r),new re(`[pool] In pool.postWork: ${e.message}`).setError(e)}},Le=()=>({min:be.min,max:be.max,all:be.numFree()+be.numUsed(),available:be.numFree(),used:be.numUsed(),pending:be.numPendingAcquires()});function Oe(){const{min:e,max:t,all:r,available:o,used:i,pending:s}=Le();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 all created resources: ${r}.`),H(5,`[pool] The number of available resources: ${o}.`),H(5,`[pool] The number of acquired resources: ${i}.`),H(5,`[pool] The number of resources waiting to be acquired: ${s}.`)}var _e=Le,ke=()=>we;let Ie=!1;const Ce=async(t,r)=>{H(4,"[chart] Starting the exporting process.");const o=((e,t={})=>{let r={};return e.svg?(r=q(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=Y(t,e,T),r.export.outfile=r.export?.outfile||`chart.${r.export?.type||"png"}`,r})(t,J()),i=o.export;if(o.payload?.svg&&""!==o.payload.svg)try{H(4,"[chart] Attempting to export from a SVG input.");const e=He(function(e){const t=new h.JSDOM("").window;return d(t).sanitize(e,{ADD_TAGS:["foreignObject"]})}(o.payload.svg),o,r);return++we.exportFromSvgAttempts,e}catch(e){return r(new re("[chart] Error loading SVG input.").setError(e))}if(i.infile&&i.infile.length)try{return H(4,"[chart] Attempting to export from an input file."),o.export.instr=e.readFileSync(i.infile,"utf8"),He(o.export.instr.trim(),o,r)}catch(e){return r(new re("[chart] Error loading input file.").setError(e))}if(i.instr&&""!==i.instr||i.options&&""!==i.options)try{return H(4,"[chart] Attempting to export from a raw input."),B(o.customLogic?.allowCodeExecution)?Pe(o,r):"string"==typeof i.instr?He(i.instr.trim(),o,r):Ne(o,i.instr||i.options,r)}catch(e){return r(new re("[chart] Error loading raw input.").setError(e))}return r(new re("[chart] No valid input specified. Check if at least one of the following parameters is correctly set: 'infile', 'instr', 'options', or 'svg'."))},Ae=e=>{const{chart:t,exporting:r}=e.export?.options||M(e.export?.instr),o=M(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 s={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(s))s[e]="string"==typeof t?+t.replace(/px|%/gi,""):t;return s},Ne=async(t,r,o,i)=>{let{export:s,customLogic:n}=t;const a="boolean"==typeof n.allowCodeExecution?n.allowCodeExecution:Ie;if(n){if(a)if("string"==typeof t.customLogic.resources)t.customLogic.resources=F(t.customLogic.resources,B(t.customLogic.allowFileResources));else if(!t.customLogic.resources)try{const r=e.readFileSync("resources.json","utf8");t.customLogic.resources=F(r,B(t.customLogic.allowFileResources))}catch(e){$(2,e,"[chart] Unable to load the default resources.json file.")}}else n=t.customLogic={};if(!a&&n){if(n.callback||n.resources||n.customCode)return o(new re("[chart] The 'callback', 'resources' and 'customCode' options have been disabled for this server."));n.callback=!1,n.resources=!1,n.customCode=!1}if(r&&(r.chart=r.chart||{},r.exporting=r.exporting||{},r.exporting.enabled=!1),s.constr=s.constr||"chart",s.type=G(s.type,s.outfile),"svg"===s.type&&(s.width=!1),["globalOptions","themeOptions"].forEach((t=>{try{s&&s[t]&&("string"==typeof s[t]&&s[t].endsWith(".json")?s[t]=M(e.readFileSync(s[t],"utf8"),!0):s[t]=M(s[t],!0))}catch(e){s[t]={},$(2,e,`[chart] The '${t}' cannot be loaded.`)}})),n.allowCodeExecution)try{n.customCode=X(n.customCode,n.allowFileResources)}catch(e){$(2,e,"[chart] The 'customCode' cannot be loaded.")}if(n&&n.callback&&n.callback?.indexOf("{")<0)if(n.allowFileResources)try{n.callback=e.readFileSync(n.callback,"utf8")}catch(e){n.callback=!1,$(2,e,"[chart] The 'callback' cannot be loaded.")}else n.callback=!1;t.export={...t.export,...Ae(t)};try{return o(!1,await Re(s.strInj||r||i,t))}catch(e){return o(e)}},Pe=(e,t)=>{try{let r,o=e.export.instr||e.export.options;return"string"!=typeof o&&(r=o=W(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,Ne(e,!1,t)}catch(r){return t(new re(`[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))}},He=(e,t,r)=>{const{allowCodeExecution:o}=t.customLogic;if(e.indexOf("=0||e.indexOf("=0)return H(4,"[chart] Parsing input as SVG."),Ne(t,!1,r,e);try{const o=JSON.parse(e.replaceAll(/\t|\n|\r/g," "));return Ne(t,o,r)}catch(e){return B(o)?Pe(t,r):r(new re("[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))}},$e=[],Ue=()=>{H(4,"[server] Clearing all registered intervals.");for(const e of $e)clearInterval(e)},De=(e,t,r,o)=>{$(1,e),"development"!==C.OTHER_NODE_ENV&&delete e.stack,o(e)},je=(e,t,r,o)=>{const{statusCode:i,status:s,message:n,stack:a}=e,l=i||s||500;r.status(l).json({statusCode:l,message:n,stack:a})};var Ge=(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=v({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 Fe extends re{constructor(e,t){super(e),this.status=this.statusCode=t}setStatus(e){return this.status=e,this}}var Me=e=>!!e&&e.post("/version/change/:newVersion",(async(e,t,r)=>{try{const r=C.HIGHCHARTS_ADMIN_TOKEN;if(!r||!r.length)throw new Fe("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 Fe("Invalid or missing token: Set the token in the hc-auth header.",401);const i=e.params.newVersion;if(!i)throw new Fe("No new version supplied.",400);try{await(async e=>{const t=J();t?.highcharts&&(t.highcharts.version=e),await ae(t)})(i)}catch(e){throw new Fe(`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)}}));const qe={png:"image/png",jpeg:"image/jpeg",gif:"image/gif",pdf:"application/pdf",svg:"image/svg+xml"};let We=0;const Ve=[],Be=[],Xe=(e,t,r,o)=>{let i=!0;const{id:s,uniqueId:n,type:a,body:l}=o;return e.some((e=>{if(e){let o=e(t,r,s,n,a,l);return void 0!==o&&!0!==o&&(i=o),!0}})),i},ze=async(e,t,r)=>{try{const r=z(),i=p.v4().replace(/-/g,""),s=J(),n=e.body,a=++We;let l=G(n.type);if(!n||"object"==typeof(o=n)&&!Array.isArray(o)&&null!==o&&0===Object.keys(o).length)throw new Fe("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=M(n.infile||n.options||n.data);if(!c&&!n.svg)throw H(2,`The request with ID ${i} from ${e.headers["x-forwarded-for"]||e.connection.remoteAddress} was incorrect. Payload received: ${JSON.stringify(n)}.`),new Fe("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=Xe(Ve,e,t,{id:a,uniqueId:i,type:l,body:n}),!0!==u)return t.send(u);let h=!1;e.socket.on("close",(()=>{h=!0})),H(4,`[export] Got an incoming HTTP request with ID ${i}.`),n.constr="string"==typeof n.constr&&n.constr||"chart";const d={export:{instr:c,type:l,constr:n.constr[0].toLowerCase()+n.constr.substr(1),height:n.height,width:n.width,scale:n.scale||s.export.scale,globalOptions:M(n.globalOptions,!0),themeOptions:M(n.themeOptions,!0)},customLogic:{allowCodeExecution:Ie,allowFileResources:!1,resources:M(n.resources,!0),callback:n.callback,customCode:n.customCode}};c&&(d.export.instr=W(c,d.customLogic.allowCodeExecution));const g=Y(s,d);if(g.export.options=c,g.payload={svg:n.svg||!1,b64:n.b64||!1,noDownload:n.noDownload||!1,requestId:i},n.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 Fe("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 Ce(g,((o,c)=>{if(e.socket.removeAllListeners("close"),s.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 Fe(`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,Xe(Be,e,t,{id:a,body:c.result}),c.result?n.b64?"pdf"===l||"svg"==l?t.send(Buffer.from(c.result,"utf8").toString("base64")):t.send(c.result):(t.header("Content-Type",qe[l]||"image/png"),n.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 Ke=JSON.parse(e.readFileSync(t.join(j,"package.json"))),Je=new Date,Ye=[];function Qe(e){if(!e)return!1;var t;t=setInterval((()=>{const e=ke(),t=0===e.exportAttempts?1:e.performedExports/e.exportAttempts*100;Ye.push(t),Ye.length>30&&Ye.shift()}),6e4),$e.push(t),e.get("/health",((e,t)=>{const r=ke(),o=Ye.length,i=Ye.reduce(((e,t)=>e+t),0)/Ye.length;H(4,"[health.js] GET /health [200] - returning server health."),t.send({status:"OK",bootTime:Je,uptime:Math.floor(((new Date).getTime()-Je.getTime())/1e3/60)+" minutes",version:Ke.version,highchartsVersion:ce(),averageProcessingTime:r.spentAverage,performedExports:r.performedExports,failedExports:r.droppedExports,exportAttempts:r.exportAttempts,sucessRatio:r.performedExports/r.exportAttempts*100,pool:_e(),period:o,movingAverage:i,message:`Last ${o} minutes had a success rate of ${i.toFixed(2)}%.`,svgExportAttempts:r.exportFromSvgAttempts,jsonExportAttempts:r.performedExports-r.exportFromSvgAttempts})}))}const Ze=new Map,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 ot=e=>{e.on("clientError",(e=>{$(1,e,`[server] Client error: ${e.message}`)})),e.on("error",(e=>{$(1,e,`[server] Server error: ${e.message}`)})),e.on("connection",(e=>{e.on("error",(e=>{$(1,e,`[server] Socket error: ${e.message}`)}))}))},it=async r=>{try{if(!r.enable)return!1;if(!r.ssl.force){const e=a.createServer(et);ot(e),e.listen(r.port,r.host),Ze.set(r.port,e),H(3,`[server] Started HTTP server on ${r.host}:${r.port}.`)}if(r.ssl.enable){let o,i;try{o=await e.promises.readFile(t.posix.join(r.ssl.certPath,"server.key"),"utf8"),i=await e.promises.readFile(t.posix.join(r.ssl.certPath,"server.crt"),"utf8")}catch(e){H(2,`[server] Unable to load key/certificate from the '${r.ssl.certPath}' path. Could not run secured layer server.`)}if(o&&i){const e=l.createServer({key:o,cert:i},et);ot(e),e.listen(r.ssl.port,r.host),Ze.set(r.ssl.port,e),H(3,`[server] Started HTTPS server on ${r.host}:${r.ssl.port}.`)}}r.rateLimiting&&r.rateLimiting.enable&&![0,NaN].includes(r.rateLimiting.maxRequests)&&Ge(et,r.rateLimiting),et.use(m.static(t.posix.join(j,"public"))),Qe(et),(e=>{e.post("/",ze),e.post("/:filename",ze)})(et),(e=>{!!e&&e.get("/",((e,r)=>{r.sendFile(t.join(j,"public","index.html"))}))})(et),Me(et),(e=>{e.use(De),e.use(je)})(et)}catch(e){throw new re("[server] Could not configure and start the server.").setError(e)}},st=()=>{H(4,"[server] Closing all servers.");for(const[e,t]of Ze)t.close((()=>{Ze.delete(e),H(4,`[server] Closed server on port: ${e}.`)}))};var nt={startServer:it,closeServers:st,getServers:()=>Ze,enableRateLimiting:e=>Ge(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)}};const at=async e=>{await Promise.allSettled([Ue(),st(),xe()]),process.exit(e)};var lt={server:nt,startServer:it,initExport:async e=>{var t;return t=e.customLogic&&e.customLogic.allowCodeExecution,Ie=B(t),(e=>{U(e&&parseInt(e.level)),e&&e.dest&&D(e.dest,e.file||"highcharts-export-server.log")})(e.logging),e.other.listenToProcessExits&&(H(3,"[process] Attaching exit listeners to the process."),process.on("exit",(e=>{H(4,`Process exited with code ${e}.`)})),process.on("SIGINT",(async(e,t)=>{H(4,`The ${e} event with code: ${t}.`),await at(0)})),process.on("SIGTERM",(async(e,t)=>{H(4,`The ${e} event with code: ${t}.`),await at(0)})),process.on("SIGHUP",(async(e,t)=>{H(4,`The ${e} event with code: ${t}.`),await at(0)})),process.on("uncaughtException",(async(e,t)=>{$(1,e,`The ${t} error.`),await at(1)}))),await ae(e),await Se({pool:e.pool||{minWorkers:1,maxWorkers:1},puppeteerArgs:e.puppeteer.args||[]}),e},singleExport:async t=>{t.export.instr=t.export.instr||t.export.options,await Ce(t,(async(t,r)=>{if(t)throw t;const{outfile:o,type:i}=r.options.export;e.writeFileSync(o||`chart.${i}`,"svg"!==i?Buffer.from(r.result,"base64"):r.result),await xe()}))},batchExport:async t=>{const r=[];for(let o of t.export.batch.split(";"))o=o.split("="),2===o.length&&r.push(Ce({...t,export:{...t.export,infile:o[0],outfile:o[1]}},((t,r)=>{if(t)throw t;e.writeFileSync(r.options.export.outfile,"svg"!==r.options.export.type?Buffer.from(r.result,"base64"):r.result)})));try{await Promise.all(r),await xe()}catch(e){throw new re("[chart] Error encountered during batch export.").setError(e)}},startExport:Ce,initPool:Se,killPool:xe,setOptions:(t,r)=>(r?.length&&(K=function(t){const r=t.findIndex((e=>"loadConfig"===e.replace(/-/g,"")));if(r>-1&&t[r+1]){const o=t[r+1];try{if(o&&o.endsWith(".json"))return JSON.parse(e.readFileSync(o))}catch(e){$(2,e,`[config] Unable to load the configuration from the ${o} file.`)}}return{}}(r)),Q(w,K),K=Z(w),t&&(K=Y(K,t,T)),r?.length&&(K=function(e,t,r){let o=!1;for(let i=0;i(n.length-1===r&&(a=e[t].type),e[t])),r),n.reduce(((e,r,l)=>(n.length-1===l&&void 0!==e[r]&&(t[++i]?"boolean"===a?e[r]=B(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 '${s}' argument. Using the default value.`),o=!0)),e[r])),e)}o&&V();return e}(K,r,w)),K),shutdownCleanUp:at,log:H,logWithStack:$,setLogLevel:U,enableFileLogging:D,mapToNewConfig:e=>{const t={};for(const[r,o]of Object.entries(e)){const e=S[r]?S[r].split("."):[];e.reduce(((t,r,i)=>t[r]=e.length-1===i?o:t[r]||{}),t)}return t},manualConfig:async t=>{let r={};e.existsSync(t)&&(r=JSON.parse(e.readFileSync(t,"utf8")));const i=Object.keys(E).map((e=>({title:`${e} options`,value:e})));return o({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,s)=>{let n=0,a=[];for(const e of s)E[e]=E[e].map((t=>({...t,section:e}))),a=[...a,...E[e]];return await o(a,{onSubmit:async(o,i)=>{if("moduleScripts"===o.name?(i=i.length?i.map((e=>o.choices[e])):o.choices,r[o.section][o.name]=i):r[o.section]=ee(Object.assign({},r[o.section]||{}),o.name.split("."),o.choices?o.choices[i]:i),++n===a.length){try{await e.promises.writeFile(t,JSON.stringify(r,null,2),"utf8")}catch(e){$(1,e,`[config] An error occurred while creating the ${t} file.`)}return!0}}}),!0}})},printLogo:r=>{const o=JSON.parse(e.readFileSync(t.join(j,"package.json"))).version;r?console.log(`Starting Highcharts Export Server v${o}...`):console.log(e.readFileSync(j+"/msg/startup.msg").toString().bold.yellow,`v${o}\n`.bold)},printUsage:V};module.exports=lt; +//# sourceMappingURL=data:application/json;charset=utf-8;base64,eyJ2ZXJzaW9uIjozLCJmaWxlIjoiaW5kZXguY2pzIiwic291cmNlcyI6WyIuLi9saWIvc2NoZW1hcy9jb25maWcuanMiLCIuLi9saWIvZW52cy5qcyIsIi4uL2xpYi9sb2dnZXIuanMiLCIuLi9saWIvdXRpbHMuanMiLCIuLi9saWIvY29uZmlnLmpzIiwiLi4vbGliL2ZldGNoLmpzIiwiLi4vbGliL2Vycm9ycy9FeHBvcnRFcnJvci5qcyIsIi4uL2xpYi9jYWNoZS5qcyIsIi4uL2xpYi9oaWdoY2hhcnRzLmpzIiwiLi4vbGliL2Jyb3dzZXIuanMiLCIuLi9saWIvZXhwb3J0LmpzIiwiLi4vdGVtcGxhdGVzL3N2Z19leHBvcnQvc3ZnX2V4cG9ydC5qcyIsIi4uL2xpYi9wb29sLmpzIiwiLi4vbGliL2NoYXJ0LmpzIiwiLi4vbGliL3Nhbml0aXplLmpzIiwiLi4vbGliL2ludGVydmFscy5qcyIsIi4uL2xpYi9zZXJ2ZXIvZXJyb3IuanMiLCIuLi9saWIvc2VydmVyL3JhdGVfbGltaXQuanMiLCIuLi9saWIvZXJyb3JzL0h0dHBFcnJvci5qcyIsIi4uL2xpYi9zZXJ2ZXIvcm91dGVzL2NoYW5nZV9oY192ZXJzaW9uLmpzIiwiLi4vbGliL3NlcnZlci9yb3V0ZXMvZXhwb3J0LmpzIiwiLi4vbGliL3NlcnZlci9yb3V0ZXMvaGVhbHRoLmpzIiwiLi4vbGliL3NlcnZlci9zZXJ2ZXIuanMiLCIuLi9saWIvc2VydmVyL3JvdXRlcy91aS5qcyIsIi4uL2xpYi9yZXNvdXJjZV9yZWxlYXNlLmpzIiwiLi4vbGliL2luZGV4LmpzIl0sInNvdXJjZXNDb250ZW50IjpbIi8qKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqXHJcblxyXG5IaWdoY2hhcnRzIEV4cG9ydCBTZXJ2ZXJcclxuXHJcbkNvcHlyaWdodCAoYykgMjAxNi0yMDI0LCBIaWdoc29mdFxyXG5cclxuTGljZW5jZWQgdW5kZXIgdGhlIE1JVCBsaWNlbmNlLlxyXG5cclxuQWRkaXRpb25hbGx5IGEgdmFsaWQgSGlnaGNoYXJ0cyBsaWNlbnNlIGlzIHJlcXVpcmVkIGZvciB1c2UuXHJcblxyXG5TZWUgTElDRU5TRSBmaWxlIGluIHJvb3QgZm9yIGRldGFpbHMuXHJcblxyXG4qKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqL1xyXG5cclxuLy8gUG9zc2libGUgbmFtZXMgZm9yIEhpZ2hjaGFydHMgc2NyaXB0c1xyXG5leHBvcnQgY29uc3Qgc2NyaXB0c05hbWVzID0ge1xyXG4gIGNvcmU6IFsnaGlnaGNoYXJ0cycsICdoaWdoY2hhcnRzLW1vcmUnLCAnaGlnaGNoYXJ0cy0zZCddLFxyXG4gIG1vZHVsZXM6IFtcclxuICAgICdzdG9jaycsXHJcbiAgICAnbWFwJyxcclxuICAgICdnYW50dCcsXHJcbiAgICAnZXhwb3J0aW5nJyxcclxuICAgICdleHBvcnQtZGF0YScsXHJcbiAgICAncGFyYWxsZWwtY29vcmRpbmF0ZXMnLFxyXG4gICAgJ2FjY2Vzc2liaWxpdHknLFxyXG4gICAgLy8gJ2Fubm90YXRpb25zLWFkdmFuY2VkJyxcclxuICAgICdib29zdC1jYW52YXMnLFxyXG4gICAgJ2Jvb3N0JyxcclxuICAgICdkYXRhJyxcclxuICAgICdkYXRhLXRvb2xzJyxcclxuICAgICdkcmFnZ2FibGUtcG9pbnRzJyxcclxuICAgICdzdGF0aWMtc2NhbGUnLFxyXG4gICAgJ2Jyb2tlbi1heGlzJyxcclxuICAgICdoZWF0bWFwJyxcclxuICAgICd0aWxlbWFwJyxcclxuICAgICd0aWxlZHdlYm1hcCcsXHJcbiAgICAndGltZWxpbmUnLFxyXG4gICAgJ3RyZWVtYXAnLFxyXG4gICAgJ3RyZWVncmFwaCcsXHJcbiAgICAnaXRlbS1zZXJpZXMnLFxyXG4gICAgJ2RyaWxsZG93bicsXHJcbiAgICAnaGlzdG9ncmFtLWJlbGxjdXJ2ZScsXHJcbiAgICAnYnVsbGV0JyxcclxuICAgICdmdW5uZWwnLFxyXG4gICAgJ2Z1bm5lbDNkJyxcclxuICAgICdnZW9oZWF0bWFwJyxcclxuICAgICdweXJhbWlkM2QnLFxyXG4gICAgJ25ldHdvcmtncmFwaCcsXHJcbiAgICAnb3ZlcmxhcHBpbmctZGF0YWxhYmVscycsXHJcbiAgICAncGFyZXRvJyxcclxuICAgICdwYXR0ZXJuLWZpbGwnLFxyXG4gICAgJ3BpY3RvcmlhbCcsXHJcbiAgICAncHJpY2UtaW5kaWNhdG9yJyxcclxuICAgICdzYW5rZXknLFxyXG4gICAgJ2FyYy1kaWFncmFtJyxcclxuICAgICdkZXBlbmRlbmN5LXdoZWVsJyxcclxuICAgICdzZXJpZXMtbGFiZWwnLFxyXG4gICAgJ3NvbGlkLWdhdWdlJyxcclxuICAgICdzb25pZmljYXRpb24nLFxyXG4gICAgLy8gJ3N0b2NrLXRvb2xzJyxcclxuICAgICdzdHJlYW1ncmFwaCcsXHJcbiAgICAnc3VuYnVyc3QnLFxyXG4gICAgJ3ZhcmlhYmxlLXBpZScsXHJcbiAgICAndmFyaXdpZGUnLFxyXG4gICAgJ3ZlY3RvcicsXHJcbiAgICAndmVubicsXHJcbiAgICAnd2luZGJhcmInLFxyXG4gICAgJ3dvcmRjbG91ZCcsXHJcbiAgICAneHJhbmdlJyxcclxuICAgICduby1kYXRhLXRvLWRpc3BsYXknLFxyXG4gICAgJ2RyYWctcGFuZXMnLFxyXG4gICAgJ2RlYnVnZ2VyJyxcclxuICAgICdkdW1iYmVsbCcsXHJcbiAgICAnbG9sbGlwb3AnLFxyXG4gICAgJ2N5bGluZGVyJyxcclxuICAgICdvcmdhbml6YXRpb24nLFxyXG4gICAgJ2RvdHBsb3QnLFxyXG4gICAgJ21hcmtlci1jbHVzdGVycycsXHJcbiAgICAnaG9sbG93Y2FuZGxlc3RpY2snLFxyXG4gICAgJ2hlaWtpbmFzaGknLFxyXG4gICAgJ2Zsb3dtYXAnXHJcbiAgXSxcclxuICBpbmRpY2F0b3JzOiBbJ2luZGljYXRvcnMtYWxsJ11cclxufTtcclxuXHJcbi8vIFRoaXMgaXMgdGhlIGNvbmZpZ3VyYXRpb24gb2JqZWN0IHdpdGggYWxsIG9wdGlvbnMgYW5kIHRoZWlyIGRlZmF1bHQgdmFsdWVzLFxyXG4vLyBhbHNvIGZyb20gdGhlIC5lbnYgZmlsZSBpZiBvbmUgZXhpc3RzXHJcbmV4cG9ydCBjb25zdCBkZWZhdWx0Q29uZmlnID0ge1xyXG4gIHB1cHBldGVlcjoge1xyXG4gICAgYXJnczoge1xyXG4gICAgICB2YWx1ZTogW1xyXG4gICAgICAgICctLWFsbG93LXJ1bm5pbmctaW5zZWN1cmUtY29udGVudCcsXHJcbiAgICAgICAgJy0tYXNoLW5vLW51ZGdlcycsXHJcbiAgICAgICAgJy0tYXV0b3BsYXktcG9saWN5PXVzZXItZ2VzdHVyZS1yZXF1aXJlZCcsXHJcbiAgICAgICAgJy0tYmxvY2stbmV3LXdlYi1jb250ZW50cycsXHJcbiAgICAgICAgJy0tZGlzYWJsZS1hY2NlbGVyYXRlZC0yZC1jYW52YXMnLFxyXG4gICAgICAgICctLWRpc2FibGUtYmFja2dyb3VuZC1uZXR3b3JraW5nJyxcclxuICAgICAgICAnLS1kaXNhYmxlLWJhY2tncm91bmQtdGltZXItdGhyb3R0bGluZycsXHJcbiAgICAgICAgJy0tZGlzYWJsZS1iYWNrZ3JvdW5kaW5nLW9jY2x1ZGVkLXdpbmRvd3MnLFxyXG4gICAgICAgICctLWRpc2FibGUtYnJlYWtwYWQnLFxyXG4gICAgICAgICctLWRpc2FibGUtY2hlY2tlci1pbWFnaW5nJyxcclxuICAgICAgICAnLS1kaXNhYmxlLWNsaWVudC1zaWRlLXBoaXNoaW5nLWRldGVjdGlvbicsXHJcbiAgICAgICAgJy0tZGlzYWJsZS1jb21wb25lbnQtZXh0ZW5zaW9ucy13aXRoLWJhY2tncm91bmQtcGFnZXMnLFxyXG4gICAgICAgICctLWRpc2FibGUtY29tcG9uZW50LXVwZGF0ZScsXHJcbiAgICAgICAgJy0tZGlzYWJsZS1kZWZhdWx0LWFwcHMnLFxyXG4gICAgICAgICctLWRpc2FibGUtZGV2LXNobS11c2FnZScsXHJcbiAgICAgICAgJy0tZGlzYWJsZS1kb21haW4tcmVsaWFiaWxpdHknLFxyXG4gICAgICAgICctLWRpc2FibGUtZXh0ZW5zaW9ucycsXHJcbiAgICAgICAgJy0tZGlzYWJsZS1mZWF0dXJlcz1DYWxjdWxhdGVOYXRpdmVXaW5PY2NsdXNpb24sSW50ZXJlc3RGZWVkQ29udGVudFN1Z2dlc3Rpb25zLFdlYk9UUCcsXHJcbiAgICAgICAgJy0tZGlzYWJsZS1oYW5nLW1vbml0b3InLFxyXG4gICAgICAgICctLWRpc2FibGUtaXBjLWZsb29kaW5nLXByb3RlY3Rpb24nLFxyXG4gICAgICAgICctLWRpc2FibGUtbG9nZ2luZycsXHJcbiAgICAgICAgJy0tZGlzYWJsZS1ub3RpZmljYXRpb25zJyxcclxuICAgICAgICAnLS1kaXNhYmxlLW9mZmVyLXN0b3JlLXVubWFza2VkLXdhbGxldC1jYXJkcycsXHJcbiAgICAgICAgJy0tZGlzYWJsZS1wb3B1cC1ibG9ja2luZycsXHJcbiAgICAgICAgJy0tZGlzYWJsZS1wcmludC1wcmV2aWV3JyxcclxuICAgICAgICAnLS1kaXNhYmxlLXByb21wdC1vbi1yZXBvc3QnLFxyXG4gICAgICAgICctLWRpc2FibGUtcmVuZGVyZXItYmFja2dyb3VuZGluZycsXHJcbiAgICAgICAgJy0tZGlzYWJsZS1zZWFyY2gtZW5naW5lLWNob2ljZS1zY3JlZW4nLFxyXG4gICAgICAgICctLWRpc2FibGUtc2Vzc2lvbi1jcmFzaGVkLWJ1YmJsZScsXHJcbiAgICAgICAgJy0tZGlzYWJsZS1zZXR1aWQtc2FuZGJveCcsXHJcbiAgICAgICAgJy0tZGlzYWJsZS1zaXRlLWlzb2xhdGlvbi10cmlhbHMnLFxyXG4gICAgICAgICctLWRpc2FibGUtc3BlZWNoLWFwaScsXHJcbiAgICAgICAgJy0tZGlzYWJsZS1zeW5jJyxcclxuICAgICAgICAnLS1lbmFibGUtdW5zYWZlLXdlYmdwdScsXHJcbiAgICAgICAgJy0taGlkZS1jcmFzaC1yZXN0b3JlLWJ1YmJsZScsXHJcbiAgICAgICAgJy0taGlkZS1zY3JvbGxiYXJzJyxcclxuICAgICAgICAnLS1tZXRyaWNzLXJlY29yZGluZy1vbmx5JyxcclxuICAgICAgICAnLS1tdXRlLWF1ZGlvJyxcclxuICAgICAgICAnLS1uby1kZWZhdWx0LWJyb3dzZXItY2hlY2snLFxyXG4gICAgICAgICctLW5vLWZpcnN0LXJ1bicsXHJcbiAgICAgICAgJy0tbm8tcGluZ3MnLFxyXG4gICAgICAgICctLW5vLXNhbmRib3gnLFxyXG4gICAgICAgICctLW5vLXN0YXJ0dXAtd2luZG93JyxcclxuICAgICAgICAnLS1uby16eWdvdGUnLFxyXG4gICAgICAgICctLXBhc3N3b3JkLXN0b3JlPWJhc2ljJyxcclxuICAgICAgICAnLS1wcm9jZXNzLXBlci10YWInLFxyXG4gICAgICAgICctLXVzZS1tb2NrLWtleWNoYWluJ1xyXG4gICAgICBdLFxyXG4gICAgICB0eXBlOiAnc3RyaW5nW10nLFxyXG4gICAgICBkZXNjcmlwdGlvbjogJ0FyZ3VtZW50cyBhcnJheSB0byBzZW5kIHRvIFB1cHBldGVlci4nXHJcbiAgICB9XHJcbiAgfSxcclxuICBoaWdoY2hhcnRzOiB7XHJcbiAgICB2ZXJzaW9uOiB7XHJcbiAgICAgIHZhbHVlOiAnbGF0ZXN0JyxcclxuICAgICAgdHlwZTogJ3N0cmluZycsXHJcbiAgICAgIGVudkxpbms6ICdISUdIQ0hBUlRTX1ZFUlNJT04nLFxyXG4gICAgICBkZXNjcmlwdGlvbjogJ1RoZSBIaWdoY2hhcnRzIHZlcnNpb24gdG8gYmUgdXNlZC4nXHJcbiAgICB9LFxyXG4gICAgY2RuVVJMOiB7XHJcbiAgICAgIHZhbHVlOiAnaHR0cHM6Ly9jb2RlLmhpZ2hjaGFydHMuY29tLycsXHJcbiAgICAgIHR5cGU6ICdzdHJpbmcnLFxyXG4gICAgICBlbnZMaW5rOiAnSElHSENIQVJUU19DRE5fVVJMJyxcclxuICAgICAgZGVzY3JpcHRpb246ICdUaGUgQ0ROIFVSTCBmb3IgSGlnaGNoYXJ0cyBzY3JpcHRzIHRvIGJlIHVzZWQuJ1xyXG4gICAgfSxcclxuICAgIGNvcmVTY3JpcHRzOiB7XHJcbiAgICAgIHZhbHVlOiBzY3JpcHRzTmFtZXMuY29yZSxcclxuICAgICAgdHlwZTogJ3N0cmluZ1tdJyxcclxuICAgICAgZW52TGluazogJ0hJR0hDSEFSVFNfQ09SRV9TQ1JJUFRTJyxcclxuICAgICAgZGVzY3JpcHRpb246ICdUaGUgY29yZSBIaWdoY2hhcnRzIHNjcmlwdHMgdG8gZmV0Y2guJ1xyXG4gICAgfSxcclxuICAgIG1vZHVsZVNjcmlwdHM6IHtcclxuICAgICAgdmFsdWU6IHNjcmlwdHNOYW1lcy5tb2R1bGVzLFxyXG4gICAgICB0eXBlOiAnc3RyaW5nW10nLFxyXG4gICAgICBlbnZMaW5rOiAnSElHSENIQVJUU19NT0RVTEVfU0NSSVBUUycsXHJcbiAgICAgIGRlc2NyaXB0aW9uOiAnVGhlIG1vZHVsZXMgb2YgSGlnaGNoYXJ0cyB0byBmZXRjaC4nXHJcbiAgICB9LFxyXG4gICAgaW5kaWNhdG9yU2NyaXB0czoge1xyXG4gICAgICB2YWx1ZTogc2NyaXB0c05hbWVzLmluZGljYXRvcnMsXHJcbiAgICAgIHR5cGU6ICdzdHJpbmdbXScsXHJcbiAgICAgIGVudkxpbms6ICdISUdIQ0hBUlRTX0lORElDQVRPUl9TQ1JJUFRTJyxcclxuICAgICAgZGVzY3JpcHRpb246ICdUaGUgaW5kaWNhdG9ycyBvZiBIaWdoY2hhcnRzIHRvIGZldGNoLidcclxuICAgIH0sXHJcbiAgICBjdXN0b21TY3JpcHRzOiB7XHJcbiAgICAgIHZhbHVlOiBbXHJcbiAgICAgICAgJ2h0dHBzOi8vY2RuanMuY2xvdWRmbGFyZS5jb20vYWpheC9saWJzL21vbWVudC5qcy8yLjI5LjQvbW9tZW50Lm1pbi5qcycsXHJcbiAgICAgICAgJ2h0dHBzOi8vY2RuanMuY2xvdWRmbGFyZS5jb20vYWpheC9saWJzL21vbWVudC10aW1lem9uZS8wLjUuMzQvbW9tZW50LXRpbWV6b25lLXdpdGgtZGF0YS5taW4uanMnXHJcbiAgICAgIF0sXHJcbiAgICAgIHR5cGU6ICdzdHJpbmdbXScsXHJcbiAgICAgIGRlc2NyaXB0aW9uOiAnQWRkaXRpb25hbCBjdXN0b20gc2NyaXB0cyBvciBkZXBlbmRlbmNpZXMgdG8gZmV0Y2guJ1xyXG4gICAgfSxcclxuICAgIGZvcmNlRmV0Y2g6IHtcclxuICAgICAgdmFsdWU6IGZhbHNlLFxyXG4gICAgICB0eXBlOiAnYm9vbGVhbicsXHJcbiAgICAgIGVudkxpbms6ICdISUdIQ0hBUlRTX0ZPUkNFX0ZFVENIJyxcclxuICAgICAgZGVzY3JpcHRpb246XHJcbiAgICAgICAgJ1RoZSBmbGFnIHRvIGRldGVybWluZSB3aGV0aGVyIHRvIHJlZmV0Y2ggYWxsIHNjcmlwdHMgYWZ0ZXIgZWFjaCBzZXJ2ZXIgcmVydW4uJ1xyXG4gICAgfSxcclxuICAgIGNhY2hlUGF0aDoge1xyXG4gICAgICB2YWx1ZTogJy5jYWNoZScsXHJcbiAgICAgIHR5cGU6ICdzdHJpbmcnLFxyXG4gICAgICBlbnZMaW5rOiAnSElHSENIQVJUU19DQUNIRV9QQVRIJyxcclxuICAgICAgZGVzY3JpcHRpb246XHJcbiAgICAgICAgJ1RoZSBwYXRoIHRvIHRoZSBjYWNoZSBkaXJlY3RvcnkuIEl0IGlzIHVzZWQgdG8gc3RvcmUgdGhlIEhpZ2hjaGFydHMgc2NyaXB0cyBhbmQgY3VzdG9tIHNjcmlwdHMuJ1xyXG4gICAgfVxyXG4gIH0sXHJcbiAgZXhwb3J0OiB7XHJcbiAgICBpbmZpbGU6IHtcclxuICAgICAgdmFsdWU6IGZhbHNlLFxyXG4gICAgICB0eXBlOiAnc3RyaW5nJyxcclxuICAgICAgZGVzY3JpcHRpb246XHJcbiAgICAgICAgJ1RoZSBpbnB1dCBmaWxlIHNob3VsZCBpbmNsdWRlIGEgbmFtZSBhbmQgYSB0eXBlIChqc29uIG9yIHN2ZykuIEl0IG11c3QgYmUgY29ycmVjdGx5IGZvcm1hdHRlZCBhcyBhIEpTT04gb3IgU1ZHIGZpbGUuJ1xyXG4gICAgfSxcclxuICAgIGluc3RyOiB7XHJcbiAgICAgIHZhbHVlOiBmYWxzZSxcclxuICAgICAgdHlwZTogJ3N0cmluZycsXHJcbiAgICAgIGRlc2NyaXB0aW9uOlxyXG4gICAgICAgICdJbnB1dCwgcHJvdmlkZWQgaW4gdGhlIGZvcm0gb2YgYSBzdHJpbmdpZmllZCBKU09OIG9yIFNWRyBmaWxlLCB3aWxsIG92ZXJyaWRlIHRoZSAtLWluZmlsZSBvcHRpb24uJ1xyXG4gICAgfSxcclxuICAgIG9wdGlvbnM6IHtcclxuICAgICAgdmFsdWU6IGZhbHNlLFxyXG4gICAgICB0eXBlOiAnc3RyaW5nJyxcclxuICAgICAgZGVzY3JpcHRpb246ICdBbiBhbGlhcyBmb3IgdGhlIC0taW5zdHIgb3B0aW9uLidcclxuICAgIH0sXHJcbiAgICBvdXRmaWxlOiB7XHJcbiAgICAgIHZhbHVlOiBmYWxzZSxcclxuICAgICAgdHlwZTogJ3N0cmluZycsXHJcbiAgICAgIGRlc2NyaXB0aW9uOlxyXG4gICAgICAgICdUaGUgb3V0cHV0IGZpbGVuYW1lIGFsb25nIHdpdGggYSB0eXBlIChqcGVnLCBwbmcsIHBkZiwgb3Igc3ZnKS4gVGhpcyB3aWxsIGlnbm9yZSB0aGUgLS10eXBlIGZsYWcuJ1xyXG4gICAgfSxcclxuICAgIHR5cGU6IHtcclxuICAgICAgdmFsdWU6ICdwbmcnLFxyXG4gICAgICB0eXBlOiAnc3RyaW5nJyxcclxuICAgICAgZW52TGluazogJ0VYUE9SVF9UWVBFJyxcclxuICAgICAgZGVzY3JpcHRpb246ICdUaGUgZmlsZSBleHBvcnQgZm9ybWF0LiBJdCBjYW4gYmUganBlZywgcG5nLCBwZGYsIG9yIHN2Zy4nXHJcbiAgICB9LFxyXG4gICAgY29uc3RyOiB7XHJcbiAgICAgIHZhbHVlOiAnY2hhcnQnLFxyXG4gICAgICB0eXBlOiAnc3RyaW5nJyxcclxuICAgICAgZW52TGluazogJ0VYUE9SVF9DT05TVFInLFxyXG4gICAgICBkZXNjcmlwdGlvbjpcclxuICAgICAgICAnVGhlIGNvbnN0cnVjdG9yIHRvIHVzZS4gQ2FuIGJlIGNoYXJ0LCBzdG9ja0NoYXJ0LCBtYXBDaGFydCwgb3IgZ2FudHRDaGFydC4nXHJcbiAgICB9LFxyXG4gICAgZGVmYXVsdEhlaWdodDoge1xyXG4gICAgICB2YWx1ZTogNDAwLFxyXG4gICAgICB0eXBlOiAnbnVtYmVyJyxcclxuICAgICAgZW52TGluazogJ0VYUE9SVF9ERUZBVUxUX0hFSUdIVCcsXHJcbiAgICAgIGRlc2NyaXB0aW9uOlxyXG4gICAgICAgICd0aGUgZGVmYXVsdCBoZWlnaHQgb2YgdGhlIGV4cG9ydGVkIGNoYXJ0LiBVc2VkIHdoZW4gbm8gdmFsdWUgaXMgc2V0LidcclxuICAgIH0sXHJcbiAgICBkZWZhdWx0V2lkdGg6IHtcclxuICAgICAgdmFsdWU6IDYwMCxcclxuICAgICAgdHlwZTogJ251bWJlcicsXHJcbiAgICAgIGVudkxpbms6ICdFWFBPUlRfREVGQVVMVF9XSURUSCcsXHJcbiAgICAgIGRlc2NyaXB0aW9uOlxyXG4gICAgICAgICdUaGUgZGVmYXVsdCB3aWR0aCBvZiB0aGUgZXhwb3J0ZWQgY2hhcnQuIFVzZWQgd2hlbiBubyB2YWx1ZSBpcyBzZXQuJ1xyXG4gICAgfSxcclxuICAgIGRlZmF1bHRTY2FsZToge1xyXG4gICAgICB2YWx1ZTogMSxcclxuICAgICAgdHlwZTogJ251bWJlcicsXHJcbiAgICAgIGVudkxpbms6ICdFWFBPUlRfREVGQVVMVF9TQ0FMRScsXHJcbiAgICAgIGRlc2NyaXB0aW9uOlxyXG4gICAgICAgICdUaGUgZGVmYXVsdCBzY2FsZSBvZiB0aGUgZXhwb3J0ZWQgY2hhcnQuIFVzZWQgd2hlbiBubyB2YWx1ZSBpcyBzZXQuJ1xyXG4gICAgfSxcclxuICAgIGhlaWdodDoge1xyXG4gICAgICB2YWx1ZTogZmFsc2UsXHJcbiAgICAgIHR5cGU6ICdudW1iZXInLFxyXG4gICAgICBkZXNjcmlwdGlvbjpcclxuICAgICAgICAnVGhlIGhlaWdodCBvZiB0aGUgZXhwb3J0ZWQgY2hhcnQsIG92ZXJyaWRpbmcgdGhlIG9wdGlvbiBpbiB0aGUgY2hhcnQgc2V0dGluZ3MuJ1xyXG4gICAgfSxcclxuICAgIHdpZHRoOiB7XHJcbiAgICAgIHZhbHVlOiBmYWxzZSxcclxuICAgICAgdHlwZTogJ251bWJlcicsXHJcbiAgICAgIGRlc2NyaXB0aW9uOlxyXG4gICAgICAgICdUaGUgd2lkdGggb2YgdGhlIGV4cG9ydGVkIGNoYXJ0LCBvdmVycmlkaW5nIHRoZSBvcHRpb24gaW4gdGhlIGNoYXJ0IHNldHRpbmdzLidcclxuICAgIH0sXHJcbiAgICBzY2FsZToge1xyXG4gICAgICB2YWx1ZTogZmFsc2UsXHJcbiAgICAgIHR5cGU6ICdudW1iZXInLFxyXG4gICAgICBkZXNjcmlwdGlvbjpcclxuICAgICAgICAnVGhlIHNjYWxlIG9mIHRoZSBleHBvcnRlZCBjaGFydCwgb3ZlcnJpZGluZyB0aGUgb3B0aW9uIGluIHRoZSBjaGFydCBzZXR0aW5ncy4gUmFuZ2VzIGJldHdlZW4gMC4xIGFuZCA1LjAuJ1xyXG4gICAgfSxcclxuICAgIGdsb2JhbE9wdGlvbnM6IHtcclxuICAgICAgdmFsdWU6IGZhbHNlLFxyXG4gICAgICB0eXBlOiAnc3RyaW5nJyxcclxuICAgICAgZGVzY3JpcHRpb246XHJcbiAgICAgICAgJ0VpdGhlciBhIHN0cmluZ2lmaWVkIEpTT04gb3IgYSBmaWxlbmFtZSBjb250YWluaW5nIG9wdGlvbnMgdG8gYmUgcGFzc2VkIGludG8gdGhlIEhpZ2hjaGFydHMuc2V0T3B0aW9ucy4nXHJcbiAgICB9LFxyXG4gICAgdGhlbWVPcHRpb25zOiB7XHJcbiAgICAgIHZhbHVlOiBmYWxzZSxcclxuICAgICAgdHlwZTogJ3N0cmluZycsXHJcbiAgICAgIGRlc2NyaXB0aW9uOlxyXG4gICAgICAgICdFaXRoZXIgYSBzdHJpbmdpZmllZCBKU09OIG9yIGEgZmlsZW5hbWUgY29udGFpbmluZyB0aGVtZSBvcHRpb25zIHRvIGJlIHBhc3NlZCBpbnRvIHRoZSBIaWdoY2hhcnRzLnNldE9wdGlvbnMuJ1xyXG4gICAgfSxcclxuICAgIGJhdGNoOiB7XHJcbiAgICAgIHZhbHVlOiBmYWxzZSxcclxuICAgICAgdHlwZTogJ3N0cmluZycsXHJcbiAgICAgIGRlc2NyaXB0aW9uOlxyXG4gICAgICAgICdJbml0aWF0ZXMgYSBiYXRjaCBqb2Igd2l0aCBhIHN0cmluZyBjb250YWluaW5nIGlucHV0L291dHB1dCBwYWlyczogXCJpbj1vdXQ7aW49b3V0Oy4uLlwiLidcclxuICAgIH0sXHJcbiAgICByYXN0ZXJpemF0aW9uVGltZW91dDoge1xyXG4gICAgICB2YWx1ZTogMTUwMCxcclxuICAgICAgdHlwZTogJ251bWJlcicsXHJcbiAgICAgIGVudkxpbms6ICdFWFBPUlRfUkFTVEVSSVpBVElPTl9USU1FT1VUJyxcclxuICAgICAgZGVzY3JpcHRpb246XHJcbiAgICAgICAgJ1RoZSBkdXJhdGlvbiBpbiBtaWxsaXNlY29uZHMgdG8gd2FpdCBmb3IgcmVuZGVyaW5nIGEgd2VicGFnZS4nXHJcbiAgICB9XHJcbiAgfSxcclxuICBjdXN0b21Mb2dpYzoge1xyXG4gICAgYWxsb3dDb2RlRXhlY3V0aW9uOiB7XHJcbiAgICAgIHZhbHVlOiBmYWxzZSxcclxuICAgICAgdHlwZTogJ2Jvb2xlYW4nLFxyXG4gICAgICBlbnZMaW5rOiAnQ1VTVE9NX0xPR0lDX0FMTE9XX0NPREVfRVhFQ1VUSU9OJyxcclxuICAgICAgZGVzY3JpcHRpb246XHJcbiAgICAgICAgJ0NvbnRyb2xzIHdoZXRoZXIgdGhlIGV4ZWN1dGlvbiBvZiBhcmJpdHJhcnkgY29kZSBpcyBhbGxvd2VkIGR1cmluZyB0aGUgZXhwb3J0aW5nIHByb2Nlc3MuJ1xyXG4gICAgfSxcclxuICAgIGFsbG93RmlsZVJlc291cmNlczoge1xyXG4gICAgICB2YWx1ZTogZmFsc2UsXHJcbiAgICAgIHR5cGU6ICdib29sZWFuJyxcclxuICAgICAgZW52TGluazogJ0NVU1RPTV9MT0dJQ19BTExPV19GSUxFX1JFU09VUkNFUycsXHJcbiAgICAgIGRlc2NyaXB0aW9uOlxyXG4gICAgICAgICdDb250cm9scyB0aGUgYWJpbGl0eSB0byBpbmplY3QgcmVzb3VyY2VzIGZyb20gdGhlIGZpbGVzeXN0ZW0uIFRoaXMgc2V0dGluZyBoYXMgbm8gZWZmZWN0IHdoZW4gcnVubmluZyBhcyBhIHNlcnZlci4nXHJcbiAgICB9LFxyXG4gICAgY3VzdG9tQ29kZToge1xyXG4gICAgICB2YWx1ZTogZmFsc2UsXHJcbiAgICAgIHR5cGU6ICdzdHJpbmcnLFxyXG4gICAgICBkZXNjcmlwdGlvbjpcclxuICAgICAgICAnQ3VzdG9tIGNvZGUgdG8gZXhlY3V0ZSBiZWZvcmUgY2hhcnQgaW5pdGlhbGl6YXRpb24uIEl0IGNhbiBiZSBhIGZ1bmN0aW9uLCBjb2RlIHdyYXBwZWQgd2l0aGluIGEgZnVuY3Rpb24sIG9yIGEgZmlsZW5hbWUgd2l0aCB0aGUgLmpzIGV4dGVuc2lvbi4nXHJcbiAgICB9LFxyXG4gICAgY2FsbGJhY2s6IHtcclxuICAgICAgdmFsdWU6IGZhbHNlLFxyXG4gICAgICB0eXBlOiAnc3RyaW5nJyxcclxuICAgICAgZGVzY3JpcHRpb246XHJcbiAgICAgICAgJ0phdmFTY3JpcHQgY29kZSB0byBydW4gZHVyaW5nIGNvbnN0cnVjdGlvbi4gSXQgY2FuIGJlIGEgZnVuY3Rpb24gb3IgYSBmaWxlbmFtZSB3aXRoIHRoZSAuanMgZXh0ZW5zaW9uLidcclxuICAgIH0sXHJcbiAgICByZXNvdXJjZXM6IHtcclxuICAgICAgdmFsdWU6IGZhbHNlLFxyXG4gICAgICB0eXBlOiAnc3RyaW5nJyxcclxuICAgICAgZGVzY3JpcHRpb246XHJcbiAgICAgICAgJ0FkZGl0aW9uYWwgcmVzb3VyY2UgaW4gdGhlIGZvcm0gb2YgYSBzdHJpbmdpZmllZCBKU09OLCB3aGljaCBtYXkgY29udGFpbiBmaWxlcywganMsIGFuZCBjc3Mgc2VjdGlvbnMuJ1xyXG4gICAgfSxcclxuICAgIGxvYWRDb25maWc6IHtcclxuICAgICAgdmFsdWU6IGZhbHNlLFxyXG4gICAgICB0eXBlOiAnc3RyaW5nJyxcclxuICAgICAgbGVnYWN5TmFtZTogJ2Zyb21GaWxlJyxcclxuICAgICAgZGVzY3JpcHRpb246ICdBIGZpbGUgY29udGFpbmluZyBhIHByZS1kZWZpbmVkIGNvbmZpZ3VyYXRpb24gdG8gdXNlLidcclxuICAgIH0sXHJcbiAgICBjcmVhdGVDb25maWc6IHtcclxuICAgICAgdmFsdWU6IGZhbHNlLFxyXG4gICAgICB0eXBlOiAnc3RyaW5nJyxcclxuICAgICAgZGVzY3JpcHRpb246XHJcbiAgICAgICAgJ0VuYWJsZXMgc2V0dGluZyBvcHRpb25zIHRocm91Z2ggYSBwcm9tcHQgYW5kIHNhdmluZyB0aGVtIGluIGEgcHJvdmlkZWQgY29uZmlnIGZpbGUuJ1xyXG4gICAgfVxyXG4gIH0sXHJcbiAgc2VydmVyOiB7XHJcbiAgICBlbmFibGU6IHtcclxuICAgICAgdmFsdWU6IGZhbHNlLFxyXG4gICAgICB0eXBlOiAnYm9vbGVhbicsXHJcbiAgICAgIGVudkxpbms6ICdTRVJWRVJfRU5BQkxFJyxcclxuICAgICAgY2xpTmFtZTogJ2VuYWJsZVNlcnZlcicsXHJcbiAgICAgIGRlc2NyaXB0aW9uOlxyXG4gICAgICAgICdXaGVuIHNldCB0byB0cnVlLCB0aGUgc2VydmVyIHN0YXJ0cyBvbiB0aGUgbG9jYWwgSVAgYWRkcmVzcyAwLjAuMC4wLidcclxuICAgIH0sXHJcbiAgICBob3N0OiB7XHJcbiAgICAgIHZhbHVlOiAnMC4wLjAuMCcsXHJcbiAgICAgIHR5cGU6ICdzdHJpbmcnLFxyXG4gICAgICBlbnZMaW5rOiAnU0VSVkVSX0hPU1QnLFxyXG4gICAgICBkZXNjcmlwdGlvbjpcclxuICAgICAgICAnVGhlIGhvc3RuYW1lIG9mIHRoZSBzZXJ2ZXIuIEFkZGl0aW9uYWxseSwgaXQgc3RhcnRzIGEgc2VydmVyIG9uIHRoZSBwcm92aWRlZCBob3N0bmFtZS4nXHJcbiAgICB9LFxyXG4gICAgcG9ydDoge1xyXG4gICAgICB2YWx1ZTogNzgwMSxcclxuICAgICAgdHlwZTogJ251bWJlcicsXHJcbiAgICAgIGVudkxpbms6ICdTRVJWRVJfUE9SVCcsXHJcbiAgICAgIGRlc2NyaXB0aW9uOiAnVGhlIHNlcnZlciBwb3J0IHdoZW4gZW5hYmxlZC4nXHJcbiAgICB9LFxyXG4gICAgYmVuY2htYXJraW5nOiB7XHJcbiAgICAgIHZhbHVlOiBmYWxzZSxcclxuICAgICAgdHlwZTogJ2Jvb2xlYW4nLFxyXG4gICAgICBlbnZMaW5rOiAnU0VSVkVSX0JFTkNITUFSS0lORycsXHJcbiAgICAgIGNsaU5hbWU6ICdzZXJ2ZXJCZW5jaG1hcmtpbmcnLFxyXG4gICAgICBkZXNjcmlwdGlvbjpcclxuICAgICAgICAnSW5kaWNhdGVzIHdoZXRoZXIgdG8gZGlzcGxheSB0aGUgZHVyYXRpb24sIGluIG1pbGxpc2Vjb25kcywgb2Ygc3BlY2lmaWMgYWN0aW9ucyB0aGF0IG9jY3VyIG9uIHRoZSBzZXJ2ZXIgd2hpbGUgc2VydmluZyBhIHJlcXVlc3QuJ1xyXG4gICAgfSxcclxuICAgIHByb3h5OiB7XHJcbiAgICAgIGhvc3Q6IHtcclxuICAgICAgICB2YWx1ZTogZmFsc2UsXHJcbiAgICAgICAgdHlwZTogJ3N0cmluZycsXHJcbiAgICAgICAgZW52TGluazogJ1NFUlZFUl9QUk9YWV9IT1NUJyxcclxuICAgICAgICBjbGlOYW1lOiAncHJveHlIb3N0JyxcclxuICAgICAgICBkZXNjcmlwdGlvbjogJ1RoZSBob3N0IG9mIHRoZSBwcm94eSBzZXJ2ZXIgdG8gdXNlLCBpZiBpdCBleGlzdHMuJ1xyXG4gICAgICB9LFxyXG4gICAgICBwb3J0OiB7XHJcbiAgICAgICAgdmFsdWU6IDgwODAsXHJcbiAgICAgICAgdHlwZTogJ251bWJlcicsXHJcbiAgICAgICAgZW52TGluazogJ1NFUlZFUl9QUk9YWV9QT1JUJyxcclxuICAgICAgICBjbGlOYW1lOiAncHJveHlQb3J0JyxcclxuICAgICAgICBkZXNjcmlwdGlvbjogJ1RoZSBwb3J0IG9mIHRoZSBwcm94eSBzZXJ2ZXIgdG8gdXNlLCBpZiBpdCBleGlzdHMuJ1xyXG4gICAgICB9LFxyXG4gICAgICB0aW1lb3V0OiB7XHJcbiAgICAgICAgdmFsdWU6IDUwMDAsXHJcbiAgICAgICAgdHlwZTogJ251bWJlcicsXHJcbiAgICAgICAgZW52TGluazogJ1NFUlZFUl9QUk9YWV9USU1FT1VUJyxcclxuICAgICAgICBjbGlOYW1lOiAncHJveHlUaW1lb3V0JyxcclxuICAgICAgICBkZXNjcmlwdGlvbjogJ1RoZSB0aW1lb3V0IGZvciB0aGUgcHJveHkgc2VydmVyIHRvIHVzZSwgaWYgaXQgZXhpc3RzLidcclxuICAgICAgfVxyXG4gICAgfSxcclxuICAgIHJhdGVMaW1pdGluZzoge1xyXG4gICAgICBlbmFibGU6IHtcclxuICAgICAgICB2YWx1ZTogZmFsc2UsXHJcbiAgICAgICAgdHlwZTogJ2Jvb2xlYW4nLFxyXG4gICAgICAgIGVudkxpbms6ICdTRVJWRVJfUkFURV9MSU1JVElOR19FTkFCTEUnLFxyXG4gICAgICAgIGNsaU5hbWU6ICdlbmFibGVSYXRlTGltaXRpbmcnLFxyXG4gICAgICAgIGRlc2NyaXB0aW9uOiAnRW5hYmxlcyByYXRlIGxpbWl0aW5nIGZvciB0aGUgc2VydmVyLidcclxuICAgICAgfSxcclxuICAgICAgbWF4UmVxdWVzdHM6IHtcclxuICAgICAgICB2YWx1ZTogMTAsXHJcbiAgICAgICAgdHlwZTogJ251bWJlcicsXHJcbiAgICAgICAgZW52TGluazogJ1NFUlZFUl9SQVRFX0xJTUlUSU5HX01BWF9SRVFVRVNUUycsXHJcbiAgICAgICAgbGVnYWN5TmFtZTogJ3JhdGVMaW1pdCcsXHJcbiAgICAgICAgZGVzY3JpcHRpb246ICdUaGUgbWF4aW11bSBudW1iZXIgb2YgcmVxdWVzdHMgYWxsb3dlZCBpbiBvbmUgbWludXRlLidcclxuICAgICAgfSxcclxuICAgICAgd2luZG93OiB7XHJcbiAgICAgICAgdmFsdWU6IDEsXHJcbiAgICAgICAgdHlwZTogJ251bWJlcicsXHJcbiAgICAgICAgZW52TGluazogJ1NFUlZFUl9SQVRFX0xJTUlUSU5HX1dJTkRPVycsXHJcbiAgICAgICAgZGVzY3JpcHRpb246ICdUaGUgdGltZSB3aW5kb3csIGluIG1pbnV0ZXMsIGZvciB0aGUgcmF0ZSBsaW1pdGluZy4nXHJcbiAgICAgIH0sXHJcbiAgICAgIGRlbGF5OiB7XHJcbiAgICAgICAgdmFsdWU6IDAsXHJcbiAgICAgICAgdHlwZTogJ251bWJlcicsXHJcbiAgICAgICAgZW52TGluazogJ1NFUlZFUl9SQVRFX0xJTUlUSU5HX0RFTEFZJyxcclxuICAgICAgICBkZXNjcmlwdGlvbjpcclxuICAgICAgICAgICdUaGUgZGVsYXkgZHVyYXRpb24gZm9yIGVhY2ggc3VjY2Vzc2l2ZSByZXF1ZXN0IGJlZm9yZSByZWFjaGluZyB0aGUgbWF4aW11bSBsaW1pdC4nXHJcbiAgICAgIH0sXHJcbiAgICAgIHRydXN0UHJveHk6IHtcclxuICAgICAgICB2YWx1ZTogZmFsc2UsXHJcbiAgICAgICAgdHlwZTogJ2Jvb2xlYW4nLFxyXG4gICAgICAgIGVudkxpbms6ICdTRVJWRVJfUkFURV9MSU1JVElOR19UUlVTVF9QUk9YWScsXHJcbiAgICAgICAgZGVzY3JpcHRpb246ICdTZXQgdGhpcyB0byB0cnVlIGlmIHRoZSBzZXJ2ZXIgaXMgYmVoaW5kIGEgbG9hZCBiYWxhbmNlci4nXHJcbiAgICAgIH0sXHJcbiAgICAgIHNraXBLZXk6IHtcclxuICAgICAgICB2YWx1ZTogZmFsc2UsXHJcbiAgICAgICAgdHlwZTogJ3N0cmluZycsXHJcbiAgICAgICAgZW52TGluazogJ1NFUlZFUl9SQVRFX0xJTUlUSU5HX1NLSVBfS0VZJyxcclxuICAgICAgICBkZXNjcmlwdGlvbjpcclxuICAgICAgICAgICdBbGxvd3MgYnlwYXNzaW5nIHRoZSByYXRlIGxpbWl0ZXIgYW5kIHNob3VsZCBiZSBwcm92aWRlZCB3aXRoIHRoZSBza2lwVG9rZW4gYXJndW1lbnQuJ1xyXG4gICAgICB9LFxyXG4gICAgICBza2lwVG9rZW46IHtcclxuICAgICAgICB2YWx1ZTogZmFsc2UsXHJcbiAgICAgICAgdHlwZTogJ3N0cmluZycsXHJcbiAgICAgICAgZW52TGluazogJ1NFUlZFUl9SQVRFX0xJTUlUSU5HX1NLSVBfVE9LRU4nLFxyXG4gICAgICAgIGRlc2NyaXB0aW9uOlxyXG4gICAgICAgICAgJ0FsbG93cyBieXBhc3NpbmcgdGhlIHJhdGUgbGltaXRlciBhbmQgc2hvdWxkIGJlIHByb3ZpZGVkIHdpdGggdGhlIHNraXBLZXkgYXJndW1lbnQuJ1xyXG4gICAgICB9XHJcbiAgICB9LFxyXG4gICAgc3NsOiB7XHJcbiAgICAgIGVuYWJsZToge1xyXG4gICAgICAgIHZhbHVlOiBmYWxzZSxcclxuICAgICAgICB0eXBlOiAnYm9vbGVhbicsXHJcbiAgICAgICAgZW52TGluazogJ1NFUlZFUl9TU0xfRU5BQkxFJyxcclxuICAgICAgICBjbGlOYW1lOiAnZW5hYmxlU3NsJyxcclxuICAgICAgICBkZXNjcmlwdGlvbjogJ0VuYWJsZXMgb3IgZGlzYWJsZXMgdGhlIFNTTCBwcm90b2NvbC4nXHJcbiAgICAgIH0sXHJcbiAgICAgIGZvcmNlOiB7XHJcbiAgICAgICAgdmFsdWU6IGZhbHNlLFxyXG4gICAgICAgIHR5cGU6ICdib29sZWFuJyxcclxuICAgICAgICBlbnZMaW5rOiAnU0VSVkVSX1NTTF9GT1JDRScsXHJcbiAgICAgICAgY2xpTmFtZTogJ3NzbEZvcmNlJyxcclxuICAgICAgICBsZWdhY3lOYW1lOiAnc3NsT25seScsXHJcbiAgICAgICAgZGVzY3JpcHRpb246XHJcbiAgICAgICAgICAnV2hlbiBzZXQgdG8gdHJ1ZSwgdGhlIHNlcnZlciBpcyBmb3JjZWQgdG8gc2VydmUgb25seSBvdmVyIEhUVFBTLidcclxuICAgICAgfSxcclxuICAgICAgcG9ydDoge1xyXG4gICAgICAgIHZhbHVlOiA0NDMsXHJcbiAgICAgICAgdHlwZTogJ251bWJlcicsXHJcbiAgICAgICAgZW52TGluazogJ1NFUlZFUl9TU0xfUE9SVCcsXHJcbiAgICAgICAgY2xpTmFtZTogJ3NzbFBvcnQnLFxyXG4gICAgICAgIGRlc2NyaXB0aW9uOiAnVGhlIHBvcnQgb24gd2hpY2ggdG8gcnVuIHRoZSBTU0wgc2VydmVyLidcclxuICAgICAgfSxcclxuICAgICAgY2VydFBhdGg6IHtcclxuICAgICAgICB2YWx1ZTogZmFsc2UsXHJcbiAgICAgICAgdHlwZTogJ3N0cmluZycsXHJcbiAgICAgICAgZW52TGluazogJ1NFUlZFUl9TU0xfQ0VSVF9QQVRIJyxcclxuICAgICAgICBsZWdhY3lOYW1lOiAnc3NsUGF0aCcsXHJcbiAgICAgICAgZGVzY3JpcHRpb246ICdUaGUgcGF0aCB0byB0aGUgU1NMIGNlcnRpZmljYXRlL2tleSBmaWxlLidcclxuICAgICAgfVxyXG4gICAgfVxyXG4gIH0sXHJcbiAgcG9vbDoge1xyXG4gICAgbWluV29ya2Vyczoge1xyXG4gICAgICB2YWx1ZTogNCxcclxuICAgICAgdHlwZTogJ251bWJlcicsXHJcbiAgICAgIGVudkxpbms6ICdQT09MX01JTl9XT1JLRVJTJyxcclxuICAgICAgZGVzY3JpcHRpb246ICdUaGUgbnVtYmVyIG9mIG1pbmltdW0gYW5kIGluaXRpYWwgcG9vbCB3b3JrZXJzIHRvIHNwYXduLidcclxuICAgIH0sXHJcbiAgICBtYXhXb3JrZXJzOiB7XHJcbiAgICAgIHZhbHVlOiA4LFxyXG4gICAgICB0eXBlOiAnbnVtYmVyJyxcclxuICAgICAgZW52TGluazogJ1BPT0xfTUFYX1dPUktFUlMnLFxyXG4gICAgICBsZWdhY3lOYW1lOiAnd29ya2VycycsXHJcbiAgICAgIGRlc2NyaXB0aW9uOiAnVGhlIG51bWJlciBvZiBtYXhpbXVtIHBvb2wgd29ya2VycyB0byBzcGF3bi4nXHJcbiAgICB9LFxyXG4gICAgd29ya0xpbWl0OiB7XHJcbiAgICAgIHZhbHVlOiA0MCxcclxuICAgICAgdHlwZTogJ251bWJlcicsXHJcbiAgICAgIGVudkxpbms6ICdQT09MX1dPUktfTElNSVQnLFxyXG4gICAgICBkZXNjcmlwdGlvbjpcclxuICAgICAgICAnVGhlIG51bWJlciBvZiB3b3JrIHBpZWNlcyB0aGF0IGNhbiBiZSBwZXJmb3JtZWQgYmVmb3JlIHJlc3RhcnRpbmcgdGhlIHdvcmtlciBwcm9jZXNzLidcclxuICAgIH0sXHJcbiAgICBhY3F1aXJlVGltZW91dDoge1xyXG4gICAgICB2YWx1ZTogNTAwMCxcclxuICAgICAgdHlwZTogJ251bWJlcicsXHJcbiAgICAgIGVudkxpbms6ICdQT09MX0FDUVVJUkVfVElNRU9VVCcsXHJcbiAgICAgIGRlc2NyaXB0aW9uOlxyXG4gICAgICAgICdUaGUgZHVyYXRpb24sIGluIG1pbGxpc2Vjb25kcywgdG8gd2FpdCBmb3IgYWNxdWlyaW5nIGEgcmVzb3VyY2UuJ1xyXG4gICAgfSxcclxuICAgIGNyZWF0ZVRpbWVvdXQ6IHtcclxuICAgICAgdmFsdWU6IDUwMDAsXHJcbiAgICAgIHR5cGU6ICdudW1iZXInLFxyXG4gICAgICBlbnZMaW5rOiAnUE9PTF9DUkVBVEVfVElNRU9VVCcsXHJcbiAgICAgIGRlc2NyaXB0aW9uOlxyXG4gICAgICAgICdUaGUgZHVyYXRpb24sIGluIG1pbGxpc2Vjb25kcywgdG8gd2FpdCBmb3IgY3JlYXRpbmcgYSByZXNvdXJjZS4nXHJcbiAgICB9LFxyXG4gICAgZGVzdHJveVRpbWVvdXQ6IHtcclxuICAgICAgdmFsdWU6IDUwMDAsXHJcbiAgICAgIHR5cGU6ICdudW1iZXInLFxyXG4gICAgICBlbnZMaW5rOiAnUE9PTF9ERVNUUk9ZX1RJTUVPVVQnLFxyXG4gICAgICBkZXNjcmlwdGlvbjpcclxuICAgICAgICAnVGhlIGR1cmF0aW9uLCBpbiBtaWxsaXNlY29uZHMsIHRvIHdhaXQgZm9yIGRlc3Ryb3lpbmcgYSByZXNvdXJjZS4nXHJcbiAgICB9LFxyXG4gICAgaWRsZVRpbWVvdXQ6IHtcclxuICAgICAgdmFsdWU6IDMwMDAwLFxyXG4gICAgICB0eXBlOiAnbnVtYmVyJyxcclxuICAgICAgZW52TGluazogJ1BPT0xfSURMRV9USU1FT1VUJyxcclxuICAgICAgZGVzY3JpcHRpb246XHJcbiAgICAgICAgJ1RoZSBkdXJhdGlvbiwgaW4gbWlsbGlzZWNvbmRzLCBhZnRlciB3aGljaCBhbiBpZGxlIHJlc291cmNlIGlzIGRlc3Ryb3llZC4nXHJcbiAgICB9LFxyXG4gICAgY3JlYXRlUmV0cnlJbnRlcnZhbDoge1xyXG4gICAgICB2YWx1ZTogMjAwLFxyXG4gICAgICB0eXBlOiAnbnVtYmVyJyxcclxuICAgICAgZW52TGluazogJ1BPT0xfQ1JFQVRFX1JFVFJZX0lOVEVSVkFMJyxcclxuICAgICAgZGVzY3JpcHRpb246XHJcbiAgICAgICAgJ1RoZSBkdXJhdGlvbiwgaW4gbWlsbGlzZWNvbmRzLCB0byB3YWl0IGJlZm9yZSByZXRyeWluZyB0aGUgY3JlYXRlIHByb2Nlc3MgaW4gY2FzZSBvZiBhIGZhaWx1cmUuJ1xyXG4gICAgfSxcclxuICAgIHJlYXBlckludGVydmFsOiB7XHJcbiAgICAgIHZhbHVlOiAxMDAwLFxyXG4gICAgICB0eXBlOiAnbnVtYmVyJyxcclxuICAgICAgZW52TGluazogJ1BPT0xfUkVBUEVSX0lOVEVSVkFMJyxcclxuICAgICAgZGVzY3JpcHRpb246XHJcbiAgICAgICAgJ1RoZSBkdXJhdGlvbiwgaW4gbWlsbGlzZWNvbmRzLCBhZnRlciB3aGljaCB0aGUgY2hlY2sgZm9yIGlkbGUgcmVzb3VyY2VzIHRvIGRlc3Ryb3kgaXMgdHJpZ2dlcmVkLidcclxuICAgIH0sXHJcbiAgICBiZW5jaG1hcmtpbmc6IHtcclxuICAgICAgdmFsdWU6IGZhbHNlLFxyXG4gICAgICB0eXBlOiAnYm9vbGVhbicsXHJcbiAgICAgIGVudkxpbms6ICdQT09MX0JFTkNITUFSS0lORycsXHJcbiAgICAgIGNsaU5hbWU6ICdwb29sQmVuY2htYXJraW5nJyxcclxuICAgICAgZGVzY3JpcHRpb246XHJcbiAgICAgICAgJ0luZGljYXRlIHdoZXRoZXIgdG8gc2hvdyBzdGF0aXN0aWNzIGZvciB0aGUgcG9vbCBvZiByZXNvdXJjZXMgb3Igbm90LidcclxuICAgIH1cclxuICB9LFxyXG4gIGxvZ2dpbmc6IHtcclxuICAgIGxldmVsOiB7XHJcbiAgICAgIHZhbHVlOiA0LFxyXG4gICAgICB0eXBlOiAnbnVtYmVyJyxcclxuICAgICAgZW52TGluazogJ0xPR0dJTkdfTEVWRUwnLFxyXG4gICAgICBjbGlOYW1lOiAnbG9nTGV2ZWwnLFxyXG4gICAgICBkZXNjcmlwdGlvbjogJ1RoZSBsb2dnaW5nIGxldmVsIHRvIGJlIHVzZWQuJ1xyXG4gICAgfSxcclxuICAgIGZpbGU6IHtcclxuICAgICAgdmFsdWU6ICdoaWdoY2hhcnRzLWV4cG9ydC1zZXJ2ZXIubG9nJyxcclxuICAgICAgdHlwZTogJ3N0cmluZycsXHJcbiAgICAgIGVudkxpbms6ICdMT0dHSU5HX0ZJTEUnLFxyXG4gICAgICBjbGlOYW1lOiAnbG9nRmlsZScsXHJcbiAgICAgIGRlc2NyaXB0aW9uOlxyXG4gICAgICAgICdUaGUgbmFtZSBvZiBhIGxvZyBmaWxlLiBUaGUgbG9nRGVzdCBvcHRpb24gYWxzbyBuZWVkcyB0byBiZSBzZXQgdG8gZW5hYmxlIGZpbGUgbG9nZ2luZy4nXHJcbiAgICB9LFxyXG4gICAgZGVzdDoge1xyXG4gICAgICB2YWx1ZTogJ2xvZy8nLFxyXG4gICAgICB0eXBlOiAnc3RyaW5nJyxcclxuICAgICAgZW52TGluazogJ0xPR0dJTkdfREVTVCcsXHJcbiAgICAgIGNsaU5hbWU6ICdsb2dEZXN0JyxcclxuICAgICAgZGVzY3JpcHRpb246XHJcbiAgICAgICAgJ1RoZSBwYXRoIHRvIHN0b3JlIGxvZyBmaWxlcy4gVGhpcyBhbHNvIGVuYWJsZXMgZmlsZSBsb2dnaW5nLidcclxuICAgIH1cclxuICB9LFxyXG4gIHVpOiB7XHJcbiAgICBlbmFibGU6IHtcclxuICAgICAgdmFsdWU6IGZhbHNlLFxyXG4gICAgICB0eXBlOiAnYm9vbGVhbicsXHJcbiAgICAgIGVudkxpbms6ICdVSV9FTkFCTEUnLFxyXG4gICAgICBjbGlOYW1lOiAnZW5hYmxlVWknLFxyXG4gICAgICBkZXNjcmlwdGlvbjpcclxuICAgICAgICAnRW5hYmxlcyBvciBkaXNhYmxlcyB0aGUgdXNlciBpbnRlcmZhY2UgKFVJKSBmb3IgdGhlIGV4cG9ydCBzZXJ2ZXIuJ1xyXG4gICAgfSxcclxuICAgIHJvdXRlOiB7XHJcbiAgICAgIHZhbHVlOiAnLycsXHJcbiAgICAgIHR5cGU6ICdzdHJpbmcnLFxyXG4gICAgICBlbnZMaW5rOiAnVUlfUk9VVEUnLFxyXG4gICAgICBjbGlOYW1lOiAndWlSb3V0ZScsXHJcbiAgICAgIGRlc2NyaXB0aW9uOlxyXG4gICAgICAgICdUaGUgZW5kcG9pbnQgcm91dGUgdG8gd2hpY2ggdGhlIHVzZXIgaW50ZXJmYWNlIChVSSkgc2hvdWxkIGJlIGF0dGFjaGVkLidcclxuICAgIH1cclxuICB9LFxyXG4gIG90aGVyOiB7XHJcbiAgICBub2RlRW52OiB7XHJcbiAgICAgIHZhbHVlOiAncHJvZHVjdGlvbicsXHJcbiAgICAgIHR5cGU6ICdzdHJpbmcnLFxyXG4gICAgICBlbnZMaW5rOiAnT1RIRVJfTk9ERV9FTlYnLFxyXG4gICAgICBkZXNjcmlwdGlvbjogJ1RoZSB0eXBlIG9mIE5vZGUuanMgZW52aXJvbm1lbnQuJ1xyXG4gICAgfSxcclxuICAgIGxpc3RlblRvUHJvY2Vzc0V4aXRzOiB7XHJcbiAgICAgIHZhbHVlOiB0cnVlLFxyXG4gICAgICB0eXBlOiAnYm9vbGVhbicsXHJcbiAgICAgIGVudkxpbms6ICdPVEhFUl9MSVNURU5fVE9fUFJPQ0VTU19FWElUUycsXHJcbiAgICAgIGRlc2NyaXB0aW9uOiAnRGVjaWRlcyB3aGV0aGVyIG9yIG5vdCB0byBhdHRhY2ggcHJvY2Vzcy5leGl0IGhhbmRsZXJzLidcclxuICAgIH0sXHJcbiAgICBub0xvZ286IHtcclxuICAgICAgdmFsdWU6IGZhbHNlLFxyXG4gICAgICB0eXBlOiAnYm9vbGVhbicsXHJcbiAgICAgIGVudkxpbms6ICdPVEhFUl9OT19MT0dPJyxcclxuICAgICAgZGVzY3JpcHRpb246XHJcbiAgICAgICAgJ1NraXAgcHJpbnRpbmcgdGhlIGxvZ28gb24gYSBzdGFydHVwLiBXaWxsIGJlIHJlcGxhY2VkIGJ5IGEgc2ltcGxlIHRleHQuJ1xyXG4gICAgfSxcclxuICAgIGhhcmRSZXNldFBhZ2U6IHtcclxuICAgICAgdmFsdWU6IGZhbHNlLFxyXG4gICAgICB0eXBlOiAnYm9vbGVhbicsXHJcbiAgICAgIGVudkxpbms6ICdPVEhFUl9IQVJEX1JFU0VUX1BBR0UnLFxyXG4gICAgICBkZXNjcmlwdGlvbjogJ0RlY2lkZXMgaWYgdGhlIHBhZ2UgY29udGVudCBzaG91bGQgYmUgcmVzZXQgZW50aXJlbHkuJ1xyXG4gICAgfVxyXG4gIH0sXHJcbiAgZGVidWc6IHtcclxuICAgIGVuYWJsZToge1xyXG4gICAgICB2YWx1ZTogZmFsc2UsXHJcbiAgICAgIHR5cGU6ICdib29sZWFuJyxcclxuICAgICAgZW52TGluazogJ0RFQlVHX0VOQUJMRScsXHJcbiAgICAgIGNsaU5hbWU6ICdlbmFibGVEZWJ1ZycsXHJcbiAgICAgIGRlc2NyaXB0aW9uOiAnRW5hYmxlcyBvciBkaXNhYmxlcyBkZWJ1ZyBtb2RlIGZvciB0aGUgdW5kZXJseWluZyBicm93c2VyLidcclxuICAgIH0sXHJcbiAgICBoZWFkbGVzczoge1xyXG4gICAgICB2YWx1ZTogdHJ1ZSxcclxuICAgICAgdHlwZTogJ2Jvb2xlYW4nLFxyXG4gICAgICBlbnZMaW5rOiAnREVCVUdfSEVBRExFU1MnLFxyXG4gICAgICBkZXNjcmlwdGlvbjpcclxuICAgICAgICAnQ29udHJvbHMgdGhlIG1vZGUgaW4gd2hpY2ggdGhlIGJyb3dzZXIgaXMgbGF1bmNoZWQgd2hlbiBpbiB0aGUgZGVidWcgbW9kZS4nXHJcbiAgICB9LFxyXG4gICAgZGV2dG9vbHM6IHtcclxuICAgICAgdmFsdWU6IGZhbHNlLFxyXG4gICAgICB0eXBlOiAnYm9vbGVhbicsXHJcbiAgICAgIGVudkxpbms6ICdERUJVR19ERVZUT09MUycsXHJcbiAgICAgIGRlc2NyaXB0aW9uOlxyXG4gICAgICAgICdEZWNpZGVzIHdoZXRoZXIgdG8gZW5hYmxlIERldlRvb2xzIHdoZW4gdGhlIGJyb3dzZXIgaXMgaW4gYSBoZWFkZnVsIHN0YXRlLidcclxuICAgIH0sXHJcbiAgICBsaXN0ZW5Ub0NvbnNvbGU6IHtcclxuICAgICAgdmFsdWU6IGZhbHNlLFxyXG4gICAgICB0eXBlOiAnYm9vbGVhbicsXHJcbiAgICAgIGVudkxpbms6ICdERUJVR19MSVNURU5fVE9fQ09OU09MRScsXHJcbiAgICAgIGRlc2NyaXB0aW9uOlxyXG4gICAgICAgICdEZWNpZGVzIHdoZXRoZXIgdG8gZW5hYmxlIGEgbGlzdGVuZXIgZm9yIGNvbnNvbGUgbWVzc2FnZXMgc2VudCBmcm9tIHRoZSBicm93c2VyLidcclxuICAgIH0sXHJcbiAgICBkdW1waW86IHtcclxuICAgICAgdmFsdWU6IGZhbHNlLFxyXG4gICAgICB0eXBlOiAnYm9vbGVhbicsXHJcbiAgICAgIGVudkxpbms6ICdERUJVR19EVU1QSU8nLFxyXG4gICAgICBkZXNjcmlwdGlvbjpcclxuICAgICAgICAnUmVkaXJlY3RzIGJyb3dzZXIgcHJvY2VzcyBzdGRvdXQgYW5kIHN0ZGVyciB0byBwcm9jZXNzLnN0ZG91dCBhbmQgcHJvY2Vzcy5zdGRlcnIuJ1xyXG4gICAgfSxcclxuICAgIHNsb3dNbzoge1xyXG4gICAgICB2YWx1ZTogMCxcclxuICAgICAgdHlwZTogJ251bWJlcicsXHJcbiAgICAgIGVudkxpbms6ICdERUJVR19TTE9XX01PJyxcclxuICAgICAgZGVzY3JpcHRpb246XHJcbiAgICAgICAgJ1Nsb3dzIGRvd24gUHVwcGV0ZWVyIG9wZXJhdGlvbnMgYnkgdGhlIHNwZWNpZmllZCBudW1iZXIgb2YgbWlsbGlzZWNvbmRzLidcclxuICAgIH0sXHJcbiAgICBkZWJ1Z2dpbmdQb3J0OiB7XHJcbiAgICAgIHZhbHVlOiA5MjIyLFxyXG4gICAgICB0eXBlOiAnbnVtYmVyJyxcclxuICAgICAgZW52TGluazogJ0RFQlVHX0RFQlVHR0lOR19QT1JUJyxcclxuICAgICAgZGVzY3JpcHRpb246ICdTcGVjaWZpZXMgdGhlIGRlYnVnZ2luZyBwb3J0LidcclxuICAgIH1cclxuICB9XHJcbn07XHJcblxyXG4vLyBUaGUgY29uZmlnIGRlc2NyaXB0aW9ucyBvYmplY3QgZm9yIHRoZSBwcm9tcHRzIGZ1bmN0aW9uYWxpdHkuIEl0IGNvbnRhaW5zXHJcbi8vIGluZm9ybWF0aW9uIGxpa2U6XHJcbi8vICogVHlwZSBvZiBhIHByb21wdFxyXG4vLyAqIE5hbWUgb2YgYW4gb3B0aW9uXHJcbi8vICogU2hvcnQgZGVzY3JpcHRpb24gb2YgYSBjaG9zZW4gb3B0aW9uXHJcbi8vICogSW5pdGlhbCB2YWx1ZVxyXG5leHBvcnQgY29uc3QgcHJvbXB0c0NvbmZpZyA9IHtcclxuICBwdXBwZXRlZXI6IFtcclxuICAgIHtcclxuICAgICAgdHlwZTogJ2xpc3QnLFxyXG4gICAgICBuYW1lOiAnYXJncycsXHJcbiAgICAgIG1lc3NhZ2U6ICdQdXBwZXRlZXIgYXJndW1lbnRzJyxcclxuICAgICAgaW5pdGlhbDogZGVmYXVsdENvbmZpZy5wdXBwZXRlZXIuYXJncy52YWx1ZS5qb2luKCcsJyksXHJcbiAgICAgIHNlcGFyYXRvcjogJywnXHJcbiAgICB9XHJcbiAgXSxcclxuICBoaWdoY2hhcnRzOiBbXHJcbiAgICB7XHJcbiAgICAgIHR5cGU6ICd0ZXh0JyxcclxuICAgICAgbmFtZTogJ3ZlcnNpb24nLFxyXG4gICAgICBtZXNzYWdlOiAnSGlnaGNoYXJ0cyB2ZXJzaW9uJyxcclxuICAgICAgaW5pdGlhbDogZGVmYXVsdENvbmZpZy5oaWdoY2hhcnRzLnZlcnNpb24udmFsdWVcclxuICAgIH0sXHJcbiAgICB7XHJcbiAgICAgIHR5cGU6ICd0ZXh0JyxcclxuICAgICAgbmFtZTogJ2NkblVSTCcsXHJcbiAgICAgIG1lc3NhZ2U6ICdUaGUgVVJMIG9mIENETicsXHJcbiAgICAgIGluaXRpYWw6IGRlZmF1bHRDb25maWcuaGlnaGNoYXJ0cy5jZG5VUkwudmFsdWVcclxuICAgIH0sXHJcbiAgICB7XHJcbiAgICAgIHR5cGU6ICdtdWx0aXNlbGVjdCcsXHJcbiAgICAgIG5hbWU6ICdjb3JlU2NyaXB0cycsXHJcbiAgICAgIG1lc3NhZ2U6ICdBdmFpbGFibGUgY29yZSBzY3JpcHRzJyxcclxuICAgICAgaW5zdHJ1Y3Rpb25zOiAnU3BhY2U6IFNlbGVjdCBzcGVjaWZpYywgQTogU2VsZWN0IGFsbCwgRW50ZXI6IENvbmZpcm0uJyxcclxuICAgICAgY2hvaWNlczogZGVmYXVsdENvbmZpZy5oaWdoY2hhcnRzLmNvcmVTY3JpcHRzLnZhbHVlXHJcbiAgICB9LFxyXG4gICAge1xyXG4gICAgICB0eXBlOiAnbXVsdGlzZWxlY3QnLFxyXG4gICAgICBuYW1lOiAnbW9kdWxlU2NyaXB0cycsXHJcbiAgICAgIG1lc3NhZ2U6ICdBdmFpbGFibGUgbW9kdWxlIHNjcmlwdHMnLFxyXG4gICAgICBpbnN0cnVjdGlvbnM6ICdTcGFjZTogU2VsZWN0IHNwZWNpZmljLCBBOiBTZWxlY3QgYWxsLCBFbnRlcjogQ29uZmlybS4nLFxyXG4gICAgICBjaG9pY2VzOiBkZWZhdWx0Q29uZmlnLmhpZ2hjaGFydHMubW9kdWxlU2NyaXB0cy52YWx1ZVxyXG4gICAgfSxcclxuICAgIHtcclxuICAgICAgdHlwZTogJ211bHRpc2VsZWN0JyxcclxuICAgICAgbmFtZTogJ2luZGljYXRvclNjcmlwdHMnLFxyXG4gICAgICBtZXNzYWdlOiAnQXZhaWxhYmxlIGluZGljYXRvciBzY3JpcHRzJyxcclxuICAgICAgaW5zdHJ1Y3Rpb25zOiAnU3BhY2U6IFNlbGVjdCBzcGVjaWZpYywgQTogU2VsZWN0IGFsbCwgRW50ZXI6IENvbmZpcm0uJyxcclxuICAgICAgY2hvaWNlczogZGVmYXVsdENvbmZpZy5oaWdoY2hhcnRzLmluZGljYXRvclNjcmlwdHMudmFsdWVcclxuICAgIH0sXHJcbiAgICB7XHJcbiAgICAgIHR5cGU6ICdsaXN0JyxcclxuICAgICAgbmFtZTogJ2N1c3RvbVNjcmlwdHMnLFxyXG4gICAgICBtZXNzYWdlOiAnQ3VzdG9tIHNjcmlwdHMnLFxyXG4gICAgICBpbml0aWFsOiBkZWZhdWx0Q29uZmlnLmhpZ2hjaGFydHMuY3VzdG9tU2NyaXB0cy52YWx1ZS5qb2luKCcsJyksXHJcbiAgICAgIHNlcGFyYXRvcjogJywnXHJcbiAgICB9LFxyXG4gICAge1xyXG4gICAgICB0eXBlOiAndG9nZ2xlJyxcclxuICAgICAgbmFtZTogJ2ZvcmNlRmV0Y2gnLFxyXG4gICAgICBtZXNzYWdlOiAnRm9yY2UgcmUtZmV0Y2ggdGhlIHNjcmlwdHMnLFxyXG4gICAgICBpbml0aWFsOiBkZWZhdWx0Q29uZmlnLmhpZ2hjaGFydHMuZm9yY2VGZXRjaC52YWx1ZVxyXG4gICAgfSxcclxuICAgIHtcclxuICAgICAgdHlwZTogJ3RleHQnLFxyXG4gICAgICBuYW1lOiAnY2FjaGVQYXRoJyxcclxuICAgICAgbWVzc2FnZTogJ1RoZSBwYXRoIHRvIHRoZSBjYWNoZSBkaXJlY3RvcnknLFxyXG4gICAgICBpbml0aWFsOiBkZWZhdWx0Q29uZmlnLmhpZ2hjaGFydHMuY2FjaGVQYXRoLnZhbHVlXHJcbiAgICB9XHJcbiAgXSxcclxuICBleHBvcnQ6IFtcclxuICAgIHtcclxuICAgICAgdHlwZTogJ3NlbGVjdCcsXHJcbiAgICAgIG5hbWU6ICd0eXBlJyxcclxuICAgICAgbWVzc2FnZTogJ1RoZSBkZWZhdWx0IGV4cG9ydCBmaWxlIHR5cGUnLFxyXG4gICAgICBoaW50OiBgRGVmYXVsdDogJHtkZWZhdWx0Q29uZmlnLmV4cG9ydC50eXBlLnZhbHVlfWAsXHJcbiAgICAgIGluaXRpYWw6IDAsXHJcbiAgICAgIGNob2ljZXM6IFsncG5nJywgJ2pwZWcnLCAncGRmJywgJ3N2ZyddXHJcbiAgICB9LFxyXG4gICAge1xyXG4gICAgICB0eXBlOiAnc2VsZWN0JyxcclxuICAgICAgbmFtZTogJ2NvbnN0cicsXHJcbiAgICAgIG1lc3NhZ2U6ICdUaGUgZGVmYXVsdCBjb25zdHJ1Y3RvciBmb3IgSGlnaGNoYXJ0cycsXHJcbiAgICAgIGhpbnQ6IGBEZWZhdWx0OiAke2RlZmF1bHRDb25maWcuZXhwb3J0LmNvbnN0ci52YWx1ZX1gLFxyXG4gICAgICBpbml0aWFsOiAwLFxyXG4gICAgICBjaG9pY2VzOiBbJ2NoYXJ0JywgJ3N0b2NrQ2hhcnQnLCAnbWFwQ2hhcnQnLCAnZ2FudHRDaGFydCddXHJcbiAgICB9LFxyXG4gICAge1xyXG4gICAgICB0eXBlOiAnbnVtYmVyJyxcclxuICAgICAgbmFtZTogJ2RlZmF1bHRIZWlnaHQnLFxyXG4gICAgICBtZXNzYWdlOiAnVGhlIGRlZmF1bHQgZmFsbGJhY2sgaGVpZ2h0IG9mIHRoZSBleHBvcnRlZCBjaGFydCcsXHJcbiAgICAgIGluaXRpYWw6IGRlZmF1bHRDb25maWcuZXhwb3J0LmRlZmF1bHRIZWlnaHQudmFsdWVcclxuICAgIH0sXHJcbiAgICB7XHJcbiAgICAgIHR5cGU6ICdudW1iZXInLFxyXG4gICAgICBuYW1lOiAnZGVmYXVsdFdpZHRoJyxcclxuICAgICAgbWVzc2FnZTogJ1RoZSBkZWZhdWx0IGZhbGxiYWNrIHdpZHRoIG9mIHRoZSBleHBvcnRlZCBjaGFydCcsXHJcbiAgICAgIGluaXRpYWw6IGRlZmF1bHRDb25maWcuZXhwb3J0LmRlZmF1bHRXaWR0aC52YWx1ZVxyXG4gICAgfSxcclxuICAgIHtcclxuICAgICAgdHlwZTogJ251bWJlcicsXHJcbiAgICAgIG5hbWU6ICdkZWZhdWx0U2NhbGUnLFxyXG4gICAgICBtZXNzYWdlOiAnVGhlIGRlZmF1bHQgZmFsbGJhY2sgc2NhbGUgb2YgdGhlIGV4cG9ydGVkIGNoYXJ0JyxcclxuICAgICAgaW5pdGlhbDogZGVmYXVsdENvbmZpZy5leHBvcnQuZGVmYXVsdFNjYWxlLnZhbHVlLFxyXG4gICAgICBtaW46IDAuMSxcclxuICAgICAgbWF4OiA1XHJcbiAgICB9LFxyXG4gICAge1xyXG4gICAgICB0eXBlOiAnbnVtYmVyJyxcclxuICAgICAgbmFtZTogJ3Jhc3Rlcml6YXRpb25UaW1lb3V0JyxcclxuICAgICAgbWVzc2FnZTogJ1RoZSByZW5kZXJpbmcgd2VicGFnZSB0aW1lb3V0IGluIG1pbGxpc2Vjb25kcycsXHJcbiAgICAgIGluaXRpYWw6IGRlZmF1bHRDb25maWcuZXhwb3J0LnJhc3Rlcml6YXRpb25UaW1lb3V0LnZhbHVlXHJcbiAgICB9XHJcbiAgXSxcclxuICBjdXN0b21Mb2dpYzogW1xyXG4gICAge1xyXG4gICAgICB0eXBlOiAndG9nZ2xlJyxcclxuICAgICAgbmFtZTogJ2FsbG93Q29kZUV4ZWN1dGlvbicsXHJcbiAgICAgIG1lc3NhZ2U6ICdFbmFibGUgZXhlY3V0aW9uIG9mIGN1c3RvbSBjb2RlJyxcclxuICAgICAgaW5pdGlhbDogZGVmYXVsdENvbmZpZy5jdXN0b21Mb2dpYy5hbGxvd0NvZGVFeGVjdXRpb24udmFsdWVcclxuICAgIH0sXHJcbiAgICB7XHJcbiAgICAgIHR5cGU6ICd0b2dnbGUnLFxyXG4gICAgICBuYW1lOiAnYWxsb3dGaWxlUmVzb3VyY2VzJyxcclxuICAgICAgbWVzc2FnZTogJ0VuYWJsZSBmaWxlIHJlc291cmNlcycsXHJcbiAgICAgIGluaXRpYWw6IGRlZmF1bHRDb25maWcuY3VzdG9tTG9naWMuYWxsb3dGaWxlUmVzb3VyY2VzLnZhbHVlXHJcbiAgICB9XHJcbiAgXSxcclxuICBzZXJ2ZXI6IFtcclxuICAgIHtcclxuICAgICAgdHlwZTogJ3RvZ2dsZScsXHJcbiAgICAgIG5hbWU6ICdlbmFibGUnLFxyXG4gICAgICBtZXNzYWdlOiAnU3RhcnRzIHRoZSBzZXJ2ZXIgb24gMC4wLjAuMCcsXHJcbiAgICAgIGluaXRpYWw6IGRlZmF1bHRDb25maWcuc2VydmVyLmVuYWJsZS52YWx1ZVxyXG4gICAgfSxcclxuICAgIHtcclxuICAgICAgdHlwZTogJ3RleHQnLFxyXG4gICAgICBuYW1lOiAnaG9zdCcsXHJcbiAgICAgIG1lc3NhZ2U6ICdTZXJ2ZXIgaG9zdG5hbWUnLFxyXG4gICAgICBpbml0aWFsOiBkZWZhdWx0Q29uZmlnLnNlcnZlci5ob3N0LnZhbHVlXHJcbiAgICB9LFxyXG4gICAge1xyXG4gICAgICB0eXBlOiAnbnVtYmVyJyxcclxuICAgICAgbmFtZTogJ3BvcnQnLFxyXG4gICAgICBtZXNzYWdlOiAnU2VydmVyIHBvcnQnLFxyXG4gICAgICBpbml0aWFsOiBkZWZhdWx0Q29uZmlnLnNlcnZlci5wb3J0LnZhbHVlXHJcbiAgICB9LFxyXG4gICAge1xyXG4gICAgICB0eXBlOiAndG9nZ2xlJyxcclxuICAgICAgbmFtZTogJ2JlbmNobWFya2luZycsXHJcbiAgICAgIG1lc3NhZ2U6ICdFbmFibGUgc2VydmVyIGJlbmNobWFya2luZycsXHJcbiAgICAgIGluaXRpYWw6IGRlZmF1bHRDb25maWcuc2VydmVyLmJlbmNobWFya2luZy52YWx1ZVxyXG4gICAgfSxcclxuICAgIHtcclxuICAgICAgdHlwZTogJ3RleHQnLFxyXG4gICAgICBuYW1lOiAncHJveHkuaG9zdCcsXHJcbiAgICAgIG1lc3NhZ2U6ICdUaGUgaG9zdCBvZiB0aGUgcHJveHkgc2VydmVyIHRvIHVzZScsXHJcbiAgICAgIGluaXRpYWw6IGRlZmF1bHRDb25maWcuc2VydmVyLnByb3h5Lmhvc3QudmFsdWVcclxuICAgIH0sXHJcbiAgICB7XHJcbiAgICAgIHR5cGU6ICdudW1iZXInLFxyXG4gICAgICBuYW1lOiAncHJveHkucG9ydCcsXHJcbiAgICAgIG1lc3NhZ2U6ICdUaGUgcG9ydCBvZiB0aGUgcHJveHkgc2VydmVyIHRvIHVzZScsXHJcbiAgICAgIGluaXRpYWw6IGRlZmF1bHRDb25maWcuc2VydmVyLnByb3h5LnBvcnQudmFsdWVcclxuICAgIH0sXHJcbiAgICB7XHJcbiAgICAgIHR5cGU6ICdudW1iZXInLFxyXG4gICAgICBuYW1lOiAncHJveHkudGltZW91dCcsXHJcbiAgICAgIG1lc3NhZ2U6ICdUaGUgdGltZW91dCBmb3IgdGhlIHByb3h5IHNlcnZlciB0byB1c2UnLFxyXG4gICAgICBpbml0aWFsOiBkZWZhdWx0Q29uZmlnLnNlcnZlci5wcm94eS50aW1lb3V0LnZhbHVlXHJcbiAgICB9LFxyXG4gICAge1xyXG4gICAgICB0eXBlOiAndG9nZ2xlJyxcclxuICAgICAgbmFtZTogJ3JhdGVMaW1pdGluZy5lbmFibGUnLFxyXG4gICAgICBtZXNzYWdlOiAnRW5hYmxlIHJhdGUgbGltaXRpbmcnLFxyXG4gICAgICBpbml0aWFsOiBkZWZhdWx0Q29uZmlnLnNlcnZlci5yYXRlTGltaXRpbmcuZW5hYmxlLnZhbHVlXHJcbiAgICB9LFxyXG4gICAge1xyXG4gICAgICB0eXBlOiAnbnVtYmVyJyxcclxuICAgICAgbmFtZTogJ3JhdGVMaW1pdGluZy5tYXhSZXF1ZXN0cycsXHJcbiAgICAgIG1lc3NhZ2U6ICdUaGUgbWF4aW11bSByZXF1ZXN0cyBhbGxvd2VkIHBlciBtaW51dGUnLFxyXG4gICAgICBpbml0aWFsOiBkZWZhdWx0Q29uZmlnLnNlcnZlci5yYXRlTGltaXRpbmcubWF4UmVxdWVzdHMudmFsdWVcclxuICAgIH0sXHJcbiAgICB7XHJcbiAgICAgIHR5cGU6ICdudW1iZXInLFxyXG4gICAgICBuYW1lOiAncmF0ZUxpbWl0aW5nLndpbmRvdycsXHJcbiAgICAgIG1lc3NhZ2U6ICdUaGUgcmF0ZS1saW1pdGluZyB0aW1lIHdpbmRvdyBpbiBtaW51dGVzJyxcclxuICAgICAgaW5pdGlhbDogZGVmYXVsdENvbmZpZy5zZXJ2ZXIucmF0ZUxpbWl0aW5nLndpbmRvdy52YWx1ZVxyXG4gICAgfSxcclxuICAgIHtcclxuICAgICAgdHlwZTogJ251bWJlcicsXHJcbiAgICAgIG5hbWU6ICdyYXRlTGltaXRpbmcuZGVsYXknLFxyXG4gICAgICBtZXNzYWdlOlxyXG4gICAgICAgICdUaGUgZGVsYXkgZm9yIGVhY2ggc3VjY2Vzc2l2ZSByZXF1ZXN0IGJlZm9yZSByZWFjaGluZyB0aGUgbWF4aW11bScsXHJcbiAgICAgIGluaXRpYWw6IGRlZmF1bHRDb25maWcuc2VydmVyLnJhdGVMaW1pdGluZy5kZWxheS52YWx1ZVxyXG4gICAgfSxcclxuICAgIHtcclxuICAgICAgdHlwZTogJ3RvZ2dsZScsXHJcbiAgICAgIG5hbWU6ICdyYXRlTGltaXRpbmcudHJ1c3RQcm94eScsXHJcbiAgICAgIG1lc3NhZ2U6ICdTZXQgdG8gdHJ1ZSBpZiBiZWhpbmQgYSBsb2FkIGJhbGFuY2VyJyxcclxuICAgICAgaW5pdGlhbDogZGVmYXVsdENvbmZpZy5zZXJ2ZXIucmF0ZUxpbWl0aW5nLnRydXN0UHJveHkudmFsdWVcclxuICAgIH0sXHJcbiAgICB7XHJcbiAgICAgIHR5cGU6ICd0ZXh0JyxcclxuICAgICAgbmFtZTogJ3JhdGVMaW1pdGluZy5za2lwS2V5JyxcclxuICAgICAgbWVzc2FnZTpcclxuICAgICAgICAnQWxsb3dzIGJ5cGFzc2luZyB0aGUgcmF0ZSBsaW1pdGVyIHdoZW4gcHJvdmlkZWQgd2l0aCB0aGUgc2tpcFRva2VuIGFyZ3VtZW50JyxcclxuICAgICAgaW5pdGlhbDogZGVmYXVsdENvbmZpZy5zZXJ2ZXIucmF0ZUxpbWl0aW5nLnNraXBLZXkudmFsdWVcclxuICAgIH0sXHJcbiAgICB7XHJcbiAgICAgIHR5cGU6ICd0ZXh0JyxcclxuICAgICAgbmFtZTogJ3JhdGVMaW1pdGluZy5za2lwVG9rZW4nLFxyXG4gICAgICBtZXNzYWdlOlxyXG4gICAgICAgICdBbGxvd3MgYnlwYXNzaW5nIHRoZSByYXRlIGxpbWl0ZXIgd2hlbiBwcm92aWRlZCB3aXRoIHRoZSBza2lwS2V5IGFyZ3VtZW50JyxcclxuICAgICAgaW5pdGlhbDogZGVmYXVsdENvbmZpZy5zZXJ2ZXIucmF0ZUxpbWl0aW5nLnNraXBUb2tlbi52YWx1ZVxyXG4gICAgfSxcclxuICAgIHtcclxuICAgICAgdHlwZTogJ3RvZ2dsZScsXHJcbiAgICAgIG5hbWU6ICdzc2wuZW5hYmxlJyxcclxuICAgICAgbWVzc2FnZTogJ0VuYWJsZSBTU0wgcHJvdG9jb2wnLFxyXG4gICAgICBpbml0aWFsOiBkZWZhdWx0Q29uZmlnLnNlcnZlci5zc2wuZW5hYmxlLnZhbHVlXHJcbiAgICB9LFxyXG4gICAge1xyXG4gICAgICB0eXBlOiAndG9nZ2xlJyxcclxuICAgICAgbmFtZTogJ3NzbC5mb3JjZScsXHJcbiAgICAgIG1lc3NhZ2U6ICdGb3JjZSBzZXJ2aW5nIG9ubHkgb3ZlciBIVFRQUycsXHJcbiAgICAgIGluaXRpYWw6IGRlZmF1bHRDb25maWcuc2VydmVyLnNzbC5mb3JjZS52YWx1ZVxyXG4gICAgfSxcclxuICAgIHtcclxuICAgICAgdHlwZTogJ251bWJlcicsXHJcbiAgICAgIG5hbWU6ICdzc2wucG9ydCcsXHJcbiAgICAgIG1lc3NhZ2U6ICdTU0wgc2VydmVyIHBvcnQnLFxyXG4gICAgICBpbml0aWFsOiBkZWZhdWx0Q29uZmlnLnNlcnZlci5zc2wucG9ydC52YWx1ZVxyXG4gICAgfSxcclxuICAgIHtcclxuICAgICAgdHlwZTogJ3RleHQnLFxyXG4gICAgICBuYW1lOiAnc3NsLmNlcnRQYXRoJyxcclxuICAgICAgbWVzc2FnZTogJ1RoZSBwYXRoIHRvIGZpbmQgdGhlIFNTTCBjZXJ0aWZpY2F0ZS9rZXknLFxyXG4gICAgICBpbml0aWFsOiBkZWZhdWx0Q29uZmlnLnNlcnZlci5zc2wuY2VydFBhdGgudmFsdWVcclxuICAgIH1cclxuICBdLFxyXG4gIHBvb2w6IFtcclxuICAgIHtcclxuICAgICAgdHlwZTogJ251bWJlcicsXHJcbiAgICAgIG5hbWU6ICdtaW5Xb3JrZXJzJyxcclxuICAgICAgbWVzc2FnZTogJ1RoZSBpbml0aWFsIG51bWJlciBvZiB3b3JrZXJzIHRvIHNwYXduJyxcclxuICAgICAgaW5pdGlhbDogZGVmYXVsdENvbmZpZy5wb29sLm1pbldvcmtlcnMudmFsdWVcclxuICAgIH0sXHJcbiAgICB7XHJcbiAgICAgIHR5cGU6ICdudW1iZXInLFxyXG4gICAgICBuYW1lOiAnbWF4V29ya2VycycsXHJcbiAgICAgIG1lc3NhZ2U6ICdUaGUgbWF4aW11bSBudW1iZXIgb2Ygd29ya2VycyB0byBzcGF3bicsXHJcbiAgICAgIGluaXRpYWw6IGRlZmF1bHRDb25maWcucG9vbC5tYXhXb3JrZXJzLnZhbHVlXHJcbiAgICB9LFxyXG4gICAge1xyXG4gICAgICB0eXBlOiAnbnVtYmVyJyxcclxuICAgICAgbmFtZTogJ3dvcmtMaW1pdCcsXHJcbiAgICAgIG1lc3NhZ2U6XHJcbiAgICAgICAgJ1RoZSBwaWVjZXMgb2Ygd29yayB0aGF0IGNhbiBiZSBwZXJmb3JtZWQgYmVmb3JlIHJlc3RhcnRpbmcgYSBQdXBwZXRlZXIgcHJvY2VzcycsXHJcbiAgICAgIGluaXRpYWw6IGRlZmF1bHRDb25maWcucG9vbC53b3JrTGltaXQudmFsdWVcclxuICAgIH0sXHJcbiAgICB7XHJcbiAgICAgIHR5cGU6ICdudW1iZXInLFxyXG4gICAgICBuYW1lOiAnYWNxdWlyZVRpbWVvdXQnLFxyXG4gICAgICBtZXNzYWdlOiAnVGhlIG51bWJlciBvZiBtaWxsaXNlY29uZHMgdG8gd2FpdCBmb3IgYWNxdWlyaW5nIGEgcmVzb3VyY2UnLFxyXG4gICAgICBpbml0aWFsOiBkZWZhdWx0Q29uZmlnLnBvb2wuYWNxdWlyZVRpbWVvdXQudmFsdWVcclxuICAgIH0sXHJcbiAgICB7XHJcbiAgICAgIHR5cGU6ICdudW1iZXInLFxyXG4gICAgICBuYW1lOiAnY3JlYXRlVGltZW91dCcsXHJcbiAgICAgIG1lc3NhZ2U6ICdUaGUgbnVtYmVyIG9mIG1pbGxpc2Vjb25kcyB0byB3YWl0IGZvciBjcmVhdGluZyBhIHJlc291cmNlJyxcclxuICAgICAgaW5pdGlhbDogZGVmYXVsdENvbmZpZy5wb29sLmNyZWF0ZVRpbWVvdXQudmFsdWVcclxuICAgIH0sXHJcbiAgICB7XHJcbiAgICAgIHR5cGU6ICdudW1iZXInLFxyXG4gICAgICBuYW1lOiAnZGVzdHJveVRpbWVvdXQnLFxyXG4gICAgICBtZXNzYWdlOiAnVGhlIG51bWJlciBvZiBtaWxsaXNlY29uZHMgdG8gd2FpdCBmb3IgZGVzdHJveWluZyBhIHJlc291cmNlJyxcclxuICAgICAgaW5pdGlhbDogZGVmYXVsdENvbmZpZy5wb29sLmRlc3Ryb3lUaW1lb3V0LnZhbHVlXHJcbiAgICB9LFxyXG4gICAge1xyXG4gICAgICB0eXBlOiAnbnVtYmVyJyxcclxuICAgICAgbmFtZTogJ2lkbGVUaW1lb3V0JyxcclxuICAgICAgbWVzc2FnZTogJ1RoZSBudW1iZXIgb2YgbWlsbGlzZWNvbmRzIGFmdGVyIGFuIGlkbGUgcmVzb3VyY2UgaXMgZGVzdHJveWVkJyxcclxuICAgICAgaW5pdGlhbDogZGVmYXVsdENvbmZpZy5wb29sLmlkbGVUaW1lb3V0LnZhbHVlXHJcbiAgICB9LFxyXG4gICAge1xyXG4gICAgICB0eXBlOiAnbnVtYmVyJyxcclxuICAgICAgbmFtZTogJ2NyZWF0ZVJldHJ5SW50ZXJ2YWwnLFxyXG4gICAgICBtZXNzYWdlOlxyXG4gICAgICAgICdUaGUgcmV0cnkgaW50ZXJ2YWwgaW4gbWlsbGlzZWNvbmRzIGFmdGVyIGEgY3JlYXRlIHByb2Nlc3MgZmFpbHMnLFxyXG4gICAgICBpbml0aWFsOiBkZWZhdWx0Q29uZmlnLnBvb2wuY3JlYXRlUmV0cnlJbnRlcnZhbC52YWx1ZVxyXG4gICAgfSxcclxuICAgIHtcclxuICAgICAgdHlwZTogJ251bWJlcicsXHJcbiAgICAgIG5hbWU6ICdyZWFwZXJJbnRlcnZhbCcsXHJcbiAgICAgIG1lc3NhZ2U6XHJcbiAgICAgICAgJ1RoZSByZWFwZXIgaW50ZXJ2YWwgaW4gbWlsbGlzZWNvbmRzIGFmdGVyIHRyaWdnZXJpbmcgdGhlIGNoZWNrIGZvciBpZGxlIHJlc291cmNlcyB0byBkZXN0cm95JyxcclxuICAgICAgaW5pdGlhbDogZGVmYXVsdENvbmZpZy5wb29sLnJlYXBlckludGVydmFsLnZhbHVlXHJcbiAgICB9LFxyXG4gICAge1xyXG4gICAgICB0eXBlOiAndG9nZ2xlJyxcclxuICAgICAgbmFtZTogJ2JlbmNobWFya2luZycsXHJcbiAgICAgIG1lc3NhZ2U6ICdFbmFibGUgYmVuY2htYXJraW5nIGZvciBhIHJlc291cmNlIHBvb2wnLFxyXG4gICAgICBpbml0aWFsOiBkZWZhdWx0Q29uZmlnLnBvb2wuYmVuY2htYXJraW5nLnZhbHVlXHJcbiAgICB9XHJcbiAgXSxcclxuICBsb2dnaW5nOiBbXHJcbiAgICB7XHJcbiAgICAgIHR5cGU6ICdudW1iZXInLFxyXG4gICAgICBuYW1lOiAnbGV2ZWwnLFxyXG4gICAgICBtZXNzYWdlOlxyXG4gICAgICAgICdUaGUgbG9nIGxldmVsICgwOiBzaWxlbnQsIDE6IGVycm9yLCAyOiB3YXJuaW5nLCAzOiBub3RpY2UsIDQ6IHZlcmJvc2UsIDU6IGJlbmNobWFyayknLFxyXG4gICAgICBpbml0aWFsOiBkZWZhdWx0Q29uZmlnLmxvZ2dpbmcubGV2ZWwudmFsdWUsXHJcbiAgICAgIHJvdW5kOiAwLFxyXG4gICAgICBtaW46IDAsXHJcbiAgICAgIG1heDogNVxyXG4gICAgfSxcclxuICAgIHtcclxuICAgICAgdHlwZTogJ3RleHQnLFxyXG4gICAgICBuYW1lOiAnZmlsZScsXHJcbiAgICAgIG1lc3NhZ2U6ICdBIGxvZyBmaWxlIG5hbWUuIFNldCB3aXRoIHRoZSAtLWxvZ0Rlc3QgdG8gZW5hYmxlIGZpbGUgbG9nZ2luZycsXHJcbiAgICAgIGluaXRpYWw6IGRlZmF1bHRDb25maWcubG9nZ2luZy5maWxlLnZhbHVlXHJcbiAgICB9LFxyXG4gICAge1xyXG4gICAgICB0eXBlOiAndGV4dCcsXHJcbiAgICAgIG5hbWU6ICdkZXN0JyxcclxuICAgICAgbWVzc2FnZTogJ1RoZSBwYXRoIHRvIGxvZyBmaWxlcy4gRW5hYmxlcyBmaWxlIGxvZ2dpbmcnLFxyXG4gICAgICBpbml0aWFsOiBkZWZhdWx0Q29uZmlnLmxvZ2dpbmcuZGVzdC52YWx1ZVxyXG4gICAgfVxyXG4gIF0sXHJcbiAgdWk6IFtcclxuICAgIHtcclxuICAgICAgdHlwZTogJ3RvZ2dsZScsXHJcbiAgICAgIG5hbWU6ICdlbmFibGUnLFxyXG4gICAgICBtZXNzYWdlOiAnRW5hYmxlIFVJIGZvciB0aGUgZXhwb3J0IHNlcnZlcicsXHJcbiAgICAgIGluaXRpYWw6IGRlZmF1bHRDb25maWcudWkuZW5hYmxlLnZhbHVlXHJcbiAgICB9LFxyXG4gICAge1xyXG4gICAgICB0eXBlOiAndGV4dCcsXHJcbiAgICAgIG5hbWU6ICdyb3V0ZScsXHJcbiAgICAgIG1lc3NhZ2U6ICdBIHJvdXRlIHRvIGF0dGFjaCB0aGUgVUknLFxyXG4gICAgICBpbml0aWFsOiBkZWZhdWx0Q29uZmlnLnVpLnJvdXRlLnZhbHVlXHJcbiAgICB9XHJcbiAgXSxcclxuICBvdGhlcjogW1xyXG4gICAge1xyXG4gICAgICB0eXBlOiAndGV4dCcsXHJcbiAgICAgIG5hbWU6ICdub2RlRW52JyxcclxuICAgICAgbWVzc2FnZTogJ1RoZSB0eXBlIG9mIE5vZGUuanMgZW52aXJvbm1lbnQnLFxyXG4gICAgICBpbml0aWFsOiBkZWZhdWx0Q29uZmlnLm90aGVyLm5vZGVFbnYudmFsdWVcclxuICAgIH0sXHJcbiAgICB7XHJcbiAgICAgIHR5cGU6ICd0b2dnbGUnLFxyXG4gICAgICBuYW1lOiAnbGlzdGVuVG9Qcm9jZXNzRXhpdHMnLFxyXG4gICAgICBtZXNzYWdlOiAnU2V0IHRvIGZhbHNlIHRvIHNraXAgYXR0YWNoaW5nIHByb2Nlc3MuZXhpdCBoYW5kbGVycycsXHJcbiAgICAgIGluaXRpYWw6IGRlZmF1bHRDb25maWcub3RoZXIubGlzdGVuVG9Qcm9jZXNzRXhpdHMudmFsdWVcclxuICAgIH0sXHJcbiAgICB7XHJcbiAgICAgIHR5cGU6ICd0b2dnbGUnLFxyXG4gICAgICBuYW1lOiAnbm9Mb2dvJyxcclxuICAgICAgbWVzc2FnZTogJ1NraXAgcHJpbnRpbmcgdGhlIGxvZ28gb24gc3RhcnR1cC4gUmVwbGFjZWQgYnkgc2ltcGxlIHRleHQnLFxyXG4gICAgICBpbml0aWFsOiBkZWZhdWx0Q29uZmlnLm90aGVyLm5vTG9nby52YWx1ZVxyXG4gICAgfSxcclxuICAgIHtcclxuICAgICAgdHlwZTogJ3RvZ2dsZScsXHJcbiAgICAgIG5hbWU6ICdoYXJkUmVzZXRQYWdlJyxcclxuICAgICAgbWVzc2FnZTogJ0RlY2lkZXMgaWYgdGhlIHBhZ2UgY29udGVudCBzaG91bGQgYmUgcmVzZXQgZW50aXJlbHknLFxyXG4gICAgICBpbml0aWFsOiBkZWZhdWx0Q29uZmlnLm90aGVyLmhhcmRSZXNldFBhZ2UudmFsdWVcclxuICAgIH1cclxuICBdLFxyXG4gIGRlYnVnOiBbXHJcbiAgICB7XHJcbiAgICAgIHR5cGU6ICd0b2dnbGUnLFxyXG4gICAgICBuYW1lOiAnZW5hYmxlJyxcclxuICAgICAgbWVzc2FnZTogJ0VuYWJsZXMgZGVidWcgbW9kZSBmb3IgdGhlIGJyb3dzZXIgaW5zdGFuY2UnLFxyXG4gICAgICBpbml0aWFsOiBkZWZhdWx0Q29uZmlnLmRlYnVnLmVuYWJsZS52YWx1ZVxyXG4gICAgfSxcclxuICAgIHtcclxuICAgICAgdHlwZTogJ3RvZ2dsZScsXHJcbiAgICAgIG5hbWU6ICdoZWFkbGVzcycsXHJcbiAgICAgIG1lc3NhZ2U6ICdUaGUgbW9kZSBzZXR0aW5nIGZvciB0aGUgYnJvd3NlcicsXHJcbiAgICAgIGluaXRpYWw6IGRlZmF1bHRDb25maWcuZGVidWcuaGVhZGxlc3MudmFsdWVcclxuICAgIH0sXHJcbiAgICB7XHJcbiAgICAgIHR5cGU6ICd0b2dnbGUnLFxyXG4gICAgICBuYW1lOiAnZGV2dG9vbHMnLFxyXG4gICAgICBtZXNzYWdlOiAnVGhlIERldlRvb2xzIGZvciB0aGUgaGVhZGZ1bCBicm93c2VyJyxcclxuICAgICAgaW5pdGlhbDogZGVmYXVsdENvbmZpZy5kZWJ1Zy5kZXZ0b29scy52YWx1ZVxyXG4gICAgfSxcclxuICAgIHtcclxuICAgICAgdHlwZTogJ3RvZ2dsZScsXHJcbiAgICAgIG5hbWU6ICdsaXN0ZW5Ub0NvbnNvbGUnLFxyXG4gICAgICBtZXNzYWdlOiAnVGhlIGV2ZW50IGxpc3RlbmVyIGZvciBjb25zb2xlIG1lc3NhZ2VzIGZyb20gdGhlIGJyb3dzZXInLFxyXG4gICAgICBpbml0aWFsOiBkZWZhdWx0Q29uZmlnLmRlYnVnLmxpc3RlblRvQ29uc29sZS52YWx1ZVxyXG4gICAgfSxcclxuICAgIHtcclxuICAgICAgdHlwZTogJ3RvZ2dsZScsXHJcbiAgICAgIG5hbWU6ICdkdW1waW8nLFxyXG4gICAgICBtZXNzYWdlOiAnUmVkaXJlY3RzIHRoZSBicm93c2VyIHN0ZG91dCBhbmQgc3RkZXJyIHRvIE5vZGVKUyBwcm9jZXNzJyxcclxuICAgICAgaW5pdGlhbDogZGVmYXVsdENvbmZpZy5kZWJ1Zy5kdW1waW8udmFsdWVcclxuICAgIH0sXHJcbiAgICB7XHJcbiAgICAgIHR5cGU6ICdudW1iZXInLFxyXG4gICAgICBuYW1lOiAnc2xvd01vJyxcclxuICAgICAgbWVzc2FnZTogJ1B1cHBldGVlciBvcGVyYXRpb25zIHNsb3cgZG93biBpbiBtaWxsaXNlY29uZHMnLFxyXG4gICAgICBpbml0aWFsOiBkZWZhdWx0Q29uZmlnLmRlYnVnLnNsb3dNby52YWx1ZVxyXG4gICAgfSxcclxuICAgIHtcclxuICAgICAgdHlwZTogJ251bWJlcicsXHJcbiAgICAgIG5hbWU6ICdkZWJ1Z2dpbmdQb3J0JyxcclxuICAgICAgbWVzc2FnZTogJ1RoZSBwb3J0IG51bWJlciBmb3IgZGVidWdnaW5nJyxcclxuICAgICAgaW5pdGlhbDogZGVmYXVsdENvbmZpZy5kZWJ1Zy5kZWJ1Z2dpbmdQb3J0LnZhbHVlXHJcbiAgICB9XHJcbiAgXVxyXG59O1xyXG5cclxuLy8gQWJzb2x1dGUgcHJvcHMgdGhhdCwgaW4gY2FzZSBvZiBtZXJnaW5nIHJlY3Vyc2l2ZWx5LCBuZWVkIHRvIGJlIGZvcmNlIG1lcmdlZFxyXG5leHBvcnQgY29uc3QgYWJzb2x1dGVQcm9wcyA9IFtcclxuICAnb3B0aW9ucycsXHJcbiAgJ2dsb2JhbE9wdGlvbnMnLFxyXG4gICd0aGVtZU9wdGlvbnMnLFxyXG4gICdyZXNvdXJjZXMnLFxyXG4gICdwYXlsb2FkJ1xyXG5dO1xyXG5cclxuLy8gQXJndW1lbnQgbmVzdGluZyBsZXZlbCBvZiBhbGwgZXhwb3J0IHNlcnZlciBvcHRpb25zXHJcbmV4cG9ydCBjb25zdCBuZXN0ZWRBcmdzID0ge307XHJcblxyXG4vKipcclxuICogUmVjdXJzaXZlbHkgY3JlYXRlcyBhIGNoYWluIG9mIG5lc3RlZCBhcmd1bWVudHMgZnJvbSBhbiBvYmplY3QuXHJcbiAqXHJcbiAqIEBwYXJhbSB7T2JqZWN0fSBvYmogLSBUaGUgb2JqZWN0IGNvbnRhaW5pbmcgbmVzdGVkIGFyZ3VtZW50cy5cclxuICogQHBhcmFtIHtzdHJpbmd9IHByb3BDaGFpbiAtIFRoZSBjdXJyZW50IGNoYWluIG9mIG5lc3RlZCBwcm9wZXJ0aWVzXHJcbiAqICh1c2VkIGludGVybmFsbHkgZHVyaW5nIHJlY3Vyc2lvbikuXHJcbiAqL1xyXG5jb25zdCBjcmVhdGVOZXN0ZWRBcmdzID0gKG9iaiwgcHJvcENoYWluID0gJycpID0+IHtcclxuICBPYmplY3Qua2V5cyhvYmopLmZvckVhY2goKGspID0+IHtcclxuICAgIGlmICghWydwdXBwZXRlZXInLCAnaGlnaGNoYXJ0cyddLmluY2x1ZGVzKGspKSB7XHJcbiAgICAgIGNvbnN0IGVudHJ5ID0gb2JqW2tdO1xyXG4gICAgICBpZiAodHlwZW9mIGVudHJ5LnZhbHVlID09PSAndW5kZWZpbmVkJykge1xyXG4gICAgICAgIC8vIEdvIGRlZXBlciBpbiB0aGUgbmVzdGVkIGFyZ3VtZW50c1xyXG4gICAgICAgIGNyZWF0ZU5lc3RlZEFyZ3MoZW50cnksIGAke3Byb3BDaGFpbn0uJHtrfWApO1xyXG4gICAgICB9IGVsc2Uge1xyXG4gICAgICAgIC8vIENyZWF0ZSB0aGUgY2hhaW4gb2YgbmVzdGVkIGFyZ3VtZW50c1xyXG4gICAgICAgIG5lc3RlZEFyZ3NbZW50cnkuY2xpTmFtZSB8fCBrXSA9IGAke3Byb3BDaGFpbn0uJHtrfWAuc3Vic3RyaW5nKDEpO1xyXG5cclxuICAgICAgICAvLyBTdXBwb3J0IGZvciB0aGUgbGVnYWN5LCBQaGFudG9tSlMgcHJvcGVydGllcyBuYW1lc1xyXG4gICAgICAgIGlmIChlbnRyeS5sZWdhY3lOYW1lICE9PSB1bmRlZmluZWQpIHtcclxuICAgICAgICAgIG5lc3RlZEFyZ3NbZW50cnkubGVnYWN5TmFtZV0gPSBgJHtwcm9wQ2hhaW59LiR7a31gLnN1YnN0cmluZygxKTtcclxuICAgICAgICB9XHJcbiAgICAgIH1cclxuICAgIH1cclxuICB9KTtcclxufTtcclxuXHJcbmNyZWF0ZU5lc3RlZEFyZ3MoZGVmYXVsdENvbmZpZyk7XHJcbiIsIi8qKlxyXG4gKiBAZmlsZW92ZXJ2aWV3XHJcbiAqIFRoaXMgZmlsZSBpcyByZXNwb25zaWJsZSBmb3IgcGFyc2luZyB0aGUgZW52aXJvbm1lbnQgdmFyaWFibGVzIHdpdGggdGhlICd6b2QnXHJcbiAqIGxpYnJhcnkuIFRoZSBwYXJzZWQgZW52aXJvbm1lbnQgdmFyaWFibGVzIGFyZSB0aGVuIGV4cG9ydGVkIHRvIGJlIHVzZWRcclxuICogaW4gdGhlIGFwcGxpY2F0aW9uIGFzIFwiZW52c1wiLiBXZSBzaG91bGQgbm90IHVzZSBwcm9jZXNzLmVudiBkaXJlY3RseVxyXG4gKiBpbiB0aGUgYXBwbGljYXRpb24gYXMgdGhlc2Ugd291bGQgbm90IGJlIHBhcnNlZCBwcm9wZXJseS5cclxuICpcclxuICogVGhlIGVudmlyb25tZW50IHZhcmlhYmxlcyBhcmUgcGFyc2VkIGFuZCB2YWxpZGF0ZWQgb25seSBvbmNlIHdoZW5cclxuICogdGhlIGFwcGxpY2F0aW9uIHN0YXJ0cy4gV2Ugc2hvdWxkIHdyaXRlIGEgY3VzdG9tIHZhbGlkYXRvciBvciBhIHRyYW5zZm9ybWVyXHJcbiAqIGZvciBlYWNoIG9mIHRoZSBvcHRpb25zLlxyXG4gKi9cclxuXHJcbmltcG9ydCBkb3RlbnYgZnJvbSAnZG90ZW52JztcclxuaW1wb3J0IHsgeiB9IGZyb20gJ3pvZCc7XHJcblxyXG5pbXBvcnQgeyBzY3JpcHRzTmFtZXMgfSBmcm9tICcuL3NjaGVtYXMvY29uZmlnLmpzJztcclxuXHJcbi8vIExvYWQgLmVudiBpbnRvIGVudmlyb25tZW50IHZhcmlhYmxlc1xyXG5kb3RlbnYuY29uZmlnKCk7XHJcblxyXG4vLyBPYmplY3Qgd2l0aCBjdXN0b20gdmFsaWRhdG9ycyBhbmQgdHJhbnNmb3JtZXJzLCB0byBhdm9pZCByZXBldGl0aW9uXHJcbi8vIGluIHRoZSBDb25maWcgb2JqZWN0XHJcbmNvbnN0IHYgPSB7XHJcbiAgLy8gU3BsaXRzIHN0cmluZyB2YWx1ZSBpbnRvIGVsZW1lbnRzIGluIGFuIGFycmF5LCB0cmltcyBldmVyeSBlbGVtZW50LCBjaGVja3NcclxuICAvLyBpZiBhbiBhcnJheSBpcyBjb3JyZWN0LCBpZiBpdCBpcyBlbXB0eSwgYW5kIGlmIGl0IGlzLCByZXR1cm5zIHVuZGVmaW5lZFxyXG4gIGFycmF5OiAoZmlsdGVyQXJyYXkpID0+XHJcbiAgICB6XHJcbiAgICAgIC5zdHJpbmcoKVxyXG4gICAgICAudHJhbnNmb3JtKCh2YWx1ZSkgPT5cclxuICAgICAgICB2YWx1ZVxyXG4gICAgICAgICAgLnNwbGl0KCcsJylcclxuICAgICAgICAgIC5tYXAoKHZhbHVlKSA9PiB2YWx1ZS50cmltKCkpXHJcbiAgICAgICAgICAuZmlsdGVyKCh2YWx1ZSkgPT4gZmlsdGVyQXJyYXkuaW5jbHVkZXModmFsdWUpKVxyXG4gICAgICApXHJcbiAgICAgIC50cmFuc2Zvcm0oKHZhbHVlKSA9PiAodmFsdWUubGVuZ3RoID8gdmFsdWUgOiB1bmRlZmluZWQpKSxcclxuXHJcbiAgLy8gQWxsb3dzIG9ubHkgdHJ1ZSwgZmFsc2UgYW5kIGNvcnJlY3RseSBwYXJzZSB0aGUgdmFsdWUgdG8gYm9vbGVhblxyXG4gIC8vIG9yIG5vIHZhbHVlIGluIHdoaWNoIGNhc2UgdGhlIHJldHVybmVkIHZhbHVlIHdpbGwgYmUgdW5kZWZpbmVkXHJcbiAgYm9vbGVhbjogKCkgPT5cclxuICAgIHpcclxuICAgICAgLmVudW0oWyd0cnVlJywgJ2ZhbHNlJywgJyddKVxyXG4gICAgICAudHJhbnNmb3JtKCh2YWx1ZSkgPT4gKHZhbHVlICE9PSAnJyA/IHZhbHVlID09PSAndHJ1ZScgOiB1bmRlZmluZWQpKSxcclxuXHJcbiAgLy8gQWxsb3dzIHBhc3NlZCB2YWx1ZXMgb3Igbm8gdmFsdWUgaW4gd2hpY2ggY2FzZSB0aGUgcmV0dXJuZWQgdmFsdWUgd2lsbFxyXG4gIC8vIGJlIHVuZGVmaW5lZFxyXG4gIGVudW06ICh2YWx1ZXMpID0+XHJcbiAgICB6XHJcbiAgICAgIC5lbnVtKFsuLi52YWx1ZXMsICcnXSlcclxuICAgICAgLnRyYW5zZm9ybSgodmFsdWUpID0+ICh2YWx1ZSAhPT0gJycgPyB2YWx1ZSA6IHVuZGVmaW5lZCkpLFxyXG5cclxuICAvLyBUcmltcyB0aGUgc3RyaW5nIHZhbHVlIGFuZCBjaGVja3MgaWYgaXQgaXMgZW1wdHkgb3IgY29udGFpbnMgc3RyaW5naWZpZWRcclxuICAvLyB2YWx1ZXMgc3VjaCBhcyBmYWxzZSwgdW5kZWZpbmVkLCBudWxsLCBOYU4sIGlmIGl0IGRvZXMsIHJldHVybnMgdW5kZWZpbmVkXHJcbiAgc3RyaW5nOiAoKSA9PlxyXG4gICAgelxyXG4gICAgICAuc3RyaW5nKClcclxuICAgICAgLnRyaW0oKVxyXG4gICAgICAucmVmaW5lKFxyXG4gICAgICAgICh2YWx1ZSkgPT5cclxuICAgICAgICAgICFbJ2ZhbHNlJywgJ3VuZGVmaW5lZCcsICdudWxsJywgJ05hTiddLmluY2x1ZGVzKHZhbHVlKSB8fFxyXG4gICAgICAgICAgdmFsdWUgPT09ICcnLFxyXG4gICAgICAgICh2YWx1ZSkgPT4gKHtcclxuICAgICAgICAgIG1lc3NhZ2U6IGBUaGUgc3RyaW5nIGNvbnRhaW5zIGZvcmJpZGRlbiB2YWx1ZXMsIHJlY2VpdmVkICcke3ZhbHVlfSdgXHJcbiAgICAgICAgfSlcclxuICAgICAgKVxyXG4gICAgICAudHJhbnNmb3JtKCh2YWx1ZSkgPT4gKHZhbHVlICE9PSAnJyA/IHZhbHVlIDogdW5kZWZpbmVkKSksXHJcblxyXG4gIC8vIEFsbG93cyBwb3NpdGl2ZSBudW1iZXJzIG9yIG5vIHZhbHVlIGluIHdoaWNoIGNhc2UgdGhlIHJldHVybmVkIHZhbHVlIHdpbGxcclxuICAvLyBiZSB1bmRlZmluZWRcclxuICBwb3NpdGl2ZU51bTogKCkgPT5cclxuICAgIHpcclxuICAgICAgLnN0cmluZygpXHJcbiAgICAgIC50cmltKClcclxuICAgICAgLnJlZmluZShcclxuICAgICAgICAodmFsdWUpID0+XHJcbiAgICAgICAgICB2YWx1ZSA9PT0gJycgfHwgKCFpc05hTihwYXJzZUZsb2F0KHZhbHVlKSkgJiYgcGFyc2VGbG9hdCh2YWx1ZSkgPiAwKSxcclxuICAgICAgICAodmFsdWUpID0+ICh7XHJcbiAgICAgICAgICBtZXNzYWdlOiBgVGhlIHZhbHVlIG11c3QgYmUgbnVtZXJpYyBhbmQgcG9zaXRpdmUsIHJlY2VpdmVkICcke3ZhbHVlfSdgXHJcbiAgICAgICAgfSlcclxuICAgICAgKVxyXG4gICAgICAudHJhbnNmb3JtKCh2YWx1ZSkgPT4gKHZhbHVlICE9PSAnJyA/IHBhcnNlRmxvYXQodmFsdWUpIDogdW5kZWZpbmVkKSksXHJcblxyXG4gIC8vIEFsbG93cyBub24tbmVnYXRpdmUgbnVtYmVycyBvciBubyB2YWx1ZSBpbiB3aGljaCBjYXNlIHRoZSByZXR1cm5lZCB2YWx1ZVxyXG4gIC8vIHdpbGwgYmUgdW5kZWZpbmVkXHJcbiAgbm9uTmVnYXRpdmVOdW06ICgpID0+XHJcbiAgICB6XHJcbiAgICAgIC5zdHJpbmcoKVxyXG4gICAgICAudHJpbSgpXHJcbiAgICAgIC5yZWZpbmUoXHJcbiAgICAgICAgKHZhbHVlKSA9PlxyXG4gICAgICAgICAgdmFsdWUgPT09ICcnIHx8ICghaXNOYU4ocGFyc2VGbG9hdCh2YWx1ZSkpICYmIHBhcnNlRmxvYXQodmFsdWUpID49IDApLFxyXG4gICAgICAgICh2YWx1ZSkgPT4gKHtcclxuICAgICAgICAgIG1lc3NhZ2U6IGBUaGUgdmFsdWUgbXVzdCBiZSBudW1lcmljIGFuZCBub24tbmVnYXRpdmUsIHJlY2VpdmVkICcke3ZhbHVlfSdgXHJcbiAgICAgICAgfSlcclxuICAgICAgKVxyXG4gICAgICAudHJhbnNmb3JtKCh2YWx1ZSkgPT4gKHZhbHVlICE9PSAnJyA/IHBhcnNlRmxvYXQodmFsdWUpIDogdW5kZWZpbmVkKSlcclxufTtcclxuXHJcbmV4cG9ydCBjb25zdCBDb25maWcgPSB6Lm9iamVjdCh7XHJcbiAgLy8gaGlnaGNoYXJ0c1xyXG4gIEhJR0hDSEFSVFNfVkVSU0lPTjogelxyXG4gICAgLnN0cmluZygpXHJcbiAgICAudHJpbSgpXHJcbiAgICAucmVmaW5lKFxyXG4gICAgICAodmFsdWUpID0+IC9eKGxhdGVzdHxcXGQrKFxcLlxcZCspezAsMn0pJC8udGVzdCh2YWx1ZSkgfHwgdmFsdWUgPT09ICcnLFxyXG4gICAgICAodmFsdWUpID0+ICh7XHJcbiAgICAgICAgbWVzc2FnZTogYEhJR0hDSEFSVFNfVkVSU0lPTiBtdXN0IGJlICdsYXRlc3QnLCBhIG1ham9yIHZlcnNpb24sIG9yIGluIHRoZSBmb3JtIFhYLllZLlpaLCByZWNlaXZlZCAnJHt2YWx1ZX0nYFxyXG4gICAgICB9KVxyXG4gICAgKVxyXG4gICAgLnRyYW5zZm9ybSgodmFsdWUpID0+ICh2YWx1ZSAhPT0gJycgPyB2YWx1ZSA6IHVuZGVmaW5lZCkpLFxyXG4gIEhJR0hDSEFSVFNfQ0ROX1VSTDogelxyXG4gICAgLnN0cmluZygpXHJcbiAgICAudHJpbSgpXHJcbiAgICAucmVmaW5lKFxyXG4gICAgICAodmFsdWUpID0+XHJcbiAgICAgICAgdmFsdWUuc3RhcnRzV2l0aCgnaHR0cHM6Ly8nKSB8fFxyXG4gICAgICAgIHZhbHVlLnN0YXJ0c1dpdGgoJ2h0dHA6Ly8nKSB8fFxyXG4gICAgICAgIHZhbHVlID09PSAnJyxcclxuICAgICAgKHZhbHVlKSA9PiAoe1xyXG4gICAgICAgIG1lc3NhZ2U6IGBJbnZhbGlkIHZhbHVlIGZvciBISUdIQ0hBUlRTX0NETl9VUkwuIEl0IHNob3VsZCBzdGFydCB3aXRoIGh0dHA6Ly8gb3IgaHR0cHM6Ly8sIHJlY2VpdmVkICcke3ZhbHVlfSdgXHJcbiAgICAgIH0pXHJcbiAgICApXHJcbiAgICAudHJhbnNmb3JtKCh2YWx1ZSkgPT4gKHZhbHVlICE9PSAnJyA/IHZhbHVlIDogdW5kZWZpbmVkKSksXHJcbiAgSElHSENIQVJUU19DT1JFX1NDUklQVFM6IHYuYXJyYXkoc2NyaXB0c05hbWVzLmNvcmUpLFxyXG4gIEhJR0hDSEFSVFNfTU9EVUxFX1NDUklQVFM6IHYuYXJyYXkoc2NyaXB0c05hbWVzLm1vZHVsZXMpLFxyXG4gIEhJR0hDSEFSVFNfSU5ESUNBVE9SX1NDUklQVFM6IHYuYXJyYXkoc2NyaXB0c05hbWVzLmluZGljYXRvcnMpLFxyXG4gIEhJR0hDSEFSVFNfRk9SQ0VfRkVUQ0g6IHYuYm9vbGVhbigpLFxyXG4gIEhJR0hDSEFSVFNfQ0FDSEVfUEFUSDogdi5zdHJpbmcoKSxcclxuICBISUdIQ0hBUlRTX0FETUlOX1RPS0VOOiB2LnN0cmluZygpLFxyXG5cclxuICAvLyBleHBvcnRcclxuICBFWFBPUlRfVFlQRTogdi5lbnVtKFsnanBlZycsICdwbmcnLCAncGRmJywgJ3N2ZyddKSxcclxuICBFWFBPUlRfQ09OU1RSOiB2LmVudW0oWydjaGFydCcsICdzdG9ja0NoYXJ0JywgJ21hcENoYXJ0JywgJ2dhbnR0Q2hhcnQnXSksXHJcbiAgRVhQT1JUX0RFRkFVTFRfSEVJR0hUOiB2LnBvc2l0aXZlTnVtKCksXHJcbiAgRVhQT1JUX0RFRkFVTFRfV0lEVEg6IHYucG9zaXRpdmVOdW0oKSxcclxuICBFWFBPUlRfREVGQVVMVF9TQ0FMRTogdi5wb3NpdGl2ZU51bSgpLFxyXG4gIEVYUE9SVF9SQVNURVJJWkFUSU9OX1RJTUVPVVQ6IHYubm9uTmVnYXRpdmVOdW0oKSxcclxuXHJcbiAgLy8gY3VzdG9tXHJcbiAgQ1VTVE9NX0xPR0lDX0FMTE9XX0NPREVfRVhFQ1VUSU9OOiB2LmJvb2xlYW4oKSxcclxuICBDVVNUT01fTE9HSUNfQUxMT1dfRklMRV9SRVNPVVJDRVM6IHYuYm9vbGVhbigpLFxyXG5cclxuICAvLyBzZXJ2ZXJcclxuICBTRVJWRVJfRU5BQkxFOiB2LmJvb2xlYW4oKSxcclxuICBTRVJWRVJfSE9TVDogdi5zdHJpbmcoKSxcclxuICBTRVJWRVJfUE9SVDogdi5wb3NpdGl2ZU51bSgpLFxyXG4gIFNFUlZFUl9CRU5DSE1BUktJTkc6IHYuYm9vbGVhbigpLFxyXG5cclxuICAvLyBzZXJ2ZXIgcHJveHlcclxuICBTRVJWRVJfUFJPWFlfSE9TVDogdi5zdHJpbmcoKSxcclxuICBTRVJWRVJfUFJPWFlfUE9SVDogdi5wb3NpdGl2ZU51bSgpLFxyXG4gIFNFUlZFUl9QUk9YWV9USU1FT1VUOiB2Lm5vbk5lZ2F0aXZlTnVtKCksXHJcblxyXG4gIC8vIHNlcnZlciByYXRlIGxpbWl0aW5nXHJcbiAgU0VSVkVSX1JBVEVfTElNSVRJTkdfRU5BQkxFOiB2LmJvb2xlYW4oKSxcclxuICBTRVJWRVJfUkFURV9MSU1JVElOR19NQVhfUkVRVUVTVFM6IHYubm9uTmVnYXRpdmVOdW0oKSxcclxuICBTRVJWRVJfUkFURV9MSU1JVElOR19XSU5ET1c6IHYubm9uTmVnYXRpdmVOdW0oKSxcclxuICBTRVJWRVJfUkFURV9MSU1JVElOR19ERUxBWTogdi5ub25OZWdhdGl2ZU51bSgpLFxyXG4gIFNFUlZFUl9SQVRFX0xJTUlUSU5HX1RSVVNUX1BST1hZOiB2LmJvb2xlYW4oKSxcclxuICBTRVJWRVJfUkFURV9MSU1JVElOR19TS0lQX0tFWTogdi5zdHJpbmcoKSxcclxuICBTRVJWRVJfUkFURV9MSU1JVElOR19TS0lQX1RPS0VOOiB2LnN0cmluZygpLFxyXG5cclxuICAvLyBzZXJ2ZXIgc3NsXHJcbiAgU0VSVkVSX1NTTF9FTkFCTEU6IHYuYm9vbGVhbigpLFxyXG4gIFNFUlZFUl9TU0xfRk9SQ0U6IHYuYm9vbGVhbigpLFxyXG4gIFNFUlZFUl9TU0xfUE9SVDogdi5wb3NpdGl2ZU51bSgpLFxyXG4gIFNFUlZFUl9TU0xfQ0VSVF9QQVRIOiB2LnN0cmluZygpLFxyXG5cclxuICAvLyBwb29sXHJcbiAgUE9PTF9NSU5fV09SS0VSUzogdi5ub25OZWdhdGl2ZU51bSgpLFxyXG4gIFBPT0xfTUFYX1dPUktFUlM6IHYubm9uTmVnYXRpdmVOdW0oKSxcclxuICBQT09MX1dPUktfTElNSVQ6IHYucG9zaXRpdmVOdW0oKSxcclxuICBQT09MX0FDUVVJUkVfVElNRU9VVDogdi5ub25OZWdhdGl2ZU51bSgpLFxyXG4gIFBPT0xfQ1JFQVRFX1RJTUVPVVQ6IHYubm9uTmVnYXRpdmVOdW0oKSxcclxuICBQT09MX0RFU1RST1lfVElNRU9VVDogdi5ub25OZWdhdGl2ZU51bSgpLFxyXG4gIFBPT0xfSURMRV9USU1FT1VUOiB2Lm5vbk5lZ2F0aXZlTnVtKCksXHJcbiAgUE9PTF9DUkVBVEVfUkVUUllfSU5URVJWQUw6IHYubm9uTmVnYXRpdmVOdW0oKSxcclxuICBQT09MX1JFQVBFUl9JTlRFUlZBTDogdi5ub25OZWdhdGl2ZU51bSgpLFxyXG4gIFBPT0xfQkVOQ0hNQVJLSU5HOiB2LmJvb2xlYW4oKSxcclxuXHJcbiAgLy8gbG9nZ2VyXHJcbiAgTE9HR0lOR19MRVZFTDogelxyXG4gICAgLnN0cmluZygpXHJcbiAgICAudHJpbSgpXHJcbiAgICAucmVmaW5lKFxyXG4gICAgICAodmFsdWUpID0+XHJcbiAgICAgICAgdmFsdWUgPT09ICcnIHx8XHJcbiAgICAgICAgKCFpc05hTihwYXJzZUZsb2F0KHZhbHVlKSkgJiZcclxuICAgICAgICAgIHBhcnNlRmxvYXQodmFsdWUpID49IDAgJiZcclxuICAgICAgICAgIHBhcnNlRmxvYXQodmFsdWUpIDw9IDUpLFxyXG4gICAgICAodmFsdWUpID0+ICh7XHJcbiAgICAgICAgbWVzc2FnZTogYEludmFsaWQgdmFsdWUgZm9yIExPR0dJTkdfTEVWRUwuIFdlIG9ubHkgYWNjZXB0IHZhbHVlcyBmcm9tIDAgdG8gNSBhcyBsb2dnaW5nIGxldmVscywgcmVjZWl2ZWQgJyR7dmFsdWV9J2BcclxuICAgICAgfSlcclxuICAgIClcclxuICAgIC50cmFuc2Zvcm0oKHZhbHVlKSA9PiAodmFsdWUgIT09ICcnID8gcGFyc2VGbG9hdCh2YWx1ZSkgOiB1bmRlZmluZWQpKSxcclxuICBMT0dHSU5HX0ZJTEU6IHYuc3RyaW5nKCksXHJcbiAgTE9HR0lOR19ERVNUOiB2LnN0cmluZygpLFxyXG5cclxuICAvLyB1aVxyXG4gIFVJX0VOQUJMRTogdi5ib29sZWFuKCksXHJcbiAgVUlfUk9VVEU6IHYuc3RyaW5nKCksXHJcblxyXG4gIC8vIG90aGVyXHJcbiAgT1RIRVJfTk9ERV9FTlY6IHYuZW51bShbJ2RldmVsb3BtZW50JywgJ3Byb2R1Y3Rpb24nLCAndGVzdCddKSxcclxuICBPVEhFUl9MSVNURU5fVE9fUFJPQ0VTU19FWElUUzogdi5ib29sZWFuKCksXHJcbiAgT1RIRVJfTk9fTE9HTzogdi5ib29sZWFuKCksXHJcbiAgT1RIRVJfSEFSRF9SRVNFVF9QQUdFOiB2LmJvb2xlYW4oKSxcclxuXHJcbiAgLy8gZGVidWdnZXJcclxuICBERUJVR19FTkFCTEU6IHYuYm9vbGVhbigpLFxyXG4gIERFQlVHX0hFQURMRVNTOiB2LmJvb2xlYW4oKSxcclxuICBERUJVR19ERVZUT09MUzogdi5ib29sZWFuKCksXHJcbiAgREVCVUdfTElTVEVOX1RPX0NPTlNPTEU6IHYuYm9vbGVhbigpLFxyXG4gIERFQlVHX0RVTVBJTzogdi5ib29sZWFuKCksXHJcbiAgREVCVUdfU0xPV19NTzogdi5ub25OZWdhdGl2ZU51bSgpLFxyXG4gIERFQlVHX0RFQlVHR0lOR19QT1JUOiB2LnBvc2l0aXZlTnVtKClcclxufSk7XHJcblxyXG5leHBvcnQgY29uc3QgZW52cyA9IENvbmZpZy5wYXJ0aWFsKCkucGFyc2UocHJvY2Vzcy5lbnYpO1xyXG4iLCIvKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKlxyXG5cclxuSGlnaGNoYXJ0cyBFeHBvcnQgU2VydmVyXHJcblxyXG5Db3B5cmlnaHQgKGMpIDIwMTYtMjAyNCwgSGlnaHNvZnRcclxuXHJcbkxpY2VuY2VkIHVuZGVyIHRoZSBNSVQgbGljZW5jZS5cclxuXHJcbkFkZGl0aW9uYWxseSBhIHZhbGlkIEhpZ2hjaGFydHMgbGljZW5zZSBpcyByZXF1aXJlZCBmb3IgdXNlLlxyXG5cclxuU2VlIExJQ0VOU0UgZmlsZSBpbiByb290IGZvciBkZXRhaWxzLlxyXG5cclxuKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKi9cclxuXHJcbmltcG9ydCB7IGFwcGVuZEZpbGUsIGV4aXN0c1N5bmMsIG1rZGlyU3luYyB9IGZyb20gJ2ZzJztcclxuXHJcbmltcG9ydCB7IGRlZmF1bHRDb25maWcgfSBmcm9tICcuL3NjaGVtYXMvY29uZmlnLmpzJztcclxuXHJcbi8vIFRoZSBhdmFpbGFibGUgY29sb3JzXHJcbmNvbnN0IGNvbG9ycyA9IFsncmVkJywgJ3llbGxvdycsICdibHVlJywgJ2dyYXknLCAnZ3JlZW4nXTtcclxuXHJcbi8vIFRoZSBkZWZhdWx0IGxvZ2dpbmcgY29uZmlnXHJcbmxldCBsb2dnaW5nID0ge1xyXG4gIC8vIEZsYWdzIGZvciBsb2dnaW5nIHN0YXR1c1xyXG4gIHRvQ29uc29sZTogdHJ1ZSxcclxuICB0b0ZpbGU6IGZhbHNlLFxyXG4gIHBhdGhDcmVhdGVkOiBmYWxzZSxcclxuICAvLyBMb2cgbGV2ZWxzXHJcbiAgbGV2ZWxzRGVzYzogW1xyXG4gICAge1xyXG4gICAgICB0aXRsZTogJ2Vycm9yJyxcclxuICAgICAgY29sb3I6IGNvbG9yc1swXVxyXG4gICAgfSxcclxuICAgIHtcclxuICAgICAgdGl0bGU6ICd3YXJuaW5nJyxcclxuICAgICAgY29sb3I6IGNvbG9yc1sxXVxyXG4gICAgfSxcclxuICAgIHtcclxuICAgICAgdGl0bGU6ICdub3RpY2UnLFxyXG4gICAgICBjb2xvcjogY29sb3JzWzJdXHJcbiAgICB9LFxyXG4gICAge1xyXG4gICAgICB0aXRsZTogJ3ZlcmJvc2UnLFxyXG4gICAgICBjb2xvcjogY29sb3JzWzNdXHJcbiAgICB9LFxyXG4gICAge1xyXG4gICAgICB0aXRsZTogJ2JlbmNobWFyaycsXHJcbiAgICAgIGNvbG9yOiBjb2xvcnNbNF1cclxuICAgIH1cclxuICBdLFxyXG4gIC8vIExvZyBsaXN0ZW5lcnNcclxuICBsaXN0ZW5lcnM6IFtdXHJcbn07XHJcblxyXG4vLyBHYXRoZXIgaW5pdCBsb2dnaW5nIG9wdGlvbnNcclxuZm9yIChjb25zdCBba2V5LCBvcHRpb25dIG9mIE9iamVjdC5lbnRyaWVzKGRlZmF1bHRDb25maWcubG9nZ2luZykpIHtcclxuICBsb2dnaW5nW2tleV0gPSBvcHRpb24udmFsdWU7XHJcbn1cclxuXHJcbi8qKlxyXG4gKiBMb2dzIHRoZSBwcm92aWRlZCB0ZXh0cyB0byBhIGZpbGUsIGlmIGZpbGUgbG9nZ2luZyBpcyBlbmFibGVkLiBJdCBjcmVhdGVzXHJcbiAqIHRoZSBuZWNlc3NhcnkgZGlyZWN0b3J5IHN0cnVjdHVyZSBpZiBub3QgYWxyZWFkeSBjcmVhdGVkIGFuZCBhcHBlbmRzIHRoZVxyXG4gKiBjb250ZW50LCBpbmNsdWRpbmcgYW4gb3B0aW9uYWwgcHJlZml4LCB0byB0aGUgc3BlY2lmaWVkIGxvZyBmaWxlLlxyXG4gKlxyXG4gKiBAcGFyYW0ge3N0cmluZ1tdfSB0ZXh0cyAtIEFuIGFycmF5IG9mIHRleHRzIHRvIGJlIGxvZ2dlZC5cclxuICogQHBhcmFtIHtzdHJpbmd9IHByZWZpeCAtIEFuIG9wdGlvbmFsIHByZWZpeCB0byBiZSBhZGRlZCB0byBlYWNoIGxvZyBlbnRyeS5cclxuICovXHJcbmNvbnN0IGxvZ1RvRmlsZSA9ICh0ZXh0cywgcHJlZml4KSA9PiB7XHJcbiAgaWYgKGxvZ2dpbmcudG9GaWxlKSB7XHJcbiAgICBpZiAoIWxvZ2dpbmcucGF0aENyZWF0ZWQpIHtcclxuICAgICAgLy8gQ3JlYXRlIGlmIGRvZXMgbm90IGV4aXN0XHJcbiAgICAgICFleGlzdHNTeW5jKGxvZ2dpbmcuZGVzdCkgJiYgbWtkaXJTeW5jKGxvZ2dpbmcuZGVzdCk7XHJcblxyXG4gICAgICAvLyBXZSBub3cgYXNzdW1lIHRoZSBwYXRoIGlzIGF2YWlsYWJsZSwgZS5nLiBpdCdzIHRoZSByZXNwb25zaWJpbGl0eVxyXG4gICAgICAvLyBvZiB0aGUgdXNlciB0byBjcmVhdGUgdGhlIHBhdGggd2l0aCB0aGUgY29ycmVjdCBhY2Nlc3MgcmlnaHRzLlxyXG4gICAgICBsb2dnaW5nLnBhdGhDcmVhdGVkID0gdHJ1ZTtcclxuICAgIH1cclxuXHJcbiAgICAvLyBBZGQgdGhlIGNvbnRlbnQgdG8gYSBmaWxlXHJcbiAgICBhcHBlbmRGaWxlKFxyXG4gICAgICBgJHtsb2dnaW5nLmRlc3R9JHtsb2dnaW5nLmZpbGV9YCxcclxuICAgICAgW3ByZWZpeF0uY29uY2F0KHRleHRzKS5qb2luKCcgJykgKyAnXFxuJyxcclxuICAgICAgKGVycm9yKSA9PiB7XHJcbiAgICAgICAgaWYgKGVycm9yKSB7XHJcbiAgICAgICAgICBjb25zb2xlLmxvZyhgW2xvZ2dlcl0gVW5hYmxlIHRvIHdyaXRlIHRvIGxvZyBmaWxlOiAke2Vycm9yfWApO1xyXG4gICAgICAgICAgbG9nZ2luZy50b0ZpbGUgPSBmYWxzZTtcclxuICAgICAgICB9XHJcbiAgICAgIH1cclxuICAgICk7XHJcbiAgfVxyXG59O1xyXG5cclxuLyoqXHJcbiAqIExvZ3MgYSBtZXNzYWdlLiBBY2NlcHRzIGEgdmFyaWFibGUgYW1vdW50IG9mIGFyZ3VtZW50cy4gQXJndW1lbnRzIGFmdGVyXHJcbiAqIGBsZXZlbGAgd2lsbCBiZSBwYXNzZWQgZGlyZWN0bHkgdG8gY29uc29sZS5sb2csIGFuZC9vciB3aWxsIGJlIGpvaW5lZFxyXG4gKiBhbmQgYXBwZW5kZWQgdG8gdGhlIGxvZyBmaWxlLlxyXG4gKlxyXG4gKiBAcGFyYW0ge2FueX0gYXJncyAtIEFuIGFycmF5IG9mIGFyZ3VtZW50cyB3aGVyZSB0aGUgZmlyc3QgaXMgdGhlIGxvZyBsZXZlbFxyXG4gKiBhbmQgdGhlIHJlc3QgYXJlIHN0cmluZ3MgdG8gYnVpbGQgYSBtZXNzYWdlIHdpdGguXHJcbiAqL1xyXG5leHBvcnQgY29uc3QgbG9nID0gKC4uLmFyZ3MpID0+IHtcclxuICBjb25zdCBbbmV3TGV2ZWwsIC4uLnRleHRzXSA9IGFyZ3M7XHJcblxyXG4gIC8vIEN1cnJlbnQgbG9nZ2luZyBvcHRpb25zXHJcbiAgY29uc3QgeyBsZXZlbCwgbGV2ZWxzRGVzYyB9ID0gbG9nZ2luZztcclxuXHJcbiAgLy8gQ2hlY2sgaWYgbG9nIGxldmVsIGlzIHdpdGhpbiBhIGNvcnJlY3QgcmFuZ2Ugb3IgaXMgYSBiZW5jaG1hcmsgbG9nXHJcbiAgaWYgKFxyXG4gICAgbmV3TGV2ZWwgIT09IDUgJiZcclxuICAgIChuZXdMZXZlbCA9PT0gMCB8fCBuZXdMZXZlbCA+IGxldmVsIHx8IGxldmVsID4gbGV2ZWxzRGVzYy5sZW5ndGgpXHJcbiAgKSB7XHJcbiAgICByZXR1cm47XHJcbiAgfVxyXG5cclxuICAvLyBHZXQgcmlkIG9mIHRoZSBHTVQgdGV4dCBpbmZvcm1hdGlvblxyXG4gIGNvbnN0IG5ld0RhdGUgPSBuZXcgRGF0ZSgpLnRvU3RyaW5nKCkuc3BsaXQoJygnKVswXS50cmltKCk7XHJcblxyXG4gIC8vIENyZWF0ZSBhIG1lc3NhZ2UncyBwcmVmaXhcclxuICBjb25zdCBwcmVmaXggPSBgJHtuZXdEYXRlfSBbJHtsZXZlbHNEZXNjW25ld0xldmVsIC0gMV0udGl0bGV9XSAtYDtcclxuXHJcbiAgLy8gQ2FsbCBhdmFpbGFibGUgbG9nIGxpc3RlbmVyc1xyXG4gIGxvZ2dpbmcubGlzdGVuZXJzLmZvckVhY2goKGZuKSA9PiB7XHJcbiAgICBmbihwcmVmaXgsIHRleHRzLmpvaW4oJyAnKSk7XHJcbiAgfSk7XHJcblxyXG4gIC8vIExvZyB0byBjb25zb2xlXHJcbiAgaWYgKGxvZ2dpbmcudG9Db25zb2xlKSB7XHJcbiAgICBjb25zb2xlLmxvZy5hcHBseShcclxuICAgICAgdW5kZWZpbmVkLFxyXG4gICAgICBbcHJlZml4LnRvU3RyaW5nKClbbG9nZ2luZy5sZXZlbHNEZXNjW25ld0xldmVsIC0gMV0uY29sb3JdXS5jb25jYXQodGV4dHMpXHJcbiAgICApO1xyXG4gIH1cclxuXHJcbiAgLy8gTG9nIHRvIGZpbGVcclxuICBsb2dUb0ZpbGUodGV4dHMsIHByZWZpeCk7XHJcbn07XHJcblxyXG4vKipcclxuICogTG9ncyBhbiBlcnJvciBtZXNzYWdlIHdpdGggaXRzIHN0YWNrIHRyYWNlLiBPcHRpb25hbGx5LCBhIGN1c3RvbSBtZXNzYWdlXHJcbiAqIGNhbiBiZSBwcm92aWRlZC5cclxuICpcclxuICogQHBhcmFtIHtudW1iZXJ9IGxldmVsIC0gVGhlIGxvZyBsZXZlbC5cclxuICogQHBhcmFtIHtFcnJvcn0gZXJyb3IgLSBUaGUgZXJyb3Igb2JqZWN0LlxyXG4gKiBAcGFyYW0ge3N0cmluZ30gY3VzdG9tTWVzc2FnZSAtIEFuIG9wdGlvbmFsIGN1c3RvbSBtZXNzYWdlIHRvIGJlIGxvZ2dlZCBhbG9uZ1xyXG4gKiB3aXRoIHRoZSBlcnJvci5cclxuICovXHJcbmV4cG9ydCBjb25zdCBsb2dXaXRoU3RhY2sgPSAobmV3TGV2ZWwsIGVycm9yLCBjdXN0b21NZXNzYWdlKSA9PiB7XHJcbiAgLy8gR2V0IHRoZSBtYWluIG1lc3NhZ2VcclxuICBjb25zdCBtYWluTWVzc2FnZSA9IGN1c3RvbU1lc3NhZ2UgfHwgZXJyb3IubWVzc2FnZTtcclxuXHJcbiAgLy8gQ3VycmVudCBsb2dnaW5nIG9wdGlvbnNcclxuICBjb25zdCB7IGxldmVsLCBsZXZlbHNEZXNjIH0gPSBsb2dnaW5nO1xyXG5cclxuICAvLyBDaGVjayBpZiBsb2cgbGV2ZWwgaXMgd2l0aGluIGEgY29ycmVjdCByYW5nZVxyXG4gIGlmIChuZXdMZXZlbCA9PT0gMCB8fCBuZXdMZXZlbCA+IGxldmVsIHx8IGxldmVsID4gbGV2ZWxzRGVzYy5sZW5ndGgpIHtcclxuICAgIHJldHVybjtcclxuICB9XHJcblxyXG4gIC8vIEdldCByaWQgb2YgdGhlIEdNVCB0ZXh0IGluZm9ybWF0aW9uXHJcbiAgY29uc3QgbmV3RGF0ZSA9IG5ldyBEYXRlKCkudG9TdHJpbmcoKS5zcGxpdCgnKCcpWzBdLnRyaW0oKTtcclxuXHJcbiAgLy8gQ3JlYXRlIGEgbWVzc2FnZSdzIHByZWZpeFxyXG4gIGNvbnN0IHByZWZpeCA9IGAke25ld0RhdGV9IFske2xldmVsc0Rlc2NbbmV3TGV2ZWwgLSAxXS50aXRsZX1dIC1gO1xyXG5cclxuICAvLyBJZiB0aGUgY3VzdG9tTWVzc2FnZSBleGlzdHMsIHdlIHdhbnQgdG8gZGlzcGxheSB0aGUgd2hvbGUgc3RhY2sgbWVzc2FnZVxyXG4gIGNvbnN0IHN0YWNrTWVzc2FnZSA9XHJcbiAgICBlcnJvci5tZXNzYWdlICE9PSBlcnJvci5zdGFja01lc3NhZ2UgfHwgZXJyb3Iuc3RhY2tNZXNzYWdlID09PSB1bmRlZmluZWRcclxuICAgICAgPyBlcnJvci5zdGFja1xyXG4gICAgICA6IGVycm9yLnN0YWNrLnNwbGl0KCdcXG4nKS5zbGljZSgxKS5qb2luKCdcXG4nKTtcclxuXHJcbiAgLy8gQ29tYmluZSBjdXN0b20gbWVzc2FnZSBvciBlcnJvciBtZXNzYWdlIHdpdGggZXJyb3Igc3RhY2sgbWVzc2FnZVxyXG4gIGNvbnN0IHRleHRzID0gW21haW5NZXNzYWdlLCAnXFxuJywgc3RhY2tNZXNzYWdlXTtcclxuXHJcbiAgLy8gTG9nIHRvIGNvbnNvbGVcclxuICBpZiAobG9nZ2luZy50b0NvbnNvbGUpIHtcclxuICAgIGNvbnNvbGUubG9nLmFwcGx5KFxyXG4gICAgICB1bmRlZmluZWQsXHJcbiAgICAgIFtwcmVmaXgudG9TdHJpbmcoKVtsb2dnaW5nLmxldmVsc0Rlc2NbbmV3TGV2ZWwgLSAxXS5jb2xvcl1dLmNvbmNhdChbXHJcbiAgICAgICAgbWFpbk1lc3NhZ2VbY29sb3JzW25ld0xldmVsIC0gMV1dLFxyXG4gICAgICAgICdcXG4nLFxyXG4gICAgICAgIHN0YWNrTWVzc2FnZVxyXG4gICAgICBdKVxyXG4gICAgKTtcclxuICB9XHJcblxyXG4gIC8vIENhbGwgYXZhaWxhYmxlIGxvZyBsaXN0ZW5lcnNcclxuICBsb2dnaW5nLmxpc3RlbmVycy5mb3JFYWNoKChmbikgPT4ge1xyXG4gICAgZm4ocHJlZml4LCB0ZXh0cy5qb2luKCcgJykpO1xyXG4gIH0pO1xyXG5cclxuICAvLyBMb2cgdG8gZmlsZVxyXG4gIGxvZ1RvRmlsZSh0ZXh0cywgcHJlZml4KTtcclxufTtcclxuXHJcbi8qKlxyXG4gKiBTZXRzIHRoZSBsb2cgbGV2ZWwgdG8gdGhlIHNwZWNpZmllZCB2YWx1ZS4gTG9nIGxldmVscyBhcmUgKDAgPSBubyBsb2dnaW5nLFxyXG4gKiAxID0gZXJyb3IsIDIgPSB3YXJuaW5nLCAzID0gbm90aWNlLCA0ID0gdmVyYm9zZSBvciA1ID0gYmVuY2htYXJrKVxyXG4gKlxyXG4gKiBAcGFyYW0ge251bWJlcn0gbmV3TGV2ZWwgLSBUaGUgbmV3IGxvZyBsZXZlbCB0byBiZSBzZXQuXHJcbiAqL1xyXG5leHBvcnQgY29uc3Qgc2V0TG9nTGV2ZWwgPSAobmV3TGV2ZWwpID0+IHtcclxuICBpZiAobmV3TGV2ZWwgPj0gMCAmJiBuZXdMZXZlbCA8PSBsb2dnaW5nLmxldmVsc0Rlc2MubGVuZ3RoKSB7XHJcbiAgICBsb2dnaW5nLmxldmVsID0gbmV3TGV2ZWw7XHJcbiAgfVxyXG59O1xyXG5cclxuLyoqXHJcbiAqIEVuYWJsZXMgZmlsZSBsb2dnaW5nIHdpdGggdGhlIHNwZWNpZmllZCBkZXN0aW5hdGlvbiBhbmQgbG9nIGZpbGUuXHJcbiAqXHJcbiAqIEBwYXJhbSB7c3RyaW5nfSBsb2dEZXN0IC0gVGhlIGRlc3RpbmF0aW9uIHBhdGggZm9yIGxvZyBmaWxlcy5cclxuICogQHBhcmFtIHtzdHJpbmd9IGxvZ0ZpbGUgLSBUaGUgbG9nIGZpbGUgbmFtZS5cclxuICovXHJcbmV4cG9ydCBjb25zdCBlbmFibGVGaWxlTG9nZ2luZyA9IChsb2dEZXN0LCBsb2dGaWxlKSA9PiB7XHJcbiAgLy8gVXBkYXRlIGxvZ2dpbmcgb3B0aW9uc1xyXG4gIGxvZ2dpbmcgPSB7XHJcbiAgICAuLi5sb2dnaW5nLFxyXG4gICAgZGVzdDogbG9nRGVzdCB8fCBsb2dnaW5nLmRlc3QsXHJcbiAgICBmaWxlOiBsb2dGaWxlIHx8IGxvZ2dpbmcuZmlsZSxcclxuICAgIHRvRmlsZTogdHJ1ZVxyXG4gIH07XHJcblxyXG4gIGlmIChsb2dnaW5nLmRlc3QubGVuZ3RoID09PSAwKSB7XHJcbiAgICByZXR1cm4gbG9nKDEsICdbbG9nZ2VyXSBGaWxlIGxvZ2dpbmcgaW5pdGlhbGl6YXRpb246IG5vIHBhdGggc3VwcGxpZWQuJyk7XHJcbiAgfVxyXG5cclxuICBpZiAoIWxvZ2dpbmcuZGVzdC5lbmRzV2l0aCgnLycpKSB7XHJcbiAgICBsb2dnaW5nLmRlc3QgKz0gJy8nO1xyXG4gIH1cclxufTtcclxuXHJcbi8qKlxyXG4gKiBJbml0aWFsaXplcyBsb2dnaW5nIHdpdGggdGhlIHNwZWNpZmllZCBsb2dnaW5nIGNvbmZpZ3VyYXRpb24uXHJcbiAqXHJcbiAqIEBwYXJhbSB7T2JqZWN0fSBsb2dnaW5nIC0gVGhlIGxvZ2dpbmcgY29uZmlndXJhdGlvbiBvYmplY3QuXHJcbiAqL1xyXG5leHBvcnQgY29uc3QgaW5pdExvZ2dpbmcgPSAobG9nZ2luZykgPT4ge1xyXG4gIC8vIFNldCB0aGUgbG9nIGxldmVsXHJcbiAgc2V0TG9nTGV2ZWwobG9nZ2luZyAmJiBwYXJzZUludChsb2dnaW5nLmxldmVsKSk7XHJcblxyXG4gIC8vIFNldCB0aGUgbG9nIGZpbGUgcGF0aCBhbmQgbmFtZVxyXG4gIGlmIChsb2dnaW5nICYmIGxvZ2dpbmcuZGVzdCkge1xyXG4gICAgZW5hYmxlRmlsZUxvZ2dpbmcoXHJcbiAgICAgIGxvZ2dpbmcuZGVzdCxcclxuICAgICAgbG9nZ2luZy5maWxlIHx8ICdoaWdoY2hhcnRzLWV4cG9ydC1zZXJ2ZXIubG9nJ1xyXG4gICAgKTtcclxuICB9XHJcbn07XHJcblxyXG4vKipcclxuICogQWRkcyBhIGxpc3RlbmVyIGZ1bmN0aW9uIHRvIHRoZSBsb2dnaW5nIHN5c3RlbS5cclxuICpcclxuICogQHBhcmFtIHtmdW5jdGlvbn0gZm4gLSBUaGUgbGlzdGVuZXIgZnVuY3Rpb24gdG8gYmUgYWRkZWQuXHJcbiAqL1xyXG5leHBvcnQgY29uc3QgbGlzdGVuID0gKGZuKSA9PiB7XHJcbiAgbG9nZ2luZy5saXN0ZW5lcnMucHVzaChmbik7XHJcbn07XHJcblxyXG4vKipcclxuICogVG9nZ2xlcyB0aGUgc3RhbmRhcmQgb3V0cHV0IChjb25zb2xlKSBsb2dnaW5nLlxyXG4gKlxyXG4gKiBAcGFyYW0ge2Jvb2xlYW59IGVuYWJsZWQgLSBJZiB0cnVlLCBlbmFibGVzIGNvbnNvbGUgbG9nZ2luZzsgaWYgZmFsc2UsXHJcbiAqIGRpc2FibGVzIGl0LlxyXG4gKi9cclxuZXhwb3J0IGNvbnN0IHRvZ2dsZVNURE91dCA9IChlbmFibGVkKSA9PiB7XHJcbiAgbG9nZ2luZy50b0NvbnNvbGUgPSBlbmFibGVkO1xyXG59O1xyXG5cclxuZXhwb3J0IGRlZmF1bHQge1xyXG4gIGxvZyxcclxuICBsb2dXaXRoU3RhY2ssXHJcbiAgc2V0TG9nTGV2ZWwsXHJcbiAgZW5hYmxlRmlsZUxvZ2dpbmcsXHJcbiAgaW5pdExvZ2dpbmcsXHJcbiAgbGlzdGVuLFxyXG4gIHRvZ2dsZVNURE91dFxyXG59O1xyXG4iLCIvKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKlxyXG5cclxuSGlnaGNoYXJ0cyBFeHBvcnQgU2VydmVyXHJcblxyXG5Db3B5cmlnaHQgKGMpIDIwMTYtMjAyNCwgSGlnaHNvZnRcclxuXHJcbkxpY2VuY2VkIHVuZGVyIHRoZSBNSVQgbGljZW5jZS5cclxuXHJcbkFkZGl0aW9uYWxseSBhIHZhbGlkIEhpZ2hjaGFydHMgbGljZW5zZSBpcyByZXF1aXJlZCBmb3IgdXNlLlxyXG5cclxuU2VlIExJQ0VOU0UgZmlsZSBpbiByb290IGZvciBkZXRhaWxzLlxyXG5cclxuKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKi9cclxuXHJcbmltcG9ydCB7IHJlYWRGaWxlU3luYyB9IGZyb20gJ2ZzJztcclxuaW1wb3J0IHsgam9pbiB9IGZyb20gJ3BhdGgnO1xyXG5pbXBvcnQgeyBmaWxlVVJMVG9QYXRoIH0gZnJvbSAndXJsJztcclxuXHJcbmltcG9ydCB7IGRlZmF1bHRDb25maWcgfSBmcm9tICcuLi9saWIvc2NoZW1hcy9jb25maWcuanMnO1xyXG5pbXBvcnQgeyBsb2csIGxvZ1dpdGhTdGFjayB9IGZyb20gJy4vbG9nZ2VyLmpzJztcclxuXHJcbmNvbnN0IE1BWF9CQUNLT0ZGX0FUVEVNUFRTID0gNjtcclxuXHJcbmV4cG9ydCBjb25zdCBfX2Rpcm5hbWUgPSBmaWxlVVJMVG9QYXRoKG5ldyBVUkwoJy4uLy4nLCBpbXBvcnQubWV0YS51cmwpKTtcclxuXHJcbi8qKlxyXG4gKiBDbGVhcnMgYW5kIHN0YW5kYXJkaXplcyB0ZXh0IGJ5IHJlcGxhY2luZyBtdWx0aXBsZSBjb25zZWN1dGl2ZSB3aGl0ZXNwYWNlXHJcbiAqIGNoYXJhY3RlcnMgd2l0aCBhIHNpbmdsZSBzcGFjZSBhbmQgdHJpbW1pbmcgYW55IGxlYWRpbmcgb3IgdHJhaWxpbmdcclxuICogd2hpdGVzcGFjZS5cclxuICpcclxuICogQHBhcmFtIHtzdHJpbmd9IHRleHQgLSBUaGUgaW5wdXQgdGV4dCB0byBiZSBjbGVhcmVkLlxyXG4gKiBAcGFyYW0ge1JlZ0V4cH0gW3J1bGU9L1xcc1xccysvZ10gLSBUaGUgcmVndWxhciBleHByZXNzaW9uIHJ1bGUgdG8gbWF0Y2hcclxuICogbXVsdGlwbGUgY29uc2VjdXRpdmUgd2hpdGVzcGFjZSBjaGFyYWN0ZXJzLlxyXG4gKiBAcGFyYW0ge3N0cmluZ30gW3JlcGxhY2VyPScgJ10gLSBUaGUgc3RyaW5nIHVzZWQgdG8gcmVwbGFjZSBtdWx0aXBsZVxyXG4gKiBjb25zZWN1dGl2ZSB3aGl0ZXNwYWNlIGNoYXJhY3RlcnMuXHJcbiAqXHJcbiAqIEByZXR1cm5zIHtzdHJpbmd9IC0gVGhlIGNsZWFyZWQgYW5kIHN0YW5kYXJkaXplZCB0ZXh0LlxyXG4gKi9cclxuZXhwb3J0IGNvbnN0IGNsZWFyVGV4dCA9ICh0ZXh0LCBydWxlID0gL1xcc1xccysvZywgcmVwbGFjZXIgPSAnICcpID0+XHJcbiAgdGV4dC5yZXBsYWNlQWxsKHJ1bGUsIHJlcGxhY2VyKS50cmltKCk7XHJcblxyXG4vKipcclxuICogSW1wbGVtZW50cyBhbiBleHBvbmVudGlhbCBiYWNrb2ZmIHN0cmF0ZWd5IGZvciByZXRyeWluZyBhIGZ1bmN0aW9uIHVudGlsXHJcbiAqIGEgY2VydGFpbiBudW1iZXIgb2YgYXR0ZW1wdHMgYXJlIHJlYWNoZWQuXHJcbiAqXHJcbiAqIEBwYXJhbSB7RnVuY3Rpb259IGZuIC0gVGhlIGZ1bmN0aW9uIHRvIGJlIHJldHJpZWQuXHJcbiAqIEBwYXJhbSB7bnVtYmVyfSBbYXR0ZW1wdD0wXSAtIFRoZSBjdXJyZW50IGF0dGVtcHQgbnVtYmVyLlxyXG4gKiBAcGFyYW0gey4uLmFueX0gYXJncyAtIEFyZ3VtZW50cyB0byBiZSBwYXNzZWQgdG8gdGhlIGZ1bmN0aW9uLlxyXG4gKlxyXG4gKiBAcmV0dXJucyB7UHJvbWlzZX0gLSBBIHByb21pc2UgdGhhdCByZXNvbHZlcyB0byB0aGUgcmVzdWx0IG9mIHRoZSBmdW5jdGlvblxyXG4gKiBpZiBzdWNjZXNzZnVsLlxyXG4gKlxyXG4gKiBAdGhyb3dzIHtFcnJvcn0gLSBUaHJvd3MgYW4gZXJyb3IgaWYgdGhlIG1heGltdW0gbnVtYmVyIG9mIGF0dGVtcHRzXHJcbiAqIGlzIHJlYWNoZWQuXHJcbiAqL1xyXG5leHBvcnQgY29uc3QgZXhwQmFja29mZiA9IGFzeW5jIChmbiwgYXR0ZW1wdCA9IDAsIC4uLmFyZ3MpID0+IHtcclxuICB0cnkge1xyXG4gICAgLy8gVHJ5IHRvIGNhbGwgdGhlIGZ1bmN0aW9uXHJcbiAgICByZXR1cm4gYXdhaXQgZm4oLi4uYXJncyk7XHJcbiAgfSBjYXRjaCAoZXJyb3IpIHtcclxuICAgIC8vIENhbGN1bGF0ZSBkZWxheSBpbiBtc1xyXG4gICAgY29uc3QgZGVsYXlJbk1zID0gMiAqKiBhdHRlbXB0ICogMTAwMDtcclxuXHJcbiAgICAvLyBJZiB0aGUgYXR0ZW1wdCBleGNlZWRzIHRoZSBtYXhpbXVtIGF0dGVtcHRzIG9mIHJlYXBlYXQsIHRocm93IGFuIGVycm9yXHJcbiAgICBpZiAoKythdHRlbXB0ID49IE1BWF9CQUNLT0ZGX0FUVEVNUFRTKSB7XHJcbiAgICAgIHRocm93IGVycm9yO1xyXG4gICAgfVxyXG5cclxuICAgIC8vIFdhaXQgZ2l2ZW4gYW1vdW50IG9mIHRpbWVcclxuICAgIGF3YWl0IG5ldyBQcm9taXNlKChyZXNwb25zZSkgPT4gc2V0VGltZW91dChyZXNwb25zZSwgZGVsYXlJbk1zKSk7XHJcbiAgICBsb2coXHJcbiAgICAgIDMsXHJcbiAgICAgIGBbcG9vbF0gV2FpdGVkICR7ZGVsYXlJbk1zfW1zIHVudGlsIG5leHQgY2FsbCBmb3IgdGhlIHJlc291cmNlIGlkOiAke2FyZ3NbMF19LmBcclxuICAgICk7XHJcblxyXG4gICAgLy8gVHJ5IGFnYWluXHJcbiAgICByZXR1cm4gZXhwQmFja29mZihmbiwgYXR0ZW1wdCwgLi4uYXJncyk7XHJcbiAgfVxyXG59O1xyXG5cclxuLyoqXHJcbiAqIEZpeGVzIHRoZSBleHBvcnQgdHlwZSBiYXNlZCBvbiBNSU1FIHR5cGVzIGFuZCBmaWxlIGV4dGVuc2lvbnMuXHJcbiAqXHJcbiAqIEBwYXJhbSB7c3RyaW5nfSB0eXBlIC0gVGhlIG9yaWdpbmFsIGV4cG9ydCB0eXBlLlxyXG4gKiBAcGFyYW0ge3N0cmluZ30gb3V0ZmlsZSAtIFRoZSBmaWxlIHBhdGggb3IgbmFtZS5cclxuICpcclxuICogQHJldHVybnMge3N0cmluZ30gLSBUaGUgY29ycmVjdGVkIGV4cG9ydCB0eXBlLlxyXG4gKi9cclxuZXhwb3J0IGNvbnN0IGZpeFR5cGUgPSAodHlwZSwgb3V0ZmlsZSkgPT4ge1xyXG4gIC8vIE1JTUUgdHlwZXNcclxuICBjb25zdCBtaW1lVHlwZXMgPSB7XHJcbiAgICAnaW1hZ2UvcG5nJzogJ3BuZycsXHJcbiAgICAnaW1hZ2UvanBlZyc6ICdqcGVnJyxcclxuICAgICdhcHBsaWNhdGlvbi9wZGYnOiAncGRmJyxcclxuICAgICdpbWFnZS9zdmcreG1sJzogJ3N2ZydcclxuICB9O1xyXG5cclxuICAvLyBGb3JtYXRzXHJcbiAgY29uc3QgZm9ybWF0cyA9IFsncG5nJywgJ2pwZWcnLCAncGRmJywgJ3N2ZyddO1xyXG5cclxuICAvLyBDaGVjayBpZiB0eXBlIGFuZCBvdXRmaWxlJ3MgZXh0ZW5zaW9ucyBhcmUgdGhlIHNhbWVcclxuICBpZiAob3V0ZmlsZSkge1xyXG4gICAgY29uc3Qgb3V0VHlwZSA9IG91dGZpbGUuc3BsaXQoJy4nKS5wb3AoKTtcclxuXHJcbiAgICBpZiAob3V0VHlwZSA9PT0gJ2pwZycpIHtcclxuICAgICAgdHlwZSA9ICdqcGVnJztcclxuICAgIH0gZWxzZSBpZiAoZm9ybWF0cy5pbmNsdWRlcyhvdXRUeXBlKSAmJiB0eXBlICE9PSBvdXRUeXBlKSB7XHJcbiAgICAgIHR5cGUgPSBvdXRUeXBlO1xyXG4gICAgfVxyXG4gIH1cclxuXHJcbiAgLy8gUmV0dXJuIGEgY29ycmVjdCB0eXBlXHJcbiAgcmV0dXJuIG1pbWVUeXBlc1t0eXBlXSB8fCBmb3JtYXRzLmZpbmQoKHQpID0+IHQgPT09IHR5cGUpIHx8ICdwbmcnO1xyXG59O1xyXG5cclxuLyoqXHJcbiAqIEhhbmRsZXMgYW5kIHZhbGlkYXRlcyByZXNvdXJjZXMgZm9yIGV4cG9ydC5cclxuICpcclxuICogQHBhcmFtIHtPYmplY3R8c3RyaW5nfSByZXNvdXJjZXMgLSBUaGUgcmVzb3VyY2VzIHRvIGJlIGhhbmRsZWQuIENhbiBiZSBlaXRoZXJcclxuICogYSBKU09OIG9iamVjdCwgc3RyaW5naWZpZWQgSlNPTiBvciBhIHBhdGggdG8gYSBKU09OIGZpbGUuXHJcbiAqIEBwYXJhbSB7Ym9vbGVhbn0gYWxsb3dGaWxlUmVzb3VyY2VzIC0gV2hldGhlciB0byBhbGxvdyBsb2FkaW5nIHJlc291cmNlcyBmcm9tXHJcbiAqIGZpbGVzLlxyXG4gKlxyXG4gKiBAcmV0dXJucyB7T2JqZWN0fHVuZGVmaW5lZH0gLSBUaGUgaGFuZGxlZCByZXNvdXJjZXMgb3IgdW5kZWZpbmVkIGlmIG5vIHZhbGlkXHJcbiAqIHJlc291cmNlcyBhcmUgZm91bmQuXHJcbiAqL1xyXG5leHBvcnQgY29uc3QgaGFuZGxlUmVzb3VyY2VzID0gKHJlc291cmNlcyA9IGZhbHNlLCBhbGxvd0ZpbGVSZXNvdXJjZXMpID0+IHtcclxuICBjb25zdCBhbGxvd2VkUHJvcHMgPSBbJ2pzJywgJ2NzcycsICdmaWxlcyddO1xyXG5cclxuICBsZXQgaGFuZGxlZFJlc291cmNlcyA9IHJlc291cmNlcztcclxuICBsZXQgY29ycmVjdFJlc291cmNlcyA9IGZhbHNlO1xyXG5cclxuICAvLyBUcnkgdG8gbG9hZCByZXNvdXJjZXMgZnJvbSBhIGZpbGVcclxuICBpZiAoYWxsb3dGaWxlUmVzb3VyY2VzICYmIHJlc291cmNlcy5lbmRzV2l0aCgnLmpzb24nKSkge1xyXG4gICAgdHJ5IHtcclxuICAgICAgaGFuZGxlZFJlc291cmNlcyA9IGlzQ29ycmVjdEpTT04ocmVhZEZpbGVTeW5jKHJlc291cmNlcywgJ3V0ZjgnKSk7XHJcbiAgICB9IGNhdGNoIChlcnJvcikge1xyXG4gICAgICByZXR1cm4gbG9nV2l0aFN0YWNrKDIsIGVycm9yLCBgW2NsaV0gTm8gcmVzb3VyY2VzIGZvdW5kLmApO1xyXG4gICAgfVxyXG4gIH0gZWxzZSB7XHJcbiAgICAvLyBUcnkgdG8gZ2V0IEpTT05cclxuICAgIGhhbmRsZWRSZXNvdXJjZXMgPSBpc0NvcnJlY3RKU09OKHJlc291cmNlcyk7XHJcblxyXG4gICAgLy8gR2V0IHJpZCBvZiB0aGUgZmlsZXMgc2VjdGlvblxyXG4gICAgaWYgKGhhbmRsZWRSZXNvdXJjZXMgJiYgIWFsbG93RmlsZVJlc291cmNlcykge1xyXG4gICAgICBkZWxldGUgaGFuZGxlZFJlc291cmNlcy5maWxlcztcclxuICAgIH1cclxuICB9XHJcblxyXG4gIC8vIEZpbHRlciBmcm9tIHVubmVjZXNzYXJ5IHByb3BlcnRpZXNcclxuICBmb3IgKGNvbnN0IHByb3BOYW1lIGluIGhhbmRsZWRSZXNvdXJjZXMpIHtcclxuICAgIGlmICghYWxsb3dlZFByb3BzLmluY2x1ZGVzKHByb3BOYW1lKSkge1xyXG4gICAgICBkZWxldGUgaGFuZGxlZFJlc291cmNlc1twcm9wTmFtZV07XHJcbiAgICB9IGVsc2UgaWYgKCFjb3JyZWN0UmVzb3VyY2VzKSB7XHJcbiAgICAgIGNvcnJlY3RSZXNvdXJjZXMgPSB0cnVlO1xyXG4gICAgfVxyXG4gIH1cclxuXHJcbiAgLy8gQ2hlY2sgaWYgYXQgbGVhc3Qgb25lIG9mIGFsbG93ZWQgcHJvcGVydGllcyBpcyBwcmVzZW50XHJcbiAgaWYgKCFjb3JyZWN0UmVzb3VyY2VzKSB7XHJcbiAgICByZXR1cm4gbG9nKDMsIGBbY2xpXSBObyByZXNvdXJjZXMgZm91bmQuYCk7XHJcbiAgfVxyXG5cclxuICAvLyBIYW5kbGUgZmlsZXMgc2VjdGlvblxyXG4gIGlmIChoYW5kbGVkUmVzb3VyY2VzLmZpbGVzKSB7XHJcbiAgICBoYW5kbGVkUmVzb3VyY2VzLmZpbGVzID0gaGFuZGxlZFJlc291cmNlcy5maWxlcy5tYXAoKGl0ZW0pID0+IGl0ZW0udHJpbSgpKTtcclxuICAgIGlmICghaGFuZGxlZFJlc291cmNlcy5maWxlcyB8fCBoYW5kbGVkUmVzb3VyY2VzLmZpbGVzLmxlbmd0aCA8PSAwKSB7XHJcbiAgICAgIGRlbGV0ZSBoYW5kbGVkUmVzb3VyY2VzLmZpbGVzO1xyXG4gICAgfVxyXG4gIH1cclxuXHJcbiAgLy8gUmV0dXJuIHJlc291cmNlc1xyXG4gIHJldHVybiBoYW5kbGVkUmVzb3VyY2VzO1xyXG59O1xyXG5cclxuLyoqXHJcbiAqIFZhbGlkYXRlcyBhbmQgcGFyc2VzIEpTT04gZGF0YS4gQ2hlY2tzIGlmIHByb3ZpZGVkIGRhdGEgaXMgb3IgY2FuXHJcbiAqIGJlIGEgY29ycmVjdCBKU09OLiBJZiBhIHByaW1pdGl2ZSBpcyBwcm92aWRlZCwgaXQgaXMgc3RyaW5naWZpZWQgYW5kIHJldHVybmVkLlxyXG4gKlxyXG4gKiBAcGFyYW0ge09iamVjdHxzdHJpbmd9IGRhdGEgLSBUaGUgSlNPTiBkYXRhIHRvIGJlIHZhbGlkYXRlZCBhbmQgcGFyc2VkLlxyXG4gKiBAcGFyYW0ge2Jvb2xlYW59IHRvU3RyaW5nIC0gV2hldGhlciB0byByZXR1cm4gYSBzdHJpbmdpZmllZCByZXByZXNlbnRhdGlvblxyXG4gKiBvZiB0aGUgcGFyc2VkIEpTT04uXHJcbiAqXHJcbiAqIEByZXR1cm5zIHtPYmplY3R8c3RyaW5nfGJvb2xlYW59IC0gVGhlIHBhcnNlZCBKU09OIG9iamVjdCwgc3RyaW5naWZpZWQgSlNPTixcclxuICogb3IgZmFsc2UgaWYgdmFsaWRhdGlvbiBmYWlscy5cclxuICovXHJcbmV4cG9ydCBmdW5jdGlvbiBpc0NvcnJlY3RKU09OKGRhdGEsIHRvU3RyaW5nKSB7XHJcbiAgdHJ5IHtcclxuICAgIC8vIEdldCB0aGUgc3RyaW5nIHJlcHJlc2VudGF0aW9uIGlmIG5vdCBhbHJlYWR5IGJlZm9yZSBwYXJzaW5nXHJcbiAgICBjb25zdCBwYXJzZWREYXRhID0gSlNPTi5wYXJzZShcclxuICAgICAgdHlwZW9mIGRhdGEgIT09ICdzdHJpbmcnID8gSlNPTi5zdHJpbmdpZnkoZGF0YSkgOiBkYXRhXHJcbiAgICApO1xyXG5cclxuICAgIC8vIFJldHVybiBhIHN0cmluZ2lmaWVkIHJlcHJlc2VudGF0aW9uIG9mIGEgSlNPTiBpZiByZXF1aXJlZFxyXG4gICAgaWYgKHR5cGVvZiBwYXJzZWREYXRhICE9PSAnc3RyaW5nJyAmJiB0b1N0cmluZykge1xyXG4gICAgICByZXR1cm4gSlNPTi5zdHJpbmdpZnkocGFyc2VkRGF0YSk7XHJcbiAgICB9XHJcblxyXG4gICAgLy8gUmV0dXJuIGEgSlNPTlxyXG4gICAgcmV0dXJuIHBhcnNlZERhdGE7XHJcbiAgfSBjYXRjaCB7XHJcbiAgICByZXR1cm4gZmFsc2U7XHJcbiAgfVxyXG59XHJcblxyXG4vKipcclxuICogQ2hlY2tzIGlmIHRoZSBnaXZlbiBpdGVtIGlzIGFuIG9iamVjdC5cclxuICpcclxuICogQHBhcmFtIHthbnl9IGl0ZW0gLSBUaGUgaXRlbSB0byBiZSBjaGVja2VkLlxyXG4gKlxyXG4gKiBAcmV0dXJucyB7Ym9vbGVhbn0gLSBUcnVlIGlmIHRoZSBpdGVtIGlzIGFuIG9iamVjdCwgZmFsc2Ugb3RoZXJ3aXNlLlxyXG4gKi9cclxuZXhwb3J0IGNvbnN0IGlzT2JqZWN0ID0gKGl0ZW0pID0+XHJcbiAgdHlwZW9mIGl0ZW0gPT09ICdvYmplY3QnICYmICFBcnJheS5pc0FycmF5KGl0ZW0pICYmIGl0ZW0gIT09IG51bGw7XHJcblxyXG4vKipcclxuICogQ2hlY2tzIGlmIHRoZSBnaXZlbiBvYmplY3QgaXMgZW1wdHkuXHJcbiAqXHJcbiAqIEBwYXJhbSB7T2JqZWN0fSBpdGVtIC0gVGhlIG9iamVjdCB0byBiZSBjaGVja2VkLlxyXG4gKlxyXG4gKiBAcmV0dXJucyB7Ym9vbGVhbn0gLSBUcnVlIGlmIHRoZSBvYmplY3QgaXMgZW1wdHksIGZhbHNlIG90aGVyd2lzZS5cclxuICovXHJcbmV4cG9ydCBjb25zdCBpc09iamVjdEVtcHR5ID0gKGl0ZW0pID0+XHJcbiAgdHlwZW9mIGl0ZW0gPT09ICdvYmplY3QnICYmXHJcbiAgIUFycmF5LmlzQXJyYXkoaXRlbSkgJiZcclxuICBpdGVtICE9PSBudWxsICYmXHJcbiAgT2JqZWN0LmtleXMoaXRlbSkubGVuZ3RoID09PSAwO1xyXG5cclxuLyoqXHJcbiAqIENoZWNrcyBpZiBhIHByaXZhdGUgSVAgcmFuZ2UgVVJMIGlzIGZvdW5kIGluIHRoZSBnaXZlbiBzdHJpbmcuXHJcbiAqXHJcbiAqIEBwYXJhbSB7c3RyaW5nfSBpdGVtIC0gVGhlIHN0cmluZyB0byBiZSBjaGVja2VkIGZvciBhIHByaXZhdGUgSVAgcmFuZ2UgVVJMLlxyXG4gKlxyXG4gKiBAcmV0dXJucyB7Ym9vbGVhbn0gLSBUcnVlIGlmIGEgcHJpdmF0ZSBJUCByYW5nZSBVUkwgaXMgZm91bmQsIGZhbHNlXHJcbiAqIG90aGVyd2lzZS5cclxuICovXHJcbmV4cG9ydCBjb25zdCBpc1ByaXZhdGVSYW5nZVVybEZvdW5kID0gKGl0ZW0pID0+IHtcclxuICBjb25zdCByZWdleFBhdHRlcm5zID0gW1xyXG4gICAgL3hsaW5rOmhyZWY9XCIoPzpodHRwOlxcL1xcL3xodHRwczpcXC9cXC8pP2xvY2FsaG9zdFxcYi8sXHJcbiAgICAveGxpbms6aHJlZj1cIig/Omh0dHA6XFwvXFwvfGh0dHBzOlxcL1xcLyk/MTBcXC5cXGR7MSwzfVxcLlxcZHsxLDN9XFwuXFxkezEsM31cXGIvLFxyXG4gICAgL3hsaW5rOmhyZWY9XCIoPzpodHRwOlxcL1xcL3xodHRwczpcXC9cXC8pPzEyN1xcLlxcZHsxLDN9XFwuXFxkezEsM31cXC5cXGR7MSwzfVxcYi8sXHJcbiAgICAveGxpbms6aHJlZj1cIig/Omh0dHA6XFwvXFwvfGh0dHBzOlxcL1xcLyk/MTcyXFwuKDFbNi05XXwyWzAtOV18M1swLTFdKVxcLlxcZHsxLDN9XFwuXFxkezEsM31cXGIvLFxyXG4gICAgL3hsaW5rOmhyZWY9XCIoPzpodHRwOlxcL1xcL3xodHRwczpcXC9cXC8pPzE5MlxcLjE2OFxcLlxcZHsxLDN9XFwuXFxkezEsM31cXGIvXHJcbiAgXTtcclxuXHJcbiAgcmV0dXJuIHJlZ2V4UGF0dGVybnMuc29tZSgocGF0dGVybikgPT4gcGF0dGVybi50ZXN0KGl0ZW0pKTtcclxufTtcclxuXHJcbi8qKlxyXG4gKiBDcmVhdGVzIGEgZGVlcCBjb3B5IG9mIHRoZSBnaXZlbiBvYmplY3Qgb3IgYXJyYXkuXHJcbiAqXHJcbiAqIEBwYXJhbSB7T2JqZWN0fEFycmF5fSBvYmogLSBUaGUgb2JqZWN0IG9yIGFycmF5IHRvIGJlIGRlZXBseSBjb3BpZWQuXHJcbiAqXHJcbiAqIEByZXR1cm5zIHtPYmplY3R8QXJyYXl9IC0gVGhlIGRlZXAgY29weSBvZiB0aGUgcHJvdmlkZWQgb2JqZWN0IG9yIGFycmF5LlxyXG4gKi9cclxuZXhwb3J0IGNvbnN0IGRlZXBDb3B5ID0gKG9iaikgPT4ge1xyXG4gIGlmIChvYmogPT09IG51bGwgfHwgdHlwZW9mIG9iaiAhPT0gJ29iamVjdCcpIHtcclxuICAgIHJldHVybiBvYmo7XHJcbiAgfVxyXG5cclxuICBjb25zdCBjb3B5ID0gQXJyYXkuaXNBcnJheShvYmopID8gW10gOiB7fTtcclxuXHJcbiAgZm9yIChjb25zdCBrZXkgaW4gb2JqKSB7XHJcbiAgICBpZiAoT2JqZWN0LnByb3RvdHlwZS5oYXNPd25Qcm9wZXJ0eS5jYWxsKG9iaiwga2V5KSkge1xyXG4gICAgICBjb3B5W2tleV0gPSBkZWVwQ29weShvYmpba2V5XSk7XHJcbiAgICB9XHJcbiAgfVxyXG5cclxuICByZXR1cm4gY29weTtcclxufTtcclxuXHJcbi8qKlxyXG4gKiBDb252ZXJ0cyB0aGUgcHJvdmlkZWQgb3B0aW9ucyBvYmplY3QgdG8gYSBKU09OLWZvcm1hdHRlZCBzdHJpbmcgd2l0aCB0aGVcclxuICogb3B0aW9uIHRvIHByZXNlcnZlIGZ1bmN0aW9ucy5cclxuICpcclxuICogQHBhcmFtIHtPYmplY3R9IG9wdGlvbnMgLSBUaGUgb3B0aW9ucyBvYmplY3QgdG8gYmUgY29udmVydGVkIHRvIGEgc3RyaW5nLlxyXG4gKiBAcGFyYW0ge2Jvb2xlYW59IGFsbG93RnVuY3Rpb25zIC0gSWYgc2V0IHRvIHRydWUsIGZ1bmN0aW9ucyBhcmUgcHJlc2VydmVkXHJcbiAqIGluIHRoZSBvdXRwdXQuXHJcbiAqXHJcbiAqIEByZXR1cm5zIHtzdHJpbmd9IC0gVGhlIEpTT04tZm9ybWF0dGVkIHN0cmluZyByZXByZXNlbnRpbmcgdGhlIG9wdGlvbnMuXHJcbiAqL1xyXG5leHBvcnQgY29uc3Qgb3B0aW9uc1N0cmluZ2lmeSA9IChvcHRpb25zLCBhbGxvd0Z1bmN0aW9ucykgPT4ge1xyXG4gIGNvbnN0IHJlcGxhY2VyQ2FsbGJhY2sgPSAobmFtZSwgdmFsdWUpID0+IHtcclxuICAgIGlmICh0eXBlb2YgdmFsdWUgPT09ICdzdHJpbmcnKSB7XHJcbiAgICAgIHZhbHVlID0gdmFsdWUudHJpbSgpO1xyXG5cclxuICAgICAgLy8gSWYgYWxsb3dGdW5jdGlvbnMgaXMgc2V0IHRvIHRydWUsIHByZXNlcnZlIGZ1bmN0aW9uc1xyXG4gICAgICBpZiAoXHJcbiAgICAgICAgKHZhbHVlLnN0YXJ0c1dpdGgoJ2Z1bmN0aW9uKCcpIHx8IHZhbHVlLnN0YXJ0c1dpdGgoJ2Z1bmN0aW9uICgnKSkgJiZcclxuICAgICAgICB2YWx1ZS5lbmRzV2l0aCgnfScpXHJcbiAgICAgICkge1xyXG4gICAgICAgIHZhbHVlID0gYWxsb3dGdW5jdGlvbnNcclxuICAgICAgICAgID8gYEVYUF9GVU4keyh2YWx1ZSArICcnKS5yZXBsYWNlQWxsKC9cXG58XFx0fFxcci9nLCAnICcpfUVYUF9GVU5gXHJcbiAgICAgICAgICA6IHVuZGVmaW5lZDtcclxuICAgICAgfVxyXG4gICAgfVxyXG5cclxuICAgIHJldHVybiB0eXBlb2YgdmFsdWUgPT09ICdmdW5jdGlvbidcclxuICAgICAgPyBgRVhQX0ZVTiR7KHZhbHVlICsgJycpLnJlcGxhY2VBbGwoL1xcbnxcXHR8XFxyL2csICcgJyl9RVhQX0ZVTmBcclxuICAgICAgOiB2YWx1ZTtcclxuICB9O1xyXG5cclxuICAvLyBTdHJpbmdpZnkgb3B0aW9ucyBhbmQgaWYgcmVxdWlyZWQsIHJlcGxhY2Ugc3BlY2lhbCBmdW5jdGlvbnMgbWFya3NcclxuICByZXR1cm4gSlNPTi5zdHJpbmdpZnkob3B0aW9ucywgcmVwbGFjZXJDYWxsYmFjaykucmVwbGFjZUFsbChcclxuICAgIC9cIkVYUF9GVU58RVhQX0ZVTlwiL2csXHJcbiAgICAnJ1xyXG4gICk7XHJcbn07XHJcblxyXG4vKipcclxuICogUHJpbnRzIHRoZSBIaWdoY2hhcnRzIEV4cG9ydCBTZXJ2ZXIgbG9nbyBhbmQgdmVyc2lvbiBpbmZvcm1hdGlvbi5cclxuICpcclxuICogQHBhcmFtIHtib29sZWFufSBub0xvZ28gLSBJZiB0cnVlLCBvbmx5IHByaW50cyB2ZXJzaW9uIGluZm9ybWF0aW9uIHdpdGhvdXRcclxuICogdGhlIGxvZ28uXHJcbiAqL1xyXG5leHBvcnQgY29uc3QgcHJpbnRMb2dvID0gKG5vTG9nbykgPT4ge1xyXG4gIC8vIEdldCBwYWNrYWdlIHZlcnNpb24gZWl0aGVyIGZyb20gZW52IG9yIGZyb20gcGFja2FnZS5qc29uXHJcbiAgY29uc3QgcGFja2FnZVZlcnNpb24gPSBKU09OLnBhcnNlKFxyXG4gICAgcmVhZEZpbGVTeW5jKGpvaW4oX19kaXJuYW1lLCAncGFja2FnZS5qc29uJykpXHJcbiAgKS52ZXJzaW9uO1xyXG5cclxuICAvLyBQcmludCB0ZXh0IG9ubHlcclxuICBpZiAobm9Mb2dvKSB7XHJcbiAgICBjb25zb2xlLmxvZyhgU3RhcnRpbmcgSGlnaGNoYXJ0cyBFeHBvcnQgU2VydmVyIHYke3BhY2thZ2VWZXJzaW9ufS4uLmApO1xyXG4gICAgcmV0dXJuO1xyXG4gIH1cclxuXHJcbiAgLy8gUHJpbnQgdGhlIGxvZ29cclxuICBjb25zb2xlLmxvZyhcclxuICAgIHJlYWRGaWxlU3luYyhfX2Rpcm5hbWUgKyAnL21zZy9zdGFydHVwLm1zZycpLnRvU3RyaW5nKCkuYm9sZC55ZWxsb3csXHJcbiAgICBgdiR7cGFja2FnZVZlcnNpb259XFxuYC5ib2xkXHJcbiAgKTtcclxufTtcclxuXHJcbi8qKlxyXG4gKiBQcmludHMgdGhlIHVzYWdlIGluZm9ybWF0aW9uIGZvciBDTEkgYXJndW1lbnRzLiBJZiByZXF1aXJlZCwgaXQgY2FuIGxpc3RcclxuICogcHJvcGVydGllcyByZWN1cnNpdmVseVxyXG4gKi9cclxuZXhwb3J0IGZ1bmN0aW9uIHByaW50VXNhZ2UoKSB7XHJcbiAgY29uc3QgcGFkID0gNDg7XHJcbiAgY29uc3QgcmVhZG1lID0gJ2h0dHBzOi8vZ2l0aHViLmNvbS9oaWdoY2hhcnRzL25vZGUtZXhwb3J0LXNlcnZlciNyZWFkbWUnO1xyXG5cclxuICAvLyBEaXNwbGF5IHJlYWRtZSBpbmZvcm1hdGlvblxyXG4gIGNvbnNvbGUubG9nKFxyXG4gICAgJ1xcblVzYWdlIG9mIENMSSBhcmd1bWVudHM6Jy5ib2xkLFxyXG4gICAgJ1xcbi0tLS0tLScsXHJcbiAgICBgXFxuRm9yIG1vcmUgZGV0YWlsZWQgaW5mb3JtYXRpb24sIHZpc2l0IHRoZSByZWFkbWUgYXQ6ICR7cmVhZG1lLmJvbGQueWVsbG93fS5gXHJcbiAgKTtcclxuXHJcbiAgY29uc3QgY3ljbGVDYXRlZ29yaWVzID0gKG9wdGlvbnMpID0+IHtcclxuICAgIGZvciAoY29uc3QgW25hbWUsIG9wdGlvbl0gb2YgT2JqZWN0LmVudHJpZXMob3B0aW9ucykpIHtcclxuICAgICAgLy8gSWYgY2F0ZWdvcnkgaGFzIG1vcmUgbGV2ZWxzLCBnbyBmdXJ0aGVyXHJcbiAgICAgIGlmICghT2JqZWN0LnByb3RvdHlwZS5oYXNPd25Qcm9wZXJ0eS5jYWxsKG9wdGlvbiwgJ3ZhbHVlJykpIHtcclxuICAgICAgICBjeWNsZUNhdGVnb3JpZXMob3B0aW9uKTtcclxuICAgICAgfSBlbHNlIHtcclxuICAgICAgICBsZXQgZGVzY05hbWUgPSBgICAtLSR7b3B0aW9uLmNsaU5hbWUgfHwgbmFtZX0gJHtcclxuICAgICAgICAgICgnPCcgKyBvcHRpb24udHlwZSArICc+JykuZ3JlZW5cclxuICAgICAgICB9IGA7XHJcbiAgICAgICAgaWYgKGRlc2NOYW1lLmxlbmd0aCA8IHBhZCkge1xyXG4gICAgICAgICAgZm9yIChsZXQgaSA9IGRlc2NOYW1lLmxlbmd0aDsgaSA8IHBhZDsgaSsrKSB7XHJcbiAgICAgICAgICAgIGRlc2NOYW1lICs9ICcuJztcclxuICAgICAgICAgIH1cclxuICAgICAgICB9XHJcblxyXG4gICAgICAgIC8vIERpc3BsYXkgY29ycmVjdGx5IGFsaWduZWQgbWVzc2FnZXNcclxuICAgICAgICBjb25zb2xlLmxvZyhcclxuICAgICAgICAgIGRlc2NOYW1lLFxyXG4gICAgICAgICAgb3B0aW9uLmRlc2NyaXB0aW9uLFxyXG4gICAgICAgICAgYFtEZWZhdWx0OiAke29wdGlvbi52YWx1ZS50b1N0cmluZygpLmJvbGR9XWAuYmx1ZVxyXG4gICAgICAgICk7XHJcbiAgICAgIH1cclxuICAgIH1cclxuICB9O1xyXG5cclxuICAvLyBDeWNsZSB0aHJvdWdoIG9wdGlvbnMgb2YgZWFjaCBjYXRlZ29yaWVzIGFuZCBkaXNwbGF5IHRoZSB1c2FnZSBpbmZvXHJcbiAgT2JqZWN0LmtleXMoZGVmYXVsdENvbmZpZykuZm9yRWFjaCgoY2F0ZWdvcnkpID0+IHtcclxuICAgIC8vIE9ubHkgcHVwcGV0ZWVyIGFuZCBoaWdoY2hhcnRzIGNhdGVnb3JpZXMgY2Fubm90IGJlIGNvbmZpZ3VyZWQgdGhyb3VnaCBDTElcclxuICAgIGlmICghWydwdXBwZXRlZXInLCAnaGlnaGNoYXJ0cyddLmluY2x1ZGVzKGNhdGVnb3J5KSkge1xyXG4gICAgICBjb25zb2xlLmxvZyhgXFxuJHtjYXRlZ29yeS50b1VwcGVyQ2FzZSgpfWAucmVkKTtcclxuICAgICAgY3ljbGVDYXRlZ29yaWVzKGRlZmF1bHRDb25maWdbY2F0ZWdvcnldKTtcclxuICAgIH1cclxuICB9KTtcclxuICBjb25zb2xlLmxvZygnXFxuJyk7XHJcbn1cclxuXHJcbi8qKlxyXG4gKiBSb3VuZHMgYSBudW1iZXIgdG8gdGhlIHNwZWNpZmllZCBwcmVjaXNpb24uXHJcbiAqXHJcbiAqIEBwYXJhbSB7bnVtYmVyfSB2YWx1ZSAtIFRoZSBudW1iZXIgdG8gYmUgcm91bmRlZC5cclxuICogQHBhcmFtIHtudW1iZXJ9IHByZWNpc2lvbiAtIFRoZSBudW1iZXIgb2YgZGVjaW1hbCBwbGFjZXMgdG8gcm91bmQgdG8uXHJcbiAqXHJcbiAqIEByZXR1cm5zIHtudW1iZXJ9IC0gVGhlIHJvdW5kZWQgbnVtYmVyLlxyXG4gKi9cclxuZXhwb3J0IGNvbnN0IHJvdW5kTnVtYmVyID0gKHZhbHVlLCBwcmVjaXNpb24gPSAxKSA9PiB7XHJcbiAgY29uc3QgbXVsdGlwbGllciA9IE1hdGgucG93KDEwLCBwcmVjaXNpb24gfHwgMCk7XHJcbiAgcmV0dXJuIE1hdGgucm91bmQoK3ZhbHVlICogbXVsdGlwbGllcikgLyBtdWx0aXBsaWVyO1xyXG59O1xyXG5cclxuLyoqXHJcbiAqIENvbnZlcnRzIGEgdmFsdWUgdG8gYSBib29sZWFuLlxyXG4gKlxyXG4gKiBAcGFyYW0ge2FueX0gaXRlbSAtIFRoZSB2YWx1ZSB0byBiZSBjb252ZXJ0ZWQgdG8gYSBib29sZWFuLlxyXG4gKlxyXG4gKiBAcmV0dXJucyB7Ym9vbGVhbn0gLSBUaGUgYm9vbGVhbiByZXByZXNlbnRhdGlvbiBvZiB0aGUgaW5wdXQgdmFsdWUuXHJcbiAqL1xyXG5leHBvcnQgY29uc3QgdG9Cb29sZWFuID0gKGl0ZW0pID0+XHJcbiAgWydmYWxzZScsICd1bmRlZmluZWQnLCAnbnVsbCcsICdOYU4nLCAnMCcsICcnXS5pbmNsdWRlcyhpdGVtKVxyXG4gICAgPyBmYWxzZVxyXG4gICAgOiAhIWl0ZW07XHJcblxyXG4vKipcclxuICogV3JhcHMgY3VzdG9tIGNvZGUgdG8gZXhlY3V0ZSBpdCBzYWZlbHkuXHJcbiAqXHJcbiAqIEBwYXJhbSB7c3RyaW5nfSBjdXN0b21Db2RlIC0gVGhlIGN1c3RvbSBjb2RlIHRvIGJlIHdyYXBwZWQuXHJcbiAqIEBwYXJhbSB7Ym9vbGVhbn0gYWxsb3dGaWxlUmVzb3VyY2VzIC0gRmxhZyB0byBhbGxvdyBsb2FkaW5nIGNvZGUgZnJvbSBhIGZpbGUuXHJcbiAqXHJcbiAqIEByZXR1cm5zIHtzdHJpbmd8Ym9vbGVhbn0gLSBUaGUgd3JhcHBlZCBjdXN0b20gY29kZSBvciBmYWxzZSBpZiB3cmFwcGluZ1xyXG4gKiBmYWlscy5cclxuICovXHJcbmV4cG9ydCBjb25zdCB3cmFwQXJvdW5kID0gKGN1c3RvbUNvZGUsIGFsbG93RmlsZVJlc291cmNlcykgPT4ge1xyXG4gIGlmIChjdXN0b21Db2RlICYmIHR5cGVvZiBjdXN0b21Db2RlID09PSAnc3RyaW5nJykge1xyXG4gICAgY3VzdG9tQ29kZSA9IGN1c3RvbUNvZGUudHJpbSgpO1xyXG5cclxuICAgIGlmIChjdXN0b21Db2RlLmVuZHNXaXRoKCcuanMnKSkge1xyXG4gICAgICByZXR1cm4gYWxsb3dGaWxlUmVzb3VyY2VzXHJcbiAgICAgICAgPyB3cmFwQXJvdW5kKHJlYWRGaWxlU3luYyhjdXN0b21Db2RlLCAndXRmOCcpKVxyXG4gICAgICAgIDogZmFsc2U7XHJcbiAgICB9IGVsc2UgaWYgKFxyXG4gICAgICBjdXN0b21Db2RlLnN0YXJ0c1dpdGgoJ2Z1bmN0aW9uKCknKSB8fFxyXG4gICAgICBjdXN0b21Db2RlLnN0YXJ0c1dpdGgoJ2Z1bmN0aW9uICgpJykgfHxcclxuICAgICAgY3VzdG9tQ29kZS5zdGFydHNXaXRoKCcoKT0+JykgfHxcclxuICAgICAgY3VzdG9tQ29kZS5zdGFydHNXaXRoKCcoKSA9PicpXHJcbiAgICApIHtcclxuICAgICAgcmV0dXJuIGAoJHtjdXN0b21Db2RlfSkoKWA7XHJcbiAgICB9XHJcbiAgICByZXR1cm4gY3VzdG9tQ29kZS5yZXBsYWNlKC87JC8sICcnKTtcclxuICB9XHJcbn07XHJcblxyXG4vKipcclxuICogVXRpbGl0eSB0byBtZWFzdXJlIGVsYXBzZWQgdGltZSB1c2luZyB0aGUgTm9kZS5qcyBwcm9jZXNzLmhydGltZSgpIG1ldGhvZC5cclxuICpcclxuICogQHJldHVybnMge2Z1bmN0aW9uKCk6IG51bWJlcn0gLSBBIGZ1bmN0aW9uIHRvIGNhbGN1bGF0ZSB0aGUgZWxhcHNlZCB0aW1lXHJcbiAqIGluIG1pbGxpc2Vjb25kcy5cclxuICovXHJcbmV4cG9ydCBjb25zdCBtZWFzdXJlVGltZSA9ICgpID0+IHtcclxuICBjb25zdCBzdGFydCA9IHByb2Nlc3MuaHJ0aW1lLmJpZ2ludCgpO1xyXG4gIHJldHVybiAoKSA9PiBOdW1iZXIocHJvY2Vzcy5ocnRpbWUuYmlnaW50KCkgLSBzdGFydCkgLyAxMDAwMDAwO1xyXG59O1xyXG5cclxuZXhwb3J0IGRlZmF1bHQge1xyXG4gIF9fZGlybmFtZSxcclxuICBjbGVhclRleHQsXHJcbiAgZXhwQmFja29mZixcclxuICBmaXhUeXBlLFxyXG4gIGhhbmRsZVJlc291cmNlcyxcclxuICBpc0NvcnJlY3RKU09OLFxyXG4gIGlzT2JqZWN0LFxyXG4gIGlzT2JqZWN0RW1wdHksXHJcbiAgaXNQcml2YXRlUmFuZ2VVcmxGb3VuZCxcclxuICBvcHRpb25zU3RyaW5naWZ5LFxyXG4gIHByaW50TG9nbyxcclxuICBwcmludFVzYWdlLFxyXG4gIHJvdW5kTnVtYmVyLFxyXG4gIHRvQm9vbGVhbixcclxuICB3cmFwQXJvdW5kLFxyXG4gIG1lYXN1cmVUaW1lXHJcbn07XHJcbiIsIi8qKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqXHJcblxyXG5IaWdoY2hhcnRzIEV4cG9ydCBTZXJ2ZXJcclxuXHJcbkNvcHlyaWdodCAoYykgMjAxNi0yMDI0LCBIaWdoc29mdFxyXG5cclxuTGljZW5jZWQgdW5kZXIgdGhlIE1JVCBsaWNlbmNlLlxyXG5cclxuQWRkaXRpb25hbGx5IGEgdmFsaWQgSGlnaGNoYXJ0cyBsaWNlbnNlIGlzIHJlcXVpcmVkIGZvciB1c2UuXHJcblxyXG5TZWUgTElDRU5TRSBmaWxlIGluIHJvb3QgZm9yIGRldGFpbHMuXHJcblxyXG4qKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqL1xyXG5cclxuaW1wb3J0IHsgZXhpc3RzU3luYywgcmVhZEZpbGVTeW5jLCBwcm9taXNlcyBhcyBmc1Byb21pc2VzIH0gZnJvbSAnZnMnO1xyXG5cclxuaW1wb3J0IHByb21wdHMgZnJvbSAncHJvbXB0cyc7XHJcblxyXG5pbXBvcnQge1xyXG4gIGFic29sdXRlUHJvcHMsXHJcbiAgZGVmYXVsdENvbmZpZyxcclxuICBuZXN0ZWRBcmdzLFxyXG4gIHByb21wdHNDb25maWdcclxufSBmcm9tICcuL3NjaGVtYXMvY29uZmlnLmpzJztcclxuaW1wb3J0IHsgZW52cyB9IGZyb20gJy4vZW52cy5qcyc7XHJcbmltcG9ydCB7IGxvZywgbG9nV2l0aFN0YWNrIH0gZnJvbSAnLi9sb2dnZXIuanMnO1xyXG5pbXBvcnQgeyBkZWVwQ29weSwgaXNPYmplY3QsIHByaW50VXNhZ2UsIHRvQm9vbGVhbiB9IGZyb20gJy4vdXRpbHMuanMnO1xyXG5cclxubGV0IGdlbmVyYWxPcHRpb25zID0ge307XHJcblxyXG4vKipcclxuICogUmV0cmlldmVzIGFuZCByZXR1cm5zIHRoZSBnZW5lcmFsIG9wdGlvbnMgZm9yIHRoZSBleHBvcnQgcHJvY2Vzcy5cclxuICpcclxuICogQHJldHVybnMge09iamVjdH0gVGhlIGdlbmVyYWwgb3B0aW9ucyBvYmplY3QuXHJcbiAqL1xyXG5leHBvcnQgY29uc3QgZ2V0T3B0aW9ucyA9ICgpID0+IGdlbmVyYWxPcHRpb25zO1xyXG5cclxuLyoqXHJcbiAqIEluaXRpYWxpemVzIGFuZCBzZXRzIHRoZSBnZW5lcmFsIG9wdGlvbnMgZm9yIHRoZSBzZXJ2ZXIgaW5zdGFjZSwga2VlcGluZ1xyXG4gKiB0aGUgcHJpbmNpcGxlIG9mIHRoZSBvcHRpb25zIGxvYWQgcHJpb3JpdHkuIEl0IGFjY2VwdHMgb3B0aW9uYWwgdXNlck9wdGlvbnNcclxuICogYW5kIGFyZ3MgZnJvbSB0aGUgQ0xJLlxyXG4gKlxyXG4gKiBAcGFyYW0ge09iamVjdH0gdXNlck9wdGlvbnMgLSBVc2VyLXByb3ZpZGVkIG9wdGlvbnMgZm9yIGN1c3RvbWl6YXRpb24uXHJcbiAqIEBwYXJhbSB7QXJyYXl9IGFyZ3MgLSBDb21tYW5kLWxpbmUgYXJndW1lbnRzIGZvciBhZGRpdGlvbmFsIGNvbmZpZ3VyYXRpb25cclxuICogKENMSSB1c2FnZSkuXHJcbiAqXHJcbiAqIEByZXR1cm5zIHtPYmplY3R9IFRoZSB1cGRhdGVkIGdlbmVyYWwgb3B0aW9ucyBvYmplY3QuXHJcbiAqL1xyXG5leHBvcnQgY29uc3Qgc2V0T3B0aW9ucyA9ICh1c2VyT3B0aW9ucywgYXJncykgPT4ge1xyXG4gIC8vIE9ubHkgZm9yIHRoZSBDTEkgdXNhZ2VcclxuICBpZiAoYXJncz8ubGVuZ3RoKSB7XHJcbiAgICAvLyBHZXQgdGhlIGFkZGl0aW9uYWwgb3B0aW9ucyBmcm9tIHRoZSBjdXN0b20gSlNPTiBmaWxlXHJcbiAgICBnZW5lcmFsT3B0aW9ucyA9IGxvYWRDb25maWdGaWxlKGFyZ3MpO1xyXG4gIH1cclxuXHJcbiAgLy8gVXBkYXRlIHRoZSBkZWZhdWx0IGNvbmZpZyB3aXRoIGEgY29ycmVjdCBvcHRpb24gdmFsdWVzXHJcbiAgdXBkYXRlRGVmYXVsdENvbmZpZyhkZWZhdWx0Q29uZmlnLCBnZW5lcmFsT3B0aW9ucyk7XHJcblxyXG4gIC8vIFNldCB2YWx1ZXMgZm9yIHNlcnZlcidzIG9wdGlvbnMgYW5kIHJldHVybnMgdGhlbVxyXG4gIGdlbmVyYWxPcHRpb25zID0gaW5pdE9wdGlvbnMoZGVmYXVsdENvbmZpZyk7XHJcblxyXG4gIC8vIEFwcGx5IHVzZXIgb3B0aW9ucyBpZiB0aGVyZSBhcmUgYW55XHJcbiAgaWYgKHVzZXJPcHRpb25zKSB7XHJcbiAgICAvLyBNZXJnZSB1c2VyIG9wdGlvbnNcclxuICAgIGdlbmVyYWxPcHRpb25zID0gbWVyZ2VDb25maWdPcHRpb25zKFxyXG4gICAgICBnZW5lcmFsT3B0aW9ucyxcclxuICAgICAgdXNlck9wdGlvbnMsXHJcbiAgICAgIGFic29sdXRlUHJvcHNcclxuICAgICk7XHJcbiAgfVxyXG5cclxuICAvLyBPbmx5IGZvciB0aGUgQ0xJIHVzYWdlXHJcbiAgaWYgKGFyZ3M/Lmxlbmd0aCkge1xyXG4gICAgLy8gUGFpciBwcm92aWRlZCBhcmd1bWVudHNcclxuICAgIGdlbmVyYWxPcHRpb25zID0gcGFpckFyZ3VtZW50VmFsdWUoZ2VuZXJhbE9wdGlvbnMsIGFyZ3MsIGRlZmF1bHRDb25maWcpO1xyXG4gIH1cclxuXHJcbiAgLy8gUmV0dXJuIGZpbmFsIGdlbmVyYWwgb3B0aW9uc1xyXG4gIHJldHVybiBnZW5lcmFsT3B0aW9ucztcclxufTtcclxuXHJcbi8qKlxyXG4gKiBBbGxvd3MgbWFudWFsIGNvbmZpZ3VyYXRpb24gYmFzZWQgb24gc3BlY2lmaWVkIHByb21wdHMgYW5kIHNhdmVzXHJcbiAqIHRoZSBjb25maWd1cmF0aW9uIHRvIGEgZmlsZS5cclxuICpcclxuICogQHBhcmFtIHtzdHJpbmd9IGNvbmZpZ0ZpbGVOYW1lIC0gVGhlIG5hbWUgb2YgdGhlIGNvbmZpZ3VyYXRpb24gZmlsZS5cclxuICpcclxuICogQHJldHVybnMge1Byb21pc2U8Ym9vbGVhbj59IEEgUHJvbWlzZSB0aGF0IHJlc29sdmVzIHRvIHRydWUgb25jZSB0aGUgbWFudWFsXHJcbiAqIGNvbmZpZ3VyYXRpb24gaXMgY29tcGxldGVkIGFuZCBzYXZlZC5cclxuICovXHJcbmV4cG9ydCBjb25zdCBtYW51YWxDb25maWcgPSBhc3luYyAoY29uZmlnRmlsZU5hbWUpID0+IHtcclxuICAvLyBQcmVwYXJlIGEgY29uZmlnIG9iamVjdFxyXG4gIGxldCBjb25maWdGaWxlID0ge307XHJcblxyXG4gIC8vIENoZWNrIGlmIHByb3ZpZGVkIGNvbmZpZyBmaWxlIGV4aXN0c1xyXG4gIGlmIChleGlzdHNTeW5jKGNvbmZpZ0ZpbGVOYW1lKSkge1xyXG4gICAgY29uZmlnRmlsZSA9IEpTT04ucGFyc2UocmVhZEZpbGVTeW5jKGNvbmZpZ0ZpbGVOYW1lLCAndXRmOCcpKTtcclxuICB9XHJcblxyXG4gIC8vIFF1ZXN0aW9uIGFib3V0IGEgY29uZmlndXJhdGlvbiBjYXRlZ29yeVxyXG4gIGNvbnN0IG9uU3VibWl0ID0gYXN5bmMgKHAsIGNhdGVnb3JpZXMpID0+IHtcclxuICAgIGxldCBxdWVzdGlvbnNDb3VudGVyID0gMDtcclxuICAgIGxldCBhbGxRdWVzdGlvbnMgPSBbXTtcclxuXHJcbiAgICAvLyBDcmVhdGUgYSBjb3JyZXNwb25kaW5nIHByb3BlcnR5IGluIHRoZSBtYW51YWxDb25maWcgb2JqZWN0XHJcbiAgICBmb3IgKGNvbnN0IHNlY3Rpb24gb2YgY2F0ZWdvcmllcykge1xyXG4gICAgICAvLyBNYXJrIGVhY2ggb3B0aW9uIHdpdGggYSBzZWN0aW9uXHJcbiAgICAgIHByb21wdHNDb25maWdbc2VjdGlvbl0gPSBwcm9tcHRzQ29uZmlnW3NlY3Rpb25dLm1hcCgob3B0aW9uKSA9PiAoe1xyXG4gICAgICAgIC4uLm9wdGlvbixcclxuICAgICAgICBzZWN0aW9uXHJcbiAgICAgIH0pKTtcclxuXHJcbiAgICAgIC8vIENvbGxlY3QgdGhlIHF1ZXN0aW9uc1xyXG4gICAgICBhbGxRdWVzdGlvbnMgPSBbLi4uYWxsUXVlc3Rpb25zLCAuLi5wcm9tcHRzQ29uZmlnW3NlY3Rpb25dXTtcclxuICAgIH1cclxuXHJcbiAgICBhd2FpdCBwcm9tcHRzKGFsbFF1ZXN0aW9ucywge1xyXG4gICAgICBvblN1Ym1pdDogYXN5bmMgKHByb21wdCwgYW5zd2VyKSA9PiB7XHJcbiAgICAgICAgLy8gR2V0IHRoZSBkZWZhdWx0IG1vZHVsZSBzY3JpcHRzXHJcbiAgICAgICAgaWYgKHByb21wdC5uYW1lID09PSAnbW9kdWxlU2NyaXB0cycpIHtcclxuICAgICAgICAgIGFuc3dlciA9IGFuc3dlci5sZW5ndGhcclxuICAgICAgICAgICAgPyBhbnN3ZXIubWFwKChtb2R1bGUpID0+IHByb21wdC5jaG9pY2VzW21vZHVsZV0pXHJcbiAgICAgICAgICAgIDogcHJvbXB0LmNob2ljZXM7XHJcblxyXG4gICAgICAgICAgY29uZmlnRmlsZVtwcm9tcHQuc2VjdGlvbl1bcHJvbXB0Lm5hbWVdID0gYW5zd2VyO1xyXG4gICAgICAgIH0gZWxzZSB7XHJcbiAgICAgICAgICBjb25maWdGaWxlW3Byb21wdC5zZWN0aW9uXSA9IHJlY3Vyc2l2ZVByb3BzKFxyXG4gICAgICAgICAgICBPYmplY3QuYXNzaWduKHt9LCBjb25maWdGaWxlW3Byb21wdC5zZWN0aW9uXSB8fCB7fSksXHJcbiAgICAgICAgICAgIHByb21wdC5uYW1lLnNwbGl0KCcuJyksXHJcbiAgICAgICAgICAgIHByb21wdC5jaG9pY2VzID8gcHJvbXB0LmNob2ljZXNbYW5zd2VyXSA6IGFuc3dlclxyXG4gICAgICAgICAgKTtcclxuICAgICAgICB9XHJcblxyXG4gICAgICAgIGlmICgrK3F1ZXN0aW9uc0NvdW50ZXIgPT09IGFsbFF1ZXN0aW9ucy5sZW5ndGgpIHtcclxuICAgICAgICAgIHRyeSB7XHJcbiAgICAgICAgICAgIGF3YWl0IGZzUHJvbWlzZXMud3JpdGVGaWxlKFxyXG4gICAgICAgICAgICAgIGNvbmZpZ0ZpbGVOYW1lLFxyXG4gICAgICAgICAgICAgIEpTT04uc3RyaW5naWZ5KGNvbmZpZ0ZpbGUsIG51bGwsIDIpLFxyXG4gICAgICAgICAgICAgICd1dGY4J1xyXG4gICAgICAgICAgICApO1xyXG4gICAgICAgICAgfSBjYXRjaCAoZXJyb3IpIHtcclxuICAgICAgICAgICAgbG9nV2l0aFN0YWNrKFxyXG4gICAgICAgICAgICAgIDEsXHJcbiAgICAgICAgICAgICAgZXJyb3IsXHJcbiAgICAgICAgICAgICAgYFtjb25maWddIEFuIGVycm9yIG9jY3VycmVkIHdoaWxlIGNyZWF0aW5nIHRoZSAke2NvbmZpZ0ZpbGVOYW1lfSBmaWxlLmBcclxuICAgICAgICAgICAgKTtcclxuICAgICAgICAgIH1cclxuICAgICAgICAgIHJldHVybiB0cnVlO1xyXG4gICAgICAgIH1cclxuICAgICAgfVxyXG4gICAgfSk7XHJcblxyXG4gICAgcmV0dXJuIHRydWU7XHJcbiAgfTtcclxuXHJcbiAgLy8gRmluZCB0aGUgY2F0ZWdvcmllc1xyXG4gIGNvbnN0IGNob2ljZXMgPSBPYmplY3Qua2V5cyhwcm9tcHRzQ29uZmlnKS5tYXAoKGNob2ljZSkgPT4gKHtcclxuICAgIHRpdGxlOiBgJHtjaG9pY2V9IG9wdGlvbnNgLFxyXG4gICAgdmFsdWU6IGNob2ljZVxyXG4gIH0pKTtcclxuXHJcbiAgLy8gQ2F0ZWdvcnkgcHJvbXB0XHJcbiAgcmV0dXJuIHByb21wdHMoXHJcbiAgICB7XHJcbiAgICAgIHR5cGU6ICdtdWx0aXNlbGVjdCcsXHJcbiAgICAgIG5hbWU6ICdjYXRlZ29yeScsXHJcbiAgICAgIG1lc3NhZ2U6ICdXaGljaCBjYXRlZ29yeSBkbyB5b3Ugd2FudCB0byBjb25maWd1cmU/JyxcclxuICAgICAgaGludDogJ1NwYWNlOiBTZWxlY3Qgc3BlY2lmaWMsIEE6IFNlbGVjdCBhbGwsIEVudGVyOiBDb25maXJtLicsXHJcbiAgICAgIGluc3RydWN0aW9uczogJycsXHJcbiAgICAgIGNob2ljZXNcclxuICAgIH0sXHJcbiAgICB7IG9uU3VibWl0IH1cclxuICApO1xyXG59O1xyXG5cclxuLyoqXHJcbiAqIE1hcHMgb2xkLXN0cnVjdHVyZWQgKFBoYW50b21KUykgb3B0aW9ucyB0byBhIG5ldyBjb25maWd1cmF0aW9uIGZvcm1hdFxyXG4gKiAoUHVwcGV0ZWVyKS5cclxuICpcclxuICogQHBhcmFtIHtPYmplY3R9IG9sZE9wdGlvbnMgLSBPbGQtc3RydWN0dXJlZCBvcHRpb25zIHRvIGJlIG1hcHBlZC5cclxuICpcclxuICogQHJldHVybnMge09iamVjdH0gTmV3IG9wdGlvbnMgc3RydWN0dXJlZCBiYXNlZCBvbiB0aGUgZGVmaW5lZCBuZXN0ZWRBcmdzXHJcbiAqIG1hcHBpbmcuXHJcbiAqL1xyXG5leHBvcnQgY29uc3QgbWFwVG9OZXdDb25maWcgPSAob2xkT3B0aW9ucykgPT4ge1xyXG4gIGNvbnN0IG5ld09wdGlvbnMgPSB7fTtcclxuICAvLyBDeWNsZSB0aHJvdWdoIG9sZC1zdHJ1Y3R1cmVkIG9wdGlvbnNcclxuICBmb3IgKGNvbnN0IFtrZXksIHZhbHVlXSBvZiBPYmplY3QuZW50cmllcyhvbGRPcHRpb25zKSkge1xyXG4gICAgY29uc3QgcHJvcGVydGllc0NoYWluID0gbmVzdGVkQXJnc1trZXldID8gbmVzdGVkQXJnc1trZXldLnNwbGl0KCcuJykgOiBbXTtcclxuXHJcbiAgICAvLyBQb3B1bGF0ZSBvYmplY3QgaW4gY29ycmVjdCBwcm9wZXJ0aWVzIGxldmVsc1xyXG4gICAgcHJvcGVydGllc0NoYWluLnJlZHVjZShcclxuICAgICAgKG9iaiwgcHJvcCwgaW5kZXgpID0+XHJcbiAgICAgICAgKG9ialtwcm9wXSA9XHJcbiAgICAgICAgICBwcm9wZXJ0aWVzQ2hhaW4ubGVuZ3RoIC0gMSA9PT0gaW5kZXggPyB2YWx1ZSA6IG9ialtwcm9wXSB8fCB7fSksXHJcbiAgICAgIG5ld09wdGlvbnNcclxuICAgICk7XHJcbiAgfVxyXG4gIHJldHVybiBuZXdPcHRpb25zO1xyXG59O1xyXG5cclxuLyoqXHJcbiAqIE1lcmdlcyB0d28gc2V0cyBvZiBjb25maWd1cmF0aW9uIG9wdGlvbnMsIGNvbnNpZGVyaW5nIGFic29sdXRlIHByb3BlcnRpZXMuXHJcbiAqXHJcbiAqIEBwYXJhbSB7T2JqZWN0fSBvcHRpb25zIC0gT3JpZ2luYWwgY29uZmlndXJhdGlvbiBvcHRpb25zLlxyXG4gKiBAcGFyYW0ge09iamVjdH0gbmV3T3B0aW9ucyAtIE5ldyBjb25maWd1cmF0aW9uIG9wdGlvbnMgdG8gYmUgbWVyZ2VkLlxyXG4gKiBAcGFyYW0ge0FycmF5fSBhYnNvbHV0ZVByb3BzIC0gTGlzdCBvZiBwcm9wZXJ0aWVzIHRoYXQgc2hvdWxkXHJcbiAqIG5vdCBiZSByZWN1cnNpdmVseSBtZXJnZWQuXHJcbiAqXHJcbiAqIEByZXR1cm5zIHtPYmplY3R9IE1lcmdlZCBjb25maWd1cmF0aW9uIG9wdGlvbnMuXHJcbiAqL1xyXG5leHBvcnQgY29uc3QgbWVyZ2VDb25maWdPcHRpb25zID0gKG9wdGlvbnMsIG5ld09wdGlvbnMsIGFic29sdXRlUHJvcHMgPSBbXSkgPT4ge1xyXG4gIGNvbnN0IG1lcmdlZE9wdGlvbnMgPSBkZWVwQ29weShvcHRpb25zKTtcclxuXHJcbiAgZm9yIChjb25zdCBba2V5LCB2YWx1ZV0gb2YgT2JqZWN0LmVudHJpZXMobmV3T3B0aW9ucykpIHtcclxuICAgIG1lcmdlZE9wdGlvbnNba2V5XSA9XHJcbiAgICAgIGlzT2JqZWN0KHZhbHVlKSAmJlxyXG4gICAgICAhYWJzb2x1dGVQcm9wcy5pbmNsdWRlcyhrZXkpICYmXHJcbiAgICAgIG1lcmdlZE9wdGlvbnNba2V5XSAhPT0gdW5kZWZpbmVkXHJcbiAgICAgICAgPyBtZXJnZUNvbmZpZ09wdGlvbnMobWVyZ2VkT3B0aW9uc1trZXldLCB2YWx1ZSwgYWJzb2x1dGVQcm9wcylcclxuICAgICAgICA6IHZhbHVlICE9PSB1bmRlZmluZWRcclxuICAgICAgICAgID8gdmFsdWVcclxuICAgICAgICAgIDogbWVyZ2VkT3B0aW9uc1trZXldO1xyXG4gIH1cclxuXHJcbiAgcmV0dXJuIG1lcmdlZE9wdGlvbnM7XHJcbn07XHJcblxyXG4vKipcclxuICogSW5pdGlhbGl6ZXMgZXhwb3J0IHNldHRpbmdzIGJhc2VkIG9uIHByb3ZpZGVkIGV4cG9ydE9wdGlvbnNcclxuICogYW5kIGdlbmVyYWxPcHRpb25zLlxyXG4gKlxyXG4gKiBAcGFyYW0ge09iamVjdH0gZXhwb3J0T3B0aW9ucyAtIE9wdGlvbnMgc3BlY2lmaWMgdG8gdGhlIGV4cG9ydCBwcm9jZXNzLlxyXG4gKiBAcGFyYW0ge09iamVjdH0gZ2VuZXJhbE9wdGlvbnMgLSBHZW5lcmFsIGNvbmZpZ3VyYXRpb24gb3B0aW9ucy5cclxuICpcclxuICogQHJldHVybnMge09iamVjdH0gSW5pdGlhbGl6ZWQgZXhwb3J0IHNldHRpbmdzLlxyXG4gKi9cclxuZXhwb3J0IGNvbnN0IGluaXRFeHBvcnRTZXR0aW5ncyA9IChleHBvcnRPcHRpb25zLCBnZW5lcmFsT3B0aW9ucyA9IHt9KSA9PiB7XHJcbiAgbGV0IG9wdGlvbnMgPSB7fTtcclxuXHJcbiAgaWYgKGV4cG9ydE9wdGlvbnMuc3ZnKSB7XHJcbiAgICBvcHRpb25zID0gZGVlcENvcHkoZ2VuZXJhbE9wdGlvbnMpO1xyXG4gICAgb3B0aW9ucy5leHBvcnQudHlwZSA9IGV4cG9ydE9wdGlvbnMudHlwZSB8fCBleHBvcnRPcHRpb25zLmV4cG9ydC50eXBlO1xyXG4gICAgb3B0aW9ucy5leHBvcnQuc2NhbGUgPSBleHBvcnRPcHRpb25zLnNjYWxlIHx8IGV4cG9ydE9wdGlvbnMuZXhwb3J0LnNjYWxlO1xyXG4gICAgb3B0aW9ucy5leHBvcnQub3V0ZmlsZSA9XHJcbiAgICAgIGV4cG9ydE9wdGlvbnMub3V0ZmlsZSB8fCBleHBvcnRPcHRpb25zLmV4cG9ydC5vdXRmaWxlO1xyXG4gICAgb3B0aW9ucy5wYXlsb2FkID0ge1xyXG4gICAgICBzdmc6IGV4cG9ydE9wdGlvbnMuc3ZnXHJcbiAgICB9O1xyXG4gIH0gZWxzZSB7XHJcbiAgICBvcHRpb25zID0gbWVyZ2VDb25maWdPcHRpb25zKFxyXG4gICAgICBnZW5lcmFsT3B0aW9ucyxcclxuICAgICAgZXhwb3J0T3B0aW9ucyxcclxuICAgICAgLy8gT21pdCBnb2luZyBkb3duIHJlY3Vyc2l2ZWx5IHdpdGggdGhlIGJlbG93c1xyXG4gICAgICBhYnNvbHV0ZVByb3BzXHJcbiAgICApO1xyXG4gIH1cclxuXHJcbiAgb3B0aW9ucy5leHBvcnQub3V0ZmlsZSA9XHJcbiAgICBvcHRpb25zLmV4cG9ydD8ub3V0ZmlsZSB8fCBgY2hhcnQuJHtvcHRpb25zLmV4cG9ydD8udHlwZSB8fCAncG5nJ31gO1xyXG4gIHJldHVybiBvcHRpb25zO1xyXG59O1xyXG5cclxuLyoqXHJcbiAqIExvYWRzIGFkZGl0aW9uYWwgY29uZmlndXJhdGlvbiBmcm9tIGEgc3BlY2lmaWVkIGZpbGUgdXNpbmdcclxuICogdGhlIC0tbG9hZENvbmZpZyBvcHRpb24uXHJcbiAqXHJcbiAqIEBwYXJhbSB7QXJyYXl9IGFyZ3MgLSBDb21tYW5kLWxpbmUgYXJndW1lbnRzIHRvIGNoZWNrIGZvclxyXG4gKiB0aGUgLS1sb2FkQ29uZmlnIG9wdGlvbi5cclxuICpcclxuICogQHJldHVybnMge09iamVjdH0gQWRkaXRpb25hbCBjb25maWd1cmF0aW9uIGxvYWRlZCBmcm9tIHRoZSBzcGVjaWZpZWQgZmlsZSxcclxuICogb3IgYW4gZW1wdHkgb2JqZWN0IGlmIG5vdCBmb3VuZCBvciBpbnZhbGlkLlxyXG4gKi9cclxuZnVuY3Rpb24gbG9hZENvbmZpZ0ZpbGUoYXJncykge1xyXG4gIC8vIENoZWNrIGlmIHRoZSAtLWxvYWRDb25maWcgb3B0aW9uIHdhcyB1c2VkXHJcbiAgY29uc3QgY29uZmlnSW5kZXggPSBhcmdzLmZpbmRJbmRleChcclxuICAgIChhcmcpID0+IGFyZy5yZXBsYWNlKC8tL2csICcnKSA9PT0gJ2xvYWRDb25maWcnXHJcbiAgKTtcclxuXHJcbiAgLy8gQ2hlY2sgaWYgdGhlIC0tbG9hZENvbmZpZyBoYXMgYSB2YWx1ZVxyXG4gIGlmIChjb25maWdJbmRleCA+IC0xICYmIGFyZ3NbY29uZmlnSW5kZXggKyAxXSkge1xyXG4gICAgY29uc3QgZmlsZU5hbWUgPSBhcmdzW2NvbmZpZ0luZGV4ICsgMV07XHJcbiAgICB0cnkge1xyXG4gICAgICAvLyBDaGVjayBpZiBhbiBhZGRpdGlvbmFsIGNvbmZpZyBmaWxlIGlzIGEgY29ycmVjdCBKU09OIGZpbGVcclxuICAgICAgaWYgKGZpbGVOYW1lICYmIGZpbGVOYW1lLmVuZHNXaXRoKCcuanNvbicpKSB7XHJcbiAgICAgICAgLy8gTG9hZCBhbiBvcHRpb25hbCBjdXN0b20gSlNPTiBjb25maWcgZmlsZVxyXG4gICAgICAgIHJldHVybiBKU09OLnBhcnNlKHJlYWRGaWxlU3luYyhmaWxlTmFtZSkpO1xyXG4gICAgICB9XHJcbiAgICB9IGNhdGNoIChlcnJvcikge1xyXG4gICAgICBsb2dXaXRoU3RhY2soXHJcbiAgICAgICAgMixcclxuICAgICAgICBlcnJvcixcclxuICAgICAgICBgW2NvbmZpZ10gVW5hYmxlIHRvIGxvYWQgdGhlIGNvbmZpZ3VyYXRpb24gZnJvbSB0aGUgJHtmaWxlTmFtZX0gZmlsZS5gXHJcbiAgICAgICk7XHJcbiAgICB9XHJcbiAgfVxyXG5cclxuICAvLyBObyBhZGRpdGlvbmFsIG9wdGlvbnMgdG8gcmV0dXJuXHJcbiAgcmV0dXJuIHt9O1xyXG59XHJcblxyXG4vKipcclxuICogVXBkYXRlcyB0aGUgZGVmYXVsdCBjb25maWd1cmF0aW9uIG9iamVjdCB3aXRoIHZhbHVlcyBmcm9tIGEgY3VzdG9tIG9iamVjdFxyXG4gKiBhbmQgZW52aXJvbm1lbnQgdmFyaWFibGVzLlxyXG4gKlxyXG4gKiBAcGFyYW0ge09iamVjdH0gY29uZmlnT2JqIC0gVGhlIGRlZmF1bHQgY29uZmlndXJhdGlvbiBvYmplY3QuXHJcbiAqIEBwYXJhbSB7T2JqZWN0fSBjdXN0b21PYmogLSBDdXN0b20gY29uZmlndXJhdGlvbiBvYmplY3QgdG8gb3ZlcnJpZGUgZGVmYXVsdHMuXHJcbiAqIEBwYXJhbSB7c3RyaW5nfSBwcm9wQ2hhaW4gLSBQcm9wZXJ0eSBjaGFpbiBmb3IgdHJhY2tpbmcgbmVzdGVkIHByb3BlcnRpZXNcclxuICogZHVyaW5nIHJlY3Vyc2lvbi5cclxuICovXHJcbmZ1bmN0aW9uIHVwZGF0ZURlZmF1bHRDb25maWcoY29uZmlnT2JqLCBjdXN0b21PYmogPSB7fSwgcHJvcENoYWluID0gJycpIHtcclxuICBPYmplY3Qua2V5cyhjb25maWdPYmopLmZvckVhY2goKGtleSkgPT4ge1xyXG4gICAgY29uc3QgZW50cnkgPSBjb25maWdPYmpba2V5XTtcclxuICAgIGNvbnN0IGN1c3RvbVZhbHVlID0gY3VzdG9tT2JqICYmIGN1c3RvbU9ialtrZXldO1xyXG5cclxuICAgIGlmICh0eXBlb2YgZW50cnkudmFsdWUgPT09ICd1bmRlZmluZWQnKSB7XHJcbiAgICAgIHVwZGF0ZURlZmF1bHRDb25maWcoZW50cnksIGN1c3RvbVZhbHVlLCBgJHtwcm9wQ2hhaW59LiR7a2V5fWApO1xyXG4gICAgfSBlbHNlIHtcclxuICAgICAgLy8gSWYgYSB2YWx1ZSBmcm9tIGEgY3VzdG9tIEpTT04gZXhpc3RzLCBpdCB0YWtlIHByZWNlZGVuY2VcclxuICAgICAgaWYgKGN1c3RvbVZhbHVlICE9PSB1bmRlZmluZWQpIHtcclxuICAgICAgICBlbnRyeS52YWx1ZSA9IGN1c3RvbVZhbHVlO1xyXG4gICAgICB9XHJcblxyXG4gICAgICAvLyBJZiBhIHZhbHVlIGZyb20gYW4gZW52IHZhcmlhYmxlIGV4aXN0cywgaXQgdGFrZSBwcmVjZWRlbmNlXHJcbiAgICAgIGlmIChlbnRyeS5lbnZMaW5rIGluIGVudnMgJiYgZW52c1tlbnRyeS5lbnZMaW5rXSAhPT0gdW5kZWZpbmVkKSB7XHJcbiAgICAgICAgZW50cnkudmFsdWUgPSBlbnZzW2VudHJ5LmVudkxpbmtdO1xyXG4gICAgICB9XHJcbiAgICB9XHJcbiAgfSk7XHJcbn1cclxuXHJcbi8qKlxyXG4gKiBJbml0aWFsaXplcyBvcHRpb25zIG9iamVjdCBiYXNlZCBvbiBwcm92aWRlZCBpdGVtcywgc2V0dGluZyB2YWx1ZXMgZnJvbVxyXG4gKiBuZXN0ZWQgcHJvcGVydGllcyByZWN1cnNpdmVseS5cclxuICpcclxuICogQHBhcmFtIHtPYmplY3R9IGl0ZW1zIC0gQ29uZmlndXJhdGlvbiBpdGVtcyB0byBiZSB1c2VkIGZvciBpbml0aWFsaXppbmdcclxuICogb3B0aW9ucy5cclxuICpcclxuICogQHJldHVybnMge09iamVjdH0gSW5pdGlhbGl6ZWQgb3B0aW9ucyBvYmplY3QuXHJcbiAqL1xyXG5mdW5jdGlvbiBpbml0T3B0aW9ucyhpdGVtcykge1xyXG4gIGxldCBvcHRpb25zID0ge307XHJcbiAgZm9yIChjb25zdCBbbmFtZSwgaXRlbV0gb2YgT2JqZWN0LmVudHJpZXMoaXRlbXMpKSB7XHJcbiAgICBvcHRpb25zW25hbWVdID0gT2JqZWN0LnByb3RvdHlwZS5oYXNPd25Qcm9wZXJ0eS5jYWxsKGl0ZW0sICd2YWx1ZScpXHJcbiAgICAgID8gaXRlbS52YWx1ZVxyXG4gICAgICA6IGluaXRPcHRpb25zKGl0ZW0pO1xyXG4gIH1cclxuICByZXR1cm4gb3B0aW9ucztcclxufVxyXG5cclxuLyoqXHJcbiAqIFBhaXJzIGFyZ3VtZW50IHZhbHVlcyB3aXRoIGNvcnJlc3BvbmRpbmcgb3B0aW9ucyBpbiB0aGUgY29uZmlndXJhdGlvbixcclxuICogdXBkYXRpbmcgdGhlIG9wdGlvbnMgb2JqZWN0LlxyXG4gKlxyXG4gKiBAcGFyYW0ge09iamVjdH0gb3B0aW9ucyAtIENvbmZpZ3VyYXRpb24gb3B0aW9ucyBvYmplY3QgdG8gYmUgdXBkYXRlZC5cclxuICogQHBhcmFtIHtBcnJheX0gYXJncyAtIENvbW1hbmQtbGluZSBhcmd1bWVudHMgY29udGFpbmluZyB2YWx1ZXMgZm9yIHNwZWNpZmljXHJcbiAqIG9wdGlvbnMuXHJcbiAqIEBwYXJhbSB7T2JqZWN0fSBkZWZhdWx0Q29uZmlnIC0gRGVmYXVsdCBjb25maWd1cmF0aW9uIG9iamVjdCBmb3IgcmVmZXJlbmNlLlxyXG4gKlxyXG4gKiBAcmV0dXJucyB7T2JqZWN0fSBVcGRhdGVkIG9wdGlvbnMgb2JqZWN0LlxyXG4gKi9cclxuZnVuY3Rpb24gcGFpckFyZ3VtZW50VmFsdWUob3B0aW9ucywgYXJncywgZGVmYXVsdENvbmZpZykge1xyXG4gIGxldCBzaG93VXNhZ2UgPSBmYWxzZTtcclxuICBmb3IgKGxldCBpID0gMDsgaSA8IGFyZ3MubGVuZ3RoOyBpKyspIHtcclxuICAgIGNvbnN0IG9wdGlvbiA9IGFyZ3NbaV0ucmVwbGFjZSgvLS9nLCAnJyk7XHJcblxyXG4gICAgLy8gRmluZCB0aGUgcmlnaHQgcGxhY2UgZm9yIHByb3BlcnR5J3MgdmFsdWVcclxuICAgIGNvbnN0IHByb3BlcnRpZXNDaGFpbiA9IG5lc3RlZEFyZ3Nbb3B0aW9uXVxyXG4gICAgICA/IG5lc3RlZEFyZ3Nbb3B0aW9uXS5zcGxpdCgnLicpXHJcbiAgICAgIDogW107XHJcblxyXG4gICAgLy8gR2V0IHRoZSBjb3JyZWN0IHR5cGUgZm9yIENMSSBhcmdzIHdoaWNoIGFyZSBwYXNzZWQgYXMgc3RyaW5nc1xyXG4gICAgbGV0IGFyZ3VtZW50VHlwZTtcclxuICAgIHByb3BlcnRpZXNDaGFpbi5yZWR1Y2UoKG9iaiwgcHJvcCwgaW5kZXgpID0+IHtcclxuICAgICAgaWYgKHByb3BlcnRpZXNDaGFpbi5sZW5ndGggLSAxID09PSBpbmRleCkge1xyXG4gICAgICAgIGFyZ3VtZW50VHlwZSA9IG9ialtwcm9wXS50eXBlO1xyXG4gICAgICB9XHJcbiAgICAgIHJldHVybiBvYmpbcHJvcF07XHJcbiAgICB9LCBkZWZhdWx0Q29uZmlnKTtcclxuXHJcbiAgICBwcm9wZXJ0aWVzQ2hhaW4ucmVkdWNlKChvYmosIHByb3AsIGluZGV4KSA9PiB7XHJcbiAgICAgIGlmIChwcm9wZXJ0aWVzQ2hhaW4ubGVuZ3RoIC0gMSA9PT0gaW5kZXgpIHtcclxuICAgICAgICAvLyBGaW5kcyBhbiBvcHRpb24gYW5kIHNldCBhIGNvcnJlc3BvbmRpbmcgdmFsdWVcclxuICAgICAgICBpZiAodHlwZW9mIG9ialtwcm9wXSAhPT0gJ3VuZGVmaW5lZCcpIHtcclxuICAgICAgICAgIGlmIChhcmdzWysraV0pIHtcclxuICAgICAgICAgICAgaWYgKGFyZ3VtZW50VHlwZSA9PT0gJ2Jvb2xlYW4nKSB7XHJcbiAgICAgICAgICAgICAgb2JqW3Byb3BdID0gdG9Cb29sZWFuKGFyZ3NbaV0pO1xyXG4gICAgICAgICAgICB9IGVsc2UgaWYgKGFyZ3VtZW50VHlwZSA9PT0gJ251bWJlcicpIHtcclxuICAgICAgICAgICAgICBvYmpbcHJvcF0gPSArYXJnc1tpXTtcclxuICAgICAgICAgICAgfSBlbHNlIGlmIChhcmd1bWVudFR5cGUuaW5kZXhPZignXScpID49IDApIHtcclxuICAgICAgICAgICAgICBvYmpbcHJvcF0gPSBhcmdzW2ldLnNwbGl0KCcsJyk7XHJcbiAgICAgICAgICAgIH0gZWxzZSB7XHJcbiAgICAgICAgICAgICAgb2JqW3Byb3BdID0gYXJnc1tpXTtcclxuICAgICAgICAgICAgfVxyXG4gICAgICAgICAgfSBlbHNlIHtcclxuICAgICAgICAgICAgbG9nKFxyXG4gICAgICAgICAgICAgIDIsXHJcbiAgICAgICAgICAgICAgYFtjb25maWddIE1pc3NpbmcgdmFsdWUgZm9yIHRoZSAnJHtvcHRpb259JyBhcmd1bWVudC4gVXNpbmcgdGhlIGRlZmF1bHQgdmFsdWUuYFxyXG4gICAgICAgICAgICApO1xyXG4gICAgICAgICAgICBzaG93VXNhZ2UgPSB0cnVlO1xyXG4gICAgICAgICAgfVxyXG4gICAgICAgIH1cclxuICAgICAgfVxyXG4gICAgICByZXR1cm4gb2JqW3Byb3BdO1xyXG4gICAgfSwgb3B0aW9ucyk7XHJcbiAgfVxyXG5cclxuICAvLyBEaXNwbGF5IHRoZSB1c2FnZSBmb3IgdGhlIHJlZmVyZW5jZSBpZiBuZWVkZWRcclxuICBpZiAoc2hvd1VzYWdlKSB7XHJcbiAgICBwcmludFVzYWdlKGRlZmF1bHRDb25maWcpO1xyXG4gIH1cclxuXHJcbiAgcmV0dXJuIG9wdGlvbnM7XHJcbn1cclxuXHJcbi8qKlxyXG4gKiBSZWN1cnNpdmVseSB1cGRhdGVzIHByb3BlcnRpZXMgaW4gYW4gb2JqZWN0IGJhc2VkIG9uIG5lc3RlZCBuYW1lcyBhbmQgYXNzaWduc1xyXG4gKiB0aGUgZmluYWwgdmFsdWUuXHJcbiAqXHJcbiAqIEBwYXJhbSB7T2JqZWN0fSBvYmplY3RUb1VwZGF0ZSAtIFRoZSBvYmplY3QgdG8gYmUgdXBkYXRlZC5cclxuICogQHBhcmFtIHtBcnJheX0gbmVzdGVkTmFtZXMgLSBBcnJheSBvZiBuZXN0ZWQgcHJvcGVydHkgbmFtZXMuXHJcbiAqIEBwYXJhbSB7YW55fSB2YWx1ZSAtIFRoZSBmaW5hbCB2YWx1ZSB0byBiZSBhc3NpZ25lZC5cclxuICpcclxuICogQHJldHVybnMge09iamVjdH0gVXBkYXRlZCBvYmplY3Qgd2l0aCBhc3NpZ25lZCB2YWx1ZXMuXHJcbiAqL1xyXG5mdW5jdGlvbiByZWN1cnNpdmVQcm9wcyhvYmplY3RUb1VwZGF0ZSwgbmVzdGVkTmFtZXMsIHZhbHVlKSB7XHJcbiAgd2hpbGUgKG5lc3RlZE5hbWVzLmxlbmd0aCA+IDEpIHtcclxuICAgIGNvbnN0IHByb3BOYW1lID0gbmVzdGVkTmFtZXMuc2hpZnQoKTtcclxuXHJcbiAgICAvLyBDcmVhdGUgYSBwcm9wZXJ0eSBpbiBvYmplY3QgaWYgaXQgZG9lc24ndCBleGlzdFxyXG4gICAgaWYgKCFPYmplY3QucHJvdG90eXBlLmhhc093blByb3BlcnR5LmNhbGwob2JqZWN0VG9VcGRhdGUsIHByb3BOYW1lKSkge1xyXG4gICAgICBvYmplY3RUb1VwZGF0ZVtwcm9wTmFtZV0gPSB7fTtcclxuICAgIH1cclxuXHJcbiAgICAvLyBDYWxsIGZ1bmN0aW9uIGFnYWluIGlmIHRoZXJlIHN0aWxsIG5hbWVzIHRvIGdvXHJcbiAgICBvYmplY3RUb1VwZGF0ZVtwcm9wTmFtZV0gPSByZWN1cnNpdmVQcm9wcyhcclxuICAgICAgT2JqZWN0LmFzc2lnbih7fSwgb2JqZWN0VG9VcGRhdGVbcHJvcE5hbWVdKSxcclxuICAgICAgbmVzdGVkTmFtZXMsXHJcbiAgICAgIHZhbHVlXHJcbiAgICApO1xyXG5cclxuICAgIHJldHVybiBvYmplY3RUb1VwZGF0ZTtcclxuICB9XHJcblxyXG4gIC8vIEFzc2lnbiB0aGUgZmluYWwgdmFsdWVcclxuICBvYmplY3RUb1VwZGF0ZVtuZXN0ZWROYW1lc1swXV0gPSB2YWx1ZTtcclxuICByZXR1cm4gb2JqZWN0VG9VcGRhdGU7XHJcbn1cclxuXHJcbmV4cG9ydCBkZWZhdWx0IHtcclxuICBnZXRPcHRpb25zLFxyXG4gIHNldE9wdGlvbnMsXHJcbiAgbWFudWFsQ29uZmlnLFxyXG4gIG1hcFRvTmV3Q29uZmlnLFxyXG4gIG1lcmdlQ29uZmlnT3B0aW9ucyxcclxuICBpbml0RXhwb3J0U2V0dGluZ3NcclxufTtcclxuIiwiLyoqXHJcbiAqIFRoaXMgbW9kdWxlIGV4cG9ydHMgdHdvIGZ1bmN0aW9uczogZmV0Y2ggKGZvciBHRVQgcmVxdWVzdHMpIGFuZCBwb3N0IChmb3IgUE9TVCByZXF1ZXN0cykuXHJcbiAqL1xyXG5cclxuaW1wb3J0IGh0dHAgZnJvbSAnaHR0cCc7XHJcbmltcG9ydCBodHRwcyBmcm9tICdodHRwcyc7XHJcblxyXG4vKipcclxuICogUmV0dXJucyB0aGUgSFRUUCBvciBIVFRQUyBwcm90b2NvbCBtb2R1bGUgYmFzZWQgb24gdGhlIHByb3ZpZGVkIFVSTC5cclxuICpcclxuICogQHBhcmFtIHtzdHJpbmd9IHVybCAtIFRoZSBVUkwgdG8gZGV0ZXJtaW5lIHRoZSBwcm90b2NvbC5cclxuICpcclxuICogQHJldHVybnMge09iamVjdH0gVGhlIEhUVFAgb3IgSFRUUFMgcHJvdG9jb2wgbW9kdWxlIChodHRwIG9yIGh0dHBzKS5cclxuICovXHJcbmNvbnN0IGdldFByb3RvY29sID0gKHVybCkgPT4gKHVybC5zdGFydHNXaXRoKCdodHRwcycpID8gaHR0cHMgOiBodHRwKTtcclxuXHJcbi8qKlxyXG4gKiBGZXRjaGVzIGRhdGEgZnJvbSB0aGUgc3BlY2lmaWVkIFVSTCB1c2luZyBlaXRoZXIgSFRUUCBvciBIVFRQUyBwcm90b2NvbC5cclxuICpcclxuICogQHBhcmFtIHtzdHJpbmd9IHVybCAtIFRoZSBVUkwgdG8gZmV0Y2ggZGF0YSBmcm9tLlxyXG4gKiBAcGFyYW0ge09iamVjdH0gcmVxdWVzdE9wdGlvbnMgLSBPcHRpb25zIGZvciB0aGUgSFRUUCByZXF1ZXN0IChvcHRpb25hbCkuXHJcbiAqXHJcbiAqIEByZXR1cm5zIHtQcm9taXNlPE9iamVjdD59IFByb21pc2UgcmVzb2x2aW5nIHRvIHRoZSBIVFRQIHJlc3BvbnNlIG9iamVjdFxyXG4gKiB3aXRoIGFkZGVkICd0ZXh0JyBwcm9wZXJ0eSBvciByZWplY3Rpbmcgd2l0aCBhbiBlcnJvci5cclxuICovXHJcbmFzeW5jIGZ1bmN0aW9uIGZldGNoKHVybCwgcmVxdWVzdE9wdGlvbnMgPSB7fSkge1xyXG4gIHJldHVybiBuZXcgUHJvbWlzZSgocmVzb2x2ZSwgcmVqZWN0KSA9PiB7XHJcbiAgICBjb25zdCBwcm90b2NvbCA9IGdldFByb3RvY29sKHVybCk7XHJcblxyXG4gICAgcHJvdG9jb2xcclxuICAgICAgLmdldCh1cmwsIHJlcXVlc3RPcHRpb25zLCAocmVzKSA9PiB7XHJcbiAgICAgICAgbGV0IGRhdGEgPSAnJztcclxuXHJcbiAgICAgICAgLy8gQSBjaHVuayBvZiBkYXRhIGhhcyBiZWVuIHJlY2VpdmVkLlxyXG4gICAgICAgIHJlcy5vbignZGF0YScsIChjaHVuaykgPT4ge1xyXG4gICAgICAgICAgZGF0YSArPSBjaHVuaztcclxuICAgICAgICB9KTtcclxuXHJcbiAgICAgICAgLy8gVGhlIHdob2xlIHJlc3BvbnNlIGhhcyBiZWVuIHJlY2VpdmVkLlxyXG4gICAgICAgIHJlcy5vbignZW5kJywgKCkgPT4ge1xyXG4gICAgICAgICAgaWYgKCFkYXRhKSB7XHJcbiAgICAgICAgICAgIHJlamVjdCgnTm90aGluZyB3YXMgZmV0Y2hlZCBmcm9tIHRoZSBVUkwuJyk7XHJcbiAgICAgICAgICB9XHJcblxyXG4gICAgICAgICAgcmVzLnRleHQgPSBkYXRhO1xyXG4gICAgICAgICAgcmVzb2x2ZShyZXMpO1xyXG4gICAgICAgIH0pO1xyXG4gICAgICB9KVxyXG4gICAgICAub24oJ2Vycm9yJywgKGVycm9yKSA9PiB7XHJcbiAgICAgICAgcmVqZWN0KGVycm9yKTtcclxuICAgICAgfSk7XHJcbiAgfSk7XHJcbn1cclxuXHJcbi8qKlxyXG4gKiBTZW5kcyBhIFBPU1QgcmVxdWVzdCB0byB0aGUgc3BlY2lmaWVkIFVSTCB3aXRoIHRoZSBwcm92aWRlZCBKU09OIGJvZHkgdXNpbmdcclxuICogZWl0aGVyIEhUVFAgb3IgSFRUUFMgcHJvdG9jb2wuXHJcbiAqXHJcbiAqIEBwYXJhbSB7c3RyaW5nfSB1cmwgLSBUaGUgVVJMIHRvIHNlbmQgdGhlIFBPU1QgcmVxdWVzdCB0by5cclxuICogQHBhcmFtIHtPYmplY3R9IGJvZHkgLSBUaGUgSlNPTiBib2R5IHRvIGluY2x1ZGUgaW4gdGhlIFBPU1QgcmVxdWVzdFxyXG4gKiAob3B0aW9uYWwsIGRlZmF1bHQgaXMgYW4gZW1wdHkgb2JqZWN0KS5cclxuICogQHBhcmFtIHtPYmplY3R9IHJlcXVlc3RPcHRpb25zIC0gT3B0aW9ucyBmb3IgdGhlIEhUVFAgcmVxdWVzdCAob3B0aW9uYWwpLlxyXG4gKlxyXG4gKiBAcmV0dXJucyB7UHJvbWlzZTxPYmplY3Q+fSBQcm9taXNlIHJlc29sdmluZyB0byB0aGUgSFRUUCByZXNwb25zZSBvYmplY3Qgd2l0aFxyXG4gKiBhZGRlZCAndGV4dCcgcHJvcGVydHkgb3IgcmVqZWN0aW5nIHdpdGggYW4gZXJyb3IuXHJcbiAqL1xyXG5hc3luYyBmdW5jdGlvbiBwb3N0KHVybCwgYm9keSA9IHt9LCByZXF1ZXN0T3B0aW9ucyA9IHt9KSB7XHJcbiAgcmV0dXJuIG5ldyBQcm9taXNlKChyZXNvbHZlLCByZWplY3QpID0+IHtcclxuICAgIGNvbnN0IHByb3RvY29sID0gZ2V0UHJvdG9jb2wodXJsKTtcclxuICAgIGNvbnN0IGRhdGEgPSBKU09OLnN0cmluZ2lmeShib2R5KTtcclxuXHJcbiAgICAvLyBTZXQgZGVmYXVsdCBoZWFkZXJzIGFuZCBtZXJnZSB3aXRoIHJlcXVlc3RPcHRpb25zXHJcbiAgICBjb25zdCBvcHRpb25zID0gT2JqZWN0LmFzc2lnbihcclxuICAgICAge1xyXG4gICAgICAgIG1ldGhvZDogJ1BPU1QnLFxyXG4gICAgICAgIGhlYWRlcnM6IHtcclxuICAgICAgICAgICdDb250ZW50LVR5cGUnOiAnYXBwbGljYXRpb24vanNvbicsXHJcbiAgICAgICAgICAnQ29udGVudC1MZW5ndGgnOiBkYXRhLmxlbmd0aFxyXG4gICAgICAgIH1cclxuICAgICAgfSxcclxuICAgICAgcmVxdWVzdE9wdGlvbnNcclxuICAgICk7XHJcblxyXG4gICAgY29uc3QgcmVxID0gcHJvdG9jb2xcclxuICAgICAgLnJlcXVlc3QodXJsLCBvcHRpb25zLCAocmVzKSA9PiB7XHJcbiAgICAgICAgbGV0IHJlc3BvbnNlRGF0YSA9ICcnO1xyXG5cclxuICAgICAgICAvLyBBIGNodW5rIG9mIGRhdGEgaGFzIGJlZW4gcmVjZWl2ZWQuXHJcbiAgICAgICAgcmVzLm9uKCdkYXRhJywgKGNodW5rKSA9PiB7XHJcbiAgICAgICAgICByZXNwb25zZURhdGEgKz0gY2h1bms7XHJcbiAgICAgICAgfSk7XHJcblxyXG4gICAgICAgIC8vIFRoZSB3aG9sZSByZXNwb25zZSBoYXMgYmVlbiByZWNlaXZlZC5cclxuICAgICAgICByZXMub24oJ2VuZCcsICgpID0+IHtcclxuICAgICAgICAgIHRyeSB7XHJcbiAgICAgICAgICAgIHJlcy50ZXh0ID0gcmVzcG9uc2VEYXRhO1xyXG4gICAgICAgICAgICByZXNvbHZlKHJlcyk7XHJcbiAgICAgICAgICB9IGNhdGNoIChlcnJvcikge1xyXG4gICAgICAgICAgICByZWplY3QoZXJyb3IpO1xyXG4gICAgICAgICAgfVxyXG4gICAgICAgIH0pO1xyXG4gICAgICB9KVxyXG4gICAgICAub24oJ2Vycm9yJywgKGVycm9yKSA9PiB7XHJcbiAgICAgICAgcmVqZWN0KGVycm9yKTtcclxuICAgICAgfSk7XHJcblxyXG4gICAgLy8gV3JpdGUgdGhlIHJlcXVlc3QgYm9keSBhbmQgZW5kIHRoZSByZXF1ZXN0LlxyXG4gICAgcmVxLndyaXRlKGRhdGEpO1xyXG4gICAgcmVxLmVuZCgpO1xyXG4gIH0pO1xyXG59XHJcblxyXG5leHBvcnQgZGVmYXVsdCBmZXRjaDtcclxuZXhwb3J0IHsgZmV0Y2gsIHBvc3QgfTtcclxuIiwiY2xhc3MgRXhwb3J0RXJyb3IgZXh0ZW5kcyBFcnJvciB7XHJcbiAgY29uc3RydWN0b3IobWVzc2FnZSkge1xyXG4gICAgc3VwZXIoKTtcclxuICAgIHRoaXMubWVzc2FnZSA9IG1lc3NhZ2U7XHJcbiAgICB0aGlzLnN0YWNrTWVzc2FnZSA9IG1lc3NhZ2U7XHJcbiAgfVxyXG5cclxuICBzZXRFcnJvcihlcnJvcikge1xyXG4gICAgdGhpcy5lcnJvciA9IGVycm9yO1xyXG4gICAgaWYgKGVycm9yLm5hbWUpIHtcclxuICAgICAgdGhpcy5uYW1lID0gZXJyb3IubmFtZTtcclxuICAgIH1cclxuICAgIGlmIChlcnJvci5zdGF0dXNDb2RlKSB7XHJcbiAgICAgIHRoaXMuc3RhdHVzQ29kZSA9IGVycm9yLnN0YXR1c0NvZGU7XHJcbiAgICB9XHJcbiAgICBpZiAoZXJyb3Iuc3RhY2spIHtcclxuICAgICAgdGhpcy5zdGFja01lc3NhZ2UgPSBlcnJvci5tZXNzYWdlO1xyXG4gICAgICB0aGlzLnN0YWNrID0gZXJyb3Iuc3RhY2s7XHJcbiAgICB9XHJcbiAgICByZXR1cm4gdGhpcztcclxuICB9XHJcbn1cclxuXHJcbmV4cG9ydCBkZWZhdWx0IEV4cG9ydEVycm9yO1xyXG4iLCIvKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKlxyXG5cclxuSGlnaGNoYXJ0cyBFeHBvcnQgU2VydmVyXHJcblxyXG5Db3B5cmlnaHQgKGMpIDIwMTYtMjAyNCwgSGlnaHNvZnRcclxuXHJcbkxpY2VuY2VkIHVuZGVyIHRoZSBNSVQgbGljZW5jZS5cclxuXHJcbkFkZGl0aW9uYWxseSBhIHZhbGlkIEhpZ2hjaGFydHMgbGljZW5zZSBpcyByZXF1aXJlZCBmb3IgdXNlLlxyXG5cclxuU2VlIExJQ0VOU0UgZmlsZSBpbiByb290IGZvciBkZXRhaWxzLlxyXG5cclxuKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKi9cclxuXHJcbi8vIFRoZSBjYWNoZSBtYW5hZ2VyIG1hbmFnZXMgdGhlIEhpZ2hjaGFydHMgbGlicmFyeSBhbmQgaXRzIGRlcGVuZGVuY2llcy5cclxuLy8gVGhlIGNhY2hlIGl0c2VsZiBpcyBzdG9yZWQgaW4gLmNhY2hlLCBhbmQgaXMgY2hlY2tlZCBieSB0aGUgY29uZmlnIHN5c3RlbVxyXG4vLyBiZWZvcmUgc3RhcnRpbmcgdGhlIHNlcnZpY2VcclxuXHJcbmltcG9ydCB7IGV4aXN0c1N5bmMsIG1rZGlyU3luYywgcmVhZEZpbGVTeW5jLCB3cml0ZUZpbGVTeW5jIH0gZnJvbSAnZnMnO1xyXG5pbXBvcnQgeyBqb2luIH0gZnJvbSAncGF0aCc7XHJcblxyXG5pbXBvcnQgeyBIdHRwc1Byb3h5QWdlbnQgfSBmcm9tICdodHRwcy1wcm94eS1hZ2VudCc7XHJcblxyXG5pbXBvcnQgeyBnZXRPcHRpb25zIH0gZnJvbSAnLi9jb25maWcuanMnO1xyXG5pbXBvcnQgeyBlbnZzIH0gZnJvbSAnLi9lbnZzLmpzJztcclxuaW1wb3J0IHsgZmV0Y2ggfSBmcm9tICcuL2ZldGNoLmpzJztcclxuaW1wb3J0IHsgbG9nIH0gZnJvbSAnLi9sb2dnZXIuanMnO1xyXG5pbXBvcnQgeyBfX2Rpcm5hbWUgfSBmcm9tICcuL3V0aWxzLmpzJztcclxuXHJcbmltcG9ydCBFeHBvcnRFcnJvciBmcm9tICcuL2Vycm9ycy9FeHBvcnRFcnJvci5qcyc7XHJcblxyXG5jb25zdCBjYWNoZSA9IHtcclxuICBjZG5VUkw6ICdodHRwczovL2NvZGUuaGlnaGNoYXJ0cy5jb20vJyxcclxuICBhY3RpdmVNYW5pZmVzdDoge30sXHJcbiAgc291cmNlczogJycsXHJcbiAgaGNWZXJzaW9uOiAnJ1xyXG59O1xyXG5cclxuLyoqXHJcbiAqIEV4dHJhY3RzIGFuZCBjYWNoZXMgdGhlIEhpZ2hjaGFydHMgdmVyc2lvbiBmcm9tIHRoZSBzb3VyY2VzIHN0cmluZy5cclxuICpcclxuICogQHJldHVybnMge3N0cmluZ30gVGhlIGV4dHJhY3RlZCBIaWdoY2hhcnRzIHZlcnNpb24uXHJcbiAqL1xyXG5leHBvcnQgY29uc3QgZXh0cmFjdFZlcnNpb24gPSAoY2FjaGUpID0+IHtcclxuICByZXR1cm4gY2FjaGUuc291cmNlc1xyXG4gICAgLnN1YnN0cmluZygwLCBjYWNoZS5zb3VyY2VzLmluZGV4T2YoJyovJykpXHJcbiAgICAucmVwbGFjZSgnLyonLCAnJylcclxuICAgIC5yZXBsYWNlKCcqLycsICcnKVxyXG4gICAgLnJlcGxhY2UoL1xcbi9nLCAnJylcclxuICAgIC50cmltKCk7XHJcbn07XHJcblxyXG4vKipcclxuICogRXh0cmFjdHMgdGhlIEhpZ2hjaGFydHMgbW9kdWxlIG5hbWUgYmFzZWQgb24gdGhlIHNjcmlwdFBhdGguXHJcbiAqL1xyXG5leHBvcnQgY29uc3QgZXh0cmFjdE1vZHVsZU5hbWUgPSAoc2NyaXB0UGF0aCkgPT4ge1xyXG4gIHJldHVybiBzY3JpcHRQYXRoLnJlcGxhY2UoXHJcbiAgICAvKC4qKVxcL3woLiopbW9kdWxlc1xcL3xzdG9ja1xcLyguKilpbmRpY2F0b3JzXFwvfG1hcHNcXC8oLiopbW9kdWxlc1xcLy9naSxcclxuICAgICcnXHJcbiAgKTtcclxufTtcclxuXHJcbi8qKlxyXG4gKiBTYXZlcyB0aGUgcHJvdmlkZWQgY29uZmlndXJhdGlvbiBhbmQgZmV0Y2hlZCBtb2R1bGVzIHRvIHRoZSBjYWNoZSBtYW5pZmVzdFxyXG4gKiBmaWxlLlxyXG4gKlxyXG4gKiBAcGFyYW0ge29iamVjdH0gY29uZmlnIC0gSGlnaGNoYXJ0cy1yZWxhdGVkIGNvbmZpZ3VyYXRpb24gb2JqZWN0LlxyXG4gKiBAcGFyYW0ge29iamVjdH0gZmV0Y2hlZE1vZHVsZXMgLSBBbiBvYmplY3QgdGhhdCBjb250YWlucyBtYXBwZWQgbmFtZXMgb2ZcclxuICogZmV0Y2hlZCBIaWdoY2hhcnRzIG1vZHVsZXMgdG8gdXNlLlxyXG4gKlxyXG4gKiBAdGhyb3dzIHtFeHBvcnRFcnJvcn0gVGhyb3dzIGFuIEV4cG9ydEVycm9yIGlmIGFuIGVycm9yIG9jY3VycyB3aGlsZSB3cml0aW5nXHJcbiAqIHRoZSBjYWNoZSBtYW5pZmVzdC5cclxuICovXHJcbmV4cG9ydCBjb25zdCBzYXZlQ29uZmlnVG9NYW5pZmVzdCA9IGFzeW5jIChjb25maWcsIGZldGNoZWRNb2R1bGVzKSA9PiB7XHJcbiAgY29uc3QgbmV3TWFuaWZlc3QgPSB7XHJcbiAgICB2ZXJzaW9uOiBjb25maWcudmVyc2lvbixcclxuICAgIG1vZHVsZXM6IGZldGNoZWRNb2R1bGVzIHx8IHt9XHJcbiAgfTtcclxuXHJcbiAgLy8gVXBkYXRlIGNhY2hlIG9iamVjdCB3aXRoIHRoZSBjdXJyZW50IG1vZHVsZXNcclxuICBjYWNoZS5hY3RpdmVNYW5pZmVzdCA9IG5ld01hbmlmZXN0O1xyXG5cclxuICBsb2coMywgJ1tjYWNoZV0gV3JpdGluZyBhIG5ldyBtYW5pZmVzdC4nKTtcclxuICB0cnkge1xyXG4gICAgd3JpdGVGaWxlU3luYyhcclxuICAgICAgam9pbihfX2Rpcm5hbWUsIGNvbmZpZy5jYWNoZVBhdGgsICdtYW5pZmVzdC5qc29uJyksXHJcbiAgICAgIEpTT04uc3RyaW5naWZ5KG5ld01hbmlmZXN0KSxcclxuICAgICAgJ3V0ZjgnXHJcbiAgICApO1xyXG4gIH0gY2F0Y2ggKGVycm9yKSB7XHJcbiAgICB0aHJvdyBuZXcgRXhwb3J0RXJyb3IoJ1tjYWNoZV0gRXJyb3Igd3JpdGluZyB0aGUgY2FjaGUgbWFuaWZlc3QuJykuc2V0RXJyb3IoXHJcbiAgICAgIGVycm9yXHJcbiAgICApO1xyXG4gIH1cclxufTtcclxuXHJcbi8qKlxyXG4gKiBGZXRjaGVzIGEgc2luZ2xlIHNjcmlwdCBhbmQgdXBkYXRlcyB0aGUgZmV0Y2hlZE1vZHVsZXMgYWNjb3JkaW5nbHkuXHJcbiAqXHJcbiAqIEBwYXJhbSB7c3RyaW5nfSBzY3JpcHQgLSBBIHBhdGggdG8gc2NyaXB0IHRvIGdldC5cclxuICogQHBhcmFtIHtPYmplY3R9IHJlcXVlc3RPcHRpb25zIC0gQWRkaXRpb25hbCBvcHRpb25zIGZvciB0aGUgcHJveHkgYWdlbnRcclxuICogdG8gdXNlIGZvciBhIHJlcXVlc3QuXHJcbiAqIEBwYXJhbSB7T2JqZWN0fSBmZXRjaGVkTW9kdWxlcyAtIEFuIG9iamVjdCB3aGljaCB0cmFja3Mgd2hpY2ggSGlnaGNoYXJ0c1xyXG4gKiBtb2R1bGVzIGhhdmUgYmVlbiBmZXRjaGVkLlxyXG4gKiBAcGFyYW0ge2Jvb2xlYW59IHNob3VsZFRocm93RXJyb3IgLSBBIGZsYWcgdG8gaW5kaWNhdGUgaWYgdGhlIGVycm9yIHNob3VsZCBiZVxyXG4gKiB0aHJvd24uIFRoaXMgc2hvdWxkIGJlIHVzZWQgb25seSBmb3IgdGhlIGNvcmUgc2NyaXB0cy5cclxuICpcclxuICogQHJldHVybnMge1Byb21pc2U8c3RyaW5nPn0gQSBQcm9taXNlIHJlc29sdmluZyB0byB0aGUgdGV4dCByZXByZXNlbnRhdGlvblxyXG4gKiBvZiB0aGUgZmV0Y2hlZCBzY3JpcHQuXHJcbiAqXHJcbiAqIEB0aHJvd3Mge0V4cG9ydEVycm9yfSBUaHJvd3MgYW4gRXhwb3J0RXJyb3IgaWYgdGhlcmUgaXMgYSBwcm9ibGVtIHdpdGhcclxuICogZmV0Y2hpbmcgdGhlIHNjcmlwdC5cclxuICovXHJcbmV4cG9ydCBjb25zdCBmZXRjaEFuZFByb2Nlc3NTY3JpcHQgPSBhc3luYyAoXHJcbiAgc2NyaXB0LFxyXG4gIHJlcXVlc3RPcHRpb25zLFxyXG4gIGZldGNoZWRNb2R1bGVzLFxyXG4gIHNob3VsZFRocm93RXJyb3IgPSBmYWxzZVxyXG4pID0+IHtcclxuICAvLyBHZXQgcmlkIG9mIHRoZSAuanMgZnJvbSB0aGUgY3VzdG9tIHN0cmluZ3NcclxuICBpZiAoc2NyaXB0LmVuZHNXaXRoKCcuanMnKSkge1xyXG4gICAgc2NyaXB0ID0gc2NyaXB0LnN1YnN0cmluZygwLCBzY3JpcHQubGVuZ3RoIC0gMyk7XHJcbiAgfVxyXG5cclxuICBsb2coNCwgYFtjYWNoZV0gRmV0Y2hpbmcgc2NyaXB0IC0gJHtzY3JpcHR9LmpzYCk7XHJcblxyXG4gIC8vIEZldGNoIHRoZSBzY3JpcHRcclxuICBjb25zdCByZXNwb25zZSA9IGF3YWl0IGZldGNoKGAke3NjcmlwdH0uanNgLCByZXF1ZXN0T3B0aW9ucyk7XHJcblxyXG4gIC8vIElmIE9LLCByZXR1cm4gaXRzIHRleHQgcmVwcmVzZW50YXRpb25cclxuICBpZiAocmVzcG9uc2Uuc3RhdHVzQ29kZSA9PT0gMjAwICYmIHR5cGVvZiByZXNwb25zZS50ZXh0ID09ICdzdHJpbmcnKSB7XHJcbiAgICBpZiAoZmV0Y2hlZE1vZHVsZXMpIHtcclxuICAgICAgY29uc3QgbW9kdWxlTmFtZSA9IGV4dHJhY3RNb2R1bGVOYW1lKHNjcmlwdCk7XHJcbiAgICAgIGZldGNoZWRNb2R1bGVzW21vZHVsZU5hbWVdID0gMTtcclxuICAgIH1cclxuXHJcbiAgICByZXR1cm4gcmVzcG9uc2UudGV4dDtcclxuICB9XHJcblxyXG4gIGlmIChzaG91bGRUaHJvd0Vycm9yKSB7XHJcbiAgICB0aHJvdyBuZXcgRXhwb3J0RXJyb3IoXHJcbiAgICAgIGBDb3VsZCBub3QgZmV0Y2ggdGhlICR7c2NyaXB0fS5qcy4gVGhlIHNjcmlwdCBtaWdodCBub3QgZXhpc3QgaW4gdGhlIHJlcXVlc3RlZCB2ZXJzaW9uIChzdGF0dXMgY29kZTogJHtyZXNwb25zZS5zdGF0dXNDb2RlfSkuYFxyXG4gICAgKS5zZXRFcnJvcihyZXNwb25zZSk7XHJcbiAgfSBlbHNlIHtcclxuICAgIGxvZyhcclxuICAgICAgMixcclxuICAgICAgYFtjYWNoZV0gQ291bGQgbm90IGZldGNoIHRoZSAke3NjcmlwdH0uanMuIFRoZSBzY3JpcHQgbWlnaHQgbm90IGV4aXN0IGluIHRoZSByZXF1ZXN0ZWQgdmVyc2lvbi5gXHJcbiAgICApO1xyXG4gIH1cclxuXHJcbiAgcmV0dXJuICcnO1xyXG59O1xyXG5cclxuLyoqXHJcbiAqIEZldGNoZXMgSGlnaGNoYXJ0cyBzY3JpcHRzIGFuZCBjdXN0b21TY3JpcHRzIGZyb20gdGhlIGdpdmVuIENETnMuXHJcbiAqXHJcbiAqIEBwYXJhbSB7c3RyaW5nfSBjb3JlU2NyaXB0cyAtIEFycmF5IG9mIEhpZ2hjaGFydHMgY29yZSBzY3JpcHRzIHRvIGZldGNoLlxyXG4gKiBAcGFyYW0ge3N0cmluZ30gbW9kdWxlU2NyaXB0cyAtIEFycmF5IG9mIEhpZ2hjaGFydHMgbW9kdWxlcyB0byBmZXRjaC5cclxuICogQHBhcmFtIHtzdHJpbmd9IGN1c3RvbVNjcmlwdHMgLSBBcnJheSBvZiBjdXN0b20gc2NyaXB0IHBhdGhzIHRvIGZldGNoXHJcbiAqIChmdWxsIFVSTHMpLlxyXG4gKiBAcGFyYW0ge29iamVjdH0gcHJveHlPcHRpb25zIC0gT3B0aW9ucyBmb3IgdGhlIHByb3h5IGFnZW50IHRvIHVzZSBmb3JcclxuICogYSByZXF1ZXN0LlxyXG4gKiBAcGFyYW0ge29iamVjdH0gZmV0Y2hlZE1vZHVsZXMgLSBBbiBvYmplY3Qgd2hpY2ggdHJhY2tzIHdoaWNoIEhpZ2hjaGFydHNcclxuICogbW9kdWxlcyBoYXZlIGJlZW4gZmV0Y2hlZC5cclxuICpcclxuICogQHJldHVybnMge1Byb21pc2U8c3RyaW5nPn0gVGhlIGZldGNoZWQgc2NyaXB0cyBjb250ZW50IGpvaW5lZC5cclxuICovXHJcbmV4cG9ydCBjb25zdCBmZXRjaFNjcmlwdHMgPSBhc3luYyAoXHJcbiAgY29yZVNjcmlwdHMsXHJcbiAgbW9kdWxlU2NyaXB0cyxcclxuICBjdXN0b21TY3JpcHRzLFxyXG4gIHByb3h5T3B0aW9ucyxcclxuICBmZXRjaGVkTW9kdWxlc1xyXG4pID0+IHtcclxuICAvLyBDb25maWd1cmUgcHJveHkgaWYgZXhpc3RzXHJcbiAgbGV0IHByb3h5QWdlbnQ7XHJcbiAgY29uc3QgcHJveHlIb3N0ID0gcHJveHlPcHRpb25zLmhvc3Q7XHJcbiAgY29uc3QgcHJveHlQb3J0ID0gcHJveHlPcHRpb25zLnBvcnQ7XHJcblxyXG4gIC8vIFRyeSB0byBjcmVhdGUgYSBQcm94eSBBZ2VudFxyXG4gIGlmIChwcm94eUhvc3QgJiYgcHJveHlQb3J0KSB7XHJcbiAgICB0cnkge1xyXG4gICAgICBwcm94eUFnZW50ID0gbmV3IEh0dHBzUHJveHlBZ2VudCh7XHJcbiAgICAgICAgaG9zdDogcHJveHlIb3N0LFxyXG4gICAgICAgIHBvcnQ6IHByb3h5UG9ydFxyXG4gICAgICB9KTtcclxuICAgIH0gY2F0Y2ggKGVycm9yKSB7XHJcbiAgICAgIHRocm93IG5ldyBFeHBvcnRFcnJvcignW2NhY2hlXSBDb3VsZCBub3QgY3JlYXRlIGEgUHJveHkgQWdlbnQuJykuc2V0RXJyb3IoXHJcbiAgICAgICAgZXJyb3JcclxuICAgICAgKTtcclxuICAgIH1cclxuICB9XHJcblxyXG4gIC8vIElmIGV4aXN0cywgYWRkIHByb3h5IGFnZW50IHRvIHJlcXVlc3Qgb3B0aW9uc1xyXG4gIGNvbnN0IHJlcXVlc3RPcHRpb25zID0gcHJveHlBZ2VudFxyXG4gICAgPyB7XHJcbiAgICAgICAgYWdlbnQ6IHByb3h5QWdlbnQsXHJcbiAgICAgICAgdGltZW91dDogZW52cy5TRVJWRVJfUFJPWFlfVElNRU9VVFxyXG4gICAgICB9XHJcbiAgICA6IHt9O1xyXG5cclxuICBjb25zdCBhbGxGZXRjaFByb21pc2VzID0gW1xyXG4gICAgLi4uY29yZVNjcmlwdHMubWFwKChzY3JpcHQpID0+XHJcbiAgICAgIGZldGNoQW5kUHJvY2Vzc1NjcmlwdChgJHtzY3JpcHR9YCwgcmVxdWVzdE9wdGlvbnMsIGZldGNoZWRNb2R1bGVzLCB0cnVlKVxyXG4gICAgKSxcclxuICAgIC4uLm1vZHVsZVNjcmlwdHMubWFwKChzY3JpcHQpID0+XHJcbiAgICAgIGZldGNoQW5kUHJvY2Vzc1NjcmlwdChgJHtzY3JpcHR9YCwgcmVxdWVzdE9wdGlvbnMsIGZldGNoZWRNb2R1bGVzKVxyXG4gICAgKSxcclxuICAgIC4uLmN1c3RvbVNjcmlwdHMubWFwKChzY3JpcHQpID0+XHJcbiAgICAgIGZldGNoQW5kUHJvY2Vzc1NjcmlwdChgJHtzY3JpcHR9YCwgcmVxdWVzdE9wdGlvbnMpXHJcbiAgICApXHJcbiAgXTtcclxuXHJcbiAgY29uc3QgZmV0Y2hlZFNjcmlwdHMgPSBhd2FpdCBQcm9taXNlLmFsbChhbGxGZXRjaFByb21pc2VzKTtcclxuICByZXR1cm4gZmV0Y2hlZFNjcmlwdHMuam9pbignO1xcbicpO1xyXG59O1xyXG5cclxuLyoqXHJcbiAqIFVwZGF0ZXMgdGhlIGxvY2FsIGNhY2hlIHdpdGggSGlnaGNoYXJ0cyBzY3JpcHRzIGFuZCB0aGVpciB2ZXJzaW9ucy5cclxuICpcclxuICogQHBhcmFtIHtPYmplY3R9IG9wdGlvbnMgLSBPYmplY3QgY29udGFpbmluZyBhbGwgb3B0aW9ucy5cclxuICogQHBhcmFtIHtzdHJpbmd9IHNvdXJjZVBhdGggLSBUaGUgcGF0aCB0byB0aGUgc291cmNlIGZpbGUgaW4gdGhlIGNhY2hlLlxyXG4gKlxyXG4gKiBAcmV0dXJucyB7UHJvbWlzZTxvYmplY3Q+fSBBIFByb21pc2UgcmVzb2x2aW5nIHRvIGFuIG9iamVjdCByZXByZXNlbnRpbmdcclxuICogdGhlIGZldGNoZWQgbW9kdWxlcy5cclxuICpcclxuICogQHRocm93cyB7RXhwb3J0RXJyb3J9IFRocm93cyBhbiBFeHBvcnRFcnJvciBpZiB0aGVyZSBpcyBhbiBpc3N1ZSB1cGRhdGluZ1xyXG4gKiB0aGUgbG9jYWwgSGlnaGNoYXJ0cyBjYWNoZS5cclxuICovXHJcbmV4cG9ydCBjb25zdCB1cGRhdGVDYWNoZSA9IGFzeW5jIChcclxuICBoaWdoY2hhcnRzT3B0aW9ucyxcclxuICBwcm94eU9wdGlvbnMsXHJcbiAgc291cmNlUGF0aFxyXG4pID0+IHtcclxuICBjb25zdCB2ZXJzaW9uID0gaGlnaGNoYXJ0c09wdGlvbnMudmVyc2lvbjtcclxuICBjb25zdCBoY1ZlcnNpb24gPSB2ZXJzaW9uID09PSAnbGF0ZXN0JyB8fCAhdmVyc2lvbiA/ICcnIDogYCR7dmVyc2lvbn0vYDtcclxuICBjb25zdCBjZG5VUkwgPSBoaWdoY2hhcnRzT3B0aW9ucy5jZG5VUkwgfHwgY2FjaGUuY2RuVVJMO1xyXG5cclxuICBsb2coXHJcbiAgICAzLFxyXG4gICAgYFtjYWNoZV0gVXBkYXRpbmcgY2FjaGUgdmVyc2lvbiB0byBIaWdoY2hhcnRzOiAke2hjVmVyc2lvbiB8fCAnbGF0ZXN0J30uYFxyXG4gICk7XHJcblxyXG4gIGNvbnN0IGZldGNoZWRNb2R1bGVzID0ge307XHJcbiAgdHJ5IHtcclxuICAgIGNhY2hlLnNvdXJjZXMgPSBhd2FpdCBmZXRjaFNjcmlwdHMoXHJcbiAgICAgIFtcclxuICAgICAgICAuLi5oaWdoY2hhcnRzT3B0aW9ucy5jb3JlU2NyaXB0cy5tYXAoKGMpID0+IGAke2NkblVSTH0ke2hjVmVyc2lvbn0ke2N9YClcclxuICAgICAgXSxcclxuICAgICAgW1xyXG4gICAgICAgIC4uLmhpZ2hjaGFydHNPcHRpb25zLm1vZHVsZVNjcmlwdHMubWFwKChtKSA9PlxyXG4gICAgICAgICAgbSA9PT0gJ21hcCdcclxuICAgICAgICAgICAgPyBgJHtjZG5VUkx9bWFwcy8ke2hjVmVyc2lvbn1tb2R1bGVzLyR7bX1gXHJcbiAgICAgICAgICAgIDogYCR7Y2RuVVJMfSR7aGNWZXJzaW9ufW1vZHVsZXMvJHttfWBcclxuICAgICAgICApLFxyXG4gICAgICAgIC4uLmhpZ2hjaGFydHNPcHRpb25zLmluZGljYXRvclNjcmlwdHMubWFwKFxyXG4gICAgICAgICAgKGkpID0+IGAke2NkblVSTH1zdG9jay8ke2hjVmVyc2lvbn1pbmRpY2F0b3JzLyR7aX1gXHJcbiAgICAgICAgKVxyXG4gICAgICBdLFxyXG4gICAgICBoaWdoY2hhcnRzT3B0aW9ucy5jdXN0b21TY3JpcHRzLFxyXG4gICAgICBwcm94eU9wdGlvbnMsXHJcbiAgICAgIGZldGNoZWRNb2R1bGVzXHJcbiAgICApO1xyXG5cclxuICAgIGNhY2hlLmhjVmVyc2lvbiA9IGV4dHJhY3RWZXJzaW9uKGNhY2hlKTtcclxuXHJcbiAgICAvLyBTYXZlIHRoZSBmZXRjaGVkIG1vZHVsZXMgaW50byBjYWNoZXMnIHNvdXJjZSBKU09OXHJcbiAgICB3cml0ZUZpbGVTeW5jKHNvdXJjZVBhdGgsIGNhY2hlLnNvdXJjZXMpO1xyXG4gICAgcmV0dXJuIGZldGNoZWRNb2R1bGVzO1xyXG4gIH0gY2F0Y2ggKGVycm9yKSB7XHJcbiAgICB0aHJvdyBuZXcgRXhwb3J0RXJyb3IoXHJcbiAgICAgICdbY2FjaGVdIFVuYWJsZSB0byB1cGRhdGUgdGhlIGxvY2FsIEhpZ2hjaGFydHMgY2FjaGUuJ1xyXG4gICAgKS5zZXRFcnJvcihlcnJvcik7XHJcbiAgfVxyXG59O1xyXG5cclxuLyoqXHJcbiAqIFVwZGF0ZXMgdGhlIEhpZ2hjaGFydHMgdmVyc2lvbiBpbiB0aGUgYXBwbGllZCBjb25maWd1cmF0aW9uIGFuZCBjaGVja3NcclxuICogdGhlIGNhY2hlIGZvciB0aGUgbmV3IHZlcnNpb24uXHJcbiAqXHJcbiAqIEBwYXJhbSB7c3RyaW5nfSBuZXdWZXJzaW9uIC0gVGhlIG5ldyBIaWdoY2hhcnRzIHZlcnNpb24gdG8gYmUgYXBwbGllZC5cclxuICpcclxuICogQHJldHVybnMge1Byb21pc2U8KG9iamVjdHxib29sZWFuKT59IEEgUHJvbWlzZSByZXNvbHZpbmcgdG8gdGhlIHVwZGF0ZWRcclxuICogY29uZmlndXJhdGlvbiB3aXRoIHRoZSBuZXcgdmVyc2lvbiwgb3IgZmFsc2UgaWYgbm8gYXBwbGllZCBjb25maWd1cmF0aW9uXHJcbiAqIGV4aXN0cy5cclxuICovXHJcbmV4cG9ydCBjb25zdCB1cGRhdGVWZXJzaW9uID0gYXN5bmMgKG5ld1ZlcnNpb24pID0+IHtcclxuICBjb25zdCBvcHRpb25zID0gZ2V0T3B0aW9ucygpO1xyXG4gIGlmIChvcHRpb25zPy5oaWdoY2hhcnRzKSB7XHJcbiAgICBvcHRpb25zLmhpZ2hjaGFydHMudmVyc2lvbiA9IG5ld1ZlcnNpb247XHJcbiAgfVxyXG4gIGF3YWl0IGNoZWNrQW5kVXBkYXRlQ2FjaGUob3B0aW9ucyk7XHJcbn07XHJcblxyXG4vKipcclxuICogQ2hlY2tzIHRoZSBjYWNoZSBmb3IgSGlnaGNoYXJ0cyBkZXBlbmRlbmNpZXMsIHVwZGF0ZXMgdGhlIGNhY2hlIGlmIG5lZWRlZCxcclxuICogYW5kIGxvYWRzIHRoZSBzb3VyY2VzLlxyXG4gKlxyXG4gKiBAcGFyYW0ge09iamVjdH0gb3B0aW9ucyAtIE9iamVjdCBjb250YWluaW5nIGFsbCBvcHRpb25zLlxyXG4gKlxyXG4gKiBAcmV0dXJucyB7UHJvbWlzZTx2b2lkPn0gQSBQcm9taXNlIHRoYXQgcmVzb2x2ZXMgb25jZSB0aGUgY2FjaGUgaXMgY2hlY2tlZFxyXG4gKiBhbmQgdXBkYXRlZC5cclxuICpcclxuICogQHRocm93cyB7RXhwb3J0RXJyb3J9IFRocm93cyBhbiBFeHBvcnRFcnJvciBpZiB0aGVyZSBpcyBhbiBpc3N1ZSB1cGRhdGluZ1xyXG4gKiBvciByZWFkaW5nIHRoZSBjYWNoZS5cclxuICovXHJcbmV4cG9ydCBjb25zdCBjaGVja0FuZFVwZGF0ZUNhY2hlID0gYXN5bmMgKG9wdGlvbnMpID0+IHtcclxuICBjb25zdCB7IGhpZ2hjaGFydHMsIHNlcnZlciB9ID0gb3B0aW9ucztcclxuICBjb25zdCBjYWNoZVBhdGggPSBqb2luKF9fZGlybmFtZSwgaGlnaGNoYXJ0cy5jYWNoZVBhdGgpO1xyXG5cclxuICBsZXQgZmV0Y2hlZE1vZHVsZXM7XHJcbiAgLy8gUHJlcGFyZSBwYXRocyB0byBtYW5pZmVzdCBhbmQgc291cmNlcyBmcm9tIHRoZSAuY2FjaGUgZm9sZGVyXHJcbiAgY29uc3QgbWFuaWZlc3RQYXRoID0gam9pbihjYWNoZVBhdGgsICdtYW5pZmVzdC5qc29uJyk7XHJcbiAgY29uc3Qgc291cmNlUGF0aCA9IGpvaW4oY2FjaGVQYXRoLCAnc291cmNlcy5qcycpO1xyXG5cclxuICAvLyBDcmVhdGUgdGhlIGNhY2hlIGRlc3RpbmF0aW9uIGlmIGl0IGRvZXNuJ3QgZXhpc3QgYWxyZWFkeVxyXG4gICFleGlzdHNTeW5jKGNhY2hlUGF0aCkgJiYgbWtkaXJTeW5jKGNhY2hlUGF0aCk7XHJcblxyXG4gIC8vIEZldGNoIGFsbCB0aGUgc2NyaXB0cyBlaXRoZXIgaWYgbWFuaWZlc3QuanNvbiBkb2VzIG5vdCBleGlzdFxyXG4gIC8vIG9yIGlmIHRoZSBmb3JjZUZldGNoIG9wdGlvbiBpcyBlbmFibGVkXHJcbiAgaWYgKCFleGlzdHNTeW5jKG1hbmlmZXN0UGF0aCkgfHwgaGlnaGNoYXJ0cy5mb3JjZUZldGNoKSB7XHJcbiAgICBsb2coMywgJ1tjYWNoZV0gRmV0Y2hpbmcgYW5kIGNhY2hpbmcgSGlnaGNoYXJ0cyBkZXBlbmRlbmNpZXMuJyk7XHJcbiAgICBmZXRjaGVkTW9kdWxlcyA9IGF3YWl0IHVwZGF0ZUNhY2hlKGhpZ2hjaGFydHMsIHNlcnZlci5wcm94eSwgc291cmNlUGF0aCk7XHJcbiAgfSBlbHNlIHtcclxuICAgIGxldCByZXF1ZXN0VXBkYXRlID0gZmFsc2U7XHJcblxyXG4gICAgLy8gUmVhZCB0aGUgbWFuaWZlc3QgSlNPTlxyXG4gICAgY29uc3QgbWFuaWZlc3QgPSBKU09OLnBhcnNlKHJlYWRGaWxlU3luYyhtYW5pZmVzdFBhdGgpKTtcclxuXHJcbiAgICAvLyBDaGVjayBpZiB0aGUgbW9kdWxlcyBpcyBhbiBhcnJheSwgaWYgc28sIHdlIHJld3JpdGUgaXQgdG8gYSBtYXAgdG8gbWFrZVxyXG4gICAgLy8gaXQgZWFzaWVyIHRvIHJlc29sdmUgbW9kdWxlcy5cclxuICAgIGlmIChtYW5pZmVzdC5tb2R1bGVzICYmIEFycmF5LmlzQXJyYXkobWFuaWZlc3QubW9kdWxlcykpIHtcclxuICAgICAgY29uc3QgbW9kdWxlTWFwID0ge307XHJcbiAgICAgIG1hbmlmZXN0Lm1vZHVsZXMuZm9yRWFjaCgobSkgPT4gKG1vZHVsZU1hcFttXSA9IDEpKTtcclxuICAgICAgbWFuaWZlc3QubW9kdWxlcyA9IG1vZHVsZU1hcDtcclxuICAgIH1cclxuXHJcbiAgICBjb25zdCB7IGNvcmVTY3JpcHRzLCBtb2R1bGVTY3JpcHRzLCBpbmRpY2F0b3JTY3JpcHRzIH0gPSBoaWdoY2hhcnRzO1xyXG4gICAgY29uc3QgbnVtYmVyT2ZNb2R1bGVzID1cclxuICAgICAgY29yZVNjcmlwdHMubGVuZ3RoICsgbW9kdWxlU2NyaXB0cy5sZW5ndGggKyBpbmRpY2F0b3JTY3JpcHRzLmxlbmd0aDtcclxuXHJcbiAgICAvLyBDb21wYXJlIHRoZSBsb2FkZWQgaGlnaGNoYXJ0cyBjb25maWcgd2l0aCB0aGUgY29udGVudHMgaW4gY2FjaGUuXHJcbiAgICAvLyBJZiB0aGVyZSBhcmUgY2hhbmdlcywgZmV0Y2ggcmVxdWVzdGVkIG1vZHVsZXMgYW5kIHByb2R1Y3RzLFxyXG4gICAgLy8gYW5kIGJha2UgdGhlbSBpbnRvIGEgZ2lhbnQgYmxvYi4gU2F2ZSB0aGUgYmxvYi5cclxuICAgIGlmIChtYW5pZmVzdC52ZXJzaW9uICE9PSBoaWdoY2hhcnRzLnZlcnNpb24pIHtcclxuICAgICAgbG9nKFxyXG4gICAgICAgIDIsXHJcbiAgICAgICAgJ1tjYWNoZV0gQSBIaWdoY2hhcnRzIHZlcnNpb24gbWlzbWF0Y2ggaW4gdGhlIGNhY2hlLCBuZWVkIHRvIHJlLWZldGNoLidcclxuICAgICAgKTtcclxuICAgICAgcmVxdWVzdFVwZGF0ZSA9IHRydWU7XHJcbiAgICB9IGVsc2UgaWYgKE9iamVjdC5rZXlzKG1hbmlmZXN0Lm1vZHVsZXMgfHwge30pLmxlbmd0aCAhPT0gbnVtYmVyT2ZNb2R1bGVzKSB7XHJcbiAgICAgIGxvZyhcclxuICAgICAgICAyLFxyXG4gICAgICAgICdbY2FjaGVdIFRoZSBjYWNoZSBhbmQgdGhlIHJlcXVlc3RlZCBtb2R1bGVzIGRvIG5vdCBtYXRjaCwgbmVlZCB0byByZS1mZXRjaC4nXHJcbiAgICAgICk7XHJcbiAgICAgIHJlcXVlc3RVcGRhdGUgPSB0cnVlO1xyXG4gICAgfSBlbHNlIHtcclxuICAgICAgLy8gQ2hlY2sgZWFjaCBtb2R1bGUsIGlmIGFueXRoaW5nIGlzIG1pc3NpbmcgcmVmZXRjaCBldmVyeXRoaW5nXHJcbiAgICAgIHJlcXVlc3RVcGRhdGUgPSAobW9kdWxlU2NyaXB0cyB8fCBbXSkuc29tZSgobW9kdWxlTmFtZSkgPT4ge1xyXG4gICAgICAgIGlmICghbWFuaWZlc3QubW9kdWxlc1ttb2R1bGVOYW1lXSkge1xyXG4gICAgICAgICAgbG9nKFxyXG4gICAgICAgICAgICAyLFxyXG4gICAgICAgICAgICBgW2NhY2hlXSBUaGUgJHttb2R1bGVOYW1lfSBpcyBtaXNzaW5nIGluIHRoZSBjYWNoZSwgbmVlZCB0byByZS1mZXRjaC5gXHJcbiAgICAgICAgICApO1xyXG4gICAgICAgICAgcmV0dXJuIHRydWU7XHJcbiAgICAgICAgfVxyXG4gICAgICB9KTtcclxuICAgIH1cclxuXHJcbiAgICBpZiAocmVxdWVzdFVwZGF0ZSkge1xyXG4gICAgICBmZXRjaGVkTW9kdWxlcyA9IGF3YWl0IHVwZGF0ZUNhY2hlKGhpZ2hjaGFydHMsIHNlcnZlci5wcm94eSwgc291cmNlUGF0aCk7XHJcbiAgICB9IGVsc2Uge1xyXG4gICAgICBsb2coMywgJ1tjYWNoZV0gRGVwZW5kZW5jeSBjYWNoZSBpcyB1cCB0byBkYXRlLCBwcm9jZWVkaW5nLicpO1xyXG5cclxuICAgICAgLy8gTG9hZCB0aGUgc291cmNlc1xyXG4gICAgICBjYWNoZS5zb3VyY2VzID0gcmVhZEZpbGVTeW5jKHNvdXJjZVBhdGgsICd1dGY4Jyk7XHJcblxyXG4gICAgICAvLyBHZXQgY3VycmVudCBtb2R1bGVzIG1hcFxyXG4gICAgICBmZXRjaGVkTW9kdWxlcyA9IG1hbmlmZXN0Lm1vZHVsZXM7XHJcblxyXG4gICAgICBjYWNoZS5oY1ZlcnNpb24gPSBleHRyYWN0VmVyc2lvbihjYWNoZSk7XHJcbiAgICB9XHJcbiAgfVxyXG5cclxuICAvLyBGaW5hbGx5LCBzYXZlIHRoZSBuZXcgbWFuaWZlc3QsIHdoaWNoIGlzIGJhc2ljYWxseSBvdXIgY3VycmVudCBjb25maWdcclxuICAvLyBpbiBhIHNsaWdodGx5IGRpZmZlcmVudCBmb3JtYXRcclxuICBhd2FpdCBzYXZlQ29uZmlnVG9NYW5pZmVzdChoaWdoY2hhcnRzLCBmZXRjaGVkTW9kdWxlcyk7XHJcbn07XHJcblxyXG5leHBvcnQgY29uc3QgZ2V0Q2FjaGVQYXRoID0gKCkgPT5cclxuICBqb2luKF9fZGlybmFtZSwgZ2V0T3B0aW9ucygpLmhpZ2hjaGFydHMuY2FjaGVQYXRoKTtcclxuXHJcbmV4cG9ydCBjb25zdCBnZXRDYWNoZSA9ICgpID0+IGNhY2hlO1xyXG5cclxuZXhwb3J0IGNvbnN0IGhpZ2hjaGFydHMgPSAoKSA9PiBjYWNoZS5zb3VyY2VzO1xyXG5cclxuZXhwb3J0IGNvbnN0IHZlcnNpb24gPSAoKSA9PiBjYWNoZS5oY1ZlcnNpb247XHJcblxyXG5leHBvcnQgZGVmYXVsdCB7XHJcbiAgY2hlY2tBbmRVcGRhdGVDYWNoZSxcclxuICBnZXRDYWNoZVBhdGgsXHJcbiAgdXBkYXRlVmVyc2lvbixcclxuICBnZXRDYWNoZSxcclxuICBoaWdoY2hhcnRzLFxyXG4gIHZlcnNpb25cclxufTtcclxuIiwiLyoqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKipcclxuXHJcbkhpZ2hjaGFydHMgRXhwb3J0IFNlcnZlclxyXG5cclxuQ29weXJpZ2h0IChjKSAyMDE2LTIwMjQsIEhpZ2hzb2Z0XHJcblxyXG5MaWNlbmNlZCB1bmRlciB0aGUgTUlUIGxpY2VuY2UuXHJcblxyXG5BZGRpdGlvbmFsbHkgYSB2YWxpZCBIaWdoY2hhcnRzIGxpY2Vuc2UgaXMgcmVxdWlyZWQgZm9yIHVzZS5cclxuXHJcblNlZSBMSUNFTlNFIGZpbGUgaW4gcm9vdCBmb3IgZGV0YWlscy5cclxuXHJcbioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKiovXHJcblxyXG4vKiBlc2xpbnQtZGlzYWJsZSBuby11bmRlZiAqL1xyXG5cclxuLyoqXHJcbiAqIFNldHRpbmcgdGhlIGFuaW1PYmplY3QuIENhbGxlZCB3aGVuIGluaXRpbmcgdGhlIHBhZ2UuXHJcbiAqL1xyXG5leHBvcnQgZnVuY3Rpb24gc2V0dXBIaWdoY2hhcnRzKCkge1xyXG4gIEhpZ2hjaGFydHMuYW5pbU9iamVjdCA9IGZ1bmN0aW9uICgpIHtcclxuICAgIHJldHVybiB7IGR1cmF0aW9uOiAwIH07XHJcbiAgfTtcclxufVxyXG5cclxuLyoqXHJcbiAqIENyZWF0ZXMgdGhlIGFjdHVhbCBjaGFydC5cclxuICpcclxuICogQHBhcmFtIHtvYmplY3R9IGNoYXJ0T3B0aW9ucyAtIFRoZSBvcHRpb25zIGZvciB0aGUgSGlnaGNoYXJ0cyBjaGFydC5cclxuICogQHBhcmFtIHtvYmplY3R9IG9wdGlvbnMgLSBUaGUgZXhwb3J0IG9wdGlvbnMuXHJcbiAqIEBwYXJhbSB7Ym9vbGVhbn0gZGlzcGxheUVycm9ycyAtIEEgZmxhZyBpbmRpY2F0aW5nIHdoZXRoZXIgdG8gZGlzcGxheSBlcnJvcnMuXHJcbiAqL1xyXG5leHBvcnQgYXN5bmMgZnVuY3Rpb24gdHJpZ2dlckV4cG9ydChjaGFydE9wdGlvbnMsIG9wdGlvbnMsIGRpc3BsYXlFcnJvcnMpIHtcclxuICAvLyBEaXNwbGF5IGVycm9ycyBmbGFnIHRha2VuIGZyb20gY2hhcnQgb3B0aW9ucyBuYWQgZGVidWdnZXIgbW9kdWxlXHJcbiAgd2luZG93Ll9kaXNwbGF5RXJyb3JzID0gZGlzcGxheUVycm9ycztcclxuXHJcbiAgLy8gR2V0IHJlcXVpcmVkIGZ1bmN0aW9uc1xyXG4gIGNvbnN0IHsgZ2V0T3B0aW9ucywgbWVyZ2UsIHNldE9wdGlvbnMsIHdyYXAgfSA9IEhpZ2hjaGFydHM7XHJcblxyXG4gIC8vIENyZWF0ZSBhIHNlcGFyYXRlIG9iamVjdCBmb3IgYSBwb3RlbnRpYWwgc2V0T3B0aW9ucyB1c2FnZXMgaW4gb3JkZXIgdG9cclxuICAvLyBwcmV2ZW50IGZyb20gcG9sbHV0aW5nIG90aGVyIGV4cG9ydHMgdGhhdCBjYW4gaGFwcGVuIG9uIHRoZSBzYW1lIHBhZ2VcclxuICBIaWdoY2hhcnRzLnNldE9wdGlvbnNPYmogPSBtZXJnZShmYWxzZSwge30sIGdldE9wdGlvbnMoKSk7XHJcblxyXG4gIC8vIFRyaWdnZXIgY3VzdG9tIGNvZGVcclxuICBpZiAob3B0aW9ucy5jdXN0b21Mb2dpYy5jdXN0b21Db2RlKSB7XHJcbiAgICBuZXcgRnVuY3Rpb24ob3B0aW9ucy5jdXN0b21Mb2dpYy5jdXN0b21Db2RlKSgpO1xyXG4gIH1cclxuXHJcbiAgLy8gQnkgZGVmYXVsdCBhbmltYXRpb24gaXMgZGlzYWJsZWRcclxuICBjb25zdCBjaGFydCA9IHtcclxuICAgIGFuaW1hdGlvbjogZmFsc2VcclxuICB9O1xyXG5cclxuICAvLyBXaGVuIHN0cmFpZ2h0IGluamVjdCwgdGhlIHNpemUgaXMgc2V0IHRocm91Z2ggQ1NTIG9ubHlcclxuICBpZiAob3B0aW9ucy5leHBvcnQuc3RySW5qKSB7XHJcbiAgICBjaGFydC5oZWlnaHQgPSBjaGFydE9wdGlvbnMuY2hhcnQuaGVpZ2h0O1xyXG4gICAgY2hhcnQud2lkdGggPSBjaGFydE9wdGlvbnMuY2hhcnQud2lkdGg7XHJcbiAgfVxyXG5cclxuICAvLyBOT1RFOiBJcyB0aGlzIHVzZWQgZm9yIGFueXRoaW5nIHVzZWZ1bD9cclxuICB3aW5kb3cuaXNSZW5kZXJDb21wbGV0ZSA9IGZhbHNlO1xyXG4gIHdyYXAoSGlnaGNoYXJ0cy5DaGFydC5wcm90b3R5cGUsICdpbml0JywgZnVuY3Rpb24gKHByb2NlZWQsIHVzZXJPcHRpb25zLCBjYikge1xyXG4gICAgLy8gT3ZlcnJpZGUgdXNlck9wdGlvbnMgd2l0aCBpbWFnZSBmcmllbmRseSBvcHRpb25zXHJcbiAgICB1c2VyT3B0aW9ucyA9IG1lcmdlKHVzZXJPcHRpb25zLCB7XHJcbiAgICAgIGV4cG9ydGluZzoge1xyXG4gICAgICAgIGVuYWJsZWQ6IGZhbHNlXHJcbiAgICAgIH0sXHJcbiAgICAgIHBsb3RPcHRpb25zOiB7XHJcbiAgICAgICAgc2VyaWVzOiB7XHJcbiAgICAgICAgICBsYWJlbDoge1xyXG4gICAgICAgICAgICBlbmFibGVkOiBmYWxzZVxyXG4gICAgICAgICAgfVxyXG4gICAgICAgIH1cclxuICAgICAgfSxcclxuICAgICAgLyogRXhwZWN0cyB0b29sdGlwIGluIHVzZXJPcHRpb25zIHdoZW4gZm9yRXhwb3J0IGlzIHRydWUuXHJcbiAgICAgICAgaHR0cHM6Ly9naXRodWIuY29tL2hpZ2hjaGFydHMvaGlnaGNoYXJ0cy9ibG9iLzNhZDQzMGEzNTNiODA1NmI5ZTc2NGFhNGU1Y2Q2ODI4YWE0NzlkYjIvanMvcGFydHMvQ2hhcnQuanMjTDI0MVxyXG4gICAgICAgICovXHJcbiAgICAgIHRvb2x0aXA6IHt9XHJcbiAgICB9KTtcclxuXHJcbiAgICAodXNlck9wdGlvbnMuc2VyaWVzIHx8IFtdKS5mb3JFYWNoKGZ1bmN0aW9uIChzZXJpZXMpIHtcclxuICAgICAgc2VyaWVzLmFuaW1hdGlvbiA9IGZhbHNlO1xyXG4gICAgfSk7XHJcblxyXG4gICAgLy8gQWRkIGZsYWcgdG8ga25vdyBpZiBjaGFydCByZW5kZXIgaGFzIGJlZW4gY2FsbGVkLlxyXG4gICAgaWYgKCF3aW5kb3cub25IaWdoY2hhcnRzUmVuZGVyKSB7XHJcbiAgICAgIHdpbmRvdy5vbkhpZ2hjaGFydHNSZW5kZXIgPSBIaWdoY2hhcnRzLmFkZEV2ZW50KHRoaXMsICdyZW5kZXInLCAoKSA9PiB7XHJcbiAgICAgICAgd2luZG93LmlzUmVuZGVyQ29tcGxldGUgPSB0cnVlO1xyXG4gICAgICB9KTtcclxuICAgIH1cclxuXHJcbiAgICBwcm9jZWVkLmFwcGx5KHRoaXMsIFt1c2VyT3B0aW9ucywgY2JdKTtcclxuICB9KTtcclxuXHJcbiAgd3JhcChIaWdoY2hhcnRzLlNlcmllcy5wcm90b3R5cGUsICdpbml0JywgZnVuY3Rpb24gKHByb2NlZWQsIGNoYXJ0LCBvcHRpb25zKSB7XHJcbiAgICBwcm9jZWVkLmFwcGx5KHRoaXMsIFtjaGFydCwgb3B0aW9uc10pO1xyXG4gIH0pO1xyXG5cclxuICAvLyBHZXQgdGhlIHVzZXIgb3B0aW9uc1xyXG4gIGNvbnN0IHVzZXJPcHRpb25zID0gb3B0aW9ucy5leHBvcnQuc3RySW5qXHJcbiAgICA/IG5ldyBGdW5jdGlvbihgcmV0dXJuICR7b3B0aW9ucy5leHBvcnQuc3RySW5qfWApKClcclxuICAgIDogY2hhcnRPcHRpb25zO1xyXG5cclxuICAvLyBNZXJnZSB0aGUgZ2xvYmFsT3B0aW9ucywgdGhlbWVPcHRpb25zLCBvcHRpb25zIGZyb20gdGhlIHdyYXBwZWRcclxuICAvLyBzZXRPcHRpb25zIGZ1bmN0aW9uIGFuZCB1c2VyIG9wdGlvbnMgdG8gY3JlYXRlIHRoZSBmaW5hbCBvcHRpb25zIG9iamVjdFxyXG4gIGNvbnN0IGZpbmFsT3B0aW9ucyA9IG1lcmdlKFxyXG4gICAgZmFsc2UsXHJcbiAgICBKU09OLnBhcnNlKG9wdGlvbnMuZXhwb3J0LnRoZW1lT3B0aW9ucyksXHJcbiAgICB1c2VyT3B0aW9ucyxcclxuICAgIC8vIFBsYWNlZCBpdCBoZXJlIGluc3RlYWQgaW4gdGhlIGluaXQgYmVjYXVzZSBvZiB0aGUgc2l6ZSBpc3N1ZXNcclxuICAgIHsgY2hhcnQgfVxyXG4gICk7XHJcblxyXG4gIGNvbnN0IGZpbmFsQ2FsbGJhY2sgPSBvcHRpb25zLmN1c3RvbUxvZ2ljLmNhbGxiYWNrXHJcbiAgICA/IG5ldyBGdW5jdGlvbihgcmV0dXJuICR7b3B0aW9ucy5jdXN0b21Mb2dpYy5jYWxsYmFja31gKSgpXHJcbiAgICA6IHVuZGVmaW5lZDtcclxuXHJcbiAgLy8gU2V0IHRoZSBnbG9iYWwgb3B0aW9ucyBpZiBleGlzdFxyXG4gIGNvbnN0IGdsb2JhbE9wdGlvbnMgPSBKU09OLnBhcnNlKG9wdGlvbnMuZXhwb3J0Lmdsb2JhbE9wdGlvbnMpO1xyXG4gIGlmIChnbG9iYWxPcHRpb25zKSB7XHJcbiAgICBzZXRPcHRpb25zKGdsb2JhbE9wdGlvbnMpO1xyXG4gIH1cclxuXHJcbiAgSGlnaGNoYXJ0c1tvcHRpb25zLmV4cG9ydC5jb25zdHIgfHwgJ2NoYXJ0J10oXHJcbiAgICAnY29udGFpbmVyJyxcclxuICAgIGZpbmFsT3B0aW9ucyxcclxuICAgIGZpbmFsQ2FsbGJhY2tcclxuICApO1xyXG5cclxuICAvLyBHZXQgdGhlIGN1cnJlbnQgZ2xvYmFsIG9wdGlvbnNcclxuICBjb25zdCBkZWZhdWx0T3B0aW9ucyA9IGdldE9wdGlvbnMoKTtcclxuXHJcbiAgLy8gQ2xlYXIgaXQganVzdCBpbiBjYXNlIChlLmcuIHRoZSBzZXRPcHRpb25zIHdhcyB1c2VkIGluIHRoZSBjdXN0b21Db2RlKVxyXG4gIGZvciAoY29uc3QgcHJvcCBpbiBkZWZhdWx0T3B0aW9ucykge1xyXG4gICAgaWYgKHR5cGVvZiBkZWZhdWx0T3B0aW9uc1twcm9wXSAhPT0gJ2Z1bmN0aW9uJykge1xyXG4gICAgICBkZWxldGUgZGVmYXVsdE9wdGlvbnNbcHJvcF07XHJcbiAgICB9XHJcbiAgfVxyXG5cclxuICAvLyBTZXQgdGhlIGRlZmF1bHQgb3B0aW9ucyBiYWNrXHJcbiAgc2V0T3B0aW9ucyhIaWdoY2hhcnRzLnNldE9wdGlvbnNPYmopO1xyXG5cclxuICAvLyBFbXB0eSB0aGUgY3VzdG9tIGdsb2JhbCBvcHRpb25zIG9iamVjdFxyXG4gIEhpZ2hjaGFydHMuc2V0T3B0aW9uc09iaiA9IHt9O1xyXG59XHJcbiIsIi8qKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqXHJcblxyXG5IaWdoY2hhcnRzIEV4cG9ydCBTZXJ2ZXJcclxuXHJcbkNvcHlyaWdodCAoYykgMjAxNi0yMDI0LCBIaWdoc29mdFxyXG5cclxuTGljZW5jZWQgdW5kZXIgdGhlIE1JVCBsaWNlbmNlLlxyXG5cclxuQWRkaXRpb25hbGx5IGEgdmFsaWQgSGlnaGNoYXJ0cyBsaWNlbnNlIGlzIHJlcXVpcmVkIGZvciB1c2UuXHJcblxyXG5TZWUgTElDRU5TRSBmaWxlIGluIHJvb3QgZm9yIGRldGFpbHMuXHJcblxyXG4qKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqL1xyXG5cclxuaW1wb3J0IHsgcmVhZEZpbGVTeW5jIH0gZnJvbSAnZnMnO1xyXG5pbXBvcnQgcGF0aCBmcm9tICdwYXRoJztcclxuXHJcbmltcG9ydCBwdXBwZXRlZXIgZnJvbSAncHVwcGV0ZWVyJztcclxuXHJcbmltcG9ydCB7IGdldENhY2hlUGF0aCB9IGZyb20gJy4vY2FjaGUuanMnO1xyXG5pbXBvcnQgeyBnZXRPcHRpb25zIH0gZnJvbSAnLi9jb25maWcuanMnO1xyXG5pbXBvcnQgeyBzZXR1cEhpZ2hjaGFydHMgfSBmcm9tICcuL2hpZ2hjaGFydHMuanMnO1xyXG5pbXBvcnQgeyBsb2csIGxvZ1dpdGhTdGFjayB9IGZyb20gJy4vbG9nZ2VyLmpzJztcclxuaW1wb3J0IHsgX19kaXJuYW1lIH0gZnJvbSAnLi91dGlscy5qcyc7XHJcblxyXG5pbXBvcnQgRXhwb3J0RXJyb3IgZnJvbSAnLi9lcnJvcnMvRXhwb3J0RXJyb3IuanMnO1xyXG5cclxuLy8gR2V0IHRoZSB0ZW1wbGF0ZSBmb3IgdGhlIHBhZ2VcclxuY29uc3QgdGVtcGxhdGUgPSByZWFkRmlsZVN5bmMoX19kaXJuYW1lICsgJy90ZW1wbGF0ZXMvdGVtcGxhdGUuaHRtbCcsICd1dGY4Jyk7XHJcblxyXG5sZXQgYnJvd3NlcjtcclxuXHJcbi8qKlxyXG4gKiBSZXRyaWV2ZXMgdGhlIGV4aXN0aW5nIFB1cHBldGVlciBicm93c2VyIGluc3RhbmNlLlxyXG4gKlxyXG4gKiBAcmV0dXJucyB7UHJvbWlzZTxvYmplY3Q+fSBBIFByb21pc2UgcmVzb2x2aW5nIHRvIHRoZSBQdXBwZXRlZXIgYnJvd3NlclxyXG4gKiBpbnN0YW5jZS5cclxuICpcclxuICogQHRocm93cyB7RXhwb3J0RXJyb3J9IFRocm93cyBhbiBFeHBvcnRFcnJvciBpZiBubyB2YWxpZCBicm93c2VyIGhhcyBiZWVuXHJcbiAqIGNyZWF0ZWQuXHJcbiAqL1xyXG5leHBvcnQgZnVuY3Rpb24gZ2V0KCkge1xyXG4gIGlmICghYnJvd3Nlcikge1xyXG4gICAgdGhyb3cgbmV3IEV4cG9ydEVycm9yKCdbYnJvd3Nlcl0gTm8gdmFsaWQgYnJvd3NlciBoYXMgYmVlbiBjcmVhdGVkLicpO1xyXG4gIH1cclxuICByZXR1cm4gYnJvd3NlcjtcclxufVxyXG5cclxuLyoqXHJcbiAqIENyZWF0ZXMgYSBQdXBwZXRlZXIgYnJvd3NlciBpbnN0YW5jZSB3aXRoIHRoZSBzcGVjaWZpZWQgYXJndW1lbnRzLlxyXG4gKlxyXG4gKiBAcGFyYW0ge0FycmF5fSBwdXBwZXRlZXJBcmdzIC0gQWRkaXRpb25hbCBhcmd1bWVudHMgZm9yIFB1cHBldGVlciBsYXVuY2guXHJcbiAqXHJcbiAqIEByZXR1cm5zIHtQcm9taXNlPG9iamVjdD59IEEgUHJvbWlzZSByZXNvbHZpbmcgdG8gdGhlIFB1cHBldGVlciBicm93c2VyXHJcbiAqIGluc3RhbmNlLlxyXG4gKlxyXG4gKiBAdGhyb3dzIHtFeHBvcnRFcnJvcn0gVGhyb3dzIGFuIEV4cG9ydEVycm9yIGlmIG1heCByZXRyaWVzIHRvIG9wZW4gYSBicm93c2VyXHJcbiAqIGluc3RhbmNlIGFyZSByZWFjaGVkLCBvciBpZiBubyBicm93c2VyIGluc3RhbmNlIGlzIGZvdW5kIGFmdGVyIHJldHJpZXMuXHJcbiAqL1xyXG5leHBvcnQgYXN5bmMgZnVuY3Rpb24gY3JlYXRlKHB1cHBldGVlckFyZ3MpIHtcclxuICAvLyBHZXQgdGhlIGRlYnVnIG9wdGlvbnNcclxuICBjb25zdCB7IGVuYWJsZTogZW5hYmxlZERlYnVnLCAuLi5kZWJ1ZyB9ID0gZ2V0T3B0aW9ucygpLmRlYnVnO1xyXG4gIGNvbnN0IGxhdW5jaE9wdGlvbnMgPSB7XHJcbiAgICBoZWFkbGVzczogJ3NoZWxsJyxcclxuICAgIHVzZXJEYXRhRGlyOiAnLi90bXAvJyxcclxuICAgIGFyZ3M6IHB1cHBldGVlckFyZ3MsXHJcbiAgICBoYW5kbGVTSUdJTlQ6IGZhbHNlLFxyXG4gICAgaGFuZGxlU0lHVEVSTTogZmFsc2UsXHJcbiAgICBoYW5kbGVTSUdIVVA6IGZhbHNlLFxyXG4gICAgd2FpdEZvckluaXRpYWxQYWdlOiBmYWxzZSxcclxuICAgIGRlZmF1bHRWaWV3cG9ydDogbnVsbCxcclxuICAgIC4uLihlbmFibGVkRGVidWcgJiYgZGVidWcpXHJcbiAgfTtcclxuXHJcbiAgLy8gQ3JlYXRlIGEgYnJvd3NlclxyXG4gIGlmICghYnJvd3Nlcikge1xyXG4gICAgbGV0IHRyeUNvdW50ID0gMDtcclxuXHJcbiAgICBjb25zdCBvcGVuID0gYXN5bmMgKCkgPT4ge1xyXG4gICAgICB0cnkge1xyXG4gICAgICAgIGxvZyhcclxuICAgICAgICAgIDMsXHJcbiAgICAgICAgICBgW2Jyb3dzZXJdIEF0dGVtcHRpbmcgdG8gZ2V0IGEgYnJvd3NlciBpbnN0YW5jZSAodHJ5ICR7Kyt0cnlDb3VudH0pLmBcclxuICAgICAgICApO1xyXG4gICAgICAgIGJyb3dzZXIgPSBhd2FpdCBwdXBwZXRlZXIubGF1bmNoKGxhdW5jaE9wdGlvbnMpO1xyXG4gICAgICB9IGNhdGNoIChlcnJvcikge1xyXG4gICAgICAgIGxvZ1dpdGhTdGFjayhcclxuICAgICAgICAgIDEsXHJcbiAgICAgICAgICBlcnJvcixcclxuICAgICAgICAgICdbYnJvd3Nlcl0gRmFpbGVkIHRvIGxhdW5jaCBhIGJyb3dzZXIgaW5zdGFuY2UuJ1xyXG4gICAgICAgICk7XHJcblxyXG4gICAgICAgIC8vIFJldHJ5IHRvIGxhdW5jaCBicm93c2VyIHVudGlsIHJlYWNoaW5nIG1heCBhdHRlbXB0c1xyXG4gICAgICAgIGlmICh0cnlDb3VudCA8IDI1KSB7XHJcbiAgICAgICAgICBsb2coMywgYFticm93c2VyXSBSZXRyeSB0byBvcGVuIGEgYnJvd3NlciAoJHt0cnlDb3VudH0gb3V0IG9mIDI1KS5gKTtcclxuICAgICAgICAgIGF3YWl0IG5ldyBQcm9taXNlKChyZXNwb25zZSkgPT4gc2V0VGltZW91dChyZXNwb25zZSwgNDAwMCkpO1xyXG4gICAgICAgICAgYXdhaXQgb3BlbigpO1xyXG4gICAgICAgIH0gZWxzZSB7XHJcbiAgICAgICAgICB0aHJvdyBlcnJvcjtcclxuICAgICAgICB9XHJcbiAgICAgIH1cclxuICAgIH07XHJcblxyXG4gICAgdHJ5IHtcclxuICAgICAgYXdhaXQgb3BlbigpO1xyXG4gICAgICAvLyBEZWJ1ZyBtb2RlIGluZm9ybVxyXG4gICAgICBpZiAoZW5hYmxlZERlYnVnKSB7XHJcbiAgICAgICAgbG9nKDMsIGBbYnJvd3Nlcl0gTGF1bmNoZWQgYnJvd3NlciBpbiBkZWJ1ZyBtb2RlLmApO1xyXG4gICAgICB9XHJcbiAgICB9IGNhdGNoIChlcnJvcikge1xyXG4gICAgICB0aHJvdyBuZXcgRXhwb3J0RXJyb3IoXHJcbiAgICAgICAgJ1ticm93c2VyXSBNYXhpbXVtIHJldHJpZXMgdG8gb3BlbiBhIGJyb3dzZXIgaW5zdGFuY2UgcmVhY2hlZC4nXHJcbiAgICAgICkuc2V0RXJyb3IoZXJyb3IpO1xyXG4gICAgfVxyXG5cclxuICAgIGlmICghYnJvd3Nlcikge1xyXG4gICAgICB0aHJvdyBuZXcgRXhwb3J0RXJyb3IoJ1ticm93c2VyXSBDYW5ub3QgZmluZCBhIGJyb3dzZXIgdG8gb3Blbi4nKTtcclxuICAgIH1cclxuICB9XHJcblxyXG4gIC8vIFJldHVybiBhIGJyb3dzZXIgcHJvbWlzZVxyXG4gIHJldHVybiBicm93c2VyO1xyXG59XHJcblxyXG4vKipcclxuICogQ2xvc2VzIHRoZSBQdXBwZXRlZXIgYnJvd3NlciBpbnN0YW5jZSBpZiBpdCBpcyBjb25uZWN0ZWQuXHJcbiAqXHJcbiAqIEByZXR1cm5zIHtQcm9taXNlPGJvb2xlYW4+fSBBIFByb21pc2UgcmVzb2x2aW5nIHRvIHRydWUgYWZ0ZXIgdGhlIGJyb3dzZXJcclxuICogaXMgY2xvc2VkLlxyXG4gKi9cclxuZXhwb3J0IGFzeW5jIGZ1bmN0aW9uIGNsb3NlKCkge1xyXG4gIC8vIENsb3NlIHRoZSBicm93c2VyIHdoZW4gY29ubm5lY3RlZFxyXG4gIGlmIChicm93c2VyPy5jb25uZWN0ZWQpIHtcclxuICAgIGF3YWl0IGJyb3dzZXIuY2xvc2UoKTtcclxuICB9XHJcbiAgbG9nKDQsICdbYnJvd3Nlcl0gQ2xvc2VkIHRoZSBicm93c2VyLicpO1xyXG59XHJcblxyXG4vKipcclxuICogQ3JlYXRlcyBhIG5ldyBQdXBwZXRlZXIgUGFnZSB3aXRoaW4gYW4gZXhpc3RpbmcgYnJvd3NlciBpbnN0YW5jZS5cclxuICpcclxuICogSWYgdGhlIGJyb3dzZXIgaW5zdGFuY2UgaXMgbm90IGF2YWlsYWJsZSwgcmV0dXJucyBmYWxzZS5cclxuICpcclxuICogVGhlIGZ1bmN0aW9uIGNyZWF0ZXMgYSBuZXcgcGFnZSwgZGlzYWJsZXMgY2FjaGluZywgc2V0cyBjb250ZW50IHVzaW5nXHJcbiAqIHNldFBhZ2VDb250ZW50KCksIGFuZCByZXR1cm5zIHRoZSBjcmVhdGVkIFB1cHBldGVlciBQYWdlLlxyXG4gKlxyXG4gKiBAcmV0dXJucyB7KGJvb2xlYW58b2JqZWN0KX0gUmV0dXJucyBmYWxzZSBpZiB0aGUgYnJvd3NlciBpbnN0YW5jZSBpcyBub3RcclxuICogYXZhaWxhYmxlLCBvciBhIFB1cHBldGVlciBQYWdlIG9iamVjdCByZXByZXNlbnRpbmcgdGhlIG5ld2x5IGNyZWF0ZWQgcGFnZS5cclxuICovXHJcbmV4cG9ydCBhc3luYyBmdW5jdGlvbiBuZXdQYWdlKCkge1xyXG4gIGlmICghYnJvd3Nlcikge1xyXG4gICAgcmV0dXJuIGZhbHNlO1xyXG4gIH1cclxuXHJcbiAgLy8gQ3JlYXRlIGEgcGFnZVxyXG4gIGNvbnN0IHBhZ2UgPSBhd2FpdCBicm93c2VyLm5ld1BhZ2UoKTtcclxuXHJcbiAgLy8gRGlzYWJsZSBjYWNoZVxyXG4gIGF3YWl0IHBhZ2Uuc2V0Q2FjaGVFbmFibGVkKGZhbHNlKTtcclxuXHJcbiAgLy8gU2V0IHRoZSBjb250ZW50XHJcbiAgYXdhaXQgc2V0UGFnZUNvbnRlbnQocGFnZSk7XHJcblxyXG4gIC8vIFNldCBwYWdlIGV2ZW50c1xyXG4gIHNldFBhZ2VFdmVudHMocGFnZSk7XHJcblxyXG4gIHJldHVybiBwYWdlO1xyXG59XHJcblxyXG4vKipcclxuICogQ2xlYXJzIHRoZSBjb250ZW50IG9mIGEgUHVwcGV0ZWVyIFBhZ2UgYmFzZWQgb24gdGhlIHNwZWNpZmllZCBtb2RlLlxyXG4gKlxyXG4gKiBAcGFyYW0ge09iamVjdH0gcGFnZSAtIFRoZSBQdXBwZXRlZXIgUGFnZSBvYmplY3QgdG8gYmUgY2xlYXJlZC5cclxuICogQHBhcmFtIHtib29sZWFufSBoYXJkUmVzZXQgLSBBIGZsYWcgaW5kaWNhdGluZyB0aGUgdHlwZSBvZiBjbGVhcmluZ1xyXG4gKiB0byBiZSBwZXJmb3JtZWQuIElmIHRydWUsIG5hdmlnYXRlcyB0byAnYWJvdXQ6YmxhbmsnIGFuZCByZXNldHMgY29udGVudFxyXG4gKiBhbmQgc2NyaXB0cy4gSWYgZmFsc2UsIGNsZWFycyB0aGUgYm9keSBjb250ZW50IGJ5IHNldHRpbmcgYSBwcmVkZWZpbmVkIEhUTUxcclxuICogc3RydWN0dXJlLlxyXG4gKlxyXG4gKiBAdGhyb3dzIHtFcnJvcn0gTG9ncyB0aHJvd24gZXJyb3IgaWYgY2xlYXJpbmcgdGhlIHBhZ2UgY29udGVudCBmYWlscy5cclxuICovXHJcbmV4cG9ydCBhc3luYyBmdW5jdGlvbiBjbGVhclBhZ2UocGFnZSwgaGFyZFJlc2V0ID0gZmFsc2UpIHtcclxuICB0cnkge1xyXG4gICAgaWYgKCFwYWdlLmlzQ2xvc2VkKCkpIHtcclxuICAgICAgaWYgKGhhcmRSZXNldCkge1xyXG4gICAgICAgIC8vIE5hdmlnYXRlIHRvIGFib3V0OmJsYW5rXHJcbiAgICAgICAgYXdhaXQgcGFnZS5nb3RvKCdhYm91dDpibGFuaycsIHsgd2FpdFVudGlsOiAnZG9tY29udGVudGxvYWRlZCcgfSk7XHJcblxyXG4gICAgICAgIC8vIFNldCB0aGUgY29udGVudCBhbmQgYW5kIHNjcmlwdHMgYWdhaW5cclxuICAgICAgICBhd2FpdCBzZXRQYWdlQ29udGVudChwYWdlKTtcclxuICAgICAgfSBlbHNlIHtcclxuICAgICAgICAvLyBDbGVhciBib2R5IGNvbnRlbnRcclxuICAgICAgICBhd2FpdCBwYWdlLmV2YWx1YXRlKCgpID0+IHtcclxuICAgICAgICAgIGRvY3VtZW50LmJvZHkuaW5uZXJIVE1MID1cclxuICAgICAgICAgICAgJzxkaXYgaWQ9XCJjaGFydC1jb250YWluZXJcIj48ZGl2IGlkPVwiY29udGFpbmVyXCI+PC9kaXY+PC9kaXY+JztcclxuICAgICAgICB9KTtcclxuICAgICAgfVxyXG4gICAgfVxyXG4gIH0gY2F0Y2ggKGVycm9yKSB7XHJcbiAgICBsb2dXaXRoU3RhY2soXHJcbiAgICAgIDIsXHJcbiAgICAgIGVycm9yLFxyXG4gICAgICAnW2Jyb3dzZXJdIENvdWxkIG5vdCBjbGVhciB0aGUgY29udGVudCBvZiB0aGUgcGFnZS4nXHJcbiAgICApO1xyXG4gIH1cclxufVxyXG5cclxuLyoqXHJcbiAqIEFkZHMgY3VzdG9tIEpTIGFuZCBDU1MgcmVzb3VyY2VzIHRvIGEgUHVwcGV0ZWVyIFBhZ2UgYmFzZWQgb24gdGhlIHNwZWNpZmllZFxyXG4gKiBvcHRpb25zLlxyXG4gKlxyXG4gKiBAcGFyYW0ge09iamVjdH0gcGFnZSAtIFRoZSBQdXBwZXRlZXIgUGFnZSBvYmplY3QgdG8gd2hpY2ggcmVzb3VyY2VzIHdpbGwgYmVcclxuICogYWRkZWQuXHJcbiAqIEBwYXJhbSB7T2JqZWN0fSBvcHRpb25zIC0gQWxsIG9wdGlvbnMgYW5kIGNvbmZpZ3VyYXRpb24uXHJcbiAqXHJcbiAqIEByZXR1cm5zIHtQcm9taXNlPEFycmF5PE9iamVjdD4+fSAtIFByb21pc2UgcmVzb2x2aW5nIHRvIGFuIGFycmF5IG9mIGluamVjdGVkXHJcbiAqIHJlc291cmNlcy5cclxuICovXHJcbmV4cG9ydCBhc3luYyBmdW5jdGlvbiBhZGRQYWdlUmVzb3VyY2VzKHBhZ2UsIG9wdGlvbnMpIHtcclxuICAvLyBJbmplY3RlZCByZXNvdXJjZXMgYXJyYXlcclxuICBjb25zdCBpbmplY3RlZFJlc291cmNlcyA9IFtdO1xyXG5cclxuICAvLyBVc2UgcmVzb3VyY2VzXHJcbiAgY29uc3QgcmVzb3VyY2VzID0gb3B0aW9ucy5jdXN0b21Mb2dpYy5yZXNvdXJjZXM7XHJcbiAgaWYgKHJlc291cmNlcykge1xyXG4gICAgY29uc3QgaW5qZWN0ZWRKcyA9IFtdO1xyXG5cclxuICAgIC8vIExvYWQgY3VzdG9tIEpTIGNvZGVcclxuICAgIGlmIChyZXNvdXJjZXMuanMpIHtcclxuICAgICAgaW5qZWN0ZWRKcy5wdXNoKHtcclxuICAgICAgICBjb250ZW50OiByZXNvdXJjZXMuanNcclxuICAgICAgfSk7XHJcbiAgICB9XHJcblxyXG4gICAgLy8gTG9hZCBzY3JpcHRzIGZyb20gYWxsIGN1c3RvbSBmaWxlc1xyXG4gICAgaWYgKHJlc291cmNlcy5maWxlcykge1xyXG4gICAgICBmb3IgKGNvbnN0IGZpbGUgb2YgcmVzb3VyY2VzLmZpbGVzKSB7XHJcbiAgICAgICAgY29uc3QgaXNMb2NhbCA9ICFmaWxlLnN0YXJ0c1dpdGgoJ2h0dHAnKSA/IHRydWUgOiBmYWxzZTtcclxuXHJcbiAgICAgICAgLy8gQWRkIGVhY2ggY3VzdG9tIHNjcmlwdCBmcm9tIHJlc291cmNlcycgZmlsZXNcclxuICAgICAgICBpbmplY3RlZEpzLnB1c2goXHJcbiAgICAgICAgICBpc0xvY2FsXHJcbiAgICAgICAgICAgID8ge1xyXG4gICAgICAgICAgICAgICAgY29udGVudDogcmVhZEZpbGVTeW5jKGZpbGUsICd1dGY4JylcclxuICAgICAgICAgICAgICB9XHJcbiAgICAgICAgICAgIDoge1xyXG4gICAgICAgICAgICAgICAgdXJsOiBmaWxlXHJcbiAgICAgICAgICAgICAgfVxyXG4gICAgICAgICk7XHJcbiAgICAgIH1cclxuICAgIH1cclxuXHJcbiAgICBmb3IgKGNvbnN0IGpzUmVzb3VyY2Ugb2YgaW5qZWN0ZWRKcykge1xyXG4gICAgICB0cnkge1xyXG4gICAgICAgIGluamVjdGVkUmVzb3VyY2VzLnB1c2goYXdhaXQgcGFnZS5hZGRTY3JpcHRUYWcoanNSZXNvdXJjZSkpO1xyXG4gICAgICB9IGNhdGNoIChlcnJvcikge1xyXG4gICAgICAgIGxvZ1dpdGhTdGFjaygyLCBlcnJvciwgYFtleHBvcnRdIFRoZSBKUyByZXNvdXJjZSBjYW5ub3QgYmUgbG9hZGVkLmApO1xyXG4gICAgICB9XHJcbiAgICB9XHJcbiAgICBpbmplY3RlZEpzLmxlbmd0aCA9IDA7XHJcblxyXG4gICAgLy8gTG9hZCBDU1NcclxuICAgIGNvbnN0IGluamVjdGVkQ3NzID0gW107XHJcbiAgICBpZiAocmVzb3VyY2VzLmNzcykge1xyXG4gICAgICBsZXQgY3NzSW1wb3J0cyA9IHJlc291cmNlcy5jc3MubWF0Y2goL0BpbXBvcnRcXHMqKFteO10qKTsvZyk7XHJcbiAgICAgIGlmIChjc3NJbXBvcnRzKSB7XHJcbiAgICAgICAgLy8gSGFuZGxlIGNzcyBzZWN0aW9uXHJcbiAgICAgICAgZm9yIChsZXQgY3NzSW1wb3J0UGF0aCBvZiBjc3NJbXBvcnRzKSB7XHJcbiAgICAgICAgICBpZiAoY3NzSW1wb3J0UGF0aCkge1xyXG4gICAgICAgICAgICBjc3NJbXBvcnRQYXRoID0gY3NzSW1wb3J0UGF0aFxyXG4gICAgICAgICAgICAgIC5yZXBsYWNlKCd1cmwoJywgJycpXHJcbiAgICAgICAgICAgICAgLnJlcGxhY2UoJ0BpbXBvcnQnLCAnJylcclxuICAgICAgICAgICAgICAucmVwbGFjZSgvXCIvZywgJycpXHJcbiAgICAgICAgICAgICAgLnJlcGxhY2UoLycvZywgJycpXHJcbiAgICAgICAgICAgICAgLnJlcGxhY2UoLzsvLCAnJylcclxuICAgICAgICAgICAgICAucmVwbGFjZSgvXFwpL2csICcnKVxyXG4gICAgICAgICAgICAgIC50cmltKCk7XHJcblxyXG4gICAgICAgICAgICAvLyBBZGQgZWFjaCBjdXN0b20gY3NzIGZyb20gcmVzb3VyY2VzXHJcbiAgICAgICAgICAgIGlmIChjc3NJbXBvcnRQYXRoLnN0YXJ0c1dpdGgoJ2h0dHAnKSkge1xyXG4gICAgICAgICAgICAgIGluamVjdGVkQ3NzLnB1c2goe1xyXG4gICAgICAgICAgICAgICAgdXJsOiBjc3NJbXBvcnRQYXRoXHJcbiAgICAgICAgICAgICAgfSk7XHJcbiAgICAgICAgICAgIH0gZWxzZSBpZiAob3B0aW9ucy5jdXN0b21Mb2dpYy5hbGxvd0ZpbGVSZXNvdXJjZXMpIHtcclxuICAgICAgICAgICAgICBpbmplY3RlZENzcy5wdXNoKHtcclxuICAgICAgICAgICAgICAgIHBhdGg6IHBhdGguam9pbihfX2Rpcm5hbWUsIGNzc0ltcG9ydFBhdGgpXHJcbiAgICAgICAgICAgICAgfSk7XHJcbiAgICAgICAgICAgIH1cclxuICAgICAgICAgIH1cclxuICAgICAgICB9XHJcbiAgICAgIH1cclxuXHJcbiAgICAgIC8vIFRoZSByZXN0IG9mIHRoZSBDU1Mgc2VjdGlvbiB3aWxsIGJlIGNvbnRlbnQgYnkgbm93XHJcbiAgICAgIGluamVjdGVkQ3NzLnB1c2goe1xyXG4gICAgICAgIGNvbnRlbnQ6IHJlc291cmNlcy5jc3MucmVwbGFjZSgvQGltcG9ydFxccyooW147XSopOy9nLCAnJykgfHwgJyAnXHJcbiAgICAgIH0pO1xyXG5cclxuICAgICAgZm9yIChjb25zdCBjc3NSZXNvdXJjZSBvZiBpbmplY3RlZENzcykge1xyXG4gICAgICAgIHRyeSB7XHJcbiAgICAgICAgICBpbmplY3RlZFJlc291cmNlcy5wdXNoKGF3YWl0IHBhZ2UuYWRkU3R5bGVUYWcoY3NzUmVzb3VyY2UpKTtcclxuICAgICAgICB9IGNhdGNoIChlcnJvcikge1xyXG4gICAgICAgICAgbG9nV2l0aFN0YWNrKDIsIGVycm9yLCBgW2V4cG9ydF0gVGhlIENTUyByZXNvdXJjZSBjYW5ub3QgYmUgbG9hZGVkLmApO1xyXG4gICAgICAgIH1cclxuICAgICAgfVxyXG4gICAgICBpbmplY3RlZENzcy5sZW5ndGggPSAwO1xyXG4gICAgfVxyXG4gIH1cclxuICByZXR1cm4gaW5qZWN0ZWRSZXNvdXJjZXM7XHJcbn1cclxuXHJcbi8qKlxyXG4gKiBDbGVhcnMgb3V0IGFsbCBzdGF0ZSBzZXQgb24gdGhlIHBhZ2Ugd2l0aCBhZGRTY3JpcHRUYWcvYWRkU3R5bGVUYWcuIFJlbW92ZXNcclxuICogaW5qZWN0ZWQgcmVzb3VyY2VzIGFuZCByZXNldHMgQ1NTIGFuZCBzY3JpcHQgdGFncyBvbiB0aGUgcGFnZS4gQWRkaXRpb25hbGx5LFxyXG4gKiBpdCBkZXN0cm95cyBwcmV2aW91c2x5IGV4aXN0aW5nIGNoYXJ0cy5cclxuICpcclxuICogQHBhcmFtIHtPYmplY3R9IHBhZ2UgLSBUaGUgUHVwcGV0ZWVyIFBhZ2Ugb2JqZWN0IGZyb20gd2hpY2ggcmVzb3VyY2VzIHdpbGxcclxuICogYmUgY2xlYXJlZC5cclxuICogQHBhcmFtIHtBcnJheTxPYmplY3Q+fSBpbmplY3RlZFJlc291cmNlcyAtIEFycmF5IG9mIGluamVjdGVkIHJlc291cmNlc1xyXG4gKiB0byBiZSBjbGVhcmVkLlxyXG4gKi9cclxuZXhwb3J0IGFzeW5jIGZ1bmN0aW9uIGNsZWFyUGFnZVJlc291cmNlcyhwYWdlLCBpbmplY3RlZFJlc291cmNlcykge1xyXG4gIGZvciAoY29uc3QgcmVzb3VyY2Ugb2YgaW5qZWN0ZWRSZXNvdXJjZXMpIHtcclxuICAgIGF3YWl0IHJlc291cmNlLmRpc3Bvc2UoKTtcclxuICB9XHJcblxyXG4gIC8vIERlc3Ryb3kgb2xkIGNoYXJ0cyBhZnRlciBleHBvcnQgaXMgZG9uZSBhbmQgcmVzZXQgYWxsIENTUyBhbmQgc2NyaXB0IHRhZ3NcclxuICBhd2FpdCBwYWdlLmV2YWx1YXRlKCgpID0+IHtcclxuICAgIC8vIFdlIGFyZSBub3QgZ3VhcmFudGVlZCB0aGF0IEhpZ2hjaGFydHMgaXMgbG9hZGVkLCBlLGcsIHdoZW4gZG9pbmcgU1ZHXHJcbiAgICAvLyBleHBvcnRzXHJcbiAgICBpZiAodHlwZW9mIEhpZ2hjaGFydHMgIT09ICd1bmRlZmluZWQnKSB7XHJcbiAgICAgIC8vIGVzbGludC1kaXNhYmxlLW5leHQtbGluZSBuby11bmRlZlxyXG4gICAgICBjb25zdCBvbGRDaGFydHMgPSBIaWdoY2hhcnRzLmNoYXJ0cztcclxuXHJcbiAgICAgIC8vIENoZWNrIGluIGFueSBhbHJlYWR5IGV4aXN0aW5nIGNoYXJ0c1xyXG4gICAgICBpZiAoQXJyYXkuaXNBcnJheShvbGRDaGFydHMpICYmIG9sZENoYXJ0cy5sZW5ndGgpIHtcclxuICAgICAgICAvLyBEZXN0cm95IG9sZCBjaGFydHNcclxuICAgICAgICBmb3IgKGNvbnN0IG9sZENoYXJ0IG9mIG9sZENoYXJ0cykge1xyXG4gICAgICAgICAgb2xkQ2hhcnQgJiYgb2xkQ2hhcnQuZGVzdHJveSgpO1xyXG4gICAgICAgICAgLy8gZXNsaW50LWRpc2FibGUtbmV4dC1saW5lIG5vLXVuZGVmXHJcbiAgICAgICAgICBIaWdoY2hhcnRzLmNoYXJ0cy5zaGlmdCgpO1xyXG4gICAgICAgIH1cclxuICAgICAgfVxyXG4gICAgfVxyXG5cclxuICAgIC8vIGVzbGludC1kaXNhYmxlLW5leHQtbGluZSBuby11bmRlZlxyXG4gICAgY29uc3QgWy4uLnNjcmlwdHNUb1JlbW92ZV0gPSBkb2N1bWVudC5nZXRFbGVtZW50c0J5VGFnTmFtZSgnc2NyaXB0Jyk7XHJcbiAgICAvLyBlc2xpbnQtZGlzYWJsZS1uZXh0LWxpbmUgbm8tdW5kZWZcclxuICAgIGNvbnN0IFssIC4uLnN0eWxlc1RvUmVtb3ZlXSA9IGRvY3VtZW50LmdldEVsZW1lbnRzQnlUYWdOYW1lKCdzdHlsZScpO1xyXG4gICAgLy8gZXNsaW50LWRpc2FibGUtbmV4dC1saW5lIG5vLXVuZGVmXHJcbiAgICBjb25zdCBbLi4ubGlua3NUb1JlbW92ZV0gPSBkb2N1bWVudC5nZXRFbGVtZW50c0J5VGFnTmFtZSgnbGluaycpO1xyXG5cclxuICAgIC8vIFJlbW92ZSB0YWdzXHJcbiAgICBmb3IgKGNvbnN0IGVsZW1lbnQgb2YgW1xyXG4gICAgICAuLi5zY3JpcHRzVG9SZW1vdmUsXHJcbiAgICAgIC4uLnN0eWxlc1RvUmVtb3ZlLFxyXG4gICAgICAuLi5saW5rc1RvUmVtb3ZlXHJcbiAgICBdKSB7XHJcbiAgICAgIGVsZW1lbnQucmVtb3ZlKCk7XHJcbiAgICB9XHJcbiAgfSk7XHJcbn1cclxuXHJcbi8qKlxyXG4gKiBTZXRzIHRoZSBjb250ZW50IGZvciBhIFB1cHBldGVlciBQYWdlIHVzaW5nIGEgcHJlZGVmaW5lZCB0ZW1wbGF0ZVxyXG4gKiBhbmQgYWRkaXRpb25hbCBzY3JpcHRzLiBBbHNvLCBzZXRzIHRoZSBwYWdlZXJyb3IgaW4gb3JkZXIgdG8gY2F0Y2hcclxuICogYW5kIGRpc3BsYXkgZXJyb3JzIGZyb20gdGhlIHdpbmRvdyBjb250ZXh0LlxyXG4gKlxyXG4gKiBAcGFyYW0ge09iamVjdH0gcGFnZSAtIFRoZSBQdXBwZXRlZXIgUGFnZSBvYmplY3QgZm9yIHdoaWNoIHRoZSBjb250ZW50XHJcbiAqIGlzIGJlaW5nIHNldC5cclxuICovXHJcbmFzeW5jIGZ1bmN0aW9uIHNldFBhZ2VDb250ZW50KHBhZ2UpIHtcclxuICBhd2FpdCBwYWdlLnNldENvbnRlbnQodGVtcGxhdGUsIHsgd2FpdFVudGlsOiAnZG9tY29udGVudGxvYWRlZCcgfSk7XHJcblxyXG4gIC8vIEFkZCBhbGwgcmVnaXN0ZXJlZCBIaWdjaGFydHMgc2NyaXB0cywgcXVpdGUgZGVtYW5kaW5nXHJcbiAgYXdhaXQgcGFnZS5hZGRTY3JpcHRUYWcoeyBwYXRoOiBgJHtnZXRDYWNoZVBhdGgoKX0vc291cmNlcy5qc2AgfSk7XHJcblxyXG4gIC8vIFNldCB0aGUgaW5pdGlhbCBhbmltT2JqZWN0XHJcbiAgYXdhaXQgcGFnZS5ldmFsdWF0ZShzZXR1cEhpZ2hjaGFydHMpO1xyXG59XHJcblxyXG4vKipcclxuICogU2V0IGV2ZW50cyBmb3IgYSBQdXBwZXRlZXIgUGFnZS5cclxuICpcclxuICogQHBhcmFtIHtPYmplY3R9IHBhZ2UgLSBUaGUgUHVwcGV0ZWVyIFBhZ2Ugb2JqZWN0IHRvIHNldCBldmVudHMgdG8uXHJcbiAqL1xyXG5mdW5jdGlvbiBzZXRQYWdlRXZlbnRzKHBhZ2UpIHtcclxuICAvLyBHZXQgZGVidWcgb3B0aW9uc1xyXG4gIGNvbnN0IHsgZGVidWcgfSA9IGdldE9wdGlvbnMoKTtcclxuXHJcbiAgLy8gU2V0IHRoZSBjb25zb2xlIGxpc3RlbmVyLCBpZiBuZWVkZWRcclxuICBpZiAoZGVidWcuZW5hYmxlICYmIGRlYnVnLmxpc3RlblRvQ29uc29sZSkge1xyXG4gICAgcGFnZS5vbignY29uc29sZScsIChtZXNzYWdlKSA9PiB7XHJcbiAgICAgIGNvbnNvbGUubG9nKGBbZGVidWddICR7bWVzc2FnZS50ZXh0KCl9YCk7XHJcbiAgICB9KTtcclxuICB9XHJcblxyXG4gIC8vIFNldCB0aGUgcGFnZWVycm9yIGxpc3RlbmVyXHJcbiAgcGFnZS5vbigncGFnZWVycm9yJywgYXN5bmMgKGVycm9yKSA9PiB7XHJcbiAgICAvLyBUT0RPOiBDb25zaWRlciBhZGRpbmcgYSBzd2l0Y2ggaGVyZSB0aGF0IHR1cm5zIG9uIGxvZygwKSBsb2dnaW5nXHJcbiAgICAvLyBvbiBwYWdlIGVycm9ycy5cclxuICAgIGF3YWl0IHBhZ2UuJGV2YWwoXHJcbiAgICAgICcjY29udGFpbmVyJyxcclxuICAgICAgKGVsZW1lbnQsIGVycm9yTWVzc2FnZSkgPT4ge1xyXG4gICAgICAgIC8vIGVzbGludC1kaXNhYmxlLW5leHQtbGluZSBuby11bmRlZlxyXG4gICAgICAgIGlmICh3aW5kb3cuX2Rpc3BsYXlFcnJvcnMpIHtcclxuICAgICAgICAgIGVsZW1lbnQuaW5uZXJIVE1MID0gZXJyb3JNZXNzYWdlO1xyXG4gICAgICAgIH1cclxuICAgICAgfSxcclxuICAgICAgYDxoMT5DaGFydCBpbnB1dCBkYXRhIGVycm9yOiA8L2gxPiR7ZXJyb3IudG9TdHJpbmcoKX1gXHJcbiAgICApO1xyXG4gIH0pO1xyXG59XHJcblxyXG5leHBvcnQgZGVmYXVsdCB7XHJcbiAgZ2V0LFxyXG4gIGNyZWF0ZSxcclxuICBjbG9zZSxcclxuICBuZXdQYWdlLFxyXG4gIGNsZWFyUGFnZSxcclxuICBhZGRQYWdlUmVzb3VyY2VzLFxyXG4gIGNsZWFyUGFnZVJlc291cmNlc1xyXG59O1xyXG4iLCIvKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKlxyXG5cclxuSGlnaGNoYXJ0cyBFeHBvcnQgU2VydmVyXHJcblxyXG5Db3B5cmlnaHQgKGMpIDIwMTYtMjAyNCwgSGlnaHNvZnRcclxuXHJcbkxpY2VuY2VkIHVuZGVyIHRoZSBNSVQgbGljZW5jZS5cclxuXHJcbkFkZGl0aW9uYWxseSBhIHZhbGlkIEhpZ2hjaGFydHMgbGljZW5zZSBpcyByZXF1aXJlZCBmb3IgdXNlLlxyXG5cclxuU2VlIExJQ0VOU0UgZmlsZSBpbiByb290IGZvciBkZXRhaWxzLlxyXG5cclxuKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKi9cclxuXHJcbmltcG9ydCB7IGFkZFBhZ2VSZXNvdXJjZXMsIGNsZWFyUGFnZVJlc291cmNlcyB9IGZyb20gJy4vYnJvd3Nlci5qcyc7XHJcbmltcG9ydCB7IGdldENhY2hlIH0gZnJvbSAnLi9jYWNoZS5qcyc7XHJcbmltcG9ydCB7IHRyaWdnZXJFeHBvcnQgfSBmcm9tICcuL2hpZ2hjaGFydHMuanMnO1xyXG5pbXBvcnQgeyBsb2cgfSBmcm9tICcuL2xvZ2dlci5qcyc7XHJcblxyXG5pbXBvcnQgc3ZnVGVtcGxhdGUgZnJvbSAnLi8uLi90ZW1wbGF0ZXMvc3ZnX2V4cG9ydC9zdmdfZXhwb3J0LmpzJztcclxuXHJcbmltcG9ydCBFeHBvcnRFcnJvciBmcm9tICcuL2Vycm9ycy9FeHBvcnRFcnJvci5qcyc7XHJcblxyXG4vKipcclxuICogUmV0cmlldmVzIHRoZSBjbGlwcGluZyByZWdpb24gY29vcmRpbmF0ZXMgb2YgdGhlIHNwZWNpZmllZCBwYWdlIGVsZW1lbnQgd2l0aFxyXG4gKiB0aGUgaWQgJ2NoYXJ0LWNvbnRhaW5lcicuXHJcbiAqXHJcbiAqIEBwYXJhbSB7T2JqZWN0fSBwYWdlIC0gUHVwcGV0ZWVyIHBhZ2Ugb2JqZWN0LlxyXG4gKlxyXG4gKiBAcmV0dXJucyB7UHJvbWlzZTxPYmplY3Q+fSBQcm9taXNlIHJlc29sdmluZyB0byBhbiBvYmplY3QgY29udGFpbmluZ1xyXG4gKiB4LCB5LCB3aWR0aCwgYW5kIGhlaWdodCBwcm9wZXJ0aWVzLlxyXG4gKi9cclxuY29uc3QgZ2V0Q2xpcFJlZ2lvbiA9IChwYWdlKSA9PlxyXG4gIHBhZ2UuJGV2YWwoJyNjaGFydC1jb250YWluZXInLCAoZWxlbWVudCkgPT4ge1xyXG4gICAgY29uc3QgeyB4LCB5LCB3aWR0aCwgaGVpZ2h0IH0gPSBlbGVtZW50LmdldEJvdW5kaW5nQ2xpZW50UmVjdCgpO1xyXG4gICAgcmV0dXJuIHtcclxuICAgICAgeCxcclxuICAgICAgeSxcclxuICAgICAgd2lkdGgsXHJcbiAgICAgIGhlaWdodDogTWF0aC50cnVuYyhoZWlnaHQgPiAxID8gaGVpZ2h0IDogNTAwKVxyXG4gICAgfTtcclxuICB9KTtcclxuXHJcbi8qKlxyXG4gKiBDcmVhdGVzIGFuIGltYWdlIHVzaW5nIFB1cHBldGVlcidzIHBhZ2Ugc2NyZWVuc2hvdCBmdW5jdGlvbmFsaXR5IHdpdGhcclxuICogc3BlY2lmaWVkIG9wdGlvbnMuXHJcbiAqXHJcbiAqIEBwYXJhbSB7T2JqZWN0fSBwYWdlIC0gUHVwcGV0ZWVyIHBhZ2Ugb2JqZWN0LlxyXG4gKiBAcGFyYW0ge3N0cmluZ30gdHlwZSAtIEltYWdlIHR5cGUuXHJcbiAqIEBwYXJhbSB7c3RyaW5nfSBlbmNvZGluZyAtIEltYWdlIGVuY29kaW5nLlxyXG4gKiBAcGFyYW0ge09iamVjdH0gY2xpcCAtIENsaXBwaW5nIHJlZ2lvbiBjb29yZGluYXRlcy5cclxuICogQHBhcmFtIHtudW1iZXJ9IHJhc3Rlcml6YXRpb25UaW1lb3V0IC0gVGltZW91dCBmb3IgcmFzdGVyaXphdGlvblxyXG4gKiBpbiBtaWxsaXNlY29uZHMuXHJcbiAqXHJcbiAqIEByZXR1cm5zIHtQcm9taXNlPEJ1ZmZlcj59IFByb21pc2UgcmVzb2x2aW5nIHRvIHRoZSBpbWFnZSBidWZmZXIgb3IgcmVqZWN0aW5nXHJcbiAqIHdpdGggYW4gRXhwb3J0RXJyb3IgZm9yIHRpbWVvdXQuXHJcbiAqL1xyXG5jb25zdCBjcmVhdGVJbWFnZSA9IChwYWdlLCB0eXBlLCBlbmNvZGluZywgY2xpcCwgcmFzdGVyaXphdGlvblRpbWVvdXQpID0+XHJcbiAgUHJvbWlzZS5yYWNlKFtcclxuICAgIHBhZ2Uuc2NyZWVuc2hvdCh7XHJcbiAgICAgIHR5cGUsXHJcbiAgICAgIGVuY29kaW5nLFxyXG4gICAgICBjbGlwLFxyXG4gICAgICBjYXB0dXJlQmV5b25kVmlld3BvcnQ6IHRydWUsXHJcbiAgICAgIGZ1bGxQYWdlOiBmYWxzZSxcclxuICAgICAgb3B0aW1pemVGb3JTcGVlZDogdHJ1ZSxcclxuICAgICAgLi4uKHR5cGUgIT09ICdwbmcnID8geyBxdWFsaXR5OiA4MCB9IDoge30pLFxyXG5cclxuICAgICAgLy8gIzQ0NywgIzQ2MyAtIGFsd2F5cyByZW5kZXIgb24gYSB0cmFuc3BhcmVudCBwYWdlIGlmIHRoZSBleHBlY3RlZCB0eXBlXHJcbiAgICAgIC8vIGZvcm1hdCBpcyBQTkdcclxuICAgICAgb21pdEJhY2tncm91bmQ6IHR5cGUgPT0gJ3BuZydcclxuICAgIH0pLFxyXG4gICAgbmV3IFByb21pc2UoKF9yZXNvbHZlLCByZWplY3QpID0+XHJcbiAgICAgIHNldFRpbWVvdXQoXHJcbiAgICAgICAgKCkgPT4gcmVqZWN0KG5ldyBFeHBvcnRFcnJvcignUmFzdGVyaXphdGlvbiB0aW1lb3V0JykpLFxyXG4gICAgICAgIHJhc3Rlcml6YXRpb25UaW1lb3V0IHx8IDE1MDBcclxuICAgICAgKVxyXG4gICAgKVxyXG4gIF0pO1xyXG5cclxuLyoqXHJcbiAqIENyZWF0ZXMgYSBQREYgdXNpbmcgUHVwcGV0ZWVyJ3MgcGFnZSBwZGYgZnVuY3Rpb25hbGl0eSB3aXRoIHNwZWNpZmllZFxyXG4gKiBvcHRpb25zLlxyXG4gKlxyXG4gKiBAcGFyYW0ge09iamVjdH0gcGFnZSAtIFB1cHBldGVlciBwYWdlIG9iamVjdC5cclxuICogQHBhcmFtIHtudW1iZXJ9IGhlaWdodCAtIFBERiBoZWlnaHQuXHJcbiAqIEBwYXJhbSB7bnVtYmVyfSB3aWR0aCAtIFBERiB3aWR0aC5cclxuICogQHBhcmFtIHtzdHJpbmd9IGVuY29kaW5nIC0gUERGIGVuY29kaW5nLlxyXG4gKlxyXG4gKiBAcmV0dXJucyB7UHJvbWlzZTxCdWZmZXI+fSBQcm9taXNlIHJlc29sdmluZyB0byB0aGUgUERGIGJ1ZmZlci5cclxuICovXHJcbmNvbnN0IGNyZWF0ZVBERiA9IGFzeW5jIChcclxuICBwYWdlLFxyXG4gIGhlaWdodCxcclxuICB3aWR0aCxcclxuICBlbmNvZGluZyxcclxuICByYXN0ZXJpemF0aW9uVGltZW91dFxyXG4pID0+IHtcclxuICBhd2FpdCBwYWdlLmVtdWxhdGVNZWRpYVR5cGUoJ3NjcmVlbicpO1xyXG4gIHJldHVybiBQcm9taXNlLnJhY2UoW1xyXG4gICAgcGFnZS5wZGYoe1xyXG4gICAgICAvLyBUaGlzIHdpbGwgcmVtb3ZlIGFuIGV4dHJhIGVtcHR5IHBhZ2UgaW4gUERGIGV4cG9ydHNcclxuICAgICAgaGVpZ2h0OiBoZWlnaHQgKyAxLFxyXG4gICAgICB3aWR0aCxcclxuICAgICAgZW5jb2RpbmdcclxuICAgIH0pLFxyXG4gICAgbmV3IFByb21pc2UoKF9yZXNvbHZlLCByZWplY3QpID0+XHJcbiAgICAgIHNldFRpbWVvdXQoXHJcbiAgICAgICAgKCkgPT4gcmVqZWN0KG5ldyBFeHBvcnRFcnJvcignUmFzdGVyaXphdGlvbiB0aW1lb3V0JykpLFxyXG4gICAgICAgIHJhc3Rlcml6YXRpb25UaW1lb3V0IHx8IDE1MDBcclxuICAgICAgKVxyXG4gICAgKVxyXG4gIF0pO1xyXG59O1xyXG5cclxuLyoqXHJcbiAqIENyZWF0ZXMgYW4gU1ZHIHN0cmluZyBieSBldmFsdWF0aW5nIHRoZSBvdXRlckhUTUwgb2YgdGhlIGZpcnN0ICdzdmcnIGVsZW1lbnRcclxuICogaW5zaWRlIGFuIGVsZW1lbnQgd2l0aCB0aGUgaWQgJ2NvbnRhaW5lcicuXHJcbiAqXHJcbiAqIEBwYXJhbSB7T2JqZWN0fSBwYWdlIC0gUHVwcGV0ZWVyIHBhZ2Ugb2JqZWN0LlxyXG4gKlxyXG4gKiBAcmV0dXJucyB7UHJvbWlzZTxzdHJpbmc+fSBQcm9taXNlIHJlc29sdmluZyB0byB0aGUgU1ZHIHN0cmluZy5cclxuICovXHJcbmNvbnN0IGNyZWF0ZVNWRyA9IChwYWdlKSA9PlxyXG4gIHBhZ2UuJGV2YWwoJyNjb250YWluZXIgc3ZnOmZpcnN0LW9mLXR5cGUnLCAoZWxlbWVudCkgPT4gZWxlbWVudC5vdXRlckhUTUwpO1xyXG5cclxuLyoqXHJcbiAqIFNldHMgdGhlIHNwZWNpZmllZCBjaGFydCBhbmQgb3B0aW9ucyBhcyBjb25maWd1cmF0aW9uIGludG8gdGhlIHRyaWdnZXJFeHBvcnRcclxuICogZnVuY3Rpb24gd2l0aGluIHRoZSB3aW5kb3cgY29udGV4dCB1c2luZyBwYWdlLmV2YWx1YXRlLlxyXG4gKlxyXG4gKiBAcGFyYW0ge09iamVjdH0gcGFnZSAtIFB1cHBldGVlciBwYWdlIG9iamVjdC5cclxuICogQHBhcmFtIHthbnl9IGNoYXJ0IC0gVGhlIGNoYXJ0IG9iamVjdCB0byBiZSBjb25maWd1cmVkLlxyXG4gKiBAcGFyYW0ge09iamVjdH0gb3B0aW9ucyAtIENvbmZpZ3VyYXRpb24gb3B0aW9ucyBmb3IgdGhlIGNoYXJ0LlxyXG4gKlxyXG4gKiBAcmV0dXJucyB7UHJvbWlzZTx2b2lkPn0gUHJvbWlzZSByZXNvbHZpbmcgYWZ0ZXIgdGhlIGNvbmZpZ3VyYXRpb24gaXMgc2V0LlxyXG4gKi9cclxuY29uc3Qgc2V0QXNDb25maWcgPSBhc3luYyAocGFnZSwgY2hhcnQsIG9wdGlvbnMsIGRpc3BsYXlFcnJvcnMpID0+XHJcbiAgcGFnZS5ldmFsdWF0ZSh0cmlnZ2VyRXhwb3J0LCBjaGFydCwgb3B0aW9ucywgZGlzcGxheUVycm9ycyk7XHJcblxyXG4vKipcclxuICogRXhwb3J0cyB0byBhIGNoYXJ0IGZyb20gYSBwYWdlIHVzaW5nIFB1cHBldGVlci5cclxuICpcclxuICogQHBhcmFtIHtPYmplY3R9IHBhZ2UgLSBQdXBwZXRlZXIgcGFnZSBvYmplY3QuXHJcbiAqIEBwYXJhbSB7YW55fSBjaGFydCAtIFRoZSBjaGFydCBvYmplY3Qgb3IgU1ZHIGNvbmZpZ3VyYXRpb24gdG8gYmUgZXhwb3J0ZWQuXHJcbiAqIEBwYXJhbSB7T2JqZWN0fSBvcHRpb25zIC0gRXhwb3J0IG9wdGlvbnMgYW5kIGNvbmZpZ3VyYXRpb24uXHJcbiAqXHJcbiAqIEByZXR1cm5zIHtQcm9taXNlPHN0cmluZyB8IEJ1ZmZlciB8IEV4cG9ydEVycm9yPn0gUHJvbWlzZSByZXNvbHZpbmcgdG9cclxuICogdGhlIGV4cG9ydGVkIGRhdGEgb3IgcmVqZWN0aW5nIHdpdGggYW4gRXhwb3J0RXJyb3IuXHJcbiAqL1xyXG5leHBvcnQgZGVmYXVsdCBhc3luYyAocGFnZSwgY2hhcnQsIG9wdGlvbnMpID0+IHtcclxuICAvLyBJbmplY3RlZCByZXNvdXJjZXMgYXJyYXkgKGFkZGl0aW9uYWwgSlMgYW5kIENTUylcclxuICBsZXQgaW5qZWN0ZWRSZXNvdXJjZXMgPSBbXTtcclxuXHJcbiAgdHJ5IHtcclxuICAgIGxvZyg0LCAnW2V4cG9ydF0gRGV0ZXJtaW5pbmcgZXhwb3J0IHBhdGguJyk7XHJcblxyXG4gICAgY29uc3QgZXhwb3J0T3B0aW9ucyA9IG9wdGlvbnMuZXhwb3J0O1xyXG5cclxuICAgIC8vIERlY2lkZSB3aGV0aGVyIGRpc3BsYXkgZXJyb3Igb3IgZGViYnVnZXIgd3JhcHBlciBhcm91bmQgaXRcclxuICAgIGNvbnN0IGRpc3BsYXlFcnJvcnMgPVxyXG4gICAgICBleHBvcnRPcHRpb25zPy5vcHRpb25zPy5jaGFydD8uZGlzcGxheUVycm9ycyAmJlxyXG4gICAgICBnZXRDYWNoZSgpLmFjdGl2ZU1hbmlmZXN0Lm1vZHVsZXMuZGVidWdnZXI7XHJcblxyXG4gICAgbGV0IGlzU1ZHO1xyXG4gICAgaWYgKFxyXG4gICAgICBjaGFydC5pbmRleE9mICYmXHJcbiAgICAgIChjaGFydC5pbmRleE9mKCc8c3ZnJykgPj0gMCB8fCBjaGFydC5pbmRleE9mKCc8P3htbCcpID49IDApXHJcbiAgICApIHtcclxuICAgICAgLy8gU1ZHIGlucHV0IGhhbmRsaW5nXHJcbiAgICAgIGxvZyg0LCAnW2V4cG9ydF0gVHJlYXRpbmcgYXMgU1ZHLicpO1xyXG5cclxuICAgICAgLy8gSWYgaW5wdXQgaXMgYWxzbyBTVkcsIGp1c3QgcmV0dXJuIGl0XHJcbiAgICAgIGlmIChleHBvcnRPcHRpb25zLnR5cGUgPT09ICdzdmcnKSB7XHJcbiAgICAgICAgcmV0dXJuIGNoYXJ0O1xyXG4gICAgICB9XHJcblxyXG4gICAgICBpc1NWRyA9IHRydWU7XHJcbiAgICAgIGF3YWl0IHBhZ2Uuc2V0Q29udGVudChzdmdUZW1wbGF0ZShjaGFydCksIHtcclxuICAgICAgICB3YWl0VW50aWw6ICdkb21jb250ZW50bG9hZGVkJ1xyXG4gICAgICB9KTtcclxuICAgIH0gZWxzZSB7XHJcbiAgICAgIC8vIEpTT04gY29uZmlnIGhhbmRsaW5nXHJcbiAgICAgIGxvZyg0LCAnW2V4cG9ydF0gVHJlYXRpbmcgYXMgY29uZmlnLicpO1xyXG5cclxuICAgICAgLy8gTmVlZCB0byBwZXJmb3JtIHN0cmFpZ2h0IGluamVjdFxyXG4gICAgICBpZiAoZXhwb3J0T3B0aW9ucy5zdHJJbmopIHtcclxuICAgICAgICAvLyBJbmplY3Rpb24gYmFzZWQgY29uZmlndXJhdGlvbiBleHBvcnRcclxuICAgICAgICBhd2FpdCBzZXRBc0NvbmZpZyhcclxuICAgICAgICAgIHBhZ2UsXHJcbiAgICAgICAgICB7XHJcbiAgICAgICAgICAgIGNoYXJ0OiB7XHJcbiAgICAgICAgICAgICAgaGVpZ2h0OiBleHBvcnRPcHRpb25zLmhlaWdodCxcclxuICAgICAgICAgICAgICB3aWR0aDogZXhwb3J0T3B0aW9ucy53aWR0aFxyXG4gICAgICAgICAgICB9XHJcbiAgICAgICAgICB9LFxyXG4gICAgICAgICAgb3B0aW9ucyxcclxuICAgICAgICAgIGRpc3BsYXlFcnJvcnNcclxuICAgICAgICApO1xyXG4gICAgICB9IGVsc2Uge1xyXG4gICAgICAgIC8vIEJhc2ljIGNvbmZpZ3VyYXRpb24gZXhwb3J0XHJcbiAgICAgICAgY2hhcnQuY2hhcnQuaGVpZ2h0ID0gZXhwb3J0T3B0aW9ucy5oZWlnaHQ7XHJcbiAgICAgICAgY2hhcnQuY2hhcnQud2lkdGggPSBleHBvcnRPcHRpb25zLndpZHRoO1xyXG5cclxuICAgICAgICBhd2FpdCBzZXRBc0NvbmZpZyhwYWdlLCBjaGFydCwgb3B0aW9ucywgZGlzcGxheUVycm9ycyk7XHJcbiAgICAgIH1cclxuICAgIH1cclxuXHJcbiAgICAvLyBLZWVwcyB0cmFjayBvZiBhbGwgcmVzb3VyY2VzIGFkZGVkIG9uIHRoZSBwYWdlIHdpdGggYWRkWFhYVGFnLiBldGNcclxuICAgIC8vIEl0J3MgVklUQUwgdGhhdCBhbGwgYWRkZWQgcmVzb3VyY2VzIGVuZHMgdXAgaGVyZSBzbyB3ZSBjYW4gY2xlYXIgdGhpbmdzXHJcbiAgICAvLyBvdXQgd2hlbiBkb2luZyBhIG5ldyBleHBvcnQgaW4gdGhlIHNhbWUgcGFnZSFcclxuICAgIGluamVjdGVkUmVzb3VyY2VzID0gYXdhaXQgYWRkUGFnZVJlc291cmNlcyhwYWdlLCBvcHRpb25zKTtcclxuXHJcbiAgICAvLyBHZXQgdGhlIHJlYWwgY2hhcnQgc2l6ZSBhbmQgc2V0IHRoZSB6b29tIGFjY29yZGluZ2x5XHJcbiAgICBjb25zdCBzaXplID0gaXNTVkdcclxuICAgICAgPyBhd2FpdCBwYWdlLmV2YWx1YXRlKChzY2FsZSkgPT4ge1xyXG4gICAgICAgICAgY29uc3Qgc3ZnRWxlbWVudCA9IGRvY3VtZW50LnF1ZXJ5U2VsZWN0b3IoXHJcbiAgICAgICAgICAgICcjY2hhcnQtY29udGFpbmVyIHN2ZzpmaXJzdC1vZi10eXBlJ1xyXG4gICAgICAgICAgKTtcclxuXHJcbiAgICAgICAgICAvLyBHZXQgdGhlIHZhbHVlcyBjb3JyZWN0bHkgc2NhbGVkXHJcbiAgICAgICAgICBjb25zdCBjaGFydEhlaWdodCA9IHN2Z0VsZW1lbnQuaGVpZ2h0LmJhc2VWYWwudmFsdWUgKiBzY2FsZTtcclxuICAgICAgICAgIGNvbnN0IGNoYXJ0V2lkdGggPSBzdmdFbGVtZW50LndpZHRoLmJhc2VWYWwudmFsdWUgKiBzY2FsZTtcclxuXHJcbiAgICAgICAgICAvLyBJbiBjYXNlIG9mIFNWRyB0aGUgem9vbSBtdXN0IGJlIHNldCBkaXJlY3RseSBmb3IgYm9keVxyXG4gICAgICAgICAgLy8gU2V0IHRoZSB6b29tIGFzIHNjYWxlXHJcbiAgICAgICAgICAvLyBlc2xpbnQtZGlzYWJsZS1uZXh0LWxpbmUgbm8tdW5kZWZcclxuICAgICAgICAgIGRvY3VtZW50LmJvZHkuc3R5bGUuem9vbSA9IHNjYWxlO1xyXG5cclxuICAgICAgICAgIC8vIFNldCB0aGUgbWFyZ2luIHRvIDBweFxyXG4gICAgICAgICAgLy8gZXNsaW50LWRpc2FibGUtbmV4dC1saW5lIG5vLXVuZGVmXHJcbiAgICAgICAgICBkb2N1bWVudC5ib2R5LnN0eWxlLm1hcmdpbiA9ICcwcHgnO1xyXG5cclxuICAgICAgICAgIHJldHVybiB7XHJcbiAgICAgICAgICAgIGNoYXJ0SGVpZ2h0LFxyXG4gICAgICAgICAgICBjaGFydFdpZHRoXHJcbiAgICAgICAgICB9O1xyXG4gICAgICAgIH0sIHBhcnNlRmxvYXQoZXhwb3J0T3B0aW9ucy5zY2FsZSkpXHJcbiAgICAgIDogYXdhaXQgcGFnZS5ldmFsdWF0ZSgoKSA9PiB7XHJcbiAgICAgICAgICAvLyBlc2xpbnQtZGlzYWJsZS1uZXh0LWxpbmUgbm8tdW5kZWZcclxuICAgICAgICAgIGNvbnN0IHsgY2hhcnRIZWlnaHQsIGNoYXJ0V2lkdGggfSA9IHdpbmRvdy5IaWdoY2hhcnRzLmNoYXJ0c1swXTtcclxuXHJcbiAgICAgICAgICAvLyBObyBuZWVkIGZvciBzdWNoIHNjYWxlIG1hbmlwdWxhdGlvbiBpbiBjYXNlIG9mIG90aGVyIHR5cGVzIG9mIGV4cG9ydHNcclxuICAgICAgICAgIC8vIFJlc2V0IHRoZSB6b29tIGZvciBvdGhlciBleHBvcnRzIHRoYW4gdG8gU1ZHc1xyXG4gICAgICAgICAgLy8gZXNsaW50LWRpc2FibGUtbmV4dC1saW5lIG5vLXVuZGVmXHJcbiAgICAgICAgICBkb2N1bWVudC5ib2R5LnN0eWxlLnpvb20gPSAxO1xyXG5cclxuICAgICAgICAgIHJldHVybiB7XHJcbiAgICAgICAgICAgIGNoYXJ0SGVpZ2h0LFxyXG4gICAgICAgICAgICBjaGFydFdpZHRoXHJcbiAgICAgICAgICB9O1xyXG4gICAgICAgIH0pO1xyXG5cclxuICAgIC8vIFNldCBmaW5hbCBoZWlnaHQgYW5kIHdpZHRoIGZvciB2aWV3cG9ydFxyXG4gICAgY29uc3Qgdmlld3BvcnRIZWlnaHQgPSBNYXRoLmNlaWwoc2l6ZS5jaGFydEhlaWdodCB8fCBleHBvcnRPcHRpb25zLmhlaWdodCk7XHJcbiAgICBjb25zdCB2aWV3cG9ydFdpZHRoID0gTWF0aC5jZWlsKHNpemUuY2hhcnRXaWR0aCB8fCBleHBvcnRPcHRpb25zLndpZHRoKTtcclxuXHJcbiAgICAvLyBHZXQgdGhlIGNsaXAgcmVnaW9uIGZvciB0aGUgcGFnZVxyXG4gICAgY29uc3QgeyB4LCB5IH0gPSBhd2FpdCBnZXRDbGlwUmVnaW9uKHBhZ2UpO1xyXG5cclxuICAgIC8vIFNldCB0aGUgZmluYWwgdmlld3BvcnQgbm93IHRoYXQgd2UgaGF2ZSB0aGUgcmVhbCBoZWlnaHRcclxuICAgIGF3YWl0IHBhZ2Uuc2V0Vmlld3BvcnQoe1xyXG4gICAgICBoZWlnaHQ6IHZpZXdwb3J0SGVpZ2h0LFxyXG4gICAgICB3aWR0aDogdmlld3BvcnRXaWR0aCxcclxuICAgICAgZGV2aWNlU2NhbGVGYWN0b3I6IGlzU1ZHID8gMSA6IHBhcnNlRmxvYXQoZXhwb3J0T3B0aW9ucy5zY2FsZSlcclxuICAgIH0pO1xyXG5cclxuICAgIGxldCBkYXRhO1xyXG4gICAgLy8gUmFzdGVyaXphdGlvbiBwcm9jZXNzXHJcbiAgICBpZiAoZXhwb3J0T3B0aW9ucy50eXBlID09PSAnc3ZnJykge1xyXG4gICAgICAvLyBTVkdcclxuICAgICAgZGF0YSA9IGF3YWl0IGNyZWF0ZVNWRyhwYWdlKTtcclxuICAgIH0gZWxzZSBpZiAoWydwbmcnLCAnanBlZyddLmluY2x1ZGVzKGV4cG9ydE9wdGlvbnMudHlwZSkpIHtcclxuICAgICAgLy8gUE5HIG9yIEpQRUdcclxuICAgICAgZGF0YSA9IGF3YWl0IGNyZWF0ZUltYWdlKFxyXG4gICAgICAgIHBhZ2UsXHJcbiAgICAgICAgZXhwb3J0T3B0aW9ucy50eXBlLFxyXG4gICAgICAgICdiYXNlNjQnLFxyXG4gICAgICAgIHtcclxuICAgICAgICAgIHdpZHRoOiB2aWV3cG9ydFdpZHRoLFxyXG4gICAgICAgICAgaGVpZ2h0OiB2aWV3cG9ydEhlaWdodCxcclxuICAgICAgICAgIHgsXHJcbiAgICAgICAgICB5XHJcbiAgICAgICAgfSxcclxuICAgICAgICBleHBvcnRPcHRpb25zLnJhc3Rlcml6YXRpb25UaW1lb3V0XHJcbiAgICAgICk7XHJcbiAgICB9IGVsc2UgaWYgKGV4cG9ydE9wdGlvbnMudHlwZSA9PT0gJ3BkZicpIHtcclxuICAgICAgLy8gUERGXHJcbiAgICAgIGRhdGEgPSBhd2FpdCBjcmVhdGVQREYoXHJcbiAgICAgICAgcGFnZSxcclxuICAgICAgICB2aWV3cG9ydEhlaWdodCxcclxuICAgICAgICB2aWV3cG9ydFdpZHRoLFxyXG4gICAgICAgICdiYXNlNjQnLFxyXG4gICAgICAgIGV4cG9ydE9wdGlvbnMucmFzdGVyaXphdGlvblRpbWVvdXRcclxuICAgICAgKTtcclxuICAgIH0gZWxzZSB7XHJcbiAgICAgIHRocm93IG5ldyBFeHBvcnRFcnJvcihcclxuICAgICAgICBgW2V4cG9ydF0gVW5zdXBwb3J0ZWQgb3V0cHV0IGZvcm1hdCAke2V4cG9ydE9wdGlvbnMudHlwZX0uYFxyXG4gICAgICApO1xyXG4gICAgfVxyXG5cclxuICAgIC8vIENsZWFyIHByZXZpb3VzbHkgaW5qZWN0ZWQgSlMgYW5kIENTUyByZXNvdXJjZXNcclxuICAgIGF3YWl0IGNsZWFyUGFnZVJlc291cmNlcyhwYWdlLCBpbmplY3RlZFJlc291cmNlcyk7XHJcbiAgICByZXR1cm4gZGF0YTtcclxuICB9IGNhdGNoIChlcnJvcikge1xyXG4gICAgYXdhaXQgY2xlYXJQYWdlUmVzb3VyY2VzKHBhZ2UsIGluamVjdGVkUmVzb3VyY2VzKTtcclxuICAgIHJldHVybiBlcnJvcjtcclxuICB9XHJcbn07XHJcbiIsIi8qKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqXHJcblxyXG5IaWdoY2hhcnRzIEV4cG9ydCBTZXJ2ZXJcclxuXHJcbkNvcHlyaWdodCAoYykgMjAxNi0yMDI0LCBIaWdoc29mdFxyXG5cclxuTGljZW5jZWQgdW5kZXIgdGhlIE1JVCBsaWNlbmNlLlxyXG5cclxuQWRkaXRpb25hbGx5IGEgdmFsaWQgSGlnaGNoYXJ0cyBsaWNlbnNlIGlzIHJlcXVpcmVkIGZvciB1c2UuXHJcblxyXG5TZWUgTElDRU5TRSBmaWxlIGluIHJvb3QgZm9yIGRldGFpbHMuXHJcblxyXG4qKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqL1xyXG5cclxuaW1wb3J0IGNzc1RlbXBsYXRlIGZyb20gJy4vY3NzLmpzJztcclxuXHJcbmV4cG9ydCBkZWZhdWx0IChjaGFydCkgPT4gYFxyXG48IURPQ1RZUEUgaHRtbD5cclxuPGh0bWwgbGFuZz0nZW4tVVMnPlxyXG4gIDxoZWFkPlxyXG4gICAgPG1ldGEgaHR0cC1lcXVpdj1cIkNvbnRlbnQtVHlwZVwiIGNvbnRlbnQ9XCJ0ZXh0L2h0bWw7IGNoYXJzZXQ9dXRmLThcIj5cclxuICAgIDx0aXRsZT5IaWdoY2hhcnRzIEV4cG9ydDwvdGl0bGU+XHJcbiAgPC9oZWFkPlxyXG4gIDxzdHlsZT5cclxuICAgICR7Y3NzVGVtcGxhdGUoKX1cclxuICA8L3N0eWxlPlxyXG4gIDxib2R5PlxyXG4gICAgPGRpdiBpZD1cImNoYXJ0LWNvbnRhaW5lclwiPlxyXG4gICAgICAke2NoYXJ0fVxyXG4gICAgPC9kaXY+XHJcbiAgPC9ib2R5PlxyXG48L2h0bWw+XHJcblxyXG5gO1xyXG4iLCIvKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKlxyXG5cclxuSGlnaGNoYXJ0cyBFeHBvcnQgU2VydmVyXHJcblxyXG5Db3B5cmlnaHQgKGMpIDIwMTYtMjAyNCwgSGlnaHNvZnRcclxuXHJcbkxpY2VuY2VkIHVuZGVyIHRoZSBNSVQgbGljZW5jZS5cclxuXHJcbkFkZGl0aW9uYWxseSBhIHZhbGlkIEhpZ2hjaGFydHMgbGljZW5zZSBpcyByZXF1aXJlZCBmb3IgdXNlLlxyXG5cclxuU2VlIExJQ0VOU0UgZmlsZSBpbiByb290IGZvciBkZXRhaWxzLlxyXG5cclxuKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKi9cclxuXHJcbmltcG9ydCB7IFBvb2wgfSBmcm9tICd0YXJuJztcclxuaW1wb3J0IHsgdjQgYXMgdXVpZCB9IGZyb20gJ3V1aWQnO1xyXG5cclxuaW1wb3J0IHtcclxuICBjcmVhdGUgYXMgY3JlYXRlQnJvd3NlcixcclxuICBjbG9zZSBhcyBjbG9zZUJyb3dzZXIsXHJcbiAgbmV3UGFnZSxcclxuICBjbGVhclBhZ2VcclxufSBmcm9tICcuL2Jyb3dzZXIuanMnO1xyXG5pbXBvcnQgcHVwcGV0ZWVyRXhwb3J0IGZyb20gJy4vZXhwb3J0LmpzJztcclxuaW1wb3J0IHsgbG9nLCBsb2dXaXRoU3RhY2sgfSBmcm9tICcuL2xvZ2dlci5qcyc7XHJcbmltcG9ydCB7IG1lYXN1cmVUaW1lIH0gZnJvbSAnLi91dGlscy5qcyc7XHJcblxyXG5pbXBvcnQgRXhwb3J0RXJyb3IgZnJvbSAnLi9lcnJvcnMvRXhwb3J0RXJyb3IuanMnO1xyXG5cclxuLy8gVGhlIHBvb2wgaW5zdGFuY2VcclxubGV0IHBvb2wgPSBmYWxzZTtcclxuXHJcbi8vIFBvb2wgc3RhdGlzdGljc1xyXG5leHBvcnQgY29uc3Qgc3RhdHMgPSB7XHJcbiAgcGVyZm9ybWVkRXhwb3J0czogMCxcclxuICBleHBvcnRBdHRlbXB0czogMCxcclxuICBleHBvcnRGcm9tU3ZnQXR0ZW1wdHM6IDAsXHJcbiAgdGltZVNwZW50OiAwLFxyXG4gIGRyb3BwZWRFeHBvcnRzOiAwLFxyXG4gIHNwZW50QXZlcmFnZTogMFxyXG59O1xyXG5cclxubGV0IHBvb2xDb25maWcgPSB7fTtcclxuXHJcbmNvbnN0IGZhY3RvcnkgPSB7XHJcbiAgLyoqXHJcbiAgICogQ3JlYXRlcyBhIG5ldyB3b3JrZXIgcGFnZSBmb3IgdGhlIGV4cG9ydCBwb29sLlxyXG4gICAqXHJcbiAgICogQHJldHVybnMge09iamVjdH0gLSBBbiBvYmplY3QgY29udGFpbmluZyB0aGUgd29ya2VyIElELCBhIHJlZmVyZW5jZSB0byB0aGVcclxuICAgKiBicm93c2VyIHBhZ2UsIGFuZCBpbml0aWFsIHdvcmsgY291bnQuXHJcbiAgICpcclxuICAgKiBAdGhyb3dzIHtFeHBvcnRFcnJvcn0gLSBJZiB0aGVyZSdzIGFuIGVycm9yIGR1cmluZyB0aGUgY3JlYXRpb24gb2YgdGhlIG5ld1xyXG4gICAqIHBhZ2UuXHJcbiAgICovXHJcbiAgY3JlYXRlOiBhc3luYyAoKSA9PiB7XHJcbiAgICBsZXQgcGFnZSA9IGZhbHNlO1xyXG5cclxuICAgIGNvbnN0IGlkID0gdXVpZCgpO1xyXG4gICAgY29uc3Qgc3RhcnREYXRlID0gbmV3IERhdGUoKS5nZXRUaW1lKCk7XHJcblxyXG4gICAgdHJ5IHtcclxuICAgICAgcGFnZSA9IGF3YWl0IG5ld1BhZ2UoKTtcclxuXHJcbiAgICAgIGlmICghcGFnZSB8fCBwYWdlLmlzQ2xvc2VkKCkpIHtcclxuICAgICAgICB0aHJvdyBuZXcgRXhwb3J0RXJyb3IoJ1RoZSBwYWdlIGlzIGludmFsaWQgb3IgY2xvc2VkLicpO1xyXG4gICAgICB9XHJcblxyXG4gICAgICBsb2coXHJcbiAgICAgICAgMyxcclxuICAgICAgICBgW3Bvb2xdIFN1Y2Nlc3NmdWxseSBjcmVhdGVkIGEgd29ya2VyICR7aWR9IC0gdG9vayAke1xyXG4gICAgICAgICAgbmV3IERhdGUoKS5nZXRUaW1lKCkgLSBzdGFydERhdGVcclxuICAgICAgICB9IG1zLmBcclxuICAgICAgKTtcclxuICAgIH0gY2F0Y2ggKGVycm9yKSB7XHJcbiAgICAgIHRocm93IG5ldyBFeHBvcnRFcnJvcihcclxuICAgICAgICAnRXJyb3IgZW5jb3VudGVyZWQgd2hlbiBjcmVhdGluZyBhIG5ldyBwYWdlLidcclxuICAgICAgKS5zZXRFcnJvcihlcnJvcik7XHJcbiAgICB9XHJcblxyXG4gICAgcmV0dXJuIHtcclxuICAgICAgaWQsXHJcbiAgICAgIHBhZ2UsXHJcbiAgICAgIC8vIFRyeSB0byBkaXN0cmlidXRlIHRoZSBpbml0aWFsIHdvcmsgY291bnRcclxuICAgICAgd29ya0NvdW50OiBNYXRoLnJvdW5kKE1hdGgucmFuZG9tKCkgKiAocG9vbENvbmZpZy53b3JrTGltaXQgLyAyKSlcclxuICAgIH07XHJcbiAgfSxcclxuXHJcbiAgLyoqXHJcbiAgICogVmFsaWRhdGVzIGEgd29ya2VyIHBhZ2UgaW4gdGhlIGV4cG9ydCBwb29sLCBjaGVja2luZyBpZiBpdCBoYXMgZXhjZWVkZWRcclxuICAgKiB0aGUgd29yayBsaW1pdC5cclxuICAgKlxyXG4gICAqIEBwYXJhbSB7T2JqZWN0fSB3b3JrZXJIYW5kbGUgLSBUaGUgaGFuZGxlIHRvIHRoZSB3b3JrZXIsIGNvbnRhaW5pbmcgdGhlXHJcbiAgICogd29ya2VyJ3MgSUQsIGEgcmVmZXJlbmNlIHRvIHRoZSBicm93c2VyIHBhZ2UsIGFuZCB3b3JrIGNvdW50LlxyXG4gICAqXHJcbiAgICogQHJldHVybnMge2Jvb2xlYW59IC0gUmV0dXJucyB0cnVlIGlmIHRoZSB3b3JrZXIgaXMgdmFsaWQgYW5kIHdpdGhpblxyXG4gICAqIHRoZSB3b3JrIGxpbWl0OyBvdGhlcndpc2UsIHJldHVybnMgZmFsc2UuXHJcbiAgICovXHJcbiAgdmFsaWRhdGU6IGFzeW5jICh3b3JrZXJIYW5kbGUpID0+IHtcclxuICAgIGlmIChcclxuICAgICAgcG9vbENvbmZpZy53b3JrTGltaXQgJiZcclxuICAgICAgKyt3b3JrZXJIYW5kbGUud29ya0NvdW50ID4gcG9vbENvbmZpZy53b3JrTGltaXRcclxuICAgICkge1xyXG4gICAgICBsb2coXHJcbiAgICAgICAgMyxcclxuICAgICAgICBgW3Bvb2xdIFdvcmtlciBmYWlsZWQgdmFsaWRhdGlvbjogZXhjZWVkZWQgd29yayBsaW1pdCAobGltaXQgaXMgJHtwb29sQ29uZmlnLndvcmtMaW1pdH0pLmBcclxuICAgICAgKTtcclxuICAgICAgcmV0dXJuIGZhbHNlO1xyXG4gICAgfVxyXG4gICAgcmV0dXJuIHRydWU7XHJcbiAgfSxcclxuXHJcbiAgLyoqXHJcbiAgICogRGVzdHJveXMgYSB3b3JrZXIgZW50cnkgaW4gdGhlIGV4cG9ydCBwb29sLCBjbG9zaW5nIGl0cyBhc3NvY2lhdGVkIHBhZ2UuXHJcbiAgICpcclxuICAgKiBAcGFyYW0ge09iamVjdH0gd29ya2VySGFuZGxlIC0gVGhlIGhhbmRsZSB0byB0aGUgd29ya2VyLCBjb250YWluaW5nXHJcbiAgICogdGhlIHdvcmtlcidzIElEIGFuZCBhIHJlZmVyZW5jZSB0byB0aGUgYnJvd3NlciBwYWdlLlxyXG4gICAqL1xyXG4gIGRlc3Ryb3k6IGFzeW5jICh3b3JrZXJIYW5kbGUpID0+IHtcclxuICAgIGxvZygzLCBgW3Bvb2xdIERlc3Ryb3lpbmcgcG9vbCBlbnRyeSAke3dvcmtlckhhbmRsZS5pZH0uYCk7XHJcblxyXG4gICAgaWYgKHdvcmtlckhhbmRsZS5wYWdlKSB7XHJcbiAgICAgIC8vIFdlIGRvbid0IHJlYWxseSBuZWVkIHRvIHdhaXQgYXJvdW5kIGZvciB0aGlzXHJcbiAgICAgIGF3YWl0IHdvcmtlckhhbmRsZS5wYWdlLmNsb3NlKCk7XHJcbiAgICB9XHJcbiAgfVxyXG59O1xyXG5cclxuLyoqXHJcbiAqIEluaXRpYWxpemVzIHRoZSBleHBvcnQgcG9vbCB3aXRoIHRoZSBwcm92aWRlZCBjb25maWd1cmF0aW9uLCBjcmVhdGluZ1xyXG4gKiBhIGJyb3dzZXIgaW5zdGFuY2UgYW5kIHNldHRpbmcgdXAgd29ya2VyIHJlc291cmNlcy5cclxuICpcclxuICogQHBhcmFtIHtPYmplY3R9IGNvbmZpZyAtIENvbmZpZ3VyYXRpb24gb3B0aW9ucyBmb3IgdGhlIGV4cG9ydCBwb29sIGFsb25nXHJcbiAqIHdpdGggY3VzdG9tIHB1cHBldGVlciBhcmd1bWVudHMgZm9yIHRoZSBwdXBwZXRlZXIubGF1bmNoIGZ1bmN0aW9uLlxyXG4gKi9cclxuZXhwb3J0IGNvbnN0IGluaXRQb29sID0gYXN5bmMgKGNvbmZpZykgPT4ge1xyXG4gIC8vIEZvciB0aGUgbW9kdWxlIHNjb3BlIHVzYWdlXHJcbiAgcG9vbENvbmZpZyA9IGNvbmZpZyAmJiBjb25maWcucG9vbCA/IHsgLi4uY29uZmlnLnBvb2wgfSA6IHt9O1xyXG5cclxuICAvLyBDcmVhdGUgYSBicm93c2VyIGluc3RhbmNlIHdpdGggdGhlIHB1cHBldGVlciBhcmd1bWVudHNcclxuICBhd2FpdCBjcmVhdGVCcm93c2VyKGNvbmZpZy5wdXBwZXRlZXJBcmdzKTtcclxuXHJcbiAgbG9nKFxyXG4gICAgMyxcclxuICAgIGBbcG9vbF0gSW5pdGlhbGl6aW5nIHBvb2wgd2l0aCB3b3JrZXJzOiBtaW4gJHtwb29sQ29uZmlnLm1pbldvcmtlcnN9LCBtYXggJHtwb29sQ29uZmlnLm1heFdvcmtlcnN9LmBcclxuICApO1xyXG5cclxuICBpZiAocG9vbCkge1xyXG4gICAgcmV0dXJuIGxvZyhcclxuICAgICAgNCxcclxuICAgICAgJ1twb29sXSBBbHJlYWR5IGluaXRpYWxpemVkLCBwbGVhc2Uga2lsbCBpdCBiZWZvcmUgY3JlYXRpbmcgYSBuZXcgb25lLidcclxuICAgICk7XHJcbiAgfVxyXG5cclxuICBpZiAocGFyc2VJbnQocG9vbENvbmZpZy5taW5Xb3JrZXJzKSA+IHBhcnNlSW50KHBvb2xDb25maWcubWF4V29ya2VycykpIHtcclxuICAgIHBvb2xDb25maWcubWluV29ya2VycyA9IHBvb2xDb25maWcubWF4V29ya2VycztcclxuICB9XHJcblxyXG4gIHRyeSB7XHJcbiAgICAvLyBDcmVhdGUgYSBwb29sIGFsb25nIHdpdGggYSBtaW5pbWFsIG51bWJlciBvZiByZXNvdXJjZXNcclxuICAgIHBvb2wgPSBuZXcgUG9vbCh7XHJcbiAgICAgIC8vIEdldCB0aGUgY3JlYXRlL3ZhbGlkYXRlL2Rlc3Ryb3kvbG9nIGZ1bmN0aW9uc1xyXG4gICAgICAuLi5mYWN0b3J5LFxyXG4gICAgICBtaW46IHBhcnNlSW50KHBvb2xDb25maWcubWluV29ya2VycyksXHJcbiAgICAgIG1heDogcGFyc2VJbnQocG9vbENvbmZpZy5tYXhXb3JrZXJzKSxcclxuICAgICAgYWNxdWlyZVRpbWVvdXRNaWxsaXM6IHBvb2xDb25maWcuYWNxdWlyZVRpbWVvdXQsXHJcbiAgICAgIGNyZWF0ZVRpbWVvdXRNaWxsaXM6IHBvb2xDb25maWcuY3JlYXRlVGltZW91dCxcclxuICAgICAgZGVzdHJveVRpbWVvdXRNaWxsaXM6IHBvb2xDb25maWcuZGVzdHJveVRpbWVvdXQsXHJcbiAgICAgIGlkbGVUaW1lb3V0TWlsbGlzOiBwb29sQ29uZmlnLmlkbGVUaW1lb3V0LFxyXG4gICAgICBjcmVhdGVSZXRyeUludGVydmFsTWlsbGlzOiBwb29sQ29uZmlnLmNyZWF0ZVJldHJ5SW50ZXJ2YWwsXHJcbiAgICAgIHJlYXBJbnRlcnZhbE1pbGxpczogcG9vbENvbmZpZy5yZWFwZXJJbnRlcnZhbCxcclxuICAgICAgcHJvcGFnYXRlQ3JlYXRlRXJyb3I6IGZhbHNlXHJcbiAgICB9KTtcclxuXHJcbiAgICAvLyBTZXQgZXZlbnRzXHJcbiAgICBwb29sLm9uKCdyZWxlYXNlJywgYXN5bmMgKHJlc291cmNlKSA9PiB7XHJcbiAgICAgIC8vIENsZWFyIHBhZ2VcclxuICAgICAgYXdhaXQgY2xlYXJQYWdlKHJlc291cmNlLnBhZ2UsIGZhbHNlKTtcclxuICAgICAgbG9nKDQsIGBbcG9vbF0gUmVsZWFzaW5nIGEgd29ya2VyIHdpdGggSUQgJHtyZXNvdXJjZS5pZH0uYCk7XHJcbiAgICB9KTtcclxuXHJcbiAgICBwb29sLm9uKCdkZXN0cm95U3VjY2VzcycsIChldmVudElkLCByZXNvdXJjZSkgPT4ge1xyXG4gICAgICBsb2coNCwgYFtwb29sXSBEZXN0cm95ZWQgYSB3b3JrZXIgd2l0aCBJRCAke3Jlc291cmNlLmlkfS5gKTtcclxuICAgIH0pO1xyXG5cclxuICAgIGNvbnN0IGluaXRpYWxSZXNvdXJjZXMgPSBbXTtcclxuICAgIC8vIENyZWF0ZSBhbiBpbml0aWFsIG51bWJlciBvZiByZXNvdXJjZXNcclxuICAgIGZvciAobGV0IGkgPSAwOyBpIDwgcG9vbENvbmZpZy5taW5Xb3JrZXJzOyBpKyspIHtcclxuICAgICAgdHJ5IHtcclxuICAgICAgICBjb25zdCByZXNvdXJjZSA9IGF3YWl0IHBvb2wuYWNxdWlyZSgpLnByb21pc2U7XHJcbiAgICAgICAgaW5pdGlhbFJlc291cmNlcy5wdXNoKHJlc291cmNlKTtcclxuICAgICAgfSBjYXRjaCAoZXJyb3IpIHtcclxuICAgICAgICBsb2dXaXRoU3RhY2soMiwgZXJyb3IsICdbcG9vbF0gQ291bGQgbm90IGNyZWF0ZSBhbiBpbml0aWFsIHJlc291cmNlLicpO1xyXG4gICAgICB9XHJcbiAgICB9XHJcblxyXG4gICAgLy8gUmVsZWFzZSB0aGUgaW5pdGlhbCBudW1iZXIgb2YgcmVzb3VyY2VzIGJhY2sgdG8gdGhlIHBvb2xcclxuICAgIGluaXRpYWxSZXNvdXJjZXMuZm9yRWFjaCgocmVzb3VyY2UpID0+IHtcclxuICAgICAgcG9vbC5yZWxlYXNlKHJlc291cmNlKTtcclxuICAgIH0pO1xyXG5cclxuICAgIGxvZyhcclxuICAgICAgMyxcclxuICAgICAgYFtwb29sXSBUaGUgcG9vbCBpcyByZWFkeSR7aW5pdGlhbFJlc291cmNlcy5sZW5ndGggPyBgIHdpdGggJHtpbml0aWFsUmVzb3VyY2VzLmxlbmd0aH0gaW5pdGlhbCByZXNvdXJjZXMgd2FpdGluZy5gIDogJy4nfWBcclxuICAgICk7XHJcbiAgfSBjYXRjaCAoZXJyb3IpIHtcclxuICAgIHRocm93IG5ldyBFeHBvcnRFcnJvcihcclxuICAgICAgJ1twb29sXSBDb3VsZCBub3QgY3JlYXRlIHRoZSBwb29sIG9mIHdvcmtlcnMuJ1xyXG4gICAgKS5zZXRFcnJvcihlcnJvcik7XHJcbiAgfVxyXG59O1xyXG5cclxuLyoqXHJcbiAqIEtpbGxzIGFsbCB3b3JrZXJzIGluIHRoZSBwb29sLCBkZXN0cm95cyB0aGUgcG9vbCwgYW5kIGNsb3NlcyB0aGUgYnJvd3NlclxyXG4gKiBpbnN0YW5jZS5cclxuICpcclxuICogQHJldHVybnMge1Byb21pc2U8dm9pZD59IEEgcHJvbWlzZSB0aGF0IHJlc29sdmVzIGFmdGVyIHRoZSB3b3JrZXJzIGFyZVxyXG4gKiBraWxsZWQsIHRoZSBwb29sIGlzIGRlc3Ryb3llZCwgYW5kIHRoZSBicm93c2VyIGlzIGNsb3NlZC5cclxuICovXHJcbmV4cG9ydCBhc3luYyBmdW5jdGlvbiBraWxsUG9vbCgpIHtcclxuICBsb2coMywgJ1twb29sXSBLaWxsaW5nIHBvb2wgd2l0aCBhbGwgd29ya2VycyBhbmQgY2xvc2luZyBicm93c2VyLicpO1xyXG5cclxuICAvLyBJZiBzdGlsbCBhbGl2ZSwgZGVzdHJveSB0aGUgcG9vbCBvZiBwYWdlcyBiZWZvcmUgY2xvc2luZyBhIGJyb3dzZXJcclxuICBpZiAocG9vbCkge1xyXG4gICAgLy8gRnJlZSB1cCBub3QgcmVsZWFzZWQgd29ya2Vyc1xyXG4gICAgZm9yIChjb25zdCB3b3JrZXIgb2YgcG9vbC51c2VkKSB7XHJcbiAgICAgIHBvb2wucmVsZWFzZSh3b3JrZXIucmVzb3VyY2UpO1xyXG4gICAgfVxyXG5cclxuICAgIC8vIERlc3Ryb3kgdGhlIHBvb2wgaWYgaXQgaXMgc3RpbGwgYXZhaWxhYmxlXHJcbiAgICBpZiAoIXBvb2wuZGVzdHJveWVkKSB7XHJcbiAgICAgIGF3YWl0IHBvb2wuZGVzdHJveSgpO1xyXG4gICAgICBsb2coNCwgJ1ticm93c2VyXSBEZXN0cm95ZWQgdGhlIHBvb2wgb2YgcmVzb3VyY2VzLicpO1xyXG4gICAgfVxyXG4gIH1cclxuXHJcbiAgLy8gQ2xvc2UgdGhlIGJyb3dzZXIgaW5zdGFuY2VcclxuICBhd2FpdCBjbG9zZUJyb3dzZXIoKTtcclxufVxyXG5cclxuLyoqXHJcbiAqIFByb2Nlc3NlcyB0aGUgZXhwb3J0IHdvcmsgdXNpbmcgYSB3b3JrZXIgZnJvbSB0aGUgcG9vbC4gQWNxdWlyZXMgYSB3b3JrZXJcclxuICogaGFuZGxlIGZyb20gdGhlIHBvb2wsIHBlcmZvcm1zIHRoZSBleHBvcnQgdXNpbmcgcHVwcGV0ZWVyLCBhbmQgcmVsZWFzZXNcclxuICogdGhlIHdvcmtlciBoYW5kbGUgYmFjayB0byB0aGUgcG9vbC5cclxuICpcclxuICogQHBhcmFtIHtzdHJpbmd9IGNoYXJ0IC0gVGhlIGNoYXJ0IGRhdGEgb3IgY29uZmlndXJhdGlvbiB0byBiZSBleHBvcnRlZC5cclxuICogQHBhcmFtIHtPYmplY3R9IG9wdGlvbnMgLSBFeHBvcnQgb3B0aW9ucyBhbmQgY29uZmlndXJhdGlvbi5cclxuICpcclxuICogQHJldHVybnMge1Byb21pc2U8T2JqZWN0Pn0gQSBwcm9taXNlIHRoYXQgcmVzb2x2ZXMgd2l0aCB0aGUgZXhwb3J0IHJlc3VsdGFuZFxyXG4gKiBvcHRpb25zLlxyXG4gKlxyXG4gKiBAdGhyb3dzIHtFeHBvcnRFcnJvcn0gSWYgYW4gZXJyb3Igb2NjdXJzIGR1cmluZyB0aGUgZXhwb3J0IHByb2Nlc3MuXHJcbiAqL1xyXG5leHBvcnQgY29uc3QgcG9zdFdvcmsgPSBhc3luYyAoY2hhcnQsIG9wdGlvbnMpID0+IHtcclxuICBsZXQgd29ya2VySGFuZGxlO1xyXG5cclxuICB0cnkge1xyXG4gICAgbG9nKDQsICdbcG9vbF0gV29yayByZWNlaXZlZCwgc3RhcnRpbmcgdG8gcHJvY2Vzcy4nKTtcclxuXHJcbiAgICArK3N0YXRzLmV4cG9ydEF0dGVtcHRzO1xyXG4gICAgaWYgKHBvb2xDb25maWcuYmVuY2htYXJraW5nKSB7XHJcbiAgICAgIGdldFBvb2xJbmZvKCk7XHJcbiAgICB9XHJcblxyXG4gICAgaWYgKCFwb29sKSB7XHJcbiAgICAgIHRocm93IG5ldyBFeHBvcnRFcnJvcignV29yayByZWNlaXZlZCwgYnV0IHBvb2wgaGFzIG5vdCBiZWVuIHN0YXJ0ZWQuJyk7XHJcbiAgICB9XHJcblxyXG4gICAgLy8gQWNxdWlyZSB0aGUgd29ya2VyIGFsb25nIHdpdGggdGhlIGlkIG9mIHJlc291cmNlIGFuZCB3b3JrIGNvdW50XHJcbiAgICBjb25zdCBhY3F1aXJlQ291bnRlciA9IG1lYXN1cmVUaW1lKCk7XHJcbiAgICB0cnkge1xyXG4gICAgICBsb2coNCwgJ1twb29sXSBBY3F1aXJpbmcgYSB3b3JrZXIgaGFuZGxlLicpO1xyXG4gICAgICB3b3JrZXJIYW5kbGUgPSBhd2FpdCBwb29sLmFjcXVpcmUoKS5wcm9taXNlO1xyXG5cclxuICAgICAgLy8gQ2hlY2sgdGhlIHBhZ2UgYWNxdWlyZSB0aW1lXHJcbiAgICAgIGlmIChvcHRpb25zLnNlcnZlci5iZW5jaG1hcmtpbmcpIHtcclxuICAgICAgICBsb2coXHJcbiAgICAgICAgICA1LFxyXG4gICAgICAgICAgb3B0aW9ucy5wYXlsb2FkPy5yZXF1ZXN0SWRcclxuICAgICAgICAgICAgPyBgW2JlbmNobWFya10gUmVxdWVzdCB3aXRoIElEICR7b3B0aW9ucy5wYXlsb2FkPy5yZXF1ZXN0SWR9IC1gXHJcbiAgICAgICAgICAgIDogJ1tiZW5jaG1hcmtdJyxcclxuICAgICAgICAgIGBBY3F1aXJlZCBhIHdvcmtlciBoYW5kbGU6ICR7YWNxdWlyZUNvdW50ZXIoKX1tcy5gXHJcbiAgICAgICAgKTtcclxuICAgICAgfVxyXG4gICAgfSBjYXRjaCAoZXJyb3IpIHtcclxuICAgICAgdGhyb3cgbmV3IEV4cG9ydEVycm9yKFxyXG4gICAgICAgIChvcHRpb25zLnBheWxvYWQ/LnJlcXVlc3RJZFxyXG4gICAgICAgICAgPyBgRm9yIHJlcXVlc3Qgd2l0aCBJRCAke29wdGlvbnMucGF5bG9hZD8ucmVxdWVzdElkfSAtIGBcclxuICAgICAgICAgIDogJycpICtcclxuICAgICAgICAgIGBFcnJvciBlbmNvdW50ZXJlZCB3aGVuIGFjcXVpcmluZyBhbiBhdmFpbGFibGUgZW50cnk6ICR7YWNxdWlyZUNvdW50ZXIoKX1tcy5gXHJcbiAgICAgICkuc2V0RXJyb3IoZXJyb3IpO1xyXG4gICAgfVxyXG4gICAgbG9nKDQsICdbcG9vbF0gQWNxdWlyZWQgYSB3b3JrZXIgaGFuZGxlLicpO1xyXG5cclxuICAgIGlmICghd29ya2VySGFuZGxlLnBhZ2UpIHtcclxuICAgICAgdGhyb3cgbmV3IEV4cG9ydEVycm9yKFxyXG4gICAgICAgICdSZXNvbHZlZCB3b3JrZXIgcGFnZSBpcyBpbnZhbGlkOiB0aGUgcG9vbCBzZXR1cCBpcyB3b25reS4nXHJcbiAgICAgICk7XHJcbiAgICB9XHJcblxyXG4gICAgLy8gU2F2ZSB0aGUgc3RhcnQgdGltZVxyXG4gICAgbGV0IHdvcmtTdGFydCA9IG5ldyBEYXRlKCkuZ2V0VGltZSgpO1xyXG5cclxuICAgIGxvZyg0LCBgW3Bvb2xdIFN0YXJ0aW5nIHdvcmsgb24gcG9vbCBlbnRyeSB3aXRoIElEICR7d29ya2VySGFuZGxlLmlkfS5gKTtcclxuXHJcbiAgICAvLyBQZXJmb3JtIGFuIGV4cG9ydCBvbiBhIHB1cHBldGVlciBsZXZlbFxyXG4gICAgY29uc3QgZXhwb3J0Q291bnRlciA9IG1lYXN1cmVUaW1lKCk7XHJcbiAgICBjb25zdCByZXN1bHQgPSBhd2FpdCBwdXBwZXRlZXJFeHBvcnQod29ya2VySGFuZGxlLnBhZ2UsIGNoYXJ0LCBvcHRpb25zKTtcclxuXHJcbiAgICAvLyBDaGVjayBpZiBpdCdzIGFuIGVycm9yXHJcbiAgICBpZiAocmVzdWx0IGluc3RhbmNlb2YgRXJyb3IpIHtcclxuICAgICAgLy8gVE9ETzogSWYgdGhlIGV4cG9ydCBmYWlsZWQgYmVjYXVzZSBwdXBwZXRlZXIgdGltZWQgb3V0LCB3ZSBuZWVkIHRvIGZvcmNlIGtpbGwgdGhlIHdvcmtlciBzbyB3ZSBnZXQgYSBuZXcgcGFnZS4gVGhhdCBuZWVkcyB0byBiZSBoYW5kbGVkIGJldHRlciB0aGFuIHRoaXMgaGFjay5cclxuICAgICAgaWYgKHJlc3VsdC5tZXNzYWdlID09PSAnUmFzdGVyaXphdGlvbiB0aW1lb3V0Jykge1xyXG4gICAgICAgIHdvcmtlckhhbmRsZS5wYWdlLmNsb3NlKCk7XHJcbiAgICAgICAgd29ya2VySGFuZGxlLnBhZ2UgPSBhd2FpdCBuZXdQYWdlKCk7XHJcbiAgICAgIH1cclxuXHJcbiAgICAgIHRocm93IG5ldyBFeHBvcnRFcnJvcihcclxuICAgICAgICAob3B0aW9ucy5wYXlsb2FkPy5yZXF1ZXN0SWRcclxuICAgICAgICAgID8gYEZvciByZXF1ZXN0IHdpdGggSUQgJHtvcHRpb25zLnBheWxvYWQ/LnJlcXVlc3RJZH0gLSBgXHJcbiAgICAgICAgICA6ICcnKSArIGBFcnJvciBlbmNvdW50ZXJlZCBkdXJpbmcgZXhwb3J0OiAke2V4cG9ydENvdW50ZXIoKX1tcy5gXHJcbiAgICAgICkuc2V0RXJyb3IocmVzdWx0KTtcclxuICAgIH1cclxuXHJcbiAgICAvLyBDaGVjayB0aGUgUHVwcGV0ZWVyIGV4cG9ydCB0aW1lXHJcbiAgICBpZiAob3B0aW9ucy5zZXJ2ZXIuYmVuY2htYXJraW5nKSB7XHJcbiAgICAgIGxvZyhcclxuICAgICAgICA1LFxyXG4gICAgICAgIG9wdGlvbnMucGF5bG9hZD8ucmVxdWVzdElkXHJcbiAgICAgICAgICA/IGBbYmVuY2htYXJrXSBSZXF1ZXN0IHdpdGggSUQgJHtvcHRpb25zLnBheWxvYWQ/LnJlcXVlc3RJZH0gLWBcclxuICAgICAgICAgIDogJ1tiZW5jaG1hcmtdJyxcclxuICAgICAgICBgRXhwb3J0ZWQgYSBjaGFydCBzdWNlc3NmdWxseTogJHtleHBvcnRDb3VudGVyKCl9bXMuYFxyXG4gICAgICApO1xyXG4gICAgfVxyXG5cclxuICAgIC8vIFJlbGVhc2UgdGhlIHJlc291cmNlIGJhY2sgdG8gdGhlIHBvb2xcclxuICAgIHBvb2wucmVsZWFzZSh3b3JrZXJIYW5kbGUpO1xyXG5cclxuICAgIC8vIFVzZWQgZm9yIHN0YXRpc3RpY3MgaW4gYXZlcmFnZVRpbWUgYW5kIHByb2Nlc3NlZFdvcmtDb3VudCwgd2hpY2hcclxuICAgIC8vIGluIHR1cm4gaXMgdXNlZCBieSB0aGUgL2hlYWx0aCByb3V0ZS5cclxuICAgIGNvbnN0IHdvcmtFbmQgPSBuZXcgRGF0ZSgpLmdldFRpbWUoKTtcclxuICAgIGNvbnN0IGV4cG9ydFRpbWUgPSB3b3JrRW5kIC0gd29ya1N0YXJ0O1xyXG4gICAgc3RhdHMudGltZVNwZW50ICs9IGV4cG9ydFRpbWU7XHJcbiAgICBzdGF0cy5zcGVudEF2ZXJhZ2UgPSBzdGF0cy50aW1lU3BlbnQgLyArK3N0YXRzLnBlcmZvcm1lZEV4cG9ydHM7XHJcblxyXG4gICAgbG9nKDQsIGBbcG9vbF0gV29yayBjb21wbGV0ZWQgaW4gJHtleHBvcnRUaW1lfSBtcy5gKTtcclxuXHJcbiAgICAvLyBPdGhlcndpc2UgcmV0dXJuIHRoZSByZXN1bHRcclxuICAgIHJldHVybiB7XHJcbiAgICAgIHJlc3VsdCxcclxuICAgICAgb3B0aW9uc1xyXG4gICAgfTtcclxuICB9IGNhdGNoIChlcnJvcikge1xyXG4gICAgKytzdGF0cy5kcm9wcGVkRXhwb3J0cztcclxuXHJcbiAgICBpZiAod29ya2VySGFuZGxlKSB7XHJcbiAgICAgIHBvb2wucmVsZWFzZSh3b3JrZXJIYW5kbGUpO1xyXG4gICAgfVxyXG5cclxuICAgIHRocm93IG5ldyBFeHBvcnRFcnJvcihgW3Bvb2xdIEluIHBvb2wucG9zdFdvcms6ICR7ZXJyb3IubWVzc2FnZX1gKS5zZXRFcnJvcihcclxuICAgICAgZXJyb3JcclxuICAgICk7XHJcbiAgfVxyXG59O1xyXG5cclxuLyoqXHJcbiAqIFJldHJpZXZlcyB0aGUgY3VycmVudCBwb29sIGluc3RhbmNlLlxyXG4gKlxyXG4gKiBAcmV0dXJucyB7T2JqZWN0fG51bGx9IFRoZSBjdXJyZW50IHBvb2wgaW5zdGFuY2UgaWYgaW5pdGlhbGl6ZWQsIG9yIG51bGxcclxuICogaWYgdGhlIHBvb2wgaGFzIG5vdCBiZWVuIGNyZWF0ZWQuXHJcbiAqL1xyXG5leHBvcnQgY29uc3QgZ2V0UG9vbCA9ICgpID0+IHBvb2w7XHJcblxyXG4vKipcclxuICogUmV0cmlldmVzIHBvb2wgaW5mb3JtYXRpb24gaW4gSlNPTiBmb3JtYXQsIGluY2x1ZGluZyBtaW5pbXVtIGFuZCBtYXhpbXVtXHJcbiAqIHdvcmtlcnMsIGF2YWlsYWJsZSB3b3JrZXJzLCB3b3JrZXJzIGluIHVzZSwgYW5kIHBlbmRpbmcgYWNxdWlyZSByZXF1ZXN0cy5cclxuICpcclxuICogQHJldHVybnMge09iamVjdH0gUG9vbCBpbmZvcm1hdGlvbiBpbiBKU09OIGZvcm1hdC5cclxuICovXHJcbmV4cG9ydCBjb25zdCBnZXRQb29sSW5mb0pTT04gPSAoKSA9PiAoe1xyXG4gIG1pbjogcG9vbC5taW4sXHJcbiAgbWF4OiBwb29sLm1heCxcclxuICBhbGw6IHBvb2wubnVtRnJlZSgpICsgcG9vbC5udW1Vc2VkKCksXHJcbiAgYXZhaWxhYmxlOiBwb29sLm51bUZyZWUoKSxcclxuICB1c2VkOiBwb29sLm51bVVzZWQoKSxcclxuICBwZW5kaW5nOiBwb29sLm51bVBlbmRpbmdBY3F1aXJlcygpXHJcbn0pO1xyXG5cclxuLyoqXHJcbiAqIExvZ3MgaW5mb3JtYXRpb24gYWJvdXQgdGhlIGN1cnJlbnQgc3RhdGUgb2YgdGhlIHBvb2wsIGluY2x1ZGluZyB0aGUgbWluaW11bVxyXG4gKiBhbmQgbWF4aW11bSB3b3JrZXJzLCBhdmFpbGFibGUgd29ya2Vycywgd29ya2VycyBpbiB1c2UsIGFuZCBwZW5kaW5nIGFjcXVpcmVcclxuICogcmVxdWVzdHMuXHJcbiAqL1xyXG5leHBvcnQgZnVuY3Rpb24gZ2V0UG9vbEluZm8oKSB7XHJcbiAgY29uc3QgeyBtaW4sIG1heCwgYWxsLCBhdmFpbGFibGUsIHVzZWQsIHBlbmRpbmcgfSA9IGdldFBvb2xJbmZvSlNPTigpO1xyXG5cclxuICBsb2coNSwgYFtwb29sXSBUaGUgbWluaW11bSBudW1iZXIgb2YgcmVzb3VyY2VzIGFsbG93ZWQgYnkgcG9vbDogJHttaW59LmApO1xyXG4gIGxvZyg1LCBgW3Bvb2xdIFRoZSBtYXhpbXVtIG51bWJlciBvZiByZXNvdXJjZXMgYWxsb3dlZCBieSBwb29sOiAke21heH0uYCk7XHJcbiAgbG9nKDUsIGBbcG9vbF0gVGhlIG51bWJlciBvZiBhbGwgY3JlYXRlZCByZXNvdXJjZXM6ICR7YWxsfS5gKTtcclxuICBsb2coNSwgYFtwb29sXSBUaGUgbnVtYmVyIG9mIGF2YWlsYWJsZSByZXNvdXJjZXM6ICR7YXZhaWxhYmxlfS5gKTtcclxuICBsb2coNSwgYFtwb29sXSBUaGUgbnVtYmVyIG9mIGFjcXVpcmVkIHJlc291cmNlczogJHt1c2VkfS5gKTtcclxuICBsb2coNSwgYFtwb29sXSBUaGUgbnVtYmVyIG9mIHJlc291cmNlcyB3YWl0aW5nIHRvIGJlIGFjcXVpcmVkOiAke3BlbmRpbmd9LmApO1xyXG59XHJcblxyXG5leHBvcnQgZGVmYXVsdCB7XHJcbiAgaW5pdFBvb2wsXHJcbiAga2lsbFBvb2wsXHJcbiAgcG9zdFdvcmssXHJcbiAgZ2V0UG9vbCxcclxuICBnZXRQb29sSW5mbyxcclxuICBnZXRQb29sSW5mb0pTT04sXHJcbiAgZ2V0U3RhdHM6ICgpID0+IHN0YXRzXHJcbn07XHJcbiIsIi8qKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqXHJcblxyXG5IaWdoY2hhcnRzIEV4cG9ydCBTZXJ2ZXJcclxuXHJcbkNvcHlyaWdodCAoYykgMjAxNi0yMDI0LCBIaWdoc29mdFxyXG5cclxuTGljZW5jZWQgdW5kZXIgdGhlIE1JVCBsaWNlbmNlLlxyXG5cclxuQWRkaXRpb25hbGx5IGEgdmFsaWQgSGlnaGNoYXJ0cyBsaWNlbnNlIGlzIHJlcXVpcmVkIGZvciB1c2UuXHJcblxyXG5TZWUgTElDRU5TRSBmaWxlIGluIHJvb3QgZm9yIGRldGFpbHMuXHJcblxyXG4qKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqL1xyXG5cclxuaW1wb3J0IHsgcmVhZEZpbGVTeW5jLCB3cml0ZUZpbGVTeW5jIH0gZnJvbSAnZnMnO1xyXG5cclxuaW1wb3J0IHsgZ2V0T3B0aW9ucywgaW5pdEV4cG9ydFNldHRpbmdzIH0gZnJvbSAnLi9jb25maWcuanMnO1xyXG5pbXBvcnQgeyBsb2csIGxvZ1dpdGhTdGFjayB9IGZyb20gJy4vbG9nZ2VyLmpzJztcclxuaW1wb3J0IHsga2lsbFBvb2wsIHBvc3RXb3JrLCBzdGF0cyB9IGZyb20gJy4vcG9vbC5qcyc7XHJcbmltcG9ydCB7XHJcbiAgZml4VHlwZSxcclxuICBoYW5kbGVSZXNvdXJjZXMsXHJcbiAgaXNDb3JyZWN0SlNPTixcclxuICBvcHRpb25zU3RyaW5naWZ5LFxyXG4gIHJvdW5kTnVtYmVyLFxyXG4gIHRvQm9vbGVhbixcclxuICB3cmFwQXJvdW5kXHJcbn0gZnJvbSAnLi91dGlscy5qcyc7XHJcbmltcG9ydCB7IHNhbml0aXplIH0gZnJvbSAnLi9zYW5pdGl6ZS5qcyc7XHJcbmltcG9ydCBFeHBvcnRFcnJvciBmcm9tICcuL2Vycm9ycy9FeHBvcnRFcnJvci5qcyc7XHJcblxyXG5sZXQgYWxsb3dDb2RlRXhlY3V0aW9uID0gZmFsc2U7XHJcblxyXG4vKipcclxuICogU3RhcnRzIGFuIGV4cG9ydCBwcm9jZXNzLiBUaGUgYHNldHRpbmdzYCBjb250YWlucyBmaW5hbCBvcHRpb25zIGdhdGhlcmVkXHJcbiAqIGZyb20gYWxsIHBvc3NpYmxlIHNvdXJjZXMgKGNvbmZpZywgZW52LCBjbGksIGpzb24pLiBUaGUgYGVuZENhbGxiYWNrYCBpc1xyXG4gKiBjYWxsZWQgd2hlbiB0aGUgZXhwb3J0IGlzIGNvbXBsZXRlZCwgd2l0aCBhbiBlcnJvciBvYmplY3QgYXMgdGhlIGZpcnN0XHJcbiAqIGFyZ3VtZW50IGFuZCB0aGUgc2Vjb25kIGNvbnRhaW5pbmcgdGhlIGJhc2U2NCByZXNwcmVzZW50YXRpb24gb2YgYSBjaGFydC5cclxuICpcclxuICogQHBhcmFtIHtPYmplY3R9IHNldHRpbmdzIC0gVGhlIHNldHRpbmdzIG9iamVjdCBjb250YWluaW5nIGV4cG9ydFxyXG4gKiBjb25maWd1cmF0aW9uLlxyXG4gKiBAcGFyYW0ge2Z1bmN0aW9ufSBlbmRDYWxsYmFjayAtIFRoZSBjYWxsYmFjayBmdW5jdGlvbiB0byBiZSBpbnZva2VkIHVwb25cclxuICogZmluYWxpemluZyB3b3JrIG9yIHVwb24gZXJyb3Igb2NjdXJhbmNlIG9mIHRoZSBleHBvcnRpbmcgcHJvY2Vzcy5cclxuICpcclxuICogQHJldHVybnMge3ZvaWR9IFRoaXMgZnVuY3Rpb24gZG9lcyBub3QgcmV0dXJuIGEgdmFsdWUgZGlyZWN0bHk7IGluc3RlYWQsXHJcbiAqIGl0IGNvbW11bmljYXRlcyByZXN1bHRzIHZpYSB0aGUgZW5kQ2FsbGJhY2suXHJcbiAqL1xyXG5leHBvcnQgY29uc3Qgc3RhcnRFeHBvcnQgPSBhc3luYyAoc2V0dGluZ3MsIGVuZENhbGxiYWNrKSA9PiB7XHJcbiAgLy8gU3RhcnRpbmcgZXhwb3J0aW5nIHByb2Nlc3MgbWVzc2FnZVxyXG4gIGxvZyg0LCAnW2NoYXJ0XSBTdGFydGluZyB0aGUgZXhwb3J0aW5nIHByb2Nlc3MuJyk7XHJcblxyXG4gIC8vIEluaXRpYWxpemUgb3B0aW9uc1xyXG4gIGNvbnN0IG9wdGlvbnMgPSBpbml0RXhwb3J0U2V0dGluZ3Moc2V0dGluZ3MsIGdldE9wdGlvbnMoKSk7XHJcblxyXG4gIC8vIEdldCB0aGUgZXhwb3J0IG9wdGlvbnNcclxuICBjb25zdCBleHBvcnRPcHRpb25zID0gb3B0aW9ucy5leHBvcnQ7XHJcblxyXG4gIC8vIElmIFNWRyBpcyBhbiBpbnB1dCAoYXJndW1lbnQgY2FuIGJlIHNlbnQgb25seSBieSB0aGUgcmVxdWVzdClcclxuICBpZiAob3B0aW9ucy5wYXlsb2FkPy5zdmcgJiYgb3B0aW9ucy5wYXlsb2FkLnN2ZyAhPT0gJycpIHtcclxuICAgIHRyeSB7XHJcbiAgICAgIGxvZyg0LCAnW2NoYXJ0XSBBdHRlbXB0aW5nIHRvIGV4cG9ydCBmcm9tIGEgU1ZHIGlucHV0LicpO1xyXG5cclxuICAgICAgY29uc3QgcmVzdWx0ID0gZXhwb3J0QXNTdHJpbmcoXHJcbiAgICAgICAgc2FuaXRpemUob3B0aW9ucy5wYXlsb2FkLnN2ZyksIC8vICMyMDlcclxuICAgICAgICBvcHRpb25zLFxyXG4gICAgICAgIGVuZENhbGxiYWNrXHJcbiAgICAgICk7XHJcblxyXG4gICAgICArK3N0YXRzLmV4cG9ydEZyb21TdmdBdHRlbXB0cztcclxuICAgICAgcmV0dXJuIHJlc3VsdDtcclxuICAgIH0gY2F0Y2ggKGVycm9yKSB7XHJcbiAgICAgIHJldHVybiBlbmRDYWxsYmFjayhcclxuICAgICAgICBuZXcgRXhwb3J0RXJyb3IoJ1tjaGFydF0gRXJyb3IgbG9hZGluZyBTVkcgaW5wdXQuJykuc2V0RXJyb3IoZXJyb3IpXHJcbiAgICAgICk7XHJcbiAgICB9XHJcbiAgfVxyXG5cclxuICAvLyBFeHBvcnQgdXNpbmcgb3B0aW9ucyBmcm9tIHRoZSBmaWxlXHJcbiAgaWYgKGV4cG9ydE9wdGlvbnMuaW5maWxlICYmIGV4cG9ydE9wdGlvbnMuaW5maWxlLmxlbmd0aCkge1xyXG4gICAgLy8gVHJ5IHRvIHJlYWQgdGhlIGZpbGUgdG8gZ2V0IHRoZSBzdHJpbmcgcmVwcmVzZW50YXRpb25cclxuICAgIHRyeSB7XHJcbiAgICAgIGxvZyg0LCAnW2NoYXJ0XSBBdHRlbXB0aW5nIHRvIGV4cG9ydCBmcm9tIGFuIGlucHV0IGZpbGUuJyk7XHJcbiAgICAgIG9wdGlvbnMuZXhwb3J0Lmluc3RyID0gcmVhZEZpbGVTeW5jKGV4cG9ydE9wdGlvbnMuaW5maWxlLCAndXRmOCcpO1xyXG4gICAgICByZXR1cm4gZXhwb3J0QXNTdHJpbmcob3B0aW9ucy5leHBvcnQuaW5zdHIudHJpbSgpLCBvcHRpb25zLCBlbmRDYWxsYmFjayk7XHJcbiAgICB9IGNhdGNoIChlcnJvcikge1xyXG4gICAgICByZXR1cm4gZW5kQ2FsbGJhY2soXHJcbiAgICAgICAgbmV3IEV4cG9ydEVycm9yKCdbY2hhcnRdIEVycm9yIGxvYWRpbmcgaW5wdXQgZmlsZS4nKS5zZXRFcnJvcihlcnJvcilcclxuICAgICAgKTtcclxuICAgIH1cclxuICB9XHJcblxyXG4gIC8vIEV4cG9ydCB3aXRoIG9wdGlvbnMgZnJvbSB0aGUgcmF3IHJlcHJlc2VudGF0aW9uXHJcbiAgaWYgKFxyXG4gICAgKGV4cG9ydE9wdGlvbnMuaW5zdHIgJiYgZXhwb3J0T3B0aW9ucy5pbnN0ciAhPT0gJycpIHx8XHJcbiAgICAoZXhwb3J0T3B0aW9ucy5vcHRpb25zICYmIGV4cG9ydE9wdGlvbnMub3B0aW9ucyAhPT0gJycpXHJcbiAgKSB7XHJcbiAgICB0cnkge1xyXG4gICAgICBsb2coNCwgJ1tjaGFydF0gQXR0ZW1wdGluZyB0byBleHBvcnQgZnJvbSBhIHJhdyBpbnB1dC4nKTtcclxuXHJcbiAgICAgIC8vIFBlcmZvcm0gYSBkaXJlY3QgaW5qZWN0IHdoZW4gZm9yY2VkXHJcbiAgICAgIGlmICh0b0Jvb2xlYW4ob3B0aW9ucy5jdXN0b21Mb2dpYz8uYWxsb3dDb2RlRXhlY3V0aW9uKSkge1xyXG4gICAgICAgIHJldHVybiBkb1N0cmFpZ2h0SW5qZWN0KG9wdGlvbnMsIGVuZENhbGxiYWNrKTtcclxuICAgICAgfVxyXG5cclxuICAgICAgLy8gRWl0aGVyIHRyeSB0byBwYXJzZSB0byBKU09OIGZpcnN0IG9yIGRvIHRoZSBkaXJlY3QgZXhwb3J0XHJcbiAgICAgIHJldHVybiB0eXBlb2YgZXhwb3J0T3B0aW9ucy5pbnN0ciA9PT0gJ3N0cmluZydcclxuICAgICAgICA/IGV4cG9ydEFzU3RyaW5nKGV4cG9ydE9wdGlvbnMuaW5zdHIudHJpbSgpLCBvcHRpb25zLCBlbmRDYWxsYmFjaylcclxuICAgICAgICA6IGRvRXhwb3J0KFxyXG4gICAgICAgICAgICBvcHRpb25zLFxyXG4gICAgICAgICAgICBleHBvcnRPcHRpb25zLmluc3RyIHx8IGV4cG9ydE9wdGlvbnMub3B0aW9ucyxcclxuICAgICAgICAgICAgZW5kQ2FsbGJhY2tcclxuICAgICAgICAgICk7XHJcbiAgICB9IGNhdGNoIChlcnJvcikge1xyXG4gICAgICByZXR1cm4gZW5kQ2FsbGJhY2soXHJcbiAgICAgICAgbmV3IEV4cG9ydEVycm9yKCdbY2hhcnRdIEVycm9yIGxvYWRpbmcgcmF3IGlucHV0LicpLnNldEVycm9yKGVycm9yKVxyXG4gICAgICApO1xyXG4gICAgfVxyXG4gIH1cclxuXHJcbiAgLy8gTm8gaW5wdXQgc3BlY2lmaWVkLCBwYXNzIGFuIGVycm9yIG1lc3NhZ2UgdG8gdGhlIGNhbGxiYWNrXHJcbiAgcmV0dXJuIGVuZENhbGxiYWNrKFxyXG4gICAgbmV3IEV4cG9ydEVycm9yKFxyXG4gICAgICBgW2NoYXJ0XSBObyB2YWxpZCBpbnB1dCBzcGVjaWZpZWQuIENoZWNrIGlmIGF0IGxlYXN0IG9uZSBvZiB0aGUgZm9sbG93aW5nIHBhcmFtZXRlcnMgaXMgY29ycmVjdGx5IHNldDogJ2luZmlsZScsICdpbnN0cicsICdvcHRpb25zJywgb3IgJ3N2ZycuYFxyXG4gICAgKVxyXG4gICk7XHJcbn07XHJcblxyXG4vKipcclxuICogU3RhcnRzIGEgYmF0Y2ggZXhwb3J0IHByb2Nlc3MgZm9yIG11bHRpcGxlIGNoYXJ0cyBiYXNlZCBvbiB0aGUgaW5mb3JtYXRpb25cclxuICogaW4gdGhlIGJhdGNoIG9wdGlvbi4gVGhlIGJhdGNoIGlzIGEgc3RyaW5nIGluIHRoZSBmb2xsb3dpbmcgZm9ybWF0OlxyXG4gKiBcImluZmlsZTEuanNvbj1vdXRmaWxlMS5wbmc7aW5maWxlMi5qc29uPW91dGZpbGUyLnBuZzsuLi5cIlxyXG4gKlxyXG4gKiBAcGFyYW0ge09iamVjdH0gb3B0aW9ucyAtIFRoZSBvcHRpb25zIG9iamVjdCBjb250YWluaW5nIGNvbmZpZ3VyYXRpb24gZm9yXHJcbiAqIGEgYmF0Y2ggZXhwb3J0LlxyXG4gKlxyXG4gKiBAcmV0dXJucyB7UHJvbWlzZTx2b2lkPn0gQSBQcm9taXNlIHRoYXQgcmVzb2x2ZXMgb25jZSB0aGUgYmF0Y2ggZXhwb3J0XHJcbiAqIHByb2Nlc3MgaXMgY29tcGxldGVkLlxyXG4gKlxyXG4gKiBAdGhyb3dzIHtFeHBvcnRFcnJvcn0gVGhyb3dzIGFuIEV4cG9ydEVycm9yIGlmIGFuIGVycm9yIG9jY3VycyBkdXJpbmdcclxuICogYW55IG9mIHRoZSBiYXRjaCBleHBvcnQgcHJvY2Vzcy5cclxuICovXHJcbmV4cG9ydCBjb25zdCBiYXRjaEV4cG9ydCA9IGFzeW5jIChvcHRpb25zKSA9PiB7XHJcbiAgY29uc3QgYmF0Y2hGdW5jdGlvbnMgPSBbXTtcclxuXHJcbiAgLy8gU3BsaXQgYW5kIHBhaXIgdGhlIC0tYmF0Y2ggYXJndW1lbnRzXHJcbiAgZm9yIChsZXQgcGFpciBvZiBvcHRpb25zLmV4cG9ydC5iYXRjaC5zcGxpdCgnOycpKSB7XHJcbiAgICBwYWlyID0gcGFpci5zcGxpdCgnPScpO1xyXG4gICAgaWYgKHBhaXIubGVuZ3RoID09PSAyKSB7XHJcbiAgICAgIGJhdGNoRnVuY3Rpb25zLnB1c2goXHJcbiAgICAgICAgc3RhcnRFeHBvcnQoXHJcbiAgICAgICAgICB7XHJcbiAgICAgICAgICAgIC4uLm9wdGlvbnMsXHJcbiAgICAgICAgICAgIGV4cG9ydDoge1xyXG4gICAgICAgICAgICAgIC4uLm9wdGlvbnMuZXhwb3J0LFxyXG4gICAgICAgICAgICAgIGluZmlsZTogcGFpclswXSxcclxuICAgICAgICAgICAgICBvdXRmaWxlOiBwYWlyWzFdXHJcbiAgICAgICAgICAgIH1cclxuICAgICAgICAgIH0sXHJcbiAgICAgICAgICAoZXJyb3IsIGluZm8pID0+IHtcclxuICAgICAgICAgICAgLy8gVGhyb3cgYW4gZXJyb3JcclxuICAgICAgICAgICAgaWYgKGVycm9yKSB7XHJcbiAgICAgICAgICAgICAgdGhyb3cgZXJyb3I7XHJcbiAgICAgICAgICAgIH1cclxuXHJcbiAgICAgICAgICAgIC8vIFNhdmUgdGhlIGJhc2U2NCBmcm9tIGEgYnVmZmVyIHRvIGEgY29ycmVjdCBpbWFnZSBmaWxlXHJcbiAgICAgICAgICAgIHdyaXRlRmlsZVN5bmMoXHJcbiAgICAgICAgICAgICAgaW5mby5vcHRpb25zLmV4cG9ydC5vdXRmaWxlLFxyXG4gICAgICAgICAgICAgIGluZm8ub3B0aW9ucy5leHBvcnQudHlwZSAhPT0gJ3N2ZydcclxuICAgICAgICAgICAgICAgID8gQnVmZmVyLmZyb20oaW5mby5yZXN1bHQsICdiYXNlNjQnKVxyXG4gICAgICAgICAgICAgICAgOiBpbmZvLnJlc3VsdFxyXG4gICAgICAgICAgICApO1xyXG4gICAgICAgICAgfVxyXG4gICAgICAgIClcclxuICAgICAgKTtcclxuICAgIH1cclxuICB9XHJcblxyXG4gIHRyeSB7XHJcbiAgICAvLyBBd2FpdCBhbGwgZXhwb3J0cyBhcmUgZG9uZVxyXG4gICAgYXdhaXQgUHJvbWlzZS5hbGwoYmF0Y2hGdW5jdGlvbnMpO1xyXG5cclxuICAgIC8vIEtpbGwgcG9vbCBhbmQgY2xvc2UgYnJvd3NlciBhZnRlciBmaW5pc2hpbmcgYmF0Y2ggZXhwb3J0XHJcbiAgICBhd2FpdCBraWxsUG9vbCgpO1xyXG4gIH0gY2F0Y2ggKGVycm9yKSB7XHJcbiAgICB0aHJvdyBuZXcgRXhwb3J0RXJyb3IoXHJcbiAgICAgICdbY2hhcnRdIEVycm9yIGVuY291bnRlcmVkIGR1cmluZyBiYXRjaCBleHBvcnQuJ1xyXG4gICAgKS5zZXRFcnJvcihlcnJvcik7XHJcbiAgfVxyXG59O1xyXG5cclxuLyoqXHJcbiAqIFN0YXJ0cyBhIHNpbmdsZSBleHBvcnQgcHJvY2VzcyBiYXNlZCBvbiB0aGUgc3BlY2lmaWVkIG9wdGlvbnMuXHJcbiAqXHJcbiAqIEBwYXJhbSB7T2JqZWN0fSBvcHRpb25zIC0gVGhlIG9wdGlvbnMgb2JqZWN0IGNvbnRhaW5pbmcgY29uZmlndXJhdGlvbiBmb3JcclxuICogYSBzaW5nbGUgZXhwb3J0LlxyXG4gKlxyXG4gKiBAcmV0dXJucyB7UHJvbWlzZTx2b2lkPn0gQSBQcm9taXNlIHRoYXQgcmVzb2x2ZXMgb25jZSB0aGUgc2luZ2xlIGV4cG9ydFxyXG4gKiBwcm9jZXNzIGlzIGNvbXBsZXRlZC5cclxuICpcclxuICogQHRocm93cyB7RXhwb3J0RXJyb3J9IFRocm93cyBhbiBFeHBvcnRFcnJvciBpZiBhbiBlcnJvciBvY2N1cnMgZHVyaW5nXHJcbiAqIHRoZSBzaW5nbGUgZXhwb3J0IHByb2Nlc3MuXHJcbiAqL1xyXG5leHBvcnQgY29uc3Qgc2luZ2xlRXhwb3J0ID0gYXN5bmMgKG9wdGlvbnMpID0+IHtcclxuICAvLyBVc2UgaW5zdHIgb3IgaXRzIGFsaWFzLCBvcHRpb25zXHJcbiAgb3B0aW9ucy5leHBvcnQuaW5zdHIgPSBvcHRpb25zLmV4cG9ydC5pbnN0ciB8fCBvcHRpb25zLmV4cG9ydC5vcHRpb25zO1xyXG5cclxuICAvLyBQZXJmb3JtIGFuIGV4cG9ydFxyXG4gIGF3YWl0IHN0YXJ0RXhwb3J0KG9wdGlvbnMsIGFzeW5jIChlcnJvciwgaW5mbykgPT4ge1xyXG4gICAgLy8gRXhpdCBwcm9jZXNzIHdoZW4gZXJyb3JcclxuICAgIGlmIChlcnJvcikge1xyXG4gICAgICB0aHJvdyBlcnJvcjtcclxuICAgIH1cclxuXHJcbiAgICBjb25zdCB7IG91dGZpbGUsIHR5cGUgfSA9IGluZm8ub3B0aW9ucy5leHBvcnQ7XHJcblxyXG4gICAgLy8gU2F2ZSB0aGUgYmFzZTY0IGZyb20gYSBidWZmZXIgdG8gYSBjb3JyZWN0IGltYWdlIGZpbGVcclxuICAgIHdyaXRlRmlsZVN5bmMoXHJcbiAgICAgIG91dGZpbGUgfHwgYGNoYXJ0LiR7dHlwZX1gLFxyXG4gICAgICB0eXBlICE9PSAnc3ZnJyA/IEJ1ZmZlci5mcm9tKGluZm8ucmVzdWx0LCAnYmFzZTY0JykgOiBpbmZvLnJlc3VsdFxyXG4gICAgKTtcclxuXHJcbiAgICAvLyBLaWxsIHBvb2wgYW5kIGNsb3NlIGJyb3dzZXIgYWZ0ZXIgZmluaXNoaW5nIHNpbmdsZSBleHBvcnRcclxuICAgIGF3YWl0IGtpbGxQb29sKCk7XHJcbiAgfSk7XHJcbn07XHJcblxyXG4vKipcclxuICogRGV0ZXJtaW5lcyB0aGUgc2l6ZSBhbmQgc2NhbGUgZm9yIGNoYXJ0IGV4cG9ydCBiYXNlZCBvbiB0aGUgcHJvdmlkZWQgb3B0aW9ucy5cclxuICpcclxuICogQHBhcmFtIHtPYmplY3R9IG9wdGlvbnMgLSBUaGUgb3B0aW9ucyBvYmplY3QgY29udGFpbmluZyBjb25maWd1cmF0aW9uIGZvclxyXG4gKiBjaGFydCBleHBvcnQuXHJcbiAqXHJcbiAqIEByZXR1cm5zIHtPYmplY3R9IEFuIG9iamVjdCBjb250YWluaW5nIHRoZSBjYWxjdWxhdGVkIGhlaWdodCwgd2lkdGgsXHJcbiAqIGFuZCBzY2FsZSBmb3IgdGhlIGNoYXJ0IGV4cG9ydC5cclxuICovXHJcbmV4cG9ydCBjb25zdCBmaW5kQ2hhcnRTaXplID0gKG9wdGlvbnMpID0+IHtcclxuICBjb25zdCB7IGNoYXJ0LCBleHBvcnRpbmcgfSA9XHJcbiAgICBvcHRpb25zLmV4cG9ydD8ub3B0aW9ucyB8fCBpc0NvcnJlY3RKU09OKG9wdGlvbnMuZXhwb3J0Py5pbnN0cik7XHJcblxyXG4gIC8vIFNlZSBpZiBnbG9iYWxPcHRpb25zIGhvbGRzIGNoYXJ0IG9yIGV4cG9ydGluZyBzaXplXHJcbiAgY29uc3QgZ2xvYmFsT3B0aW9ucyA9IGlzQ29ycmVjdEpTT04ob3B0aW9ucy5leHBvcnQ/Lmdsb2JhbE9wdGlvbnMpO1xyXG5cclxuICAvLyBTZWN1cmUgc2NhbGUgdmFsdWVcclxuICBsZXQgc2NhbGUgPVxyXG4gICAgb3B0aW9ucy5leHBvcnQ/LnNjYWxlIHx8XHJcbiAgICBleHBvcnRpbmc/LnNjYWxlIHx8XHJcbiAgICBnbG9iYWxPcHRpb25zPy5leHBvcnRpbmc/LnNjYWxlIHx8XHJcbiAgICBvcHRpb25zLmV4cG9ydD8uZGVmYXVsdFNjYWxlIHx8XHJcbiAgICAxO1xyXG5cclxuICAvLyB0aGUgc2NhbGUgY2Fubm90IGJlIGxvd2VyIHRoYW4gMC4xIGFuZCBjYW5ub3QgYmUgaGlnaGVyIHRoYW4gNS4wXHJcbiAgc2NhbGUgPSBNYXRoLm1heCgwLjEsIE1hdGgubWluKHNjYWxlLCA1LjApKTtcclxuXHJcbiAgLy8gd2Ugd2FudCB0byByb3VuZCB0aGUgbnVtYmVycyBsaWtlIDAuMjMyMzQgLT4gMC4yM1xyXG4gIHNjYWxlID0gcm91bmROdW1iZXIoc2NhbGUsIDIpO1xyXG5cclxuICAvLyBGaW5kIGNoYXJ0IHNpemUgYW5kIHNjYWxlXHJcbiAgY29uc3Qgc2l6ZSA9IHtcclxuICAgIGhlaWdodDpcclxuICAgICAgb3B0aW9ucy5leHBvcnQ/LmhlaWdodCB8fFxyXG4gICAgICBleHBvcnRpbmc/LnNvdXJjZUhlaWdodCB8fFxyXG4gICAgICBjaGFydD8uaGVpZ2h0IHx8XHJcbiAgICAgIGdsb2JhbE9wdGlvbnM/LmV4cG9ydGluZz8uc291cmNlSGVpZ2h0IHx8XHJcbiAgICAgIGdsb2JhbE9wdGlvbnM/LmNoYXJ0Py5oZWlnaHQgfHxcclxuICAgICAgb3B0aW9ucy5leHBvcnQ/LmRlZmF1bHRIZWlnaHQgfHxcclxuICAgICAgNDAwLFxyXG4gICAgd2lkdGg6XHJcbiAgICAgIG9wdGlvbnMuZXhwb3J0Py53aWR0aCB8fFxyXG4gICAgICBleHBvcnRpbmc/LnNvdXJjZVdpZHRoIHx8XHJcbiAgICAgIGNoYXJ0Py53aWR0aCB8fFxyXG4gICAgICBnbG9iYWxPcHRpb25zPy5leHBvcnRpbmc/LnNvdXJjZVdpZHRoIHx8XHJcbiAgICAgIGdsb2JhbE9wdGlvbnM/LmNoYXJ0Py53aWR0aCB8fFxyXG4gICAgICBvcHRpb25zLmV4cG9ydD8uZGVmYXVsdFdpZHRoIHx8XHJcbiAgICAgIDYwMCxcclxuICAgIHNjYWxlXHJcbiAgfTtcclxuXHJcbiAgLy8gR2V0IHJpZCBvZiBwb3RlbnRpYWwgcHggYW5kICVcclxuICBmb3IgKGxldCBbcGFyYW0sIHZhbHVlXSBvZiBPYmplY3QuZW50cmllcyhzaXplKSkge1xyXG4gICAgc2l6ZVtwYXJhbV0gPVxyXG4gICAgICB0eXBlb2YgdmFsdWUgPT09ICdzdHJpbmcnID8gK3ZhbHVlLnJlcGxhY2UoL3B4fCUvZ2ksICcnKSA6IHZhbHVlO1xyXG4gIH1cclxuICByZXR1cm4gc2l6ZTtcclxufTtcclxuXHJcbi8qKlxyXG4gKiBGdW5jdGlvbiBmb3IgZmluYWxpemluZyBvcHRpb25zIGJlZm9yZSBleHBvcnQuXHJcbiAqXHJcbiAqIEBwYXJhbSB7T2JqZWN0fSBvcHRpb25zIC0gVGhlIG9wdGlvbnMgb2JqZWN0IGNvbnRhaW5pbmcgY29uZmlndXJhdGlvbiBmb3JcclxuICogdGhlIGV4cG9ydCBwcm9jZXNzLlxyXG4gKiBAcGFyYW0ge09iamVjdH0gY2hhcnRKc29uIC0gVGhlIEpTT04gcmVwcmVzZW50YXRpb24gb2YgdGhlIGNoYXJ0LlxyXG4gKiBAcGFyYW0ge0Z1bmN0aW9ufSBlbmRDYWxsYmFjayAtIFRoZSBjYWxsYmFjayBmdW5jdGlvbiB0byBiZSBjYWxsZWQgdXBvblxyXG4gKiBjb21wbGV0aW9uIG9yIGVycm9yLlxyXG4gKiBAcGFyYW0ge3N0cmluZ30gc3ZnIC0gVGhlIFNWRyByZXByZXNlbnRhdGlvbiBvZiB0aGUgY2hhcnQuXHJcbiAqXHJcbiAqIEByZXR1cm5zIHtQcm9taXNlPHZvaWQ+fSBBIFByb21pc2UgdGhhdCByZXNvbHZlcyBvbmNlIHRoZSBleHBvcnQgcHJvY2Vzc1xyXG4gKiBpcyBjb21wbGV0ZWQuXHJcbiAqL1xyXG5jb25zdCBkb0V4cG9ydCA9IGFzeW5jIChvcHRpb25zLCBjaGFydEpzb24sIGVuZENhbGxiYWNrLCBzdmcpID0+IHtcclxuICBsZXQgeyBleHBvcnQ6IGV4cG9ydE9wdGlvbnMsIGN1c3RvbUxvZ2ljOiBjdXN0b21Mb2dpY09wdGlvbnMgfSA9IG9wdGlvbnM7XHJcblxyXG4gIGNvbnN0IGFsbG93Q29kZUV4ZWN1dGlvblNjb3BlZCA9XHJcbiAgICB0eXBlb2YgY3VzdG9tTG9naWNPcHRpb25zLmFsbG93Q29kZUV4ZWN1dGlvbiA9PT0gJ2Jvb2xlYW4nXHJcbiAgICAgID8gY3VzdG9tTG9naWNPcHRpb25zLmFsbG93Q29kZUV4ZWN1dGlvblxyXG4gICAgICA6IGFsbG93Q29kZUV4ZWN1dGlvbjtcclxuXHJcbiAgaWYgKCFjdXN0b21Mb2dpY09wdGlvbnMpIHtcclxuICAgIGN1c3RvbUxvZ2ljT3B0aW9ucyA9IG9wdGlvbnMuY3VzdG9tTG9naWMgPSB7fTtcclxuICB9IGVsc2UgaWYgKGFsbG93Q29kZUV4ZWN1dGlvblNjb3BlZCkge1xyXG4gICAgaWYgKHR5cGVvZiBvcHRpb25zLmN1c3RvbUxvZ2ljLnJlc291cmNlcyA9PT0gJ3N0cmluZycpIHtcclxuICAgICAgLy8gUHJvY2VzcyByZXNvdXJjZXNcclxuICAgICAgb3B0aW9ucy5jdXN0b21Mb2dpYy5yZXNvdXJjZXMgPSBoYW5kbGVSZXNvdXJjZXMoXHJcbiAgICAgICAgb3B0aW9ucy5jdXN0b21Mb2dpYy5yZXNvdXJjZXMsXHJcbiAgICAgICAgdG9Cb29sZWFuKG9wdGlvbnMuY3VzdG9tTG9naWMuYWxsb3dGaWxlUmVzb3VyY2VzKVxyXG4gICAgICApO1xyXG4gICAgfSBlbHNlIGlmICghb3B0aW9ucy5jdXN0b21Mb2dpYy5yZXNvdXJjZXMpIHtcclxuICAgICAgdHJ5IHtcclxuICAgICAgICBjb25zdCByZXNvdXJjZXMgPSByZWFkRmlsZVN5bmMoJ3Jlc291cmNlcy5qc29uJywgJ3V0ZjgnKTtcclxuICAgICAgICBvcHRpb25zLmN1c3RvbUxvZ2ljLnJlc291cmNlcyA9IGhhbmRsZVJlc291cmNlcyhcclxuICAgICAgICAgIHJlc291cmNlcyxcclxuICAgICAgICAgIHRvQm9vbGVhbihvcHRpb25zLmN1c3RvbUxvZ2ljLmFsbG93RmlsZVJlc291cmNlcylcclxuICAgICAgICApO1xyXG4gICAgICB9IGNhdGNoIChlcnJvcikge1xyXG4gICAgICAgIGxvZ1dpdGhTdGFjayhcclxuICAgICAgICAgIDIsXHJcbiAgICAgICAgICBlcnJvcixcclxuICAgICAgICAgIGBbY2hhcnRdIFVuYWJsZSB0byBsb2FkIHRoZSBkZWZhdWx0IHJlc291cmNlcy5qc29uIGZpbGUuYFxyXG4gICAgICAgICk7XHJcbiAgICAgIH1cclxuICAgIH1cclxuICB9XHJcblxyXG4gIC8vIElmIHRoZSBhbGxvd0NvZGVFeGVjdXRpb24gZmxhZyBpc24ndCBzZXQsIHdlIHNob3VsZCByZWZ1c2UgdGhlIHVzYWdlXHJcbiAgLy8gb2YgY2FsbGJhY2ssIHJlc291cmNlcywgYW5kIGN1c3RvbSBjb2RlLiBBZGRpdGlvbmFsbHksIHRoZSB3b3JrZXIgd2lsbFxyXG4gIC8vIHJlZnVzZSB0byBydW4gYXJiaXRyYXJ5IEphdmFTY3JpcHQuIFByaW9yaXRpemVkIHNob3VsZCBiZSB0aGUgc2NvcGVkXHJcbiAgLy8gb3B0aW9uLCB0aGVuIHdlIHNob3VsZCB0YWtlIGEgbG9vayBhdCB0aGUgb3ZlcmFsbCBwb29sIG9wdGlvbi5cclxuICBpZiAoIWFsbG93Q29kZUV4ZWN1dGlvblNjb3BlZCAmJiBjdXN0b21Mb2dpY09wdGlvbnMpIHtcclxuICAgIGlmIChcclxuICAgICAgY3VzdG9tTG9naWNPcHRpb25zLmNhbGxiYWNrIHx8XHJcbiAgICAgIGN1c3RvbUxvZ2ljT3B0aW9ucy5yZXNvdXJjZXMgfHxcclxuICAgICAgY3VzdG9tTG9naWNPcHRpb25zLmN1c3RvbUNvZGVcclxuICAgICkge1xyXG4gICAgICAvLyBTZW5kIGJhY2sgYSBmcmllbmRseSBtZXNzYWdlIHNheWluZyB0aGF0IHRoZSBleHBvcnRlciBkb2VzIG5vdCBzdXBwb3J0XHJcbiAgICAgIC8vIHRoZXNlIHNldHRpbmdzLlxyXG4gICAgICByZXR1cm4gZW5kQ2FsbGJhY2soXHJcbiAgICAgICAgbmV3IEV4cG9ydEVycm9yKFxyXG4gICAgICAgICAgYFtjaGFydF0gVGhlICdjYWxsYmFjaycsICdyZXNvdXJjZXMnIGFuZCAnY3VzdG9tQ29kZScgb3B0aW9ucyBoYXZlIGJlZW4gZGlzYWJsZWQgZm9yIHRoaXMgc2VydmVyLmBcclxuICAgICAgICApXHJcbiAgICAgICk7XHJcbiAgICB9XHJcblxyXG4gICAgLy8gUmVzZXQgYWxsIGFkZGl0aW9uYWwgY3VzdG9tIGNvZGVcclxuICAgIGN1c3RvbUxvZ2ljT3B0aW9ucy5jYWxsYmFjayA9IGZhbHNlO1xyXG4gICAgY3VzdG9tTG9naWNPcHRpb25zLnJlc291cmNlcyA9IGZhbHNlO1xyXG4gICAgY3VzdG9tTG9naWNPcHRpb25zLmN1c3RvbUNvZGUgPSBmYWxzZTtcclxuICB9XHJcblxyXG4gIC8vIENsZWFuIHByb3BlcnRpZXMgdG8ga2VlcCBpdCBsZWFuIGFuZCBtZWFuXHJcbiAgaWYgKGNoYXJ0SnNvbikge1xyXG4gICAgY2hhcnRKc29uLmNoYXJ0ID0gY2hhcnRKc29uLmNoYXJ0IHx8IHt9O1xyXG4gICAgY2hhcnRKc29uLmV4cG9ydGluZyA9IGNoYXJ0SnNvbi5leHBvcnRpbmcgfHwge307XHJcbiAgICBjaGFydEpzb24uZXhwb3J0aW5nLmVuYWJsZWQgPSBmYWxzZTtcclxuICB9XHJcblxyXG4gIGV4cG9ydE9wdGlvbnMuY29uc3RyID0gZXhwb3J0T3B0aW9ucy5jb25zdHIgfHwgJ2NoYXJ0JztcclxuICBleHBvcnRPcHRpb25zLnR5cGUgPSBmaXhUeXBlKGV4cG9ydE9wdGlvbnMudHlwZSwgZXhwb3J0T3B0aW9ucy5vdXRmaWxlKTtcclxuICBpZiAoZXhwb3J0T3B0aW9ucy50eXBlID09PSAnc3ZnJykge1xyXG4gICAgZXhwb3J0T3B0aW9ucy53aWR0aCA9IGZhbHNlO1xyXG4gIH1cclxuXHJcbiAgLy8gUHJlcGFyZSBnbG9iYWwgYW5kIHRoZW1lIG9wdGlvbnNcclxuICBbJ2dsb2JhbE9wdGlvbnMnLCAndGhlbWVPcHRpb25zJ10uZm9yRWFjaCgob3B0aW9uc05hbWUpID0+IHtcclxuICAgIHRyeSB7XHJcbiAgICAgIGlmIChleHBvcnRPcHRpb25zICYmIGV4cG9ydE9wdGlvbnNbb3B0aW9uc05hbWVdKSB7XHJcbiAgICAgICAgaWYgKFxyXG4gICAgICAgICAgdHlwZW9mIGV4cG9ydE9wdGlvbnNbb3B0aW9uc05hbWVdID09PSAnc3RyaW5nJyAmJlxyXG4gICAgICAgICAgZXhwb3J0T3B0aW9uc1tvcHRpb25zTmFtZV0uZW5kc1dpdGgoJy5qc29uJylcclxuICAgICAgICApIHtcclxuICAgICAgICAgIGV4cG9ydE9wdGlvbnNbb3B0aW9uc05hbWVdID0gaXNDb3JyZWN0SlNPTihcclxuICAgICAgICAgICAgcmVhZEZpbGVTeW5jKGV4cG9ydE9wdGlvbnNbb3B0aW9uc05hbWVdLCAndXRmOCcpLFxyXG4gICAgICAgICAgICB0cnVlXHJcbiAgICAgICAgICApO1xyXG4gICAgICAgIH0gZWxzZSB7XHJcbiAgICAgICAgICBleHBvcnRPcHRpb25zW29wdGlvbnNOYW1lXSA9IGlzQ29ycmVjdEpTT04oXHJcbiAgICAgICAgICAgIGV4cG9ydE9wdGlvbnNbb3B0aW9uc05hbWVdLFxyXG4gICAgICAgICAgICB0cnVlXHJcbiAgICAgICAgICApO1xyXG4gICAgICAgIH1cclxuICAgICAgfVxyXG4gICAgfSBjYXRjaCAoZXJyb3IpIHtcclxuICAgICAgZXhwb3J0T3B0aW9uc1tvcHRpb25zTmFtZV0gPSB7fTtcclxuICAgICAgbG9nV2l0aFN0YWNrKDIsIGVycm9yLCBgW2NoYXJ0XSBUaGUgJyR7b3B0aW9uc05hbWV9JyBjYW5ub3QgYmUgbG9hZGVkLmApO1xyXG4gICAgfVxyXG4gIH0pO1xyXG5cclxuICAvLyBQcmVwYXJlIHRoZSBjdXN0b21Db2RlXHJcbiAgaWYgKGN1c3RvbUxvZ2ljT3B0aW9ucy5hbGxvd0NvZGVFeGVjdXRpb24pIHtcclxuICAgIHRyeSB7XHJcbiAgICAgIGN1c3RvbUxvZ2ljT3B0aW9ucy5jdXN0b21Db2RlID0gd3JhcEFyb3VuZChcclxuICAgICAgICBjdXN0b21Mb2dpY09wdGlvbnMuY3VzdG9tQ29kZSxcclxuICAgICAgICBjdXN0b21Mb2dpY09wdGlvbnMuYWxsb3dGaWxlUmVzb3VyY2VzXHJcbiAgICAgICk7XHJcbiAgICB9IGNhdGNoIChlcnJvcikge1xyXG4gICAgICBsb2dXaXRoU3RhY2soMiwgZXJyb3IsIGBbY2hhcnRdIFRoZSAnY3VzdG9tQ29kZScgY2Fubm90IGJlIGxvYWRlZC5gKTtcclxuICAgIH1cclxuICB9XHJcblxyXG4gIC8vIEdldCB0aGUgY2FsbGJhY2tcclxuICBpZiAoXHJcbiAgICBjdXN0b21Mb2dpY09wdGlvbnMgJiZcclxuICAgIGN1c3RvbUxvZ2ljT3B0aW9ucy5jYWxsYmFjayAmJlxyXG4gICAgY3VzdG9tTG9naWNPcHRpb25zLmNhbGxiYWNrPy5pbmRleE9mKCd7JykgPCAwXHJcbiAgKSB7XHJcbiAgICAvLyBUaGUgYWxsb3dGaWxlUmVzb3VyY2VzIGlzIGFsd2F5cyBzZXQgdG8gZmFsc2UgZm9yIEhUVFAgcmVxdWVzdHMgdG8gYXZvaWRcclxuICAgIC8vIGluamVjdGluZyBhcmJpdHJhcnkgZmlsZXMgZnJvbSB0aGUgZnNcclxuICAgIGlmIChjdXN0b21Mb2dpY09wdGlvbnMuYWxsb3dGaWxlUmVzb3VyY2VzKSB7XHJcbiAgICAgIHRyeSB7XHJcbiAgICAgICAgY3VzdG9tTG9naWNPcHRpb25zLmNhbGxiYWNrID0gcmVhZEZpbGVTeW5jKFxyXG4gICAgICAgICAgY3VzdG9tTG9naWNPcHRpb25zLmNhbGxiYWNrLFxyXG4gICAgICAgICAgJ3V0ZjgnXHJcbiAgICAgICAgKTtcclxuICAgICAgfSBjYXRjaCAoZXJyb3IpIHtcclxuICAgICAgICBjdXN0b21Mb2dpY09wdGlvbnMuY2FsbGJhY2sgPSBmYWxzZTtcclxuICAgICAgICBsb2dXaXRoU3RhY2soMiwgZXJyb3IsIGBbY2hhcnRdIFRoZSAnY2FsbGJhY2snIGNhbm5vdCBiZSBsb2FkZWQuYCk7XHJcbiAgICAgIH1cclxuICAgIH0gZWxzZSB7XHJcbiAgICAgIGN1c3RvbUxvZ2ljT3B0aW9ucy5jYWxsYmFjayA9IGZhbHNlO1xyXG4gICAgfVxyXG4gIH1cclxuXHJcbiAgLy8gU2l6ZSBzZWFyY2hcclxuICBvcHRpb25zLmV4cG9ydCA9IHtcclxuICAgIC4uLm9wdGlvbnMuZXhwb3J0LFxyXG4gICAgLi4uZmluZENoYXJ0U2l6ZShvcHRpb25zKVxyXG4gIH07XHJcblxyXG4gIC8vIFBvc3QgdGhlIHdvcmsgdG8gdGhlIHBvb2xcclxuICB0cnkge1xyXG4gICAgY29uc3QgcmVzdWx0ID0gYXdhaXQgcG9zdFdvcmsoXHJcbiAgICAgIGV4cG9ydE9wdGlvbnMuc3RySW5qIHx8IGNoYXJ0SnNvbiB8fCBzdmcsXHJcbiAgICAgIG9wdGlvbnNcclxuICAgICk7XHJcbiAgICByZXR1cm4gZW5kQ2FsbGJhY2soZmFsc2UsIHJlc3VsdCk7XHJcbiAgfSBjYXRjaCAoZXJyb3IpIHtcclxuICAgIHJldHVybiBlbmRDYWxsYmFjayhlcnJvcik7XHJcbiAgfVxyXG59O1xyXG5cclxuLyoqXHJcbiAqIFBlcmZvcm1zIGEgZGlyZWN0IGluamVjdCBvZiBvcHRpb25zIGJlZm9yZSBleHBvcnQuIFRoZSBmdW5jdGlvbiBhdHRlbXB0c1xyXG4gKiB0byBzdHJpbmdpZnkgdGhlIHByb3ZpZGVkIG9wdGlvbnMgYW5kIHJlbW92ZXMgdW5uZWNlc3NhcnkgY2hhcmFjdGVycyxcclxuICogZW5zdXJpbmcgYSBjbGVhbiBhbmQgZm9ybWF0dGVkIGlucHV0LiBUaGUgcmVzdWx0aW5nIHN0cmluZyBpcyBzYXZlZCBhc1xyXG4gKiBhIFwic3RyaWdodCBpbmplY3RcIiBzdHJpbmcgaW4gdGhlIGV4cG9ydCBvcHRpb25zLiBJdCB0aGVuIGludm9rZXMgdGhlXHJcbiAqIGRvRXhwb3J0IGZ1bmN0aW9uIHdpdGggdGhlIHVwZGF0ZWQgb3B0aW9ucy5cclxuICpcclxuICogSU1QT1JUQU5UOiBEYW5nZXJvdXMgYW5kIG11c3QgYmUgdXNlZCBkZWxpYmVyYXRlbHkgYnkgc29tZW9uZSB3aG8gc2V0cyB1cFxyXG4gKiBhIHNlcnZlciAoc2VlIHRoZSAgLS1hbGxvd0NvZGVFeGVjdXRpb24gb3B0aW9uKS5cclxuICpcclxuICogQHBhcmFtIHtPYmplY3R9IG9wdGlvbnMgLSBUaGUgZXhwb3J0IG9wdGlvbnMgY29udGFpbmluZyB0aGUgaW5wdXRcclxuICogdG8gYmUgaW5qZWN0ZWQuXHJcbiAqIEBwYXJhbSB7ZnVuY3Rpb259IGVuZENhbGxiYWNrIC0gVGhlIGNhbGxiYWNrIGZ1bmN0aW9uIHRvIGJlIGludm9rZWRcclxuICogYXQgdGhlIGVuZCBvZiB0aGUgcHJvY2Vzcy5cclxuICpcclxuICogQHJldHVybnMge1Byb21pc2V9IEEgUHJvbWlzZSB0aGF0IHJlc29sdmVzIHdpdGggdGhlIHJlc3VsdCBvZiB0aGUgZXhwb3J0XHJcbiAqIG9wZXJhdGlvbiBvciByZWplY3RzIHdpdGggYW4gZXJyb3IgaWYgYW55IGlzc3VlcyBvY2N1ciBkdXJpbmcgdGhlIHByb2Nlc3MuXHJcbiAqL1xyXG5jb25zdCBkb1N0cmFpZ2h0SW5qZWN0ID0gKG9wdGlvbnMsIGVuZENhbGxiYWNrKSA9PiB7XHJcbiAgdHJ5IHtcclxuICAgIGxldCBzdHJJbmo7XHJcbiAgICBsZXQgaW5zdHIgPSBvcHRpb25zLmV4cG9ydC5pbnN0ciB8fCBvcHRpb25zLmV4cG9ydC5vcHRpb25zO1xyXG5cclxuICAgIGlmICh0eXBlb2YgaW5zdHIgIT09ICdzdHJpbmcnKSB7XHJcbiAgICAgIC8vIFRyeSB0byBzdHJpbmdpZnkgb3B0aW9uc1xyXG4gICAgICBzdHJJbmogPSBpbnN0ciA9IG9wdGlvbnNTdHJpbmdpZnkoXHJcbiAgICAgICAgaW5zdHIsXHJcbiAgICAgICAgb3B0aW9ucy5jdXN0b21Mb2dpYz8uYWxsb3dDb2RlRXhlY3V0aW9uXHJcbiAgICAgICk7XHJcbiAgICB9XHJcbiAgICBzdHJJbmogPSBpbnN0ci5yZXBsYWNlQWxsKC9cXHR8XFxufFxcci9nLCAnJykudHJpbSgpO1xyXG5cclxuICAgIC8vIEdldCByaWQgb2YgdGhlIDtcclxuICAgIGlmIChzdHJJbmpbc3RySW5qLmxlbmd0aCAtIDFdID09PSAnOycpIHtcclxuICAgICAgc3RySW5qID0gc3RySW5qLnN1YnN0cmluZygwLCBzdHJJbmoubGVuZ3RoIC0gMSk7XHJcbiAgICB9XHJcblxyXG4gICAgLy8gU2F2ZSBhcyBzdHJpZ2h0IGluamVjdCBzdHJpbmdcclxuICAgIG9wdGlvbnMuZXhwb3J0LnN0ckluaiA9IHN0ckluajtcclxuICAgIHJldHVybiBkb0V4cG9ydChvcHRpb25zLCBmYWxzZSwgZW5kQ2FsbGJhY2spO1xyXG4gIH0gY2F0Y2ggKGVycm9yKSB7XHJcbiAgICByZXR1cm4gZW5kQ2FsbGJhY2soXHJcbiAgICAgIG5ldyBFeHBvcnRFcnJvcihcclxuICAgICAgICBgW2NoYXJ0XSBNYWxmb3JtZWQgaW5wdXQgZGV0ZWN0ZWQgZm9yICR7b3B0aW9ucy5leHBvcnQ/LnJlcXVlc3RJZCB8fCAnPyd9LiBQbGVhc2UgbWFrZSBzdXJlIHRoYXQgeW91ciBKU09OL0phdmFTY3JpcHQgb3B0aW9ucyBhcmUgc2VudCB1c2luZyB0aGUgXCJvcHRpb25zXCIgYXR0cmlidXRlLCBhbmQgdGhhdCBpZiB5b3UncmUgdXNpbmcgU1ZHLCBpdCBpcyB1bmVzY2FwZWQuYFxyXG4gICAgICApLnNldEVycm9yKGVycm9yKVxyXG4gICAgKTtcclxuICB9XHJcbn07XHJcblxyXG4vKipcclxuICogRXhwb3J0cyBhIHN0cmluZyBiYXNlZCBvbiB0aGUgcHJvdmlkZWQgb3B0aW9ucyBhbmQgaW52b2tlcyBhbiBlbmQgY2FsbGJhY2suXHJcbiAqXHJcbiAqIEBwYXJhbSB7c3RyaW5nfSBzdHJpbmdUb0V4cG9ydCAtIFRoZSBzdHJpbmcgY29udGVudCB0byBiZSBleHBvcnRlZC5cclxuICogQHBhcmFtIHtPYmplY3R9IG9wdGlvbnMgLSBFeHBvcnQgb3B0aW9ucywgaW5jbHVkaW5nIGN1c3RvbUxvZ2ljIHdpdGhcclxuICogYWxsb3dDb2RlRXhlY3V0aW9uIGZsYWcuXHJcbiAqIEBwYXJhbSB7RnVuY3Rpb259IGVuZENhbGxiYWNrIC0gQ2FsbGJhY2sgZnVuY3Rpb24gdG8gYmUgaW52b2tlZCBhdCB0aGUgZW5kXHJcbiAqIG9mIHRoZSBleHBvcnQgcHJvY2Vzcy5cclxuICpcclxuICogQHJldHVybnMge2FueX0gUmVzdWx0IG9mIHRoZSBleHBvcnQgcHJvY2VzcyBvciBhbiBlcnJvciBpZiBlbmNvdW50ZXJlZC5cclxuICovXHJcbmNvbnN0IGV4cG9ydEFzU3RyaW5nID0gKHN0cmluZ1RvRXhwb3J0LCBvcHRpb25zLCBlbmRDYWxsYmFjaykgPT4ge1xyXG4gIGNvbnN0IHsgYWxsb3dDb2RlRXhlY3V0aW9uIH0gPSBvcHRpb25zLmN1c3RvbUxvZ2ljO1xyXG5cclxuICAvLyBDaGVjayBpZiBpdCBpcyBTVkdcclxuICBpZiAoXHJcbiAgICBzdHJpbmdUb0V4cG9ydC5pbmRleE9mKCc8c3ZnJykgPj0gMCB8fFxyXG4gICAgc3RyaW5nVG9FeHBvcnQuaW5kZXhPZignPD94bWwnKSA+PSAwXHJcbiAgKSB7XHJcbiAgICBsb2coNCwgJ1tjaGFydF0gUGFyc2luZyBpbnB1dCBhcyBTVkcuJyk7XHJcbiAgICByZXR1cm4gZG9FeHBvcnQob3B0aW9ucywgZmFsc2UsIGVuZENhbGxiYWNrLCBzdHJpbmdUb0V4cG9ydCk7XHJcbiAgfVxyXG5cclxuICB0cnkge1xyXG4gICAgLy8gVHJ5IHRvIHBhcnNlIHRvIEpTT04gYW5kIGNhbGwgdGhlIGRvRXhwb3J0IGZ1bmN0aW9uXHJcbiAgICBjb25zdCBjaGFydEpTT04gPSBKU09OLnBhcnNlKHN0cmluZ1RvRXhwb3J0LnJlcGxhY2VBbGwoL1xcdHxcXG58XFxyL2csICcgJykpO1xyXG5cclxuICAgIC8vIElmIGEgY29ycmVjdCBKU09OLCBkbyB0aGUgZXhwb3J0XHJcbiAgICByZXR1cm4gZG9FeHBvcnQob3B0aW9ucywgY2hhcnRKU09OLCBlbmRDYWxsYmFjayk7XHJcbiAgfSBjYXRjaCAoZXJyb3IpIHtcclxuICAgIC8vIE5vdCBhIHZhbGlkIEpTT05cclxuICAgIGlmICh0b0Jvb2xlYW4oYWxsb3dDb2RlRXhlY3V0aW9uKSkge1xyXG4gICAgICByZXR1cm4gZG9TdHJhaWdodEluamVjdChvcHRpb25zLCBlbmRDYWxsYmFjayk7XHJcbiAgICB9IGVsc2Uge1xyXG4gICAgICAvLyBEbyBub3QgYWxsb3cgc3RyYWlnaHQgaW5qZWN0aW9uIHdpdGhvdXQgdGhlIGFsbG93Q29kZUV4ZWN1dGlvbiBmbGFnXHJcbiAgICAgIHJldHVybiBlbmRDYWxsYmFjayhcclxuICAgICAgICBuZXcgRXhwb3J0RXJyb3IoXHJcbiAgICAgICAgICAnW2NoYXJ0XSBPbmx5IEpTT04gY29uZmlndXJhdGlvbnMgYW5kIFNWRyBhcmUgYWxsb3dlZCBmb3IgdGhpcyBzZXJ2ZXIuIElmIHRoaXMgaXMgeW91ciBzZXJ2ZXIsIEphdmFTY3JpcHQgY3VzdG9tIGNvZGUgY2FuIGJlIGVuYWJsZWQgYnkgc3RhcnRpbmcgdGhlIHNlcnZlciB3aXRoIHRoZSAtLWFsbG93Q29kZUV4ZWN1dGlvbiBmbGFnLidcclxuICAgICAgICApLnNldEVycm9yKGVycm9yKVxyXG4gICAgICApO1xyXG4gICAgfVxyXG4gIH1cclxufTtcclxuXHJcbi8qKlxyXG4gKiBSZXRyaWV2ZXMgYW5kIHJldHVybnMgdGhlIGN1cnJlbnQgc3RhdHVzIG9mIGNvZGUgZXhlY3V0aW9uIHBlcm1pc3Npb24uXHJcbiAqXHJcbiAqIEByZXR1cm5zIHthbnl9IFRoZSB2YWx1ZSBvZiBhbGxvd0NvZGVFeGVjdXRpb24uXHJcbiAqL1xyXG5leHBvcnQgY29uc3QgZ2V0QWxsb3dDb2RlRXhlY3V0aW9uID0gKCkgPT4gYWxsb3dDb2RlRXhlY3V0aW9uO1xyXG5cclxuLyoqXHJcbiAqIFNldHMgdGhlIGNvZGUgZXhlY3V0aW9uIHBlcm1pc3Npb24gYmFzZWQgb24gdGhlIHByb3ZpZGVkIGJvb2xlYW4gdmFsdWUuXHJcbiAqXHJcbiAqIEBwYXJhbSB7YW55fSB2YWx1ZSAtIFRoZSB2YWx1ZSB0byBiZSBjb252ZXJ0ZWQgYW5kIGFzc2lnbmVkXHJcbiAqIHRvIGFsbG93Q29kZUV4ZWN1dGlvbi5cclxuICovXHJcbmV4cG9ydCBjb25zdCBzZXRBbGxvd0NvZGVFeGVjdXRpb24gPSAodmFsdWUpID0+IHtcclxuICBhbGxvd0NvZGVFeGVjdXRpb24gPSB0b0Jvb2xlYW4odmFsdWUpO1xyXG59O1xyXG5cclxuZXhwb3J0IGRlZmF1bHQge1xyXG4gIGJhdGNoRXhwb3J0LFxyXG4gIHNpbmdsZUV4cG9ydCxcclxuICBnZXRBbGxvd0NvZGVFeGVjdXRpb24sXHJcbiAgc2V0QWxsb3dDb2RlRXhlY3V0aW9uLFxyXG4gIHN0YXJ0RXhwb3J0LFxyXG4gIGZpbmRDaGFydFNpemVcclxufTtcclxuIiwiLyoqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKipcclxuXHJcbkhpZ2hjaGFydHMgRXhwb3J0IFNlcnZlclxyXG5cclxuQ29weXJpZ2h0IChjKSAyMDE2LTIwMjQsIEhpZ2hzb2Z0XHJcblxyXG5MaWNlbmNlZCB1bmRlciB0aGUgTUlUIGxpY2VuY2UuXHJcblxyXG5BZGRpdGlvbmFsbHkgYSB2YWxpZCBIaWdoY2hhcnRzIGxpY2Vuc2UgaXMgcmVxdWlyZWQgZm9yIHVzZS5cclxuXHJcblNlZSBMSUNFTlNFIGZpbGUgaW4gcm9vdCBmb3IgZGV0YWlscy5cclxuXHJcbioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKiovXHJcblxyXG4vKipcclxuICogQG92ZXJ2aWV3IFVzZWQgdG8gc2FuaXRpemUgdGhlIHN0cmluZ3MgY29taW5nIGZyb20gdGhlIGV4cG9ydGluZyBtb2R1bGVcclxuICogdG8gcHJldmVudCBYU1MgYXR0YWNrcyAod2l0aCB0aGUgRE9NUHVyaWZ5IGxpYnJhcnkpLlxyXG4gKiovXHJcblxyXG5pbXBvcnQgeyBKU0RPTSB9IGZyb20gJ2pzZG9tJztcclxuaW1wb3J0IERPTVB1cmlmeSBmcm9tICdkb21wdXJpZnknO1xyXG5cclxuLyoqXHJcbiAqIFNhbml0aXplcyBhIGdpdmVuIEhUTUwgc3RyaW5nIGJ5IHJlbW92aW5nIDxzY3JpcHQ+IHRhZ3MuXHJcbiAqIFRoaXMgZnVuY3Rpb24gdXNlcyBhIHJlZ3VsYXIgZXhwcmVzc2lvbiB0byBmaW5kIGFuZCByZW1vdmUgYWxsXHJcbiAqIG9jY3VycmVuY2VzIG9mIDxzY3JpcHQ+Li4uPC9zY3JpcHQ+IHRhZ3MgYW5kIGFueSBjb250ZW50IHdpdGhpbiB0aGVtLlxyXG4gKlxyXG4gKiBAcGFyYW0ge3N0cmluZ30gaW5wdXQgVGhlIEhUTUwgc3RyaW5nIHRvIGJlIHNhbml0aXplZC5cclxuICogQHJldHVybnMge3N0cmluZ30gVGhlIHNhbml0aXplZCBIVE1MIHN0cmluZy5cclxuICovXHJcbmV4cG9ydCBmdW5jdGlvbiBzYW5pdGl6ZShpbnB1dCkge1xyXG4gIGNvbnN0IHdpbmRvdyA9IG5ldyBKU0RPTSgnJykud2luZG93O1xyXG4gIGNvbnN0IHB1cmlmeSA9IERPTVB1cmlmeSh3aW5kb3cpO1xyXG4gIHJldHVybiBwdXJpZnkuc2FuaXRpemUoaW5wdXQsIHsgQUREX1RBR1M6IFsnZm9yZWlnbk9iamVjdCddIH0pO1xyXG59XHJcblxyXG5leHBvcnQgZGVmYXVsdCBzYW5pdGl6ZTtcclxuIiwiLyoqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKipcclxuXHJcbkhpZ2hjaGFydHMgRXhwb3J0IFNlcnZlclxyXG5cclxuQ29weXJpZ2h0IChjKSAyMDE2LTIwMjQsIEhpZ2hzb2Z0XHJcblxyXG5MaWNlbmNlZCB1bmRlciB0aGUgTUlUIGxpY2VuY2UuXHJcblxyXG5BZGRpdGlvbmFsbHkgYSB2YWxpZCBIaWdoY2hhcnRzIGxpY2Vuc2UgaXMgcmVxdWlyZWQgZm9yIHVzZS5cclxuXHJcblNlZSBMSUNFTlNFIGZpbGUgaW4gcm9vdCBmb3IgZGV0YWlscy5cclxuXHJcbioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKiovXHJcblxyXG5pbXBvcnQgeyBsb2cgfSBmcm9tICcuL2xvZ2dlci5qcyc7XHJcblxyXG4vLyBBcnJheSB0aGF0IGNvbnRhaW5zIGlkcyBvZiBhbGwgb25nb2luZyBpbnRlcnZhbHNcclxuY29uc3QgaW50ZXJ2YWxJZHMgPSBbXTtcclxuXHJcbi8qKlxyXG4gKiBBZGRzIGlkIG9mIGEgc2V0SW50ZXJ2YWwgdG8gdGhlIGludGVydmFsSWRzIGFycmF5LlxyXG4gKlxyXG4gKiBAcGFyYW0ge05vZGVKUy5UaW1lb3V0fSBpZCAtIElkIG9mIGFuIGludGVydmFsLlxyXG4gKi9cclxuZXhwb3J0IGNvbnN0IGFkZEludGVydmFsID0gKGlkKSA9PiB7XHJcbiAgaW50ZXJ2YWxJZHMucHVzaChpZCk7XHJcbn07XHJcblxyXG4vKipcclxuICogQ2xlYXJzIGFsbCBvZiBvbmdvaW5nIGludGVydmFscyBieSBpZHMgZ2F0aGVyZWQgaW4gdGhlIGludGVydmFsSWRzIGFycmF5LlxyXG4gKi9cclxuZXhwb3J0IGNvbnN0IGNsZWFyQWxsSW50ZXJ2YWxzID0gKCkgPT4ge1xyXG4gIGxvZyg0LCBgW3NlcnZlcl0gQ2xlYXJpbmcgYWxsIHJlZ2lzdGVyZWQgaW50ZXJ2YWxzLmApO1xyXG4gIGZvciAoY29uc3QgaWQgb2YgaW50ZXJ2YWxJZHMpIHtcclxuICAgIGNsZWFySW50ZXJ2YWwoaWQpO1xyXG4gIH1cclxufTtcclxuXHJcbmV4cG9ydCBkZWZhdWx0IHtcclxuICBhZGRJbnRlcnZhbCxcclxuICBjbGVhckFsbEludGVydmFsc1xyXG59O1xyXG4iLCJpbXBvcnQgeyBlbnZzIH0gZnJvbSAnLi4vZW52cy5qcyc7XHJcbmltcG9ydCB7IGxvZ1dpdGhTdGFjayB9IGZyb20gJy4uL2xvZ2dlci5qcyc7XHJcblxyXG4vKipcclxuICogTWlkZGxld2FyZSBmb3IgbG9nZ2luZyBlcnJvcnMgd2l0aCBzdGFjayB0cmFjZSBhbmQgaGFuZGxpbmcgZXJyb3IgcmVzcG9uc2UuXHJcbiAqXHJcbiAqIEBwYXJhbSB7RXJyb3J9IGVycm9yIC0gVGhlIGVycm9yIG9iamVjdC5cclxuICogQHBhcmFtIHtFeHByZXNzLlJlcXVlc3R9IHJlcSAtIFRoZSBFeHByZXNzIHJlcXVlc3Qgb2JqZWN0LlxyXG4gKiBAcGFyYW0ge0V4cHJlc3MuUmVzcG9uc2V9IHJlcyAtIFRoZSBFeHByZXNzIHJlc3BvbnNlIG9iamVjdC5cclxuICogQHBhcmFtIHtGdW5jdGlvbn0gbmV4dCAtIFRoZSBuZXh0IG1pZGRsZXdhcmUgZnVuY3Rpb24uXHJcbiAqL1xyXG5jb25zdCBsb2dFcnJvck1pZGRsZXdhcmUgPSAoZXJyb3IsIHJlcSwgcmVzLCBuZXh0KSA9PiB7XHJcbiAgLy8gRGlzcGxheSB0aGUgZXJyb3Igd2l0aCBzdGFjayBpbiBhIGNvcnJlY3QgZm9ybWF0XHJcbiAgbG9nV2l0aFN0YWNrKDEsIGVycm9yKTtcclxuXHJcbiAgLy8gRGVsZXRlIHRoZSBzdGFjayBmb3IgdGhlIGVudmlyb25tZW50IG90aGVyIHRoYW4gdGhlIGRldmVsb3BtZW50XHJcbiAgaWYgKGVudnMuT1RIRVJfTk9ERV9FTlYgIT09ICdkZXZlbG9wbWVudCcpIHtcclxuICAgIGRlbGV0ZSBlcnJvci5zdGFjaztcclxuICB9XHJcblxyXG4gIC8vIENhbGwgdGhlIHJldHVybkVycm9yTWlkZGxld2FyZVxyXG4gIG5leHQoZXJyb3IpO1xyXG59O1xyXG5cclxuLyoqXHJcbiAqIE1pZGRsZXdhcmUgZm9yIHJldHVybmluZyBlcnJvciByZXNwb25zZS5cclxuICpcclxuICogQHBhcmFtIHtFcnJvcn0gZXJyb3IgLSBUaGUgZXJyb3Igb2JqZWN0LlxyXG4gKiBAcGFyYW0ge0V4cHJlc3MuUmVxdWVzdH0gcmVxIC0gVGhlIEV4cHJlc3MgcmVxdWVzdCBvYmplY3QuXHJcbiAqIEBwYXJhbSB7RXhwcmVzcy5SZXNwb25zZX0gcmVzIC0gVGhlIEV4cHJlc3MgcmVzcG9uc2Ugb2JqZWN0LlxyXG4gKiBAcGFyYW0ge0Z1bmN0aW9ufSBuZXh0IC0gVGhlIG5leHQgbWlkZGxld2FyZSBmdW5jdGlvbi5cclxuICovXHJcbmNvbnN0IHJldHVybkVycm9yTWlkZGxld2FyZSA9IChlcnJvciwgcmVxLCByZXMsIG5leHQpID0+IHtcclxuICAvLyBHYXRoZXIgYWxsIHJlcXVpZWQgaW5mb3JtYXRpb24gZm9yIHRoZSByZXNwb25zZVxyXG4gIGNvbnN0IHsgc3RhdHVzQ29kZTogc3RDb2RlLCBzdGF0dXMsIG1lc3NhZ2UsIHN0YWNrIH0gPSBlcnJvcjtcclxuICBjb25zdCBzdGF0dXNDb2RlID0gc3RDb2RlIHx8IHN0YXR1cyB8fCA1MDA7XHJcblxyXG4gIC8vIFNldCBhbmQgcmV0dXJuIHJlc3BvbnNlXHJcbiAgcmVzLnN0YXR1cyhzdGF0dXNDb2RlKS5qc29uKHsgc3RhdHVzQ29kZSwgbWVzc2FnZSwgc3RhY2sgfSk7XHJcbn07XHJcblxyXG5leHBvcnQgZGVmYXVsdCAoYXBwKSA9PiB7XHJcbiAgLy8gQWRkIGxvZyBlcnJvciBtaWRkbGV3YXJlXHJcbiAgYXBwLnVzZShsb2dFcnJvck1pZGRsZXdhcmUpO1xyXG5cclxuICAvLyBBZGQgc2V0IHN0YXR1cyBhbmQgcmV0dXJuIGVycm9yIG1pZGRsZXdhcmVcclxuICBhcHAudXNlKHJldHVybkVycm9yTWlkZGxld2FyZSk7XHJcbn07XHJcbiIsIi8qKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqXHJcblxyXG5IaWdoY2hhcnRzIEV4cG9ydCBTZXJ2ZXJcclxuXHJcbkNvcHlyaWdodCAoYykgMjAxNi0yMDI0LCBIaWdoc29mdFxyXG5cclxuTGljZW5jZWQgdW5kZXIgdGhlIE1JVCBsaWNlbmNlLlxyXG5cclxuQWRkaXRpb25hbGx5IGEgdmFsaWQgSGlnaGNoYXJ0cyBsaWNlbnNlIGlzIHJlcXVpcmVkIGZvciB1c2UuXHJcblxyXG5TZWUgTElDRU5TRSBmaWxlIGluIHJvb3QgZm9yIGRldGFpbHMuXHJcblxyXG4qKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqL1xyXG5cclxuaW1wb3J0IHJhdGVMaW1pdCBmcm9tICdleHByZXNzLXJhdGUtbGltaXQnO1xyXG5cclxuaW1wb3J0IHsgbG9nIH0gZnJvbSAnLi4vbG9nZ2VyLmpzJztcclxuXHJcbi8qKlxyXG4gKiBNaWRkbGV3YXJlIGZvciBlbmFibGluZyByYXRlIGxpbWl0aW5nIG9uIHRoZSBzcGVjaWZpZWQgRXhwcmVzcyBhcHAuXHJcbiAqXHJcbiAqIEBwYXJhbSB7RXhwcmVzc30gYXBwIC0gVGhlIEV4cHJlc3MgYXBwIGluc3RhbmNlLlxyXG4gKiBAcGFyYW0ge09iamVjdH0gbGltaXRDb25maWcgLSBDb25maWd1cmF0aW9uIG9wdGlvbnMgZm9yIHJhdGUgbGltaXRpbmcuXHJcbiAqL1xyXG5leHBvcnQgZGVmYXVsdCAoYXBwLCBsaW1pdENvbmZpZykgPT4ge1xyXG4gIGNvbnN0IG1zZyA9XHJcbiAgICAnVG9vIG1hbnkgcmVxdWVzdHMsIHlvdSBoYXZlIGJlZW4gcmF0ZSBsaW1pdGVkLiBQbGVhc2UgdHJ5IGFnYWluIGxhdGVyLic7XHJcblxyXG4gIC8vIE9wdGlvbnMgZm9yIHRoZSByYXRlIGxpbWl0ZXJcclxuICBjb25zdCByYXRlT3B0aW9ucyA9IHtcclxuICAgIG1heDogbGltaXRDb25maWcubWF4UmVxdWVzdHMgfHwgMzAsXHJcbiAgICB3aW5kb3c6IGxpbWl0Q29uZmlnLndpbmRvdyB8fCAxLFxyXG4gICAgZGVsYXk6IGxpbWl0Q29uZmlnLmRlbGF5IHx8IDAsXHJcbiAgICB0cnVzdFByb3h5OiBsaW1pdENvbmZpZy50cnVzdFByb3h5IHx8IGZhbHNlLFxyXG4gICAgc2tpcEtleTogbGltaXRDb25maWcuc2tpcEtleSB8fCBmYWxzZSxcclxuICAgIHNraXBUb2tlbjogbGltaXRDb25maWcuc2tpcFRva2VuIHx8IGZhbHNlXHJcbiAgfTtcclxuXHJcbiAgLy8gU2V0IGlmIGJlaGluZCBhIHByb3h5XHJcbiAgaWYgKHJhdGVPcHRpb25zLnRydXN0UHJveHkpIHtcclxuICAgIGFwcC5lbmFibGUoJ3RydXN0IHByb3h5Jyk7XHJcbiAgfVxyXG5cclxuICAvLyBDcmVhdGUgYSBsaW1pdGVyXHJcbiAgY29uc3QgbGltaXRlciA9IHJhdGVMaW1pdCh7XHJcbiAgICB3aW5kb3dNczogcmF0ZU9wdGlvbnMud2luZG93ICogNjAgKiAxMDAwLFxyXG4gICAgLy8gTGltaXQgZWFjaCBJUCB0byAxMDAgcmVxdWVzdHMgcGVyIHdpbmRvd01zXHJcbiAgICBtYXg6IHJhdGVPcHRpb25zLm1heCxcclxuICAgIC8vIERpc2FibGUgZGVsYXlpbmcsIGZ1bGwgc3BlZWQgdW50aWwgdGhlIG1heCBsaW1pdCBpcyByZWFjaGVkXHJcbiAgICBkZWxheU1zOiByYXRlT3B0aW9ucy5kZWxheSxcclxuICAgIGhhbmRsZXI6IChyZXF1ZXN0LCByZXNwb25zZSkgPT4ge1xyXG4gICAgICByZXNwb25zZS5mb3JtYXQoe1xyXG4gICAgICAgIGpzb246ICgpID0+IHtcclxuICAgICAgICAgIHJlc3BvbnNlLnN0YXR1cyg0MjkpLnNlbmQoeyBtZXNzYWdlOiBtc2cgfSk7XHJcbiAgICAgICAgfSxcclxuICAgICAgICBkZWZhdWx0OiAoKSA9PiB7XHJcbiAgICAgICAgICByZXNwb25zZS5zdGF0dXMoNDI5KS5zZW5kKG1zZyk7XHJcbiAgICAgICAgfVxyXG4gICAgICB9KTtcclxuICAgIH0sXHJcbiAgICBza2lwOiAocmVxdWVzdCkgPT4ge1xyXG4gICAgICAvLyBBbGxvdyBieXBhc3NpbmcgdGhlIGxpbWl0ZXIgaWYgYSB2YWxpZCBrZXkvdG9rZW4gaGFzIGJlZW4gc2VudFxyXG4gICAgICBpZiAoXHJcbiAgICAgICAgcmF0ZU9wdGlvbnMuc2tpcEtleSAhPT0gZmFsc2UgJiZcclxuICAgICAgICByYXRlT3B0aW9ucy5za2lwVG9rZW4gIT09IGZhbHNlICYmXHJcbiAgICAgICAgcmVxdWVzdC5xdWVyeS5rZXkgPT09IHJhdGVPcHRpb25zLnNraXBLZXkgJiZcclxuICAgICAgICByZXF1ZXN0LnF1ZXJ5LmFjY2Vzc190b2tlbiA9PT0gcmF0ZU9wdGlvbnMuc2tpcFRva2VuXHJcbiAgICAgICkge1xyXG4gICAgICAgIGxvZyg0LCAnW3JhdGUgbGltaXRpbmddIFNraXBwaW5nIHJhdGUgbGltaXRlci4nKTtcclxuICAgICAgICByZXR1cm4gdHJ1ZTtcclxuICAgICAgfVxyXG4gICAgICByZXR1cm4gZmFsc2U7XHJcbiAgICB9XHJcbiAgfSk7XHJcblxyXG4gIC8vIFVzZSBhIGxpbWl0ZXIgYXMgYSBtaWRkbGV3YXJlXHJcbiAgYXBwLnVzZShsaW1pdGVyKTtcclxuXHJcbiAgbG9nKFxyXG4gICAgMyxcclxuICAgIGBbcmF0ZSBsaW1pdGluZ10gRW5hYmxlZCByYXRlIGxpbWl0aW5nIHdpdGggJHtyYXRlT3B0aW9ucy5tYXh9IHJlcXVlc3RzIHBlciAke3JhdGVPcHRpb25zLndpbmRvd30gbWludXRlIGZvciBlYWNoIElQLCB0cnVzdGluZyBwcm94eTogJHtyYXRlT3B0aW9ucy50cnVzdFByb3h5fS5gXHJcbiAgKTtcclxufTtcclxuIiwiaW1wb3J0IEV4cG9ydEVycm9yIGZyb20gJy4vRXhwb3J0RXJyb3IuanMnO1xyXG5cclxuY2xhc3MgSHR0cEVycm9yIGV4dGVuZHMgRXhwb3J0RXJyb3Ige1xyXG4gIGNvbnN0cnVjdG9yKG1lc3NhZ2UsIHN0YXR1cykge1xyXG4gICAgc3VwZXIobWVzc2FnZSk7XHJcbiAgICB0aGlzLnN0YXR1cyA9IHRoaXMuc3RhdHVzQ29kZSA9IHN0YXR1cztcclxuICB9XHJcblxyXG4gIHNldFN0YXR1cyhzdGF0dXMpIHtcclxuICAgIHRoaXMuc3RhdHVzID0gc3RhdHVzO1xyXG4gICAgcmV0dXJuIHRoaXM7XHJcbiAgfVxyXG59XHJcblxyXG5leHBvcnQgZGVmYXVsdCBIdHRwRXJyb3I7XHJcbiIsIi8qKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqXHJcblxyXG5IaWdoY2hhcnRzIEV4cG9ydCBTZXJ2ZXJcclxuXHJcbkNvcHlyaWdodCAoYykgMjAxNi0yMDI0LCBIaWdoc29mdFxyXG5cclxuTGljZW5jZWQgdW5kZXIgdGhlIE1JVCBsaWNlbmNlLlxyXG5cclxuQWRkaXRpb25hbGx5IGEgdmFsaWQgSGlnaGNoYXJ0cyBsaWNlbnNlIGlzIHJlcXVpcmVkIGZvciB1c2UuXHJcblxyXG5TZWUgTElDRU5TRSBmaWxlIGluIHJvb3QgZm9yIGRldGFpbHMuXHJcblxyXG4qKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqL1xyXG5cclxuaW1wb3J0IHsgdXBkYXRlVmVyc2lvbiwgdmVyc2lvbiB9IGZyb20gJy4uLy4uL2NhY2hlLmpzJztcclxuaW1wb3J0IHsgZW52cyB9IGZyb20gJy4uLy4uL2VudnMuanMnO1xyXG5cclxuaW1wb3J0IEh0dHBFcnJvciBmcm9tICcuLi8uLi9lcnJvcnMvSHR0cEVycm9yLmpzJztcclxuXHJcbi8qKlxyXG4gKiBBZGRzIHRoZSBQT1NUIC9jaGFuZ2VfaGNfdmVyc2lvbi86bmV3VmVyc2lvbiByb3V0ZSB0aGF0IGNhbiBiZSB1dGlsaXplZCB0byBtb2RpZnlcclxuICogdGhlIEhpZ2hjaGFydHMgdmVyc2lvbiBvbiB0aGUgc2VydmVyLlxyXG4gKlxyXG4gKiBUT0RPOiBBZGQgYXV0aCB0b2tlbiBhbmQgY29ubmVjdCB0byBBUElcclxuICovXHJcbmV4cG9ydCBkZWZhdWx0IChhcHApID0+XHJcbiAgIWFwcFxyXG4gICAgPyBmYWxzZVxyXG4gICAgOiBhcHAucG9zdChcclxuICAgICAgICAnL3ZlcnNpb24vY2hhbmdlLzpuZXdWZXJzaW9uJyxcclxuICAgICAgICBhc3luYyAocmVxdWVzdCwgcmVzcG9uc2UsIG5leHQpID0+IHtcclxuICAgICAgICAgIHRyeSB7XHJcbiAgICAgICAgICAgIGNvbnN0IGFkbWluVG9rZW4gPSBlbnZzLkhJR0hDSEFSVFNfQURNSU5fVE9LRU47XHJcblxyXG4gICAgICAgICAgICAvLyBDaGVjayB0aGUgZXhpc3RlbmNlIG9mIHRoZSB0b2tlblxyXG4gICAgICAgICAgICBpZiAoIWFkbWluVG9rZW4gfHwgIWFkbWluVG9rZW4ubGVuZ3RoKSB7XHJcbiAgICAgICAgICAgICAgdGhyb3cgbmV3IEh0dHBFcnJvcihcclxuICAgICAgICAgICAgICAgICdUaGUgc2VydmVyIGlzIG5vdCBjb25maWd1cmVkIHRvIHBlcmZvcm0gcnVuLXRpbWUgdmVyc2lvbiBjaGFuZ2VzOiBISUdIQ0hBUlRTX0FETUlOX1RPS0VOIGlzIG5vdCBzZXQuJyxcclxuICAgICAgICAgICAgICAgIDQwMVxyXG4gICAgICAgICAgICAgICk7XHJcbiAgICAgICAgICAgIH1cclxuXHJcbiAgICAgICAgICAgIC8vIENoZWNrIGlmIHRoZSBoYy1hdXRoIGhlYWRlciBjb250YWluIGEgY29ycmVjdCB0b2tlblxyXG4gICAgICAgICAgICBjb25zdCB0b2tlbiA9IHJlcXVlc3QuZ2V0KCdoYy1hdXRoJyk7XHJcbiAgICAgICAgICAgIGlmICghdG9rZW4gfHwgdG9rZW4gIT09IGFkbWluVG9rZW4pIHtcclxuICAgICAgICAgICAgICB0aHJvdyBuZXcgSHR0cEVycm9yKFxyXG4gICAgICAgICAgICAgICAgJ0ludmFsaWQgb3IgbWlzc2luZyB0b2tlbjogU2V0IHRoZSB0b2tlbiBpbiB0aGUgaGMtYXV0aCBoZWFkZXIuJyxcclxuICAgICAgICAgICAgICAgIDQwMVxyXG4gICAgICAgICAgICAgICk7XHJcbiAgICAgICAgICAgIH1cclxuXHJcbiAgICAgICAgICAgIC8vIENvbXBhcmUgdmVyc2lvbnNcclxuICAgICAgICAgICAgY29uc3QgbmV3VmVyc2lvbiA9IHJlcXVlc3QucGFyYW1zLm5ld1ZlcnNpb247XHJcbiAgICAgICAgICAgIGlmIChuZXdWZXJzaW9uKSB7XHJcbiAgICAgICAgICAgICAgdHJ5IHtcclxuICAgICAgICAgICAgICAgIC8vIGVzbGludC1kaXNhYmxlLW5leHQtbGluZSBpbXBvcnQvbm8tbmFtZWQtYXMtZGVmYXVsdC1tZW1iZXJcclxuICAgICAgICAgICAgICAgIGF3YWl0IHVwZGF0ZVZlcnNpb24obmV3VmVyc2lvbik7XHJcbiAgICAgICAgICAgICAgfSBjYXRjaCAoZXJyb3IpIHtcclxuICAgICAgICAgICAgICAgIHRocm93IG5ldyBIdHRwRXJyb3IoXHJcbiAgICAgICAgICAgICAgICAgIGBWZXJzaW9uIGNoYW5nZTogJHtlcnJvci5tZXNzYWdlfWAsXHJcbiAgICAgICAgICAgICAgICAgIGVycm9yLnN0YXR1c0NvZGVcclxuICAgICAgICAgICAgICAgICkuc2V0RXJyb3IoZXJyb3IpO1xyXG4gICAgICAgICAgICAgIH1cclxuXHJcbiAgICAgICAgICAgICAgLy8gU3VjY2Vzc1xyXG4gICAgICAgICAgICAgIHJlc3BvbnNlLnN0YXR1cygyMDApLnNlbmQoe1xyXG4gICAgICAgICAgICAgICAgc3RhdHVzQ29kZTogMjAwLFxyXG4gICAgICAgICAgICAgICAgdmVyc2lvbjogdmVyc2lvbigpLFxyXG4gICAgICAgICAgICAgICAgbWVzc2FnZTogYFN1Y2Nlc3NmdWxseSB1cGRhdGVkIEhpZ2hjaGFydHMgdG8gdmVyc2lvbjogJHtuZXdWZXJzaW9ufS5gXHJcbiAgICAgICAgICAgICAgfSk7XHJcbiAgICAgICAgICAgIH0gZWxzZSB7XHJcbiAgICAgICAgICAgICAgLy8gTm8gdmVyc2lvbiBzcGVjaWZpZWRcclxuICAgICAgICAgICAgICB0aHJvdyBuZXcgSHR0cEVycm9yKCdObyBuZXcgdmVyc2lvbiBzdXBwbGllZC4nLCA0MDApO1xyXG4gICAgICAgICAgICB9XHJcbiAgICAgICAgICB9IGNhdGNoIChlcnJvcikge1xyXG4gICAgICAgICAgICBuZXh0KGVycm9yKTtcclxuICAgICAgICAgIH1cclxuICAgICAgICB9XHJcbiAgICAgICk7XHJcbiIsIi8qKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqXHJcblxyXG5IaWdoY2hhcnRzIEV4cG9ydCBTZXJ2ZXJcclxuXHJcbkNvcHlyaWdodCAoYykgMjAxNi0yMDI0LCBIaWdoc29mdFxyXG5cclxuTGljZW5jZWQgdW5kZXIgdGhlIE1JVCBsaWNlbmNlLlxyXG5cclxuQWRkaXRpb25hbGx5IGEgdmFsaWQgSGlnaGNoYXJ0cyBsaWNlbnNlIGlzIHJlcXVpcmVkIGZvciB1c2UuXHJcblxyXG5TZWUgTElDRU5TRSBmaWxlIGluIHJvb3QgZm9yIGRldGFpbHMuXHJcblxyXG4qKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqL1xyXG5cclxuaW1wb3J0IHsgdjQgYXMgdXVpZCB9IGZyb20gJ3V1aWQnO1xyXG5cclxuaW1wb3J0IHsgZ2V0QWxsb3dDb2RlRXhlY3V0aW9uLCBzdGFydEV4cG9ydCB9IGZyb20gJy4uLy4uL2NoYXJ0LmpzJztcclxuaW1wb3J0IHsgZ2V0T3B0aW9ucywgbWVyZ2VDb25maWdPcHRpb25zIH0gZnJvbSAnLi4vLi4vY29uZmlnLmpzJztcclxuaW1wb3J0IHsgbG9nIH0gZnJvbSAnLi4vLi4vbG9nZ2VyLmpzJztcclxuaW1wb3J0IHtcclxuICBmaXhUeXBlLFxyXG4gIGlzQ29ycmVjdEpTT04sXHJcbiAgaXNPYmplY3RFbXB0eSxcclxuICBpc1ByaXZhdGVSYW5nZVVybEZvdW5kLFxyXG4gIG9wdGlvbnNTdHJpbmdpZnksXHJcbiAgbWVhc3VyZVRpbWVcclxufSBmcm9tICcuLi8uLi91dGlscy5qcyc7XHJcblxyXG5pbXBvcnQgSHR0cEVycm9yIGZyb20gJy4uLy4uL2Vycm9ycy9IdHRwRXJyb3IuanMnO1xyXG5cclxuLy8gUmV2ZXJzZWQgTUlNRSB0eXBlc1xyXG5jb25zdCByZXZlcnNlZE1pbWUgPSB7XHJcbiAgcG5nOiAnaW1hZ2UvcG5nJyxcclxuICBqcGVnOiAnaW1hZ2UvanBlZycsXHJcbiAgZ2lmOiAnaW1hZ2UvZ2lmJyxcclxuICBwZGY6ICdhcHBsaWNhdGlvbi9wZGYnLFxyXG4gIHN2ZzogJ2ltYWdlL3N2Zyt4bWwnXHJcbn07XHJcblxyXG4vLyBUaGUgcmVxdWVzdHMgY291bnRlclxyXG5sZXQgcmVxdWVzdHNDb3VudGVyID0gMDtcclxuXHJcbi8vIFRoZSBhcnJheSBvZiBjYWxsYmFja3MgdG8gY2FsbCBiZWZvcmUgYSByZXF1ZXN0XHJcbmNvbnN0IGJlZm9yZVJlcXVlc3QgPSBbXTtcclxuXHJcbi8vIFRoZSBhcnJheSBvZiBjYWxsYmFja3MgdG8gY2FsbCBhZnRlciBhIHJlcXVlc3RcclxuY29uc3QgYWZ0ZXJSZXF1ZXN0ID0gW107XHJcblxyXG4vKipcclxuICogSW52b2tlcyBhbiBhcnJheSBvZiBjYWxsYmFjayBmdW5jdGlvbnMgd2l0aCBzcGVjaWZpZWQgcGFyYW1ldGVycywgYWxsb3dpbmdcclxuICogY3VzdG9taXphdGlvbiBvZiByZXF1ZXN0IGhhbmRsaW5nLlxyXG4gKlxyXG4gKiBAcGFyYW0ge0Z1bmN0aW9uW119IGNhbGxiYWNrcyAtIEFuIGFycmF5IG9mIGNhbGxiYWNrIGZ1bmN0aW9uc1xyXG4gKiB0byBiZSBleGVjdXRlZC5cclxuICogQHBhcmFtIHtFeHByZXNzLlJlcXVlc3R9IHJlcXVlc3QgLSBUaGUgRXhwcmVzcyByZXF1ZXN0IG9iamVjdC5cclxuICogQHBhcmFtIHtFeHByZXNzLlJlc3BvbnNlfSByZXNwb25zZSAtIFRoZSBFeHByZXNzIHJlc3BvbnNlIG9iamVjdC5cclxuICogQHBhcmFtIHtPYmplY3R9IGRhdGEgLSBBbiBvYmplY3QgY29udGFpbmluZyBwYXJhbWV0ZXJzIGxpa2UgaWQsIHVuaXF1ZUlkLFxyXG4gKiB0eXBlLCBhbmQgYm9keS5cclxuICpcclxuICogQHJldHVybnMge2Jvb2xlYW59IC0gUmV0dXJucyBhIGJvb2xlYW4gaW5kaWNhdGluZyB0aGUgb3ZlcmFsbCByZXN1bHRcclxuICogb2YgdGhlIGNhbGxiYWNrIGludm9jYXRpb25zLlxyXG4gKi9cclxuY29uc3QgZG9DYWxsYmFja3MgPSAoY2FsbGJhY2tzLCByZXF1ZXN0LCByZXNwb25zZSwgZGF0YSkgPT4ge1xyXG4gIGxldCByZXN1bHQgPSB0cnVlO1xyXG4gIGNvbnN0IHsgaWQsIHVuaXF1ZUlkLCB0eXBlLCBib2R5IH0gPSBkYXRhO1xyXG5cclxuICBjYWxsYmFja3Muc29tZSgoY2FsbGJhY2spID0+IHtcclxuICAgIGlmIChjYWxsYmFjaykge1xyXG4gICAgICBsZXQgY2FsbFJlc3BvbnNlID0gY2FsbGJhY2socmVxdWVzdCwgcmVzcG9uc2UsIGlkLCB1bmlxdWVJZCwgdHlwZSwgYm9keSk7XHJcblxyXG4gICAgICBpZiAoY2FsbFJlc3BvbnNlICE9PSB1bmRlZmluZWQgJiYgY2FsbFJlc3BvbnNlICE9PSB0cnVlKSB7XHJcbiAgICAgICAgcmVzdWx0ID0gY2FsbFJlc3BvbnNlO1xyXG4gICAgICB9XHJcblxyXG4gICAgICByZXR1cm4gdHJ1ZTtcclxuICAgIH1cclxuICB9KTtcclxuXHJcbiAgcmV0dXJuIHJlc3VsdDtcclxufTtcclxuXHJcbi8qKlxyXG4gKiBIYW5kbGVzIHRoZSBleHBvcnQgcmVxdWVzdHMgZnJvbSB0aGUgY2xpZW50LlxyXG4gKlxyXG4gKiBAcGFyYW0ge0V4cHJlc3MuUmVxdWVzdH0gcmVxdWVzdCAtIFRoZSBFeHByZXNzIHJlcXVlc3Qgb2JqZWN0LlxyXG4gKiBAcGFyYW0ge0V4cHJlc3MuUmVzcG9uc2V9IHJlc3BvbnNlIC0gVGhlIEV4cHJlc3MgcmVzcG9uc2Ugb2JqZWN0LlxyXG4gKiBAcGFyYW0ge0Z1bmN0aW9ufSBuZXh0IC0gVGhlIG5leHQgbWlkZGxld2FyZSBmdW5jdGlvbi5cclxuICpcclxuICogQHJldHVybnMge1Byb21pc2U8dm9pZD59IC0gQSBwcm9taXNlIHRoYXQgcmVzb2x2ZXMgb25jZSB0aGUgZXhwb3J0IHByb2Nlc3NcclxuICogaXMgY29tcGxldGUuXHJcbiAqL1xyXG5jb25zdCBleHBvcnRIYW5kbGVyID0gYXN5bmMgKHJlcXVlc3QsIHJlc3BvbnNlLCBuZXh0KSA9PiB7XHJcbiAgdHJ5IHtcclxuICAgIC8vIFN0YXJ0IGNvdW50aW5nIHRpbWVcclxuICAgIGNvbnN0IHN0b3BDb3VudGVyID0gbWVhc3VyZVRpbWUoKTtcclxuXHJcbiAgICAvLyBDcmVhdGUgYSB1bmlxdWUgSUQgZm9yIGEgcmVxdWVzdFxyXG4gICAgY29uc3QgdW5pcXVlSWQgPSB1dWlkKCkucmVwbGFjZSgvLS9nLCAnJyk7XHJcblxyXG4gICAgLy8gR2V0IHRoZSBjdXJyZW50IHNlcnZlcidzIGdlbmVyYWwgb3B0aW9uc1xyXG4gICAgY29uc3QgZGVmYXVsdE9wdGlvbnMgPSBnZXRPcHRpb25zKCk7XHJcblxyXG4gICAgY29uc3QgYm9keSA9IHJlcXVlc3QuYm9keTtcclxuICAgIGNvbnN0IGlkID0gKytyZXF1ZXN0c0NvdW50ZXI7XHJcblxyXG4gICAgbGV0IHR5cGUgPSBmaXhUeXBlKGJvZHkudHlwZSk7XHJcblxyXG4gICAgLy8gVGhyb3cgJ0JhZCBSZXF1ZXN0JyBpZiB0aGVyZSdzIG5vIGJvZHlcclxuICAgIGlmICghYm9keSB8fCBpc09iamVjdEVtcHR5KGJvZHkpKSB7XHJcbiAgICAgIHRocm93IG5ldyBIdHRwRXJyb3IoXHJcbiAgICAgICAgJ1RoZSByZXF1ZXN0IGJvZHkgaXMgcmVxdWlyZWQuIFBsZWFzZSBlbnN1cmUgdGhhdCB5b3VyIENvbnRlbnQtVHlwZSBoZWFkZXIgaXMgY29ycmVjdCAoYWNjZXB0ZWQgdHlwZXMgYXJlIGFwcGxpY2F0aW9uL2pzb24gYW5kIG11bHRpcGFydC9mb3JtLWRhdGEpLicsXHJcbiAgICAgICAgNDAwXHJcbiAgICAgICk7XHJcbiAgICB9XHJcblxyXG4gICAgLy8gQWxsIG9mIHRoZSBiZWxvdyBjYW4gYmUgdXNlZFxyXG4gICAgbGV0IGluc3RyID0gaXNDb3JyZWN0SlNPTihib2R5LmluZmlsZSB8fCBib2R5Lm9wdGlvbnMgfHwgYm9keS5kYXRhKTtcclxuXHJcbiAgICAvLyBUaHJvdyAnQmFkIFJlcXVlc3QnIGlmIHRoZXJlJ3Mgbm8gSlNPTiBvciBTVkcgdG8gZXhwb3J0XHJcbiAgICBpZiAoIWluc3RyICYmICFib2R5LnN2Zykge1xyXG4gICAgICBsb2coXHJcbiAgICAgICAgMixcclxuICAgICAgICBgVGhlIHJlcXVlc3Qgd2l0aCBJRCAke3VuaXF1ZUlkfSBmcm9tICR7XHJcbiAgICAgICAgICByZXF1ZXN0LmhlYWRlcnNbJ3gtZm9yd2FyZGVkLWZvciddIHx8IHJlcXVlc3QuY29ubmVjdGlvbi5yZW1vdGVBZGRyZXNzXHJcbiAgICAgICAgfSB3YXMgaW5jb3JyZWN0LiBQYXlsb2FkIHJlY2VpdmVkOiAke0pTT04uc3RyaW5naWZ5KGJvZHkpfS5gXHJcbiAgICAgICk7XHJcblxyXG4gICAgICB0aHJvdyBuZXcgSHR0cEVycm9yKFxyXG4gICAgICAgIFwiTm8gY29ycmVjdCBjaGFydCBkYXRhIGZvdW5kLiBFbnN1cmUgdGhhdCB5b3UgYXJlIHVzaW5nIGVpdGhlciBhcHBsaWNhdGlvbi9qc29uIG9yIG11bHRpcGFydC9mb3JtLWRhdGEgaGVhZGVycy4gSWYgc2VuZGluZyBKU09OLCBtYWtlIHN1cmUgdGhlIGNoYXJ0IGRhdGEgaXMgaW4gdGhlICdpbmZpbGUnLCAnb3B0aW9ucycsIG9yICdkYXRhJyBhdHRyaWJ1dGUuIElmIHNlbmRpbmcgU1ZHLCBlbnN1cmUgaXQgaXMgaW4gdGhlICdzdmcnIGF0dHJpYnV0ZS5cIixcclxuICAgICAgICA0MDBcclxuICAgICAgKTtcclxuICAgIH1cclxuXHJcbiAgICBsZXQgY2FsbFJlc3BvbnNlID0gZmFsc2U7XHJcblxyXG4gICAgLy8gQ2FsbCB0aGUgYmVmb3JlIHJlcXVlc3QgZnVuY3Rpb25zXHJcbiAgICBjYWxsUmVzcG9uc2UgPSBkb0NhbGxiYWNrcyhiZWZvcmVSZXF1ZXN0LCByZXF1ZXN0LCByZXNwb25zZSwge1xyXG4gICAgICBpZCxcclxuICAgICAgdW5pcXVlSWQsXHJcbiAgICAgIHR5cGUsXHJcbiAgICAgIGJvZHlcclxuICAgIH0pO1xyXG5cclxuICAgIC8vIEJsb2NrIHRoZSByZXF1ZXN0IGlmIG9uZSBvZiBhIGNhbGxiYWNrcyBmYWlsZWRcclxuICAgIGlmIChjYWxsUmVzcG9uc2UgIT09IHRydWUpIHtcclxuICAgICAgcmV0dXJuIHJlc3BvbnNlLnNlbmQoY2FsbFJlc3BvbnNlKTtcclxuICAgIH1cclxuXHJcbiAgICBsZXQgY29ubmVjdGlvbkFib3J0ZWQgPSBmYWxzZTtcclxuXHJcbiAgICAvLyBJbiBjYXNlIHRoZSBjb25uZWN0aW9uIGlzIGNsb3NlZCwgZm9yY2UgdG8gYWJvcnQgZnVydGhlciBhY3Rpb25zXHJcbiAgICByZXF1ZXN0LnNvY2tldC5vbignY2xvc2UnLCAoKSA9PiB7XHJcbiAgICAgIGNvbm5lY3Rpb25BYm9ydGVkID0gdHJ1ZTtcclxuICAgIH0pO1xyXG5cclxuICAgIGxvZyg0LCBgW2V4cG9ydF0gR290IGFuIGluY29taW5nIEhUVFAgcmVxdWVzdCB3aXRoIElEICR7dW5pcXVlSWR9LmApO1xyXG5cclxuICAgIGJvZHkuY29uc3RyID0gKHR5cGVvZiBib2R5LmNvbnN0ciA9PT0gJ3N0cmluZycgJiYgYm9keS5jb25zdHIpIHx8ICdjaGFydCc7XHJcblxyXG4gICAgLy8gR2F0aGVyIGFuZCBvcmdhbml6ZSBvcHRpb25zIGZyb20gdGhlIHBheWxvYWRcclxuICAgIGNvbnN0IHJlcXVlc3RPcHRpb25zID0ge1xyXG4gICAgICBleHBvcnQ6IHtcclxuICAgICAgICBpbnN0cixcclxuICAgICAgICB0eXBlLFxyXG4gICAgICAgIGNvbnN0cjogYm9keS5jb25zdHJbMF0udG9Mb3dlckNhc2UoKSArIGJvZHkuY29uc3RyLnN1YnN0cigxKSxcclxuICAgICAgICBoZWlnaHQ6IGJvZHkuaGVpZ2h0LFxyXG4gICAgICAgIHdpZHRoOiBib2R5LndpZHRoLFxyXG4gICAgICAgIHNjYWxlOiBib2R5LnNjYWxlIHx8IGRlZmF1bHRPcHRpb25zLmV4cG9ydC5zY2FsZSxcclxuICAgICAgICBnbG9iYWxPcHRpb25zOiBpc0NvcnJlY3RKU09OKGJvZHkuZ2xvYmFsT3B0aW9ucywgdHJ1ZSksXHJcbiAgICAgICAgdGhlbWVPcHRpb25zOiBpc0NvcnJlY3RKU09OKGJvZHkudGhlbWVPcHRpb25zLCB0cnVlKVxyXG4gICAgICB9LFxyXG4gICAgICBjdXN0b21Mb2dpYzoge1xyXG4gICAgICAgIGFsbG93Q29kZUV4ZWN1dGlvbjogZ2V0QWxsb3dDb2RlRXhlY3V0aW9uKCksXHJcbiAgICAgICAgYWxsb3dGaWxlUmVzb3VyY2VzOiBmYWxzZSxcclxuICAgICAgICByZXNvdXJjZXM6IGlzQ29ycmVjdEpTT04oYm9keS5yZXNvdXJjZXMsIHRydWUpLFxyXG4gICAgICAgIGNhbGxiYWNrOiBib2R5LmNhbGxiYWNrLFxyXG4gICAgICAgIGN1c3RvbUNvZGU6IGJvZHkuY3VzdG9tQ29kZVxyXG4gICAgICB9XHJcbiAgICB9O1xyXG5cclxuICAgIGlmIChpbnN0cikge1xyXG4gICAgICAvLyBTdHJpbmdpZnkgSlNPTiB3aXRoIG9wdGlvbnNcclxuICAgICAgcmVxdWVzdE9wdGlvbnMuZXhwb3J0Lmluc3RyID0gb3B0aW9uc1N0cmluZ2lmeShcclxuICAgICAgICBpbnN0cixcclxuICAgICAgICByZXF1ZXN0T3B0aW9ucy5jdXN0b21Mb2dpYy5hbGxvd0NvZGVFeGVjdXRpb25cclxuICAgICAgKTtcclxuICAgIH1cclxuXHJcbiAgICAvLyBNZXJnZSB0aGUgcmVxdWVzdCBvcHRpb25zIGludG8gZGVmYXVsdCBvbmVzXHJcbiAgICBjb25zdCBvcHRpb25zID0gbWVyZ2VDb25maWdPcHRpb25zKGRlZmF1bHRPcHRpb25zLCByZXF1ZXN0T3B0aW9ucyk7XHJcblxyXG4gICAgLy8gU2F2ZSB0aGUgSlNPTiBpZiBleGlzdHNcclxuICAgIG9wdGlvbnMuZXhwb3J0Lm9wdGlvbnMgPSBpbnN0cjtcclxuXHJcbiAgICAvLyBMYXN0bHksIGFkZCB0aGUgc2VydmVyIHNwZWNpZmljIGFyZ3VtZW50cyBpbnRvIG9wdGlvbnMgYXMgcGF5bG9hZFxyXG4gICAgb3B0aW9ucy5wYXlsb2FkID0ge1xyXG4gICAgICBzdmc6IGJvZHkuc3ZnIHx8IGZhbHNlLFxyXG4gICAgICBiNjQ6IGJvZHkuYjY0IHx8IGZhbHNlLFxyXG4gICAgICBub0Rvd25sb2FkOiBib2R5Lm5vRG93bmxvYWQgfHwgZmFsc2UsXHJcbiAgICAgIHJlcXVlc3RJZDogdW5pcXVlSWRcclxuICAgIH07XHJcblxyXG4gICAgLy8gVGVzdCB4bGluazpocmVmIGVsZW1lbnRzIGZyb20gcGF5bG9hZCdzIFNWR1xyXG4gICAgaWYgKGJvZHkuc3ZnICYmIGlzUHJpdmF0ZVJhbmdlVXJsRm91bmQob3B0aW9ucy5wYXlsb2FkLnN2ZykpIHtcclxuICAgICAgdGhyb3cgbmV3IEh0dHBFcnJvcihcclxuICAgICAgICAnU1ZHIHBvdGVudGlhbGx5IGNvbnRhaW4gYXQgbGVhc3Qgb25lIGZvcmJpZGRlbiBVUkwgaW4geGxpbms6aHJlZiBlbGVtZW50LiBQbGVhc2UgcmV2aWV3IHRoZSBTVkcgY29udGVudCBhbmQgZW5zdXJlIHRoYXQgYWxsIHJlZmVyZW5jZWQgVVJMcyBjb21wbHkgd2l0aCBzZWN1cml0eSBwb2xpY2llcy4nLFxyXG4gICAgICAgIDQwMFxyXG4gICAgICApO1xyXG4gICAgfVxyXG5cclxuICAgIC8vIFN0YXJ0IHRoZSBleHBvcnQgcHJvY2Vzc1xyXG4gICAgYXdhaXQgc3RhcnRFeHBvcnQob3B0aW9ucywgKGVycm9yLCBpbmZvKSA9PiB7XHJcbiAgICAgIC8vIFJlbW92ZSB0aGUgY2xvc2UgZXZlbnQgZnJvbSB0aGUgc29ja2V0XHJcbiAgICAgIHJlcXVlc3Quc29ja2V0LnJlbW92ZUFsbExpc3RlbmVycygnY2xvc2UnKTtcclxuXHJcbiAgICAgIC8vIEFmdGVyIHRoZSB3aG9sZSBleHBvcnRpbmcgcHJvY2Vzc1xyXG4gICAgICBpZiAoZGVmYXVsdE9wdGlvbnMuc2VydmVyLmJlbmNobWFya2luZykge1xyXG4gICAgICAgIGxvZyhcclxuICAgICAgICAgIDUsXHJcbiAgICAgICAgICBgW2JlbmNobWFya10gUmVxdWVzdCB3aXRoIElEICR7dW5pcXVlSWR9IC0gQWZ0ZXIgdGhlIHdob2xlIGV4cG9ydGluZyBwcm9jZXNzOiAke3N0b3BDb3VudGVyKCl9bXMuYFxyXG4gICAgICAgICk7XHJcbiAgICAgIH1cclxuXHJcbiAgICAgIC8vIElmIHRoZSBjb25uZWN0aW9uIHdhcyBjbG9zZWQsIGRvIG5vdGhpbmdcclxuICAgICAgaWYgKGNvbm5lY3Rpb25BYm9ydGVkKSB7XHJcbiAgICAgICAgcmV0dXJuIGxvZyhcclxuICAgICAgICAgIDMsXHJcbiAgICAgICAgICBgW2V4cG9ydF0gVGhlIGNsaWVudCBjbG9zZWQgdGhlIGNvbm5lY3Rpb24gYmVmb3JlIHRoZSBjaGFydCBmaW5pc2hlZCBwcm9jZXNzaW5nLmBcclxuICAgICAgICApO1xyXG4gICAgICB9XHJcblxyXG4gICAgICAvLyBJZiBlcnJvciwgbG9nIGl0IGFuZCBzZW5kIGl0IHRvIHRoZSBlcnJvciBtaWRkbGV3YXJlXHJcbiAgICAgIGlmIChlcnJvcikge1xyXG4gICAgICAgIHRocm93IGVycm9yO1xyXG4gICAgICB9XHJcblxyXG4gICAgICAvLyBJZiBkYXRhIGlzIG1pc3NpbmcsIGxvZyB0aGUgbWVzc2FnZSBhbmQgc2VuZCBpdCB0byB0aGUgZXJyb3IgbWlkZGxld2FyZVxyXG4gICAgICBpZiAoIWluZm8gfHwgIWluZm8ucmVzdWx0KSB7XHJcbiAgICAgICAgdGhyb3cgbmV3IEh0dHBFcnJvcihcclxuICAgICAgICAgIGBVbmV4cGVjdGVkIHJldHVybiBmcm9tIGNoYXJ0IGdlbmVyYXRpb24uIFBsZWFzZSBjaGVjayB5b3VyIHJlcXVlc3QgZGF0YS4gRm9yIHRoZSByZXF1ZXN0IHdpdGggSUQgJHt1bmlxdWVJZH0sIHRoZSByZXN1bHQgaXMgJHtpbmZvLnJlc3VsdH0uYCxcclxuICAgICAgICAgIDQwMFxyXG4gICAgICAgICk7XHJcbiAgICAgIH1cclxuXHJcbiAgICAgIC8vIEdldCB0aGUgdHlwZSBmcm9tIG9wdGlvbnNcclxuICAgICAgdHlwZSA9IGluZm8ub3B0aW9ucy5leHBvcnQudHlwZTtcclxuXHJcbiAgICAgIC8vIFRoZSBhZnRlciByZXF1ZXN0IGNhbGxiYWNrc1xyXG4gICAgICBkb0NhbGxiYWNrcyhhZnRlclJlcXVlc3QsIHJlcXVlc3QsIHJlc3BvbnNlLCB7IGlkLCBib2R5OiBpbmZvLnJlc3VsdCB9KTtcclxuXHJcbiAgICAgIGlmIChpbmZvLnJlc3VsdCkge1xyXG4gICAgICAgIC8vIElmIG9ubHkgYmFzZTY0IGlzIHJlcXVpcmVkLCByZXR1cm4gaXRcclxuICAgICAgICBpZiAoYm9keS5iNjQpIHtcclxuICAgICAgICAgIC8vIFNWRyBFeGNlcHRpb24gZm9yIHRoZSBIaWdoY2hhcnRzIDExLjMuMCB2ZXJzaW9uXHJcbiAgICAgICAgICBpZiAodHlwZSA9PT0gJ3BkZicgfHwgdHlwZSA9PSAnc3ZnJykge1xyXG4gICAgICAgICAgICByZXR1cm4gcmVzcG9uc2Uuc2VuZChcclxuICAgICAgICAgICAgICBCdWZmZXIuZnJvbShpbmZvLnJlc3VsdCwgJ3V0ZjgnKS50b1N0cmluZygnYmFzZTY0JylcclxuICAgICAgICAgICAgKTtcclxuICAgICAgICAgIH1cclxuXHJcbiAgICAgICAgICByZXR1cm4gcmVzcG9uc2Uuc2VuZChpbmZvLnJlc3VsdCk7XHJcbiAgICAgICAgfVxyXG5cclxuICAgICAgICAvLyBTZXQgY29ycmVjdCBjb250ZW50IHR5cGVcclxuICAgICAgICByZXNwb25zZS5oZWFkZXIoJ0NvbnRlbnQtVHlwZScsIHJldmVyc2VkTWltZVt0eXBlXSB8fCAnaW1hZ2UvcG5nJyk7XHJcblxyXG4gICAgICAgIC8vIERlY2lkZSB3aGV0aGVyIHRvIGRvd25sb2FkIG9yIG5vdCBjaGFydCBmaWxlXHJcbiAgICAgICAgaWYgKCFib2R5Lm5vRG93bmxvYWQpIHtcclxuICAgICAgICAgIHJlc3BvbnNlLmF0dGFjaG1lbnQoXHJcbiAgICAgICAgICAgIGAke3JlcXVlc3QucGFyYW1zLmZpbGVuYW1lIHx8IHJlcXVlc3QuYm9keS5maWxlbmFtZSB8fCAnY2hhcnQnfS4ke1xyXG4gICAgICAgICAgICAgIHR5cGUgfHwgJ3BuZydcclxuICAgICAgICAgICAgfWBcclxuICAgICAgICAgICk7XHJcbiAgICAgICAgfVxyXG5cclxuICAgICAgICAvLyBJZiBTVkcsIHJldHVybiBwbGFpbiBjb250ZW50XHJcbiAgICAgICAgcmV0dXJuIHR5cGUgPT09ICdzdmcnXHJcbiAgICAgICAgICA/IHJlc3BvbnNlLnNlbmQoaW5mby5yZXN1bHQpXHJcbiAgICAgICAgICA6IHJlc3BvbnNlLnNlbmQoQnVmZmVyLmZyb20oaW5mby5yZXN1bHQsICdiYXNlNjQnKSk7XHJcbiAgICAgIH1cclxuICAgIH0pO1xyXG4gIH0gY2F0Y2ggKGVycm9yKSB7XHJcbiAgICBuZXh0KGVycm9yKTtcclxuICB9XHJcbn07XHJcblxyXG5leHBvcnQgZGVmYXVsdCAoYXBwKSA9PiB7XHJcbiAgLyoqXHJcbiAgICogQWRkcyB0aGUgUE9TVCAvIGEgcm91dGUgZm9yIGhhbmRsaW5nIFBPU1QgcmVxdWVzdHMgYXQgdGhlIHJvb3QgZW5kcG9pbnQuXHJcbiAgICovXHJcbiAgYXBwLnBvc3QoJy8nLCBleHBvcnRIYW5kbGVyKTtcclxuXHJcbiAgLyoqXHJcbiAgICogQWRkcyB0aGUgUE9TVCAvOmZpbGVuYW1lIGEgcm91dGUgZm9yIGhhbmRsaW5nIFBPU1QgcmVxdWVzdHMgd2l0aFxyXG4gICAqIGEgc3BlY2lmaWVkIGZpbGVuYW1lIHBhcmFtZXRlci5cclxuICAgKi9cclxuICBhcHAucG9zdCgnLzpmaWxlbmFtZScsIGV4cG9ydEhhbmRsZXIpO1xyXG59O1xyXG4iLCIvKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKlxyXG5cclxuSGlnaGNoYXJ0cyBFeHBvcnQgU2VydmVyXHJcblxyXG5Db3B5cmlnaHQgKGMpIDIwMTYtMjAyNCwgSGlnaHNvZnRcclxuXHJcbkxpY2VuY2VkIHVuZGVyIHRoZSBNSVQgbGljZW5jZS5cclxuXHJcbkFkZGl0aW9uYWxseSBhIHZhbGlkIEhpZ2hjaGFydHMgbGljZW5zZSBpcyByZXF1aXJlZCBmb3IgdXNlLlxyXG5cclxuU2VlIExJQ0VOU0UgZmlsZSBpbiByb290IGZvciBkZXRhaWxzLlxyXG5cclxuKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKi9cclxuXHJcbmltcG9ydCB7IHJlYWRGaWxlU3luYyB9IGZyb20gJ2ZzJztcclxuaW1wb3J0IHsgam9pbiBhcyBwYXRoZXIgfSBmcm9tICdwYXRoJztcclxuaW1wb3J0IHsgbG9nIH0gZnJvbSAnLi4vLi4vbG9nZ2VyLmpzJztcclxuXHJcbmltcG9ydCB7IHZlcnNpb24gfSBmcm9tICcuLi8uLi9jYWNoZS5qcyc7XHJcbmltcG9ydCB7IGFkZEludGVydmFsIH0gZnJvbSAnLi4vLi4vaW50ZXJ2YWxzLmpzJztcclxuaW1wb3J0IHBvb2wgZnJvbSAnLi4vLi4vcG9vbC5qcyc7XHJcbmltcG9ydCB7IF9fZGlybmFtZSB9IGZyb20gJy4uLy4uL3V0aWxzLmpzJztcclxuXHJcbmNvbnN0IHBrZ0ZpbGUgPSBKU09OLnBhcnNlKHJlYWRGaWxlU3luYyhwYXRoZXIoX19kaXJuYW1lLCAncGFja2FnZS5qc29uJykpKTtcclxuXHJcbmNvbnN0IHNlcnZlclN0YXJ0VGltZSA9IG5ldyBEYXRlKCk7XHJcblxyXG5jb25zdCBzdWNjZXNzUmF0ZXMgPSBbXTtcclxuY29uc3QgcmVjb3JkSW50ZXJ2YWwgPSA2MCAqIDEwMDA7IC8vIHJlY29yZCBldmVyeSBtaW51dGVcclxuY29uc3Qgd2luZG93U2l6ZSA9IDMwOyAvLyAzMCBtaW51dGVzXHJcblxyXG4vKipcclxuICogQ2FsY3VsYXRlcyBtb3ZpbmcgYXZlcmFnZSBpbmRpY2F0b3IgYmFzZWQgb24gdGhlIGRhdGEgZnJvbSB0aGUgc3VjY2Vzc1JhdGVzXHJcbiAqIGFycmF5LlxyXG4gKlxyXG4gKiBAcmV0dXJucyB7bnVtYmVyfSAtIEEgbW92aW5nIGF2ZXJhZ2UgZm9yIHN1Y2Nlc3MgcmF0aW8gb2YgdGhlIHNlcnZlciBleHBvcnRzLlxyXG4gKi9cclxuZnVuY3Rpb24gY2FsY3VsYXRlTW92aW5nQXZlcmFnZSgpIHtcclxuICBjb25zdCBzdW0gPSBzdWNjZXNzUmF0ZXMucmVkdWNlKChhLCBiKSA9PiBhICsgYiwgMCk7XHJcbiAgcmV0dXJuIHN1bSAvIHN1Y2Nlc3NSYXRlcy5sZW5ndGg7XHJcbn1cclxuXHJcbi8qKlxyXG4gKiBTdGFydHMgdGhlIGludGVydmFsIHJlc3BvbnNpYmxlIGZvciBjYWxjdWxhdGluZyBjdXJyZW50IHN1Y2Nlc3MgcmF0ZSByYXRpb1xyXG4gKiBhbmQgZ2F0aGVyc1xyXG4gKlxyXG4gKiBAcmV0dXJucyB7Tm9kZUpTLlRpbWVvdXR9IGlkIC0gSWQgb2YgYW4gaW50ZXJ2YWwuXHJcbiAqL1xyXG5leHBvcnQgY29uc3Qgc3RhcnRTdWNjZXNzUmF0ZSA9ICgpID0+XHJcbiAgc2V0SW50ZXJ2YWwoKCkgPT4ge1xyXG4gICAgY29uc3Qgc3RhdHMgPSBwb29sLmdldFN0YXRzKCk7XHJcbiAgICBjb25zdCBzdWNjZXNzUmF0aW8gPVxyXG4gICAgICBzdGF0cy5leHBvcnRBdHRlbXB0cyA9PT0gMFxyXG4gICAgICAgID8gMVxyXG4gICAgICAgIDogKHN0YXRzLnBlcmZvcm1lZEV4cG9ydHMgLyBzdGF0cy5leHBvcnRBdHRlbXB0cykgKiAxMDA7XHJcblxyXG4gICAgc3VjY2Vzc1JhdGVzLnB1c2goc3VjY2Vzc1JhdGlvKTtcclxuICAgIGlmIChzdWNjZXNzUmF0ZXMubGVuZ3RoID4gd2luZG93U2l6ZSkge1xyXG4gICAgICBzdWNjZXNzUmF0ZXMuc2hpZnQoKTtcclxuICAgIH1cclxuICB9LCByZWNvcmRJbnRlcnZhbCk7XHJcblxyXG4vKipcclxuICogQWRkcyB0aGUgL2hlYWx0aCBhbmQgL3N1Y2Nlc3MtbW92aW5nLWF2ZXJhZ2Ugcm91dGVzXHJcbiAqIHdoaWNoIG91dHB1dCBiYXNpYyBzdGF0cyBmb3IgdGhlIHNlcnZlci5cclxuICovXHJcbmV4cG9ydCBkZWZhdWx0IGZ1bmN0aW9uIGFkZEhlYWx0aFJvdXRlcyhhcHApIHtcclxuICBpZiAoIWFwcCkge1xyXG4gICAgcmV0dXJuIGZhbHNlO1xyXG4gIH1cclxuXHJcbiAgLy8gU3RhcnQgcHJvY2Vzc2luZyBzdWNjZXNzIHJhdGUgcmF0aW8gaW50ZXJ2YWwgYW5kIHNhdmUgaXRzIGlkIHRvIHRoZSBhcnJheVxyXG4gIC8vIGZvciB0aGUgZ3JhY2VmdWwgY2xlYXJpbmcgb24gc2h1dGRvd24gd2l0aCBpbmplY3RlZCBhZGRJbnRlcnZhbCBmdW50aW9uXHJcbiAgYWRkSW50ZXJ2YWwoc3RhcnRTdWNjZXNzUmF0ZSgpKTtcclxuXHJcbiAgYXBwLmdldCgnL2hlYWx0aCcsIChfLCByZXMpID0+IHtcclxuICAgIGNvbnN0IHN0YXRzID0gcG9vbC5nZXRTdGF0cygpO1xyXG4gICAgY29uc3QgcGVyaW9kID0gc3VjY2Vzc1JhdGVzLmxlbmd0aDtcclxuICAgIGNvbnN0IG1vdmluZ0F2ZXJhZ2UgPSBjYWxjdWxhdGVNb3ZpbmdBdmVyYWdlKCk7XHJcblxyXG4gICAgbG9nKDQsICdbaGVhbHRoLmpzXSBHRVQgL2hlYWx0aCBbMjAwXSAtIHJldHVybmluZyBzZXJ2ZXIgaGVhbHRoLicpO1xyXG5cclxuICAgIHJlcy5zZW5kKHtcclxuICAgICAgc3RhdHVzOiAnT0snLFxyXG4gICAgICBib290VGltZTogc2VydmVyU3RhcnRUaW1lLFxyXG4gICAgICB1cHRpbWU6XHJcbiAgICAgICAgTWF0aC5mbG9vcihcclxuICAgICAgICAgIChuZXcgRGF0ZSgpLmdldFRpbWUoKSAtIHNlcnZlclN0YXJ0VGltZS5nZXRUaW1lKCkpIC8gMTAwMCAvIDYwXHJcbiAgICAgICAgKSArICcgbWludXRlcycsXHJcbiAgICAgIHZlcnNpb246IHBrZ0ZpbGUudmVyc2lvbixcclxuICAgICAgaGlnaGNoYXJ0c1ZlcnNpb246IHZlcnNpb24oKSxcclxuICAgICAgYXZlcmFnZVByb2Nlc3NpbmdUaW1lOiBzdGF0cy5zcGVudEF2ZXJhZ2UsXHJcbiAgICAgIHBlcmZvcm1lZEV4cG9ydHM6IHN0YXRzLnBlcmZvcm1lZEV4cG9ydHMsXHJcbiAgICAgIGZhaWxlZEV4cG9ydHM6IHN0YXRzLmRyb3BwZWRFeHBvcnRzLFxyXG4gICAgICBleHBvcnRBdHRlbXB0czogc3RhdHMuZXhwb3J0QXR0ZW1wdHMsXHJcbiAgICAgIHN1Y2Vzc1JhdGlvOiAoc3RhdHMucGVyZm9ybWVkRXhwb3J0cyAvIHN0YXRzLmV4cG9ydEF0dGVtcHRzKSAqIDEwMCxcclxuICAgICAgLy8gZXNsaW50LWRpc2FibGUtbmV4dC1saW5lIGltcG9ydC9uby1uYW1lZC1hcy1kZWZhdWx0LW1lbWJlclxyXG4gICAgICBwb29sOiBwb29sLmdldFBvb2xJbmZvSlNPTigpLFxyXG5cclxuICAgICAgLy8gTW92aW5nIGF2ZXJhZ2VcclxuICAgICAgcGVyaW9kLFxyXG4gICAgICBtb3ZpbmdBdmVyYWdlLFxyXG4gICAgICBtZXNzYWdlOiBgTGFzdCAke3BlcmlvZH0gbWludXRlcyBoYWQgYSBzdWNjZXNzIHJhdGUgb2YgJHttb3ZpbmdBdmVyYWdlLnRvRml4ZWQoMil9JS5gLFxyXG5cclxuICAgICAgLy8gU1ZHL0pTT04gYXR0ZW1wdHNcclxuICAgICAgc3ZnRXhwb3J0QXR0ZW1wdHM6IHN0YXRzLmV4cG9ydEZyb21TdmdBdHRlbXB0cyxcclxuICAgICAganNvbkV4cG9ydEF0dGVtcHRzOiBzdGF0cy5wZXJmb3JtZWRFeHBvcnRzIC0gc3RhdHMuZXhwb3J0RnJvbVN2Z0F0dGVtcHRzXHJcbiAgICB9KTtcclxuICB9KTtcclxufVxyXG4iLCIvKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKlxyXG5cclxuSGlnaGNoYXJ0cyBFeHBvcnQgU2VydmVyXHJcblxyXG5Db3B5cmlnaHQgKGMpIDIwMTYtMjAyNCwgSGlnaHNvZnRcclxuXHJcbkxpY2VuY2VkIHVuZGVyIHRoZSBNSVQgbGljZW5jZS5cclxuXHJcbkFkZGl0aW9uYWxseSBhIHZhbGlkIEhpZ2hjaGFydHMgbGljZW5zZSBpcyByZXF1aXJlZCBmb3IgdXNlLlxyXG5cclxuU2VlIExJQ0VOU0UgZmlsZSBpbiByb290IGZvciBkZXRhaWxzLlxyXG5cclxuKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKi9cclxuXHJcbmltcG9ydCB7IHByb21pc2VzIGFzIGZzUHJvbWlzZXMgfSBmcm9tICdmcyc7XHJcbmltcG9ydCB7IHBvc2l4IH0gZnJvbSAncGF0aCc7XHJcblxyXG5pbXBvcnQgY29ycyBmcm9tICdjb3JzJztcclxuaW1wb3J0IGV4cHJlc3MgZnJvbSAnZXhwcmVzcyc7XHJcbmltcG9ydCBodHRwIGZyb20gJ2h0dHAnO1xyXG5pbXBvcnQgaHR0cHMgZnJvbSAnaHR0cHMnO1xyXG5pbXBvcnQgbXVsdGVyIGZyb20gJ211bHRlcic7XHJcblxyXG5pbXBvcnQgZXJyb3JIYW5kbGVyIGZyb20gJy4vZXJyb3IuanMnO1xyXG5pbXBvcnQgcmF0ZUxpbWl0IGZyb20gJy4vcmF0ZV9saW1pdC5qcyc7XHJcbmltcG9ydCB7IGxvZywgbG9nV2l0aFN0YWNrIH0gZnJvbSAnLi4vbG9nZ2VyLmpzJztcclxuaW1wb3J0IHsgX19kaXJuYW1lIH0gZnJvbSAnLi4vdXRpbHMuanMnO1xyXG5cclxuaW1wb3J0IHZTd2l0Y2hSb3V0ZSBmcm9tICcuL3JvdXRlcy9jaGFuZ2VfaGNfdmVyc2lvbi5qcyc7XHJcbmltcG9ydCBleHBvcnRSb3V0ZXMgZnJvbSAnLi9yb3V0ZXMvZXhwb3J0LmpzJztcclxuaW1wb3J0IGhlYWx0aFJvdXRlIGZyb20gJy4vcm91dGVzL2hlYWx0aC5qcyc7XHJcbmltcG9ydCB1aVJvdXRlIGZyb20gJy4vcm91dGVzL3VpLmpzJztcclxuXHJcbmltcG9ydCBFeHBvcnRFcnJvciBmcm9tICcuLi9lcnJvcnMvRXhwb3J0RXJyb3IuanMnO1xyXG5cclxuLy8gQXJyYXkgb2YgYW4gYWN0aXZlIHNlcnZlcnNcclxuY29uc3QgYWN0aXZlU2VydmVycyA9IG5ldyBNYXAoKTtcclxuXHJcbi8vIENyZWF0ZSBleHByZXNzIGFwcFxyXG5jb25zdCBhcHAgPSBleHByZXNzKCk7XHJcblxyXG4vLyBEaXNhYmxlIHRoZSBYLVBvd2VyZWQtQnkgaGVhZGVyXHJcbmFwcC5kaXNhYmxlKCd4LXBvd2VyZWQtYnknKTtcclxuXHJcbi8vIEVuYWJsZSBDT1JTIHN1cHBvcnRcclxuYXBwLnVzZShjb3JzKCkpO1xyXG5cclxuLy8gRW5hYmxlIHBhcnNpbmcgb2YgZm9ybSBkYXRhIChmaWxlcykgd2l0aCBNdWx0ZXIgcGFja2FnZVxyXG5jb25zdCBzdG9yYWdlID0gbXVsdGVyLm1lbW9yeVN0b3JhZ2UoKTtcclxuY29uc3QgdXBsb2FkID0gbXVsdGVyKHtcclxuICBzdG9yYWdlLFxyXG4gIGxpbWl0czoge1xyXG4gICAgZmllbGRTaXplOiA1MCAqIDEwMjQgKiAxMDI0XHJcbiAgfVxyXG59KTtcclxuXHJcbi8vIEVuYWJsZSBib2R5IHBhcnNlclxyXG5hcHAudXNlKGV4cHJlc3MuanNvbih7IGxpbWl0OiA1MCAqIDEwMjQgKiAxMDI0IH0pKTtcclxuYXBwLnVzZShleHByZXNzLnVybGVuY29kZWQoeyBleHRlbmRlZDogdHJ1ZSwgbGltaXQ6IDUwICogMTAyNCAqIDEwMjQgfSkpO1xyXG5cclxuLy8gVXNlIG9ubHkgbm9uLWZpbGUgbXVsdGlwYXJ0IGZvcm0gZmllbGRzXHJcbmFwcC51c2UodXBsb2FkLm5vbmUoKSk7XHJcblxyXG4vKipcclxuICogQXR0YWNoIGVycm9yIGhhbmRsZXJzIHRvIHRoZSBzZXJ2ZXIuXHJcbiAqXHJcbiAqIEBwYXJhbSB7aHR0cC5TZXJ2ZXJ9IHNlcnZlciAtIFRoZSBIVFRQL0hUVFBTIHNlcnZlciBpbnN0YW5jZS5cclxuICovXHJcbmNvbnN0IGF0dGFjaFNlcnZlckVycm9ySGFuZGxlcnMgPSAoc2VydmVyKSA9PiB7XHJcbiAgc2VydmVyLm9uKCdjbGllbnRFcnJvcicsIChlcnJvcikgPT4ge1xyXG4gICAgbG9nV2l0aFN0YWNrKDEsIGVycm9yLCBgW3NlcnZlcl0gQ2xpZW50IGVycm9yOiAke2Vycm9yLm1lc3NhZ2V9YCk7XHJcbiAgfSk7XHJcblxyXG4gIHNlcnZlci5vbignZXJyb3InLCAoZXJyb3IpID0+IHtcclxuICAgIGxvZ1dpdGhTdGFjaygxLCBlcnJvciwgYFtzZXJ2ZXJdIFNlcnZlciBlcnJvcjogJHtlcnJvci5tZXNzYWdlfWApO1xyXG4gIH0pO1xyXG5cclxuICBzZXJ2ZXIub24oJ2Nvbm5lY3Rpb24nLCAoc29ja2V0KSA9PiB7XHJcbiAgICBzb2NrZXQub24oJ2Vycm9yJywgKGVycm9yKSA9PiB7XHJcbiAgICAgIGxvZ1dpdGhTdGFjaygxLCBlcnJvciwgYFtzZXJ2ZXJdIFNvY2tldCBlcnJvcjogJHtlcnJvci5tZXNzYWdlfWApO1xyXG4gICAgfSk7XHJcbiAgfSk7XHJcbn07XHJcblxyXG4vKipcclxuICogU3RhcnRzIGFuIEhUVFAgc2VydmVyIGJhc2VkIG9uIHRoZSBwcm92aWRlZCBjb25maWd1cmF0aW9uLiBUaGUgYHNlcnZlckNvbmZpZ2BcclxuICogb2JqZWN0IGNvbnRhaW5zIGFsbCBzZXJ2ZXIgcmVsYXRlZCBwcm9wZXJ0aWVzIChzZWUgdGhlIGBzZXJ2ZXJgIHNlY3Rpb25cclxuICogaW4gdGhlIGBsaWIvc2NoZW1hcy9jb25maWcuanNgIGZpbGUgZm9yIGEgcmVmZXJlbmNlKS5cclxuICpcclxuICogQHBhcmFtIHtPYmplY3R9IHNlcnZlckNvbmZpZyAtIFRoZSBzZXJ2ZXIgY29uZmlndXJhdGlvbiBvYmplY3QuXHJcbiAqXHJcbiAqIEB0aHJvd3Mge0V4cG9ydEVycm9yfSAtIFRocm93cyBhbiBlcnJvciBpZiB0aGUgc2VydmVyIGNhbm5vdCBiZSBjb25maWd1cmVkXHJcbiAqIGFuZCBzdGFydGVkLlxyXG4gKi9cclxuZXhwb3J0IGNvbnN0IHN0YXJ0U2VydmVyID0gYXN5bmMgKHNlcnZlckNvbmZpZykgPT4ge1xyXG4gIHRyeSB7XHJcbiAgICAvLyBTdG9wIGlmIG5vdCBlbmFibGVkXHJcbiAgICBpZiAoIXNlcnZlckNvbmZpZy5lbmFibGUpIHtcclxuICAgICAgcmV0dXJuIGZhbHNlO1xyXG4gICAgfVxyXG5cclxuICAgIC8vIExpc3RlbiBIVFRQIHNlcnZlclxyXG4gICAgaWYgKCFzZXJ2ZXJDb25maWcuc3NsLmZvcmNlKSB7XHJcbiAgICAgIC8vIE1haW4gc2VydmVyIGluc3RhbmNlIChIVFRQKVxyXG4gICAgICBjb25zdCBodHRwU2VydmVyID0gaHR0cC5jcmVhdGVTZXJ2ZXIoYXBwKTtcclxuXHJcbiAgICAgIC8vIEF0dGFjaCBlcnJvciBoYW5kbGVycyBhbmQgbGlzdGVuIHRvIHRoZSBzZXJ2ZXJcclxuICAgICAgYXR0YWNoU2VydmVyRXJyb3JIYW5kbGVycyhodHRwU2VydmVyKTtcclxuXHJcbiAgICAgIC8vIExpc3RlblxyXG4gICAgICBodHRwU2VydmVyLmxpc3RlbihzZXJ2ZXJDb25maWcucG9ydCwgc2VydmVyQ29uZmlnLmhvc3QpO1xyXG5cclxuICAgICAgLy8gU2F2ZSB0aGUgcmVmZXJlbmNlIHRvIEhUVFAgc2VydmVyXHJcbiAgICAgIGFjdGl2ZVNlcnZlcnMuc2V0KHNlcnZlckNvbmZpZy5wb3J0LCBodHRwU2VydmVyKTtcclxuXHJcbiAgICAgIGxvZyhcclxuICAgICAgICAzLFxyXG4gICAgICAgIGBbc2VydmVyXSBTdGFydGVkIEhUVFAgc2VydmVyIG9uICR7c2VydmVyQ29uZmlnLmhvc3R9OiR7c2VydmVyQ29uZmlnLnBvcnR9LmBcclxuICAgICAgKTtcclxuICAgIH1cclxuXHJcbiAgICAvLyBMaXN0ZW4gSFRUUFMgc2VydmVyXHJcbiAgICBpZiAoc2VydmVyQ29uZmlnLnNzbC5lbmFibGUpIHtcclxuICAgICAgLy8gU2V0IHVwIGFuIFNTTCBzZXJ2ZXIgYWxzb1xyXG4gICAgICBsZXQga2V5LCBjZXJ0O1xyXG5cclxuICAgICAgdHJ5IHtcclxuICAgICAgICAvLyBHZXQgdGhlIFNTTCBrZXlcclxuICAgICAgICBrZXkgPSBhd2FpdCBmc1Byb21pc2VzLnJlYWRGaWxlKFxyXG4gICAgICAgICAgcG9zaXguam9pbihzZXJ2ZXJDb25maWcuc3NsLmNlcnRQYXRoLCAnc2VydmVyLmtleScpLFxyXG4gICAgICAgICAgJ3V0ZjgnXHJcbiAgICAgICAgKTtcclxuXHJcbiAgICAgICAgLy8gR2V0IHRoZSBTU0wgY2VydGlmaWNhdGVcclxuICAgICAgICBjZXJ0ID0gYXdhaXQgZnNQcm9taXNlcy5yZWFkRmlsZShcclxuICAgICAgICAgIHBvc2l4LmpvaW4oc2VydmVyQ29uZmlnLnNzbC5jZXJ0UGF0aCwgJ3NlcnZlci5jcnQnKSxcclxuICAgICAgICAgICd1dGY4J1xyXG4gICAgICAgICk7XHJcbiAgICAgIH0gY2F0Y2ggKGVycm9yKSB7XHJcbiAgICAgICAgbG9nKFxyXG4gICAgICAgICAgMixcclxuICAgICAgICAgIGBbc2VydmVyXSBVbmFibGUgdG8gbG9hZCBrZXkvY2VydGlmaWNhdGUgZnJvbSB0aGUgJyR7c2VydmVyQ29uZmlnLnNzbC5jZXJ0UGF0aH0nIHBhdGguIENvdWxkIG5vdCBydW4gc2VjdXJlZCBsYXllciBzZXJ2ZXIuYFxyXG4gICAgICAgICk7XHJcbiAgICAgIH1cclxuXHJcbiAgICAgIGlmIChrZXkgJiYgY2VydCkge1xyXG4gICAgICAgIC8vIE1haW4gc2VydmVyIGluc3RhbmNlIChIVFRQUylcclxuICAgICAgICBjb25zdCBodHRwc1NlcnZlciA9IGh0dHBzLmNyZWF0ZVNlcnZlcih7IGtleSwgY2VydCB9LCBhcHApO1xyXG5cclxuICAgICAgICAvLyBBdHRhY2ggZXJyb3IgaGFuZGxlcnMgYW5kIGxpc3RlbiB0byB0aGUgc2VydmVyXHJcbiAgICAgICAgYXR0YWNoU2VydmVyRXJyb3JIYW5kbGVycyhodHRwc1NlcnZlcik7XHJcblxyXG4gICAgICAgIC8vIExpc3RlblxyXG4gICAgICAgIGh0dHBzU2VydmVyLmxpc3RlbihzZXJ2ZXJDb25maWcuc3NsLnBvcnQsIHNlcnZlckNvbmZpZy5ob3N0KTtcclxuXHJcbiAgICAgICAgLy8gU2F2ZSB0aGUgcmVmZXJlbmNlIHRvIEhUVFBTIHNlcnZlclxyXG4gICAgICAgIGFjdGl2ZVNlcnZlcnMuc2V0KHNlcnZlckNvbmZpZy5zc2wucG9ydCwgaHR0cHNTZXJ2ZXIpO1xyXG5cclxuICAgICAgICBsb2coXHJcbiAgICAgICAgICAzLFxyXG4gICAgICAgICAgYFtzZXJ2ZXJdIFN0YXJ0ZWQgSFRUUFMgc2VydmVyIG9uICR7c2VydmVyQ29uZmlnLmhvc3R9OiR7c2VydmVyQ29uZmlnLnNzbC5wb3J0fS5gXHJcbiAgICAgICAgKTtcclxuICAgICAgfVxyXG4gICAgfVxyXG5cclxuICAgIC8vIEVuYWJsZSB0aGUgcmF0ZSBsaW1pdGVyIGlmIGNvbmZpZyBzYXlzIHNvXHJcbiAgICBpZiAoXHJcbiAgICAgIHNlcnZlckNvbmZpZy5yYXRlTGltaXRpbmcgJiZcclxuICAgICAgc2VydmVyQ29uZmlnLnJhdGVMaW1pdGluZy5lbmFibGUgJiZcclxuICAgICAgIVswLCBOYU5dLmluY2x1ZGVzKHNlcnZlckNvbmZpZy5yYXRlTGltaXRpbmcubWF4UmVxdWVzdHMpXHJcbiAgICApIHtcclxuICAgICAgcmF0ZUxpbWl0KGFwcCwgc2VydmVyQ29uZmlnLnJhdGVMaW1pdGluZyk7XHJcbiAgICB9XHJcblxyXG4gICAgLy8gU2V0IHVwIHN0YXRpYyBmb2xkZXIncyByb3V0ZVxyXG4gICAgYXBwLnVzZShleHByZXNzLnN0YXRpYyhwb3NpeC5qb2luKF9fZGlybmFtZSwgJ3B1YmxpYycpKSk7XHJcblxyXG4gICAgLy8gU2V0IHVwIHJvdXRlc1xyXG4gICAgaGVhbHRoUm91dGUoYXBwKTtcclxuICAgIGV4cG9ydFJvdXRlcyhhcHApO1xyXG4gICAgdWlSb3V0ZShhcHApO1xyXG4gICAgdlN3aXRjaFJvdXRlKGFwcCk7XHJcblxyXG4gICAgLy8gU2V0IHVwIGNlbnRyYWxpemVkIGVycm9yIGhhbmRsZXJcclxuICAgIGVycm9ySGFuZGxlcihhcHApO1xyXG4gIH0gY2F0Y2ggKGVycm9yKSB7XHJcbiAgICB0aHJvdyBuZXcgRXhwb3J0RXJyb3IoXHJcbiAgICAgICdbc2VydmVyXSBDb3VsZCBub3QgY29uZmlndXJlIGFuZCBzdGFydCB0aGUgc2VydmVyLidcclxuICAgICkuc2V0RXJyb3IoZXJyb3IpO1xyXG4gIH1cclxufTtcclxuXHJcbi8qKlxyXG4gKiBDbG9zZXMgYWxsIHNlcnZlcnMgYXNzb2NpYXRlZCB3aXRoIEV4cHJlc3MgYXBwIGluc3RhbmNlLlxyXG4gKi9cclxuZXhwb3J0IGNvbnN0IGNsb3NlU2VydmVycyA9ICgpID0+IHtcclxuICBsb2coNCwgYFtzZXJ2ZXJdIENsb3NpbmcgYWxsIHNlcnZlcnMuYCk7XHJcbiAgZm9yIChjb25zdCBbcG9ydCwgc2VydmVyXSBvZiBhY3RpdmVTZXJ2ZXJzKSB7XHJcbiAgICBzZXJ2ZXIuY2xvc2UoKCkgPT4ge1xyXG4gICAgICBhY3RpdmVTZXJ2ZXJzLmRlbGV0ZShwb3J0KTtcclxuICAgICAgbG9nKDQsIGBbc2VydmVyXSBDbG9zZWQgc2VydmVyIG9uIHBvcnQ6ICR7cG9ydH0uYCk7XHJcbiAgICB9KTtcclxuICB9XHJcbn07XHJcblxyXG4vKipcclxuICogR2V0IGFsbCBzZXJ2ZXJzIGFzc29jaWF0ZWQgd2l0aCBFeHByZXNzIGFwcCBpbnN0YW5jZS5cclxuICpcclxuICogQHJldHVybnMge0FycmF5fSAtIFNlcnZlcnMgYXNzb2NpYXRlZCB3aXRoIEV4cHJlc3MgYXBwIGluc3RhbmNlLlxyXG4gKi9cclxuZXhwb3J0IGNvbnN0IGdldFNlcnZlcnMgPSAoKSA9PiBhY3RpdmVTZXJ2ZXJzO1xyXG5cclxuLyoqXHJcbiAqIEVuYWJsZSByYXRlIGxpbWl0aW5nIGZvciB0aGUgc2VydmVyLlxyXG4gKlxyXG4gKiBAcGFyYW0ge09iamVjdH0gbGltaXRDb25maWcgLSBDb25maWd1cmF0aW9uIG9iamVjdCBmb3IgcmF0ZSBsaW1pdGluZy5cclxuICovXHJcbmV4cG9ydCBjb25zdCBlbmFibGVSYXRlTGltaXRpbmcgPSAobGltaXRDb25maWcpID0+IHJhdGVMaW1pdChhcHAsIGxpbWl0Q29uZmlnKTtcclxuXHJcbi8qKlxyXG4gKiBHZXQgdGhlIEV4cHJlc3MgaW5zdGFuY2UuXHJcbiAqXHJcbiAqIEByZXR1cm5zIHtPYmplY3R9IC0gVGhlIEV4cHJlc3MgaW5zdGFuY2UuXHJcbiAqL1xyXG5leHBvcnQgY29uc3QgZ2V0RXhwcmVzcyA9ICgpID0+IGV4cHJlc3M7XHJcblxyXG4vKipcclxuICogR2V0IHRoZSBFeHByZXNzIGFwcCBpbnN0YW5jZS5cclxuICpcclxuICogQHJldHVybnMge09iamVjdH0gLSBUaGUgRXhwcmVzcyBhcHAgaW5zdGFuY2UuXHJcbiAqL1xyXG5leHBvcnQgY29uc3QgZ2V0QXBwID0gKCkgPT4gYXBwO1xyXG5cclxuLyoqXHJcbiAqIEFwcGx5IG1pZGRsZXdhcmUocykgdG8gYSBzcGVjaWZpYyBwYXRoLlxyXG4gKlxyXG4gKiBAcGFyYW0ge3N0cmluZ30gcGF0aCAtIFRoZSBwYXRoIHRvIHdoaWNoIHRoZSBtaWRkbGV3YXJlKHMpIHNob3VsZCBiZSBhcHBsaWVkLlxyXG4gKiBAcGFyYW0gey4uLkZ1bmN0aW9ufSBtaWRkbGV3YXJlcyAtIFRoZSBtaWRkbGV3YXJlIGZ1bmN0aW9ucyB0byBiZSBhcHBsaWVkLlxyXG4gKi9cclxuZXhwb3J0IGNvbnN0IHVzZSA9IChwYXRoLCAuLi5taWRkbGV3YXJlcykgPT4ge1xyXG4gIGFwcC51c2UocGF0aCwgLi4ubWlkZGxld2FyZXMpO1xyXG59O1xyXG5cclxuLyoqXHJcbiAqIFNldCB1cCBhIHJvdXRlIHdpdGggR0VUIG1ldGhvZCBhbmQgYXBwbHkgbWlkZGxld2FyZShzKS5cclxuICpcclxuICogQHBhcmFtIHtzdHJpbmd9IHBhdGggLSBUaGUgcm91dGUgcGF0aC5cclxuICogQHBhcmFtIHsuLi5GdW5jdGlvbn0gbWlkZGxld2FyZXMgLSBUaGUgbWlkZGxld2FyZSBmdW5jdGlvbnMgdG8gYmUgYXBwbGllZC5cclxuICovXHJcbmV4cG9ydCBjb25zdCBnZXQgPSAocGF0aCwgLi4ubWlkZGxld2FyZXMpID0+IHtcclxuICBhcHAuZ2V0KHBhdGgsIC4uLm1pZGRsZXdhcmVzKTtcclxufTtcclxuXHJcbi8qKlxyXG4gKiBTZXQgdXAgYSByb3V0ZSB3aXRoIFBPU1QgbWV0aG9kIGFuZCBhcHBseSBtaWRkbGV3YXJlKHMpLlxyXG4gKlxyXG4gKiBAcGFyYW0ge3N0cmluZ30gcGF0aCAtIFRoZSByb3V0ZSBwYXRoLlxyXG4gKiBAcGFyYW0gey4uLkZ1bmN0aW9ufSBtaWRkbGV3YXJlcyAtIFRoZSBtaWRkbGV3YXJlIGZ1bmN0aW9ucyB0byBiZSBhcHBsaWVkLlxyXG4gKi9cclxuZXhwb3J0IGNvbnN0IHBvc3QgPSAocGF0aCwgLi4ubWlkZGxld2FyZXMpID0+IHtcclxuICBhcHAucG9zdChwYXRoLCAuLi5taWRkbGV3YXJlcyk7XHJcbn07XHJcblxyXG5leHBvcnQgZGVmYXVsdCB7XHJcbiAgc3RhcnRTZXJ2ZXIsXHJcbiAgY2xvc2VTZXJ2ZXJzLFxyXG4gIGdldFNlcnZlcnMsXHJcbiAgZW5hYmxlUmF0ZUxpbWl0aW5nLFxyXG4gIGdldEV4cHJlc3MsXHJcbiAgZ2V0QXBwLFxyXG4gIHVzZSxcclxuICBnZXQsXHJcbiAgcG9zdFxyXG59O1xyXG4iLCIvKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKlxyXG5cclxuSGlnaGNoYXJ0cyBFeHBvcnQgU2VydmVyXHJcblxyXG5Db3B5cmlnaHQgKGMpIDIwMTYtMjAyNCwgSGlnaHNvZnRcclxuXHJcbkxpY2VuY2VkIHVuZGVyIHRoZSBNSVQgbGljZW5jZS5cclxuXHJcbkFkZGl0aW9uYWxseSBhIHZhbGlkIEhpZ2hjaGFydHMgbGljZW5zZSBpcyByZXF1aXJlZCBmb3IgdXNlLlxyXG5cclxuU2VlIExJQ0VOU0UgZmlsZSBpbiByb290IGZvciBkZXRhaWxzLlxyXG5cclxuKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKi9cclxuXHJcbmltcG9ydCB7IGpvaW4gfSBmcm9tICdwYXRoJztcclxuXHJcbmltcG9ydCB7IF9fZGlybmFtZSB9IGZyb20gJy4uLy4uL3V0aWxzLmpzJztcclxuXHJcbi8qKlxyXG4gKiBBZGRzIHRoZSBHRVQgLyByb3V0ZSBmb3IgYSBVSSB3aGVuIGVuYWJsZWQgb24gdGhlIGV4cG9ydCBzZXJ2ZXIuXHJcbiAqL1xyXG5leHBvcnQgZGVmYXVsdCAoYXBwKSA9PlxyXG4gICFhcHBcclxuICAgID8gZmFsc2VcclxuICAgIDogYXBwLmdldCgnLycsIChyZXF1ZXN0LCByZXNwb25zZSkgPT4ge1xyXG4gICAgICAgIHJlc3BvbnNlLnNlbmRGaWxlKGpvaW4oX19kaXJuYW1lLCAncHVibGljJywgJ2luZGV4Lmh0bWwnKSk7XHJcbiAgICAgIH0pO1xyXG4iLCIvKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKlxyXG5cclxuSGlnaGNoYXJ0cyBFeHBvcnQgU2VydmVyXHJcblxyXG5Db3B5cmlnaHQgKGMpIDIwMTYtMjAyNCwgSGlnaHNvZnRcclxuXHJcbkxpY2VuY2VkIHVuZGVyIHRoZSBNSVQgbGljZW5jZS5cclxuXHJcbkFkZGl0aW9uYWxseSBhIHZhbGlkIEhpZ2hjaGFydHMgbGljZW5zZSBpcyByZXF1aXJlZCBmb3IgdXNlLlxyXG5cclxuU2VlIExJQ0VOU0UgZmlsZSBpbiByb290IGZvciBkZXRhaWxzLlxyXG5cclxuKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKi9cclxuXHJcbmltcG9ydCB7IGNsZWFyQWxsSW50ZXJ2YWxzIH0gZnJvbSAnLi9pbnRlcnZhbHMuanMnO1xyXG5pbXBvcnQgeyBraWxsUG9vbCB9IGZyb20gJy4vcG9vbC5qcyc7XHJcbmltcG9ydCB7IGNsb3NlU2VydmVycyB9IGZyb20gJy4vc2VydmVyL3NlcnZlci5qcyc7XHJcblxyXG4vKipcclxuICogQ2xlYW4gdXAgZnVuY3Rpb24gdG8gdHJpZ2dlciBiZWZvcmUgZW5kaW5nIHByb2Nlc3MgZm9yIHRoZSBncmFjZWZ1bCBzaHV0ZG93bi5cclxuICpcclxuICogQHBhcmFtIHtudW1iZXJ9IGV4aXRDb2RlIC0gQW4gZXhpdCBjb2RlIGZvciB0aGUgcHJvY2Vzcy5leGl0KCkgZnVuY3Rpb24uXHJcbiAqL1xyXG5leHBvcnQgY29uc3Qgc2h1dGRvd25DbGVhblVwID0gYXN5bmMgKGV4aXRDb2RlKSA9PiB7XHJcbiAgLy8gQXdhaXQgZnJlZWluZyBhbGwgcmVzb3VyY2VzXHJcbiAgYXdhaXQgUHJvbWlzZS5hbGxTZXR0bGVkKFtcclxuICAgIC8vIENsZWFyIGFsbCBvbmdvaW5nIGludGVydmFsc1xyXG4gICAgY2xlYXJBbGxJbnRlcnZhbHMoKSxcclxuXHJcbiAgICAvLyBHZXQgYXZhaWxhYmxlIHNlcnZlciBpbnN0YW5jZXMgKEhUVFAvSFRUUFMpIGFuZCBjbG9zZSB0aGVtXHJcbiAgICBjbG9zZVNlcnZlcnMoKSxcclxuXHJcbiAgICAvLyBDbG9zZSBwb29sIGFsb25nIHdpdGggaXRzIHdvcmtlcnMgYW5kIHRoZSBicm93c2VyIGluc3RhbmNlLCBpZiBleGlzdHNcclxuICAgIGtpbGxQb29sKClcclxuICBdKTtcclxuXHJcbiAgLy8gRXhpdCBwcm9jZXNzIHdpdGggYSBjb3JyZWN0IGNvZGVcclxuICBwcm9jZXNzLmV4aXQoZXhpdENvZGUpO1xyXG59O1xyXG5cclxuZXhwb3J0IGRlZmF1bHQge1xyXG4gIHNodXRkb3duQ2xlYW5VcFxyXG59O1xyXG4iLCIvKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKlxyXG5cclxuSGlnaGNoYXJ0cyBFeHBvcnQgU2VydmVyXHJcblxyXG5Db3B5cmlnaHQgKGMpIDIwMTYtMjAyNCwgSGlnaHNvZnRcclxuXHJcbkxpY2VuY2VkIHVuZGVyIHRoZSBNSVQgbGljZW5jZS5cclxuXHJcbkFkZGl0aW9uYWxseSBhIHZhbGlkIEhpZ2hjaGFydHMgbGljZW5zZSBpcyByZXF1aXJlZCBmb3IgdXNlLlxyXG5cclxuU2VlIExJQ0VOU0UgZmlsZSBpbiByb290IGZvciBkZXRhaWxzLlxyXG5cclxuKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKi9cclxuXHJcbmltcG9ydCAnY29sb3JzJztcclxuXHJcbmltcG9ydCB7IGNoZWNrQW5kVXBkYXRlQ2FjaGUgfSBmcm9tICcuL2NhY2hlLmpzJztcclxuaW1wb3J0IHtcclxuICBiYXRjaEV4cG9ydCxcclxuICBzZXRBbGxvd0NvZGVFeGVjdXRpb24sXHJcbiAgc2luZ2xlRXhwb3J0LFxyXG4gIHN0YXJ0RXhwb3J0XHJcbn0gZnJvbSAnLi9jaGFydC5qcyc7XHJcbmltcG9ydCB7IG1hcFRvTmV3Q29uZmlnLCBtYW51YWxDb25maWcsIHNldE9wdGlvbnMgfSBmcm9tICcuL2NvbmZpZy5qcyc7XHJcbmltcG9ydCB7XHJcbiAgaW5pdExvZ2dpbmcsXHJcbiAgbG9nLFxyXG4gIGxvZ1dpdGhTdGFjayxcclxuICBzZXRMb2dMZXZlbCxcclxuICBlbmFibGVGaWxlTG9nZ2luZ1xyXG59IGZyb20gJy4vbG9nZ2VyLmpzJztcclxuaW1wb3J0IHsgaW5pdFBvb2wsIGtpbGxQb29sIH0gZnJvbSAnLi9wb29sLmpzJztcclxuaW1wb3J0IHsgc2h1dGRvd25DbGVhblVwIH0gZnJvbSAnLi9yZXNvdXJjZV9yZWxlYXNlLmpzJztcclxuaW1wb3J0IHNlcnZlciwgeyBzdGFydFNlcnZlciB9IGZyb20gJy4vc2VydmVyL3NlcnZlci5qcyc7XHJcbmltcG9ydCB7IHByaW50TG9nbywgcHJpbnRVc2FnZSB9IGZyb20gJy4vdXRpbHMuanMnO1xyXG5cclxuLyoqXHJcbiAqIEF0dGFjaGVzIGV4aXQgbGlzdGVuZXJzIHRvIHRoZSBwcm9jZXNzLCBlbnN1cmluZyBwcm9wZXIgY2xlYW51cCBvZiByZXNvdXJjZXNcclxuICogYW5kIHRlcm1pbmF0aW9uIG9uIGV4aXQgc2lnbmFscy4gSGFuZGxlcyAnZXhpdCcsICdTSUdJTlQnLCAnU0lHVEVSTScsIGFuZFxyXG4gKiAndW5jYXVnaHRFeGNlcHRpb24nIGV2ZW50cy5cclxuICovXHJcbmNvbnN0IGF0dGFjaFByb2Nlc3NFeGl0TGlzdGVuZXJzID0gKCkgPT4ge1xyXG4gIGxvZygzLCAnW3Byb2Nlc3NdIEF0dGFjaGluZyBleGl0IGxpc3RlbmVycyB0byB0aGUgcHJvY2Vzcy4nKTtcclxuXHJcbiAgLy8gSGFuZGxlciBmb3IgdGhlICdleGl0J1xyXG4gIHByb2Nlc3Mub24oJ2V4aXQnLCAoY29kZSkgPT4ge1xyXG4gICAgbG9nKDQsIGBQcm9jZXNzIGV4aXRlZCB3aXRoIGNvZGUgJHtjb2RlfS5gKTtcclxuICB9KTtcclxuXHJcbiAgLy8gSGFuZGxlciBmb3IgdGhlICdTSUdJTlQnXHJcbiAgcHJvY2Vzcy5vbignU0lHSU5UJywgYXN5bmMgKG5hbWUsIGNvZGUpID0+IHtcclxuICAgIGxvZyg0LCBgVGhlICR7bmFtZX0gZXZlbnQgd2l0aCBjb2RlOiAke2NvZGV9LmApO1xyXG4gICAgYXdhaXQgc2h1dGRvd25DbGVhblVwKDApO1xyXG4gIH0pO1xyXG5cclxuICAvLyBIYW5kbGVyIGZvciB0aGUgJ1NJR1RFUk0nXHJcbiAgcHJvY2Vzcy5vbignU0lHVEVSTScsIGFzeW5jIChuYW1lLCBjb2RlKSA9PiB7XHJcbiAgICBsb2coNCwgYFRoZSAke25hbWV9IGV2ZW50IHdpdGggY29kZTogJHtjb2RlfS5gKTtcclxuICAgIGF3YWl0IHNodXRkb3duQ2xlYW5VcCgwKTtcclxuICB9KTtcclxuXHJcbiAgLy8gSGFuZGxlciBmb3IgdGhlICdTSUdIVVAnXHJcbiAgcHJvY2Vzcy5vbignU0lHSFVQJywgYXN5bmMgKG5hbWUsIGNvZGUpID0+IHtcclxuICAgIGxvZyg0LCBgVGhlICR7bmFtZX0gZXZlbnQgd2l0aCBjb2RlOiAke2NvZGV9LmApO1xyXG4gICAgYXdhaXQgc2h1dGRvd25DbGVhblVwKDApO1xyXG4gIH0pO1xyXG5cclxuICAvLyBIYW5kbGVyIGZvciB0aGUgJ3VuY2F1Z2h0RXhjZXB0aW9uJ1xyXG4gIHByb2Nlc3Mub24oJ3VuY2F1Z2h0RXhjZXB0aW9uJywgYXN5bmMgKGVycm9yLCBuYW1lKSA9PiB7XHJcbiAgICBsb2dXaXRoU3RhY2soMSwgZXJyb3IsIGBUaGUgJHtuYW1lfSBlcnJvci5gKTtcclxuICAgIGF3YWl0IHNodXRkb3duQ2xlYW5VcCgxKTtcclxuICB9KTtcclxufTtcclxuXHJcbi8qKlxyXG4gKiBJbml0aWFsaXplcyB0aGUgZXhwb3J0IHByb2Nlc3MuIFRhc2tzIHN1Y2ggYXMgY29uZmlndXJpbmcgbG9nZ2luZywgY2hlY2tpbmdcclxuICogY2FjaGUgYW5kIHNvdXJjZXMsIGFuZCBpbml0aWFsaXppbmcgdGhlIHBvb2wgb2YgcmVzb3VyY2VzIGhhcHBlbiBkdXJpbmdcclxuICogdGhpcyBzdGFnZS4gRnVuY3Rpb24gdGhhdCBpcyByZXF1aXJlZCB0byBiZSBjYWxsZWQgYmVmb3JlIHRyeWluZyB0byBleHBvcnQgY2hhcnRzIG9yIHNldHRpbmcgYSBzZXJ2ZXIuIFRoZSBgb3B0aW9uc2AgaXMgYW4gb2JqZWN0IHRoYXQgY29udGFpbnMgYWxsIG9wdGlvbnMuXHJcbiAqXHJcbiAqIEBwYXJhbSB7T2JqZWN0fSBvcHRpb25zIC0gQWxsIGV4cG9ydCBvcHRpb25zLlxyXG4gKlxyXG4gKiBAcmV0dXJucyB7UHJvbWlzZTxPYmplY3Q+fSBQcm9taXNlIHJlc29sdmluZyB0byB0aGUgdXBkYXRlZCBleHBvcnQgb3B0aW9ucy5cclxuICovXHJcbmNvbnN0IGluaXRFeHBvcnQgPSBhc3luYyAob3B0aW9ucykgPT4ge1xyXG4gIC8vIFNldCB0aGUgYWxsb3dDb2RlRXhlY3V0aW9uIHBlciBleHBvcnQgbW9kdWxlIHNjb3BlXHJcbiAgc2V0QWxsb3dDb2RlRXhlY3V0aW9uKFxyXG4gICAgb3B0aW9ucy5jdXN0b21Mb2dpYyAmJiBvcHRpb25zLmN1c3RvbUxvZ2ljLmFsbG93Q29kZUV4ZWN1dGlvblxyXG4gICk7XHJcblxyXG4gIC8vIEluaXQgdGhlIGxvZ2dpbmdcclxuICBpbml0TG9nZ2luZyhvcHRpb25zLmxvZ2dpbmcpO1xyXG5cclxuICAvLyBBdHRhY2ggcHJvY2VzcycgZXhpdCBsaXN0ZW5lcnNcclxuICBpZiAob3B0aW9ucy5vdGhlci5saXN0ZW5Ub1Byb2Nlc3NFeGl0cykge1xyXG4gICAgYXR0YWNoUHJvY2Vzc0V4aXRMaXN0ZW5lcnMoKTtcclxuICB9XHJcblxyXG4gIC8vIENoZWNrIGlmIGNhY2hlIG5lZWRzIHRvIGJlIHVwZGF0ZWRcclxuICBhd2FpdCBjaGVja0FuZFVwZGF0ZUNhY2hlKG9wdGlvbnMpO1xyXG5cclxuICAvLyBJbml0IHRoZSBwb29sXHJcbiAgYXdhaXQgaW5pdFBvb2woe1xyXG4gICAgcG9vbDogb3B0aW9ucy5wb29sIHx8IHtcclxuICAgICAgbWluV29ya2VyczogMSxcclxuICAgICAgbWF4V29ya2VyczogMVxyXG4gICAgfSxcclxuICAgIHB1cHBldGVlckFyZ3M6IG9wdGlvbnMucHVwcGV0ZWVyLmFyZ3MgfHwgW11cclxuICB9KTtcclxuXHJcbiAgLy8gUmV0dXJuIHVwZGF0ZWQgb3B0aW9uc1xyXG4gIHJldHVybiBvcHRpb25zO1xyXG59O1xyXG5cclxuZXhwb3J0IGRlZmF1bHQge1xyXG4gIC8vIFNlcnZlclxyXG4gIHNlcnZlcixcclxuICBzdGFydFNlcnZlcixcclxuXHJcbiAgLy8gRXhwb3J0aW5nXHJcbiAgaW5pdEV4cG9ydCxcclxuICBzaW5nbGVFeHBvcnQsXHJcbiAgYmF0Y2hFeHBvcnQsXHJcbiAgc3RhcnRFeHBvcnQsXHJcblxyXG4gIC8vIFBvb2xcclxuICBpbml0UG9vbCxcclxuICBraWxsUG9vbCxcclxuXHJcbiAgLy8gT3RoZXJcclxuICBzZXRPcHRpb25zLFxyXG4gIHNodXRkb3duQ2xlYW5VcCxcclxuXHJcbiAgLy8gTG9nc1xyXG4gIGxvZyxcclxuICBsb2dXaXRoU3RhY2ssXHJcbiAgc2V0TG9nTGV2ZWwsXHJcbiAgZW5hYmxlRmlsZUxvZ2dpbmcsXHJcblxyXG4gIC8vIFV0aWxzXHJcbiAgbWFwVG9OZXdDb25maWcsXHJcbiAgbWFudWFsQ29uZmlnLFxyXG4gIHByaW50TG9nbyxcclxuICBwcmludFVzYWdlXHJcbn07XHJcbiJdLCJuYW1lcyI6WyJzY3JpcHRzTmFtZXMiLCJjb3JlIiwibW9kdWxlcyIsImluZGljYXRvcnMiLCJkZWZhdWx0Q29uZmlnIiwicHVwcGV0ZWVyIiwiYXJncyIsInZhbHVlIiwidHlwZSIsImRlc2NyaXB0aW9uIiwiaGlnaGNoYXJ0cyIsInZlcnNpb24iLCJlbnZMaW5rIiwiY2RuVVJMIiwiY29yZVNjcmlwdHMiLCJtb2R1bGVTY3JpcHRzIiwiaW5kaWNhdG9yU2NyaXB0cyIsImN1c3RvbVNjcmlwdHMiLCJmb3JjZUZldGNoIiwiY2FjaGVQYXRoIiwiZXhwb3J0IiwiaW5maWxlIiwiaW5zdHIiLCJvcHRpb25zIiwib3V0ZmlsZSIsImNvbnN0ciIsImRlZmF1bHRIZWlnaHQiLCJkZWZhdWx0V2lkdGgiLCJkZWZhdWx0U2NhbGUiLCJoZWlnaHQiLCJ3aWR0aCIsInNjYWxlIiwiZ2xvYmFsT3B0aW9ucyIsInRoZW1lT3B0aW9ucyIsImJhdGNoIiwicmFzdGVyaXphdGlvblRpbWVvdXQiLCJjdXN0b21Mb2dpYyIsImFsbG93Q29kZUV4ZWN1dGlvbiIsImFsbG93RmlsZVJlc291cmNlcyIsImN1c3RvbUNvZGUiLCJjYWxsYmFjayIsInJlc291cmNlcyIsImxvYWRDb25maWciLCJsZWdhY3lOYW1lIiwiY3JlYXRlQ29uZmlnIiwic2VydmVyIiwiZW5hYmxlIiwiY2xpTmFtZSIsImhvc3QiLCJwb3J0IiwiYmVuY2htYXJraW5nIiwicHJveHkiLCJ0aW1lb3V0IiwicmF0ZUxpbWl0aW5nIiwibWF4UmVxdWVzdHMiLCJ3aW5kb3ciLCJkZWxheSIsInRydXN0UHJveHkiLCJza2lwS2V5Iiwic2tpcFRva2VuIiwic3NsIiwiZm9yY2UiLCJjZXJ0UGF0aCIsInBvb2wiLCJtaW5Xb3JrZXJzIiwibWF4V29ya2VycyIsIndvcmtMaW1pdCIsImFjcXVpcmVUaW1lb3V0IiwiY3JlYXRlVGltZW91dCIsImRlc3Ryb3lUaW1lb3V0IiwiaWRsZVRpbWVvdXQiLCJjcmVhdGVSZXRyeUludGVydmFsIiwicmVhcGVySW50ZXJ2YWwiLCJsb2dnaW5nIiwibGV2ZWwiLCJmaWxlIiwiZGVzdCIsInVpIiwicm91dGUiLCJvdGhlciIsIm5vZGVFbnYiLCJsaXN0ZW5Ub1Byb2Nlc3NFeGl0cyIsIm5vTG9nbyIsImhhcmRSZXNldFBhZ2UiLCJkZWJ1ZyIsImhlYWRsZXNzIiwiZGV2dG9vbHMiLCJsaXN0ZW5Ub0NvbnNvbGUiLCJkdW1waW8iLCJzbG93TW8iLCJkZWJ1Z2dpbmdQb3J0IiwicHJvbXB0c0NvbmZpZyIsIm5hbWUiLCJtZXNzYWdlIiwiaW5pdGlhbCIsImpvaW4iLCJzZXBhcmF0b3IiLCJpbnN0cnVjdGlvbnMiLCJjaG9pY2VzIiwiaGludCIsIm1pbiIsIm1heCIsInJvdW5kIiwiYWJzb2x1dGVQcm9wcyIsIm5lc3RlZEFyZ3MiLCJjcmVhdGVOZXN0ZWRBcmdzIiwib2JqIiwicHJvcENoYWluIiwiT2JqZWN0Iiwia2V5cyIsImZvckVhY2giLCJrIiwiaW5jbHVkZXMiLCJlbnRyeSIsInN1YnN0cmluZyIsInVuZGVmaW5lZCIsImRvdGVudiIsImNvbmZpZyIsInYiLCJmaWx0ZXJBcnJheSIsInoiLCJzdHJpbmciLCJ0cmFuc2Zvcm0iLCJzcGxpdCIsIm1hcCIsInRyaW0iLCJmaWx0ZXIiLCJsZW5ndGgiLCJlbnVtIiwidmFsdWVzIiwicmVmaW5lIiwiaXNOYU4iLCJwYXJzZUZsb2F0IiwiZW52cyIsIm9iamVjdCIsIkhJR0hDSEFSVFNfVkVSU0lPTiIsInRlc3QiLCJISUdIQ0hBUlRTX0NETl9VUkwiLCJzdGFydHNXaXRoIiwiSElHSENIQVJUU19DT1JFX1NDUklQVFMiLCJISUdIQ0hBUlRTX01PRFVMRV9TQ1JJUFRTIiwiSElHSENIQVJUU19JTkRJQ0FUT1JfU0NSSVBUUyIsIkhJR0hDSEFSVFNfRk9SQ0VfRkVUQ0giLCJISUdIQ0hBUlRTX0NBQ0hFX1BBVEgiLCJISUdIQ0hBUlRTX0FETUlOX1RPS0VOIiwiRVhQT1JUX1RZUEUiLCJFWFBPUlRfQ09OU1RSIiwiRVhQT1JUX0RFRkFVTFRfSEVJR0hUIiwiRVhQT1JUX0RFRkFVTFRfV0lEVEgiLCJFWFBPUlRfREVGQVVMVF9TQ0FMRSIsIkVYUE9SVF9SQVNURVJJWkFUSU9OX1RJTUVPVVQiLCJDVVNUT01fTE9HSUNfQUxMT1dfQ09ERV9FWEVDVVRJT04iLCJDVVNUT01fTE9HSUNfQUxMT1dfRklMRV9SRVNPVVJDRVMiLCJTRVJWRVJfRU5BQkxFIiwiU0VSVkVSX0hPU1QiLCJTRVJWRVJfUE9SVCIsIlNFUlZFUl9CRU5DSE1BUktJTkciLCJTRVJWRVJfUFJPWFlfSE9TVCIsIlNFUlZFUl9QUk9YWV9QT1JUIiwiU0VSVkVSX1BST1hZX1RJTUVPVVQiLCJTRVJWRVJfUkFURV9MSU1JVElOR19FTkFCTEUiLCJTRVJWRVJfUkFURV9MSU1JVElOR19NQVhfUkVRVUVTVFMiLCJTRVJWRVJfUkFURV9MSU1JVElOR19XSU5ET1ciLCJTRVJWRVJfUkFURV9MSU1JVElOR19ERUxBWSIsIlNFUlZFUl9SQVRFX0xJTUlUSU5HX1RSVVNUX1BST1hZIiwiU0VSVkVSX1JBVEVfTElNSVRJTkdfU0tJUF9LRVkiLCJTRVJWRVJfUkFURV9MSU1JVElOR19TS0lQX1RPS0VOIiwiU0VSVkVSX1NTTF9FTkFCTEUiLCJTRVJWRVJfU1NMX0ZPUkNFIiwiU0VSVkVSX1NTTF9QT1JUIiwiU0VSVkVSX1NTTF9DRVJUX1BBVEgiLCJQT09MX01JTl9XT1JLRVJTIiwiUE9PTF9NQVhfV09SS0VSUyIsIlBPT0xfV09SS19MSU1JVCIsIlBPT0xfQUNRVUlSRV9USU1FT1VUIiwiUE9PTF9DUkVBVEVfVElNRU9VVCIsIlBPT0xfREVTVFJPWV9USU1FT1VUIiwiUE9PTF9JRExFX1RJTUVPVVQiLCJQT09MX0NSRUFURV9SRVRSWV9JTlRFUlZBTCIsIlBPT0xfUkVBUEVSX0lOVEVSVkFMIiwiUE9PTF9CRU5DSE1BUktJTkciLCJMT0dHSU5HX0xFVkVMIiwiTE9HR0lOR19GSUxFIiwiTE9HR0lOR19ERVNUIiwiVUlfRU5BQkxFIiwiVUlfUk9VVEUiLCJPVEhFUl9OT0RFX0VOViIsIk9USEVSX0xJU1RFTl9UT19QUk9DRVNTX0VYSVRTIiwiT1RIRVJfTk9fTE9HTyIsIk9USEVSX0hBUkRfUkVTRVRfUEFHRSIsIkRFQlVHX0VOQUJMRSIsIkRFQlVHX0hFQURMRVNTIiwiREVCVUdfREVWVE9PTFMiLCJERUJVR19MSVNURU5fVE9fQ09OU09MRSIsIkRFQlVHX0RVTVBJTyIsIkRFQlVHX1NMT1dfTU8iLCJERUJVR19ERUJVR0dJTkdfUE9SVCIsInBhcnRpYWwiLCJwYXJzZSIsInByb2Nlc3MiLCJlbnYiLCJjb2xvcnMiLCJ0b0NvbnNvbGUiLCJ0b0ZpbGUiLCJwYXRoQ3JlYXRlZCIsImxldmVsc0Rlc2MiLCJ0aXRsZSIsImNvbG9yIiwibGlzdGVuZXJzIiwia2V5Iiwib3B0aW9uIiwiZW50cmllcyIsImxvZ1RvRmlsZSIsInRleHRzIiwicHJlZml4IiwiZXhpc3RzU3luYyIsIm1rZGlyU3luYyIsImFwcGVuZEZpbGUiLCJjb25jYXQiLCJlcnJvciIsImNvbnNvbGUiLCJsb2ciLCJuZXdMZXZlbCIsIkRhdGUiLCJ0b1N0cmluZyIsImZuIiwiYXBwbHkiLCJsb2dXaXRoU3RhY2siLCJjdXN0b21NZXNzYWdlIiwibWFpbk1lc3NhZ2UiLCJzdGFja01lc3NhZ2UiLCJzdGFjayIsInNsaWNlIiwic2V0TG9nTGV2ZWwiLCJlbmFibGVGaWxlTG9nZ2luZyIsImxvZ0Rlc3QiLCJsb2dGaWxlIiwiZW5kc1dpdGgiLCJfX2Rpcm5hbWUiLCJmaWxlVVJMVG9QYXRoIiwiVVJMIiwiZG9jdW1lbnQiLCJyZXF1aXJlIiwicGF0aFRvRmlsZVVSTCIsIl9fZmlsZW5hbWUiLCJocmVmIiwiX2RvY3VtZW50Q3VycmVudFNjcmlwdCIsInNyYyIsImJhc2VVUkkiLCJmaXhUeXBlIiwiZm9ybWF0cyIsIm91dFR5cGUiLCJwb3AiLCJmaW5kIiwidCIsImhhbmRsZVJlc291cmNlcyIsImFsbG93ZWRQcm9wcyIsImhhbmRsZWRSZXNvdXJjZXMiLCJjb3JyZWN0UmVzb3VyY2VzIiwiaXNDb3JyZWN0SlNPTiIsInJlYWRGaWxlU3luYyIsImZpbGVzIiwicHJvcE5hbWUiLCJpdGVtIiwiZGF0YSIsInBhcnNlZERhdGEiLCJKU09OIiwic3RyaW5naWZ5IiwiZGVlcENvcHkiLCJjb3B5IiwiQXJyYXkiLCJpc0FycmF5IiwicHJvdG90eXBlIiwiaGFzT3duUHJvcGVydHkiLCJjYWxsIiwib3B0aW9uc1N0cmluZ2lmeSIsImFsbG93RnVuY3Rpb25zIiwicmVwbGFjZUFsbCIsInByaW50VXNhZ2UiLCJib2xkIiwieWVsbG93IiwiY3ljbGVDYXRlZ29yaWVzIiwiZGVzY05hbWUiLCJncmVlbiIsImkiLCJibHVlIiwiY2F0ZWdvcnkiLCJ0b1VwcGVyQ2FzZSIsInJlZCIsInRvQm9vbGVhbiIsIndyYXBBcm91bmQiLCJyZXBsYWNlIiwibWVhc3VyZVRpbWUiLCJzdGFydCIsImhydGltZSIsImJpZ2ludCIsIk51bWJlciIsImdlbmVyYWxPcHRpb25zIiwiZ2V0T3B0aW9ucyIsIm1lcmdlQ29uZmlnT3B0aW9ucyIsIm5ld09wdGlvbnMiLCJtZXJnZWRPcHRpb25zIiwidXBkYXRlRGVmYXVsdENvbmZpZyIsImNvbmZpZ09iaiIsImN1c3RvbU9iaiIsImN1c3RvbVZhbHVlIiwiaW5pdE9wdGlvbnMiLCJpdGVtcyIsInJlY3Vyc2l2ZVByb3BzIiwib2JqZWN0VG9VcGRhdGUiLCJuZXN0ZWROYW1lcyIsInNoaWZ0IiwiYXNzaWduIiwiYXN5bmMiLCJmZXRjaCIsInVybCIsInJlcXVlc3RPcHRpb25zIiwiUHJvbWlzZSIsInJlc29sdmUiLCJyZWplY3QiLCJwcm90b2NvbCIsImh0dHBzIiwiaHR0cCIsImdldFByb3RvY29sIiwiZ2V0IiwicmVzIiwib24iLCJjaHVuayIsInRleHQiLCJFeHBvcnRFcnJvciIsIkVycm9yIiwiY29uc3RydWN0b3IiLCJzdXBlciIsInRoaXMiLCJzZXRFcnJvciIsInN0YXR1c0NvZGUiLCJjYWNoZSIsImFjdGl2ZU1hbmlmZXN0Iiwic291cmNlcyIsImhjVmVyc2lvbiIsImV4dHJhY3RWZXJzaW9uIiwiaW5kZXhPZiIsImZldGNoQW5kUHJvY2Vzc1NjcmlwdCIsInNjcmlwdCIsImZldGNoZWRNb2R1bGVzIiwic2hvdWxkVGhyb3dFcnJvciIsInJlc3BvbnNlIiwidXBkYXRlQ2FjaGUiLCJoaWdoY2hhcnRzT3B0aW9ucyIsInByb3h5T3B0aW9ucyIsInNvdXJjZVBhdGgiLCJwcm94eUFnZW50IiwicHJveHlIb3N0IiwicHJveHlQb3J0IiwiSHR0cHNQcm94eUFnZW50IiwiYWdlbnQiLCJhbGxGZXRjaFByb21pc2VzIiwiYWxsIiwiZmV0Y2hTY3JpcHRzIiwiYyIsIm0iLCJ3cml0ZUZpbGVTeW5jIiwiY2hlY2tBbmRVcGRhdGVDYWNoZSIsIm1hbmlmZXN0UGF0aCIsInJlcXVlc3RVcGRhdGUiLCJtYW5pZmVzdCIsIm1vZHVsZU1hcCIsIm51bWJlck9mTW9kdWxlcyIsInNvbWUiLCJtb2R1bGVOYW1lIiwibmV3TWFuaWZlc3QiLCJzYXZlQ29uZmlnVG9NYW5pZmVzdCIsImdldENhY2hlUGF0aCIsInNldHVwSGlnaGNoYXJ0cyIsIkhpZ2hjaGFydHMiLCJhbmltT2JqZWN0IiwiZHVyYXRpb24iLCJ0cmlnZ2VyRXhwb3J0IiwiY2hhcnRPcHRpb25zIiwiZGlzcGxheUVycm9ycyIsIl9kaXNwbGF5RXJyb3JzIiwibWVyZ2UiLCJzZXRPcHRpb25zIiwid3JhcCIsInNldE9wdGlvbnNPYmoiLCJGdW5jdGlvbiIsImNoYXJ0IiwiYW5pbWF0aW9uIiwic3RySW5qIiwiaXNSZW5kZXJDb21wbGV0ZSIsIkNoYXJ0IiwicHJvY2VlZCIsInVzZXJPcHRpb25zIiwiY2IiLCJleHBvcnRpbmciLCJlbmFibGVkIiwicGxvdE9wdGlvbnMiLCJzZXJpZXMiLCJsYWJlbCIsInRvb2x0aXAiLCJvbkhpZ2hjaGFydHNSZW5kZXIiLCJhZGRFdmVudCIsIlNlcmllcyIsImZpbmFsT3B0aW9ucyIsImZpbmFsQ2FsbGJhY2siLCJkZWZhdWx0T3B0aW9ucyIsInByb3AiLCJ0ZW1wbGF0ZSIsImJyb3dzZXIiLCJuZXdQYWdlIiwicGFnZSIsInNldENhY2hlRW5hYmxlZCIsInNldFBhZ2VDb250ZW50IiwiJGV2YWwiLCJlbGVtZW50IiwiZXJyb3JNZXNzYWdlIiwiaW5uZXJIVE1MIiwic2V0UGFnZUV2ZW50cyIsImNsZWFyUGFnZVJlc291cmNlcyIsImluamVjdGVkUmVzb3VyY2VzIiwicmVzb3VyY2UiLCJkaXNwb3NlIiwiZXZhbHVhdGUiLCJvbGRDaGFydHMiLCJjaGFydHMiLCJvbGRDaGFydCIsImRlc3Ryb3kiLCJzY3JpcHRzVG9SZW1vdmUiLCJnZXRFbGVtZW50c0J5VGFnTmFtZSIsInN0eWxlc1RvUmVtb3ZlIiwibGlua3NUb1JlbW92ZSIsInJlbW92ZSIsInNldENvbnRlbnQiLCJ3YWl0VW50aWwiLCJhZGRTY3JpcHRUYWciLCJwYXRoIiwic2V0QXNDb25maWciLCJwdXBwZXRlZXJFeHBvcnQiLCJleHBvcnRPcHRpb25zIiwiZGVidWdnZXIiLCJpc1NWRyIsInN2Z1RlbXBsYXRlIiwiaW5qZWN0ZWRKcyIsImpzIiwicHVzaCIsImNvbnRlbnQiLCJpc0xvY2FsIiwianNSZXNvdXJjZSIsImluamVjdGVkQ3NzIiwiY3NzIiwiY3NzSW1wb3J0cyIsIm1hdGNoIiwiY3NzSW1wb3J0UGF0aCIsImNzc1Jlc291cmNlIiwiYWRkU3R5bGVUYWciLCJhZGRQYWdlUmVzb3VyY2VzIiwic2l6ZSIsInN2Z0VsZW1lbnQiLCJxdWVyeVNlbGVjdG9yIiwiY2hhcnRIZWlnaHQiLCJiYXNlVmFsIiwiY2hhcnRXaWR0aCIsImJvZHkiLCJzdHlsZSIsInpvb20iLCJtYXJnaW4iLCJ2aWV3cG9ydEhlaWdodCIsIk1hdGgiLCJjZWlsIiwidmlld3BvcnRXaWR0aCIsIngiLCJ5IiwiZ2V0Qm91bmRpbmdDbGllbnRSZWN0IiwidHJ1bmMiLCJnZXRDbGlwUmVnaW9uIiwic2V0Vmlld3BvcnQiLCJkZXZpY2VTY2FsZUZhY3RvciIsIm91dGVySFRNTCIsImNyZWF0ZVNWRyIsImVuY29kaW5nIiwiY2xpcCIsInJhY2UiLCJzY3JlZW5zaG90IiwiY2FwdHVyZUJleW9uZFZpZXdwb3J0IiwiZnVsbFBhZ2UiLCJvcHRpbWl6ZUZvclNwZWVkIiwicXVhbGl0eSIsIm9taXRCYWNrZ3JvdW5kIiwiX3Jlc29sdmUiLCJzZXRUaW1lb3V0IiwiY3JlYXRlSW1hZ2UiLCJlbXVsYXRlTWVkaWFUeXBlIiwicGRmIiwiY3JlYXRlUERGIiwic3RhdHMiLCJwZXJmb3JtZWRFeHBvcnRzIiwiZXhwb3J0QXR0ZW1wdHMiLCJleHBvcnRGcm9tU3ZnQXR0ZW1wdHMiLCJ0aW1lU3BlbnQiLCJkcm9wcGVkRXhwb3J0cyIsInNwZW50QXZlcmFnZSIsInBvb2xDb25maWciLCJmYWN0b3J5IiwiY3JlYXRlIiwiaWQiLCJ1dWlkIiwic3RhcnREYXRlIiwiZ2V0VGltZSIsImlzQ2xvc2VkIiwid29ya0NvdW50IiwicmFuZG9tIiwidmFsaWRhdGUiLCJ3b3JrZXJIYW5kbGUiLCJjbG9zZSIsImluaXRQb29sIiwicHVwcGV0ZWVyQXJncyIsImVuYWJsZWREZWJ1ZyIsImxhdW5jaE9wdGlvbnMiLCJ1c2VyRGF0YURpciIsImhhbmRsZVNJR0lOVCIsImhhbmRsZVNJR1RFUk0iLCJoYW5kbGVTSUdIVVAiLCJ3YWl0Rm9ySW5pdGlhbFBhZ2UiLCJkZWZhdWx0Vmlld3BvcnQiLCJ0cnlDb3VudCIsIm9wZW4iLCJsYXVuY2giLCJjcmVhdGVCcm93c2VyIiwicGFyc2VJbnQiLCJQb29sIiwiYWNxdWlyZVRpbWVvdXRNaWxsaXMiLCJjcmVhdGVUaW1lb3V0TWlsbGlzIiwiZGVzdHJveVRpbWVvdXRNaWxsaXMiLCJpZGxlVGltZW91dE1pbGxpcyIsImNyZWF0ZVJldHJ5SW50ZXJ2YWxNaWxsaXMiLCJyZWFwSW50ZXJ2YWxNaWxsaXMiLCJwcm9wYWdhdGVDcmVhdGVFcnJvciIsImhhcmRSZXNldCIsImdvdG8iLCJjbGVhclBhZ2UiLCJldmVudElkIiwiaW5pdGlhbFJlc291cmNlcyIsImFjcXVpcmUiLCJwcm9taXNlIiwicmVsZWFzZSIsImtpbGxQb29sIiwid29ya2VyIiwidXNlZCIsImRlc3Ryb3llZCIsImNvbm5lY3RlZCIsImNsb3NlQnJvd3NlciIsInBvc3RXb3JrIiwiZ2V0UG9vbEluZm8iLCJhY3F1aXJlQ291bnRlciIsInBheWxvYWQiLCJyZXF1ZXN0SWQiLCJ3b3JrU3RhcnQiLCJleHBvcnRDb3VudGVyIiwicmVzdWx0IiwiZXhwb3J0VGltZSIsImdldFBvb2xJbmZvSlNPTiIsIm51bUZyZWUiLCJudW1Vc2VkIiwiYXZhaWxhYmxlIiwicGVuZGluZyIsIm51bVBlbmRpbmdBY3F1aXJlcyIsInBvb2wkMSIsInN0YXJ0RXhwb3J0Iiwic2V0dGluZ3MiLCJlbmRDYWxsYmFjayIsInN2ZyIsImluaXRFeHBvcnRTZXR0aW5ncyIsImV4cG9ydEFzU3RyaW5nIiwiaW5wdXQiLCJKU0RPTSIsIkRPTVB1cmlmeSIsInNhbml0aXplIiwiQUREX1RBR1MiLCJkb1N0cmFpZ2h0SW5qZWN0IiwiZG9FeHBvcnQiLCJmaW5kQ2hhcnRTaXplIiwicHJlY2lzaW9uIiwibXVsdGlwbGllciIsInBvdyIsInJvdW5kTnVtYmVyIiwic291cmNlSGVpZ2h0Iiwic291cmNlV2lkdGgiLCJwYXJhbSIsImNoYXJ0SnNvbiIsImN1c3RvbUxvZ2ljT3B0aW9ucyIsImFsbG93Q29kZUV4ZWN1dGlvblNjb3BlZCIsIm9wdGlvbnNOYW1lIiwic3RyaW5nVG9FeHBvcnQiLCJjaGFydEpTT04iLCJpbnRlcnZhbElkcyIsImNsZWFyQWxsSW50ZXJ2YWxzIiwiY2xlYXJJbnRlcnZhbCIsImxvZ0Vycm9yTWlkZGxld2FyZSIsInJlcSIsIm5leHQiLCJyZXR1cm5FcnJvck1pZGRsZXdhcmUiLCJzdENvZGUiLCJzdGF0dXMiLCJqc29uIiwicmF0ZUxpbWl0IiwiYXBwIiwibGltaXRDb25maWciLCJtc2ciLCJyYXRlT3B0aW9ucyIsImxpbWl0ZXIiLCJ3aW5kb3dNcyIsImRlbGF5TXMiLCJoYW5kbGVyIiwicmVxdWVzdCIsImZvcm1hdCIsInNlbmQiLCJkZWZhdWx0Iiwic2tpcCIsInF1ZXJ5IiwiYWNjZXNzX3Rva2VuIiwidXNlIiwiSHR0cEVycm9yIiwic2V0U3RhdHVzIiwidlN3aXRjaFJvdXRlIiwicG9zdCIsImFkbWluVG9rZW4iLCJ0b2tlbiIsIm5ld1ZlcnNpb24iLCJwYXJhbXMiLCJ1cGRhdGVWZXJzaW9uIiwicmV2ZXJzZWRNaW1lIiwicG5nIiwianBlZyIsImdpZiIsInJlcXVlc3RzQ291bnRlciIsImJlZm9yZVJlcXVlc3QiLCJhZnRlclJlcXVlc3QiLCJkb0NhbGxiYWNrcyIsImNhbGxiYWNrcyIsInVuaXF1ZUlkIiwiY2FsbFJlc3BvbnNlIiwiZXhwb3J0SGFuZGxlciIsInN0b3BDb3VudGVyIiwiaGVhZGVycyIsImNvbm5lY3Rpb24iLCJyZW1vdGVBZGRyZXNzIiwiY29ubmVjdGlvbkFib3J0ZWQiLCJzb2NrZXQiLCJ0b0xvd2VyQ2FzZSIsInN1YnN0ciIsImI2NCIsIm5vRG93bmxvYWQiLCJwYXR0ZXJuIiwiaXNQcml2YXRlUmFuZ2VVcmxGb3VuZCIsImluZm8iLCJyZW1vdmVBbGxMaXN0ZW5lcnMiLCJCdWZmZXIiLCJmcm9tIiwiaGVhZGVyIiwiYXR0YWNobWVudCIsImZpbGVuYW1lIiwicGtnRmlsZSIsInBhdGhlciIsInNlcnZlclN0YXJ0VGltZSIsInN1Y2Nlc3NSYXRlcyIsImFkZEhlYWx0aFJvdXRlcyIsInNldEludGVydmFsIiwic3VjY2Vzc1JhdGlvIiwiXyIsInBlcmlvZCIsIm1vdmluZ0F2ZXJhZ2UiLCJyZWR1Y2UiLCJhIiwiYiIsImJvb3RUaW1lIiwidXB0aW1lIiwiZmxvb3IiLCJoaWdoY2hhcnRzVmVyc2lvbiIsImF2ZXJhZ2VQcm9jZXNzaW5nVGltZSIsImZhaWxlZEV4cG9ydHMiLCJzdWNlc3NSYXRpbyIsInRvRml4ZWQiLCJzdmdFeHBvcnRBdHRlbXB0cyIsImpzb25FeHBvcnRBdHRlbXB0cyIsImFjdGl2ZVNlcnZlcnMiLCJNYXAiLCJleHByZXNzIiwiZGlzYWJsZSIsImNvcnMiLCJzdG9yYWdlIiwibXVsdGVyIiwibWVtb3J5U3RvcmFnZSIsInVwbG9hZCIsImxpbWl0cyIsImZpZWxkU2l6ZSIsImxpbWl0IiwidXJsZW5jb2RlZCIsImV4dGVuZGVkIiwibm9uZSIsImF0dGFjaFNlcnZlckVycm9ySGFuZGxlcnMiLCJzdGFydFNlcnZlciIsInNlcnZlckNvbmZpZyIsImh0dHBTZXJ2ZXIiLCJjcmVhdGVTZXJ2ZXIiLCJsaXN0ZW4iLCJzZXQiLCJjZXJ0IiwiZnNQcm9taXNlcyIsInJlYWRGaWxlIiwicG9zaXgiLCJodHRwc1NlcnZlciIsIk5hTiIsInN0YXRpYyIsImhlYWx0aFJvdXRlIiwiZXhwb3J0Um91dGVzIiwic2VuZEZpbGUiLCJ1aVJvdXRlIiwiZXJyb3JIYW5kbGVyIiwiY2xvc2VTZXJ2ZXJzIiwiZGVsZXRlIiwiZ2V0U2VydmVycyIsImVuYWJsZVJhdGVMaW1pdGluZyIsImdldEV4cHJlc3MiLCJnZXRBcHAiLCJtaWRkbGV3YXJlcyIsInNodXRkb3duQ2xlYW5VcCIsImV4aXRDb2RlIiwiYWxsU2V0dGxlZCIsImV4aXQiLCJpbmRleCIsImluaXRFeHBvcnQiLCJpbml0TG9nZ2luZyIsImNvZGUiLCJzaW5nbGVFeHBvcnQiLCJiYXRjaEV4cG9ydCIsImJhdGNoRnVuY3Rpb25zIiwicGFpciIsImNvbmZpZ0luZGV4IiwiZmluZEluZGV4IiwiYXJnIiwiZmlsZU5hbWUiLCJsb2FkQ29uZmlnRmlsZSIsInNob3dVc2FnZSIsInByb3BlcnRpZXNDaGFpbiIsImFyZ3VtZW50VHlwZSIsInBhaXJBcmd1bWVudFZhbHVlIiwibWFwVG9OZXdDb25maWciLCJvbGRPcHRpb25zIiwibWFudWFsQ29uZmlnIiwiY29uZmlnRmlsZU5hbWUiLCJjb25maWdGaWxlIiwiY2hvaWNlIiwicHJvbXB0cyIsIm9uU3VibWl0IiwicCIsImNhdGVnb3JpZXMiLCJxdWVzdGlvbnNDb3VudGVyIiwiYWxsUXVlc3Rpb25zIiwic2VjdGlvbiIsInByb21wdCIsImFuc3dlciIsIm1vZHVsZSIsInByb21pc2VzIiwid3JpdGVGaWxlIiwicHJpbnRMb2dvIiwicGFja2FnZVZlcnNpb24iXSwibWFwcGluZ3MiOiIrY0FlTyxNQUFNQSxFQUFlLENBQzFCQyxLQUFNLENBQUMsYUFBYyxrQkFBbUIsaUJBQ3hDQyxRQUFTLENBQ1AsUUFDQSxNQUNBLFFBQ0EsWUFDQSxjQUNBLHVCQUNBLGdCQUVBLGVBQ0EsUUFDQSxPQUNBLGFBQ0EsbUJBQ0EsZUFDQSxjQUNB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 diff --git a/dist/index.esm.js b/dist/index.esm.js index 2a83a136..c0557975 100644 --- a/dist/index.esm.js +++ b/dist/index.esm.js @@ -1,2 +1,2 @@ -import"colors";import e,{existsSync as t,mkdirSync as r,appendFile as o,readFileSync as i,promises as s,writeFileSync as n}from"fs";import a,{join as l,posix as c}from"path";import{HttpsProxyAgent as p}from"https-proxy-agent";import h from"prompts";import u from"dotenv";import{z as d}from"zod";import*as g from"url";import{fileURLToPath as m}from"url";import f from"http";import v from"https";import{Pool as y}from"tarn";import{v4 as b}from"uuid";import w from"puppeteer";import{JSDOM as E}from"jsdom";import T from"dompurify";import S from"cors";import x from"express";import R from"multer";import L from"express-rate-limit";const O={core:["highcharts","highcharts-more","highcharts-3d"],modules:["stock","map","gantt","exporting","export-data","parallel-coordinates","accessibility","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","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"],indicators:["indicators-all"]},_={puppeteer:{args:{value:["--allow-running-insecure-content","--ash-no-nudges","--autoplay-policy=user-gesture-required","--block-new-web-contents","--disable-accelerated-2d-canvas","--disable-background-networking","--disable-background-timer-throttling","--disable-backgrounding-occluded-windows","--disable-breakpad","--disable-checker-imaging","--disable-client-side-phishing-detection","--disable-component-extensions-with-background-pages","--disable-component-update","--disable-default-apps","--disable-dev-shm-usage","--disable-domain-reliability","--disable-extensions","--disable-features=CalculateNativeWinOcclusion,InterestFeedContentSuggestions,WebOTP","--disable-hang-monitor","--disable-ipc-flooding-protection","--disable-logging","--disable-notifications","--disable-offer-store-unmasked-wallet-cards","--disable-popup-blocking","--disable-print-preview","--disable-prompt-on-repost","--disable-renderer-backgrounding","--disable-search-engine-choice-screen","--disable-session-crashed-bubble","--disable-setuid-sandbox","--disable-site-isolation-trials","--disable-speech-api","--disable-sync","--enable-unsafe-webgpu","--hide-crash-restore-bubble","--hide-scrollbars","--metrics-recording-only","--mute-audio","--no-default-browser-check","--no-first-run","--no-pings","--no-sandbox","--no-startup-window","--no-zygote","--password-store=basic","--process-per-tab","--use-mock-keychain"],type:"string[]",description:"Arguments array to send to Puppeteer."}},highcharts:{version:{value:"latest",type:"string",envLink:"HIGHCHARTS_VERSION",description:"The Highcharts version to be used."},cdnURL:{value:"https://code.highcharts.com/",type:"string",envLink:"HIGHCHARTS_CDN_URL",description:"The CDN URL for Highcharts scripts to be used."},coreScripts:{value:O.core,type:"string[]",envLink:"HIGHCHARTS_CORE_SCRIPTS",description:"The core Highcharts scripts to fetch."},moduleScripts:{value:O.modules,type:"string[]",envLink:"HIGHCHARTS_MODULE_SCRIPTS",description:"The modules of Highcharts to fetch."},indicatorScripts:{value:O.indicators,type:"string[]",envLink:"HIGHCHARTS_INDICATOR_SCRIPTS",description:"The indicators of Highcharts to fetch."},customScripts:{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 custom scripts or dependencies to fetch."},forceFetch:{value:!1,type:"boolean",envLink:"HIGHCHARTS_FORCE_FETCH",description:"The flag to determine whether to refetch all scripts after each server rerun."},cachePath:{value:".cache",type:"string",envLink:"HIGHCHARTS_CACHE_PATH",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:{value:"png",type:"string",envLink:"EXPORT_TYPE",description:"The file export format. It can be jpeg, png, pdf, or svg."},constr:{value:"chart",type:"string",envLink:"EXPORT_CONSTR",description:"The constructor to use. Can be chart, stockChart, mapChart, or ganttChart."},defaultHeight:{value:400,type:"number",envLink:"EXPORT_DEFAULT_HEIGHT",description:"the default height of the exported chart. Used when no value is set."},defaultWidth:{value:600,type:"number",envLink:"EXPORT_DEFAULT_WIDTH",description:"The default width of the exported chart. Used when no value is set."},defaultScale:{value:1,type:"number",envLink:"EXPORT_DEFAULT_SCALE",description:"The default scale of the exported chart. Used when no value is set."},height:{value:!1,type:"number",description:"The height of the exported chart, overriding the option in the chart settings."},width:{value:!1,type:"number",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:{value:1500,type:"number",envLink:"EXPORT_RASTERIZATION_TIMEOUT",description:"The duration in milliseconds to wait for rendering a webpage."}},customLogic:{allowCodeExecution:{value:!1,type:"boolean",envLink:"CUSTOM_LOGIC_ALLOW_CODE_EXECUTION",description:"Controls whether the execution of arbitrary code is allowed during the exporting process."},allowFileResources:{value:!1,type:"boolean",envLink:"CUSTOM_LOGIC_ALLOW_FILE_RESOURCES",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:{value:!1,type:"boolean",envLink:"SERVER_ENABLE",cliName:"enableServer",description:"When set to true, the server starts on the local IP address 0.0.0.0."},host:{value:"0.0.0.0",type:"string",envLink:"SERVER_HOST",description:"The hostname of the server. Additionally, it starts a server on the provided hostname."},port:{value:7801,type:"number",envLink:"SERVER_PORT",description:"The server port when enabled."},benchmarking:{value:!1,type:"boolean",envLink:"SERVER_BENCHMARKING",cliName:"serverBenchmarking",description:"Indicates whether to display the duration, in milliseconds, of specific actions that occur on the server while serving a request."},proxy:{host:{value:!1,type:"string",envLink:"SERVER_PROXY_HOST",cliName:"proxyHost",description:"The host of the proxy server to use, if it exists."},port:{value:8080,type:"number",envLink:"SERVER_PROXY_PORT",cliName:"proxyPort",description:"The port of the proxy server to use, if it exists."},timeout:{value:5e3,type:"number",envLink:"SERVER_PROXY_TIMEOUT",cliName:"proxyTimeout",description:"The timeout for the proxy server to use, if it exists."}},rateLimiting:{enable:{value:!1,type:"boolean",envLink:"SERVER_RATE_LIMITING_ENABLE",cliName:"enableRateLimiting",description:"Enables rate limiting for the server."},maxRequests:{value:10,type:"number",envLink:"SERVER_RATE_LIMITING_MAX_REQUESTS",legacyName:"rateLimit",description:"The maximum number of requests allowed in one minute."},window:{value:1,type:"number",envLink:"SERVER_RATE_LIMITING_WINDOW",description:"The time window, in minutes, for the rate limiting."},delay:{value:0,type:"number",envLink:"SERVER_RATE_LIMITING_DELAY",description:"The delay duration for each successive request before reaching the maximum limit."},trustProxy:{value:!1,type:"boolean",envLink:"SERVER_RATE_LIMITING_TRUST_PROXY",description:"Set this to true if the server is behind a load balancer."},skipKey:{value:!1,type:"string",envLink:"SERVER_RATE_LIMITING_SKIP_KEY",description:"Allows bypassing the rate limiter and should be provided with the skipToken argument."},skipToken:{value:!1,type:"string",envLink:"SERVER_RATE_LIMITING_SKIP_TOKEN",description:"Allows bypassing the rate limiter and should be provided with the skipKey argument."}},ssl:{enable:{value:!1,type:"boolean",envLink:"SERVER_SSL_ENABLE",cliName:"enableSsl",description:"Enables or disables the SSL protocol."},force:{value:!1,type:"boolean",envLink:"SERVER_SSL_FORCE",cliName:"sslForce",legacyName:"sslOnly",description:"When set to true, the server is forced to serve only over HTTPS."},port:{value:443,type:"number",envLink:"SERVER_SSL_PORT",cliName:"sslPort",description:"The port on which to run the SSL server."},certPath:{value:!1,type:"string",envLink:"SERVER_SSL_CERT_PATH",legacyName:"sslPath",description:"The path to the SSL certificate/key file."}}},pool:{minWorkers:{value:4,type:"number",envLink:"POOL_MIN_WORKERS",description:"The number of minimum and initial pool workers to spawn."},maxWorkers:{value:8,type:"number",envLink:"POOL_MAX_WORKERS",legacyName:"workers",description:"The number of maximum pool workers to spawn."},workLimit:{value:40,type:"number",envLink:"POOL_WORK_LIMIT",description:"The number of work pieces that can be performed before restarting the worker process."},acquireTimeout:{value:5e3,type:"number",envLink:"POOL_ACQUIRE_TIMEOUT",description:"The duration, in milliseconds, to wait for acquiring a resource."},createTimeout:{value:5e3,type:"number",envLink:"POOL_CREATE_TIMEOUT",description:"The duration, in milliseconds, to wait for creating a resource."},destroyTimeout:{value:5e3,type:"number",envLink:"POOL_DESTROY_TIMEOUT",description:"The duration, in milliseconds, to wait for destroying a resource."},idleTimeout:{value:3e4,type:"number",envLink:"POOL_IDLE_TIMEOUT",description:"The duration, in milliseconds, after which an idle resource is destroyed."},createRetryInterval:{value:200,type:"number",envLink:"POOL_CREATE_RETRY_INTERVAL",description:"The duration, in milliseconds, to wait before retrying the create process in case of a failure."},reaperInterval:{value:1e3,type:"number",envLink:"POOL_REAPER_INTERVAL",description:"The duration, in milliseconds, after which the check for idle resources to destroy is triggered."},benchmarking:{value:!1,type:"boolean",envLink:"POOL_BENCHMARKING",cliName:"poolBenchmarking",description:"Indicate whether to show statistics for the pool of resources or not."}},logging:{level:{value:4,type:"number",envLink:"LOGGING_LEVEL",cliName:"logLevel",description:"The logging level to be used."},file:{value:"highcharts-export-server.log",type:"string",envLink:"LOGGING_FILE",cliName:"logFile",description:"The name of a log file. The logDest option also needs to be set to enable file logging."},dest:{value:"log/",type:"string",envLink:"LOGGING_DEST",cliName:"logDest",description:"The path to store log files. This also enables file logging."}},ui:{enable:{value:!1,type:"boolean",envLink:"UI_ENABLE",cliName:"enableUi",description:"Enables or disables the user interface (UI) for the export server."},route:{value:"/",type:"string",envLink:"UI_ROUTE",cliName:"uiRoute",description:"The endpoint route to which the user interface (UI) should be attached."}},other:{nodeEnv:{value:"production",type:"string",envLink:"OTHER_NODE_ENV",description:"The type of Node.js environment."},listenToProcessExits:{value:!0,type:"boolean",envLink:"OTHER_LISTEN_TO_PROCESS_EXITS",description:"Decides whether or not to attach process.exit handlers."},noLogo:{value:!1,type:"boolean",envLink:"OTHER_NO_LOGO",description:"Skip printing the logo on a startup. Will be replaced by a simple text."},hardResetPage:{value:!1,type:"boolean",envLink:"OTHER_HARD_RESET_PAGE",description:"Decides if the page content should be reset entirely."}},debug:{enable:{value:!1,type:"boolean",envLink:"DEBUG_ENABLE",cliName:"enableDebug",description:"Enables or disables debug mode for the underlying browser."},headless:{value:!0,type:"boolean",envLink:"DEBUG_HEADLESS",description:"Controls the mode in which the browser is launched when in the debug mode."},devtools:{value:!1,type:"boolean",envLink:"DEBUG_DEVTOOLS",description:"Decides whether to enable DevTools when the browser is in a headful state."},listenToConsole:{value:!1,type:"boolean",envLink:"DEBUG_LISTEN_TO_CONSOLE",description:"Decides whether to enable a listener for console messages sent from the browser."},dumpio:{value:!1,type:"boolean",envLink:"DEBUG_DUMPIO",description:"Redirects browser process stdout and stderr to process.stdout and process.stderr."},slowMo:{value:0,type:"number",envLink:"DEBUG_SLOW_MO",description:"Slows down Puppeteer operations by the specified number of milliseconds."},debuggingPort:{value:9222,type:"number",envLink:"DEBUG_DEBUGGING_PORT",description:"Specifies the debugging port."}}},k={puppeteer:[{type:"list",name:"args",message:"Puppeteer arguments",initial:_.puppeteer.args.value.join(","),separator:","}],highcharts:[{type:"text",name:"version",message:"Highcharts version",initial:_.highcharts.version.value},{type:"text",name:"cdnURL",message:"The URL of CDN",initial:_.highcharts.cdnURL.value},{type:"multiselect",name:"coreScripts",message:"Available core scripts",instructions:"Space: Select specific, A: Select all, Enter: Confirm.",choices:_.highcharts.coreScripts.value},{type:"multiselect",name:"moduleScripts",message:"Available module scripts",instructions:"Space: Select specific, A: Select all, Enter: Confirm.",choices:_.highcharts.moduleScripts.value},{type:"multiselect",name:"indicatorScripts",message:"Available indicator scripts",instructions:"Space: Select specific, A: Select all, Enter: Confirm.",choices:_.highcharts.indicatorScripts.value},{type:"list",name:"customScripts",message:"Custom scripts",initial:_.highcharts.customScripts.value.join(","),separator:","},{type:"toggle",name:"forceFetch",message:"Force re-fetch the scripts",initial:_.highcharts.forceFetch.value},{type:"text",name:"cachePath",message:"The path to the cache directory",initial:_.highcharts.cachePath.value}],export:[{type:"select",name:"type",message:"The default export file type",hint:`Default: ${_.export.type.value}`,initial:0,choices:["png","jpeg","pdf","svg"]},{type:"select",name:"constr",message:"The default constructor for Highcharts",hint:`Default: ${_.export.constr.value}`,initial:0,choices:["chart","stockChart","mapChart","ganttChart"]},{type:"number",name:"defaultHeight",message:"The default fallback height of the exported chart",initial:_.export.defaultHeight.value},{type:"number",name:"defaultWidth",message:"The default fallback width of the exported chart",initial:_.export.defaultWidth.value},{type:"number",name:"defaultScale",message:"The default fallback scale of the exported chart",initial:_.export.defaultScale.value,min:.1,max:5},{type:"number",name:"rasterizationTimeout",message:"The rendering webpage timeout in milliseconds",initial:_.export.rasterizationTimeout.value}],customLogic:[{type:"toggle",name:"allowCodeExecution",message:"Enable execution of custom code",initial:_.customLogic.allowCodeExecution.value},{type:"toggle",name:"allowFileResources",message:"Enable file resources",initial:_.customLogic.allowFileResources.value}],server:[{type:"toggle",name:"enable",message:"Starts the server on 0.0.0.0",initial:_.server.enable.value},{type:"text",name:"host",message:"Server hostname",initial:_.server.host.value},{type:"number",name:"port",message:"Server port",initial:_.server.port.value},{type:"toggle",name:"benchmarking",message:"Enable server benchmarking",initial:_.server.benchmarking.value},{type:"text",name:"proxy.host",message:"The host of the proxy server to use",initial:_.server.proxy.host.value},{type:"number",name:"proxy.port",message:"The port of the proxy server to use",initial:_.server.proxy.port.value},{type:"number",name:"proxy.timeout",message:"The timeout for the proxy server to use",initial:_.server.proxy.timeout.value},{type:"toggle",name:"rateLimiting.enable",message:"Enable rate limiting",initial:_.server.rateLimiting.enable.value},{type:"number",name:"rateLimiting.maxRequests",message:"The maximum requests allowed per minute",initial:_.server.rateLimiting.maxRequests.value},{type:"number",name:"rateLimiting.window",message:"The rate-limiting time window in minutes",initial:_.server.rateLimiting.window.value},{type:"number",name:"rateLimiting.delay",message:"The delay for each successive request before reaching the maximum",initial:_.server.rateLimiting.delay.value},{type:"toggle",name:"rateLimiting.trustProxy",message:"Set to true if behind a load balancer",initial:_.server.rateLimiting.trustProxy.value},{type:"text",name:"rateLimiting.skipKey",message:"Allows bypassing the rate limiter when provided with the skipToken argument",initial:_.server.rateLimiting.skipKey.value},{type:"text",name:"rateLimiting.skipToken",message:"Allows bypassing the rate limiter when provided with the skipKey argument",initial:_.server.rateLimiting.skipToken.value},{type:"toggle",name:"ssl.enable",message:"Enable SSL protocol",initial:_.server.ssl.enable.value},{type:"toggle",name:"ssl.force",message:"Force serving only over HTTPS",initial:_.server.ssl.force.value},{type:"number",name:"ssl.port",message:"SSL server port",initial:_.server.ssl.port.value},{type:"text",name:"ssl.certPath",message:"The path to find the SSL certificate/key",initial:_.server.ssl.certPath.value}],pool:[{type:"number",name:"minWorkers",message:"The initial number of workers to spawn",initial:_.pool.minWorkers.value},{type:"number",name:"maxWorkers",message:"The maximum number of workers to spawn",initial:_.pool.maxWorkers.value},{type:"number",name:"workLimit",message:"The pieces of work that can be performed before restarting a Puppeteer process",initial:_.pool.workLimit.value},{type:"number",name:"acquireTimeout",message:"The number of milliseconds to wait for acquiring a resource",initial:_.pool.acquireTimeout.value},{type:"number",name:"createTimeout",message:"The number of milliseconds to wait for creating a resource",initial:_.pool.createTimeout.value},{type:"number",name:"destroyTimeout",message:"The number of milliseconds to wait for destroying a resource",initial:_.pool.destroyTimeout.value},{type:"number",name:"idleTimeout",message:"The number of milliseconds after an idle resource is destroyed",initial:_.pool.idleTimeout.value},{type:"number",name:"createRetryInterval",message:"The retry interval in milliseconds after a create process fails",initial:_.pool.createRetryInterval.value},{type:"number",name:"reaperInterval",message:"The reaper interval in milliseconds after triggering the check for idle resources to destroy",initial:_.pool.reaperInterval.value},{type:"toggle",name:"benchmarking",message:"Enable benchmarking for a resource pool",initial:_.pool.benchmarking.value}],logging:[{type:"number",name:"level",message:"The log level (0: silent, 1: error, 2: warning, 3: notice, 4: verbose, 5: benchmark)",initial:_.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:_.logging.file.value},{type:"text",name:"dest",message:"The path to log files. Enables file logging",initial:_.logging.dest.value}],ui:[{type:"toggle",name:"enable",message:"Enable UI for the export server",initial:_.ui.enable.value},{type:"text",name:"route",message:"A route to attach the UI",initial:_.ui.route.value}],other:[{type:"text",name:"nodeEnv",message:"The type of Node.js environment",initial:_.other.nodeEnv.value},{type:"toggle",name:"listenToProcessExits",message:"Set to false to skip attaching process.exit handlers",initial:_.other.listenToProcessExits.value},{type:"toggle",name:"noLogo",message:"Skip printing the logo on startup. Replaced by simple text",initial:_.other.noLogo.value},{type:"toggle",name:"hardResetPage",message:"Decides if the page content should be reset entirely",initial:_.other.hardResetPage.value}],debug:[{type:"toggle",name:"enable",message:"Enables debug mode for the browser instance",initial:_.debug.enable.value},{type:"toggle",name:"headless",message:"The mode setting for the browser",initial:_.debug.headless.value},{type:"toggle",name:"devtools",message:"The DevTools for the headful browser",initial:_.debug.devtools.value},{type:"toggle",name:"listenToConsole",message:"The event listener for console messages from the browser",initial:_.debug.listenToConsole.value},{type:"toggle",name:"dumpio",message:"Redirects the browser stdout and stderr to NodeJS process",initial:_.debug.dumpio.value},{type:"number",name:"slowMo",message:"Puppeteer operations slow down in milliseconds",initial:_.debug.slowMo.value},{type:"number",name:"debuggingPort",message:"The port number for debugging",initial:_.debug.debuggingPort.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(_),u.config();const N=e=>d.string().transform((t=>t.split(",").map((e=>e.trim())).filter((t=>e.includes(t))))).transform((e=>e.length?e:void 0)),P=()=>d.enum(["true","false",""]).transform((e=>""!==e?"true"===e:void 0)),H=e=>d.enum([...e,""]).transform((e=>""!==e?e:void 0)),$=()=>d.string().trim().refine((e=>!["false","undefined","null","NaN"].includes(e)||""===e),(e=>({message:`The string contains forbidden values, received '${e}'`}))).transform((e=>""!==e?e:void 0)),U=()=>d.string().trim().refine((e=>""===e||!isNaN(parseFloat(e))&&parseFloat(e)>0),(e=>({message:`The value must be numeric and positive, received '${e}'`}))).transform((e=>""!==e?parseFloat(e):void 0)),D=()=>d.string().trim().refine((e=>""===e||!isNaN(parseFloat(e))&&parseFloat(e)>=0),(e=>({message:`The value must be numeric and non-negative, received '${e}'`}))).transform((e=>""!==e?parseFloat(e):void 0)),G=d.object({HIGHCHARTS_VERSION:d.string().trim().refine((e=>/^(latest|\d+(\.\d+){0,2})$/.test(e)||""===e),(e=>({message:`HIGHCHARTS_VERSION must be 'latest', a major version, or in the form XX.YY.ZZ, received '${e}'`}))).transform((e=>""!==e?e:void 0)),HIGHCHARTS_CDN_URL:d.string().trim().refine((e=>e.startsWith("https://")||e.startsWith("http://")||""===e),(e=>({message:`Invalid value for HIGHCHARTS_CDN_URL. It should start with http:// or https://, received '${e}'`}))).transform((e=>""!==e?e:void 0)),HIGHCHARTS_CORE_SCRIPTS:N(O.core),HIGHCHARTS_MODULE_SCRIPTS:N(O.modules),HIGHCHARTS_INDICATOR_SCRIPTS:N(O.indicators),HIGHCHARTS_FORCE_FETCH:P(),HIGHCHARTS_CACHE_PATH:$(),HIGHCHARTS_ADMIN_TOKEN:$(),EXPORT_TYPE:H(["jpeg","png","pdf","svg"]),EXPORT_CONSTR:H(["chart","stockChart","mapChart","ganttChart"]),EXPORT_DEFAULT_HEIGHT:U(),EXPORT_DEFAULT_WIDTH:U(),EXPORT_DEFAULT_SCALE:U(),EXPORT_RASTERIZATION_TIMEOUT:D(),CUSTOM_LOGIC_ALLOW_CODE_EXECUTION:P(),CUSTOM_LOGIC_ALLOW_FILE_RESOURCES:P(),SERVER_ENABLE:P(),SERVER_HOST:$(),SERVER_PORT:U(),SERVER_BENCHMARKING:P(),SERVER_PROXY_HOST:$(),SERVER_PROXY_PORT:U(),SERVER_PROXY_TIMEOUT:D(),SERVER_RATE_LIMITING_ENABLE:P(),SERVER_RATE_LIMITING_MAX_REQUESTS:D(),SERVER_RATE_LIMITING_WINDOW:D(),SERVER_RATE_LIMITING_DELAY:D(),SERVER_RATE_LIMITING_TRUST_PROXY:P(),SERVER_RATE_LIMITING_SKIP_KEY:$(),SERVER_RATE_LIMITING_SKIP_TOKEN:$(),SERVER_SSL_ENABLE:P(),SERVER_SSL_FORCE:P(),SERVER_SSL_PORT:U(),SERVER_SSL_CERT_PATH:$(),POOL_MIN_WORKERS:D(),POOL_MAX_WORKERS:D(),POOL_WORK_LIMIT:U(),POOL_ACQUIRE_TIMEOUT:D(),POOL_CREATE_TIMEOUT:D(),POOL_DESTROY_TIMEOUT:D(),POOL_IDLE_TIMEOUT:D(),POOL_CREATE_RETRY_INTERVAL:D(),POOL_REAPER_INTERVAL:D(),POOL_BENCHMARKING:P(),LOGGING_LEVEL:d.string().trim().refine((e=>""===e||!isNaN(parseFloat(e))&&parseFloat(e)>=0&&parseFloat(e)<=5),(e=>({message:`Invalid value for LOGGING_LEVEL. We only accept values from 0 to 5 as logging levels, received '${e}'`}))).transform((e=>""!==e?parseFloat(e):void 0)),LOGGING_FILE:$(),LOGGING_DEST:$(),UI_ENABLE:P(),UI_ROUTE:$(),OTHER_NODE_ENV:H(["development","production","test"]),OTHER_LISTEN_TO_PROCESS_EXITS:P(),OTHER_NO_LOGO:P(),OTHER_HARD_RESET_PAGE:P(),DEBUG_ENABLE:P(),DEBUG_HEADLESS:P(),DEBUG_DEVTOOLS:P(),DEBUG_LISTEN_TO_CONSOLE:P(),DEBUG_DUMPIO:P(),DEBUG_SLOW_MO:D(),DEBUG_DEBUGGING_PORT:U()}).partial().parse(process.env),j=["red","yellow","blue","gray","green"];let M={toConsole:!0,toFile:!1,pathCreated:!1,levelsDesc:[{title:"error",color:j[0]},{title:"warning",color:j[1]},{title:"notice",color:j[2]},{title:"verbose",color:j[3]},{title:"benchmark",color:j[4]}],listeners:[]};for(const[e,t]of Object.entries(_.logging))M[e]=t.value;const F=(e,i)=>{M.toFile&&(M.pathCreated||(!t(M.dest)&&r(M.dest),M.pathCreated=!0),o(`${M.dest}${M.file}`,[i].concat(e).join(" ")+"\n",(e=>{e&&(console.log(`[logger] Unable to write to log file: ${e}`),M.toFile=!1)})))},W=(...e)=>{const[t,...r]=e,{level:o,levelsDesc:i}=M;if(5!==t&&(0===t||t>o||o>i.length))return;const s=`${(new Date).toString().split("(")[0].trim()} [${i[t-1].title}] -`;M.listeners.forEach((e=>{e(s,r.join(" "))})),M.toConsole&&console.log.apply(void 0,[s.toString()[M.levelsDesc[t-1].color]].concat(r)),F(r,s)},V=(e,t,r)=>{const o=r||t.message,{level:i,levelsDesc:s}=M;if(0===e||e>i||i>s.length)return;const n=`${(new Date).toString().split("(")[0].trim()} [${s[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];M.toConsole&&console.log.apply(void 0,[n.toString()[M.levelsDesc[e-1].color]].concat([o[j[e-1]],"\n",a])),M.listeners.forEach((e=>{e(n,l.join(" "))})),F(l,n)},q=e=>{e>=0&&e<=M.levelsDesc.length&&(M.level=e)},B=(e,t)=>{if(M={...M,dest:e||M.dest,file:t||M.file,toFile:!0},0===M.dest.length)return W(1,"[logger] File logging initialization: no path supplied.");M.dest.endsWith("/")||(M.dest+="/")},X=m(new URL("../.",import.meta.url)),K=(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"},J=(e=!1,t)=>{const r=["js","css","files"];let o=e,s=!1;if(t&&e.endsWith(".json"))try{o=z(i(e,"utf8"))}catch(e){return V(2,e,"[cli] No resources found.")}else o=z(e),o&&!t&&delete o.files;for(const e in o)r.includes(e)?s||(s=!0):delete o[e];return s?(o.files&&(o.files=o.files.map((e=>e.trim())),(!o.files||o.files.length<=0)&&delete o.files),o):W(3,"[cli] No resources found.")};function z(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 Y=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]=Y(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 Z(){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(_).forEach((t=>{["puppeteer","highcharts"].includes(t)||(console.log(`\n${t.toUpperCase()}`.red),e(_[t]))})),console.log("\n")}const ee=e=>!["false","undefined","null","NaN","0",""].includes(e)&&!!e,te=(e,t)=>{if(e&&"string"==typeof e)return(e=e.trim()).endsWith(".js")?!!t&&te(i(e,"utf8")):e.startsWith("function()")||e.startsWith("function ()")||e.startsWith("()=>")||e.startsWith("() =>")?`(${e})()`:e.replace(/;$/,"")},re=()=>{const e=process.hrtime.bigint();return()=>Number(process.hrtime.bigint()-e)/1e6};let oe={};const ie=()=>oe,se=(e,t,r=[])=>{const o=Y(e);for(const[e,s]of Object.entries(t))o[e]="object"!=typeof(i=s)||Array.isArray(i)||null===i||r.includes(e)||void 0===o[e]?void 0!==s?s:o[e]:se(o[e],s,r);var i;return o};function ne(e,t={},r=""){Object.keys(e).forEach((o=>{const i=e[o],s=t&&t[o];void 0===i.value?ne(i,s,`${r}.${o}`):(void 0!==s&&(i.value=s),i.envLink in G&&void 0!==G[i.envLink]&&(i.value=G[i.envLink]))}))}function ae(e){let t={};for(const[r,o]of Object.entries(e))t[r]=Object.prototype.hasOwnProperty.call(o,"value")?o.value:ae(o);return t}function le(e,t,r){for(;t.length>1;){const o=t.shift();return Object.prototype.hasOwnProperty.call(e,o)||(e[o]={}),e[o]=le(Object.assign({},e[o]),t,r),e}return e[t[0]]=r,e}async function ce(e,t={}){return new Promise(((r,o)=>{const i=(e=>e.startsWith("https")?v:f)(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)}))}))}class pe 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 he={cdnURL:"https://code.highcharts.com/",activeManifest:{},sources:"",hcVersion:""},ue=e=>e.sources.substring(0,e.sources.indexOf("*/")).replace("/*","").replace("*/","").replace(/\n/g,"").trim(),de=async(e,t,r,o=!1)=>{e.endsWith(".js")&&(e=e.substring(0,e.length-3)),W(4,`[cache] Fetching script - ${e}.js`);const i=await ce(`${e}.js`,t);if(200===i.statusCode&&"string"==typeof i.text){if(r){r[e.replace(/(.*)\/|(.*)modules\/|stock\/(.*)indicators\/|maps\/(.*)modules\//gi,"")]=1}return i.text}if(o)throw new pe(`Could not fetch the ${e}.js. The script might not exist in the requested version (status code: ${i.statusCode}).`).setError(i);return W(2,`[cache] Could not fetch the ${e}.js. The script might not exist in the requested version.`),""},ge=async(e,t,r)=>{const o=e.version,i="latest"!==o&&o?`${o}/`:"",s=e.cdnURL||he.cdnURL;W(3,`[cache] Updating cache version to Highcharts: ${i||"latest"}.`);const a={};try{return he.sources=await(async(e,t,r,o,i)=>{let s;const n=o.host,a=o.port;if(n&&a)try{s=new p({host:n,port:a})}catch(e){throw new pe("[cache] Could not create a Proxy Agent.").setError(e)}const l=s?{agent:s,timeout:G.SERVER_PROXY_TIMEOUT}:{},c=[...e.map((e=>de(`${e}`,l,i,!0))),...t.map((e=>de(`${e}`,l,i))),...r.map((e=>de(`${e}`,l)))];return(await Promise.all(c)).join(";\n")})([...e.coreScripts.map((e=>`${s}${i}${e}`))],[...e.moduleScripts.map((e=>"map"===e?`${s}maps/${i}modules/${e}`:`${s}${i}modules/${e}`)),...e.indicatorScripts.map((e=>`${s}stock/${i}indicators/${e}`))],e.customScripts,t,a),he.hcVersion=ue(he),n(r,he.sources),a}catch(e){throw new pe("[cache] Unable to update the local Highcharts cache.").setError(e)}},me=async e=>{const{highcharts:o,server:s}=e,a=l(X,o.cachePath);let c;const p=l(a,"manifest.json"),h=l(a,"sources.js");if(!t(a)&&r(a),!t(p)||o.forceFetch)W(3,"[cache] Fetching and caching Highcharts dependencies."),c=await ge(o,s.proxy,h);else{let e=!1;const t=JSON.parse(i(p));if(t.modules&&Array.isArray(t.modules)){const e={};t.modules.forEach((t=>e[t]=1)),t.modules=e}const{coreScripts:r,moduleScripts:n,indicatorScripts:a}=o,l=r.length+n.length+a.length;t.version!==o.version?(W(2,"[cache] A Highcharts version mismatch in the cache, need to re-fetch."),e=!0):Object.keys(t.modules||{}).length!==l?(W(2,"[cache] The cache and the requested modules do not match, need to re-fetch."),e=!0):e=(n||[]).some((e=>{if(!t.modules[e])return W(2,`[cache] The ${e} is missing in the cache, need to re-fetch.`),!0})),e?c=await ge(o,s.proxy,h):(W(3,"[cache] Dependency cache is up to date, proceeding."),he.sources=i(h,"utf8"),c=t.modules,he.hcVersion=ue(he))}await(async(e,t)=>{const r={version:e.version,modules:t||{}};he.activeManifest=r,W(3,"[cache] Writing a new manifest.");try{n(l(X,e.cachePath,"manifest.json"),JSON.stringify(r),"utf8")}catch(e){throw new pe("[cache] Error writing the cache manifest.").setError(e)}})(o,c)},fe=()=>l(X,ie().highcharts.cachePath);var ve=async e=>{const t=ie();t?.highcharts&&(t.highcharts.version=e),await me(t)},ye=()=>he,be=()=>he.hcVersion;function we(){Highcharts.animObject=function(){return{duration:0}}}function Ee(e,t,r){window._displayErrors=r;const{getOptions:o,merge:i,setOptions:s,wrap:n}=Highcharts;Highcharts.setOptionsObj=i(!1,{},o()),t.customLogic.customCode&&new Function(t.customLogic.customCode)();const a={animation:!1};t.export.strInj&&(a.height=e.chart.height,a.width=e.chart.width),window.isRenderComplete=!1,n(Highcharts.Chart.prototype,"init",(function(e,t,r){((t=i(t,{exporting:{enabled:!1},plotOptions:{series:{label:{enabled:!1}}},tooltip:{}})).series||[]).forEach((function(e){e.animation=!1})),window.onHighchartsRender||(window.onHighchartsRender=Highcharts.addEvent(this,"render",(()=>{window.isRenderComplete=!0}))),e.apply(this,[t,r])})),n(Highcharts.Series.prototype,"init",(function(e,t,r){e.apply(this,[t,r])}));const l=t.export.strInj?new Function(`return ${t.export.strInj}`)():e,c=i(!1,JSON.parse(t.export.themeOptions),l,{chart:a}),p=t.customLogic.callback?new Function(`return ${t.customLogic.callback}`)():void 0,h=JSON.parse(t.export.globalOptions);h&&s(h),Highcharts[t.export.constr||"chart"]("container",c,p);const u=o();for(const e in u)"function"!=typeof u[e]&&delete u[e];s(Highcharts.setOptionsObj),Highcharts.setOptionsObj={}}const Te=g.fileURLToPath(new URL(".",import.meta.url)),Se=e.readFileSync(Te+"/../templates/template.html","utf8");let xe;async function Re(){if(!xe)return!1;const e=await xe.newPage();return await e.setCacheEnabled(!1),await Le(e),e}async function Le(e){await e.setContent(Se,{waitUntil:"domcontentloaded"}),await e.addScriptTag({path:`${fe()}/sources.js`}),await e.evaluate(we)}const Oe=g.fileURLToPath(new URL(".",import.meta.url)),_e=(e,t,r,o)=>e.evaluate(Ee,t,r,o);var ke=async(e,t,r)=>{const o=[],s=async e=>{for(const e of o)await e.dispose();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()}const[...e]=document.getElementsByTagName("script"),[,...t]=document.getElementsByTagName("style"),[...r]=document.getElementsByTagName("link");for(const o of[...e,...t,...r])o.remove()}))};try{W(4,"[export] Determining export path.");const n=r.export,l=n?.options?.chart?.displayErrors&&ye().activeManifest.modules.debugger;let c;if(t.indexOf&&(t.indexOf("=0||t.indexOf("=0)){if(W(4,"[export] Treating as SVG."),"svg"===n.type)return t;c=!0,await e.setContent((e=>`\n\n\n \n \n Highcharts Export\n \n \n \n
\n ${e}\n
\n \n\n\n`)(t),{waitUntil:"domcontentloaded"})}else W(4,"[export] Treating as config."),n.strInj?await _e(e,{chart:{height:n.height,width:n.width}},r,l):(t.chart.height=n.height,t.chart.width=n.width,await _e(e,t,r,l));const p=r.customLogic.resources;if(p){const t=[];if(p.js&&t.push({content:p.js}),p.files)for(const e of p.files){const r=!e.startsWith("http");t.push(r?{content:i(e,"utf8")}:{url:e})}for(const r of t)try{o.push(await e.addScriptTag(r))}catch(e){V(2,e,"[export] The JS resource cannot be loaded.")}t.length=0;const s=[];if(p.css){let t=p.css.match(/@import\s*([^;]*);/g);if(t)for(let e of t)e&&(e=e.replace("url(","").replace("@import","").replace(/"/g,"").replace(/'/g,"").replace(/;/,"").replace(/\)/g,"").trim(),e.startsWith("http")?s.push({url:e}):r.customLogic.allowFileResources&&s.push({path:a.join(Oe,e)}));s.push({content:p.css.replace(/@import\s*([^;]*);/g,"")||" "});for(const t of s)try{o.push(await e.addStyleTag(t))}catch(e){V(2,e,"[export] The CSS resource cannot be loaded.")}s.length=0}}const h=c?await e.evaluate((e=>{const t=document.querySelector("#chart-container svg:first-of-type"),r=t.height.baseVal.value*e,o=t.width.baseVal.value*e;return document.body.style.zoom=e,document.body.style.margin="0px",{chartHeight:r,chartWidth:o}}),parseFloat(n.scale)):await e.evaluate((()=>{const{chartHeight:e,chartWidth:t}=window.Highcharts.charts[0];return document.body.style.zoom=1,{chartHeight:e,chartWidth:t}})),u=Math.ceil(h.chartHeight||n.height),d=Math.ceil(h.chartWidth||n.width),{x:g,y:m}=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 f;if(await e.setViewport({height:u,width:d,deviceScaleFactor:c?1:parseFloat(n.scale)}),"svg"===n.type)f=await(e=>e.$eval("#container svg:first-of-type",(e=>e.outerHTML)))(e);else if(["png","jpeg"].includes(n.type))f=await((e,t,r,o,i)=>Promise.race([e.screenshot({type:t,encoding:r,clip:o,captureBeyondViewport:!0,fullPage:!1,optimizeForSpeed:!0,..."png"!==t?{quality:80}:{},omitBackground:"png"==t}),new Promise(((e,t)=>setTimeout((()=>t(new pe("Rasterization timeout"))),i||1500)))]))(e,n.type,"base64",{width:d,height:u,x:g,y:m},n.rasterizationTimeout);else{if("pdf"!==n.type)throw new pe(`[export] Unsupported output format ${n.type}.`);f=await(async(e,t,r,o,i)=>(await e.emulateMediaType("screen"),Promise.race([e.pdf({height:t+1,width:r,encoding:o}),new Promise(((e,t)=>setTimeout((()=>t(new pe("Rasterization timeout"))),i||1500)))])))(e,u,d,"base64",n.rasterizationTimeout)}return await s(e),f}catch(t){return await s(e),t}};let Ie=!1;const Ce={performedExports:0,exportAttempts:0,exportFromSvgAttempts:0,timeSpent:0,droppedExports:0,spentAverage:0};let Ae={};const Ne={create:async()=>{let e=!1;const t=b(),r=(new Date).getTime();try{if(e=await Re(),!e||e.isClosed())throw new pe("The page is invalid or closed.");W(3,`[pool] Successfully created a worker ${t} - took ${(new Date).getTime()-r} ms.`)}catch(e){throw new pe("Error encountered when creating a new page.").setError(e)}const{debug:o}=ie();return o.enable&&o.listenToConsole&&e.on("console",(e=>{console.log(`[debug] ${e.text()}`)})),e.on("pageerror",(async t=>{await e.$eval("#container",((e,t)=>{window._displayErrors&&(e.innerHTML=t)}),`

Chart input data error:

${t.toString()}`)})),{id:t,page:e,workCount:Math.round(Math.random()*(Ae.workLimit/2))}},validate:async e=>!(Ae.workLimit&&++e.workCount>Ae.workLimit)||(W(3,`[pool] Worker failed validation: exceeded work limit (limit is ${Ae.workLimit}).`),!1),destroy:async e=>{W(3,`[pool] Destroying pool entry ${e.id}.`),e.page&&await e.page.close()}},Pe=async e=>{if(Ae=e&&e.pool?{...e.pool}:{},await async function(e){const{enable:t,...r}=ie().debug,o={headless:"shell",userDataDir:"./tmp/",args:e,handleSIGINT:!1,handleSIGTERM:!1,handleSIGHUP:!1,waitForInitialPage:!1,defaultViewport:null,...t&&r};if(!xe){let e=0;const r=async()=>{try{W(3,`[browser] Attempting to get a browser instance (try ${++e}).`),xe=await w.launch(o)}catch(t){if(V(1,t,"[browser] Failed to launch a browser instance."),!(e<25))throw t;W(3,`[browser] Retry to open a browser (${e} out of 25).`),await new Promise((e=>setTimeout(e,4e3))),await r()}};try{await r(),t&&W(3,"[browser] Launched browser in debug mode.")}catch(e){throw new pe("[browser] Maximum retries to open a browser instance reached.").setError(e)}if(!xe)throw new pe("[browser] Cannot find a browser to open.")}return xe}(e.puppeteerArgs),W(3,`[pool] Initializing pool with workers: min ${Ae.minWorkers}, max ${Ae.maxWorkers}.`),Ie)return W(4,"[pool] Already initialized, please kill it before creating a new one.");parseInt(Ae.minWorkers)>parseInt(Ae.maxWorkers)&&(Ae.minWorkers=Ae.maxWorkers);try{Ie=new y({...Ne,min:parseInt(Ae.minWorkers),max:parseInt(Ae.maxWorkers),acquireTimeoutMillis:Ae.acquireTimeout,createTimeoutMillis:Ae.createTimeout,destroyTimeoutMillis:Ae.destroyTimeout,idleTimeoutMillis:Ae.idleTimeout,createRetryIntervalMillis:Ae.createRetryInterval,reapIntervalMillis:Ae.reaperInterval,propagateCreateError:!1}),Ie.on("release",(async e=>{await async function(e,t=!1){try{e.isClosed()||(t?(await e.goto("about:blank",{waitUntil:"domcontentloaded"}),await Le(e)):await e.evaluate((()=>{document.body.innerHTML='
'})))}catch(e){V(2,e,"[browser] Could not clear the content of the page.")}}(e.page,!1),W(4,`[pool] Releasing a worker with ID ${e.id}.`)})),Ie.on("destroySuccess",((e,t)=>{W(4,`[pool] Destroyed a worker with ID ${t.id}.`)}));const e=[];for(let t=0;t{Ie.release(e)})),W(3,"[pool] The pool is ready"+(e.length?` with ${e.length} initial resources waiting.`:"."))}catch(e){throw new pe("[pool] Could not create the pool of workers.").setError(e)}};async function He(){if(W(3,"[pool] Killing pool with all workers and closing browser."),Ie){for(const e of Ie.used)Ie.release(e.resource);Ie.destroyed||(await Ie.destroy(),W(4,"[browser] Destroyed the pool of resources."))}await async function(){xe?.connected&&await xe.close(),W(4,"[browser] Closed the browser.")}()}const $e=async(e,t)=>{let r;try{if(W(4,"[pool] Work received, starting to process."),++Ce.exportAttempts,Ae.benchmarking&&De(),!Ie)throw new pe("Work received, but pool has not been started.");const o=re();try{W(4,"[pool] Acquiring a worker handle."),r=await Ie.acquire().promise,t.server.benchmarking&&W(5,t.payload?.requestId?`[benchmark] Request with ID ${t.payload?.requestId} -`:"[benchmark]",`Acquired a worker handle: ${o()}ms.`)}catch(e){throw new pe((t.payload?.requestId?`For request with ID ${t.payload?.requestId} - `:"")+`Error encountered when acquiring an available entry: ${o()}ms.`).setError(e)}if(W(4,"[pool] Acquired a worker handle."),!r.page)throw new pe("Resolved worker page is invalid: the pool setup is wonky.");let i=(new Date).getTime();W(4,`[pool] Starting work on pool entry with ID ${r.id}.`);const s=re(),n=await ke(r.page,e,t);if(n instanceof Error)throw"Rasterization timeout"===n.message&&(r.page.close(),r.page=await Re()),new pe((t.payload?.requestId?`For request with ID ${t.payload?.requestId} - `:"")+`Error encountered during export: ${s()}ms.`).setError(n);t.server.benchmarking&&W(5,t.payload?.requestId?`[benchmark] Request with ID ${t.payload?.requestId} -`:"[benchmark]",`Exported a chart sucessfully: ${s()}ms.`),Ie.release(r);const a=(new Date).getTime()-i;return Ce.timeSpent+=a,Ce.spentAverage=Ce.timeSpent/++Ce.performedExports,W(4,`[pool] Work completed in ${a} ms.`),{result:n,options:t}}catch(e){throw++Ce.droppedExports,r&&Ie.release(r),new pe(`[pool] In pool.postWork: ${e.message}`).setError(e)}},Ue=()=>({min:Ie.min,max:Ie.max,all:Ie.numFree()+Ie.numUsed(),available:Ie.numFree(),used:Ie.numUsed(),pending:Ie.numPendingAcquires()});function De(){const{min:e,max:t,all:r,available:o,used:i,pending:s}=Ue();W(5,`[pool] The minimum number of resources allowed by pool: ${e}.`),W(5,`[pool] The maximum number of resources allowed by pool: ${t}.`),W(5,`[pool] The number of all created resources: ${r}.`),W(5,`[pool] The number of available resources: ${o}.`),W(5,`[pool] The number of acquired resources: ${i}.`),W(5,`[pool] The number of resources waiting to be acquired: ${s}.`)}var Ge=Ue,je=()=>Ce;let Me=!1;const Fe=async(e,t)=>{W(4,"[chart] Starting the exporting process.");const r=((e,t={})=>{let r={};return e.svg?(r=Y(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,I),r.export.outfile=r.export?.outfile||`chart.${r.export?.type||"png"}`,r})(e,ie()),o=r.export;if(r.payload?.svg&&""!==r.payload.svg)try{W(4,"[chart] Attempting to export from a SVG input.");const e=Be(function(e){const t=new E("").window;return T(t).sanitize(e,{ADD_TAGS:["foreignObject"]})}(r.payload.svg),r,t);return++Ce.exportFromSvgAttempts,e}catch(e){return t(new pe("[chart] Error loading SVG input.").setError(e))}if(o.infile&&o.infile.length)try{return W(4,"[chart] Attempting to export from an input file."),r.export.instr=i(o.infile,"utf8"),Be(r.export.instr.trim(),r,t)}catch(e){return t(new pe("[chart] Error loading input file.").setError(e))}if(o.instr&&""!==o.instr||o.options&&""!==o.options)try{return W(4,"[chart] Attempting to export from a raw input."),ee(r.customLogic?.allowCodeExecution)?qe(r,t):"string"==typeof o.instr?Be(o.instr.trim(),r,t):Ve(r,o.instr||o.options,t)}catch(e){return t(new pe("[chart] Error loading raw input.").setError(e))}return t(new pe("[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||z(e.export?.instr),o=z(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 s={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(s))s[e]="string"==typeof t?+t.replace(/px|%/gi,""):t;return s},Ve=async(e,t,r,o)=>{let{export:s,customLogic:n}=e;const a="boolean"==typeof n.allowCodeExecution?n.allowCodeExecution:Me;if(n){if(a)if("string"==typeof e.customLogic.resources)e.customLogic.resources=J(e.customLogic.resources,ee(e.customLogic.allowFileResources));else if(!e.customLogic.resources)try{const t=i("resources.json","utf8");e.customLogic.resources=J(t,ee(e.customLogic.allowFileResources))}catch(e){V(2,e,"[chart] Unable to load the default resources.json file.")}}else n=e.customLogic={};if(!a&&n){if(n.callback||n.resources||n.customCode)return r(new pe("[chart] The 'callback', 'resources' and 'customCode' options have been disabled for this server."));n.callback=!1,n.resources=!1,n.customCode=!1}if(t&&(t.chart=t.chart||{},t.exporting=t.exporting||{},t.exporting.enabled=!1),s.constr=s.constr||"chart",s.type=K(s.type,s.outfile),"svg"===s.type&&(s.width=!1),["globalOptions","themeOptions"].forEach((e=>{try{s&&s[e]&&("string"==typeof s[e]&&s[e].endsWith(".json")?s[e]=z(i(s[e],"utf8"),!0):s[e]=z(s[e],!0))}catch(t){s[e]={},V(2,t,`[chart] The '${e}' cannot be loaded.`)}})),n.allowCodeExecution)try{n.customCode=te(n.customCode,n.allowFileResources)}catch(e){V(2,e,"[chart] The 'customCode' cannot be loaded.")}if(n&&n.callback&&n.callback?.indexOf("{")<0)if(n.allowFileResources)try{n.callback=i(n.callback,"utf8")}catch(e){n.callback=!1,V(2,e,"[chart] The 'callback' cannot be loaded.")}else n.callback=!1;e.export={...e.export,...We(e)};try{return r(!1,await $e(s.strInj||t||o,e))}catch(e){return r(e)}},qe=(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 pe(`[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))}},Be=(e,t,r)=>{const{allowCodeExecution:o}=t.customLogic;if(e.indexOf("=0||e.indexOf("=0)return W(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 ee(o)?qe(t,r):r(new pe("[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=[],Ke=()=>{W(4,"[server] Clearing all registered intervals.");for(const e of Xe)clearInterval(e)},Je=(e,t,r,o)=>{V(1,e),"development"!==G.OTHER_NODE_ENV&&delete e.stack,o(e)},ze=(e,t,r,o)=>{const{statusCode:i,status:s,message:n,stack:a}=e,l=i||s||500;r.status(l).json({statusCode:l,message:n,stack:a})};var Ye=(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&&(W(4,"[rate limiting] Skipping rate limiter."),!0)});e.use(i),W(3,`[rate limiting] Enabled rate limiting with ${o.max} requests per ${o.window} minute for each IP, trusting proxy: ${o.trustProxy}.`)};class Qe extends pe{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 et=0;const tt=[],rt=[],ot=(e,t,r,o)=>{let i=!0;const{id:s,uniqueId:n,type:a,body:l}=o;return e.some((e=>{if(e){let o=e(t,r,s,n,a,l);return void 0!==o&&!0!==o&&(i=o),!0}})),i},it=async(e,t,r)=>{try{const r=re(),i=b().replace(/-/g,""),s=ie(),n=e.body,a=++et;let l=K(n.type);if(!n||"object"==typeof(o=n)&&!Array.isArray(o)&&null!==o&&0===Object.keys(o).length)throw new Qe("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=z(n.infile||n.options||n.data);if(!c&&!n.svg)throw W(2,`The request with ID ${i} from ${e.headers["x-forwarded-for"]||e.connection.remoteAddress} was incorrect. Payload received: ${JSON.stringify(n)}.`),new Qe("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=ot(tt,e,t,{id:a,uniqueId:i,type:l,body:n}),!0!==p)return t.send(p);let h=!1;e.socket.on("close",(()=>{h=!0})),W(4,`[export] Got an incoming HTTP request with ID ${i}.`),n.constr="string"==typeof n.constr&&n.constr||"chart";const u={export:{instr:c,type:l,constr:n.constr[0].toLowerCase()+n.constr.substr(1),height:n.height,width:n.width,scale:n.scale||s.export.scale,globalOptions:z(n.globalOptions,!0),themeOptions:z(n.themeOptions,!0)},customLogic:{allowCodeExecution:Me,allowFileResources:!1,resources:z(n.resources,!0),callback:n.callback,customCode:n.customCode}};c&&(u.export.instr=Q(c,u.customLogic.allowCodeExecution));const d=se(s,u);if(d.export.options=c,d.payload={svg:n.svg||!1,b64:n.b64||!1,noDownload:n.noDownload||!1,requestId:i},n.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 Qe("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 Fe(d,((o,c)=>{if(e.socket.removeAllListeners("close"),s.server.benchmarking&&W(5,`[benchmark] Request with ID ${i} - After the whole exporting process: ${r()}ms.`),h)return W(3,"[export] The client closed the connection before the chart finished processing.");if(o)throw o;if(!c||!c.result)throw new Qe(`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,ot(rt,e,t,{id:a,body:c.result}),c.result?n.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"),n.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 st=JSON.parse(i(l(X,"package.json"))),nt=new Date,at=[];function lt(e){if(!e)return!1;var t;t=setInterval((()=>{const e=je(),t=0===e.exportAttempts?1:e.performedExports/e.exportAttempts*100;at.push(t),at.length>30&&at.shift()}),6e4),Xe.push(t),e.get("/health",((e,t)=>{const r=je(),o=at.length,i=at.reduce(((e,t)=>e+t),0)/at.length;W(4,"[health.js] GET /health [200] - returning server health."),t.send({status:"OK",bootTime:nt,uptime:Math.floor(((new Date).getTime()-nt.getTime())/1e3/60)+" minutes",version:st.version,highchartsVersion:be(),averageProcessingTime:r.spentAverage,performedExports:r.performedExports,failedExports:r.droppedExports,exportAttempts:r.exportAttempts,sucessRatio:r.performedExports/r.exportAttempts*100,pool:Ge(),period:o,movingAverage:i,message:`Last ${o} minutes had a success rate of ${i.toFixed(2)}%.`,svgExportAttempts:r.exportFromSvgAttempts,jsonExportAttempts:r.performedExports-r.exportFromSvgAttempts})}))}const ct=new Map,pt=x();pt.disable("x-powered-by"),pt.use(S());const ht=R.memoryStorage(),ut=R({storage:ht,limits:{fieldSize:52428800}});pt.use(x.json({limit:52428800})),pt.use(x.urlencoded({extended:!0,limit:52428800})),pt.use(ut.none());const dt=e=>{e.on("clientError",(e=>{V(1,e,`[server] Client error: ${e.message}`)})),e.on("error",(e=>{V(1,e,`[server] Server error: ${e.message}`)})),e.on("connection",(e=>{e.on("error",(e=>{V(1,e,`[server] Socket error: ${e.message}`)}))}))},gt=async e=>{try{if(!e.enable)return!1;if(!e.ssl.force){const t=f.createServer(pt);dt(t),t.listen(e.port,e.host),ct.set(e.port,t),W(3,`[server] Started HTTP server on ${e.host}:${e.port}.`)}if(e.ssl.enable){let t,r;try{t=await s.readFile(c.join(e.ssl.certPath,"server.key"),"utf8"),r=await s.readFile(c.join(e.ssl.certPath,"server.crt"),"utf8")}catch(t){W(2,`[server] Unable to load key/certificate from the '${e.ssl.certPath}' path. Could not run secured layer server.`)}if(t&&r){const o=v.createServer({key:t,cert:r},pt);dt(o),o.listen(e.ssl.port,e.host),ct.set(e.ssl.port,o),W(3,`[server] Started HTTPS server on ${e.host}:${e.ssl.port}.`)}}e.rateLimiting&&e.rateLimiting.enable&&![0,NaN].includes(e.rateLimiting.maxRequests)&&Ye(pt,e.rateLimiting),pt.use(x.static(c.join(X,"public"))),lt(pt),(e=>{e.post("/",it),e.post("/:filename",it)})(pt),(e=>{!!e&&e.get("/",((e,t)=>{t.sendFile(l(X,"public","index.html"))}))})(pt),(e=>{!!e&&e.post("/version/change/:newVersion",(async(e,t,r)=>{try{const r=G.HIGHCHARTS_ADMIN_TOKEN;if(!r||!r.length)throw new Qe("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 Qe("Invalid or missing token: Set the token in the hc-auth header.",401);const i=e.params.newVersion;if(!i)throw new Qe("No new version supplied.",400);try{await ve(i)}catch(e){throw new Qe(`Version change: ${e.message}`,e.statusCode).setError(e)}t.status(200).send({statusCode:200,version:be(),message:`Successfully updated Highcharts to version: ${i}.`})}catch(e){r(e)}}))})(pt),(e=>{e.use(Je),e.use(ze)})(pt)}catch(e){throw new pe("[server] Could not configure and start the server.").setError(e)}},mt=()=>{W(4,"[server] Closing all servers.");for(const[e,t]of ct)t.close((()=>{ct.delete(e),W(4,`[server] Closed server on port: ${e}.`)}))};var ft={startServer:gt,closeServers:mt,getServers:()=>ct,enableRateLimiting:e=>Ye(pt,e),getExpress:()=>x,getApp:()=>pt,use:(e,...t)=>{pt.use(e,...t)},get:(e,...t)=>{pt.get(e,...t)},post:(e,...t)=>{pt.post(e,...t)}};const vt=async e=>{await Promise.allSettled([Ke(),mt(),He()]),process.exit(e)};var yt={server:ft,startServer:gt,initExport:async e=>{var t;return t=e.customLogic&&e.customLogic.allowCodeExecution,Me=ee(t),(e=>{q(e&&parseInt(e.level)),e&&e.dest&&B(e.dest,e.file||"highcharts-export-server.log")})(e.logging),e.other.listenToProcessExits&&(W(3,"[process] Attaching exit listeners to the process."),process.on("exit",(e=>{W(4,`Process exited with code ${e}.`)})),process.on("SIGINT",(async(e,t)=>{W(4,`The ${e} event with code: ${t}.`),await vt(0)})),process.on("SIGTERM",(async(e,t)=>{W(4,`The ${e} event with code: ${t}.`),await vt(0)})),process.on("SIGHUP",(async(e,t)=>{W(4,`The ${e} event with code: ${t}.`),await vt(0)})),process.on("uncaughtException",(async(e,t)=>{V(1,e,`The ${t} error.`),await vt(1)}))),await me(e),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 Fe(e,(async(e,t)=>{if(e)throw e;const{outfile:r,type:o}=t.options.export;n(r||`chart.${o}`,"svg"!==o?Buffer.from(t.result,"base64"):t.result),await He()}))},batchExport:async e=>{const t=[];for(let r of e.export.batch.split(";"))r=r.split("="),2===r.length&&t.push(Fe({...e,export:{...e.export,infile:r[0],outfile:r[1]}},((e,t)=>{if(e)throw e;n(t.options.export.outfile,"svg"!==t.options.export.type?Buffer.from(t.result,"base64"):t.result)})));try{await Promise.all(t),await He()}catch(e){throw new pe("[chart] Error encountered during batch export.").setError(e)}},startExport:Fe,initPool:Pe,killPool:He,setOptions:(e,t)=>(t?.length&&(oe=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(i(r))}catch(e){V(2,e,`[config] Unable to load the configuration from the ${r} file.`)}}return{}}(t)),ne(_,oe),oe=ae(_),e&&(oe=se(oe,e,I)),t?.length&&(oe=function(e,t,r){let o=!1;for(let i=0;i(n.length-1===r&&(a=e[t].type),e[t])),r),n.reduce(((e,r,l)=>(n.length-1===l&&void 0!==e[r]&&(t[++i]?"boolean"===a?e[r]=ee(t[i]):"number"===a?e[r]=+t[i]:a.indexOf("]")>=0?e[r]=t[i].split(","):e[r]=t[i]:(W(2,`[config] Missing value for the '${s}' argument. Using the default value.`),o=!0)),e[r])),e)}o&&Z();return e}(oe,t,_)),oe),shutdownCleanUp:vt,log:W,logWithStack:V,setLogLevel:q,enableFileLogging:B,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 r={};t(e)&&(r=JSON.parse(i(e,"utf8")));const o=Object.keys(k).map((e=>({title:`${e} options`,value:e})));return h({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(t,o)=>{let i=0,n=[];for(const e of o)k[e]=k[e].map((t=>({...t,section:e}))),n=[...n,...k[e]];return await h(n,{onSubmit:async(t,o)=>{if("moduleScripts"===t.name?(o=o.length?o.map((e=>t.choices[e])):t.choices,r[t.section][t.name]=o):r[t.section]=le(Object.assign({},r[t.section]||{}),t.name.split("."),t.choices?t.choices[o]:o),++i===n.length){try{await s.writeFile(e,JSON.stringify(r,null,2),"utf8")}catch(t){V(1,t,`[config] An error occurred while creating the ${e} file.`)}return!0}}}),!0}})},printLogo:e=>{const t=JSON.parse(i(l(X,"package.json"))).version;e?console.log(`Starting Highcharts Export Server v${t}...`):console.log(i(X+"/msg/startup.msg").toString().bold.yellow,`v${t}\n`.bold)},printUsage:Z};export{yt as default}; +import"colors";import{existsSync as e,mkdirSync as t,appendFile as r,readFileSync as o,promises as i,writeFileSync as s}from"fs";import n,{join as a,posix as l}from"path";import{HttpsProxyAgent as c}from"https-proxy-agent";import p from"prompts";import h from"dotenv";import{z as u}from"zod";import{fileURLToPath as d}from"url";import g from"http";import m from"https";import{Pool as f}from"tarn";import{v4 as v}from"uuid";import y from"puppeteer";import{JSDOM as b}from"jsdom";import w from"dompurify";import E from"cors";import T from"express";import S from"multer";import x from"express-rate-limit";const R={core:["highcharts","highcharts-more","highcharts-3d"],modules:["stock","map","gantt","exporting","export-data","parallel-coordinates","accessibility","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","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"],indicators:["indicators-all"]},L={puppeteer:{args:{value:["--allow-running-insecure-content","--ash-no-nudges","--autoplay-policy=user-gesture-required","--block-new-web-contents","--disable-accelerated-2d-canvas","--disable-background-networking","--disable-background-timer-throttling","--disable-backgrounding-occluded-windows","--disable-breakpad","--disable-checker-imaging","--disable-client-side-phishing-detection","--disable-component-extensions-with-background-pages","--disable-component-update","--disable-default-apps","--disable-dev-shm-usage","--disable-domain-reliability","--disable-extensions","--disable-features=CalculateNativeWinOcclusion,InterestFeedContentSuggestions,WebOTP","--disable-hang-monitor","--disable-ipc-flooding-protection","--disable-logging","--disable-notifications","--disable-offer-store-unmasked-wallet-cards","--disable-popup-blocking","--disable-print-preview","--disable-prompt-on-repost","--disable-renderer-backgrounding","--disable-search-engine-choice-screen","--disable-session-crashed-bubble","--disable-setuid-sandbox","--disable-site-isolation-trials","--disable-speech-api","--disable-sync","--enable-unsafe-webgpu","--hide-crash-restore-bubble","--hide-scrollbars","--metrics-recording-only","--mute-audio","--no-default-browser-check","--no-first-run","--no-pings","--no-sandbox","--no-startup-window","--no-zygote","--password-store=basic","--process-per-tab","--use-mock-keychain"],type:"string[]",description:"Arguments array to send to Puppeteer."}},highcharts:{version:{value:"latest",type:"string",envLink:"HIGHCHARTS_VERSION",description:"The Highcharts version to be used."},cdnURL:{value:"https://code.highcharts.com/",type:"string",envLink:"HIGHCHARTS_CDN_URL",description:"The CDN URL for Highcharts scripts to be used."},coreScripts:{value:R.core,type:"string[]",envLink:"HIGHCHARTS_CORE_SCRIPTS",description:"The core Highcharts scripts to fetch."},moduleScripts:{value:R.modules,type:"string[]",envLink:"HIGHCHARTS_MODULE_SCRIPTS",description:"The modules of Highcharts to fetch."},indicatorScripts:{value:R.indicators,type:"string[]",envLink:"HIGHCHARTS_INDICATOR_SCRIPTS",description:"The indicators of Highcharts to fetch."},customScripts:{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 custom scripts or dependencies to fetch."},forceFetch:{value:!1,type:"boolean",envLink:"HIGHCHARTS_FORCE_FETCH",description:"The flag to determine whether to refetch all scripts after each server rerun."},cachePath:{value:".cache",type:"string",envLink:"HIGHCHARTS_CACHE_PATH",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:{value:"png",type:"string",envLink:"EXPORT_TYPE",description:"The file export format. It can be jpeg, png, pdf, or svg."},constr:{value:"chart",type:"string",envLink:"EXPORT_CONSTR",description:"The constructor to use. Can be chart, stockChart, mapChart, or ganttChart."},defaultHeight:{value:400,type:"number",envLink:"EXPORT_DEFAULT_HEIGHT",description:"the default height of the exported chart. Used when no value is set."},defaultWidth:{value:600,type:"number",envLink:"EXPORT_DEFAULT_WIDTH",description:"The default width of the exported chart. Used when no value is set."},defaultScale:{value:1,type:"number",envLink:"EXPORT_DEFAULT_SCALE",description:"The default scale of the exported chart. Used when no value is set."},height:{value:!1,type:"number",description:"The height of the exported chart, overriding the option in the chart settings."},width:{value:!1,type:"number",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:{value:1500,type:"number",envLink:"EXPORT_RASTERIZATION_TIMEOUT",description:"The duration in milliseconds to wait for rendering a webpage."}},customLogic:{allowCodeExecution:{value:!1,type:"boolean",envLink:"CUSTOM_LOGIC_ALLOW_CODE_EXECUTION",description:"Controls whether the execution of arbitrary code is allowed during the exporting process."},allowFileResources:{value:!1,type:"boolean",envLink:"CUSTOM_LOGIC_ALLOW_FILE_RESOURCES",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:{value:!1,type:"boolean",envLink:"SERVER_ENABLE",cliName:"enableServer",description:"When set to true, the server starts on the local IP address 0.0.0.0."},host:{value:"0.0.0.0",type:"string",envLink:"SERVER_HOST",description:"The hostname of the server. Additionally, it starts a server on the provided hostname."},port:{value:7801,type:"number",envLink:"SERVER_PORT",description:"The server port when enabled."},benchmarking:{value:!1,type:"boolean",envLink:"SERVER_BENCHMARKING",cliName:"serverBenchmarking",description:"Indicates whether to display the duration, in milliseconds, of specific actions that occur on the server while serving a request."},proxy:{host:{value:!1,type:"string",envLink:"SERVER_PROXY_HOST",cliName:"proxyHost",description:"The host of the proxy server to use, if it exists."},port:{value:8080,type:"number",envLink:"SERVER_PROXY_PORT",cliName:"proxyPort",description:"The port of the proxy server to use, if it exists."},timeout:{value:5e3,type:"number",envLink:"SERVER_PROXY_TIMEOUT",cliName:"proxyTimeout",description:"The timeout for the proxy server to use, if it exists."}},rateLimiting:{enable:{value:!1,type:"boolean",envLink:"SERVER_RATE_LIMITING_ENABLE",cliName:"enableRateLimiting",description:"Enables rate limiting for the server."},maxRequests:{value:10,type:"number",envLink:"SERVER_RATE_LIMITING_MAX_REQUESTS",legacyName:"rateLimit",description:"The maximum number of requests allowed in one minute."},window:{value:1,type:"number",envLink:"SERVER_RATE_LIMITING_WINDOW",description:"The time window, in minutes, for the rate limiting."},delay:{value:0,type:"number",envLink:"SERVER_RATE_LIMITING_DELAY",description:"The delay duration for each successive request before reaching the maximum limit."},trustProxy:{value:!1,type:"boolean",envLink:"SERVER_RATE_LIMITING_TRUST_PROXY",description:"Set this to true if the server is behind a load balancer."},skipKey:{value:!1,type:"string",envLink:"SERVER_RATE_LIMITING_SKIP_KEY",description:"Allows bypassing the rate limiter and should be provided with the skipToken argument."},skipToken:{value:!1,type:"string",envLink:"SERVER_RATE_LIMITING_SKIP_TOKEN",description:"Allows bypassing the rate limiter and should be provided with the skipKey argument."}},ssl:{enable:{value:!1,type:"boolean",envLink:"SERVER_SSL_ENABLE",cliName:"enableSsl",description:"Enables or disables the SSL protocol."},force:{value:!1,type:"boolean",envLink:"SERVER_SSL_FORCE",cliName:"sslForce",legacyName:"sslOnly",description:"When set to true, the server is forced to serve only over HTTPS."},port:{value:443,type:"number",envLink:"SERVER_SSL_PORT",cliName:"sslPort",description:"The port on which to run the SSL server."},certPath:{value:!1,type:"string",envLink:"SERVER_SSL_CERT_PATH",legacyName:"sslPath",description:"The path to the SSL certificate/key file."}}},pool:{minWorkers:{value:4,type:"number",envLink:"POOL_MIN_WORKERS",description:"The number of minimum and initial pool workers to spawn."},maxWorkers:{value:8,type:"number",envLink:"POOL_MAX_WORKERS",legacyName:"workers",description:"The number of maximum pool workers to spawn."},workLimit:{value:40,type:"number",envLink:"POOL_WORK_LIMIT",description:"The number of work pieces that can be performed before restarting the worker process."},acquireTimeout:{value:5e3,type:"number",envLink:"POOL_ACQUIRE_TIMEOUT",description:"The duration, in milliseconds, to wait for acquiring a resource."},createTimeout:{value:5e3,type:"number",envLink:"POOL_CREATE_TIMEOUT",description:"The duration, in milliseconds, to wait for creating a resource."},destroyTimeout:{value:5e3,type:"number",envLink:"POOL_DESTROY_TIMEOUT",description:"The duration, in milliseconds, to wait for destroying a resource."},idleTimeout:{value:3e4,type:"number",envLink:"POOL_IDLE_TIMEOUT",description:"The duration, in milliseconds, after which an idle resource is destroyed."},createRetryInterval:{value:200,type:"number",envLink:"POOL_CREATE_RETRY_INTERVAL",description:"The duration, in milliseconds, to wait before retrying the create process in case of a failure."},reaperInterval:{value:1e3,type:"number",envLink:"POOL_REAPER_INTERVAL",description:"The duration, in milliseconds, after which the check for idle resources to destroy is triggered."},benchmarking:{value:!1,type:"boolean",envLink:"POOL_BENCHMARKING",cliName:"poolBenchmarking",description:"Indicate whether to show statistics for the pool of resources or not."}},logging:{level:{value:4,type:"number",envLink:"LOGGING_LEVEL",cliName:"logLevel",description:"The logging level to be used."},file:{value:"highcharts-export-server.log",type:"string",envLink:"LOGGING_FILE",cliName:"logFile",description:"The name of a log file. The logDest option also needs to be set to enable file logging."},dest:{value:"log/",type:"string",envLink:"LOGGING_DEST",cliName:"logDest",description:"The path to store log files. This also enables file logging."}},ui:{enable:{value:!1,type:"boolean",envLink:"UI_ENABLE",cliName:"enableUi",description:"Enables or disables the user interface (UI) for the export server."},route:{value:"/",type:"string",envLink:"UI_ROUTE",cliName:"uiRoute",description:"The endpoint route to which the user interface (UI) should be attached."}},other:{nodeEnv:{value:"production",type:"string",envLink:"OTHER_NODE_ENV",description:"The type of Node.js environment."},listenToProcessExits:{value:!0,type:"boolean",envLink:"OTHER_LISTEN_TO_PROCESS_EXITS",description:"Decides whether or not to attach process.exit handlers."},noLogo:{value:!1,type:"boolean",envLink:"OTHER_NO_LOGO",description:"Skip printing the logo on a startup. Will be replaced by a simple text."},hardResetPage:{value:!1,type:"boolean",envLink:"OTHER_HARD_RESET_PAGE",description:"Decides if the page content should be reset entirely."}},debug:{enable:{value:!1,type:"boolean",envLink:"DEBUG_ENABLE",cliName:"enableDebug",description:"Enables or disables debug mode for the underlying browser."},headless:{value:!0,type:"boolean",envLink:"DEBUG_HEADLESS",description:"Controls the mode in which the browser is launched when in the debug mode."},devtools:{value:!1,type:"boolean",envLink:"DEBUG_DEVTOOLS",description:"Decides whether to enable DevTools when the browser is in a headful state."},listenToConsole:{value:!1,type:"boolean",envLink:"DEBUG_LISTEN_TO_CONSOLE",description:"Decides whether to enable a listener for console messages sent from the browser."},dumpio:{value:!1,type:"boolean",envLink:"DEBUG_DUMPIO",description:"Redirects browser process stdout and stderr to process.stdout and process.stderr."},slowMo:{value:0,type:"number",envLink:"DEBUG_SLOW_MO",description:"Slows down Puppeteer operations by the specified number of milliseconds."},debuggingPort:{value:9222,type:"number",envLink:"DEBUG_DEBUGGING_PORT",description:"Specifies the debugging port."}}},O={puppeteer:[{type:"list",name:"args",message:"Puppeteer arguments",initial:L.puppeteer.args.value.join(","),separator:","}],highcharts:[{type:"text",name:"version",message:"Highcharts version",initial:L.highcharts.version.value},{type:"text",name:"cdnURL",message:"The URL of CDN",initial:L.highcharts.cdnURL.value},{type:"multiselect",name:"coreScripts",message:"Available core scripts",instructions:"Space: Select specific, A: Select all, Enter: Confirm.",choices:L.highcharts.coreScripts.value},{type:"multiselect",name:"moduleScripts",message:"Available module scripts",instructions:"Space: Select specific, A: Select all, Enter: Confirm.",choices:L.highcharts.moduleScripts.value},{type:"multiselect",name:"indicatorScripts",message:"Available indicator scripts",instructions:"Space: Select specific, A: Select all, Enter: Confirm.",choices:L.highcharts.indicatorScripts.value},{type:"list",name:"customScripts",message:"Custom scripts",initial:L.highcharts.customScripts.value.join(","),separator:","},{type:"toggle",name:"forceFetch",message:"Force re-fetch the scripts",initial:L.highcharts.forceFetch.value},{type:"text",name:"cachePath",message:"The path to the cache directory",initial:L.highcharts.cachePath.value}],export:[{type:"select",name:"type",message:"The default export file type",hint:`Default: ${L.export.type.value}`,initial:0,choices:["png","jpeg","pdf","svg"]},{type:"select",name:"constr",message:"The default constructor for Highcharts",hint:`Default: ${L.export.constr.value}`,initial:0,choices:["chart","stockChart","mapChart","ganttChart"]},{type:"number",name:"defaultHeight",message:"The default fallback height of the exported chart",initial:L.export.defaultHeight.value},{type:"number",name:"defaultWidth",message:"The default fallback width of the exported chart",initial:L.export.defaultWidth.value},{type:"number",name:"defaultScale",message:"The default fallback scale of the exported chart",initial:L.export.defaultScale.value,min:.1,max:5},{type:"number",name:"rasterizationTimeout",message:"The rendering webpage timeout in milliseconds",initial:L.export.rasterizationTimeout.value}],customLogic:[{type:"toggle",name:"allowCodeExecution",message:"Enable execution of custom code",initial:L.customLogic.allowCodeExecution.value},{type:"toggle",name:"allowFileResources",message:"Enable file resources",initial:L.customLogic.allowFileResources.value}],server:[{type:"toggle",name:"enable",message:"Starts the server on 0.0.0.0",initial:L.server.enable.value},{type:"text",name:"host",message:"Server hostname",initial:L.server.host.value},{type:"number",name:"port",message:"Server port",initial:L.server.port.value},{type:"toggle",name:"benchmarking",message:"Enable server benchmarking",initial:L.server.benchmarking.value},{type:"text",name:"proxy.host",message:"The host of the proxy server to use",initial:L.server.proxy.host.value},{type:"number",name:"proxy.port",message:"The port of the proxy server to use",initial:L.server.proxy.port.value},{type:"number",name:"proxy.timeout",message:"The timeout for the proxy server to use",initial:L.server.proxy.timeout.value},{type:"toggle",name:"rateLimiting.enable",message:"Enable rate limiting",initial:L.server.rateLimiting.enable.value},{type:"number",name:"rateLimiting.maxRequests",message:"The maximum requests allowed per minute",initial:L.server.rateLimiting.maxRequests.value},{type:"number",name:"rateLimiting.window",message:"The rate-limiting time window in minutes",initial:L.server.rateLimiting.window.value},{type:"number",name:"rateLimiting.delay",message:"The delay for each successive request before reaching the maximum",initial:L.server.rateLimiting.delay.value},{type:"toggle",name:"rateLimiting.trustProxy",message:"Set to true if behind a load balancer",initial:L.server.rateLimiting.trustProxy.value},{type:"text",name:"rateLimiting.skipKey",message:"Allows bypassing the rate limiter when provided with the skipToken argument",initial:L.server.rateLimiting.skipKey.value},{type:"text",name:"rateLimiting.skipToken",message:"Allows bypassing the rate limiter when provided with the skipKey argument",initial:L.server.rateLimiting.skipToken.value},{type:"toggle",name:"ssl.enable",message:"Enable SSL protocol",initial:L.server.ssl.enable.value},{type:"toggle",name:"ssl.force",message:"Force serving only over HTTPS",initial:L.server.ssl.force.value},{type:"number",name:"ssl.port",message:"SSL server port",initial:L.server.ssl.port.value},{type:"text",name:"ssl.certPath",message:"The path to find the SSL certificate/key",initial:L.server.ssl.certPath.value}],pool:[{type:"number",name:"minWorkers",message:"The initial number of workers to spawn",initial:L.pool.minWorkers.value},{type:"number",name:"maxWorkers",message:"The maximum number of workers to spawn",initial:L.pool.maxWorkers.value},{type:"number",name:"workLimit",message:"The pieces of work that can be performed before restarting a Puppeteer process",initial:L.pool.workLimit.value},{type:"number",name:"acquireTimeout",message:"The number of milliseconds to wait for acquiring a resource",initial:L.pool.acquireTimeout.value},{type:"number",name:"createTimeout",message:"The number of milliseconds to wait for creating a resource",initial:L.pool.createTimeout.value},{type:"number",name:"destroyTimeout",message:"The number of milliseconds to wait for destroying a resource",initial:L.pool.destroyTimeout.value},{type:"number",name:"idleTimeout",message:"The number of milliseconds after an idle resource is destroyed",initial:L.pool.idleTimeout.value},{type:"number",name:"createRetryInterval",message:"The retry interval in milliseconds after a create process fails",initial:L.pool.createRetryInterval.value},{type:"number",name:"reaperInterval",message:"The reaper interval in milliseconds after triggering the check for idle resources to destroy",initial:L.pool.reaperInterval.value},{type:"toggle",name:"benchmarking",message:"Enable benchmarking for a resource pool",initial:L.pool.benchmarking.value}],logging:[{type:"number",name:"level",message:"The log level (0: silent, 1: error, 2: warning, 3: notice, 4: verbose, 5: benchmark)",initial:L.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:L.logging.file.value},{type:"text",name:"dest",message:"The path to log files. Enables file logging",initial:L.logging.dest.value}],ui:[{type:"toggle",name:"enable",message:"Enable UI for the export server",initial:L.ui.enable.value},{type:"text",name:"route",message:"A route to attach the UI",initial:L.ui.route.value}],other:[{type:"text",name:"nodeEnv",message:"The type of Node.js environment",initial:L.other.nodeEnv.value},{type:"toggle",name:"listenToProcessExits",message:"Set to false to skip attaching process.exit handlers",initial:L.other.listenToProcessExits.value},{type:"toggle",name:"noLogo",message:"Skip printing the logo on startup. Replaced by simple text",initial:L.other.noLogo.value},{type:"toggle",name:"hardResetPage",message:"Decides if the page content should be reset entirely",initial:L.other.hardResetPage.value}],debug:[{type:"toggle",name:"enable",message:"Enables debug mode for the browser instance",initial:L.debug.enable.value},{type:"toggle",name:"headless",message:"The mode setting for the browser",initial:L.debug.headless.value},{type:"toggle",name:"devtools",message:"The DevTools for the headful browser",initial:L.debug.devtools.value},{type:"toggle",name:"listenToConsole",message:"The event listener for console messages from the browser",initial:L.debug.listenToConsole.value},{type:"toggle",name:"dumpio",message:"Redirects the browser stdout and stderr to NodeJS process",initial:L.debug.dumpio.value},{type:"number",name:"slowMo",message:"Puppeteer operations slow down in milliseconds",initial:L.debug.slowMo.value},{type:"number",name:"debuggingPort",message:"The port number for debugging",initial:L.debug.debuggingPort.value}]},_=["options","globalOptions","themeOptions","resources","payload"],k={},I=(e,t="")=>{Object.keys(e).forEach((r=>{if(!["puppeteer","highcharts"].includes(r)){const o=e[r];void 0===o.value?I(o,`${t}.${r}`):(k[o.cliName||r]=`${t}.${r}`.substring(1),void 0!==o.legacyName&&(k[o.legacyName]=`${t}.${r}`.substring(1)))}}))};I(L),h.config();const C=e=>u.string().transform((t=>t.split(",").map((e=>e.trim())).filter((t=>e.includes(t))))).transform((e=>e.length?e:void 0)),A=()=>u.enum(["true","false",""]).transform((e=>""!==e?"true"===e:void 0)),N=e=>u.enum([...e,""]).transform((e=>""!==e?e:void 0)),P=()=>u.string().trim().refine((e=>!["false","undefined","null","NaN"].includes(e)||""===e),(e=>({message:`The string contains forbidden values, received '${e}'`}))).transform((e=>""!==e?e:void 0)),H=()=>u.string().trim().refine((e=>""===e||!isNaN(parseFloat(e))&&parseFloat(e)>0),(e=>({message:`The value must be numeric and positive, received '${e}'`}))).transform((e=>""!==e?parseFloat(e):void 0)),$=()=>u.string().trim().refine((e=>""===e||!isNaN(parseFloat(e))&&parseFloat(e)>=0),(e=>({message:`The value must be numeric and non-negative, received '${e}'`}))).transform((e=>""!==e?parseFloat(e):void 0)),D=u.object({HIGHCHARTS_VERSION:u.string().trim().refine((e=>/^(latest|\d+(\.\d+){0,2})$/.test(e)||""===e),(e=>({message:`HIGHCHARTS_VERSION must be 'latest', a major version, or in the form XX.YY.ZZ, received '${e}'`}))).transform((e=>""!==e?e:void 0)),HIGHCHARTS_CDN_URL:u.string().trim().refine((e=>e.startsWith("https://")||e.startsWith("http://")||""===e),(e=>({message:`Invalid value for HIGHCHARTS_CDN_URL. It should start with http:// or https://, received '${e}'`}))).transform((e=>""!==e?e:void 0)),HIGHCHARTS_CORE_SCRIPTS:C(R.core),HIGHCHARTS_MODULE_SCRIPTS:C(R.modules),HIGHCHARTS_INDICATOR_SCRIPTS:C(R.indicators),HIGHCHARTS_FORCE_FETCH:A(),HIGHCHARTS_CACHE_PATH:P(),HIGHCHARTS_ADMIN_TOKEN:P(),EXPORT_TYPE:N(["jpeg","png","pdf","svg"]),EXPORT_CONSTR:N(["chart","stockChart","mapChart","ganttChart"]),EXPORT_DEFAULT_HEIGHT:H(),EXPORT_DEFAULT_WIDTH:H(),EXPORT_DEFAULT_SCALE:H(),EXPORT_RASTERIZATION_TIMEOUT:$(),CUSTOM_LOGIC_ALLOW_CODE_EXECUTION:A(),CUSTOM_LOGIC_ALLOW_FILE_RESOURCES:A(),SERVER_ENABLE:A(),SERVER_HOST:P(),SERVER_PORT:H(),SERVER_BENCHMARKING:A(),SERVER_PROXY_HOST:P(),SERVER_PROXY_PORT:H(),SERVER_PROXY_TIMEOUT:$(),SERVER_RATE_LIMITING_ENABLE:A(),SERVER_RATE_LIMITING_MAX_REQUESTS:$(),SERVER_RATE_LIMITING_WINDOW:$(),SERVER_RATE_LIMITING_DELAY:$(),SERVER_RATE_LIMITING_TRUST_PROXY:A(),SERVER_RATE_LIMITING_SKIP_KEY:P(),SERVER_RATE_LIMITING_SKIP_TOKEN:P(),SERVER_SSL_ENABLE:A(),SERVER_SSL_FORCE:A(),SERVER_SSL_PORT:H(),SERVER_SSL_CERT_PATH:P(),POOL_MIN_WORKERS:$(),POOL_MAX_WORKERS:$(),POOL_WORK_LIMIT:H(),POOL_ACQUIRE_TIMEOUT:$(),POOL_CREATE_TIMEOUT:$(),POOL_DESTROY_TIMEOUT:$(),POOL_IDLE_TIMEOUT:$(),POOL_CREATE_RETRY_INTERVAL:$(),POOL_REAPER_INTERVAL:$(),POOL_BENCHMARKING:A(),LOGGING_LEVEL:u.string().trim().refine((e=>""===e||!isNaN(parseFloat(e))&&parseFloat(e)>=0&&parseFloat(e)<=5),(e=>({message:`Invalid value for LOGGING_LEVEL. We only accept values from 0 to 5 as logging levels, received '${e}'`}))).transform((e=>""!==e?parseFloat(e):void 0)),LOGGING_FILE:P(),LOGGING_DEST:P(),UI_ENABLE:A(),UI_ROUTE:P(),OTHER_NODE_ENV:N(["development","production","test"]),OTHER_LISTEN_TO_PROCESS_EXITS:A(),OTHER_NO_LOGO:A(),OTHER_HARD_RESET_PAGE:A(),DEBUG_ENABLE:A(),DEBUG_HEADLESS:A(),DEBUG_DEVTOOLS:A(),DEBUG_LISTEN_TO_CONSOLE:A(),DEBUG_DUMPIO:A(),DEBUG_SLOW_MO:$(),DEBUG_DEBUGGING_PORT:H()}).partial().parse(process.env),U=["red","yellow","blue","gray","green"];let G={toConsole:!0,toFile:!1,pathCreated:!1,levelsDesc:[{title:"error",color:U[0]},{title:"warning",color:U[1]},{title:"notice",color:U[2]},{title:"verbose",color:U[3]},{title:"benchmark",color:U[4]}],listeners:[]};for(const[e,t]of Object.entries(L.logging))G[e]=t.value;const j=(o,i)=>{G.toFile&&(G.pathCreated||(!e(G.dest)&&t(G.dest),G.pathCreated=!0),r(`${G.dest}${G.file}`,[i].concat(o).join(" ")+"\n",(e=>{e&&(console.log(`[logger] Unable to write to log file: ${e}`),G.toFile=!1)})))},M=(...e)=>{const[t,...r]=e,{level:o,levelsDesc:i}=G;if(5!==t&&(0===t||t>o||o>i.length))return;const s=`${(new Date).toString().split("(")[0].trim()} [${i[t-1].title}] -`;G.listeners.forEach((e=>{e(s,r.join(" "))})),G.toConsole&&console.log.apply(void 0,[s.toString()[G.levelsDesc[t-1].color]].concat(r)),j(r,s)},F=(e,t,r)=>{const o=r||t.message,{level:i,levelsDesc:s}=G;if(0===e||e>i||i>s.length)return;const n=`${(new Date).toString().split("(")[0].trim()} [${s[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];G.toConsole&&console.log.apply(void 0,[n.toString()[G.levelsDesc[e-1].color]].concat([o[U[e-1]],"\n",a])),G.listeners.forEach((e=>{e(n,l.join(" "))})),j(l,n)},W=e=>{e>=0&&e<=G.levelsDesc.length&&(G.level=e)},V=(e,t)=>{if(G={...G,dest:e||G.dest,file:t||G.file,toFile:!0},0===G.dest.length)return M(1,"[logger] File logging initialization: no path supplied.");G.dest.endsWith("/")||(G.dest+="/")},q=d(new URL("../.",import.meta.url)),B=(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"},X=(e=!1,t)=>{const r=["js","css","files"];let i=e,s=!1;if(t&&e.endsWith(".json"))try{i=K(o(e,"utf8"))}catch(e){return F(2,e,"[cli] No resources found.")}else i=K(e),i&&!t&&delete i.files;for(const e in i)r.includes(e)?s||(s=!0):delete i[e];return s?(i.files&&(i.files=i.files.map((e=>e.trim())),(!i.files||i.files.length<=0)&&delete i.files),i):M(3,"[cli] No resources found.")};function K(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 J=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]=J(e[r]));return t},z=(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 Y(){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(L).forEach((t=>{["puppeteer","highcharts"].includes(t)||(console.log(`\n${t.toUpperCase()}`.red),e(L[t]))})),console.log("\n")}const Q=e=>!["false","undefined","null","NaN","0",""].includes(e)&&!!e,Z=(e,t)=>{if(e&&"string"==typeof e)return(e=e.trim()).endsWith(".js")?!!t&&Z(o(e,"utf8")):e.startsWith("function()")||e.startsWith("function ()")||e.startsWith("()=>")||e.startsWith("() =>")?`(${e})()`:e.replace(/;$/,"")},ee=()=>{const e=process.hrtime.bigint();return()=>Number(process.hrtime.bigint()-e)/1e6};let te={};const re=()=>te,oe=(e,t,r=[])=>{const o=J(e);for(const[e,s]of Object.entries(t))o[e]="object"!=typeof(i=s)||Array.isArray(i)||null===i||r.includes(e)||void 0===o[e]?void 0!==s?s:o[e]:oe(o[e],s,r);var i;return o};function ie(e,t={},r=""){Object.keys(e).forEach((o=>{const i=e[o],s=t&&t[o];void 0===i.value?ie(i,s,`${r}.${o}`):(void 0!==s&&(i.value=s),i.envLink in D&&void 0!==D[i.envLink]&&(i.value=D[i.envLink]))}))}function se(e){let t={};for(const[r,o]of Object.entries(e))t[r]=Object.prototype.hasOwnProperty.call(o,"value")?o.value:se(o);return t}function ne(e,t,r){for(;t.length>1;){const o=t.shift();return Object.prototype.hasOwnProperty.call(e,o)||(e[o]={}),e[o]=ne(Object.assign({},e[o]),t,r),e}return e[t[0]]=r,e}async function ae(e,t={}){return new Promise(((r,o)=>{const i=(e=>e.startsWith("https")?m:g)(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)}))}))}class le 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 ce={cdnURL:"https://code.highcharts.com/",activeManifest:{},sources:"",hcVersion:""},pe=e=>e.sources.substring(0,e.sources.indexOf("*/")).replace("/*","").replace("*/","").replace(/\n/g,"").trim(),he=async(e,t,r,o=!1)=>{e.endsWith(".js")&&(e=e.substring(0,e.length-3)),M(4,`[cache] Fetching script - ${e}.js`);const i=await ae(`${e}.js`,t);if(200===i.statusCode&&"string"==typeof i.text){if(r){r[e.replace(/(.*)\/|(.*)modules\/|stock\/(.*)indicators\/|maps\/(.*)modules\//gi,"")]=1}return i.text}if(o)throw new le(`Could not fetch the ${e}.js. The script might not exist in the requested version (status code: ${i.statusCode}).`).setError(i);return M(2,`[cache] Could not fetch the ${e}.js. The script might not exist in the requested version.`),""},ue=async(e,t,r)=>{const o=e.version,i="latest"!==o&&o?`${o}/`:"",n=e.cdnURL||ce.cdnURL;M(3,`[cache] Updating cache version to Highcharts: ${i||"latest"}.`);const a={};try{return ce.sources=await(async(e,t,r,o,i)=>{let s;const n=o.host,a=o.port;if(n&&a)try{s=new c({host:n,port:a})}catch(e){throw new le("[cache] Could not create a Proxy Agent.").setError(e)}const l=s?{agent:s,timeout:D.SERVER_PROXY_TIMEOUT}:{},p=[...e.map((e=>he(`${e}`,l,i,!0))),...t.map((e=>he(`${e}`,l,i))),...r.map((e=>he(`${e}`,l)))];return(await Promise.all(p)).join(";\n")})([...e.coreScripts.map((e=>`${n}${i}${e}`))],[...e.moduleScripts.map((e=>"map"===e?`${n}maps/${i}modules/${e}`:`${n}${i}modules/${e}`)),...e.indicatorScripts.map((e=>`${n}stock/${i}indicators/${e}`))],e.customScripts,t,a),ce.hcVersion=pe(ce),s(r,ce.sources),a}catch(e){throw new le("[cache] Unable to update the local Highcharts cache.").setError(e)}},de=async r=>{const{highcharts:i,server:n}=r,l=a(q,i.cachePath);let c;const p=a(l,"manifest.json"),h=a(l,"sources.js");if(!e(l)&&t(l),!e(p)||i.forceFetch)M(3,"[cache] Fetching and caching Highcharts dependencies."),c=await ue(i,n.proxy,h);else{let e=!1;const t=JSON.parse(o(p));if(t.modules&&Array.isArray(t.modules)){const e={};t.modules.forEach((t=>e[t]=1)),t.modules=e}const{coreScripts:r,moduleScripts:s,indicatorScripts:a}=i,l=r.length+s.length+a.length;t.version!==i.version?(M(2,"[cache] A Highcharts version mismatch in the cache, need to re-fetch."),e=!0):Object.keys(t.modules||{}).length!==l?(M(2,"[cache] The cache and the requested modules do not match, need to re-fetch."),e=!0):e=(s||[]).some((e=>{if(!t.modules[e])return M(2,`[cache] The ${e} is missing in the cache, need to re-fetch.`),!0})),e?c=await ue(i,n.proxy,h):(M(3,"[cache] Dependency cache is up to date, proceeding."),ce.sources=o(h,"utf8"),c=t.modules,ce.hcVersion=pe(ce))}await(async(e,t)=>{const r={version:e.version,modules:t||{}};ce.activeManifest=r,M(3,"[cache] Writing a new manifest.");try{s(a(q,e.cachePath,"manifest.json"),JSON.stringify(r),"utf8")}catch(e){throw new le("[cache] Error writing the cache manifest.").setError(e)}})(i,c)},ge=()=>a(q,re().highcharts.cachePath),me=()=>ce.hcVersion;function fe(){Highcharts.animObject=function(){return{duration:0}}}async function ve(e,t,r){window._displayErrors=r;const{getOptions:o,merge:i,setOptions:s,wrap:n}=Highcharts;Highcharts.setOptionsObj=i(!1,{},o()),t.customLogic.customCode&&new Function(t.customLogic.customCode)();const a={animation:!1};t.export.strInj&&(a.height=e.chart.height,a.width=e.chart.width),window.isRenderComplete=!1,n(Highcharts.Chart.prototype,"init",(function(e,t,r){((t=i(t,{exporting:{enabled:!1},plotOptions:{series:{label:{enabled:!1}}},tooltip:{}})).series||[]).forEach((function(e){e.animation=!1})),window.onHighchartsRender||(window.onHighchartsRender=Highcharts.addEvent(this,"render",(()=>{window.isRenderComplete=!0}))),e.apply(this,[t,r])})),n(Highcharts.Series.prototype,"init",(function(e,t,r){e.apply(this,[t,r])}));const l=t.export.strInj?new Function(`return ${t.export.strInj}`)():e,c=i(!1,JSON.parse(t.export.themeOptions),l,{chart:a}),p=t.customLogic.callback?new Function(`return ${t.customLogic.callback}`)():void 0,h=JSON.parse(t.export.globalOptions);h&&s(h),Highcharts[t.export.constr||"chart"]("container",c,p);const u=o();for(const e in u)"function"!=typeof u[e]&&delete u[e];s(Highcharts.setOptionsObj),Highcharts.setOptionsObj={}}const ye=o(q+"/templates/template.html","utf8");let be;async function we(){if(!be)return!1;const e=await be.newPage();return await e.setCacheEnabled(!1),await Te(e),function(e){const{debug:t}=re();t.enable&&t.listenToConsole&&e.on("console",(e=>{console.log(`[debug] ${e.text()}`)}));e.on("pageerror",(async t=>{await e.$eval("#container",((e,t)=>{window._displayErrors&&(e.innerHTML=t)}),`

Chart input data error:

${t.toString()}`)}))}(e),e}async function Ee(e,t){for(const e of t)await e.dispose();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()}const[...e]=document.getElementsByTagName("script"),[,...t]=document.getElementsByTagName("style"),[...r]=document.getElementsByTagName("link");for(const o of[...e,...t,...r])o.remove()}))}async function Te(e){await e.setContent(ye,{waitUntil:"domcontentloaded"}),await e.addScriptTag({path:`${ge()}/sources.js`}),await e.evaluate(fe)}const Se=async(e,t,r,o)=>e.evaluate(ve,t,r,o);var xe=async(e,t,r)=>{let i=[];try{M(4,"[export] Determining export path.");const s=r.export,a=s?.options?.chart?.displayErrors&&ce.activeManifest.modules.debugger;let l;if(t.indexOf&&(t.indexOf("=0||t.indexOf("=0)){if(M(4,"[export] Treating as SVG."),"svg"===s.type)return t;l=!0,await e.setContent((e=>`\n\n\n \n \n Highcharts Export\n \n \n \n
\n ${e}\n
\n \n\n\n`)(t),{waitUntil:"domcontentloaded"})}else M(4,"[export] Treating as config."),s.strInj?await Se(e,{chart:{height:s.height,width:s.width}},r,a):(t.chart.height=s.height,t.chart.width=s.width,await Se(e,t,r,a));i=await async function(e,t){const r=[],i=t.customLogic.resources;if(i){const s=[];if(i.js&&s.push({content:i.js}),i.files)for(const e of i.files){const t=!e.startsWith("http");s.push(t?{content:o(e,"utf8")}:{url:e})}for(const t of s)try{r.push(await e.addScriptTag(t))}catch(e){F(2,e,"[export] The JS resource cannot be loaded.")}s.length=0;const a=[];if(i.css){let o=i.css.match(/@import\s*([^;]*);/g);if(o)for(let e of o)e&&(e=e.replace("url(","").replace("@import","").replace(/"/g,"").replace(/'/g,"").replace(/;/,"").replace(/\)/g,"").trim(),e.startsWith("http")?a.push({url:e}):t.customLogic.allowFileResources&&a.push({path:n.join(q,e)}));a.push({content:i.css.replace(/@import\s*([^;]*);/g,"")||" "});for(const t of a)try{r.push(await e.addStyleTag(t))}catch(e){F(2,e,"[export] The CSS resource cannot be loaded.")}a.length=0}}return r}(e,r);const c=l?await e.evaluate((e=>{const t=document.querySelector("#chart-container svg:first-of-type"),r=t.height.baseVal.value*e,o=t.width.baseVal.value*e;return document.body.style.zoom=e,document.body.style.margin="0px",{chartHeight:r,chartWidth:o}}),parseFloat(s.scale)):await e.evaluate((()=>{const{chartHeight:e,chartWidth:t}=window.Highcharts.charts[0];return document.body.style.zoom=1,{chartHeight:e,chartWidth:t}})),p=Math.ceil(c.chartHeight||s.height),h=Math.ceil(c.chartWidth||s.width),{x:u,y:d}=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 g;if(await e.setViewport({height:p,width:h,deviceScaleFactor:l?1:parseFloat(s.scale)}),"svg"===s.type)g=await(e=>e.$eval("#container svg:first-of-type",(e=>e.outerHTML)))(e);else if(["png","jpeg"].includes(s.type))g=await((e,t,r,o,i)=>Promise.race([e.screenshot({type:t,encoding:r,clip:o,captureBeyondViewport:!0,fullPage:!1,optimizeForSpeed:!0,..."png"!==t?{quality:80}:{},omitBackground:"png"==t}),new Promise(((e,t)=>setTimeout((()=>t(new le("Rasterization timeout"))),i||1500)))]))(e,s.type,"base64",{width:h,height:p,x:u,y:d},s.rasterizationTimeout);else{if("pdf"!==s.type)throw new le(`[export] Unsupported output format ${s.type}.`);g=await(async(e,t,r,o,i)=>(await e.emulateMediaType("screen"),Promise.race([e.pdf({height:t+1,width:r,encoding:o}),new Promise(((e,t)=>setTimeout((()=>t(new le("Rasterization timeout"))),i||1500)))])))(e,p,h,"base64",s.rasterizationTimeout)}return await Ee(e,i),g}catch(t){return await Ee(e,i),t}};let Re=!1;const Le={performedExports:0,exportAttempts:0,exportFromSvgAttempts:0,timeSpent:0,droppedExports:0,spentAverage:0};let Oe={};const _e={create:async()=>{let e=!1;const t=v(),r=(new Date).getTime();try{if(e=await we(),!e||e.isClosed())throw new le("The page is invalid or closed.");M(3,`[pool] Successfully created a worker ${t} - took ${(new Date).getTime()-r} ms.`)}catch(e){throw new le("Error encountered when creating a new page.").setError(e)}return{id:t,page:e,workCount:Math.round(Math.random()*(Oe.workLimit/2))}},validate:async e=>!(Oe.workLimit&&++e.workCount>Oe.workLimit)||(M(3,`[pool] Worker failed validation: exceeded work limit (limit is ${Oe.workLimit}).`),!1),destroy:async e=>{M(3,`[pool] Destroying pool entry ${e.id}.`),e.page&&await e.page.close()}},ke=async e=>{if(Oe=e&&e.pool?{...e.pool}:{},await async function(e){const{enable:t,...r}=re().debug,o={headless:"shell",userDataDir:"./tmp/",args:e,handleSIGINT:!1,handleSIGTERM:!1,handleSIGHUP:!1,waitForInitialPage:!1,defaultViewport:null,...t&&r};if(!be){let e=0;const r=async()=>{try{M(3,`[browser] Attempting to get a browser instance (try ${++e}).`),be=await y.launch(o)}catch(t){if(F(1,t,"[browser] Failed to launch a browser instance."),!(e<25))throw t;M(3,`[browser] Retry to open a browser (${e} out of 25).`),await new Promise((e=>setTimeout(e,4e3))),await r()}};try{await r(),t&&M(3,"[browser] Launched browser in debug mode.")}catch(e){throw new le("[browser] Maximum retries to open a browser instance reached.").setError(e)}if(!be)throw new le("[browser] Cannot find a browser to open.")}return be}(e.puppeteerArgs),M(3,`[pool] Initializing pool with workers: min ${Oe.minWorkers}, max ${Oe.maxWorkers}.`),Re)return M(4,"[pool] Already initialized, please kill it before creating a new one.");parseInt(Oe.minWorkers)>parseInt(Oe.maxWorkers)&&(Oe.minWorkers=Oe.maxWorkers);try{Re=new f({..._e,min:parseInt(Oe.minWorkers),max:parseInt(Oe.maxWorkers),acquireTimeoutMillis:Oe.acquireTimeout,createTimeoutMillis:Oe.createTimeout,destroyTimeoutMillis:Oe.destroyTimeout,idleTimeoutMillis:Oe.idleTimeout,createRetryIntervalMillis:Oe.createRetryInterval,reapIntervalMillis:Oe.reaperInterval,propagateCreateError:!1}),Re.on("release",(async e=>{await async function(e,t=!1){try{e.isClosed()||(t?(await e.goto("about:blank",{waitUntil:"domcontentloaded"}),await Te(e)):await e.evaluate((()=>{document.body.innerHTML='
'})))}catch(e){F(2,e,"[browser] Could not clear the content of the page.")}}(e.page,!1),M(4,`[pool] Releasing a worker with ID ${e.id}.`)})),Re.on("destroySuccess",((e,t)=>{M(4,`[pool] Destroyed a worker with ID ${t.id}.`)}));const e=[];for(let t=0;t{Re.release(e)})),M(3,"[pool] The pool is ready"+(e.length?` with ${e.length} initial resources waiting.`:"."))}catch(e){throw new le("[pool] Could not create the pool of workers.").setError(e)}};async function Ie(){if(M(3,"[pool] Killing pool with all workers and closing browser."),Re){for(const e of Re.used)Re.release(e.resource);Re.destroyed||(await Re.destroy(),M(4,"[browser] Destroyed the pool of resources."))}await async function(){be?.connected&&await be.close(),M(4,"[browser] Closed the browser.")}()}const Ce=async(e,t)=>{let r;try{if(M(4,"[pool] Work received, starting to process."),++Le.exportAttempts,Oe.benchmarking&&Ne(),!Re)throw new le("Work received, but pool has not been started.");const o=ee();try{M(4,"[pool] Acquiring a worker handle."),r=await Re.acquire().promise,t.server.benchmarking&&M(5,t.payload?.requestId?`[benchmark] Request with ID ${t.payload?.requestId} -`:"[benchmark]",`Acquired a worker handle: ${o()}ms.`)}catch(e){throw new le((t.payload?.requestId?`For request with ID ${t.payload?.requestId} - `:"")+`Error encountered when acquiring an available entry: ${o()}ms.`).setError(e)}if(M(4,"[pool] Acquired a worker handle."),!r.page)throw new le("Resolved worker page is invalid: the pool setup is wonky.");let i=(new Date).getTime();M(4,`[pool] Starting work on pool entry with ID ${r.id}.`);const s=ee(),n=await xe(r.page,e,t);if(n instanceof Error)throw"Rasterization timeout"===n.message&&(r.page.close(),r.page=await we()),new le((t.payload?.requestId?`For request with ID ${t.payload?.requestId} - `:"")+`Error encountered during export: ${s()}ms.`).setError(n);t.server.benchmarking&&M(5,t.payload?.requestId?`[benchmark] Request with ID ${t.payload?.requestId} -`:"[benchmark]",`Exported a chart sucessfully: ${s()}ms.`),Re.release(r);const a=(new Date).getTime()-i;return Le.timeSpent+=a,Le.spentAverage=Le.timeSpent/++Le.performedExports,M(4,`[pool] Work completed in ${a} ms.`),{result:n,options:t}}catch(e){throw++Le.droppedExports,r&&Re.release(r),new le(`[pool] In pool.postWork: ${e.message}`).setError(e)}},Ae=()=>({min:Re.min,max:Re.max,all:Re.numFree()+Re.numUsed(),available:Re.numFree(),used:Re.numUsed(),pending:Re.numPendingAcquires()});function Ne(){const{min:e,max:t,all:r,available:o,used:i,pending:s}=Ae();M(5,`[pool] The minimum number of resources allowed by pool: ${e}.`),M(5,`[pool] The maximum number of resources allowed by pool: ${t}.`),M(5,`[pool] The number of all created resources: ${r}.`),M(5,`[pool] The number of available resources: ${o}.`),M(5,`[pool] The number of acquired resources: ${i}.`),M(5,`[pool] The number of resources waiting to be acquired: ${s}.`)}var Pe=Ae,He=()=>Le;let $e=!1;const De=async(e,t)=>{M(4,"[chart] Starting the exporting process.");const r=((e,t={})=>{let r={};return e.svg?(r=J(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=oe(t,e,_),r.export.outfile=r.export?.outfile||`chart.${r.export?.type||"png"}`,r})(e,re()),i=r.export;if(r.payload?.svg&&""!==r.payload.svg)try{M(4,"[chart] Attempting to export from a SVG input.");const e=Me(function(e){const t=new b("").window;return w(t).sanitize(e,{ADD_TAGS:["foreignObject"]})}(r.payload.svg),r,t);return++Le.exportFromSvgAttempts,e}catch(e){return t(new le("[chart] Error loading SVG input.").setError(e))}if(i.infile&&i.infile.length)try{return M(4,"[chart] Attempting to export from an input file."),r.export.instr=o(i.infile,"utf8"),Me(r.export.instr.trim(),r,t)}catch(e){return t(new le("[chart] Error loading input file.").setError(e))}if(i.instr&&""!==i.instr||i.options&&""!==i.options)try{return M(4,"[chart] Attempting to export from a raw input."),Q(r.customLogic?.allowCodeExecution)?je(r,t):"string"==typeof i.instr?Me(i.instr.trim(),r,t):Ge(r,i.instr||i.options,t)}catch(e){return t(new le("[chart] Error loading raw input.").setError(e))}return t(new le("[chart] No valid input specified. Check if at least one of the following parameters is correctly set: 'infile', 'instr', 'options', or 'svg'."))},Ue=e=>{const{chart:t,exporting:r}=e.export?.options||K(e.export?.instr),o=K(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 s={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(s))s[e]="string"==typeof t?+t.replace(/px|%/gi,""):t;return s},Ge=async(e,t,r,i)=>{let{export:s,customLogic:n}=e;const a="boolean"==typeof n.allowCodeExecution?n.allowCodeExecution:$e;if(n){if(a)if("string"==typeof e.customLogic.resources)e.customLogic.resources=X(e.customLogic.resources,Q(e.customLogic.allowFileResources));else if(!e.customLogic.resources)try{const t=o("resources.json","utf8");e.customLogic.resources=X(t,Q(e.customLogic.allowFileResources))}catch(e){F(2,e,"[chart] Unable to load the default resources.json file.")}}else n=e.customLogic={};if(!a&&n){if(n.callback||n.resources||n.customCode)return r(new le("[chart] The 'callback', 'resources' and 'customCode' options have been disabled for this server."));n.callback=!1,n.resources=!1,n.customCode=!1}if(t&&(t.chart=t.chart||{},t.exporting=t.exporting||{},t.exporting.enabled=!1),s.constr=s.constr||"chart",s.type=B(s.type,s.outfile),"svg"===s.type&&(s.width=!1),["globalOptions","themeOptions"].forEach((e=>{try{s&&s[e]&&("string"==typeof s[e]&&s[e].endsWith(".json")?s[e]=K(o(s[e],"utf8"),!0):s[e]=K(s[e],!0))}catch(t){s[e]={},F(2,t,`[chart] The '${e}' cannot be loaded.`)}})),n.allowCodeExecution)try{n.customCode=Z(n.customCode,n.allowFileResources)}catch(e){F(2,e,"[chart] The 'customCode' cannot be loaded.")}if(n&&n.callback&&n.callback?.indexOf("{")<0)if(n.allowFileResources)try{n.callback=o(n.callback,"utf8")}catch(e){n.callback=!1,F(2,e,"[chart] The 'callback' cannot be loaded.")}else n.callback=!1;e.export={...e.export,...Ue(e)};try{return r(!1,await Ce(s.strInj||t||i,e))}catch(e){return r(e)}},je=(e,t)=>{try{let r,o=e.export.instr||e.export.options;return"string"!=typeof o&&(r=o=z(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,Ge(e,!1,t)}catch(r){return t(new le(`[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))}},Me=(e,t,r)=>{const{allowCodeExecution:o}=t.customLogic;if(e.indexOf("=0||e.indexOf("=0)return M(4,"[chart] Parsing input as SVG."),Ge(t,!1,r,e);try{const o=JSON.parse(e.replaceAll(/\t|\n|\r/g," "));return Ge(t,o,r)}catch(e){return Q(o)?je(t,r):r(new le("[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))}},Fe=[],We=()=>{M(4,"[server] Clearing all registered intervals.");for(const e of Fe)clearInterval(e)},Ve=(e,t,r,o)=>{F(1,e),"development"!==D.OTHER_NODE_ENV&&delete e.stack,o(e)},qe=(e,t,r,o)=>{const{statusCode:i,status:s,message:n,stack:a}=e,l=i||s||500;r.status(l).json({statusCode:l,message:n,stack:a})};var Be=(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=x({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&&(M(4,"[rate limiting] Skipping rate limiter."),!0)});e.use(i),M(3,`[rate limiting] Enabled rate limiting with ${o.max} requests per ${o.window} minute for each IP, trusting proxy: ${o.trustProxy}.`)};class Xe extends le{constructor(e,t){super(e),this.status=this.statusCode=t}setStatus(e){return this.status=e,this}}var Ke=e=>!!e&&e.post("/version/change/:newVersion",(async(e,t,r)=>{try{const r=D.HIGHCHARTS_ADMIN_TOKEN;if(!r||!r.length)throw new Xe("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 Xe("Invalid or missing token: Set the token in the hc-auth header.",401);const i=e.params.newVersion;if(!i)throw new Xe("No new version supplied.",400);try{await(async e=>{const t=re();t?.highcharts&&(t.highcharts.version=e),await de(t)})(i)}catch(e){throw new Xe(`Version change: ${e.message}`,e.statusCode).setError(e)}t.status(200).send({statusCode:200,version:me(),message:`Successfully updated Highcharts to version: ${i}.`})}catch(e){r(e)}}));const Je={png:"image/png",jpeg:"image/jpeg",gif:"image/gif",pdf:"application/pdf",svg:"image/svg+xml"};let ze=0;const Ye=[],Qe=[],Ze=(e,t,r,o)=>{let i=!0;const{id:s,uniqueId:n,type:a,body:l}=o;return e.some((e=>{if(e){let o=e(t,r,s,n,a,l);return void 0!==o&&!0!==o&&(i=o),!0}})),i},et=async(e,t,r)=>{try{const r=ee(),i=v().replace(/-/g,""),s=re(),n=e.body,a=++ze;let l=B(n.type);if(!n||"object"==typeof(o=n)&&!Array.isArray(o)&&null!==o&&0===Object.keys(o).length)throw new Xe("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=K(n.infile||n.options||n.data);if(!c&&!n.svg)throw M(2,`The request with ID ${i} from ${e.headers["x-forwarded-for"]||e.connection.remoteAddress} was incorrect. Payload received: ${JSON.stringify(n)}.`),new Xe("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=Ze(Ye,e,t,{id:a,uniqueId:i,type:l,body:n}),!0!==p)return t.send(p);let h=!1;e.socket.on("close",(()=>{h=!0})),M(4,`[export] Got an incoming HTTP request with ID ${i}.`),n.constr="string"==typeof n.constr&&n.constr||"chart";const u={export:{instr:c,type:l,constr:n.constr[0].toLowerCase()+n.constr.substr(1),height:n.height,width:n.width,scale:n.scale||s.export.scale,globalOptions:K(n.globalOptions,!0),themeOptions:K(n.themeOptions,!0)},customLogic:{allowCodeExecution:$e,allowFileResources:!1,resources:K(n.resources,!0),callback:n.callback,customCode:n.customCode}};c&&(u.export.instr=z(c,u.customLogic.allowCodeExecution));const d=oe(s,u);if(d.export.options=c,d.payload={svg:n.svg||!1,b64:n.b64||!1,noDownload:n.noDownload||!1,requestId:i},n.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 Xe("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"),s.server.benchmarking&&M(5,`[benchmark] Request with ID ${i} - After the whole exporting process: ${r()}ms.`),h)return M(3,"[export] The client closed the connection before the chart finished processing.");if(o)throw o;if(!c||!c.result)throw new Xe(`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,Ze(Qe,e,t,{id:a,body:c.result}),c.result?n.b64?"pdf"===l||"svg"==l?t.send(Buffer.from(c.result,"utf8").toString("base64")):t.send(c.result):(t.header("Content-Type",Je[l]||"image/png"),n.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 tt=JSON.parse(o(a(q,"package.json"))),rt=new Date,ot=[];function it(e){if(!e)return!1;var t;t=setInterval((()=>{const e=He(),t=0===e.exportAttempts?1:e.performedExports/e.exportAttempts*100;ot.push(t),ot.length>30&&ot.shift()}),6e4),Fe.push(t),e.get("/health",((e,t)=>{const r=He(),o=ot.length,i=ot.reduce(((e,t)=>e+t),0)/ot.length;M(4,"[health.js] GET /health [200] - returning server health."),t.send({status:"OK",bootTime:rt,uptime:Math.floor(((new Date).getTime()-rt.getTime())/1e3/60)+" minutes",version:tt.version,highchartsVersion:me(),averageProcessingTime:r.spentAverage,performedExports:r.performedExports,failedExports:r.droppedExports,exportAttempts:r.exportAttempts,sucessRatio:r.performedExports/r.exportAttempts*100,pool:Pe(),period:o,movingAverage:i,message:`Last ${o} minutes had a success rate of ${i.toFixed(2)}%.`,svgExportAttempts:r.exportFromSvgAttempts,jsonExportAttempts:r.performedExports-r.exportFromSvgAttempts})}))}const st=new Map,nt=T();nt.disable("x-powered-by"),nt.use(E());const at=S.memoryStorage(),lt=S({storage:at,limits:{fieldSize:52428800}});nt.use(T.json({limit:52428800})),nt.use(T.urlencoded({extended:!0,limit:52428800})),nt.use(lt.none());const ct=e=>{e.on("clientError",(e=>{F(1,e,`[server] Client error: ${e.message}`)})),e.on("error",(e=>{F(1,e,`[server] Server error: ${e.message}`)})),e.on("connection",(e=>{e.on("error",(e=>{F(1,e,`[server] Socket error: ${e.message}`)}))}))},pt=async e=>{try{if(!e.enable)return!1;if(!e.ssl.force){const t=g.createServer(nt);ct(t),t.listen(e.port,e.host),st.set(e.port,t),M(3,`[server] Started HTTP server on ${e.host}:${e.port}.`)}if(e.ssl.enable){let t,r;try{t=await i.readFile(l.join(e.ssl.certPath,"server.key"),"utf8"),r=await i.readFile(l.join(e.ssl.certPath,"server.crt"),"utf8")}catch(t){M(2,`[server] Unable to load key/certificate from the '${e.ssl.certPath}' path. Could not run secured layer server.`)}if(t&&r){const o=m.createServer({key:t,cert:r},nt);ct(o),o.listen(e.ssl.port,e.host),st.set(e.ssl.port,o),M(3,`[server] Started HTTPS server on ${e.host}:${e.ssl.port}.`)}}e.rateLimiting&&e.rateLimiting.enable&&![0,NaN].includes(e.rateLimiting.maxRequests)&&Be(nt,e.rateLimiting),nt.use(T.static(l.join(q,"public"))),it(nt),(e=>{e.post("/",et),e.post("/:filename",et)})(nt),(e=>{!!e&&e.get("/",((e,t)=>{t.sendFile(a(q,"public","index.html"))}))})(nt),Ke(nt),(e=>{e.use(Ve),e.use(qe)})(nt)}catch(e){throw new le("[server] Could not configure and start the server.").setError(e)}},ht=()=>{M(4,"[server] Closing all servers.");for(const[e,t]of st)t.close((()=>{st.delete(e),M(4,`[server] Closed server on port: ${e}.`)}))};var ut={startServer:pt,closeServers:ht,getServers:()=>st,enableRateLimiting:e=>Be(nt,e),getExpress:()=>T,getApp:()=>nt,use:(e,...t)=>{nt.use(e,...t)},get:(e,...t)=>{nt.get(e,...t)},post:(e,...t)=>{nt.post(e,...t)}};const dt=async e=>{await Promise.allSettled([We(),ht(),Ie()]),process.exit(e)};var gt={server:ut,startServer:pt,initExport:async e=>{var t;return t=e.customLogic&&e.customLogic.allowCodeExecution,$e=Q(t),(e=>{W(e&&parseInt(e.level)),e&&e.dest&&V(e.dest,e.file||"highcharts-export-server.log")})(e.logging),e.other.listenToProcessExits&&(M(3,"[process] Attaching exit listeners to the process."),process.on("exit",(e=>{M(4,`Process exited with code ${e}.`)})),process.on("SIGINT",(async(e,t)=>{M(4,`The ${e} event with code: ${t}.`),await dt(0)})),process.on("SIGTERM",(async(e,t)=>{M(4,`The ${e} event with code: ${t}.`),await dt(0)})),process.on("SIGHUP",(async(e,t)=>{M(4,`The ${e} event with code: ${t}.`),await dt(0)})),process.on("uncaughtException",(async(e,t)=>{F(1,e,`The ${t} error.`),await dt(1)}))),await de(e),await ke({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 Ie()}))},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,"svg"!==t.options.export.type?Buffer.from(t.result,"base64"):t.result)})));try{await Promise.all(t),await Ie()}catch(e){throw new le("[chart] Error encountered during batch export.").setError(e)}},startExport:De,initPool:ke,killPool:Ie,setOptions:(e,t)=>(t?.length&&(te=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(o(r))}catch(e){F(2,e,`[config] Unable to load the configuration from the ${r} file.`)}}return{}}(t)),ie(L,te),te=se(L),e&&(te=oe(te,e,_)),t?.length&&(te=function(e,t,r){let o=!1;for(let i=0;i(n.length-1===r&&(a=e[t].type),e[t])),r),n.reduce(((e,r,l)=>(n.length-1===l&&void 0!==e[r]&&(t[++i]?"boolean"===a?e[r]=Q(t[i]):"number"===a?e[r]=+t[i]:a.indexOf("]")>=0?e[r]=t[i].split(","):e[r]=t[i]:(M(2,`[config] Missing value for the '${s}' argument. Using the default value.`),o=!0)),e[r])),e)}o&&Y();return e}(te,t,L)),te),shutdownCleanUp:dt,log:M,logWithStack:F,setLogLevel:W,enableFileLogging:V,mapToNewConfig:e=>{const t={};for(const[r,o]of Object.entries(e)){const e=k[r]?k[r].split("."):[];e.reduce(((t,r,i)=>t[r]=e.length-1===i?o:t[r]||{}),t)}return t},manualConfig:async t=>{let r={};e(t)&&(r=JSON.parse(o(t,"utf8")));const s=Object.keys(O).map((e=>({title:`${e} options`,value:e})));return p({type:"multiselect",name:"category",message:"Which category do you want to configure?",hint:"Space: Select specific, A: Select all, Enter: Confirm.",instructions:"",choices:s},{onSubmit:async(e,o)=>{let s=0,n=[];for(const e of o)O[e]=O[e].map((t=>({...t,section:e}))),n=[...n,...O[e]];return await p(n,{onSubmit:async(e,o)=>{if("moduleScripts"===e.name?(o=o.length?o.map((t=>e.choices[t])):e.choices,r[e.section][e.name]=o):r[e.section]=ne(Object.assign({},r[e.section]||{}),e.name.split("."),e.choices?e.choices[o]:o),++s===n.length){try{await i.writeFile(t,JSON.stringify(r,null,2),"utf8")}catch(e){F(1,e,`[config] An error occurred while creating the ${t} file.`)}return!0}}}),!0}})},printLogo:e=>{const t=JSON.parse(o(a(q,"package.json"))).version;e?console.log(`Starting Highcharts Export Server v${t}...`):console.log(o(q+"/msg/startup.msg").toString().bold.yellow,`v${t}\n`.bold)},printUsage:Y};export{gt as default}; //# sourceMappingURL=index.esm.js.map diff --git a/dist/index.esm.js.map b/dist/index.esm.js.map index ebc98c8b..b14a474e 100644 --- a/dist/index.esm.js.map +++ b/dist/index.esm.js.map @@ -1 +1 @@ -{"version":3,"file":"index.esm.js","sources":["../lib/schemas/config.js","../lib/envs.js","../lib/logger.js","../lib/utils.js","../lib/config.js","../lib/fetch.js","../lib/errors/ExportError.js","../lib/cache.js","../lib/highcharts.js","../lib/browser.js","../lib/export.js","../templates/svg_export/svg_export.js","../lib/pool.js","../lib/chart.js","../lib/sanitize.js","../lib/intervals.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/resource_release.js","../lib/index.js"],"sourcesContent":["/*******************************************************************************\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// Possible names for Highcharts scripts\r\nexport const scriptsNames = {\r\n core: ['highcharts', 'highcharts-more', 'highcharts-3d'],\r\n modules: [\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 indicators: ['indicators-all']\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 '--allow-running-insecure-content',\r\n '--ash-no-nudges',\r\n '--autoplay-policy=user-gesture-required',\r\n '--block-new-web-contents',\r\n '--disable-accelerated-2d-canvas',\r\n '--disable-background-networking',\r\n '--disable-background-timer-throttling',\r\n '--disable-backgrounding-occluded-windows',\r\n '--disable-breakpad',\r\n '--disable-checker-imaging',\r\n '--disable-client-side-phishing-detection',\r\n '--disable-component-extensions-with-background-pages',\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=CalculateNativeWinOcclusion,InterestFeedContentSuggestions,WebOTP',\r\n '--disable-hang-monitor',\r\n '--disable-ipc-flooding-protection',\r\n '--disable-logging',\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-search-engine-choice-screen',\r\n '--disable-session-crashed-bubble',\r\n '--disable-setuid-sandbox',\r\n '--disable-site-isolation-trials',\r\n '--disable-speech-api',\r\n '--disable-sync',\r\n '--enable-unsafe-webgpu',\r\n '--hide-crash-restore-bubble',\r\n '--hide-scrollbars',\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-startup-window',\r\n '--no-zygote',\r\n '--password-store=basic',\r\n '--process-per-tab',\r\n '--use-mock-keychain'\r\n ],\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 type: 'string',\r\n envLink: 'HIGHCHARTS_VERSION',\r\n description: 'The Highcharts version to be used.'\r\n },\r\n cdnURL: {\r\n value: 'https://code.highcharts.com/',\r\n type: 'string',\r\n envLink: 'HIGHCHARTS_CDN_URL',\r\n description: 'The CDN URL for Highcharts scripts to be used.'\r\n },\r\n coreScripts: {\r\n value: scriptsNames.core,\r\n type: 'string[]',\r\n envLink: 'HIGHCHARTS_CORE_SCRIPTS',\r\n description: 'The core Highcharts scripts to fetch.'\r\n },\r\n moduleScripts: {\r\n value: scriptsNames.modules,\r\n type: 'string[]',\r\n envLink: 'HIGHCHARTS_MODULE_SCRIPTS',\r\n description: 'The modules of Highcharts to fetch.'\r\n },\r\n indicatorScripts: {\r\n value: scriptsNames.indicators,\r\n type: 'string[]',\r\n envLink: 'HIGHCHARTS_INDICATOR_SCRIPTS',\r\n description: 'The indicators of Highcharts to fetch.'\r\n },\r\n customScripts: {\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 custom scripts or dependencies to fetch.'\r\n },\r\n forceFetch: {\r\n value: false,\r\n type: 'boolean',\r\n envLink: 'HIGHCHARTS_FORCE_FETCH',\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 value: '.cache',\r\n type: 'string',\r\n envLink: 'HIGHCHARTS_CACHE_PATH',\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 value: 'png',\r\n type: 'string',\r\n envLink: 'EXPORT_TYPE',\r\n description: 'The file export format. It can be jpeg, png, pdf, or svg.'\r\n },\r\n constr: {\r\n value: 'chart',\r\n type: 'string',\r\n envLink: 'EXPORT_CONSTR',\r\n description:\r\n 'The constructor to use. Can be chart, stockChart, mapChart, or ganttChart.'\r\n },\r\n defaultHeight: {\r\n value: 400,\r\n type: 'number',\r\n envLink: 'EXPORT_DEFAULT_HEIGHT',\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 value: 600,\r\n type: 'number',\r\n envLink: 'EXPORT_DEFAULT_WIDTH',\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 value: 1,\r\n type: 'number',\r\n envLink: 'EXPORT_DEFAULT_SCALE',\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 value: false,\r\n type: 'number',\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 value: false,\r\n type: 'number',\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 value: 1500,\r\n type: 'number',\r\n envLink: 'EXPORT_RASTERIZATION_TIMEOUT',\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 value: false,\r\n type: 'boolean',\r\n envLink: 'CUSTOM_LOGIC_ALLOW_CODE_EXECUTION',\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 value: false,\r\n type: 'boolean',\r\n envLink: 'CUSTOM_LOGIC_ALLOW_FILE_RESOURCES',\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 value: false,\r\n type: 'boolean',\r\n envLink: 'SERVER_ENABLE',\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 value: '0.0.0.0',\r\n type: 'string',\r\n envLink: 'SERVER_HOST',\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 value: 7801,\r\n type: 'number',\r\n envLink: 'SERVER_PORT',\r\n description: 'The server port when enabled.'\r\n },\r\n benchmarking: {\r\n value: false,\r\n type: 'boolean',\r\n envLink: 'SERVER_BENCHMARKING',\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 proxy: {\r\n host: {\r\n value: false,\r\n type: 'string',\r\n envLink: 'SERVER_PROXY_HOST',\r\n cliName: 'proxyHost',\r\n description: 'The host of the proxy server to use, if it exists.'\r\n },\r\n port: {\r\n value: 8080,\r\n type: 'number',\r\n envLink: 'SERVER_PROXY_PORT',\r\n cliName: 'proxyPort',\r\n description: 'The port of the proxy server to use, if it exists.'\r\n },\r\n timeout: {\r\n value: 5000,\r\n type: 'number',\r\n envLink: 'SERVER_PROXY_TIMEOUT',\r\n cliName: 'proxyTimeout',\r\n description: 'The timeout for the proxy server to use, if it exists.'\r\n }\r\n },\r\n rateLimiting: {\r\n enable: {\r\n value: false,\r\n type: 'boolean',\r\n envLink: 'SERVER_RATE_LIMITING_ENABLE',\r\n cliName: 'enableRateLimiting',\r\n description: 'Enables rate limiting for the server.'\r\n },\r\n maxRequests: {\r\n value: 10,\r\n type: 'number',\r\n envLink: 'SERVER_RATE_LIMITING_MAX_REQUESTS',\r\n legacyName: 'rateLimit',\r\n description: 'The maximum number of requests allowed in one minute.'\r\n },\r\n window: {\r\n value: 1,\r\n type: 'number',\r\n envLink: 'SERVER_RATE_LIMITING_WINDOW',\r\n description: 'The time window, in minutes, for the rate limiting.'\r\n },\r\n delay: {\r\n value: 0,\r\n type: 'number',\r\n envLink: 'SERVER_RATE_LIMITING_DELAY',\r\n description:\r\n 'The delay duration for each successive request before reaching the maximum limit.'\r\n },\r\n trustProxy: {\r\n value: false,\r\n type: 'boolean',\r\n envLink: 'SERVER_RATE_LIMITING_TRUST_PROXY',\r\n description: 'Set this to true if the server is behind a load balancer.'\r\n },\r\n skipKey: {\r\n value: false,\r\n type: 'string',\r\n envLink: 'SERVER_RATE_LIMITING_SKIP_KEY',\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 value: false,\r\n type: 'string',\r\n envLink: 'SERVER_RATE_LIMITING_SKIP_TOKEN',\r\n description:\r\n 'Allows bypassing the rate limiter and should be provided with the skipKey argument.'\r\n }\r\n },\r\n ssl: {\r\n enable: {\r\n value: false,\r\n type: 'boolean',\r\n envLink: 'SERVER_SSL_ENABLE',\r\n cliName: 'enableSsl',\r\n description: 'Enables or disables the SSL protocol.'\r\n },\r\n force: {\r\n value: false,\r\n type: 'boolean',\r\n envLink: 'SERVER_SSL_FORCE',\r\n cliName: 'sslForce',\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 value: 443,\r\n type: 'number',\r\n envLink: 'SERVER_SSL_PORT',\r\n cliName: 'sslPort',\r\n description: 'The port on which to run the SSL server.'\r\n },\r\n certPath: {\r\n value: false,\r\n type: 'string',\r\n envLink: 'SERVER_SSL_CERT_PATH',\r\n legacyName: 'sslPath',\r\n description: 'The path to the SSL certificate/key file.'\r\n }\r\n }\r\n },\r\n pool: {\r\n minWorkers: {\r\n value: 4,\r\n type: 'number',\r\n envLink: 'POOL_MIN_WORKERS',\r\n description: 'The number of minimum and initial pool workers to spawn.'\r\n },\r\n maxWorkers: {\r\n value: 8,\r\n type: 'number',\r\n envLink: 'POOL_MAX_WORKERS',\r\n legacyName: 'workers',\r\n description: 'The number of maximum pool workers to spawn.'\r\n },\r\n workLimit: {\r\n value: 40,\r\n type: 'number',\r\n envLink: 'POOL_WORK_LIMIT',\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 value: 5000,\r\n type: 'number',\r\n envLink: 'POOL_ACQUIRE_TIMEOUT',\r\n description:\r\n 'The duration, in milliseconds, to wait for acquiring a resource.'\r\n },\r\n createTimeout: {\r\n value: 5000,\r\n type: 'number',\r\n envLink: 'POOL_CREATE_TIMEOUT',\r\n description:\r\n 'The duration, in milliseconds, to wait for creating a resource.'\r\n },\r\n destroyTimeout: {\r\n value: 5000,\r\n type: 'number',\r\n envLink: 'POOL_DESTROY_TIMEOUT',\r\n description:\r\n 'The duration, in milliseconds, to wait for destroying a resource.'\r\n },\r\n idleTimeout: {\r\n value: 30000,\r\n type: 'number',\r\n envLink: 'POOL_IDLE_TIMEOUT',\r\n description:\r\n 'The duration, in milliseconds, after which an idle resource is destroyed.'\r\n },\r\n createRetryInterval: {\r\n value: 200,\r\n type: 'number',\r\n envLink: 'POOL_CREATE_RETRY_INTERVAL',\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 value: 1000,\r\n type: 'number',\r\n envLink: 'POOL_REAPER_INTERVAL',\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 value: false,\r\n type: 'boolean',\r\n envLink: 'POOL_BENCHMARKING',\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 },\r\n logging: {\r\n level: {\r\n value: 4,\r\n type: 'number',\r\n envLink: 'LOGGING_LEVEL',\r\n cliName: 'logLevel',\r\n description: 'The logging level to be used.'\r\n },\r\n file: {\r\n value: 'highcharts-export-server.log',\r\n type: 'string',\r\n envLink: 'LOGGING_FILE',\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 value: 'log/',\r\n type: 'string',\r\n envLink: 'LOGGING_DEST',\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 value: false,\r\n type: 'boolean',\r\n envLink: 'UI_ENABLE',\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 value: '/',\r\n type: 'string',\r\n envLink: 'UI_ROUTE',\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 nodeEnv: {\r\n value: 'production',\r\n type: 'string',\r\n envLink: 'OTHER_NODE_ENV',\r\n description: 'The type of Node.js environment.'\r\n },\r\n listenToProcessExits: {\r\n value: true,\r\n type: 'boolean',\r\n envLink: 'OTHER_LISTEN_TO_PROCESS_EXITS',\r\n description: 'Decides whether or not to attach process.exit handlers.'\r\n },\r\n noLogo: {\r\n value: false,\r\n type: 'boolean',\r\n envLink: 'OTHER_NO_LOGO',\r\n description:\r\n 'Skip printing the logo on a startup. Will be replaced by a simple text.'\r\n },\r\n hardResetPage: {\r\n value: false,\r\n type: 'boolean',\r\n envLink: 'OTHER_HARD_RESET_PAGE',\r\n description: 'Decides if the page content should be reset entirely.'\r\n }\r\n },\r\n debug: {\r\n enable: {\r\n value: false,\r\n type: 'boolean',\r\n envLink: 'DEBUG_ENABLE',\r\n cliName: 'enableDebug',\r\n description: 'Enables or disables debug mode for the underlying browser.'\r\n },\r\n headless: {\r\n value: true,\r\n type: 'boolean',\r\n envLink: 'DEBUG_HEADLESS',\r\n description:\r\n 'Controls the mode in which the browser is launched when in the debug mode.'\r\n },\r\n devtools: {\r\n value: false,\r\n type: 'boolean',\r\n envLink: 'DEBUG_DEVTOOLS',\r\n description:\r\n 'Decides whether to enable DevTools when the browser is in a headful state.'\r\n },\r\n listenToConsole: {\r\n value: false,\r\n type: 'boolean',\r\n envLink: 'DEBUG_LISTEN_TO_CONSOLE',\r\n description:\r\n 'Decides whether to enable a listener for console messages sent from the browser.'\r\n },\r\n dumpio: {\r\n value: false,\r\n type: 'boolean',\r\n envLink: 'DEBUG_DUMPIO',\r\n description:\r\n 'Redirects browser process stdout and stderr to process.stdout and process.stderr.'\r\n },\r\n slowMo: {\r\n value: 0,\r\n type: 'number',\r\n envLink: 'DEBUG_SLOW_MO',\r\n description:\r\n 'Slows down Puppeteer operations by the specified number of milliseconds.'\r\n },\r\n debuggingPort: {\r\n value: 9222,\r\n type: 'number',\r\n envLink: 'DEBUG_DEBUGGING_PORT',\r\n description: 'Specifies the debugging port.'\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: 'coreScripts',\r\n message: 'Available core scripts',\r\n instructions: 'Space: Select specific, A: Select all, Enter: Confirm.',\r\n choices: defaultConfig.highcharts.coreScripts.value\r\n },\r\n {\r\n type: 'multiselect',\r\n name: 'moduleScripts',\r\n message: 'Available module scripts',\r\n instructions: 'Space: Select specific, A: Select all, Enter: Confirm.',\r\n choices: defaultConfig.highcharts.moduleScripts.value\r\n },\r\n {\r\n type: 'multiselect',\r\n name: 'indicatorScripts',\r\n message: 'Available indicator scripts',\r\n instructions: 'Space: Select specific, A: Select all, Enter: Confirm.',\r\n choices: defaultConfig.highcharts.indicatorScripts.value\r\n },\r\n {\r\n type: 'list',\r\n name: 'customScripts',\r\n message: 'Custom scripts',\r\n initial: defaultConfig.highcharts.customScripts.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: 'text',\r\n name: 'proxy.host',\r\n message: 'The host of the proxy server to use',\r\n initial: defaultConfig.server.proxy.host.value\r\n },\r\n {\r\n type: 'number',\r\n name: 'proxy.port',\r\n message: 'The port of the proxy server to use',\r\n initial: defaultConfig.server.proxy.port.value\r\n },\r\n {\r\n type: 'number',\r\n name: 'proxy.timeout',\r\n message: 'The timeout for the proxy server to use',\r\n initial: defaultConfig.server.proxy.timeout.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 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 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 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: 'text',\r\n name: 'nodeEnv',\r\n message: 'The type of Node.js environment',\r\n initial: defaultConfig.other.nodeEnv.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.other.listenToProcessExits.value\r\n },\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 type: 'toggle',\r\n name: 'hardResetPage',\r\n message: 'Decides if the page content should be reset entirely',\r\n initial: defaultConfig.other.hardResetPage.value\r\n }\r\n ],\r\n debug: [\r\n {\r\n type: 'toggle',\r\n name: 'enable',\r\n message: 'Enables debug mode for the browser instance',\r\n initial: defaultConfig.debug.enable.value\r\n },\r\n {\r\n type: 'toggle',\r\n name: 'headless',\r\n message: 'The mode setting for the browser',\r\n initial: defaultConfig.debug.headless.value\r\n },\r\n {\r\n type: 'toggle',\r\n name: 'devtools',\r\n message: 'The DevTools for the headful browser',\r\n initial: defaultConfig.debug.devtools.value\r\n },\r\n {\r\n type: 'toggle',\r\n name: 'listenToConsole',\r\n message: 'The event listener for console messages from the browser',\r\n initial: defaultConfig.debug.listenToConsole.value\r\n },\r\n {\r\n type: 'toggle',\r\n name: 'dumpio',\r\n message: 'Redirects the browser stdout and stderr to NodeJS process',\r\n initial: defaultConfig.debug.dumpio.value\r\n },\r\n {\r\n type: 'number',\r\n name: 'slowMo',\r\n message: 'Puppeteer operations slow down in milliseconds',\r\n initial: defaultConfig.debug.slowMo.value\r\n },\r\n {\r\n type: 'number',\r\n name: 'debuggingPort',\r\n message: 'The port number for debugging',\r\n initial: defaultConfig.debug.debuggingPort.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 * @fileoverview\r\n * This file is responsible for parsing the environment variables with the 'zod'\r\n * library. The parsed environment variables are then exported to be used\r\n * in the application as \"envs\". We should not use process.env directly\r\n * in the application as these would not be parsed properly.\r\n *\r\n * The environment variables are parsed and validated only once when\r\n * the application starts. We should write a custom validator or a transformer\r\n * for each of the options.\r\n */\r\n\r\nimport dotenv from 'dotenv';\r\nimport { z } from 'zod';\r\n\r\nimport { scriptsNames } from './schemas/config.js';\r\n\r\n// Load .env into environment variables\r\ndotenv.config();\r\n\r\n// Object with custom validators and transformers, to avoid repetition\r\n// in the Config object\r\nconst v = {\r\n // Splits string value into elements in an array, trims every element, checks\r\n // if an array is correct, if it is empty, and if it is, returns undefined\r\n array: (filterArray) =>\r\n z\r\n .string()\r\n .transform((value) =>\r\n value\r\n .split(',')\r\n .map((value) => value.trim())\r\n .filter((value) => filterArray.includes(value))\r\n )\r\n .transform((value) => (value.length ? value : undefined)),\r\n\r\n // Allows only true, false and correctly parse the value to boolean\r\n // or no value in which case the returned value will be undefined\r\n boolean: () =>\r\n z\r\n .enum(['true', 'false', ''])\r\n .transform((value) => (value !== '' ? value === 'true' : undefined)),\r\n\r\n // Allows passed values or no value in which case the returned value will\r\n // be undefined\r\n enum: (values) =>\r\n z\r\n .enum([...values, ''])\r\n .transform((value) => (value !== '' ? value : undefined)),\r\n\r\n // Trims the string value and checks if it is empty or contains stringified\r\n // values such as false, undefined, null, NaN, if it does, returns undefined\r\n string: () =>\r\n z\r\n .string()\r\n .trim()\r\n .refine(\r\n (value) =>\r\n !['false', 'undefined', 'null', 'NaN'].includes(value) ||\r\n value === '',\r\n (value) => ({\r\n message: `The string contains forbidden values, received '${value}'`\r\n })\r\n )\r\n .transform((value) => (value !== '' ? value : undefined)),\r\n\r\n // Allows positive numbers or no value in which case the returned value will\r\n // be undefined\r\n positiveNum: () =>\r\n z\r\n .string()\r\n .trim()\r\n .refine(\r\n (value) =>\r\n value === '' || (!isNaN(parseFloat(value)) && parseFloat(value) > 0),\r\n (value) => ({\r\n message: `The value must be numeric and positive, received '${value}'`\r\n })\r\n )\r\n .transform((value) => (value !== '' ? parseFloat(value) : undefined)),\r\n\r\n // Allows non-negative numbers or no value in which case the returned value\r\n // will be undefined\r\n nonNegativeNum: () =>\r\n z\r\n .string()\r\n .trim()\r\n .refine(\r\n (value) =>\r\n value === '' || (!isNaN(parseFloat(value)) && parseFloat(value) >= 0),\r\n (value) => ({\r\n message: `The value must be numeric and non-negative, received '${value}'`\r\n })\r\n )\r\n .transform((value) => (value !== '' ? parseFloat(value) : undefined))\r\n};\r\n\r\nexport const Config = z.object({\r\n // highcharts\r\n HIGHCHARTS_VERSION: z\r\n .string()\r\n .trim()\r\n .refine(\r\n (value) => /^(latest|\\d+(\\.\\d+){0,2})$/.test(value) || value === '',\r\n (value) => ({\r\n message: `HIGHCHARTS_VERSION must be 'latest', a major version, or in the form XX.YY.ZZ, received '${value}'`\r\n })\r\n )\r\n .transform((value) => (value !== '' ? value : undefined)),\r\n HIGHCHARTS_CDN_URL: z\r\n .string()\r\n .trim()\r\n .refine(\r\n (value) =>\r\n value.startsWith('https://') ||\r\n value.startsWith('http://') ||\r\n value === '',\r\n (value) => ({\r\n message: `Invalid value for HIGHCHARTS_CDN_URL. It should start with http:// or https://, received '${value}'`\r\n })\r\n )\r\n .transform((value) => (value !== '' ? value : undefined)),\r\n HIGHCHARTS_CORE_SCRIPTS: v.array(scriptsNames.core),\r\n HIGHCHARTS_MODULE_SCRIPTS: v.array(scriptsNames.modules),\r\n HIGHCHARTS_INDICATOR_SCRIPTS: v.array(scriptsNames.indicators),\r\n HIGHCHARTS_FORCE_FETCH: v.boolean(),\r\n HIGHCHARTS_CACHE_PATH: v.string(),\r\n HIGHCHARTS_ADMIN_TOKEN: v.string(),\r\n\r\n // export\r\n EXPORT_TYPE: v.enum(['jpeg', 'png', 'pdf', 'svg']),\r\n EXPORT_CONSTR: v.enum(['chart', 'stockChart', 'mapChart', 'ganttChart']),\r\n EXPORT_DEFAULT_HEIGHT: v.positiveNum(),\r\n EXPORT_DEFAULT_WIDTH: v.positiveNum(),\r\n EXPORT_DEFAULT_SCALE: v.positiveNum(),\r\n EXPORT_RASTERIZATION_TIMEOUT: v.nonNegativeNum(),\r\n\r\n // custom\r\n CUSTOM_LOGIC_ALLOW_CODE_EXECUTION: v.boolean(),\r\n CUSTOM_LOGIC_ALLOW_FILE_RESOURCES: v.boolean(),\r\n\r\n // server\r\n SERVER_ENABLE: v.boolean(),\r\n SERVER_HOST: v.string(),\r\n SERVER_PORT: v.positiveNum(),\r\n SERVER_BENCHMARKING: v.boolean(),\r\n\r\n // server proxy\r\n SERVER_PROXY_HOST: v.string(),\r\n SERVER_PROXY_PORT: v.positiveNum(),\r\n SERVER_PROXY_TIMEOUT: v.nonNegativeNum(),\r\n\r\n // server rate limiting\r\n SERVER_RATE_LIMITING_ENABLE: v.boolean(),\r\n SERVER_RATE_LIMITING_MAX_REQUESTS: v.nonNegativeNum(),\r\n SERVER_RATE_LIMITING_WINDOW: v.nonNegativeNum(),\r\n SERVER_RATE_LIMITING_DELAY: v.nonNegativeNum(),\r\n SERVER_RATE_LIMITING_TRUST_PROXY: v.boolean(),\r\n SERVER_RATE_LIMITING_SKIP_KEY: v.string(),\r\n SERVER_RATE_LIMITING_SKIP_TOKEN: v.string(),\r\n\r\n // server ssl\r\n SERVER_SSL_ENABLE: v.boolean(),\r\n SERVER_SSL_FORCE: v.boolean(),\r\n SERVER_SSL_PORT: v.positiveNum(),\r\n SERVER_SSL_CERT_PATH: v.string(),\r\n\r\n // pool\r\n POOL_MIN_WORKERS: v.nonNegativeNum(),\r\n POOL_MAX_WORKERS: v.nonNegativeNum(),\r\n POOL_WORK_LIMIT: v.positiveNum(),\r\n POOL_ACQUIRE_TIMEOUT: v.nonNegativeNum(),\r\n POOL_CREATE_TIMEOUT: v.nonNegativeNum(),\r\n POOL_DESTROY_TIMEOUT: v.nonNegativeNum(),\r\n POOL_IDLE_TIMEOUT: v.nonNegativeNum(),\r\n POOL_CREATE_RETRY_INTERVAL: v.nonNegativeNum(),\r\n POOL_REAPER_INTERVAL: v.nonNegativeNum(),\r\n POOL_BENCHMARKING: v.boolean(),\r\n\r\n // logger\r\n LOGGING_LEVEL: z\r\n .string()\r\n .trim()\r\n .refine(\r\n (value) =>\r\n value === '' ||\r\n (!isNaN(parseFloat(value)) &&\r\n parseFloat(value) >= 0 &&\r\n parseFloat(value) <= 5),\r\n (value) => ({\r\n message: `Invalid value for LOGGING_LEVEL. We only accept values from 0 to 5 as logging levels, received '${value}'`\r\n })\r\n )\r\n .transform((value) => (value !== '' ? parseFloat(value) : undefined)),\r\n LOGGING_FILE: v.string(),\r\n LOGGING_DEST: v.string(),\r\n\r\n // ui\r\n UI_ENABLE: v.boolean(),\r\n UI_ROUTE: v.string(),\r\n\r\n // other\r\n OTHER_NODE_ENV: v.enum(['development', 'production', 'test']),\r\n OTHER_LISTEN_TO_PROCESS_EXITS: v.boolean(),\r\n OTHER_NO_LOGO: v.boolean(),\r\n OTHER_HARD_RESET_PAGE: v.boolean(),\r\n\r\n // debugger\r\n DEBUG_ENABLE: v.boolean(),\r\n DEBUG_HEADLESS: v.boolean(),\r\n DEBUG_DEVTOOLS: v.boolean(),\r\n DEBUG_LISTEN_TO_CONSOLE: v.boolean(),\r\n DEBUG_DUMPIO: v.boolean(),\r\n DEBUG_SLOW_MO: v.nonNegativeNum(),\r\n DEBUG_DEBUGGING_PORT: v.positiveNum()\r\n});\r\n\r\nexport const envs = Config.partial().parse(process.env);\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}\\n`.bold\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\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 { envs } from './envs.js';\r\nimport { log, logWithStack } from './logger.js';\r\nimport { deepCopy, isObject, printUsage, toBoolean } from './utils.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 module scripts\r\n if (prompt.name === 'moduleScripts') {\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 && envs[entry.envLink] !== undefined) {\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 * 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","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 { getOptions } from './config.js';\r\nimport { envs } from './envs.js';\r\nimport { fetch } from './fetch.js';\r\nimport { log } from './logger.js';\r\nimport { __dirname } from './utils.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/**\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} requestOptions - Additional options for the proxy agent\r\n * to use for a request.\r\n * @param {Object} fetchedModules - An object which tracks which Highcharts\r\n * modules have been fetched.\r\n * @param {boolean} shouldThrowError - A flag to indicate if the error should be\r\n * 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 requestOptions,\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 // 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} coreScripts - Array of Highcharts core scripts to fetch.\r\n * @param {string} moduleScripts - Array of Highcharts modules to fetch.\r\n * @param {string} customScripts - Array of custom script paths to fetch\r\n * (full URLs).\r\n * @param {object} proxyOptions - Options for the proxy agent to use for\r\n * a request.\r\n * @param {object} fetchedModules - An object which tracks which Highcharts\r\n * modules have been fetched.\r\n *\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 proxyOptions,\r\n fetchedModules\r\n) => {\r\n // Configure proxy if exists\r\n let proxyAgent;\r\n const proxyHost = proxyOptions.host;\r\n const proxyPort = proxyOptions.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 // If exists, add proxy agent to request options\r\n const requestOptions = proxyAgent\r\n ? {\r\n agent: proxyAgent,\r\n timeout: envs.SERVER_PROXY_TIMEOUT\r\n }\r\n : {};\r\n\r\n const allFetchPromises = [\r\n ...coreScripts.map((script) =>\r\n fetchAndProcessScript(`${script}`, requestOptions, fetchedModules, true)\r\n ),\r\n ...moduleScripts.map((script) =>\r\n fetchAndProcessScript(`${script}`, requestOptions, fetchedModules)\r\n ),\r\n ...customScripts.map((script) =>\r\n fetchAndProcessScript(`${script}`, requestOptions)\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} options - Object containing all options.\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 (\r\n highchartsOptions,\r\n proxyOptions,\r\n sourcePath\r\n) => {\r\n const version = highchartsOptions.version;\r\n const hcVersion = version === 'latest' || !version ? '' : `${version}/`;\r\n const cdnURL = highchartsOptions.cdnURL || cache.cdnURL;\r\n\r\n log(\r\n 3,\r\n `[cache] Updating cache version to Highcharts: ${hcVersion || 'latest'}.`\r\n );\r\n\r\n const fetchedModules = {};\r\n try {\r\n cache.sources = await fetchScripts(\r\n [\r\n ...highchartsOptions.coreScripts.map((c) => `${cdnURL}${hcVersion}${c}`)\r\n ],\r\n [\r\n ...highchartsOptions.moduleScripts.map((m) =>\r\n m === 'map'\r\n ? `${cdnURL}maps/${hcVersion}modules/${m}`\r\n : `${cdnURL}${hcVersion}modules/${m}`\r\n ),\r\n ...highchartsOptions.indicatorScripts.map(\r\n (i) => `${cdnURL}stock/${hcVersion}indicators/${i}`\r\n )\r\n ],\r\n highchartsOptions.customScripts,\r\n proxyOptions,\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 const options = getOptions();\r\n if (options?.highcharts) {\r\n options.highcharts.version = newVersion;\r\n }\r\n await checkAndUpdateCache(options);\r\n};\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} options - Object containing all options.\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 (options) => {\r\n const { highcharts, server } = options;\r\n const cachePath = join(__dirname, highcharts.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 // 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) || highcharts.forceFetch) {\r\n log(3, '[cache] Fetching and caching Highcharts dependencies.');\r\n fetchedModules = await updateCache(highcharts, server.proxy, 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 { coreScripts, moduleScripts, indicatorScripts } = highcharts;\r\n const numberOfModules =\r\n coreScripts.length + moduleScripts.length + indicatorScripts.length;\r\n\r\n // Compare the loaded highcharts 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 !== highcharts.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 = (moduleScripts || []).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(highcharts, server.proxy, 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(highcharts, fetchedModules);\r\n};\r\n\r\nexport const getCachePath = () =>\r\n join(__dirname, getOptions().highcharts.cachePath);\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\n/* eslint-disable no-undef */\r\n\r\n/**\r\n * Setting the animObject. Called when initing the page.\r\n */\r\nexport function setupHighcharts() {\r\n Highcharts.animObject = function () {\r\n return { duration: 0 };\r\n };\r\n}\r\n\r\n/**\r\n * Creates the actual chart.\r\n *\r\n * @param {object} chartOptions - The options for the Highcharts chart.\r\n * @param {object} options - The export options.\r\n * @param {boolean} displayErrors - A flag indicating whether to display errors.\r\n */\r\nexport function triggerExport(chartOptions, options, displayErrors) {\r\n // Display errors flag taken from chart options nad debugger module\r\n window._displayErrors = displayErrors;\r\n\r\n // Get required functions\r\n const { getOptions, merge, setOptions, wrap } = Highcharts;\r\n\r\n // Create a separate object for a potential setOptions usages in order to\r\n // prevent from polluting other exports that can happen on the same page\r\n Highcharts.setOptionsObj = merge(false, {}, getOptions());\r\n\r\n // Trigger custom code\r\n if (options.customLogic.customCode) {\r\n new Function(options.customLogic.customCode)();\r\n }\r\n\r\n // By default animation is disabled\r\n const chart = {\r\n animation: false\r\n };\r\n\r\n // When straight inject, the size is set through CSS only\r\n if (options.export.strInj) {\r\n chart.height = chartOptions.chart.height;\r\n chart.width = chartOptions.chart.width;\r\n }\r\n\r\n // NOTE: Is this used for anything useful?\r\n window.isRenderComplete = false;\r\n wrap(Highcharts.Chart.prototype, 'init', function (proceed, userOptions, cb) {\r\n // Override userOptions with image friendly options\r\n userOptions = merge(userOptions, {\r\n exporting: {\r\n enabled: false\r\n },\r\n plotOptions: {\r\n series: {\r\n label: {\r\n enabled: false\r\n }\r\n }\r\n },\r\n /* Expects tooltip in userOptions when forExport is true.\r\n https://github.com/highcharts/highcharts/blob/3ad430a353b8056b9e764aa4e5cd6828aa479db2/js/parts/Chart.js#L241\r\n */\r\n tooltip: {}\r\n });\r\n\r\n (userOptions.series || []).forEach(function (series) {\r\n series.animation = false;\r\n });\r\n\r\n // Add flag to know if chart render has been called.\r\n if (!window.onHighchartsRender) {\r\n window.onHighchartsRender = Highcharts.addEvent(this, 'render', () => {\r\n window.isRenderComplete = true;\r\n });\r\n }\r\n\r\n proceed.apply(this, [userOptions, cb]);\r\n });\r\n\r\n wrap(Highcharts.Series.prototype, 'init', function (proceed, chart, options) {\r\n proceed.apply(this, [chart, options]);\r\n });\r\n\r\n // Get the user options\r\n const userOptions = options.export.strInj\r\n ? new Function(`return ${options.export.strInj}`)()\r\n : chartOptions;\r\n\r\n // Merge the globalOptions, themeOptions, options from the wrapped\r\n // setOptions function and user options to create the final options object\r\n const finalOptions = merge(\r\n false,\r\n JSON.parse(options.export.themeOptions),\r\n userOptions,\r\n // Placed it here instead in the init because of the size issues\r\n { chart }\r\n );\r\n\r\n const finalCallback = options.customLogic.callback\r\n ? new Function(`return ${options.customLogic.callback}`)()\r\n : undefined;\r\n\r\n // Set the global options if exist\r\n const globalOptions = JSON.parse(options.export.globalOptions);\r\n if (globalOptions) {\r\n setOptions(globalOptions);\r\n }\r\n\r\n Highcharts[options.export.constr || 'chart'](\r\n 'container',\r\n finalOptions,\r\n finalCallback\r\n );\r\n\r\n // Get the current global options\r\n const defaultOptions = getOptions();\r\n\r\n // Clear it just in case (e.g. the setOptions was used in the customCode)\r\n for (const prop in defaultOptions) {\r\n if (typeof defaultOptions[prop] !== 'function') {\r\n delete defaultOptions[prop];\r\n }\r\n }\r\n\r\n // Set the default options back\r\n setOptions(Highcharts.setOptionsObj);\r\n\r\n // Empty the custom global options object\r\n Highcharts.setOptionsObj = {};\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\n\r\nimport puppeteer from 'puppeteer';\r\n\r\nimport { getCachePath } from './cache.js';\r\nimport { getOptions } from './config.js';\r\nimport { setupHighcharts } from './highcharts.js';\r\nimport { log, logWithStack } from './logger.js';\r\n\r\nimport ExportError from './errors/ExportError.js';\r\n\r\n// Get the template for the page\r\nconst __dirname = url.fileURLToPath(new URL('.', import.meta.url));\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 * 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 function get() {\r\n if (!browser) {\r\n throw new ExportError('[browser] No valid browser has been created.');\r\n }\r\n return browser;\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 async function create(puppeteerArgs) {\r\n // Get the debug options\r\n const { enable: enabledDebug, ...debug } = getOptions().debug;\r\n const launchOptions = {\r\n headless: 'shell',\r\n userDataDir: './tmp/',\r\n args: puppeteerArgs,\r\n handleSIGINT: false,\r\n handleSIGTERM: false,\r\n handleSIGHUP: false,\r\n waitForInitialPage: false,\r\n defaultViewport: null,\r\n ...(enabledDebug && debug)\r\n };\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(launchOptions);\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 // Debug mode inform\r\n if (enabledDebug) {\r\n log(3, `[browser] Launched browser in debug mode.`);\r\n }\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 * 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 async function close() {\r\n // Close the browser when connnected\r\n if (browser?.connected) {\r\n await browser.close();\r\n }\r\n log(4, '[browser] Closed the browser.');\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 async function newPage() {\r\n if (!browser) {\r\n return false;\r\n }\r\n\r\n // Create a page\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\r\n return page;\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 async function clearPage(page, hardReset = false) {\r\n try {\r\n if (!page.isClosed()) {\r\n if (hardReset) {\r\n // Navigate to about:blank\r\n await page.goto('about:blank', { waitUntil: 'domcontentloaded' });\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 }\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 * 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\nasync function setPageContent(page) {\r\n await page.setContent(template, { waitUntil: 'domcontentloaded' });\r\n\r\n // Add all registered Higcharts scripts, quite demanding\r\n await page.addScriptTag({ path: `${getCachePath()}/sources.js` });\r\n\r\n // Set the initial animObject\r\n await page.evaluate(setupHighcharts);\r\n}\r\n\r\nexport default {\r\n get,\r\n create,\r\n close,\r\n newPage,\r\n clearPage\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 { triggerExport } from './highcharts.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 captureBeyondViewport: true,\r\n fullPage: false,\r\n optimizeForSpeed: true,\r\n ...(type !== 'png' ? { quality: 80 } : {}),\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 = async (\r\n page,\r\n height,\r\n width,\r\n encoding,\r\n rasterizationTimeout\r\n) => {\r\n await page.emulateMediaType('screen');\r\n return Promise.race([\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 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/**\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, displayErrors) =>\r\n page.evaluate(triggerExport, chart, options, displayErrors);\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 resource of injectedResources) {\r\n await resource.dispose();\r\n }\r\n\r\n // Destroy old charts after export is done and reset all CSS and script tags\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 // 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 // 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 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 waitUntil: 'domcontentloaded'\r\n });\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 displayErrors\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, displayErrors);\r\n }\r\n }\r\n\r\n // Use resources\r\n const resources = options.customLogic.resources;\r\n if (resources) {\r\n const injectedJs = [];\r\n\r\n // Load custom JS code\r\n if (resources.js) {\r\n injectedJs.push({\r\n content: resources.js\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 const isLocal = !file.startsWith('http') ? true : false;\r\n\r\n // Add each custom script from resources' files\r\n injectedJs.push(\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 }\r\n\r\n for (const jsResource of injectedJs) {\r\n try {\r\n injectedResources.push(await page.addScriptTag(jsResource));\r\n } catch (error) {\r\n logWithStack(2, error, `[export] The JS resource cannot be loaded.`);\r\n }\r\n }\r\n injectedJs.length = 0;\r\n\r\n // Load CSS\r\n const injectedCss = [];\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 injectedCss.push({\r\n url: cssImportPath\r\n });\r\n } else if (options.customLogic.allowFileResources) {\r\n injectedCss.push({\r\n path: path.join(__basedir, cssImportPath)\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 injectedCss.push({\r\n content: resources.css.replace(/@import\\s*([^;]*);/g, '') || ' '\r\n });\r\n\r\n for (const cssResource of injectedCss) {\r\n try {\r\n injectedResources.push(await page.addStyleTag(cssResource));\r\n } catch (error) {\r\n logWithStack(\r\n 2,\r\n error,\r\n `[export] The CSS resource cannot be loaded.`\r\n );\r\n }\r\n }\r\n injectedCss.length = 0;\r\n }\r\n }\r\n\r\n // Get the real chart size and set the zoom accordingly\r\n const size = isSVG\r\n ? await page.evaluate((scale) => {\r\n const svgElement = document.querySelector(\r\n '#chart-container svg:first-of-type'\r\n );\r\n\r\n // Get the values correctly scaled\r\n const chartHeight = svgElement.height.baseVal.value * scale;\r\n const chartWidth = svgElement.width.baseVal.value * scale;\r\n\r\n // In case of SVG the zoom must be set directly for body\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 return {\r\n chartHeight,\r\n chartWidth\r\n };\r\n }, parseFloat(exportOptions.scale))\r\n : await page.evaluate(() => {\r\n // eslint-disable-next-line no-undef\r\n const { chartHeight, chartWidth } = window.Highcharts.charts[0];\r\n\r\n // No need for such scale manipulation in case of other types of exports\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 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 // Get the clip region for the page\r\n const { x, y } = await getClipRegion(page);\r\n\r\n // Set the final viewport now that we have the real height\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 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(\r\n page,\r\n viewportHeight,\r\n viewportWidth,\r\n 'base64',\r\n exportOptions.rasterizationTimeout\r\n );\r\n } else {\r\n throw new ExportError(\r\n `[export] Unsupported output format ${exportOptions.type}.`\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 Highcharts 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 { getOptions } from './config.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// The pool instance\r\nlet pool = false;\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\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 const { debug } = getOptions();\r\n // Set the console listener, if needed\r\n if (debug.enable && debug.listenToConsole) {\r\n page.on('console', (message) => {\r\n console.log(`[debug] ${message.text()}`);\r\n });\r\n }\r\n\r\n // Set the pageerror listener\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 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 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: async (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 await 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 // Create a browser instance with the puppeteer arguments\r\n await createBrowser(config.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 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 * 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 pool with all workers and closing browser.');\r\n\r\n // If still alive, destroy the pool of pages before closing a browser\r\n if (pool) {\r\n // Free up not released workers\r\n for (const worker of pool.used) {\r\n pool.release(worker.resource);\r\n }\r\n\r\n // Destroy the pool if it is still available\r\n if (!pool.destroyed) {\r\n await pool.destroy();\r\n log(4, '[browser] Destroyed the pool of resources.');\r\n }\r\n }\r\n\r\n // Close the browser instance\r\n await 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 const acquireCounter = measureTime();\r\n try {\r\n log(4, '[pool] Acquiring a worker handle.');\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 (options.payload?.requestId\r\n ? `For request with ID ${options.payload?.requestId} - `\r\n : '') +\r\n `Error encountered when acquiring an available entry: ${acquireCounter()}ms.`\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(\r\n (options.payload?.requestId\r\n ? `For request with ID ${options.payload?.requestId} - `\r\n : '') + `Error encountered during export: ${exportCounter()}ms.`\r\n ).setError(result);\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 const getPool = () => pool;\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 all: pool.numFree() + pool.numUsed(),\r\n available: pool.numFree(),\r\n used: pool.numUsed(),\r\n pending: 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, all, available, used, pending } = getPoolInfoJSON();\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(5, `[pool] The number of all created resources: ${all}.`);\r\n log(5, `[pool] The number of available resources: ${available}.`);\r\n log(5, `[pool] The number of acquired resources: ${used}.`);\r\n log(5, `[pool] The number of resources waiting to be acquired: ${pending}.`);\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\nimport { sanitize } from './sanitize.js';\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\r\n const result = exportAsString(\r\n sanitize(options.payload.svg), // #209\r\n options,\r\n endCallback\r\n );\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 info.options.export.type !== 'svg'\r\n ? Buffer.from(info.result, 'base64')\r\n : info.result\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 pool and close browser after finishing single export\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","/*******************************************************************************\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/**\r\n * @overview Used to sanitize the strings coming from the exporting module\r\n * to prevent XSS attacks (with the DOMPurify library).\r\n **/\r\n\r\nimport { JSDOM } from 'jsdom';\r\nimport DOMPurify from 'dompurify';\r\n\r\n/**\r\n * Sanitizes a given HTML string by removing tags and any content within them.\r\n *\r\n * @param {string} input The HTML string to be sanitized.\r\n * @returns {string} The sanitized HTML string.\r\n */\r\nexport function sanitize(input) {\r\n const window = new JSDOM('').window;\r\n const purify = DOMPurify(window);\r\n return purify.sanitize(input, { ADD_TAGS: ['foreignObject'] });\r\n}\r\n\r\nexport default sanitize;\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 { log } from './logger.js';\r\n\r\n// Array that contains ids of all ongoing intervals\r\nconst intervalIds = [];\r\n\r\n/**\r\n * Adds id of a setInterval to the intervalIds array.\r\n *\r\n * @param {NodeJS.Timeout} id - Id of an interval.\r\n */\r\nexport const addInterval = (id) => {\r\n intervalIds.push(id);\r\n};\r\n\r\n/**\r\n * Clears all of ongoing intervals by ids gathered in the intervalIds array.\r\n */\r\nexport const clearAllIntervals = () => {\r\n log(4, `[server] Clearing all registered intervals.`);\r\n for (const id of intervalIds) {\r\n clearInterval(id);\r\n }\r\n};\r\n\r\nexport default {\r\n addInterval,\r\n clearAllIntervals\r\n};\r\n","import { envs } from '../envs.js';\r\nimport { logWithStack } from '../logger.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.OTHER_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 { addInterval } from '../../intervals.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\n/**\r\n * Calculates moving average indicator based on the data from the successRates\r\n * array.\r\n *\r\n * @returns {number} - A moving average for success ratio of the server exports.\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\n/**\r\n * Starts the interval responsible for calculating current success rate ratio\r\n * and gathers\r\n *\r\n * @returns {NodeJS.Timeout} id - Id of an interval.\r\n */\r\nexport const startSuccessRate = () =>\r\n setInterval(() => {\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 }, 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 // Start processing success rate ratio interval and save its id to the array\r\n // for the graceful clearing on shutdown with injected addInterval funtion\r\n addInterval(startSuccessRate());\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 rateLimit from './rate_limit.js';\r\nimport { log, logWithStack } from '../logger.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// Array of an active servers\r\nconst activeServers = new Map();\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 attachServerErrorHandlers = (server) => {\r\n server.on('clientError', (error) => {\r\n logWithStack(1, error, `[server] Client error: ${error.message}`);\r\n });\r\n\r\n server.on('error', (error) => {\r\n logWithStack(1, error, `[server] Server error: ${error.message}`);\r\n });\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 attachServerErrorHandlers(httpServer);\r\n\r\n // Listen\r\n httpServer.listen(serverConfig.port, serverConfig.host);\r\n\r\n // Save the reference to HTTP server\r\n activeServers.set(serverConfig.port, httpServer);\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 attachServerErrorHandlers(httpsServer);\r\n\r\n // Listen\r\n httpsServer.listen(serverConfig.ssl.port, serverConfig.host);\r\n\r\n // Save the reference to HTTPS server\r\n activeServers.set(serverConfig.ssl.port, httpsServer);\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 * Closes all servers associated with Express app instance.\r\n */\r\nexport const closeServers = () => {\r\n log(4, `[server] Closing all servers.`);\r\n for (const [port, server] of activeServers) {\r\n server.close(() => {\r\n activeServers.delete(port);\r\n log(4, `[server] Closed server on port: ${port}.`);\r\n });\r\n }\r\n};\r\n\r\n/**\r\n * Get all servers associated with Express app instance.\r\n *\r\n * @returns {Array} - Servers associated with Express app instance.\r\n */\r\nexport const getServers = () => activeServers;\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 closeServers,\r\n getServers,\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 { envs } from '../../envs.js';\r\n\r\nimport HttpError from '../../errors/HttpError.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 { clearAllIntervals } from './intervals.js';\r\nimport { killPool } from './pool.js';\r\nimport { closeServers } from './server/server.js';\r\n\r\n/**\r\n * Clean up function to trigger before ending process for the graceful shutdown.\r\n *\r\n * @param {number} exitCode - An exit code for the process.exit() function.\r\n */\r\nexport const shutdownCleanUp = async (exitCode) => {\r\n // Await freeing all resources\r\n await Promise.allSettled([\r\n // Clear all ongoing intervals\r\n clearAllIntervals(),\r\n\r\n // Get available server instances (HTTP/HTTPS) and close them\r\n closeServers(),\r\n\r\n // Close pool along with its workers and the browser instance, if exists\r\n killPool()\r\n ]);\r\n\r\n // Exit process with a correct code\r\n process.exit(exitCode);\r\n};\r\n\r\nexport default {\r\n shutdownCleanUp\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\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 { shutdownCleanUp } from './resource_release.js';\r\nimport server, { startServer } from './server/server.js';\r\nimport { printLogo, printUsage } from './utils.js';\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\nconst attachProcessExitListeners = () => {\r\n log(3, '[process] Attaching exit listeners to the process.');\r\n\r\n // Handler for the 'exit'\r\n process.on('exit', (code) => {\r\n log(4, `Process exited with code ${code}.`);\r\n });\r\n\r\n // Handler for the 'SIGINT'\r\n process.on('SIGINT', async (name, code) => {\r\n log(4, `The ${name} event with code: ${code}.`);\r\n await shutdownCleanUp(0);\r\n });\r\n\r\n // Handler for the 'SIGTERM'\r\n process.on('SIGTERM', async (name, code) => {\r\n log(4, `The ${name} event with code: ${code}.`);\r\n await shutdownCleanUp(0);\r\n });\r\n\r\n // Handler for the 'SIGHUP'\r\n process.on('SIGHUP', async (name, code) => {\r\n log(4, `The ${name} event with code: ${code}.`);\r\n await shutdownCleanUp(0);\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 shutdownCleanUp(1);\r\n });\r\n};\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 // Attach process' exit listeners\r\n if (options.other.listenToProcessExits) {\r\n attachProcessExitListeners();\r\n }\r\n\r\n // Check if cache needs to be updated\r\n await checkAndUpdateCache(options);\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\r\n // Exporting\r\n initExport,\r\n singleExport,\r\n batchExport,\r\n startExport,\r\n\r\n // Pool\r\n initPool,\r\n killPool,\r\n\r\n // Other\r\n setOptions,\r\n shutdownCleanUp,\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":["scriptsNames","core","modules","indicators","defaultConfig","puppeteer","args","value","type","description","highcharts","version","envLink","cdnURL","coreScripts","moduleScripts","indicatorScripts","customScripts","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","proxy","timeout","rateLimiting","maxRequests","window","delay","trustProxy","skipKey","skipToken","ssl","force","certPath","pool","minWorkers","maxWorkers","workLimit","acquireTimeout","createTimeout","destroyTimeout","idleTimeout","createRetryInterval","reaperInterval","logging","level","file","dest","ui","route","other","nodeEnv","listenToProcessExits","noLogo","hardResetPage","debug","headless","devtools","listenToConsole","dumpio","slowMo","debuggingPort","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","dotenv","config","v","filterArray","z","string","transform","split","map","trim","filter","length","enum","values","refine","isNaN","parseFloat","envs","object","HIGHCHARTS_VERSION","test","HIGHCHARTS_CDN_URL","startsWith","HIGHCHARTS_CORE_SCRIPTS","HIGHCHARTS_MODULE_SCRIPTS","HIGHCHARTS_INDICATOR_SCRIPTS","HIGHCHARTS_FORCE_FETCH","HIGHCHARTS_CACHE_PATH","HIGHCHARTS_ADMIN_TOKEN","EXPORT_TYPE","EXPORT_CONSTR","EXPORT_DEFAULT_HEIGHT","EXPORT_DEFAULT_WIDTH","EXPORT_DEFAULT_SCALE","EXPORT_RASTERIZATION_TIMEOUT","CUSTOM_LOGIC_ALLOW_CODE_EXECUTION","CUSTOM_LOGIC_ALLOW_FILE_RESOURCES","SERVER_ENABLE","SERVER_HOST","SERVER_PORT","SERVER_BENCHMARKING","SERVER_PROXY_HOST","SERVER_PROXY_PORT","SERVER_PROXY_TIMEOUT","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","SERVER_SSL_ENABLE","SERVER_SSL_FORCE","SERVER_SSL_PORT","SERVER_SSL_CERT_PATH","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","LOGGING_LEVEL","LOGGING_FILE","LOGGING_DEST","UI_ENABLE","UI_ROUTE","OTHER_NODE_ENV","OTHER_LISTEN_TO_PROCESS_EXITS","OTHER_NO_LOGO","OTHER_HARD_RESET_PAGE","DEBUG_ENABLE","DEBUG_HEADLESS","DEBUG_DEVTOOLS","DEBUG_LISTEN_TO_CONSOLE","DEBUG_DUMPIO","DEBUG_SLOW_MO","DEBUG_DEBUGGING_PORT","partial","parse","process","env","colors","toConsole","toFile","pathCreated","levelsDesc","title","color","listeners","key","option","entries","logToFile","texts","prefix","existsSync","mkdirSync","appendFile","concat","error","console","log","newLevel","Date","toString","fn","apply","logWithStack","customMessage","mainMessage","stackMessage","stack","slice","setLogLevel","enableFileLogging","logDest","logFile","endsWith","__dirname","fileURLToPath","URL","url","fixType","formats","outType","pop","find","t","handleResources","allowedProps","handledResources","correctResources","isCorrectJSON","readFileSync","files","propName","item","data","parsedData","JSON","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","hrtime","bigint","Number","generalOptions","getOptions","mergeConfigOptions","newOptions","mergedOptions","updateDefaultConfig","configObj","customObj","customValue","initOptions","items","recursiveProps","objectToUpdate","nestedNames","shift","assign","async","fetch","requestOptions","Promise","resolve","reject","protocol","https","http","getProtocol","get","res","on","chunk","text","ExportError","Error","constructor","super","this","setError","statusCode","cache","activeManifest","sources","hcVersion","extractVersion","indexOf","fetchAndProcessScript","script","fetchedModules","shouldThrowError","response","updateCache","highchartsOptions","proxyOptions","sourcePath","proxyAgent","proxyHost","proxyPort","HttpsProxyAgent","agent","allFetchPromises","all","fetchScripts","c","m","writeFileSync","checkAndUpdateCache","manifestPath","requestUpdate","manifest","moduleMap","numberOfModules","some","moduleName","newManifest","saveConfigToManifest","getCachePath","cache$1","newVersion","setupHighcharts","Highcharts","animObject","duration","triggerExport","chartOptions","displayErrors","_displayErrors","merge","setOptions","wrap","setOptionsObj","Function","chart","animation","strInj","isRenderComplete","Chart","proceed","userOptions","cb","exporting","enabled","plotOptions","series","label","tooltip","onHighchartsRender","addEvent","Series","finalOptions","finalCallback","defaultOptions","prop","template","fs","browser","newPage","page","setCacheEnabled","setPageContent","setContent","waitUntil","addScriptTag","path","evaluate","__basedir","setAsConfig","puppeteerExport","injectedResources","clearInjected","resource","dispose","oldCharts","charts","oldChart","destroy","scriptsToRemove","document","getElementsByTagName","stylesToRemove","linksToRemove","element","remove","exportOptions","debugger","isSVG","svgTemplate","injectedJs","js","push","content","isLocal","jsResource","injectedCss","css","cssImports","match","cssImportPath","cssResource","addStyleTag","size","svgElement","querySelector","chartHeight","baseVal","chartWidth","body","style","zoom","margin","viewportHeight","Math","ceil","viewportWidth","x","y","$eval","getBoundingClientRect","trunc","getClipRegion","setViewport","deviceScaleFactor","outerHTML","createSVG","encoding","clip","race","screenshot","captureBeyondViewport","fullPage","optimizeForSpeed","quality","omitBackground","_resolve","setTimeout","createImage","emulateMediaType","pdf","createPDF","stats","performedExports","exportAttempts","exportFromSvgAttempts","timeSpent","droppedExports","spentAverage","poolConfig","factory","create","id","uuid","startDate","getTime","browserNewPage","isClosed","errorMessage","innerHTML","workCount","random","validate","workerHandle","close","initPool","puppeteerArgs","enabledDebug","launchOptions","userDataDir","handleSIGINT","handleSIGTERM","handleSIGHUP","waitForInitialPage","defaultViewport","tryCount","open","launch","createBrowser","parseInt","Pool","acquireTimeoutMillis","createTimeoutMillis","destroyTimeoutMillis","idleTimeoutMillis","createRetryIntervalMillis","reapIntervalMillis","propagateCreateError","hardReset","goto","clearPage","eventId","initialResources","acquire","promise","release","killPool","worker","used","destroyed","connected","browserClose","postWork","getPoolInfo","acquireCounter","payload","requestId","workStart","exportCounter","result","exportTime","getPoolInfoJSON","numFree","numUsed","available","pending","numPendingAcquires","pool$1","startExport","settings","endCallback","svg","initExportSettings","exportAsString","input","JSDOM","DOMPurify","sanitize","ADD_TAGS","doStraightInject","doExport","findChartSize","precision","multiplier","pow","roundNumber","sourceHeight","sourceWidth","param","chartJson","customLogicOptions","allowCodeExecutionScoped","optionsName","stringToExport","chartJSON","intervalIds","clearAllIntervals","clearInterval","logErrorMiddleware","req","next","returnErrorMiddleware","stCode","status","json","rateLimit","app","limitConfig","msg","rateOptions","limiter","windowMs","delayMs","handler","request","format","send","default","skip","query","access_token","use","HttpError","setStatus","reversedMime","png","jpeg","gif","requestsCounter","beforeRequest","afterRequest","doCallbacks","callbacks","uniqueId","callResponse","exportHandler","stopCounter","headers","connection","remoteAddress","connectionAborted","socket","toLowerCase","substr","b64","noDownload","pattern","isPrivateRangeUrlFound","info","removeAllListeners","Buffer","from","header","attachment","params","filename","pkgFile","pather","serverStartTime","successRates","addHealthRoutes","setInterval","successRatio","_","period","movingAverage","reduce","a","b","bootTime","uptime","floor","highchartsVersion","averageProcessingTime","failedExports","sucessRatio","toFixed","svgExportAttempts","jsonExportAttempts","activeServers","Map","express","disable","cors","storage","multer","memoryStorage","upload","limits","fieldSize","limit","urlencoded","extended","none","attachServerErrorHandlers","startServer","serverConfig","httpServer","createServer","listen","set","cert","fsPromises","readFile","posix","httpsServer","NaN","static","healthRoute","post","exportRoutes","sendFile","uiRoute","adminToken","token","vSwitchRoute","errorHandler","closeServers","delete","getServers","enableRateLimiting","getExpress","getApp","middlewares","shutdownCleanUp","exitCode","allSettled","exit","index","initExport","initLogging","code","singleExport","batchExport","batchFunctions","pair","configIndex","findIndex","arg","fileName","loadConfigFile","showUsage","propertiesChain","argumentType","pairArgumentValue","mapToNewConfig","oldOptions","manualConfig","configFileName","configFile","choice","prompts","onSubmit","p","categories","questionsCounter","allQuestions","section","prompt","answer","module","writeFile","printLogo","packageVersion"],"mappings":"mnBAeO,MAAMA,EAAe,CAC1BC,KAAM,CAAC,aAAc,kBAAmB,iBACxCC,QAAS,CACP,QACA,MACA,QACA,YACA,cACA,uBACA,gBAEA,eACA,QACA,OACA,aACA,mBACA,eACA,cACA,UACA,UACA,cACA,WACA,UACA,YACA,cACA,YACA,sBACA,SACA,SACA,WACA,aACA,YACA,eACA,yBACA,SACA,eACA,YACA,kBACA,SACA,cACA,mBACA,eACA,cACA,eAEA,cACA,WACA,eACA,WACA,SACA,OACA,WACA,YACA,SACA,qBACA,aACA,WACA,WACA,WACA,WACA,eACA,UACA,kBACA,oBACA,aACA,WAEFC,WAAY,CAAC,mBAKFC,EAAgB,CAC3BC,UAAW,CACTC,KAAM,CACJC,MAAO,CACL,mCACA,kBACA,0CACA,2BACA,kCACA,kCACA,wCACA,2CACA,qBACA,4BACA,2CACA,uDACA,6BACA,yBACA,0BACA,+BACA,uBACA,uFACA,yBACA,oCACA,oBACA,0BACA,8CACA,2BACA,0BACA,6BACA,mCACA,wCACA,mCACA,2BACA,kCACA,uBACA,iBACA,yBACA,8BACA,oBACA,2BACA,eACA,6BACA,iBACA,aACA,eACA,sBACA,cACA,yBACA,oBACA,uBAEFC,KAAM,WACNC,YAAa,0CAGjBC,WAAY,CACVC,QAAS,CACPJ,MAAO,SACPC,KAAM,SACNI,QAAS,qBACTH,YAAa,sCAEfI,OAAQ,CACNN,MAAO,+BACPC,KAAM,SACNI,QAAS,qBACTH,YAAa,kDAEfK,YAAa,CACXP,MAAOP,EAAaC,KACpBO,KAAM,WACNI,QAAS,0BACTH,YAAa,yCAEfM,cAAe,CACbR,MAAOP,EAAaE,QACpBM,KAAM,WACNI,QAAS,4BACTH,YAAa,uCAEfO,iBAAkB,CAChBT,MAAOP,EAAaG,WACpBK,KAAM,WACNI,QAAS,+BACTH,YAAa,0CAEfQ,cAAe,CACbV,MAAO,CACL,wEACA,kGAEFC,KAAM,WACNC,YAAa,uDAEfS,WAAY,CACVX,OAAO,EACPC,KAAM,UACNI,QAAS,yBACTH,YACE,iFAEJU,UAAW,CACTZ,MAAO,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"} \ No newline at end of file +{"version":3,"file":"index.esm.js","sources":["../lib/schemas/config.js","../lib/envs.js","../lib/logger.js","../lib/utils.js","../lib/config.js","../lib/fetch.js","../lib/errors/ExportError.js","../lib/cache.js","../lib/highcharts.js","../lib/browser.js","../lib/export.js","../templates/svg_export/svg_export.js","../lib/pool.js","../lib/chart.js","../lib/sanitize.js","../lib/intervals.js","../lib/server/error.js","../lib/server/rate_limit.js","../lib/errors/HttpError.js","../lib/server/routes/change_hc_version.js","../lib/server/routes/export.js","../lib/server/routes/health.js","../lib/server/server.js","../lib/server/routes/ui.js","../lib/resource_release.js","../lib/index.js"],"sourcesContent":["/*******************************************************************************\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// Possible names for Highcharts scripts\r\nexport const scriptsNames = {\r\n core: ['highcharts', 'highcharts-more', 'highcharts-3d'],\r\n modules: [\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 indicators: ['indicators-all']\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 '--allow-running-insecure-content',\r\n '--ash-no-nudges',\r\n '--autoplay-policy=user-gesture-required',\r\n '--block-new-web-contents',\r\n '--disable-accelerated-2d-canvas',\r\n '--disable-background-networking',\r\n '--disable-background-timer-throttling',\r\n '--disable-backgrounding-occluded-windows',\r\n '--disable-breakpad',\r\n '--disable-checker-imaging',\r\n '--disable-client-side-phishing-detection',\r\n '--disable-component-extensions-with-background-pages',\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=CalculateNativeWinOcclusion,InterestFeedContentSuggestions,WebOTP',\r\n '--disable-hang-monitor',\r\n '--disable-ipc-flooding-protection',\r\n '--disable-logging',\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-search-engine-choice-screen',\r\n '--disable-session-crashed-bubble',\r\n '--disable-setuid-sandbox',\r\n '--disable-site-isolation-trials',\r\n '--disable-speech-api',\r\n '--disable-sync',\r\n '--enable-unsafe-webgpu',\r\n '--hide-crash-restore-bubble',\r\n '--hide-scrollbars',\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-startup-window',\r\n '--no-zygote',\r\n '--password-store=basic',\r\n '--process-per-tab',\r\n '--use-mock-keychain'\r\n ],\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 type: 'string',\r\n envLink: 'HIGHCHARTS_VERSION',\r\n description: 'The Highcharts version to be used.'\r\n },\r\n cdnURL: {\r\n value: 'https://code.highcharts.com/',\r\n type: 'string',\r\n envLink: 'HIGHCHARTS_CDN_URL',\r\n description: 'The CDN URL for Highcharts scripts to be used.'\r\n },\r\n coreScripts: {\r\n value: scriptsNames.core,\r\n type: 'string[]',\r\n envLink: 'HIGHCHARTS_CORE_SCRIPTS',\r\n description: 'The core Highcharts scripts to fetch.'\r\n },\r\n moduleScripts: {\r\n value: scriptsNames.modules,\r\n type: 'string[]',\r\n envLink: 'HIGHCHARTS_MODULE_SCRIPTS',\r\n description: 'The modules of Highcharts to fetch.'\r\n },\r\n indicatorScripts: {\r\n value: scriptsNames.indicators,\r\n type: 'string[]',\r\n envLink: 'HIGHCHARTS_INDICATOR_SCRIPTS',\r\n description: 'The indicators of Highcharts to fetch.'\r\n },\r\n customScripts: {\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 custom scripts or dependencies to fetch.'\r\n },\r\n forceFetch: {\r\n value: false,\r\n type: 'boolean',\r\n envLink: 'HIGHCHARTS_FORCE_FETCH',\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 value: '.cache',\r\n type: 'string',\r\n envLink: 'HIGHCHARTS_CACHE_PATH',\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 value: 'png',\r\n type: 'string',\r\n envLink: 'EXPORT_TYPE',\r\n description: 'The file export format. It can be jpeg, png, pdf, or svg.'\r\n },\r\n constr: {\r\n value: 'chart',\r\n type: 'string',\r\n envLink: 'EXPORT_CONSTR',\r\n description:\r\n 'The constructor to use. Can be chart, stockChart, mapChart, or ganttChart.'\r\n },\r\n defaultHeight: {\r\n value: 400,\r\n type: 'number',\r\n envLink: 'EXPORT_DEFAULT_HEIGHT',\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 value: 600,\r\n type: 'number',\r\n envLink: 'EXPORT_DEFAULT_WIDTH',\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 value: 1,\r\n type: 'number',\r\n envLink: 'EXPORT_DEFAULT_SCALE',\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 value: false,\r\n type: 'number',\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 value: false,\r\n type: 'number',\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 value: 1500,\r\n type: 'number',\r\n envLink: 'EXPORT_RASTERIZATION_TIMEOUT',\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 value: false,\r\n type: 'boolean',\r\n envLink: 'CUSTOM_LOGIC_ALLOW_CODE_EXECUTION',\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 value: false,\r\n type: 'boolean',\r\n envLink: 'CUSTOM_LOGIC_ALLOW_FILE_RESOURCES',\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 value: false,\r\n type: 'boolean',\r\n envLink: 'SERVER_ENABLE',\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 value: '0.0.0.0',\r\n type: 'string',\r\n envLink: 'SERVER_HOST',\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 value: 7801,\r\n type: 'number',\r\n envLink: 'SERVER_PORT',\r\n description: 'The server port when enabled.'\r\n },\r\n benchmarking: {\r\n value: false,\r\n type: 'boolean',\r\n envLink: 'SERVER_BENCHMARKING',\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 proxy: {\r\n host: {\r\n value: false,\r\n type: 'string',\r\n envLink: 'SERVER_PROXY_HOST',\r\n cliName: 'proxyHost',\r\n description: 'The host of the proxy server to use, if it exists.'\r\n },\r\n port: {\r\n value: 8080,\r\n type: 'number',\r\n envLink: 'SERVER_PROXY_PORT',\r\n cliName: 'proxyPort',\r\n description: 'The port of the proxy server to use, if it exists.'\r\n },\r\n timeout: {\r\n value: 5000,\r\n type: 'number',\r\n envLink: 'SERVER_PROXY_TIMEOUT',\r\n cliName: 'proxyTimeout',\r\n description: 'The timeout for the proxy server to use, if it exists.'\r\n }\r\n },\r\n rateLimiting: {\r\n enable: {\r\n value: false,\r\n type: 'boolean',\r\n envLink: 'SERVER_RATE_LIMITING_ENABLE',\r\n cliName: 'enableRateLimiting',\r\n description: 'Enables rate limiting for the server.'\r\n },\r\n maxRequests: {\r\n value: 10,\r\n type: 'number',\r\n envLink: 'SERVER_RATE_LIMITING_MAX_REQUESTS',\r\n legacyName: 'rateLimit',\r\n description: 'The maximum number of requests allowed in one minute.'\r\n },\r\n window: {\r\n value: 1,\r\n type: 'number',\r\n envLink: 'SERVER_RATE_LIMITING_WINDOW',\r\n description: 'The time window, in minutes, for the rate limiting.'\r\n },\r\n delay: {\r\n value: 0,\r\n type: 'number',\r\n envLink: 'SERVER_RATE_LIMITING_DELAY',\r\n description:\r\n 'The delay duration for each successive request before reaching the maximum limit.'\r\n },\r\n trustProxy: {\r\n value: false,\r\n type: 'boolean',\r\n envLink: 'SERVER_RATE_LIMITING_TRUST_PROXY',\r\n description: 'Set this to true if the server is behind a load balancer.'\r\n },\r\n skipKey: {\r\n value: false,\r\n type: 'string',\r\n envLink: 'SERVER_RATE_LIMITING_SKIP_KEY',\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 value: false,\r\n type: 'string',\r\n envLink: 'SERVER_RATE_LIMITING_SKIP_TOKEN',\r\n description:\r\n 'Allows bypassing the rate limiter and should be provided with the skipKey argument.'\r\n }\r\n },\r\n ssl: {\r\n enable: {\r\n value: false,\r\n type: 'boolean',\r\n envLink: 'SERVER_SSL_ENABLE',\r\n cliName: 'enableSsl',\r\n description: 'Enables or disables the SSL protocol.'\r\n },\r\n force: {\r\n value: false,\r\n type: 'boolean',\r\n envLink: 'SERVER_SSL_FORCE',\r\n cliName: 'sslForce',\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 value: 443,\r\n type: 'number',\r\n envLink: 'SERVER_SSL_PORT',\r\n cliName: 'sslPort',\r\n description: 'The port on which to run the SSL server.'\r\n },\r\n certPath: {\r\n value: false,\r\n type: 'string',\r\n envLink: 'SERVER_SSL_CERT_PATH',\r\n legacyName: 'sslPath',\r\n description: 'The path to the SSL certificate/key file.'\r\n }\r\n }\r\n },\r\n pool: {\r\n minWorkers: {\r\n value: 4,\r\n type: 'number',\r\n envLink: 'POOL_MIN_WORKERS',\r\n description: 'The number of minimum and initial pool workers to spawn.'\r\n },\r\n maxWorkers: {\r\n value: 8,\r\n type: 'number',\r\n envLink: 'POOL_MAX_WORKERS',\r\n legacyName: 'workers',\r\n description: 'The number of maximum pool workers to spawn.'\r\n },\r\n workLimit: {\r\n value: 40,\r\n type: 'number',\r\n envLink: 'POOL_WORK_LIMIT',\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 value: 5000,\r\n type: 'number',\r\n envLink: 'POOL_ACQUIRE_TIMEOUT',\r\n description:\r\n 'The duration, in milliseconds, to wait for acquiring a resource.'\r\n },\r\n createTimeout: {\r\n value: 5000,\r\n type: 'number',\r\n envLink: 'POOL_CREATE_TIMEOUT',\r\n description:\r\n 'The duration, in milliseconds, to wait for creating a resource.'\r\n },\r\n destroyTimeout: {\r\n value: 5000,\r\n type: 'number',\r\n envLink: 'POOL_DESTROY_TIMEOUT',\r\n description:\r\n 'The duration, in milliseconds, to wait for destroying a resource.'\r\n },\r\n idleTimeout: {\r\n value: 30000,\r\n type: 'number',\r\n envLink: 'POOL_IDLE_TIMEOUT',\r\n description:\r\n 'The duration, in milliseconds, after which an idle resource is destroyed.'\r\n },\r\n createRetryInterval: {\r\n value: 200,\r\n type: 'number',\r\n envLink: 'POOL_CREATE_RETRY_INTERVAL',\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 value: 1000,\r\n type: 'number',\r\n envLink: 'POOL_REAPER_INTERVAL',\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 value: false,\r\n type: 'boolean',\r\n envLink: 'POOL_BENCHMARKING',\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 },\r\n logging: {\r\n level: {\r\n value: 4,\r\n type: 'number',\r\n envLink: 'LOGGING_LEVEL',\r\n cliName: 'logLevel',\r\n description: 'The logging level to be used.'\r\n },\r\n file: {\r\n value: 'highcharts-export-server.log',\r\n type: 'string',\r\n envLink: 'LOGGING_FILE',\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 value: 'log/',\r\n type: 'string',\r\n envLink: 'LOGGING_DEST',\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 value: false,\r\n type: 'boolean',\r\n envLink: 'UI_ENABLE',\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 value: '/',\r\n type: 'string',\r\n envLink: 'UI_ROUTE',\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 nodeEnv: {\r\n value: 'production',\r\n type: 'string',\r\n envLink: 'OTHER_NODE_ENV',\r\n description: 'The type of Node.js environment.'\r\n },\r\n listenToProcessExits: {\r\n value: true,\r\n type: 'boolean',\r\n envLink: 'OTHER_LISTEN_TO_PROCESS_EXITS',\r\n description: 'Decides whether or not to attach process.exit handlers.'\r\n },\r\n noLogo: {\r\n value: false,\r\n type: 'boolean',\r\n envLink: 'OTHER_NO_LOGO',\r\n description:\r\n 'Skip printing the logo on a startup. Will be replaced by a simple text.'\r\n },\r\n hardResetPage: {\r\n value: false,\r\n type: 'boolean',\r\n envLink: 'OTHER_HARD_RESET_PAGE',\r\n description: 'Decides if the page content should be reset entirely.'\r\n }\r\n },\r\n debug: {\r\n enable: {\r\n value: false,\r\n type: 'boolean',\r\n envLink: 'DEBUG_ENABLE',\r\n cliName: 'enableDebug',\r\n description: 'Enables or disables debug mode for the underlying browser.'\r\n },\r\n headless: {\r\n value: true,\r\n type: 'boolean',\r\n envLink: 'DEBUG_HEADLESS',\r\n description:\r\n 'Controls the mode in which the browser is launched when in the debug mode.'\r\n },\r\n devtools: {\r\n value: false,\r\n type: 'boolean',\r\n envLink: 'DEBUG_DEVTOOLS',\r\n description:\r\n 'Decides whether to enable DevTools when the browser is in a headful state.'\r\n },\r\n listenToConsole: {\r\n value: false,\r\n type: 'boolean',\r\n envLink: 'DEBUG_LISTEN_TO_CONSOLE',\r\n description:\r\n 'Decides whether to enable a listener for console messages sent from the browser.'\r\n },\r\n dumpio: {\r\n value: false,\r\n type: 'boolean',\r\n envLink: 'DEBUG_DUMPIO',\r\n description:\r\n 'Redirects browser process stdout and stderr to process.stdout and process.stderr.'\r\n },\r\n slowMo: {\r\n value: 0,\r\n type: 'number',\r\n envLink: 'DEBUG_SLOW_MO',\r\n description:\r\n 'Slows down Puppeteer operations by the specified number of milliseconds.'\r\n },\r\n debuggingPort: {\r\n value: 9222,\r\n type: 'number',\r\n envLink: 'DEBUG_DEBUGGING_PORT',\r\n description: 'Specifies the debugging port.'\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: 'coreScripts',\r\n message: 'Available core scripts',\r\n instructions: 'Space: Select specific, A: Select all, Enter: Confirm.',\r\n choices: defaultConfig.highcharts.coreScripts.value\r\n },\r\n {\r\n type: 'multiselect',\r\n name: 'moduleScripts',\r\n message: 'Available module scripts',\r\n instructions: 'Space: Select specific, A: Select all, Enter: Confirm.',\r\n choices: defaultConfig.highcharts.moduleScripts.value\r\n },\r\n {\r\n type: 'multiselect',\r\n name: 'indicatorScripts',\r\n message: 'Available indicator scripts',\r\n instructions: 'Space: Select specific, A: Select all, Enter: Confirm.',\r\n choices: defaultConfig.highcharts.indicatorScripts.value\r\n },\r\n {\r\n type: 'list',\r\n name: 'customScripts',\r\n message: 'Custom scripts',\r\n initial: defaultConfig.highcharts.customScripts.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: 'text',\r\n name: 'proxy.host',\r\n message: 'The host of the proxy server to use',\r\n initial: defaultConfig.server.proxy.host.value\r\n },\r\n {\r\n type: 'number',\r\n name: 'proxy.port',\r\n message: 'The port of the proxy server to use',\r\n initial: defaultConfig.server.proxy.port.value\r\n },\r\n {\r\n type: 'number',\r\n name: 'proxy.timeout',\r\n message: 'The timeout for the proxy server to use',\r\n initial: defaultConfig.server.proxy.timeout.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 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 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 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: 'text',\r\n name: 'nodeEnv',\r\n message: 'The type of Node.js environment',\r\n initial: defaultConfig.other.nodeEnv.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.other.listenToProcessExits.value\r\n },\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 type: 'toggle',\r\n name: 'hardResetPage',\r\n message: 'Decides if the page content should be reset entirely',\r\n initial: defaultConfig.other.hardResetPage.value\r\n }\r\n ],\r\n debug: [\r\n {\r\n type: 'toggle',\r\n name: 'enable',\r\n message: 'Enables debug mode for the browser instance',\r\n initial: defaultConfig.debug.enable.value\r\n },\r\n {\r\n type: 'toggle',\r\n name: 'headless',\r\n message: 'The mode setting for the browser',\r\n initial: defaultConfig.debug.headless.value\r\n },\r\n {\r\n type: 'toggle',\r\n name: 'devtools',\r\n message: 'The DevTools for the headful browser',\r\n initial: defaultConfig.debug.devtools.value\r\n },\r\n {\r\n type: 'toggle',\r\n name: 'listenToConsole',\r\n message: 'The event listener for console messages from the browser',\r\n initial: defaultConfig.debug.listenToConsole.value\r\n },\r\n {\r\n type: 'toggle',\r\n name: 'dumpio',\r\n message: 'Redirects the browser stdout and stderr to NodeJS process',\r\n initial: defaultConfig.debug.dumpio.value\r\n },\r\n {\r\n type: 'number',\r\n name: 'slowMo',\r\n message: 'Puppeteer operations slow down in milliseconds',\r\n initial: defaultConfig.debug.slowMo.value\r\n },\r\n {\r\n type: 'number',\r\n name: 'debuggingPort',\r\n message: 'The port number for debugging',\r\n initial: defaultConfig.debug.debuggingPort.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 * @fileoverview\r\n * This file is responsible for parsing the environment variables with the 'zod'\r\n * library. The parsed environment variables are then exported to be used\r\n * in the application as \"envs\". We should not use process.env directly\r\n * in the application as these would not be parsed properly.\r\n *\r\n * The environment variables are parsed and validated only once when\r\n * the application starts. We should write a custom validator or a transformer\r\n * for each of the options.\r\n */\r\n\r\nimport dotenv from 'dotenv';\r\nimport { z } from 'zod';\r\n\r\nimport { scriptsNames } from './schemas/config.js';\r\n\r\n// Load .env into environment variables\r\ndotenv.config();\r\n\r\n// Object with custom validators and transformers, to avoid repetition\r\n// in the Config object\r\nconst v = {\r\n // Splits string value into elements in an array, trims every element, checks\r\n // if an array is correct, if it is empty, and if it is, returns undefined\r\n array: (filterArray) =>\r\n z\r\n .string()\r\n .transform((value) =>\r\n value\r\n .split(',')\r\n .map((value) => value.trim())\r\n .filter((value) => filterArray.includes(value))\r\n )\r\n .transform((value) => (value.length ? value : undefined)),\r\n\r\n // Allows only true, false and correctly parse the value to boolean\r\n // or no value in which case the returned value will be undefined\r\n boolean: () =>\r\n z\r\n .enum(['true', 'false', ''])\r\n .transform((value) => (value !== '' ? value === 'true' : undefined)),\r\n\r\n // Allows passed values or no value in which case the returned value will\r\n // be undefined\r\n enum: (values) =>\r\n z\r\n .enum([...values, ''])\r\n .transform((value) => (value !== '' ? value : undefined)),\r\n\r\n // Trims the string value and checks if it is empty or contains stringified\r\n // values such as false, undefined, null, NaN, if it does, returns undefined\r\n string: () =>\r\n z\r\n .string()\r\n .trim()\r\n .refine(\r\n (value) =>\r\n !['false', 'undefined', 'null', 'NaN'].includes(value) ||\r\n value === '',\r\n (value) => ({\r\n message: `The string contains forbidden values, received '${value}'`\r\n })\r\n )\r\n .transform((value) => (value !== '' ? value : undefined)),\r\n\r\n // Allows positive numbers or no value in which case the returned value will\r\n // be undefined\r\n positiveNum: () =>\r\n z\r\n .string()\r\n .trim()\r\n .refine(\r\n (value) =>\r\n value === '' || (!isNaN(parseFloat(value)) && parseFloat(value) > 0),\r\n (value) => ({\r\n message: `The value must be numeric and positive, received '${value}'`\r\n })\r\n )\r\n .transform((value) => (value !== '' ? parseFloat(value) : undefined)),\r\n\r\n // Allows non-negative numbers or no value in which case the returned value\r\n // will be undefined\r\n nonNegativeNum: () =>\r\n z\r\n .string()\r\n .trim()\r\n .refine(\r\n (value) =>\r\n value === '' || (!isNaN(parseFloat(value)) && parseFloat(value) >= 0),\r\n (value) => ({\r\n message: `The value must be numeric and non-negative, received '${value}'`\r\n })\r\n )\r\n .transform((value) => (value !== '' ? parseFloat(value) : undefined))\r\n};\r\n\r\nexport const Config = z.object({\r\n // highcharts\r\n HIGHCHARTS_VERSION: z\r\n .string()\r\n .trim()\r\n .refine(\r\n (value) => /^(latest|\\d+(\\.\\d+){0,2})$/.test(value) || value === '',\r\n (value) => ({\r\n message: `HIGHCHARTS_VERSION must be 'latest', a major version, or in the form XX.YY.ZZ, received '${value}'`\r\n })\r\n )\r\n .transform((value) => (value !== '' ? value : undefined)),\r\n HIGHCHARTS_CDN_URL: z\r\n .string()\r\n .trim()\r\n .refine(\r\n (value) =>\r\n value.startsWith('https://') ||\r\n value.startsWith('http://') ||\r\n value === '',\r\n (value) => ({\r\n message: `Invalid value for HIGHCHARTS_CDN_URL. It should start with http:// or https://, received '${value}'`\r\n })\r\n )\r\n .transform((value) => (value !== '' ? value : undefined)),\r\n HIGHCHARTS_CORE_SCRIPTS: v.array(scriptsNames.core),\r\n HIGHCHARTS_MODULE_SCRIPTS: v.array(scriptsNames.modules),\r\n HIGHCHARTS_INDICATOR_SCRIPTS: v.array(scriptsNames.indicators),\r\n HIGHCHARTS_FORCE_FETCH: v.boolean(),\r\n HIGHCHARTS_CACHE_PATH: v.string(),\r\n HIGHCHARTS_ADMIN_TOKEN: v.string(),\r\n\r\n // export\r\n EXPORT_TYPE: v.enum(['jpeg', 'png', 'pdf', 'svg']),\r\n EXPORT_CONSTR: v.enum(['chart', 'stockChart', 'mapChart', 'ganttChart']),\r\n EXPORT_DEFAULT_HEIGHT: v.positiveNum(),\r\n EXPORT_DEFAULT_WIDTH: v.positiveNum(),\r\n EXPORT_DEFAULT_SCALE: v.positiveNum(),\r\n EXPORT_RASTERIZATION_TIMEOUT: v.nonNegativeNum(),\r\n\r\n // custom\r\n CUSTOM_LOGIC_ALLOW_CODE_EXECUTION: v.boolean(),\r\n CUSTOM_LOGIC_ALLOW_FILE_RESOURCES: v.boolean(),\r\n\r\n // server\r\n SERVER_ENABLE: v.boolean(),\r\n SERVER_HOST: v.string(),\r\n SERVER_PORT: v.positiveNum(),\r\n SERVER_BENCHMARKING: v.boolean(),\r\n\r\n // server proxy\r\n SERVER_PROXY_HOST: v.string(),\r\n SERVER_PROXY_PORT: v.positiveNum(),\r\n SERVER_PROXY_TIMEOUT: v.nonNegativeNum(),\r\n\r\n // server rate limiting\r\n SERVER_RATE_LIMITING_ENABLE: v.boolean(),\r\n SERVER_RATE_LIMITING_MAX_REQUESTS: v.nonNegativeNum(),\r\n SERVER_RATE_LIMITING_WINDOW: v.nonNegativeNum(),\r\n SERVER_RATE_LIMITING_DELAY: v.nonNegativeNum(),\r\n SERVER_RATE_LIMITING_TRUST_PROXY: v.boolean(),\r\n SERVER_RATE_LIMITING_SKIP_KEY: v.string(),\r\n SERVER_RATE_LIMITING_SKIP_TOKEN: v.string(),\r\n\r\n // server ssl\r\n SERVER_SSL_ENABLE: v.boolean(),\r\n SERVER_SSL_FORCE: v.boolean(),\r\n SERVER_SSL_PORT: v.positiveNum(),\r\n SERVER_SSL_CERT_PATH: v.string(),\r\n\r\n // pool\r\n POOL_MIN_WORKERS: v.nonNegativeNum(),\r\n POOL_MAX_WORKERS: v.nonNegativeNum(),\r\n POOL_WORK_LIMIT: v.positiveNum(),\r\n POOL_ACQUIRE_TIMEOUT: v.nonNegativeNum(),\r\n POOL_CREATE_TIMEOUT: v.nonNegativeNum(),\r\n POOL_DESTROY_TIMEOUT: v.nonNegativeNum(),\r\n POOL_IDLE_TIMEOUT: v.nonNegativeNum(),\r\n POOL_CREATE_RETRY_INTERVAL: v.nonNegativeNum(),\r\n POOL_REAPER_INTERVAL: v.nonNegativeNum(),\r\n POOL_BENCHMARKING: v.boolean(),\r\n\r\n // logger\r\n LOGGING_LEVEL: z\r\n .string()\r\n .trim()\r\n .refine(\r\n (value) =>\r\n value === '' ||\r\n (!isNaN(parseFloat(value)) &&\r\n parseFloat(value) >= 0 &&\r\n parseFloat(value) <= 5),\r\n (value) => ({\r\n message: `Invalid value for LOGGING_LEVEL. We only accept values from 0 to 5 as logging levels, received '${value}'`\r\n })\r\n )\r\n .transform((value) => (value !== '' ? parseFloat(value) : undefined)),\r\n LOGGING_FILE: v.string(),\r\n LOGGING_DEST: v.string(),\r\n\r\n // ui\r\n UI_ENABLE: v.boolean(),\r\n UI_ROUTE: v.string(),\r\n\r\n // other\r\n OTHER_NODE_ENV: v.enum(['development', 'production', 'test']),\r\n OTHER_LISTEN_TO_PROCESS_EXITS: v.boolean(),\r\n OTHER_NO_LOGO: v.boolean(),\r\n OTHER_HARD_RESET_PAGE: v.boolean(),\r\n\r\n // debugger\r\n DEBUG_ENABLE: v.boolean(),\r\n DEBUG_HEADLESS: v.boolean(),\r\n DEBUG_DEVTOOLS: v.boolean(),\r\n DEBUG_LISTEN_TO_CONSOLE: v.boolean(),\r\n DEBUG_DUMPIO: v.boolean(),\r\n DEBUG_SLOW_MO: v.nonNegativeNum(),\r\n DEBUG_DEBUGGING_PORT: v.positiveNum()\r\n});\r\n\r\nexport const envs = Config.partial().parse(process.env);\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}\\n`.bold\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\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 { envs } from './envs.js';\r\nimport { log, logWithStack } from './logger.js';\r\nimport { deepCopy, isObject, printUsage, toBoolean } from './utils.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 module scripts\r\n if (prompt.name === 'moduleScripts') {\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 && envs[entry.envLink] !== undefined) {\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 * 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","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 { getOptions } from './config.js';\r\nimport { envs } from './envs.js';\r\nimport { fetch } from './fetch.js';\r\nimport { log } from './logger.js';\r\nimport { __dirname } from './utils.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/**\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} requestOptions - Additional options for the proxy agent\r\n * to use for a request.\r\n * @param {Object} fetchedModules - An object which tracks which Highcharts\r\n * modules have been fetched.\r\n * @param {boolean} shouldThrowError - A flag to indicate if the error should be\r\n * 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 requestOptions,\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 // 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} coreScripts - Array of Highcharts core scripts to fetch.\r\n * @param {string} moduleScripts - Array of Highcharts modules to fetch.\r\n * @param {string} customScripts - Array of custom script paths to fetch\r\n * (full URLs).\r\n * @param {object} proxyOptions - Options for the proxy agent to use for\r\n * a request.\r\n * @param {object} fetchedModules - An object which tracks which Highcharts\r\n * modules have been fetched.\r\n *\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 proxyOptions,\r\n fetchedModules\r\n) => {\r\n // Configure proxy if exists\r\n let proxyAgent;\r\n const proxyHost = proxyOptions.host;\r\n const proxyPort = proxyOptions.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 // If exists, add proxy agent to request options\r\n const requestOptions = proxyAgent\r\n ? {\r\n agent: proxyAgent,\r\n timeout: envs.SERVER_PROXY_TIMEOUT\r\n }\r\n : {};\r\n\r\n const allFetchPromises = [\r\n ...coreScripts.map((script) =>\r\n fetchAndProcessScript(`${script}`, requestOptions, fetchedModules, true)\r\n ),\r\n ...moduleScripts.map((script) =>\r\n fetchAndProcessScript(`${script}`, requestOptions, fetchedModules)\r\n ),\r\n ...customScripts.map((script) =>\r\n fetchAndProcessScript(`${script}`, requestOptions)\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} options - Object containing all options.\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 (\r\n highchartsOptions,\r\n proxyOptions,\r\n sourcePath\r\n) => {\r\n const version = highchartsOptions.version;\r\n const hcVersion = version === 'latest' || !version ? '' : `${version}/`;\r\n const cdnURL = highchartsOptions.cdnURL || cache.cdnURL;\r\n\r\n log(\r\n 3,\r\n `[cache] Updating cache version to Highcharts: ${hcVersion || 'latest'}.`\r\n );\r\n\r\n const fetchedModules = {};\r\n try {\r\n cache.sources = await fetchScripts(\r\n [\r\n ...highchartsOptions.coreScripts.map((c) => `${cdnURL}${hcVersion}${c}`)\r\n ],\r\n [\r\n ...highchartsOptions.moduleScripts.map((m) =>\r\n m === 'map'\r\n ? `${cdnURL}maps/${hcVersion}modules/${m}`\r\n : `${cdnURL}${hcVersion}modules/${m}`\r\n ),\r\n ...highchartsOptions.indicatorScripts.map(\r\n (i) => `${cdnURL}stock/${hcVersion}indicators/${i}`\r\n )\r\n ],\r\n highchartsOptions.customScripts,\r\n proxyOptions,\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 const options = getOptions();\r\n if (options?.highcharts) {\r\n options.highcharts.version = newVersion;\r\n }\r\n await checkAndUpdateCache(options);\r\n};\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} options - Object containing all options.\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 (options) => {\r\n const { highcharts, server } = options;\r\n const cachePath = join(__dirname, highcharts.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 // 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) || highcharts.forceFetch) {\r\n log(3, '[cache] Fetching and caching Highcharts dependencies.');\r\n fetchedModules = await updateCache(highcharts, server.proxy, 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 { coreScripts, moduleScripts, indicatorScripts } = highcharts;\r\n const numberOfModules =\r\n coreScripts.length + moduleScripts.length + indicatorScripts.length;\r\n\r\n // Compare the loaded highcharts 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 !== highcharts.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 = (moduleScripts || []).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(highcharts, server.proxy, 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(highcharts, fetchedModules);\r\n};\r\n\r\nexport const getCachePath = () =>\r\n join(__dirname, getOptions().highcharts.cachePath);\r\n\r\nexport const getCache = () => cache;\r\n\r\nexport const highcharts = () => cache.sources;\r\n\r\nexport const version = () => cache.hcVersion;\r\n\r\nexport default {\r\n checkAndUpdateCache,\r\n getCachePath,\r\n updateVersion,\r\n getCache,\r\n highcharts,\r\n version\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\n/* eslint-disable no-undef */\r\n\r\n/**\r\n * Setting the animObject. Called when initing the page.\r\n */\r\nexport function setupHighcharts() {\r\n Highcharts.animObject = function () {\r\n return { duration: 0 };\r\n };\r\n}\r\n\r\n/**\r\n * Creates the actual chart.\r\n *\r\n * @param {object} chartOptions - The options for the Highcharts chart.\r\n * @param {object} options - The export options.\r\n * @param {boolean} displayErrors - A flag indicating whether to display errors.\r\n */\r\nexport async function triggerExport(chartOptions, options, displayErrors) {\r\n // Display errors flag taken from chart options nad debugger module\r\n window._displayErrors = displayErrors;\r\n\r\n // Get required functions\r\n const { getOptions, merge, setOptions, wrap } = Highcharts;\r\n\r\n // Create a separate object for a potential setOptions usages in order to\r\n // prevent from polluting other exports that can happen on the same page\r\n Highcharts.setOptionsObj = merge(false, {}, getOptions());\r\n\r\n // Trigger custom code\r\n if (options.customLogic.customCode) {\r\n new Function(options.customLogic.customCode)();\r\n }\r\n\r\n // By default animation is disabled\r\n const chart = {\r\n animation: false\r\n };\r\n\r\n // When straight inject, the size is set through CSS only\r\n if (options.export.strInj) {\r\n chart.height = chartOptions.chart.height;\r\n chart.width = chartOptions.chart.width;\r\n }\r\n\r\n // NOTE: Is this used for anything useful?\r\n window.isRenderComplete = false;\r\n wrap(Highcharts.Chart.prototype, 'init', function (proceed, userOptions, cb) {\r\n // Override userOptions with image friendly options\r\n userOptions = merge(userOptions, {\r\n exporting: {\r\n enabled: false\r\n },\r\n plotOptions: {\r\n series: {\r\n label: {\r\n enabled: false\r\n }\r\n }\r\n },\r\n /* Expects tooltip in userOptions when forExport is true.\r\n https://github.com/highcharts/highcharts/blob/3ad430a353b8056b9e764aa4e5cd6828aa479db2/js/parts/Chart.js#L241\r\n */\r\n tooltip: {}\r\n });\r\n\r\n (userOptions.series || []).forEach(function (series) {\r\n series.animation = false;\r\n });\r\n\r\n // Add flag to know if chart render has been called.\r\n if (!window.onHighchartsRender) {\r\n window.onHighchartsRender = Highcharts.addEvent(this, 'render', () => {\r\n window.isRenderComplete = true;\r\n });\r\n }\r\n\r\n proceed.apply(this, [userOptions, cb]);\r\n });\r\n\r\n wrap(Highcharts.Series.prototype, 'init', function (proceed, chart, options) {\r\n proceed.apply(this, [chart, options]);\r\n });\r\n\r\n // Get the user options\r\n const userOptions = options.export.strInj\r\n ? new Function(`return ${options.export.strInj}`)()\r\n : chartOptions;\r\n\r\n // Merge the globalOptions, themeOptions, options from the wrapped\r\n // setOptions function and user options to create the final options object\r\n const finalOptions = merge(\r\n false,\r\n JSON.parse(options.export.themeOptions),\r\n userOptions,\r\n // Placed it here instead in the init because of the size issues\r\n { chart }\r\n );\r\n\r\n const finalCallback = options.customLogic.callback\r\n ? new Function(`return ${options.customLogic.callback}`)()\r\n : undefined;\r\n\r\n // Set the global options if exist\r\n const globalOptions = JSON.parse(options.export.globalOptions);\r\n if (globalOptions) {\r\n setOptions(globalOptions);\r\n }\r\n\r\n Highcharts[options.export.constr || 'chart'](\r\n 'container',\r\n finalOptions,\r\n finalCallback\r\n );\r\n\r\n // Get the current global options\r\n const defaultOptions = getOptions();\r\n\r\n // Clear it just in case (e.g. the setOptions was used in the customCode)\r\n for (const prop in defaultOptions) {\r\n if (typeof defaultOptions[prop] !== 'function') {\r\n delete defaultOptions[prop];\r\n }\r\n }\r\n\r\n // Set the default options back\r\n setOptions(Highcharts.setOptionsObj);\r\n\r\n // Empty the custom global options object\r\n Highcharts.setOptionsObj = {};\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\n\r\nimport puppeteer from 'puppeteer';\r\n\r\nimport { getCachePath } from './cache.js';\r\nimport { getOptions } from './config.js';\r\nimport { setupHighcharts } from './highcharts.js';\r\nimport { log, logWithStack } from './logger.js';\r\nimport { __dirname } from './utils.js';\r\n\r\nimport ExportError from './errors/ExportError.js';\r\n\r\n// Get the template for the page\r\nconst template = readFileSync(__dirname + '/templates/template.html', 'utf8');\r\n\r\nlet browser;\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 function get() {\r\n if (!browser) {\r\n throw new ExportError('[browser] No valid browser has been created.');\r\n }\r\n return browser;\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 async function create(puppeteerArgs) {\r\n // Get the debug options\r\n const { enable: enabledDebug, ...debug } = getOptions().debug;\r\n const launchOptions = {\r\n headless: 'shell',\r\n userDataDir: './tmp/',\r\n args: puppeteerArgs,\r\n handleSIGINT: false,\r\n handleSIGTERM: false,\r\n handleSIGHUP: false,\r\n waitForInitialPage: false,\r\n defaultViewport: null,\r\n ...(enabledDebug && debug)\r\n };\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(launchOptions);\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 // Debug mode inform\r\n if (enabledDebug) {\r\n log(3, `[browser] Launched browser in debug mode.`);\r\n }\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 * 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 async function close() {\r\n // Close the browser when connnected\r\n if (browser?.connected) {\r\n await browser.close();\r\n }\r\n log(4, '[browser] Closed the browser.');\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 async function newPage() {\r\n if (!browser) {\r\n return false;\r\n }\r\n\r\n // Create a page\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\r\n // Set page events\r\n setPageEvents(page);\r\n\r\n return page;\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 async function clearPage(page, hardReset = false) {\r\n try {\r\n if (!page.isClosed()) {\r\n if (hardReset) {\r\n // Navigate to about:blank\r\n await page.goto('about:blank', { waitUntil: 'domcontentloaded' });\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 }\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 * Adds custom JS and CSS resources to a Puppeteer Page based on the specified\r\n * options.\r\n *\r\n * @param {Object} page - The Puppeteer Page object to which resources will be\r\n * added.\r\n * @param {Object} options - All options and configuration.\r\n *\r\n * @returns {Promise>} - Promise resolving to an array of injected\r\n * resources.\r\n */\r\nexport async function addPageResources(page, options) {\r\n // Injected resources array\r\n const injectedResources = [];\r\n\r\n // Use resources\r\n const resources = options.customLogic.resources;\r\n if (resources) {\r\n const injectedJs = [];\r\n\r\n // Load custom JS code\r\n if (resources.js) {\r\n injectedJs.push({\r\n content: resources.js\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 const isLocal = !file.startsWith('http') ? true : false;\r\n\r\n // Add each custom script from resources' files\r\n injectedJs.push(\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 }\r\n\r\n for (const jsResource of injectedJs) {\r\n try {\r\n injectedResources.push(await page.addScriptTag(jsResource));\r\n } catch (error) {\r\n logWithStack(2, error, `[export] The JS resource cannot be loaded.`);\r\n }\r\n }\r\n injectedJs.length = 0;\r\n\r\n // Load CSS\r\n const injectedCss = [];\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 injectedCss.push({\r\n url: cssImportPath\r\n });\r\n } else if (options.customLogic.allowFileResources) {\r\n injectedCss.push({\r\n path: path.join(__dirname, cssImportPath)\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 injectedCss.push({\r\n content: resources.css.replace(/@import\\s*([^;]*);/g, '') || ' '\r\n });\r\n\r\n for (const cssResource of injectedCss) {\r\n try {\r\n injectedResources.push(await page.addStyleTag(cssResource));\r\n } catch (error) {\r\n logWithStack(2, error, `[export] The CSS resource cannot be loaded.`);\r\n }\r\n }\r\n injectedCss.length = 0;\r\n }\r\n }\r\n return injectedResources;\r\n}\r\n\r\n/**\r\n * Clears out all state set on the page with addScriptTag/addStyleTag. Removes\r\n * injected resources and resets CSS and script tags on the page. Additionally,\r\n * it destroys previously existing charts.\r\n *\r\n * @param {Object} page - The Puppeteer Page object from which resources will\r\n * be cleared.\r\n * @param {Array} injectedResources - Array of injected resources\r\n * to be cleared.\r\n */\r\nexport async function clearPageResources(page, injectedResources) {\r\n for (const resource of injectedResources) {\r\n await resource.dispose();\r\n }\r\n\r\n // Destroy old charts after export is done and reset all CSS and script tags\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 // 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/**\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\nasync function setPageContent(page) {\r\n await page.setContent(template, { waitUntil: 'domcontentloaded' });\r\n\r\n // Add all registered Higcharts scripts, quite demanding\r\n await page.addScriptTag({ path: `${getCachePath()}/sources.js` });\r\n\r\n // Set the initial animObject\r\n await page.evaluate(setupHighcharts);\r\n}\r\n\r\n/**\r\n * Set events for a Puppeteer Page.\r\n *\r\n * @param {Object} page - The Puppeteer Page object to set events to.\r\n */\r\nfunction setPageEvents(page) {\r\n // Get debug options\r\n const { debug } = getOptions();\r\n\r\n // Set the console listener, if needed\r\n if (debug.enable && debug.listenToConsole) {\r\n page.on('console', (message) => {\r\n console.log(`[debug] ${message.text()}`);\r\n });\r\n }\r\n\r\n // Set the pageerror listener\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\nexport default {\r\n get,\r\n create,\r\n close,\r\n newPage,\r\n clearPage,\r\n addPageResources,\r\n clearPageResources\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 { addPageResources, clearPageResources } from './browser.js';\r\nimport { getCache } from './cache.js';\r\nimport { triggerExport } from './highcharts.js';\r\nimport { log } from './logger.js';\r\n\r\nimport svgTemplate from './../templates/svg_export/svg_export.js';\r\n\r\nimport ExportError from './errors/ExportError.js';\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 captureBeyondViewport: true,\r\n fullPage: false,\r\n optimizeForSpeed: true,\r\n ...(type !== 'png' ? { quality: 80 } : {}),\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 = async (\r\n page,\r\n height,\r\n width,\r\n encoding,\r\n rasterizationTimeout\r\n) => {\r\n await page.emulateMediaType('screen');\r\n return Promise.race([\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 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/**\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 = async (page, chart, options, displayErrors) =>\r\n page.evaluate(triggerExport, chart, options, displayErrors);\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 // Injected resources array (additional JS and CSS)\r\n let injectedResources = [];\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 // Decide whether display error or debbuger wrapper around it\r\n const displayErrors =\r\n exportOptions?.options?.chart?.displayErrors &&\r\n getCache().activeManifest.modules.debugger;\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 waitUntil: 'domcontentloaded'\r\n });\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 displayErrors\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, displayErrors);\r\n }\r\n }\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 injectedResources = await addPageResources(page, options);\r\n\r\n // Get the real chart size and set the zoom accordingly\r\n const size = isSVG\r\n ? await page.evaluate((scale) => {\r\n const svgElement = document.querySelector(\r\n '#chart-container svg:first-of-type'\r\n );\r\n\r\n // Get the values correctly scaled\r\n const chartHeight = svgElement.height.baseVal.value * scale;\r\n const chartWidth = svgElement.width.baseVal.value * scale;\r\n\r\n // In case of SVG the zoom must be set directly for body\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 return {\r\n chartHeight,\r\n chartWidth\r\n };\r\n }, parseFloat(exportOptions.scale))\r\n : await page.evaluate(() => {\r\n // eslint-disable-next-line no-undef\r\n const { chartHeight, chartWidth } = window.Highcharts.charts[0];\r\n\r\n // No need for such scale manipulation in case of other types of exports\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 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 // Get the clip region for the page\r\n const { x, y } = await getClipRegion(page);\r\n\r\n // Set the final viewport now that we have the real height\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 let data;\r\n // Rasterization process\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(\r\n page,\r\n viewportHeight,\r\n viewportWidth,\r\n 'base64',\r\n exportOptions.rasterizationTimeout\r\n );\r\n } else {\r\n throw new ExportError(\r\n `[export] Unsupported output format ${exportOptions.type}.`\r\n );\r\n }\r\n\r\n // Clear previously injected JS and CSS resources\r\n await clearPageResources(page, injectedResources);\r\n return data;\r\n } catch (error) {\r\n await clearPageResources(page, injectedResources);\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 Highcharts 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 create as createBrowser,\r\n close as closeBrowser,\r\n newPage,\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// The pool instance\r\nlet pool = false;\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\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 newPage();\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 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: async (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 await 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 // Create a browser instance with the puppeteer arguments\r\n await createBrowser(config.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 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 * 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 pool with all workers and closing browser.');\r\n\r\n // If still alive, destroy the pool of pages before closing a browser\r\n if (pool) {\r\n // Free up not released workers\r\n for (const worker of pool.used) {\r\n pool.release(worker.resource);\r\n }\r\n\r\n // Destroy the pool if it is still available\r\n if (!pool.destroyed) {\r\n await pool.destroy();\r\n log(4, '[browser] Destroyed the pool of resources.');\r\n }\r\n }\r\n\r\n // Close the browser instance\r\n await closeBrowser();\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 const acquireCounter = measureTime();\r\n try {\r\n log(4, '[pool] Acquiring a worker handle.');\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 (options.payload?.requestId\r\n ? `For request with ID ${options.payload?.requestId} - `\r\n : '') +\r\n `Error encountered when acquiring an available entry: ${acquireCounter()}ms.`\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 newPage();\r\n }\r\n\r\n throw new ExportError(\r\n (options.payload?.requestId\r\n ? `For request with ID ${options.payload?.requestId} - `\r\n : '') + `Error encountered during export: ${exportCounter()}ms.`\r\n ).setError(result);\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 const getPool = () => pool;\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 all: pool.numFree() + pool.numUsed(),\r\n available: pool.numFree(),\r\n used: pool.numUsed(),\r\n pending: 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, all, available, used, pending } = getPoolInfoJSON();\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(5, `[pool] The number of all created resources: ${all}.`);\r\n log(5, `[pool] The number of available resources: ${available}.`);\r\n log(5, `[pool] The number of acquired resources: ${used}.`);\r\n log(5, `[pool] The number of resources waiting to be acquired: ${pending}.`);\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\nimport { sanitize } from './sanitize.js';\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\r\n const result = exportAsString(\r\n sanitize(options.payload.svg), // #209\r\n options,\r\n endCallback\r\n );\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 info.options.export.type !== 'svg'\r\n ? Buffer.from(info.result, 'base64')\r\n : info.result\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 pool and close browser after finishing single export\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","/*******************************************************************************\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/**\r\n * @overview Used to sanitize the strings coming from the exporting module\r\n * to prevent XSS attacks (with the DOMPurify library).\r\n **/\r\n\r\nimport { JSDOM } from 'jsdom';\r\nimport DOMPurify from 'dompurify';\r\n\r\n/**\r\n * Sanitizes a given HTML string by removing tags and any content within them.\r\n *\r\n * @param {string} input The HTML string to be sanitized.\r\n * @returns {string} The sanitized HTML string.\r\n */\r\nexport function sanitize(input) {\r\n const window = new JSDOM('').window;\r\n const purify = DOMPurify(window);\r\n return purify.sanitize(input, { ADD_TAGS: ['foreignObject'] });\r\n}\r\n\r\nexport default sanitize;\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 { log } from './logger.js';\r\n\r\n// Array that contains ids of all ongoing intervals\r\nconst intervalIds = [];\r\n\r\n/**\r\n * Adds id of a setInterval to the intervalIds array.\r\n *\r\n * @param {NodeJS.Timeout} id - Id of an interval.\r\n */\r\nexport const addInterval = (id) => {\r\n intervalIds.push(id);\r\n};\r\n\r\n/**\r\n * Clears all of ongoing intervals by ids gathered in the intervalIds array.\r\n */\r\nexport const clearAllIntervals = () => {\r\n log(4, `[server] Clearing all registered intervals.`);\r\n for (const id of intervalIds) {\r\n clearInterval(id);\r\n }\r\n};\r\n\r\nexport default {\r\n addInterval,\r\n clearAllIntervals\r\n};\r\n","import { envs } from '../envs.js';\r\nimport { logWithStack } from '../logger.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.OTHER_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 { updateVersion, version } from '../../cache.js';\r\nimport { envs } from '../../envs.js';\r\n\r\nimport HttpError from '../../errors/HttpError.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 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: 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 { 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 { version } from '../../cache.js';\r\nimport { addInterval } from '../../intervals.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\n/**\r\n * Calculates moving average indicator based on the data from the successRates\r\n * array.\r\n *\r\n * @returns {number} - A moving average for success ratio of the server exports.\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\n/**\r\n * Starts the interval responsible for calculating current success rate ratio\r\n * and gathers\r\n *\r\n * @returns {NodeJS.Timeout} id - Id of an interval.\r\n */\r\nexport const startSuccessRate = () =>\r\n setInterval(() => {\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 }, 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 // Start processing success rate ratio interval and save its id to the array\r\n // for the graceful clearing on shutdown with injected addInterval funtion\r\n addInterval(startSuccessRate());\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: 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 rateLimit from './rate_limit.js';\r\nimport { log, logWithStack } from '../logger.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// Array of an active servers\r\nconst activeServers = new Map();\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 attachServerErrorHandlers = (server) => {\r\n server.on('clientError', (error) => {\r\n logWithStack(1, error, `[server] Client error: ${error.message}`);\r\n });\r\n\r\n server.on('error', (error) => {\r\n logWithStack(1, error, `[server] Server error: ${error.message}`);\r\n });\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 attachServerErrorHandlers(httpServer);\r\n\r\n // Listen\r\n httpServer.listen(serverConfig.port, serverConfig.host);\r\n\r\n // Save the reference to HTTP server\r\n activeServers.set(serverConfig.port, httpServer);\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 attachServerErrorHandlers(httpsServer);\r\n\r\n // Listen\r\n httpsServer.listen(serverConfig.ssl.port, serverConfig.host);\r\n\r\n // Save the reference to HTTPS server\r\n activeServers.set(serverConfig.ssl.port, httpsServer);\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 * Closes all servers associated with Express app instance.\r\n */\r\nexport const closeServers = () => {\r\n log(4, `[server] Closing all servers.`);\r\n for (const [port, server] of activeServers) {\r\n server.close(() => {\r\n activeServers.delete(port);\r\n log(4, `[server] Closed server on port: ${port}.`);\r\n });\r\n }\r\n};\r\n\r\n/**\r\n * Get all servers associated with Express app instance.\r\n *\r\n * @returns {Array} - Servers associated with Express app instance.\r\n */\r\nexport const getServers = () => activeServers;\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 closeServers,\r\n getServers,\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 { clearAllIntervals } from './intervals.js';\r\nimport { killPool } from './pool.js';\r\nimport { closeServers } from './server/server.js';\r\n\r\n/**\r\n * Clean up function to trigger before ending process for the graceful shutdown.\r\n *\r\n * @param {number} exitCode - An exit code for the process.exit() function.\r\n */\r\nexport const shutdownCleanUp = async (exitCode) => {\r\n // Await freeing all resources\r\n await Promise.allSettled([\r\n // Clear all ongoing intervals\r\n clearAllIntervals(),\r\n\r\n // Get available server instances (HTTP/HTTPS) and close them\r\n closeServers(),\r\n\r\n // Close pool along with its workers and the browser instance, if exists\r\n killPool()\r\n ]);\r\n\r\n // Exit process with a correct code\r\n process.exit(exitCode);\r\n};\r\n\r\nexport default {\r\n shutdownCleanUp\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\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 { shutdownCleanUp } from './resource_release.js';\r\nimport server, { startServer } from './server/server.js';\r\nimport { printLogo, printUsage } from './utils.js';\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\nconst attachProcessExitListeners = () => {\r\n log(3, '[process] Attaching exit listeners to the process.');\r\n\r\n // Handler for the 'exit'\r\n process.on('exit', (code) => {\r\n log(4, `Process exited with code ${code}.`);\r\n });\r\n\r\n // Handler for the 'SIGINT'\r\n process.on('SIGINT', async (name, code) => {\r\n log(4, `The ${name} event with code: ${code}.`);\r\n await shutdownCleanUp(0);\r\n });\r\n\r\n // Handler for the 'SIGTERM'\r\n process.on('SIGTERM', async (name, code) => {\r\n log(4, `The ${name} event with code: ${code}.`);\r\n await shutdownCleanUp(0);\r\n });\r\n\r\n // Handler for the 'SIGHUP'\r\n process.on('SIGHUP', async (name, code) => {\r\n log(4, `The ${name} event with code: ${code}.`);\r\n await shutdownCleanUp(0);\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 shutdownCleanUp(1);\r\n });\r\n};\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 // Attach process' exit listeners\r\n if (options.other.listenToProcessExits) {\r\n attachProcessExitListeners();\r\n }\r\n\r\n // Check if cache needs to be updated\r\n await checkAndUpdateCache(options);\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\r\n // Exporting\r\n initExport,\r\n singleExport,\r\n batchExport,\r\n startExport,\r\n\r\n // Pool\r\n initPool,\r\n killPool,\r\n\r\n // Other\r\n setOptions,\r\n shutdownCleanUp,\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":["scriptsNames","core","modules","indicators","defaultConfig","puppeteer","args","value","type","description","highcharts","version","envLink","cdnURL","coreScripts","moduleScripts","indicatorScripts","customScripts","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","proxy","timeout","rateLimiting","maxRequests","window","delay","trustProxy","skipKey","skipToken","ssl","force","certPath","pool","minWorkers","maxWorkers","workLimit","acquireTimeout","createTimeout","destroyTimeout","idleTimeout","createRetryInterval","reaperInterval","logging","level","file","dest","ui","route","other","nodeEnv","listenToProcessExits","noLogo","hardResetPage","debug","headless","devtools","listenToConsole","dumpio","slowMo","debuggingPort","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","dotenv","config","v","filterArray","z","string","transform","split","map","trim","filter","length","enum","values","refine","isNaN","parseFloat","envs","object","HIGHCHARTS_VERSION","test","HIGHCHARTS_CDN_URL","startsWith","HIGHCHARTS_CORE_SCRIPTS","HIGHCHARTS_MODULE_SCRIPTS","HIGHCHARTS_INDICATOR_SCRIPTS","HIGHCHARTS_FORCE_FETCH","HIGHCHARTS_CACHE_PATH","HIGHCHARTS_ADMIN_TOKEN","EXPORT_TYPE","EXPORT_CONSTR","EXPORT_DEFAULT_HEIGHT","EXPORT_DEFAULT_WIDTH","EXPORT_DEFAULT_SCALE","EXPORT_RASTERIZATION_TIMEOUT","CUSTOM_LOGIC_ALLOW_CODE_EXECUTION","CUSTOM_LOGIC_ALLOW_FILE_RESOURCES","SERVER_ENABLE","SERVER_HOST","SERVER_PORT","SERVER_BENCHMARKING","SERVER_PROXY_HOST","SERVER_PROXY_PORT","SERVER_PROXY_TIMEOUT","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","SERVER_SSL_ENABLE","SERVER_SSL_FORCE","SERVER_SSL_PORT","SERVER_SSL_CERT_PATH","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","LOGGING_LEVEL","LOGGING_FILE","LOGGING_DEST","UI_ENABLE","UI_ROUTE","OTHER_NODE_ENV","OTHER_LISTEN_TO_PROCESS_EXITS","OTHER_NO_LOGO","OTHER_HARD_RESET_PAGE","DEBUG_ENABLE","DEBUG_HEADLESS","DEBUG_DEVTOOLS","DEBUG_LISTEN_TO_CONSOLE","DEBUG_DUMPIO","DEBUG_SLOW_MO","DEBUG_DEBUGGING_PORT","partial","parse","process","env","colors","toConsole","toFile","pathCreated","levelsDesc","title","color","listeners","key","option","entries","logToFile","texts","prefix","existsSync","mkdirSync","appendFile","concat","error","console","log","newLevel","Date","toString","fn","apply","logWithStack","customMessage","mainMessage","stackMessage","stack","slice","setLogLevel","enableFileLogging","logDest","logFile","endsWith","__dirname","fileURLToPath","URL","url","fixType","formats","outType","pop","find","t","handleResources","allowedProps","handledResources","correctResources","isCorrectJSON","readFileSync","files","propName","item","data","parsedData","JSON","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","hrtime","bigint","Number","generalOptions","getOptions","mergeConfigOptions","newOptions","mergedOptions","updateDefaultConfig","configObj","customObj","customValue","initOptions","items","recursiveProps","objectToUpdate","nestedNames","shift","assign","async","fetch","requestOptions","Promise","resolve","reject","protocol","https","http","getProtocol","get","res","on","chunk","text","ExportError","Error","constructor","super","this","setError","statusCode","cache","activeManifest","sources","hcVersion","extractVersion","indexOf","fetchAndProcessScript","script","fetchedModules","shouldThrowError","response","updateCache","highchartsOptions","proxyOptions","sourcePath","proxyAgent","proxyHost","proxyPort","HttpsProxyAgent","agent","allFetchPromises","all","fetchScripts","c","m","writeFileSync","checkAndUpdateCache","manifestPath","requestUpdate","manifest","moduleMap","numberOfModules","some","moduleName","newManifest","saveConfigToManifest","getCachePath","setupHighcharts","Highcharts","animObject","duration","triggerExport","chartOptions","displayErrors","_displayErrors","merge","setOptions","wrap","setOptionsObj","Function","chart","animation","strInj","isRenderComplete","Chart","proceed","userOptions","cb","exporting","enabled","plotOptions","series","label","tooltip","onHighchartsRender","addEvent","Series","finalOptions","finalCallback","defaultOptions","prop","template","browser","newPage","page","setCacheEnabled","setPageContent","$eval","element","errorMessage","innerHTML","setPageEvents","clearPageResources","injectedResources","resource","dispose","evaluate","oldCharts","charts","oldChart","destroy","scriptsToRemove","document","getElementsByTagName","stylesToRemove","linksToRemove","remove","setContent","waitUntil","addScriptTag","path","setAsConfig","puppeteerExport","exportOptions","debugger","isSVG","svgTemplate","injectedJs","js","push","content","isLocal","jsResource","injectedCss","css","cssImports","match","cssImportPath","cssResource","addStyleTag","addPageResources","size","svgElement","querySelector","chartHeight","baseVal","chartWidth","body","style","zoom","margin","viewportHeight","Math","ceil","viewportWidth","x","y","getBoundingClientRect","trunc","getClipRegion","setViewport","deviceScaleFactor","outerHTML","createSVG","encoding","clip","race","screenshot","captureBeyondViewport","fullPage","optimizeForSpeed","quality","omitBackground","_resolve","setTimeout","createImage","emulateMediaType","pdf","createPDF","stats","performedExports","exportAttempts","exportFromSvgAttempts","timeSpent","droppedExports","spentAverage","poolConfig","factory","create","id","uuid","startDate","getTime","isClosed","workCount","random","validate","workerHandle","close","initPool","puppeteerArgs","enabledDebug","launchOptions","userDataDir","handleSIGINT","handleSIGTERM","handleSIGHUP","waitForInitialPage","defaultViewport","tryCount","open","launch","createBrowser","parseInt","Pool","acquireTimeoutMillis","createTimeoutMillis","destroyTimeoutMillis","idleTimeoutMillis","createRetryIntervalMillis","reapIntervalMillis","propagateCreateError","hardReset","goto","clearPage","eventId","initialResources","acquire","promise","release","killPool","worker","used","destroyed","connected","closeBrowser","postWork","getPoolInfo","acquireCounter","payload","requestId","workStart","exportCounter","result","exportTime","getPoolInfoJSON","numFree","numUsed","available","pending","numPendingAcquires","pool$1","startExport","settings","endCallback","svg","initExportSettings","exportAsString","input","JSDOM","DOMPurify","sanitize","ADD_TAGS","doStraightInject","doExport","findChartSize","precision","multiplier","pow","roundNumber","sourceHeight","sourceWidth","param","chartJson","customLogicOptions","allowCodeExecutionScoped","optionsName","stringToExport","chartJSON","intervalIds","clearAllIntervals","clearInterval","logErrorMiddleware","req","next","returnErrorMiddleware","stCode","status","json","rateLimit","app","limitConfig","msg","rateOptions","limiter","windowMs","delayMs","handler","request","format","send","default","skip","query","access_token","use","HttpError","setStatus","vSwitchRoute","post","adminToken","token","newVersion","params","updateVersion","reversedMime","png","jpeg","gif","requestsCounter","beforeRequest","afterRequest","doCallbacks","callbacks","uniqueId","callResponse","exportHandler","stopCounter","headers","connection","remoteAddress","connectionAborted","socket","toLowerCase","substr","b64","noDownload","pattern","isPrivateRangeUrlFound","info","removeAllListeners","Buffer","from","header","attachment","filename","pkgFile","pather","serverStartTime","successRates","addHealthRoutes","setInterval","successRatio","_","period","movingAverage","reduce","a","b","bootTime","uptime","floor","highchartsVersion","averageProcessingTime","failedExports","sucessRatio","toFixed","svgExportAttempts","jsonExportAttempts","activeServers","Map","express","disable","cors","storage","multer","memoryStorage","upload","limits","fieldSize","limit","urlencoded","extended","none","attachServerErrorHandlers","startServer","serverConfig","httpServer","createServer","listen","set","cert","fsPromises","readFile","posix","httpsServer","NaN","static","healthRoute","exportRoutes","sendFile","uiRoute","errorHandler","closeServers","delete","getServers","enableRateLimiting","getExpress","getApp","middlewares","shutdownCleanUp","exitCode","allSettled","exit","index","initExport","initLogging","code","singleExport","batchExport","batchFunctions","pair","configIndex","findIndex","arg","fileName","loadConfigFile","showUsage","propertiesChain","argumentType","pairArgumentValue","mapToNewConfig","oldOptions","manualConfig","configFileName","configFile","choice","prompts","onSubmit","p","categories","questionsCounter","allQuestions","section","prompt","answer","module","writeFile","printLogo","packageVersion"],"mappings":"0lBAeO,MAAMA,EAAe,CAC1BC,KAAM,CAAC,aAAc,kBAAmB,iBACxCC,QAAS,CACP,QACA,MACA,QACA,YACA,cACA,uBACA,gBAEA,eACA,QACA,OACA,aACA,mBACA,eACA,cACA,UACA,UACA,cACA,WACA,UACA,YACA,cACA,YACA,sBACA,SACA,SACA,WACA,aACA,YACA,eACA,yBACA,SACA,eACA,YACA,kBACA,SACA,cACA,mBACA,eACA,cACA,eAEA,cACA,WACA,eACA,WACA,SACA,OACA,WACA,YACA,SACA,qBACA,aACA,WACA,WACA,WACA,WACA,eACA,UACA,kBACA,oBACA,aACA,WAEFC,WAAY,CAAC,mBAKFC,EAAgB,CAC3BC,UAAW,CACTC,KAAM,CACJC,MAAO,CACL,mCACA,kBACA,0CACA,2BACA,kCACA,kCACA,wCACA,2CACA,qBACA,4BACA,2CACA,uDACA,6BACA,yBACA,0BACA,+BACA,uBACA,uFACA,yBACA,oCACA,oBACA,0BACA,8CACA,2BACA,0BACA,6BACA,mCACA,wCACA,mCACA,2BACA,kCACA,uBACA,iBACA,yBACA,8BACA,oBACA,2BACA,eACA,6BACA,iBACA,aACA,eACA,sBACA,cACA,yBACA,oBACA,uBAEFC,KAAM,WACNC,YAAa,0CAGjBC,WAAY,CACVC,QAAS,CACPJ,MAAO,SACPC,KAAM,SACNI,QAAS,qBACTH,YAAa,sCAEfI,OAAQ,CACNN,MAAO,+BACPC,KAAM,SACNI,QAAS,qBACTH,YAAa,kDAEfK,YAAa,CACXP,MAAOP,EAAaC,KACpBO,KAAM,WACNI,QAAS,0BACTH,YAAa,yCAEfM,cAAe,CACbR,MAAOP,EAAaE,QACpBM,KAAM,WACNI,QAAS,4BACTH,YAAa,uCAEfO,iBAAkB,CAChBT,MAAOP,EAAaG,WACpBK,KAAM,WACNI,QAAS,+BACTH,YAAa,0CAEfQ,cAAe,CACbV,MAAO,CACL,wEACA,kGAEFC,KAAM,WACNC,YAAa,uDAEfS,WAAY,CACVX,OAAO,EACPC,KAAM,UACNI,QAAS,yBACTH,YACE,iFAEJU,UAAW,CACTZ,MAAO,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"} \ No newline at end of file diff --git a/lib/browser.js b/lib/browser.js index 2533caa7..6c4d9d40 100644 --- a/lib/browser.js +++ b/lib/browser.js @@ -12,8 +12,8 @@ See LICENSE file in root for details. *******************************************************************************/ -import fs from 'fs'; -import * as url from 'url'; +import { readFileSync } from 'fs'; +import path from 'path'; import puppeteer from 'puppeteer'; @@ -21,15 +21,12 @@ import { getCachePath } from './cache.js'; import { getOptions } from './config.js'; import { setupHighcharts } from './highcharts.js'; import { log, logWithStack } from './logger.js'; +import { __dirname } from './utils.js'; import ExportError from './errors/ExportError.js'; // Get the template for the page -const __dirname = url.fileURLToPath(new URL('.', import.meta.url)); -const template = fs.readFileSync( - __dirname + '/../templates/template.html', - 'utf8' -); +const template = readFileSync(__dirname + '/templates/template.html', 'utf8'); let browser; @@ -164,6 +161,9 @@ export async function newPage() { // Set the content await setPageContent(page); + // Set page events + setPageEvents(page); + return page; } @@ -204,6 +204,161 @@ export async function clearPage(page, hardReset = false) { } } +/** + * Adds custom JS and CSS resources to a Puppeteer Page based on the specified + * options. + * + * @param {Object} page - The Puppeteer Page object to which resources will be + * added. + * @param {Object} options - All options and configuration. + * + * @returns {Promise>} - Promise resolving to an array of injected + * resources. + */ +export async function addPageResources(page, options) { + // Injected resources array + const injectedResources = []; + + // Use resources + const resources = options.customLogic.resources; + if (resources) { + const injectedJs = []; + + // Load custom JS code + if (resources.js) { + injectedJs.push({ + content: resources.js + }); + } + + // Load scripts from all custom files + if (resources.files) { + for (const file of resources.files) { + const isLocal = !file.startsWith('http') ? true : false; + + // Add each custom script from resources' files + injectedJs.push( + isLocal + ? { + content: readFileSync(file, 'utf8') + } + : { + url: file + } + ); + } + } + + for (const jsResource of injectedJs) { + try { + injectedResources.push(await page.addScriptTag(jsResource)); + } catch (error) { + logWithStack(2, error, `[export] The JS resource cannot be loaded.`); + } + } + injectedJs.length = 0; + + // Load CSS + const injectedCss = []; + if (resources.css) { + let cssImports = resources.css.match(/@import\s*([^;]*);/g); + if (cssImports) { + // Handle css section + for (let cssImportPath of cssImports) { + if (cssImportPath) { + cssImportPath = cssImportPath + .replace('url(', '') + .replace('@import', '') + .replace(/"/g, '') + .replace(/'/g, '') + .replace(/;/, '') + .replace(/\)/g, '') + .trim(); + + // Add each custom css from resources + if (cssImportPath.startsWith('http')) { + injectedCss.push({ + url: cssImportPath + }); + } else if (options.customLogic.allowFileResources) { + injectedCss.push({ + path: path.join(__dirname, cssImportPath) + }); + } + } + } + } + + // The rest of the CSS section will be content by now + injectedCss.push({ + content: resources.css.replace(/@import\s*([^;]*);/g, '') || ' ' + }); + + for (const cssResource of injectedCss) { + try { + injectedResources.push(await page.addStyleTag(cssResource)); + } catch (error) { + logWithStack(2, error, `[export] The CSS resource cannot be loaded.`); + } + } + injectedCss.length = 0; + } + } + return injectedResources; +} + +/** + * Clears out all state set on the page with addScriptTag/addStyleTag. Removes + * injected resources and resets CSS and script tags on the page. Additionally, + * it destroys previously existing charts. + * + * @param {Object} page - The Puppeteer Page object from which resources will + * be cleared. + * @param {Array} injectedResources - Array of injected resources + * to be cleared. + */ +export async function clearPageResources(page, injectedResources) { + for (const resource of injectedResources) { + await resource.dispose(); + } + + // Destroy old charts after export is done and reset all CSS and script tags + await page.evaluate(() => { + // We are not guaranteed that Highcharts is loaded, e,g, when doing SVG + // exports + if (typeof Highcharts !== 'undefined') { + // eslint-disable-next-line no-undef + const oldCharts = Highcharts.charts; + + // Check in any already existing charts + if (Array.isArray(oldCharts) && oldCharts.length) { + // Destroy old charts + for (const oldChart of oldCharts) { + oldChart && oldChart.destroy(); + // eslint-disable-next-line no-undef + Highcharts.charts.shift(); + } + } + } + + // eslint-disable-next-line no-undef + const [...scriptsToRemove] = document.getElementsByTagName('script'); + // eslint-disable-next-line no-undef + const [, ...stylesToRemove] = document.getElementsByTagName('style'); + // eslint-disable-next-line no-undef + const [...linksToRemove] = document.getElementsByTagName('link'); + + // Remove tags + for (const element of [ + ...scriptsToRemove, + ...stylesToRemove, + ...linksToRemove + ]) { + element.remove(); + } + }); +} + /** * Sets the content for a Puppeteer Page using a predefined template * and additional scripts. Also, sets the pageerror in order to catch @@ -222,10 +377,45 @@ async function setPageContent(page) { await page.evaluate(setupHighcharts); } +/** + * Set events for a Puppeteer Page. + * + * @param {Object} page - The Puppeteer Page object to set events to. + */ +function setPageEvents(page) { + // Get debug options + const { debug } = getOptions(); + + // Set the console listener, if needed + if (debug.enable && debug.listenToConsole) { + page.on('console', (message) => { + console.log(`[debug] ${message.text()}`); + }); + } + + // Set the pageerror listener + page.on('pageerror', async (error) => { + // TODO: Consider adding a switch here that turns on log(0) logging + // on page errors. + await page.$eval( + '#container', + (element, errorMessage) => { + // eslint-disable-next-line no-undef + if (window._displayErrors) { + element.innerHTML = errorMessage; + } + }, + `

Chart input data error:

${error.toString()}` + ); + }); +} + export default { get, create, close, newPage, - clearPage + clearPage, + addPageResources, + clearPageResources }; diff --git a/lib/cache.js b/lib/cache.js index 2a2522f9..cd712a15 100644 --- a/lib/cache.js +++ b/lib/cache.js @@ -390,11 +390,17 @@ export const checkAndUpdateCache = async (options) => { export const getCachePath = () => join(__dirname, getOptions().highcharts.cachePath); +export const getCache = () => cache; + +export const highcharts = () => cache.sources; + +export const version = () => cache.hcVersion; + export default { checkAndUpdateCache, getCachePath, updateVersion, - getCache: () => cache, - highcharts: () => cache.sources, - version: () => cache.hcVersion + getCache, + highcharts, + version }; diff --git a/lib/export.js b/lib/export.js index cc808a25..5ccd8629 100644 --- a/lib/export.js +++ b/lib/export.js @@ -12,19 +12,15 @@ See LICENSE file in root for details. *******************************************************************************/ -import { readFileSync } from 'fs'; -import path from 'path'; -import * as url from 'url'; - -import cache from './cache.js'; +import { addPageResources, clearPageResources } from './browser.js'; +import { getCache } from './cache.js'; import { triggerExport } from './highcharts.js'; -import { log, logWithStack } from './logger.js'; +import { log } from './logger.js'; + import svgTemplate from './../templates/svg_export/svg_export.js'; import ExportError from './errors/ExportError.js'; -const __basedir = url.fileURLToPath(new URL('.', import.meta.url)); - /** * Retrieves the clipping region coordinates of the specified page element with * the id 'chart-container'. @@ -138,7 +134,7 @@ const createSVG = (page) => * * @returns {Promise} Promise resolving after the configuration is set. */ -const setAsConfig = (page, chart, options, displayErrors) => +const setAsConfig = async (page, chart, options, displayErrors) => page.evaluate(triggerExport, chart, options, displayErrors); /** @@ -152,55 +148,8 @@ const setAsConfig = (page, chart, options, displayErrors) => * the exported data or rejecting with an ExportError. */ export default async (page, chart, options) => { - /** - * Keeps track of all resources added on the page with addXXXTag. etc - * It's VITAL that all added resources ends up here so we can clear things - * out when doing a new export in the same page! - */ - const injectedResources = []; - - /** Clear out all state set on the page with addScriptTag/addStyleTag. */ - const clearInjected = async (page) => { - for (const resource of injectedResources) { - await resource.dispose(); - } - - // Destroy old charts after export is done and reset all CSS and script tags - await page.evaluate(() => { - // We are not guaranteed that Highcharts is loaded, e,g, when doing SVG - // exports - if (typeof Highcharts !== 'undefined') { - // eslint-disable-next-line no-undef - const oldCharts = Highcharts.charts; - - // Check in any already existing charts - if (Array.isArray(oldCharts) && oldCharts.length) { - // Destroy old charts - for (const oldChart of oldCharts) { - oldChart && oldChart.destroy(); - // eslint-disable-next-line no-undef - Highcharts.charts.shift(); - } - } - } - - // eslint-disable-next-line no-undef - const [...scriptsToRemove] = document.getElementsByTagName('script'); - // eslint-disable-next-line no-undef - const [, ...stylesToRemove] = document.getElementsByTagName('style'); - // eslint-disable-next-line no-undef - const [...linksToRemove] = document.getElementsByTagName('link'); - - // Remove tags - for (const element of [ - ...scriptsToRemove, - ...stylesToRemove, - ...linksToRemove - ]) { - element.remove(); - } - }); - }; + // Injected resources array (additional JS and CSS) + let injectedResources = []; try { log(4, '[export] Determining export path.'); @@ -210,7 +159,7 @@ export default async (page, chart, options) => { // Decide whether display error or debbuger wrapper around it const displayErrors = exportOptions?.options?.chart?.displayErrors && - cache.getCache().activeManifest.modules.debugger; + getCache().activeManifest.modules.debugger; let isSVG; if ( @@ -256,95 +205,10 @@ export default async (page, chart, options) => { } } - // Use resources - const resources = options.customLogic.resources; - if (resources) { - const injectedJs = []; - - // Load custom JS code - if (resources.js) { - injectedJs.push({ - content: resources.js - }); - } - - // Load scripts from all custom files - if (resources.files) { - for (const file of resources.files) { - const isLocal = !file.startsWith('http') ? true : false; - - // Add each custom script from resources' files - injectedJs.push( - isLocal - ? { - content: readFileSync(file, 'utf8') - } - : { - url: file - } - ); - } - } - - for (const jsResource of injectedJs) { - try { - injectedResources.push(await page.addScriptTag(jsResource)); - } catch (error) { - logWithStack(2, error, `[export] The JS resource cannot be loaded.`); - } - } - injectedJs.length = 0; - - // Load CSS - const injectedCss = []; - if (resources.css) { - let cssImports = resources.css.match(/@import\s*([^;]*);/g); - if (cssImports) { - // Handle css section - for (let cssImportPath of cssImports) { - if (cssImportPath) { - cssImportPath = cssImportPath - .replace('url(', '') - .replace('@import', '') - .replace(/"/g, '') - .replace(/'/g, '') - .replace(/;/, '') - .replace(/\)/g, '') - .trim(); - - // Add each custom css from resources - if (cssImportPath.startsWith('http')) { - injectedCss.push({ - url: cssImportPath - }); - } else if (options.customLogic.allowFileResources) { - injectedCss.push({ - path: path.join(__basedir, cssImportPath) - }); - } - } - } - } - - // The rest of the CSS section will be content by now - injectedCss.push({ - content: resources.css.replace(/@import\s*([^;]*);/g, '') || ' ' - }); - - for (const cssResource of injectedCss) { - try { - injectedResources.push(await page.addStyleTag(cssResource)); - } catch (error) { - logWithStack( - 2, - error, - `[export] The CSS resource cannot be loaded.` - ); - } - } - injectedCss.length = 0; - } - } + // Keeps track of all resources added on the page with addXXXTag. etc + // It's VITAL that all added resources ends up here so we can clear things + // out when doing a new export in the same page! + injectedResources = await addPageResources(page, options); // Get the real chart size and set the zoom accordingly const size = isSVG @@ -401,7 +265,7 @@ export default async (page, chart, options) => { }); let data; - // RASTERIZATION + // Rasterization process if (exportOptions.type === 'svg') { // SVG data = await createSVG(page); @@ -434,10 +298,11 @@ export default async (page, chart, options) => { ); } - await clearInjected(page); + // Clear previously injected JS and CSS resources + await clearPageResources(page, injectedResources); return data; } catch (error) { - await clearInjected(page); + await clearPageResources(page, injectedResources); return error; } }; diff --git a/lib/highcharts.js b/lib/highcharts.js index 6ba92066..76afd976 100644 --- a/lib/highcharts.js +++ b/lib/highcharts.js @@ -30,7 +30,7 @@ export function setupHighcharts() { * @param {object} options - The export options. * @param {boolean} displayErrors - A flag indicating whether to display errors. */ -export function triggerExport(chartOptions, options, displayErrors) { +export async function triggerExport(chartOptions, options, displayErrors) { // Display errors flag taken from chart options nad debugger module window._displayErrors = displayErrors; diff --git a/lib/pool.js b/lib/pool.js index dc20a83f..6a875a5e 100644 --- a/lib/pool.js +++ b/lib/pool.js @@ -16,12 +16,11 @@ import { Pool } from 'tarn'; import { v4 as uuid } from 'uuid'; import { - close as browserClose, create as createBrowser, - newPage as browserNewPage, + close as closeBrowser, + newPage, clearPage } from './browser.js'; -import { getOptions } from './config.js'; import puppeteerExport from './export.js'; import { log, logWithStack } from './logger.js'; import { measureTime } from './utils.js'; @@ -60,7 +59,7 @@ const factory = { const startDate = new Date().getTime(); try { - page = await browserNewPage(); + page = await newPage(); if (!page || page.isClosed()) { throw new ExportError('The page is invalid or closed.'); @@ -78,30 +77,6 @@ const factory = { ).setError(error); } - const { debug } = getOptions(); - // Set the console listener, if needed - if (debug.enable && debug.listenToConsole) { - page.on('console', (message) => { - console.log(`[debug] ${message.text()}`); - }); - } - - // Set the pageerror listener - page.on('pageerror', async (error) => { - // TODO: Consider adding a switch here that turns on log(0) logging - // on page errors. - await page.$eval( - '#container', - (element, errorMessage) => { - // eslint-disable-next-line no-undef - if (window._displayErrors) { - element.innerHTML = errorMessage; - } - }, - `

Chart input data error:

${error.toString()}` - ); - }); - return { id, page, @@ -259,7 +234,7 @@ export async function killPool() { } // Close the browser instance - await browserClose(); + await closeBrowser(); } /** @@ -336,7 +311,7 @@ export const postWork = async (chart, options) => { // 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. if (result.message === 'Rasterization timeout') { workerHandle.page.close(); - workerHandle.page = await browserNewPage(); + workerHandle.page = await newPage(); } throw new ExportError( diff --git a/lib/server/routes/change_hc_version.js b/lib/server/routes/change_hc_version.js index 02b0dfb4..df72049e 100644 --- a/lib/server/routes/change_hc_version.js +++ b/lib/server/routes/change_hc_version.js @@ -12,7 +12,7 @@ See LICENSE file in root for details. *******************************************************************************/ -import cache from '../../cache.js'; +import { updateVersion, version } from '../../cache.js'; import { envs } from '../../envs.js'; import HttpError from '../../errors/HttpError.js'; @@ -54,7 +54,7 @@ export default (app) => if (newVersion) { try { // eslint-disable-next-line import/no-named-as-default-member - await cache.updateVersion(newVersion); + await updateVersion(newVersion); } catch (error) { throw new HttpError( `Version change: ${error.message}`, @@ -65,7 +65,7 @@ export default (app) => // Success response.status(200).send({ statusCode: 200, - version: cache.version(), + version: version(), message: `Successfully updated Highcharts to version: ${newVersion}.` }); } else { diff --git a/lib/server/routes/health.js b/lib/server/routes/health.js index ab9adf11..c6defa26 100644 --- a/lib/server/routes/health.js +++ b/lib/server/routes/health.js @@ -16,7 +16,7 @@ import { readFileSync } from 'fs'; import { join as pather } from 'path'; import { log } from '../../logger.js'; -import cache from '../../cache.js'; +import { version } from '../../cache.js'; import { addInterval } from '../../intervals.js'; import pool from '../../pool.js'; import { __dirname } from '../../utils.js'; @@ -88,7 +88,7 @@ export default function addHealthRoutes(app) { (new Date().getTime() - serverStartTime.getTime()) / 1000 / 60 ) + ' minutes', version: pkgFile.version, - highchartsVersion: cache.version(), + highchartsVersion: version(), averageProcessingTime: stats.spentAverage, performedExports: stats.performedExports, failedExports: stats.droppedExports, diff --git a/package-lock.json b/package-lock.json index df9138b2..9c8b240a 100644 --- a/package-lock.json +++ b/package-lock.json @@ -1268,6 +1268,201 @@ } } }, + "node_modules/@rollup/rollup-android-arm-eabi": { + "version": "4.17.2", + "resolved": "https://registry.npmjs.org/@rollup/rollup-android-arm-eabi/-/rollup-android-arm-eabi-4.17.2.tgz", + "integrity": "sha512-NM0jFxY8bB8QLkoKxIQeObCaDlJKewVlIEkuyYKm5An1tdVZ966w2+MPQ2l8LBZLjR+SgyV+nRkTIunzOYBMLQ==", + "cpu": [ + "arm" + ], + "dev": true, + "optional": true, + "os": [ + "android" + ] + }, + "node_modules/@rollup/rollup-android-arm64": { + "version": "4.17.2", + "resolved": "https://registry.npmjs.org/@rollup/rollup-android-arm64/-/rollup-android-arm64-4.17.2.tgz", + "integrity": "sha512-yeX/Usk7daNIVwkq2uGoq2BYJKZY1JfyLTaHO/jaiSwi/lsf8fTFoQW/n6IdAsx5tx+iotu2zCJwz8MxI6D/Bw==", + "cpu": [ + "arm64" + ], + "dev": true, + "optional": true, + "os": [ + "android" + ] + }, + "node_modules/@rollup/rollup-darwin-arm64": { + "version": "4.17.2", + "resolved": "https://registry.npmjs.org/@rollup/rollup-darwin-arm64/-/rollup-darwin-arm64-4.17.2.tgz", + "integrity": "sha512-kcMLpE6uCwls023+kknm71ug7MZOrtXo+y5p/tsg6jltpDtgQY1Eq5sGfHcQfb+lfuKwhBmEURDga9N0ol4YPw==", + "cpu": [ + "arm64" + ], + "dev": true, + "optional": true, + "os": [ + "darwin" + ] + }, + "node_modules/@rollup/rollup-darwin-x64": { + "version": "4.17.2", + "resolved": "https://registry.npmjs.org/@rollup/rollup-darwin-x64/-/rollup-darwin-x64-4.17.2.tgz", + "integrity": "sha512-AtKwD0VEx0zWkL0ZjixEkp5tbNLzX+FCqGG1SvOu993HnSz4qDI6S4kGzubrEJAljpVkhRSlg5bzpV//E6ysTQ==", + "cpu": [ + "x64" + ], + "dev": true, + "optional": true, + "os": [ + "darwin" + ] + }, + "node_modules/@rollup/rollup-linux-arm-gnueabihf": { + "version": "4.17.2", + "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-arm-gnueabihf/-/rollup-linux-arm-gnueabihf-4.17.2.tgz", + "integrity": "sha512-3reX2fUHqN7sffBNqmEyMQVj/CKhIHZd4y631duy0hZqI8Qoqf6lTtmAKvJFYa6bhU95B1D0WgzHkmTg33In0A==", + "cpu": [ + "arm" + ], + "dev": true, + "optional": true, + "os": [ + "linux" + ] + }, + "node_modules/@rollup/rollup-linux-arm-musleabihf": { + "version": "4.17.2", + "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-arm-musleabihf/-/rollup-linux-arm-musleabihf-4.17.2.tgz", + "integrity": "sha512-uSqpsp91mheRgw96xtyAGP9FW5ChctTFEoXP0r5FAzj/3ZRv3Uxjtc7taRQSaQM/q85KEKjKsZuiZM3GyUivRg==", + "cpu": [ + "arm" + ], + "dev": true, + "optional": true, + "os": [ + "linux" + ] + }, + "node_modules/@rollup/rollup-linux-arm64-gnu": { + "version": "4.17.2", + "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-arm64-gnu/-/rollup-linux-arm64-gnu-4.17.2.tgz", + "integrity": "sha512-EMMPHkiCRtE8Wdk3Qhtciq6BndLtstqZIroHiiGzB3C5LDJmIZcSzVtLRbwuXuUft1Cnv+9fxuDtDxz3k3EW2A==", + "cpu": [ + "arm64" + ], + "dev": true, + "optional": true, + "os": [ + "linux" + ] + }, + "node_modules/@rollup/rollup-linux-arm64-musl": { + "version": "4.17.2", + "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-arm64-musl/-/rollup-linux-arm64-musl-4.17.2.tgz", + "integrity": "sha512-NMPylUUZ1i0z/xJUIx6VUhISZDRT+uTWpBcjdv0/zkp7b/bQDF+NfnfdzuTiB1G6HTodgoFa93hp0O1xl+/UbA==", + "cpu": [ + "arm64" + ], + "dev": true, + "optional": true, + "os": [ + "linux" + ] + }, + "node_modules/@rollup/rollup-linux-powerpc64le-gnu": { + "version": "4.17.2", + "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-powerpc64le-gnu/-/rollup-linux-powerpc64le-gnu-4.17.2.tgz", + "integrity": "sha512-T19My13y8uYXPw/L/k0JYaX1fJKFT/PWdXiHr8mTbXWxjVF1t+8Xl31DgBBvEKclw+1b00Chg0hxE2O7bTG7GQ==", + "cpu": [ + "ppc64" + ], + "dev": true, + "optional": true, + "os": [ + "linux" + ] + }, + "node_modules/@rollup/rollup-linux-riscv64-gnu": { + "version": "4.17.2", + "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-riscv64-gnu/-/rollup-linux-riscv64-gnu-4.17.2.tgz", + "integrity": "sha512-BOaNfthf3X3fOWAB+IJ9kxTgPmMqPPH5f5k2DcCsRrBIbWnaJCgX2ll77dV1TdSy9SaXTR5iDXRL8n7AnoP5cg==", + "cpu": [ + "riscv64" + ], + "dev": true, + "optional": true, + "os": [ + "linux" + ] + }, + "node_modules/@rollup/rollup-linux-s390x-gnu": { + "version": "4.17.2", + "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-s390x-gnu/-/rollup-linux-s390x-gnu-4.17.2.tgz", + "integrity": "sha512-W0UP/x7bnn3xN2eYMql2T/+wpASLE5SjObXILTMPUBDB/Fg/FxC+gX4nvCfPBCbNhz51C+HcqQp2qQ4u25ok6g==", + "cpu": [ + "s390x" + ], + "dev": true, + "optional": true, + "os": [ + "linux" + ] + }, + "node_modules/@rollup/rollup-linux-x64-gnu": { + "version": "4.17.2", + "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-x64-gnu/-/rollup-linux-x64-gnu-4.17.2.tgz", + "integrity": "sha512-Hy7pLwByUOuyaFC6mAr7m+oMC+V7qyifzs/nW2OJfC8H4hbCzOX07Ov0VFk/zP3kBsELWNFi7rJtgbKYsav9QQ==", + "cpu": [ + "x64" + ], + "dev": true, + "optional": true, + "os": [ + "linux" + ] + }, + "node_modules/@rollup/rollup-linux-x64-musl": { + "version": "4.17.2", + "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-x64-musl/-/rollup-linux-x64-musl-4.17.2.tgz", + "integrity": "sha512-h1+yTWeYbRdAyJ/jMiVw0l6fOOm/0D1vNLui9iPuqgRGnXA0u21gAqOyB5iHjlM9MMfNOm9RHCQ7zLIzT0x11Q==", + "cpu": [ + "x64" + ], + "dev": true, + "optional": true, + "os": [ + "linux" + ] + }, + "node_modules/@rollup/rollup-win32-arm64-msvc": { + "version": "4.17.2", + "resolved": "https://registry.npmjs.org/@rollup/rollup-win32-arm64-msvc/-/rollup-win32-arm64-msvc-4.17.2.tgz", + "integrity": "sha512-tmdtXMfKAjy5+IQsVtDiCfqbynAQE/TQRpWdVataHmhMb9DCoJxp9vLcCBjEQWMiUYxO1QprH/HbY9ragCEFLA==", + "cpu": [ + "arm64" + ], + "dev": true, + "optional": true, + "os": [ + "win32" + ] + }, + "node_modules/@rollup/rollup-win32-ia32-msvc": { + "version": "4.17.2", + "resolved": "https://registry.npmjs.org/@rollup/rollup-win32-ia32-msvc/-/rollup-win32-ia32-msvc-4.17.2.tgz", + "integrity": "sha512-7II/QCSTAHuE5vdZaQEwJq2ZACkBpQDOmQsE6D6XUbnBHW8IAhm4eTufL6msLJorzrHDFv3CF8oCA/hSIRuZeQ==", + "cpu": [ + "ia32" + ], + "dev": true, + "optional": true, + "os": [ + "win32" + ] + }, "node_modules/@rollup/rollup-win32-x64-msvc": { "version": "4.17.2", "resolved": "https://registry.npmjs.org/@rollup/rollup-win32-x64-msvc/-/rollup-win32-x64-msvc-4.17.2.tgz", @@ -1397,9 +1592,9 @@ "dev": true }, "node_modules/@types/node": { - "version": "20.12.11", - "resolved": "https://registry.npmjs.org/@types/node/-/node-20.12.11.tgz", - "integrity": "sha512-vDg9PZ/zi+Nqp6boSOT7plNuthRugEKixDv5sFTIpkE89MmNtEArAShI4mxuX2+UrLEe9pxC1vm2cjm9YlWbJw==", + "version": "20.12.12", + "resolved": "https://registry.npmjs.org/@types/node/-/node-20.12.12.tgz", + "integrity": "sha512-eWLDGF/FOSPtAvEqeRAQ4C8LSA7M1I7i0ky1I8U7kD1J5ITyW3AsRhQrKVoWf5pFKZ2kILsEGJhsI9r93PYnOw==", "devOptional": true, "dependencies": { "undici-types": "~5.26.4" @@ -1441,12 +1636,6 @@ "integrity": "sha512-zuVdFrMJiuCDQUMCzQaD6KL28MjnqqN8XnAqiEq9PNm/hCPTSGfrXCOfwj1ow4LFb/tNymJPwsNbVePc1xFqrQ==", "dev": true }, - "node_modules/abbrev": { - "version": "1.1.1", - "resolved": "https://registry.npmjs.org/abbrev/-/abbrev-1.1.1.tgz", - "integrity": "sha512-nne9/IiQ/hzIhY6pdDnbBtz7DjPTKrY00P/zvPSm5pOFkl6xuGrGnXn/VtTNNfNtAfZ9/1RtehkszU9qcTii0Q==", - "dev": true - }, "node_modules/accepts": { "version": "1.3.8", "resolved": "https://registry.npmjs.org/accepts/-/accepts-1.3.8.tgz", @@ -2117,9 +2306,9 @@ } }, "node_modules/caniuse-lite": { - "version": "1.0.30001617", - "resolved": "https://registry.npmjs.org/caniuse-lite/-/caniuse-lite-1.0.30001617.tgz", - "integrity": "sha512-mLyjzNI9I+Pix8zwcrpxEbGlfqOkF9kM3ptzmKNw5tizSyYwMe+nGLTqMK9cO+0E+Bh6TsBxNAaHWEM8xwSsmA==", + "version": "1.0.30001618", + "resolved": "https://registry.npmjs.org/caniuse-lite/-/caniuse-lite-1.0.30001618.tgz", + "integrity": "sha512-p407+D1tIkDvsEAPS22lJxLQQaG8OTBEqo0KhzfABGk0TU4juBNDSfH0hyAp/HRyx+M8L17z/ltyhxh27FTfQg==", "dev": true, "funding": [ { @@ -2786,9 +2975,9 @@ "integrity": "sha512-WMwm9LhRUo+WUaRN+vRuETqG89IgZphVSNkdFgeb6sS/E4OrDIN7t48CAewSHXc6C8lefD8KKfr5vY61brQlow==" }, "node_modules/electron-to-chromium": { - "version": "1.4.762", - "resolved": "https://registry.npmjs.org/electron-to-chromium/-/electron-to-chromium-1.4.762.tgz", - "integrity": "sha512-rrFvGweLxPwwSwJOjIopy3Vr+J3cIPtZzuc74bmlvmBIgQO3VYJDvVrlj94iKZ3ukXUH64Ex31hSfRTLqvjYJQ==", + "version": "1.4.767", + "resolved": "https://registry.npmjs.org/electron-to-chromium/-/electron-to-chromium-1.4.767.tgz", + "integrity": "sha512-nzzHfmQqBss7CE3apQHkHjXW77+8w3ubGCIoEijKCJebPufREaFETgGXWTkh32t259F3Kcq+R8MZdFdOJROgYw==", "dev": true }, "node_modules/emittery": { @@ -3685,6 +3874,20 @@ "integrity": "sha512-OO0pH2lK6a0hZnAdau5ItzHPI6pUlvI7jMVnxUQRtw4owF2wk8lOSabtGDCTP4Ggrg2MbGnWO9X8K1t4+fGMDw==", "dev": true }, + "node_modules/fsevents": { + "version": "2.3.3", + "resolved": "https://registry.npmjs.org/fsevents/-/fsevents-2.3.3.tgz", + "integrity": "sha512-5xoDfX+fL7faATnagmWPpbFtwh/R77WmMMqqHGS65C3vvB0YHrgF+B1YmZ3441tMj5n63k0212XNoJwzlhffQw==", + "dev": true, + "hasInstallScript": true, + "optional": true, + "os": [ + "darwin" + ], + "engines": { + "node": "^8.16.0 || ^10.6.0 || >=11.0.0" + } + }, "node_modules/function-bind": { "version": "1.1.2", "resolved": "https://registry.npmjs.org/function-bind/-/function-bind-1.1.2.tgz", @@ -5921,21 +6124,6 @@ "node": ">=4" } }, - "node_modules/nopt": { - "version": "1.0.10", - "resolved": "https://registry.npmjs.org/nopt/-/nopt-1.0.10.tgz", - "integrity": "sha512-NWmpvLSqUrgrAC9HCuxEvb+PSloHpqVu+FqcO4eeF2h5qYRhA7ev6KvelyQAKtegUbC6RypJnlEOhd8vloNKYg==", - "dev": true, - "dependencies": { - "abbrev": "1" - }, - "bin": { - "nopt": "bin/nopt.js" - }, - "engines": { - "node": "*" - } - }, "node_modules/normalize-path": { "version": "3.0.0", "resolved": "https://registry.npmjs.org/normalize-path/-/normalize-path-3.0.0.tgz", @@ -5958,9 +6146,9 @@ } }, "node_modules/nwsapi": { - "version": "2.2.9", - "resolved": "https://registry.npmjs.org/nwsapi/-/nwsapi-2.2.9.tgz", - "integrity": "sha512-2f3F0SEEer8bBu0dsNCFF50N0cTThV1nWFYcEYFZttdW0lDAoybv9cQoK7X7/68Z89S7FoRrVjP1LPX4XRf9vg==" + "version": "2.2.10", + "resolved": "https://registry.npmjs.org/nwsapi/-/nwsapi-2.2.10.tgz", + "integrity": "sha512-QK0sRs7MKv0tKe1+5uZIQk/C8XGza4DAnztJG8iD+TpJIORARrCxczA738awHrZoHeTjSSoHqao2teO0dC/gFQ==" }, "node_modules/object-assign": { "version": "4.1.1", @@ -6265,9 +6453,9 @@ "integrity": "sha512-F3asv42UuXchdzt+xXqfW1OGlVBe+mxa2mqI0pg5yAHZPvFmY3Y6drSf/GQ1A86WgWEN9Kzh/WrgKa6iGcHXLg==" }, "node_modules/picocolors": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/picocolors/-/picocolors-1.0.0.tgz", - "integrity": "sha512-1fygroTLlHu66zi26VoTDv8yRgm0Fccecssto+MhsZ0D/DGW2sm8E8AjW7NU5VVTRt5GxbeZ5qBuJr+HyLYkjQ==" + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/picocolors/-/picocolors-1.0.1.tgz", + "integrity": "sha512-anP1Z8qwhkbmu7MFP5iTt+wQKXgwzf7zTyGlcdzabySa9vd0Xt392U0rVmz9poOaBj0uHJKyyo9/upk0HrEQew==" }, "node_modules/picomatch": { "version": "2.3.1", @@ -7639,13 +7827,10 @@ } }, "node_modules/touch": { - "version": "3.1.0", - "resolved": "https://registry.npmjs.org/touch/-/touch-3.1.0.tgz", - "integrity": "sha512-WBx8Uy5TLtOSRtIq+M03/sKDrXCLHxwDcquSP2c43Le03/9serjQBIztjRz6FkJez9D/hleyAXTBGLwwZUw9lA==", + "version": "3.1.1", + "resolved": "https://registry.npmjs.org/touch/-/touch-3.1.1.tgz", + "integrity": "sha512-r0eojU4bI8MnHr8c5bNo7lJDdI2qXlWWJk6a9EAFG7vbhTjElYhBVS3/miuE0uOuoLdb8Mc/rVfsmm6eo5o9GA==", "dev": true, - "dependencies": { - "nopt": "~1.0.10" - }, "bin": { "nodetouch": "bin/nodetouch.js" } @@ -7897,9 +8082,9 @@ } }, "node_modules/update-browserslist-db": { - "version": "1.0.15", - "resolved": "https://registry.npmjs.org/update-browserslist-db/-/update-browserslist-db-1.0.15.tgz", - "integrity": "sha512-K9HWH62x3/EalU1U6sjSZiylm9C8tgq2mSvshZpqc7QE69RaA2qjhkW2HlNA0tFpEbtyFz7HTqbSdN4MSwUodA==", + "version": "1.0.16", + "resolved": "https://registry.npmjs.org/update-browserslist-db/-/update-browserslist-db-1.0.16.tgz", + "integrity": "sha512-KVbTxlBYlckhF5wgfyZXTWnMn7MMZjMu9XG8bPlliUOP9ThaF4QnhP8qrjrH7DRzHfSk0oQv1wToW+iA5GajEQ==", "dev": true, "funding": [ { @@ -7917,7 +8102,7 @@ ], "dependencies": { "escalade": "^3.1.2", - "picocolors": "^1.0.0" + "picocolors": "^1.0.1" }, "bin": { "update-browserslist-db": "cli.js"