From 1aa2ef71cecbb4db02efac11b0c2e6fd701a2c08 Mon Sep 17 00:00:00 2001 From: PaulDalek Date: Mon, 15 Jan 2024 19:54:00 +0100 Subject: [PATCH 1/8] Bunch of pool related stuff corrected. --- .env.sample | 9 ++- README.md | 60 ++++++++------- bin/cli.js | 12 ++- dist/index.cjs | 4 +- dist/index.esm.js | 2 +- dist/index.esm.js.map | 2 +- lib/browser.js | 44 +++++++---- lib/export.js | 26 +++++-- lib/index.js | 2 +- lib/pool.js | 35 +++++---- lib/schemas/config.js | 103 ++++++++++++++++++-------- lib/server/server.js | 4 +- package-lock.json | 4 +- samples/module/options_puppeteer.js | 2 +- tests/node/node_test_runner_single.js | 2 +- 15 files changed, 198 insertions(+), 113 deletions(-) diff --git a/.env.sample b/.env.sample index 50c2b731..a6846111 100644 --- a/.env.sample +++ b/.env.sample @@ -40,10 +40,13 @@ HIGHCHARTS_RATE_LIMIT_SKIP_TOKEN = HIGHCHARTS_POOL_MIN_WORKERS = 8 HIGHCHARTS_POOL_MAX_WORKERS = 8 HIGHCHARTS_POOL_WORK_LIMIT = 40 -HIGHCHARTS_POOL_QUEUE_SIZE = 5 -HIGHCHARTS_POOL_TIMEOUT = 5000 HIGHCHARTS_POOL_ACQUIRE_TIMEOUT = 5000 -HIGHCHARTS_POOL_ENABLE_REAPER = true +HIGHCHARTS_POOL_CREATE_TIMEOUT = 5000 +HIGHCHARTS_POOL_DESTROY_TIMEOUT = 5000 +HIGHCHARTS_POOL_IDLE_TIMEOUT = 30000 +HIGHCHARTS_POOL_RASTERIZATION_TIMEOUT = 1500 +HIGHCHARTS_POOL_CREATE_RETRY_INTERVAL = 200 +HIGHCHARTS_POOL_REAPER_INTERVAL = 1000 HIGHCHARTS_POOL_BENCHMARKING = false HIGHCHARTS_POOL_LISTEN_TO_PROCESS_EXITS = true diff --git a/README.md b/README.md index a1e792a3..4b9441f2 100644 --- a/README.md +++ b/README.md @@ -2,7 +2,6 @@ Convert Highcharts.JS charts to static image files. - ## Upgrade notes for V3.0 V3 should be a drop in replacement for V2 in most cases. However, due to changing out the browser back-end part, the various tweaks related to process handling (e.g. worker counts and so on) may have different effects than they did previously. @@ -227,13 +226,14 @@ The format, with its default values are as follows (using the below ordering of } }, "pool": { - "initialWorkers": 4, + "minWorkers": 4, "maxWorkers": 8, "workLimit": 40, - "queueSize": 5, - "timeoutThreshold": 5000, "acquireTimeout": 5000, - "reaper": true, + "createTimeout": 5000, + "destroyTimeout": 5000, + "idleTimeout": 30000, + "reaperInterval": 1000, "benchmarking": false, "listenToProcessExits": true }, @@ -252,7 +252,7 @@ The format, with its default values are as follows (using the below ordering of } ``` -*Library fetches* +_Library fetches_ The `width` argument is mostly to set a zoom factor rather than an absolute width. @@ -290,32 +290,32 @@ If `--resources` is not set, and a file `resources.json` exist in the folder fro The server accepts the following arguments: - * `infile`: A string containing JSON or SVG for the chart - * `options`: Alias for `infile` - * `svg`: A string containing SVG to render - * `type`: The format: `png`, `jpeg`, `pdf`, `svg`. Mimetypes can also be used. - * `scale`: The scale factor. Use it to improve resolution in PNG and JPG, for example setting scale to 2 on a 600px chart will result in a 1200px output. - * `width`: The chart width (overrides scale) - * `callback`: Javascript to execute in the highcharts constructor. - * `resources`: Additional resources. - * `constr`: The constructor to use. Either `Chart` or `Stock`. - * `b64`: Bool, set to true to get base64 back instead of binary. - * ~~`async`: Get a download link instead of the file data. Note that the `b64` option overrides the `async` option.~~ This option is deprecated and will be removed as of Desember 1st 2021. Read the [announcement article on how to replace async](https://www.highcharts.com/docs/export-module/deprecated-async-option). - * `noDownload`: Bool, set to true to not send attachment headers on the response. - * `asyncRendering`: Wait for the included scripts to call `highexp.done()` before rendering the chart. - * `globalOptions`: A JSON object with options to be passed to `Highcharts.setOptions`. - * `dataOptions`: Passed to `Highcharts.data(..)` - * `customCode`: When `dataOptions` is supplied, this is a function to be called with the after applying the data options. Its only argument is the complete options object which will be passed to the Highcharts constructor on return. +- `infile`: A string containing JSON or SVG for the chart +- `options`: Alias for `infile` +- `svg`: A string containing SVG to render +- `type`: The format: `png`, `jpeg`, `pdf`, `svg`. Mimetypes can also be used. +- `scale`: The scale factor. Use it to improve resolution in PNG and JPG, for example setting scale to 2 on a 600px chart will result in a 1200px output. +- `width`: The chart width (overrides scale) +- `callback`: Javascript to execute in the highcharts constructor. +- `resources`: Additional resources. +- `constr`: The constructor to use. Either `Chart` or `Stock`. +- `b64`: Bool, set to true to get base64 back instead of binary. +- ~~`async`: Get a download link instead of the file data. Note that the `b64` option overrides the `async` option.~~ This option is deprecated and will be removed as of December 1st 2021. Read the [announcement article on how to replace async](https://www.highcharts.com/docs/export-module/deprecated-async-option). +- `noDownload`: Bool, set to true to not send attachment headers on the response. +- `asyncRendering`: Wait for the included scripts to call `highexp.done()` before rendering the chart. +- `globalOptions`: A JSON object with options to be passed to `Highcharts.setOptions`. +- `dataOptions`: Passed to `Highcharts.data(..)` +- `customCode`: When `dataOptions` is supplied, this is a function to be called with the after applying the data options. Its only argument is the complete options object which will be passed to the Highcharts constructor on return. It responds to `application/json`, `multipart/form-data`, and URL encoded requests. -Each of the workers has a maximum number of requests it can handle before it restarts itself to keep everything responsive. This number is 40 by default, and can be tweaked with `--workLimit`. As with `--initialWorkers` and `--maxWorkers`, this number should also be tweaked to fit your use case. Also, the `--acquireTimeout` option is worth to mention as well, in case there would be problems with acquiring resources. It is set in miliseconds with 5000 as a default value. +Each of the workers has a maximum number of requests it can handle before it restarts itself to keep everything responsive. This number is 40 by default, and can be tweaked with `--workLimit`. As with `--minWorkers` and `--maxWorkers`, this number should also be tweaked to fit your use case. Also, the `--acquireTimeout` option is worth to mention as well, in case there would be problems with acquiring resources. It is set in miliseconds with 5000 as a default value. Lastly, the `--createTimeout` and `--destroyTimeout` options are similar to the `--acquireTimeout` but for resource's create and destroy actions. ## Setup: Injecting the Highcharts dependency In order to use the export server, Highcharts.js needs to be injected into the export template. -Since version 3.0.0 Highcharts is fetched in a Just-In-Time manner, which makes it easy to switch configurations. It is no longer required to explicitly accept the license as in older versions - __but the export server still requires a valid Highcharts license to be used__. +Since version 3.0.0 Highcharts is fetched in a Just-In-Time manner, which makes it easy to switch configurations. It is no longer required to explicitly accept the license as in older versions - **but the export server still requires a valid Highcharts license to be used**. ## Using In Automated Deployments @@ -473,6 +473,7 @@ exporter.startExport(exportSettings, function (res, err) { ``` ## CommonJS support + This package supports both CommonJS and ES modules. ## Node.js API Reference @@ -492,6 +493,7 @@ This package supports both CommonJS and ES modules. - `startServer(serverConfig)`: Start an http server on the given port. The `serverConfig` object contains all server related properties (see the `server` section in the `lib/schemas/config.js` file for a reference). - `server` - The server instance: + - `startServer(serverConfig)` - The same as `startServer` from above. - `getExpress()` - Return the express module instance. - `getApp()` - Return the app instance. @@ -506,11 +508,15 @@ This package supports both CommonJS and ES modules. - `skipKey`/`skipToken` - key/token pair that allows bypassing the rate limiter. On requests, these should be sent as such: `?key=&access_token=`. - `initPool(options)`: Init the pool of Puppeteer browser's pages - must be done prior to exporting. The `options` is an object that contains all options with, among others, the `pool` section which is required to successfuly init the pool: - - `initialWorkers` (default 4) - Initial worker process count. + + - `minWorkers` (default 4) - Min and initial worker process count. - `maxWorkers` (default 8) - Max worker processes count. - `workLimit` (default 40) - How many task can be performed by a worker process before it's automatically restarted. - - `timeoutThreshold` (default 3500) - The maximum allowed time for each export job execution, in milliseconds. If a worker has been executing a job for longer than this period, it will be restarted. - - `acquireTimeout` (default 3000) - the maximum allowed time for each resource acquire, in milliseconds. + + - `acquireTimeout` (default 5000) - the maximum allowed time for each resource acquire, in milliseconds. + - `createTimeout` (default 5000) - the maximum allowed time for each resource create, in milliseconds. + - `destroyTimeout` (default 5000) - the maximum allowed time for each resource destroy, in milliseconds. + - `idleTimeout` (default 30000) - the maximum allowed time after an idle resource is destroyed, in milliseconds. - `benchmarking` (default false) - Enable benchmarking. - `listenToProcessExits` (default true) - Set to false in order to skip attaching process.exit handlers. diff --git a/bin/cli.js b/bin/cli.js index a42d1f32..2bce8c28 100755 --- a/bin/cli.js +++ b/bin/cli.js @@ -58,12 +58,11 @@ const start = async () => { // Perform batch exports if (options.export.batch) { // If not set explicitly, use default option for batch exports - if (!args.includes('--initialWorkers', '--maxWorkers')) { + if (!args.includes('--minWorkers', '--maxWorkers')) { options.pool = { ...options.pool, - initialWorkers: 5, - maxWorkers: 25, - reaper: false + minWorkers: 5, + maxWorkers: 25 }; } @@ -76,9 +75,8 @@ const start = async () => { // No need for multiple workers in case of a single CLI export options.pool = { ...options.pool, - initialWorkers: 1, - maxWorkers: 1, - reaper: false + minWorkers: 1, + maxWorkers: 1 }; // Init a pool for one export diff --git a/dist/index.cjs b/dist/index.cjs index 4d2a6560..0a8ea4f4 100644 --- a/dist/index.cjs +++ b/dist/index.cjs @@ -1,2 +1,2 @@ -"use strict";require("colors");var e=require("fs"),t=require("path"),o=require("body-parser"),r=require("cors"),i=require("express"),n=require("multer"),s=require("http"),a=require("https"),l=require("dotenv"),c=require("express-rate-limit"),p=require("url"),u=require("https-proxy-agent"),d=require("uuid"),h=require("tarn"),g=require("puppeteer"),f=require("node:path"),m=require("node:crypto");require("prompts");var v="undefined"!=typeof document?document.currentScript:null;function y(e){var t=Object.create(null);return e&&Object.keys(e).forEach((function(o){if("default"!==o){var r=Object.getOwnPropertyDescriptor(e,o);Object.defineProperty(t,o,r.get?r:{enumerable:!0,get:function(){return e[o]}})}})),t.default=e,Object.freeze(t)}var b=y(p);l.config();const w={puppeteer:{args:{value:[],type:"string[]",description:"Array of arguments to send to puppeteer."}},highcharts:{version:{value:"latest",envLink:"HIGHCHARTS_VERSION",type:"string",description:"Highcharts version to use."},cdnURL:{value:"https://code.highcharts.com/",envLink:"HIGHCHARTS_CDN",type:"string",description:"The CDN URL of Highcharts scripts to use."},coreScripts:{envLink:"HIGHCHARTS_CORE_SCRIPTS",value:["highcharts","highcharts-more","highcharts-3d"],type:"string[]",description:"Highcharts core scripts to fetch."},modules:{envLink:"HIGHCHARTS_MODULES",value:["stock","map","gantt","exporting","export-data","parallel-coordinates","accessibility","annotations-advanced","boost-canvas","boost","data","draggable-points","static-scale","broken-axis","heatmap","tilemap","timeline","treemap","treegraph","item-series","drilldown","histogram-bellcurve","bullet","funnel","funnel3d","pyramid3d","networkgraph","pareto","pattern-fill","pictorial","price-indicator","sankey","arc-diagram","dependency-wheel","series-label","solid-gauge","sonification","stock-tools","streamgraph","sunburst","variable-pie","variwide","vector","venn","windbarb","wordcloud","xrange","no-data-to-display","drag-panes","debugger","dumbbell","lollipop","cylinder","organization","dotplot","marker-clusters","hollowcandlestick","heikinashi"],type:"string[]",description:"Highcharts modules to fetch."},indicators:{envLink:"HIGHCHARTS_INDICATORS",value:["indicators-all"],type:"string[]",description:"Highcharts indicators to fetch."},scripts:{value:["https://cdnjs.cloudflare.com/ajax/libs/moment.js/2.29.4/moment.min.js","https://cdnjs.cloudflare.com/ajax/libs/moment-timezone/0.5.34/moment-timezone-with-data.min.js"],type:"string[]",description:"Additional direct scripts/optional dependencies (e.g. moment.js)."},forceFetch:{envLink:"HIGHCHARTS_FORCE_FETCH",value:!1,type:"boolean",description:"Should all the scripts be refetched after rerunning the server."}},export:{infile:{value:!1,type:"string",description:"The input file name along with a type (json or svg). It can be a correct JSON or SVG file."},instr:{value:!1,type:"string",description:"An input in a form of a stringified JSON or SVG file. Overrides the --infile."},options:{value:!1,type:"string",description:"An alias for the --instr option."},outfile:{value:!1,type:"string",description:"The output filename along with a type (jpeg, png, pdf or svg). Ignores the --type flag."},type:{envLink:"EXPORT_DEFAULT_TYPE",value:"png",type:"string",description:"The format of the file to export to. Can be jpeg, png, pdf or svg."},constr:{envLink:"EXPORT_DEFAULT_CONSTR",value:"chart",type:"string",description:"The constructor to use. Can be chart, stockChart, mapChart or ganttChart."},defaultHeight:{envLink:"EXPORT_DEFAULT_HEIGHT",value:400,type:"number",description:"The default height of the exported chart. Used when not found any value set."},defaultWidth:{envLink:"EXPORT_DEFAULT_WIDTH",value:600,type:"number",description:"The default width of the exported chart. Used when not found any value set."},defaultScale:{envLink:"EXPORT_DEFAULT_SCALE",value:1,type:"number",description:"The default scale of the exported chart. Ranges between 1 and 5."},height:{type:"number",value:!1,description:"The default height of the exported chart. Overrides the option in the chart settings."},width:{type:"number",value:!1,description:"The width of the exported chart. Overrides the option in the chart settings."},scale:{value:!1,type:"number",description:"The scale of the exported chart. Ranges between 1 and 5."},globalOptions:{value:!1,type:"string",description:"A stringified JSON or a filename with options to be passed into the Highcharts.setOptions."},themeOptions:{value:!1,type:"string",description:"A stringified JSON or a filename with theme options to be passed into the Highcharts.setOptions."},batch:{value:!1,type:"string",description:'Starts a batch job. A string that contains input/output pairs: "in=out;in=out;..".'}},customCode:{allowCodeExecution:{envLink:"HIGHCHARTS_ALLOW_CODE_EXECUTION",value:!1,type:"boolean",description:"If set to true, allow for the execution of arbitrary code when exporting."},allowFileResources:{envLink:"HIGHCHARTS_ALLOW_FILE_RESOURCES",value:!0,type:"boolean",description:"Allow injecting resources from the filesystem. Has no effect when running as a server."},customCode:{value:!1,type:"string",description:"A function to be called before chart initialization. Can be a filename with the js extension."},callback:{value:!1,type:"string",description:"A JavaScript file with a function to run on construction."},resources:{value:!1,type:"string",description:"An additional resource in a form of stringified JSON. It can contain files, js and css sections."},loadConfig:{value:!1,type:"string",description:"A file that contains a pre-defined config to use."},createConfig:{value:!1,type:"string",description:"Allows to set options through a prompt and save in a provided config file."}},server:{enable:{envLink:"HIGHCHARTS_SERVER_ENABLE",value:!1,type:"boolean",cliName:"enableServer",description:"If set to true, starts a server on 0.0.0.0."},host:{envLink:"HIGHCHARTS_SERVER_HOST",value:"0.0.0.0",type:"string",description:"The hostname of the server. Also starts a server listening on the supplied hostname."},port:{envLink:"HIGHCHARTS_SERVER_PORT",value:7801,type:"number",description:"The port to use for the server. Defaults to 7801."},ssl:{enable:{envLink:"HIGHCHARTS_SERVER_SSL_ENABLE",value:!1,type:"boolean",cliName:"enableSsl",description:"Enables the SSL protocol."},force:{envLink:"HIGHCHARTS_SERVER_SSL_FORCE",value:!1,type:"boolean",cliName:"sslForced",description:"If set to true, forces the server to only serve over HTTPS."},port:{envLink:"HIGHCHARTS_SERVER_SSL_PORT",value:443,type:"number",cliName:"sslPort",description:"The port on which to run the SSL server."},certPath:{envLink:"HIGHCHARTS_SSL_CERT_PATH",value:"",type:"string",description:"The path to the SSL certificate/key."}},rateLimiting:{enable:{envLink:"HIGHCHARTS_RATE_LIMIT_ENABLE",value:!1,type:"boolean",cliName:"enableRateLimiting",description:"Enables rate limiting."},maxRequests:{envLink:"HIGHCHARTS_RATE_LIMIT_MAX",value:10,type:"number",description:"Max requests allowed in a one minute."},window:{envLink:"HIGHCHARTS_RATE_LIMIT_WINDOW",value:1,type:"number",description:"The time window in minutes for rate limiting."},delay:{envLink:"HIGHCHARTS_RATE_LIMIT_DELAY",value:0,type:"number",description:"The amount to delay each successive request before hitting the max."},trustProxy:{envLink:"HIGHCHARTS_RATE_LIMIT_TRUST_PROXY",value:!1,type:"boolean",description:"Set this to true if behind a load balancer."},skipKey:{envLink:"HIGHCHARTS_RATE_LIMIT_SKIP_KEY",value:"",type:"number|string",description:"Allows bypassing the rate limiter and should be provided with skipToken argument."},skipToken:{envLink:"HIGHCHARTS_RATE_LIMIT_SKIP_TOKEN",value:"",type:"number|string",description:"Allows bypassing the rate limiter and should be provided with skipKey argument."}}},pool:{initialWorkers:{envLink:"HIGHCHARTS_POOL_MIN_WORKERS",value:4,type:"number",description:"The number of initial workers to spawn."},maxWorkers:{envLink:"HIGHCHARTS_POOL_MAX_WORKERS",value:8,type:"number",description:"The number of max workers to spawn."},workLimit:{envLink:"HIGHCHARTS_POOL_WORK_LIMIT",value:40,type:"number",description:"The pieces of work that can be performed before restarting process."},queueSize:{envLink:"HIGHCHARTS_POOL_QUEUE_SIZE",value:5,type:"number",description:"The size of the request overflow queue."},timeoutThreshold:{envLink:"HIGHCHARTS_POOL_TIMEOUT",value:5e3,type:"number",description:"The number of milliseconds before timing out."},acquireTimeout:{envLink:"HIGHCHARTS_POOL_ACQUIRE_TIMEOUT",value:5e3,type:"number",description:"The number of milliseconds to wait for acquiring a resource."},reaper:{envLink:"HIGHCHARTS_POOL_ENABLE_REAPER",value:!0,type:"boolean",description:"Whether or not to evict workers after a certain time period."},benchmarking:{envLink:"HIGHCHARTS_POOL_BENCHMARKING",value:!1,type:"boolean",description:"Enable benchmarking."},listenToProcessExits:{envLink:"HIGHCHARTS_POOL_LISTEN_TO_PROCESS_EXITS",value:!0,type:"boolean",description:"Set to false in order to skip attaching process.exit handlers."}},logging:{level:{envLink:"HIGHCHARTS_LOG_LEVEL",value:4,type:"number",cliName:"logLevel",description:"The log level (0: silent, 1: error, 2: warning, 3: notice, 4: verbose)."},file:{envLink:"HIGHCHARTS_LOG_FILE",value:"highcharts-export-server.log",type:"string",cliName:"logFile",description:"A name of a log file. The --logDest also needs to be set to enable file logging."},dest:{envLink:"HIGHCHARTS_LOG_DEST",value:"log/",type:"string",cliName:"logDest",description:"The path to store log files. Also enables file logging."}},ui:{enable:{envLink:"HIGHCHARTS_UI_ENABLE",value:!1,type:"boolean",cliName:"enableUi",description:"Enables the UI for the export server."},route:{envLink:"HIGHCHARTS_UI_ROUTE",value:"/",type:"string",cliName:"uiRoute",description:"The route to attach the UI to."}},other:{noLogo:{envLink:"HIGHCHARTS_NO_LOGO",value:!1,type:"boolean",description:"Skip printing the logo on a startup. Will be replaced by a simple text."}},payload:{}};w.puppeteer.args.value.join(","),w.highcharts.version.value,w.highcharts.cdnURL.value,w.highcharts.modules.value,w.highcharts.scripts.value.join(","),w.highcharts.forceFetch.value,w.export.type.value,w.export.constr.value,w.export.defaultHeight.value,w.export.defaultWidth.value,w.export.defaultScale.value,w.customCode.allowCodeExecution.value,w.customCode.allowFileResources.value,w.server.enable.value,w.server.host.value,w.server.port.value,w.server.ssl.enable.value,w.server.ssl.force.value,w.server.ssl.port.value,w.server.ssl.certPath.value,w.server.rateLimiting.enable.value,w.server.rateLimiting.maxRequests.value,w.server.rateLimiting.window.value,w.server.rateLimiting.delay.value,w.server.rateLimiting.trustProxy.value,w.server.rateLimiting.skipKey.value,w.server.rateLimiting.skipToken.value,w.pool.initialWorkers.value,w.pool.maxWorkers.value,w.pool.workLimit.value,w.pool.queueSize.value,w.pool.timeoutThreshold.value,w.pool.acquireTimeout.value,w.pool.reaper.value,w.pool.benchmarking.value,w.pool.listenToProcessExits.value,w.logging.level.value,w.logging.file.value,w.logging.dest.value,w.ui.enable.value,w.ui.route.value,w.other.noLogo.value;const x=["options","globalOptions","themeOptions","resources","payload"],T={},k=(e,t="")=>{Object.keys(e).forEach((o=>{if(!["puppeteer","highcharts"].includes(o)){const r=e[o];void 0===r.value?k(r,`${t}.${o}`):T[r.cliName||o]=`${t}.${o}`.substring(1)}}))};k(w);let S={toConsole:!0,toFile:!1,pathCreated:!1,levelsDesc:[{title:"error",color:"red"},{title:"warning",color:"yellow"},{title:"notice",color:"blue"},{title:"verbose",color:"gray"}],listeners:[]};for(const[e,t]of Object.entries(w.logging))S[e]=t.value;const H=(...t)=>{const[o,...r]=t,{level:i,levelsDesc:n}=S;if(0===o||o>i||i>n.length)return;const s=`${(new Date).toString().split("(")[0].trim()} [${n[o-1].title}] -`;S.listeners.forEach((e=>{e(s,r.join(" "))})),S.toFile&&(S.pathCreated||(!e.existsSync(S.dest)&&e.mkdirSync(S.dest),S.pathCreated=!0),e.appendFile(`${S.dest}${S.file}`,[s].concat(r).join(" ")+"\n",(e=>{e&&(console.log(`[logger] Unable to write to log file: ${e}`),S.toFile=!1)}))),S.toConsole&&console.log.apply(void 0,[s.toString()[S.levelsDesc[o-1].color]].concat(r))},E=p.fileURLToPath(new URL("../.","undefined"==typeof document?require("url").pathToFileURL(__filename).href:v&&v.src||new URL("index.cjs",document.baseURI).href)),R=(e,t=/\s\s+/g,o=" ")=>e.replaceAll(t,o).trim(),L=(e,t)=>{const o=["png","jpeg","pdf","svg"];if(t){const r=t.split(".").pop();o.includes(r)&&e!==r&&(e=r)}return{"image/png":"png","image/jpeg":"jpeg","application/pdf":"pdf","image/svg+xml":"svg"}[e]||o.find((t=>t===e))||"png"},C=(t=!1,o)=>{const r=["js","css","files"];let i=t,n=!1;if(o&&t.endsWith(".json"))try{t?t&&t.endsWith(".json")?i=O(e.readFileSync(t,"utf8")):(i=O(t),!0===i&&(i=O(e.readFileSync("resources.json","utf8")))):i=O(e.readFileSync("resources.json","utf8"))}catch(e){return H(3,"[cli] No resources found.")}else i=O(t),o||delete i.files;for(const e in i)r.includes(e)?n||(n=!0):delete i[e];return n?(i.files&&(i.files=i.files.map((e=>e.trim())),(!i.files||i.files.length<=0)&&delete i.files),i):H(3,"[cli] No resources found.")};function O(e,t){try{const o=JSON.parse("string"!=typeof e?JSON.stringify(e):e);return"string"!=typeof o&&t?JSON.stringify(o):o}catch(e){return!1}}const _=e=>{if(null===e||"object"!=typeof e)return e;const t=Array.isArray(e)?[]:{};for(const o in e)Object.prototype.hasOwnProperty.call(e,o)&&(t[o]=_(e[o]));return t},A=(e,t)=>JSON.stringify(e,((e,o)=>("string"==typeof o&&((o=o.trim()).startsWith("function(")||o.startsWith("function ("))&&o.endsWith("}")&&(o=t?`EXP_FUN${(o+"").replaceAll(/\n|\t|\r/g," ")}EXP_FUN`:void 0),"function"==typeof o?`EXP_FUN${(o+"").replaceAll(/\n|\t|\r/g," ")}EXP_FUN`:o))).replaceAll(/"EXP_FUN|EXP_FUN"/g,"");function $(){console.log("Usage of CLI arguments:".bold,"\n------",`\nFor more detailed information visit readme at: ${"https://github.com/highcharts/node-export-server#readme".bold.yellow}.`);const e=t=>{for(const[o,r]of Object.entries(t))if(Object.prototype.hasOwnProperty.call(r,"value")){let e=` --${r.cliName||o} ${("<"+r.type+">").green} `;if(e.length<48)for(let t=e.length;t<48;t++)e+=".";console.log(e,r.description,`[Default: ${r.value.toString().bold}]`.blue)}else e(r)};Object.keys(w).forEach((t=>{["puppeteer","highcharts"].includes(t)||(console.log(`\n${t.toUpperCase()}`.red),e(w[t]))})),console.log("\n")}const I=e=>!["false","undefined","null","NaN","0",""].includes(e)&&!!e,j=(t,o)=>{if(t&&"string"==typeof t)return(t=t.trim()).endsWith(".js")?!!o&&j(e.readFileSync(t,"utf8")):t.startsWith("function()")||t.startsWith("function ()")||t.startsWith("()=>")||t.startsWith("() =>")?`(${t})()`:t.replace(/;$/,"")};var P=(e,t)=>{const o="Too many requests, you have been rate limited. Please try again later.",r={max:t.maxRequests||30,window:t.window||1,delay:t.delay||0,trustProxy:t.trustProxy||!1,skipKey:t.skipKey||!1,skipToken:t.skipToken||!1};r.trustProxy&&e.enable("trust proxy");const i=c({windowMs:60*r.window*1e3,max:r.max,delayMs:r.delay,handler:(e,t)=>{t.format({json:()=>{t.status(429).send({message:o})},default:()=>{t.status(429).send(o)}})},skip:e=>!1!==r.skipKey&&!1!==r.skipToken&&e.query.key===r.skipKey&&e.query.access_token===r.skipToken&&(H(4,"[rate-limiting] Skipping rate limiter."),!0)});e.use(i),H(3,R(`[rate-limiting] Enabled rate limiting: ${r.max} requests\n per ${r.window} minute per IP, trusting proxy:\n ${r.trustProxy}.`))};async function N(e,t={}){return new Promise(((o,r)=>{const i=(e=>e.startsWith("https")?a:s)(e);i.get(e,t,(e=>{let t="";e.on("data",(e=>{t+=e})),e.on("end",(()=>{t||r("Nothing was fetched from the URL."),e.text=t,o(e)}))})).on("error",(e=>{r(e)}))}))}l.config();const F=t.join(E,".cache"),U={cdnURL:"https://code.highcharts.com/",activeManifest:{},sources:"",hcVersion:""};let q=!1;const G=()=>U.hcVersion=U.sources.substr(0,U.sources.indexOf("*/")).replace("/*","").replace("*/","").replace(/\n/g,"").trim(),W=async(e,t)=>{try{e.endsWith(".js")&&(e=e.substring(0,e.length-3)),H(4,`[cache] Fetching script - ${e}.js`);const o=t?{agent:t,timeout:+process.env.PROXY_SERVER_TIMEOUT||5e3}:{},r=await N(`${e}.js`,o);if(200===r.statusCode)return r.text;throw`${r.statusCode}`}catch(t){throw H(1,`[cache] Error fetching script ${e}.js: ${t}.`),t}},M=async(t,o)=>{const{coreScripts:r,modules:i,indicators:n,scripts:s}=t,a="latest"!==t.version&&t.version?`${t.version}/`:"";H(3,"[cache] Updating cache to Highcharts ",a);const l=[...r.map((e=>`${a}${e}`)),...i.map((e=>"map"===e?`maps/${a}modules/${e}`:`${a}modules/${e}`)),...n.map((e=>`stock/${a}indicators/${e}`))];let c;const p=process.env.PROXY_SERVER_HOST,d=process.env.PROXY_SERVER_PORT;p&&d&&(c=new u({host:p,port:+d}));const h={};try{return U.sources=(await Promise.all([...l.map((async e=>{const o=await W(`${t.cdnURL||U.cdnURL}${e}`,c);return"string"==typeof o&&(h[e.replace(/(.*)\/|(.*)modules\/|stock\/(.*)indicators\/|maps\/(.*)modules\//gi,"")]=1),o})),...s.map((e=>W(e,c)))])).join(";\n"),G(),e.writeFileSync(o,U.sources),h}catch(e){H(1,"[cache] Unable to update local Highcharts cache.")}},D=async o=>{let r;const i=t.join(F,"manifest.json"),n=t.join(F,"sources.js");if(q=o,!e.existsSync(F)&&e.mkdirSync(F),!e.existsSync(i)||o.forceFetch)H(3,"[cache] Fetching and caching Highcharts dependencies."),r=await M(o,n);else{let t=!1;const s=JSON.parse(e.readFileSync(i));if(s.modules&&Array.isArray(s.modules)){const e={};s.modules.forEach((t=>e[t]=1)),s.modules=e}const{modules:a,coreScripts:l,indicators:c}=o,p=a.length+l.length+c.length;s.version!==o.version?(H(3,"[cache] Highcharts version mismatch in cache, need to re-fetch."),t=!0):Object.keys(s.modules||{}).length!==p?(H(3,"[cache] Cache and requested modules does not match, need to re-fetch."),t=!0):t=(o.modules||[]).some((e=>{if(!s.modules[e])return H(3,`[cache] The ${e} missing in cache, need to re-fetch.`),!0})),t?r=await M(o,n):(H(3,"[cache] Dependency cache is up to date, proceeding."),U.sources=e.readFileSync(n,"utf8"),r=s.modules,G())}await(async(o,r)=>{const i={version:o.version,modules:r||{}};U.activeManifest=i,H(4,"[cache] writing new manifest");try{e.writeFileSync(t.join(F,"manifest.json"),JSON.stringify(i),"utf8")}catch(e){H(1,`[cache] Error writing cache manifest: ${e}.`)}})(o,r)};var V=async e=>!!q&&await D(Object.assign(q,{version:e})),z=()=>U,J=()=>U.hcVersion;const K=m.randomBytes(64).toString("base64url"),X=f.join("tmp",`puppeteer-${K}`),B=[`--user-data-dir=${f.join(X,"profile")}`,"--autoplay-policy=user-gesture-required","--disable-background-networking","--disable-background-timer-throttling","--disable-backgrounding-occluded-windows","--disable-breakpad","--disable-client-side-phishing-detection","--disable-component-update","--disable-default-apps","--disable-dev-shm-usage","--disable-domain-reliability","--disable-extensions","--disable-features=AudioServiceOutOfProcess","--disable-hang-monitor","--disable-ipc-flooding-protection","--disable-notifications","--disable-offer-store-unmasked-wallet-cards","--disable-popup-blocking","--disable-print-preview","--disable-prompt-on-repost","--disable-renderer-backgrounding","--disable-session-crashed-bubble","--disable-setuid-sandbox","--disable-speech-api","--disable-sync","--hide-crash-restore-bubble","--hide-scrollbars","--ignore-gpu-blacklist","--metrics-recording-only","--mute-audio","--no-default-browser-check","--no-first-run","--no-pings","--no-sandbox","--no-zygote","--password-store=basic","--use-mock-keychain"],Y=b.fileURLToPath(new URL(".","undefined"==typeof document?require("url").pathToFileURL(__filename).href:v&&v.src||new URL("index.cjs",document.baseURI).href)),Q=e.readFileSync(Y+"/../templates/template.html","utf8");let Z;const ee=async()=>{if(!Z)return!1;const e=await Z.newPage();return await e.setContent(Q),await e.addScriptTag({path:Y+"/../.cache/sources.js"}),await e.evaluate((()=>window.setupHighcharts())),e.on("pageerror",(async t=>{H(1,"[page error]",t),await e.$eval("#container",((e,t)=>{window._displayErrors&&(e.innerHTML=t)}),`

Chart input data error

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

Chart input data error

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

Chart input data error

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

Chart input data error

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

Chart input data error

${err.toString()}`\n );\n });\n\n return p;\n};\n\nexport const create = async (puppeteerArgs) => {\n const allArgs = [...minimalArgs, ...(puppeteerArgs || [])];\n\n // Create a browser\n if (!browser) {\n let tryCount = 0;\n\n const open = async () => {\n try {\n log(\n 3,\n '[browser] attempting to get a browser instance (try',\n tryCount + ')'\n );\n\n browser = await puppeteer.launch({\n headless: 'new',\n args: allArgs,\n userDataDir: './tmp/'\n });\n } catch (e) {\n log(0, '[browser]', e);\n if (++tryCount < 25) {\n log(3, '[browser] failed:', e);\n await new Promise((response) => setTimeout(response, 4000));\n await open();\n } else {\n log(0, 'Max retries reached');\n }\n }\n };\n\n try {\n await open();\n } catch (e) {\n log(0, '[browser] Unable to open browser');\n return false;\n }\n\n if (!browser) {\n log(0, '[browser] Unable to open browser');\n return false;\n }\n }\n\n // Return a browser promise\n return browser;\n};\n\nexport const get = async () => {\n if (!browser) {\n throw 'No valid browser has been created';\n }\n\n return browser;\n};\n\nexport const close = async () => {\n // Close the browser when connnected\n if (browser.connected) {\n await browser.close();\n }\n};\n\nexport default {\n get,\n close,\n newPage\n};\n","/*******************************************************************************\n\nHighcharts Export Server\n\nCopyright (c) 2016-2023, Highsoft\n\nLicenced under the MIT licence.\n\nAdditionally a valid Highcharts license is required for use.\n\nSee LICENSE file in root for details.\n\n*******************************************************************************/\n\n// TODO: remove this temp benchmark stuff. I had this idea of doing a general benchmarking\n// system, but it adds so much bloat in the code that it shouldn't be there.\n\nimport benchmark from './benchmark.js';\nimport cache from './cache.js';\nimport { log } from './logger.js';\nimport svgTemplate from './../templates/svg_export/svg_export.js';\n\nimport { readFileSync } from 'fs';\nimport path from 'path';\nimport * as url from 'url';\n\nconst __basedir = url.fileURLToPath(new URL('.', import.meta.url));\n\n// const jsonTemplate = require('./../templates/json_export/json_export.js');\n\n/**\n * Gets the clip region for the chart DOM node.\n *\n * @param {object} page - A page of a browser instance.\n * @return {object} - A clipped region.\n */\nconst getClipRegion = (page) =>\n page.$eval('#chart-container', (element) => {\n const { x, y, width, height } = element.getBoundingClientRect();\n return {\n x,\n y,\n width,\n height: Math.trunc(height > 1 ? height : 500)\n };\n });\n\n/**\n * Rasterizes the page to an image (PNG or JPEG)\n *\n * @param {object} page - A page of a browser instance.\n * @param {string} type - The type of a result image.\n * @param {string} encoding - The type of encoding used.\n * @param {string} clip - The clip region.\n * @returns {string} - A string representation of a screenshot.\n */\nconst createImage = async (page, type, encoding, clip) =>\n await Promise.race([\n page.screenshot({\n type,\n encoding,\n clip,\n\n // #447, #463 - always render on a transparent page if\n // the expected type format is PNG\n omitBackground: type == 'png'\n }),\n new Promise((resolve, reject) =>\n setTimeout(() => reject(new Error('Rasterization timeout')), 1500)\n )\n ]);\n\n/**\n * Turns page into a PDF.\n *\n * @param {object} page - A page of a browser instance.\n * @param {number} height - The height of a chart.\n * @param {number} width - The width of a chart.\n * @param {string} encoding - The type of encoding used.\n * @return {object} - A buffer with PDF representation.\n */\nconst createPDF = async (page, height, width, encoding) =>\n await page.pdf({\n // This will remove an extra empty page in PDF exports\n height: height + 1,\n width,\n encoding\n });\n\n/**\n * Exports as a SVG.\n *\n * @param {object} page - A page of a browser instance.\n * @return {object} - The outerHTML element with the SVG representation.\n */\nconst createSVG = async (page) =>\n await page.$eval(\n '#container svg:first-of-type',\n (element) => element.outerHTML\n );\n\n/** Load config into a page and render a chart */\nconst setAsConfig = async (page, chart, options) =>\n await page.evaluate(\n // eslint-disable-next-line no-undef\n (chart, options) => window.triggerExport(chart, options),\n chart,\n options\n );\n\n/** Load SVG into a page */\n// const setAsSVG = async (page, svgStr) => true;\n\n/**\n * Does an export for a given browser.\n *\n * @param {object} browser - A browser instance.\n * @param {object} chart - Chart's options.\n * @param {object} options - All options object.\n * @return {object} - The data returned from one of the methods for exporting\n * a specific type of an image.\n */\nexport default async (page, chart, options) => {\n /**\n * Keeps track of all resources added on the page with addXXXTag. etc\n * It's VITAL that all added resources ends up here so we can clear things\n * out when doing a new export in the same page!\n */\n const injectedResources = [];\n\n /** Clear out all state set on the page with addScriptTag/addStyleTag. */\n const clearInjected = async (page) => {\n for (const res of injectedResources) {\n await res.dispose();\n }\n\n // Reset all CSS and script tags\n await page.evaluate(() => {\n // eslint-disable-next-line no-undef\n const [, ...scriptsToRemove] = document.getElementsByTagName('script');\n // eslint-disable-next-line no-undef\n const [, ...stylesToRemove] = document.getElementsByTagName('style');\n // eslint-disable-next-line no-undef\n const [...linksToRemove] = document.getElementsByTagName('link');\n\n // Remove tags\n for (const element of [\n ...scriptsToRemove,\n ...stylesToRemove,\n ...linksToRemove\n ]) {\n element.remove();\n }\n });\n };\n\n try {\n const exportBench = benchmark('Puppeteer');\n\n log(4, '[export] Determining export path.');\n\n const exportOptions = options.export;\n\n // Force a rAF\n // See https://github.com/puppeteer/puppeteer/issues/7507\n // eslint-disable-next-line no-undef\n await page.evaluate(() => requestAnimationFrame(() => {}));\n\n // Decide whether display error or debbuger wrapper around it\n const displayErrors =\n exportOptions?.options?.chart?.displayErrors &&\n cache.getCache().activeManifest.modules.debugger;\n\n // eslint-disable-next-line no-undef\n await page.evaluate((d) => (window._displayErrors = d), displayErrors);\n\n const svgBench = benchmark('SVG handling');\n\n let isSVG;\n\n if (\n chart.indexOf &&\n (chart.indexOf('= 0 || chart.indexOf('= 0)\n ) {\n // SVG INPUT HANDLING\n\n log(4, '[export] Treating as SVG.');\n\n // If input is also svg, just return it\n if (exportOptions.type === 'svg') {\n return chart;\n }\n\n isSVG = true;\n const setPageBench = benchmark('Setting content');\n await page.setContent(svgTemplate(chart));\n setPageBench();\n } else {\n // JSON Config handling\n\n log(4, '[export] Treating as config.');\n\n // Need to perform straight inject\n if (exportOptions.strInj) {\n // Injection based configuration export\n const setPageBench = benchmark('Setting page content (inject)');\n\n await setAsConfig(\n page,\n {\n chart: {\n height: exportOptions.height,\n width: exportOptions.width\n }\n },\n options\n );\n\n setPageBench();\n } else {\n // Basic configuration export\n\n chart.chart.height = exportOptions.height;\n chart.chart.width = exportOptions.width;\n\n const setContentBench = benchmark('Setting page content (config)');\n await setAsConfig(page, chart, options);\n setContentBench();\n }\n }\n\n svgBench();\n const resBench = benchmark('Applying resources');\n\n // Use resources\n const resources = options.customCode.resources;\n if (resources) {\n // Load custom JS code\n if (resources.js) {\n injectedResources.push(\n await page.addScriptTag({\n content: resources.js\n })\n );\n }\n\n // Load scripts from all custom files\n if (resources.files) {\n for (const file of resources.files) {\n try {\n const isLocal = !file.startsWith('http') ? true : false;\n\n // Add each custom script from resources' files\n injectedResources.push(\n await page.addScriptTag(\n isLocal\n ? {\n content: readFileSync(file, 'utf8')\n }\n : {\n url: file\n }\n )\n );\n } catch (notice) {\n log(4, '[export] JS file not found.');\n }\n }\n }\n\n const cssBench = benchmark('Loading css');\n\n // Load CSS\n if (resources.css) {\n let cssImports = resources.css.match(/@import\\s*([^;]*);/g);\n if (cssImports) {\n // Handle css section\n for (let cssImportPath of cssImports) {\n if (cssImportPath) {\n cssImportPath = cssImportPath\n .replace('url(', '')\n .replace('@import', '')\n .replace(/\"/g, '')\n .replace(/'/g, '')\n .replace(/;/, '')\n .replace(/\\)/g, '')\n .trim();\n\n // Add each custom css from resources\n if (cssImportPath.startsWith('http')) {\n injectedResources.push(\n await page.addStyleTag({\n url: cssImportPath\n })\n );\n } else if (options.customCode.allowFileResources) {\n injectedResources.push(\n await page.addStyleTag({\n path: path.join(__basedir, cssImportPath)\n })\n );\n }\n }\n }\n }\n\n // The rest of the CSS section will be content by now\n injectedResources.push(\n await page.addStyleTag({\n content: resources.css.replace(/@import\\s*([^;]*);/g, '') || ' '\n })\n );\n }\n\n cssBench();\n }\n\n resBench();\n\n // Get the real chart size\n const size = isSVG\n ? await page.$eval(\n '#chart-container svg:first-of-type',\n async (element, scale) => {\n return {\n chartHeight: element.height.baseVal.value * scale,\n chartWidth: element.width.baseVal.value * scale\n };\n },\n parseFloat(exportOptions.scale)\n )\n : await page.evaluate(async () => {\n // eslint-disable-next-line no-undef\n const { chartHeight, chartWidth } = window.Highcharts.charts[0];\n return {\n chartHeight,\n chartWidth\n };\n });\n\n const vpBench = benchmark('Setting viewport');\n\n // Set final height and width for viewport\n const viewportHeight = Math.ceil(size?.chartHeight || exportOptions.height);\n const viewportWidth = Math.ceil(size?.chartWidth || exportOptions.width);\n\n // Set the viewport for the first time\n // NOTE: the call to setViewport is expensive - can we get away with only\n // calling it once, e.g. moving this one into the isSVG condition below?\n await page.setViewport({\n height: viewportHeight,\n width: viewportWidth,\n deviceScaleFactor: isSVG ? 1 : parseFloat(exportOptions.scale)\n });\n\n // Prepare a zoom callback for the next evaluate call\n const zoomCallback = isSVG\n ? // In case of SVG the zoom must be set directly for body\n (scale) => {\n // Set the zoom as scale\n // eslint-disable-next-line no-undef\n document.body.style.zoom = scale;\n\n // Set the margin to 0px\n // eslint-disable-next-line no-undef\n document.body.style.margin = '0px';\n }\n : // No need for such scale manipulation in case of other types of exports\n () => {\n // Reset the zoom for other exports than to SVGs\n // eslint-disable-next-line no-undef\n document.body.style.zoom = 1;\n };\n\n // Set the zoom accordingly\n await page.evaluate(zoomCallback, parseFloat(exportOptions.scale));\n\n // Get the clip region for the page\n const { height, width, x, y } = await getClipRegion(page);\n\n if (!isSVG) {\n // Set the final viewport now that we have the real height\n await page.setViewport({\n width: Math.round(width),\n height: Math.round(height),\n deviceScaleFactor: parseFloat(exportOptions.scale)\n });\n }\n\n vpBench();\n\n let data;\n\n const expBenchmark = benchmark('Rasterizing chart');\n\n // RASTERIZATION\n if (exportOptions.type === 'svg') {\n // SVG\n data = await createSVG(page);\n } else if (exportOptions.type === 'png' || exportOptions.type === 'jpeg') {\n // PNG or JPEG\n data = await createImage(page, exportOptions.type, 'base64', {\n width: viewportWidth,\n height: viewportHeight,\n x,\n y\n });\n } else if (exportOptions.type === 'pdf') {\n // PDF\n data = await createPDF(page, viewportHeight, viewportWidth, 'base64');\n } else {\n throw `Unsupported output format ${exportOptions.type}`;\n }\n\n // Destroy old charts after the export is done\n await page.evaluate(() => {\n // eslint-disable-next-line no-undef\n const oldCharts = Highcharts.charts;\n\n // Check in any already existing charts\n if (oldCharts.length) {\n // Destroy old charts\n for (const oldChart of oldCharts) {\n oldChart && oldChart.destroy();\n // eslint-disable-next-line no-undef\n Highcharts.charts.shift();\n }\n }\n });\n\n expBenchmark();\n exportBench();\n\n await clearInjected(page);\n\n return data;\n } catch (error) {\n await clearInjected(page);\n log(1, `[export] Error encountered during export: ${error}`);\n\n return error;\n }\n};\n","/*******************************************************************************\n\nHighcharts Export Server\n\nCopyright (c) 2016-2022, Highsoft\n\nLicenced under the MIT licence.\n\nAdditionally a valid Highcharts license is required for use.\n\nSee LICENSE file in root for details.\n\n*******************************************************************************/\n\nimport { log } from './logger.js';\nconst timers = {};\n\n// TODO: Read from config\nlet enabled = false;\n\nexport default (id) => {\n if (!enabled) {\n return () => {};\n }\n\n timers[id] = new Date();\n return () => {\n log(\n 3,\n `[benchmark] - ${id}: ${new Date().getTime() - timers[id].getTime()}ms`\n );\n };\n};\n","/*******************************************************************************\n\nHighcharts Export Server\n\nCopyright (c) 2016-2023, Highsoft\n\nLicenced under the MIT licence.\n\nAdditionally a valid Highcharts license is required for use.\n\nSee LICENSE file in root for details.\n\n*******************************************************************************/\n\nimport cssTemplate from './css.js';\n\nexport default (chart) => `\n\n\n \n \n Highcarts Export\n \n \n \n
\n ${chart}\n
\n \n\n\n`;\n","/*******************************************************************************\n\nHighcharts Export Server\n\nCopyright (c) 2016-2023, Highsoft\n\nLicenced under the MIT licence.\n\nAdditionally a valid Highcharts license is required for use.\n\nSee LICENSE file in root for details.\n\n*******************************************************************************/\n\nimport { v4 as uuid } from 'uuid';\nimport { Pool } from 'tarn';\nimport {\n close,\n newPage as browserNewPage,\n create as createBrowser\n} from './browser.js';\nimport { log } from './logger.js';\n\nimport puppeteerExport from './export.js';\n\nlet performedExports = 0;\nlet exportAttempts = 0;\nlet timeSpent = 0;\nlet droppedExports = 0;\nlet spentAverage = 0;\nlet poolConfig = {};\n\n// The pool instance\nlet pool = false;\n\n// Custom puppeteer arguments\nlet puppeteerArgs;\n\nconst factory = {\n /**\n * Creates a new worker.\n *\n * @return {object} - An object with the id of a resource, the work count and\n * a reference to the browser page.\n */\n create: async () => {\n const id = uuid();\n let page = false;\n\n const s = new Date().getTime();\n\n try {\n page = await browserNewPage();\n\n if (!page || page.isClosed()) {\n throw 'invalid page';\n }\n\n log(\n 3,\n `[pool] Successfully created a worker ${id} - took ${\n new Date().getTime() - s\n } ms.`\n );\n } catch (error) {\n log(\n 1,\n `[pool] Error creating a new page in pool entry creation! ${error}`\n );\n\n throw 'Error creating page';\n }\n\n return {\n id,\n page,\n // Try to distribute the initial work count\n workCount: Math.round(Math.random() * (poolConfig.workLimit / 2))\n };\n },\n\n /**\n * Validates a worker.\n *\n * @param {object} workerHandle - A browser's instance.\n *\n * @return {boolean} - Bool that indicates if a resource is valid or not.\n */\n validate: (workerHandle) => {\n if (\n poolConfig.workLimit &&\n ++workerHandle.workCount > poolConfig.workLimit\n ) {\n log(\n 3,\n `[pool] Worker failed validation:`,\n `exceeded work limit (limit is ${poolConfig.workLimit})`\n );\n return false;\n }\n return true;\n },\n\n /**\n * Destroys a worker.\n *\n * @param {object} workerHandle - A browser's instance.\n */\n destroy: (workerHandle) => {\n log(3, `[pool] Destroying pool entry ${workerHandle.id}.`);\n\n if (workerHandle.page) {\n // We don't really need to wait around for this.\n workerHandle.page.close();\n }\n },\n\n // Logger function\n log: (message, logLevel) => console.log(`${logLevel}: ${message}`)\n};\n\n/**\n * Inits the pool of resources.\n *\n * @param {object} config - Pool configuration along with custom puppeteer\n * arguments for the puppeteer.launch function.\n */\nexport const init = async (config) => {\n // The newest puppeteer arguments for the browser creation\n puppeteerArgs = config.puppeteerArgs;\n\n // Wait until we've sucessfully created a browser instance.\n try {\n await createBrowser(puppeteerArgs);\n } catch (e) {\n log(0, '[pool|browser]', e);\n }\n\n // For the module scope usage\n poolConfig = config && config.pool ? { ...config.pool } : {};\n\n log(\n 3,\n '[pool] Initializing pool:',\n `min ${poolConfig.initialWorkers}, max ${poolConfig.maxWorkers}.`\n );\n\n if (pool) {\n return log(\n 4,\n '[pool] Already initialized, please kill it before creating a new one.'\n );\n }\n\n // Attach process' exit listeners\n if (poolConfig.listenToProcessExits) {\n attachProcessExitListeners();\n }\n\n try {\n // Create a pool along with a minimal number of resources\n pool = new Pool({\n // Get the create/validate/destroy/log functions\n ...factory,\n min: poolConfig.initialWorkers,\n max: poolConfig.maxWorkers,\n createRetryIntervalMillis: 200,\n createTimeoutMillis: poolConfig.acquireTimeout,\n acquireTimeoutMillis: poolConfig.acquireTimeout,\n destroyTimeoutMillis: poolConfig.acquireTimeout,\n idleTimeoutMillis: poolConfig.timeoutThreshold,\n reapIntervalMillis: 1000, // poolConfig.reaper ? 120000 : 0, for now\n propagateCreateError: false\n });\n\n // Set events\n pool.on('createFail', (eventId, err) => {\n log(\n 1,\n `[pool] Error when creating worker of an event id ${eventId}:`,\n err\n );\n });\n\n pool.on('acquireFail', (eventId, err) => {\n log(\n 1,\n `[pool] Error when acquiring worker of an event id ${eventId}:`,\n err\n );\n });\n\n pool.on('destroyFail', (eventId, resource, err) => {\n log(\n 1,\n `[pool] Error when destroying worker of an id ${resource.id}, event id ${eventId}:`,\n err\n );\n });\n\n pool.on('release', (resource) => {\n log(4, `[pool] Releasing a worker of an id ${resource.id}`);\n });\n\n pool.on('destroySuccess', (eventId, resource) => {\n log(4, `[pool] Destroyed a worker of an id ${resource.id}`);\n });\n\n const initialResources = [];\n // Create an initial number of resources\n for (let i = 0; i < poolConfig.initialWorkers; i++) {\n initialResources.push(await pool.acquire().promise);\n }\n\n // Release the initial number of resources back to the pool\n initialResources.forEach((resource) => {\n pool.release(resource);\n });\n\n log(\n 3,\n `[pool] The pool is ready with ${poolConfig.initialWorkers} initial resources waiting.`\n );\n } catch (error) {\n log(1, `[pool] Couldn't create the worker pool ${error}`);\n throw error;\n }\n};\n\n/**\n * Attaches process' exit listeners.\n */\nexport function attachProcessExitListeners() {\n log(4, '[pool] Attaching exit listeners to the process.');\n\n // Kill all pool resources on exit\n process.on('exit', async () => {\n await killPool();\n });\n\n // Handler for the SIGINT\n process.on('SIGINT', (name, code) => {\n log(4, `The ${name} event with code: ${code}.`);\n process.exit(1);\n });\n\n // Handler for the SIGTERM\n process.on('SIGTERM', (name, code) => {\n log(4, `The ${name} event with code: ${code}.`);\n process.exit(1);\n });\n\n // Handler for the uncaughtException\n process.on('uncaughtException', async (error, name) => {\n log(4, `The ${name} error, message: ${error.message}.`);\n });\n}\n\n/**\n * Kills the pool and flush the browser instance.\n */\nexport async function killPool() {\n log(3, '[pool] Killing all workers.');\n\n // Return true when the pool is already destroyed\n if (pool.destroyed) {\n // Close the browser instance if still connected\n await close();\n return true;\n }\n\n // If still alive, destroy the pool of pages before closing a browser\n await pool.destroy();\n\n // Close the browser instance\n await close();\n return true;\n}\n\n/**\n * Posts work to the pool.\n *\n * @param {object} chart - Chart's options.\n * @param {object} options - All options object.\n */\nexport const postWork = async (chart, options) => {\n let workerHandle;\n\n // Handle fail conditions\n const fail = (msg) => {\n ++droppedExports;\n\n if (workerHandle) {\n pool.release(workerHandle);\n }\n\n throw 'In pool.postWork: ' + msg;\n };\n\n log(4, '[pool] Work received, starting to process.');\n\n if (poolConfig.benchmarking) {\n getPoolInfo();\n }\n\n ++exportAttempts;\n\n if (!pool) {\n log(1, '[pool] Work received, but pool has not been started.');\n return fail('Pool is not inited but work was posted to it!');\n }\n\n // Acquire the worker along with the id of resource and work count\n try {\n log(4, '[pool] Acquiring worker');\n workerHandle = await pool.acquire().promise;\n } catch (error) {\n return fail(`[pool] Error when acquiring available entry: ${error}`);\n }\n\n log(4, '[pool] Acquired worker handle');\n\n if (!workerHandle.page) {\n return fail('Resolved worker page is invalid: pool setup is wonky');\n }\n\n try {\n // Save the start time\n let workStart = new Date().getTime();\n\n log(4, `[pool] Starting work on pool entry ${workerHandle.id}.`);\n\n // Perform an export on a puppeteer level\n const result = await puppeteerExport(workerHandle.page, chart, options);\n\n // Check if it's an error\n if (result instanceof Error) {\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.\n if (result.message === 'Rasterization timeout') {\n workerHandle.page.close();\n workerHandle.page = await browserNewPage();\n }\n\n return fail(result);\n }\n\n // Release the resource back to the pool\n pool.release(workerHandle);\n\n // Used for statistics in averageTime and processedWorkCount, which\n // in turn is used by the /health route.\n const workEnd = new Date().getTime();\n const exportTime = workEnd - workStart;\n timeSpent += exportTime;\n spentAverage = timeSpent / ++performedExports;\n\n log(4, `[pool] Work completed in ${exportTime} ms.`);\n\n // Otherwise return the result\n return {\n data: result,\n options\n };\n } catch (error) {\n fail(`Error trying to perform puppeteer export: ${error}.`);\n }\n};\n\n/**\n * Gets the pool.\n */\nexport function getPool() {\n return pool;\n}\n\nexport const getPoolInfoJSON = () => ({\n min: pool.min,\n max: pool.max,\n size: pool.size,\n available: pool.available,\n borrowed: pool.borrowed,\n pending: pool.pending,\n spareResourceCapacity: pool.spareResourceCapacity\n});\n\n/**\n * Gets the pool's information.\n */\nexport function getPoolInfo() {\n const {\n min,\n max,\n size,\n available,\n borrowed,\n pending,\n spareResourceCapacity\n } = pool;\n\n log(4, `[pool] The minimum number of resources allowed by pool: ${min}.`);\n log(4, `[pool] The maximum number of resources allowed by pool: ${max}.`);\n log(\n 4,\n `[pool] The number of all resources in pool (free or in use): ${size}.`\n );\n log(\n 4,\n `[pool] The number of resources that are currently available: ${available}.`\n );\n log(\n 4,\n `[pool] The number of resources that are currently acquired: ${borrowed}.`\n );\n log(\n 4,\n `[pool] The number of callers waiting to acquire a resource: ${pending}.`\n );\n log(\n 4,\n `[pool] The number of how many more resources can the pool manage/create: ${spareResourceCapacity}.`\n );\n}\n\nexport default {\n init,\n killPool,\n postWork,\n getPool,\n getPoolInfo,\n getPoolInfoJSON,\n workAttempts: () => exportAttempts,\n droppedWork: () => droppedExports,\n averageTime: () => spentAverage,\n processedWorkCount: () => performedExports\n};\n","/*******************************************************************************\n\nHighcharts Export Server\n\nCopyright (c) 2016-2023, Highsoft\n\nLicenced under the MIT licence.\n\nAdditionally a valid Highcharts license is required for use.\n\nSee LICENSE file in root for details.\n\n*******************************************************************************/\n\nimport cache from '../../cache.js';\nimport pool from '../../pool.js';\n\nconst packageVersion = process.env.npm_package_version;\nconst serverStartTime = new Date();\n\n/**\n * Adds the /health route which outputs basic stats for the server\n */\nexport default (app) =>\n !app\n ? false\n : app.get('/health', (request, response) => {\n response.send({\n status: 'OK',\n bootTime: serverStartTime,\n uptime:\n Math.floor(\n (new Date().getTime() - serverStartTime.getTime()) / 1000 / 60\n ) + ' minutes',\n version: packageVersion,\n highchartsVersion: cache.version(),\n averageProcessingTime: pool.averageTime(),\n performedExports: pool.processedWorkCount(),\n failedExports: pool.droppedWork(),\n exportAttempts: pool.workAttempts(),\n sucessRatio: (pool.processedWorkCount() / pool.workAttempts()) * 100,\n // eslint-disable-next-line import/no-named-as-default-member\n pool: pool.getPoolInfoJSON()\n });\n });\n","/*******************************************************************************\n\nHighcharts Export Server\n\nCopyright (c) 2016-2023, Highsoft\n\nLicenced under the MIT licence.\n\nAdditionally a valid Highcharts license is required for use.\n\nSee LICENSE file in root for details.\n\n*******************************************************************************/\n\nimport { existsSync, readFileSync, promises as fsPromises } from 'fs';\n\nimport prompts from 'prompts';\n\nimport { log } from './logger.js';\nimport { deepCopy, isObject, printUsage, toBoolean } from './utils.js';\nimport {\n absoluteProps,\n defaultConfig,\n nestedArgs,\n promptsConfig\n} from './schemas/config.js';\n\nlet generalOptions = {};\n\n/**\n * Getter for the general options.\n *\n * @return {object} - General options object.\n */\nexport const getOptions = () => generalOptions;\n\n/**\n * Initializes and sets the general options for the server instace.\n *\n * @param {object} userOptions - Additional user options (e.g. from the node\n * module usage).\n * @param {string[]} args - CLI arguments.\n * @return {object} - General options object.\n */\nexport const setOptions = (userOptions, args) => {\n // Only for the CLI usage\n if (args?.length) {\n // Get the additional options from the custom JSON file\n generalOptions = loadConfigFile(args);\n }\n\n // Update the default config with a correct option values\n updateDefaultConfig(defaultConfig, generalOptions);\n\n // Set values for server's options and returns them\n generalOptions = initOptions(defaultConfig);\n\n // Apply user options if there are any\n if (userOptions) {\n // Merge user options\n generalOptions = mergeConfigOptions(\n generalOptions,\n userOptions,\n absoluteProps\n );\n }\n\n // Only for the CLI usage\n if (args?.length) {\n // Pair provided arguments\n generalOptions = pairArgumentValue(generalOptions, args, defaultConfig);\n }\n\n // Return final general options\n return generalOptions;\n};\n\n/**\n * Displays a prompt for the manual configuration.\n *\n * @param {string} configFileName - The name of a configuration file.\n */\nexport const manualConfig = async (configFileName) => {\n // Prepare a config object\n let configFile = {};\n\n // Check if provided config file exists\n if (existsSync(configFileName)) {\n configFile = JSON.parse(readFileSync(configFileName, 'utf8'));\n }\n\n // Question about a configuration category\n const onSubmit = async (p, categories) => {\n let questionsCounter = 0;\n let allQuestions = [];\n\n // Create a corresponding property in the manualConfig object\n for (const section of categories) {\n // Mark each option with a section\n promptsConfig[section] = promptsConfig[section].map((option) => ({\n ...option,\n section\n }));\n\n // Collect the questions\n allQuestions = [...allQuestions, ...promptsConfig[section]];\n }\n\n await prompts(allQuestions, {\n onSubmit: async (prompt, answer) => {\n // Get the default modules\n if (prompt.name === 'modules') {\n answer = answer.length\n ? answer.map((module) => prompt.choices[module])\n : prompt.choices;\n\n configFile[prompt.section][prompt.name] = answer;\n } else {\n configFile[prompt.section] = recursiveProps(\n Object.assign({}, configFile[prompt.section] || {}),\n prompt.name.split('.'),\n answer\n );\n }\n\n if (++questionsCounter === allQuestions.length) {\n try {\n await fsPromises.writeFile(\n configFileName,\n JSON.stringify(configFile, null, 2),\n 'utf8'\n );\n } catch (error) {\n log(1, `[config] Error while creating config.json: ${error}`);\n }\n return true;\n }\n }\n });\n\n return true;\n };\n\n // Find the categories\n const choices = Object.keys(promptsConfig).map((choice) => ({\n title: `${choice} options`,\n value: choice\n }));\n\n // Category prompt\n return prompts(\n {\n type: 'multiselect',\n name: 'category',\n message: 'Which category do you want to configure?',\n hint: 'Space: Select specific, A: Select all, Enter: Confirm.',\n instructions: '',\n choices\n },\n { onSubmit }\n );\n};\n\n/**\n * Maps the old options to the new config structure.\n *\n * @param {object} oldOptions - Options to be mapped.\n */\nexport const mapToNewConfig = (oldOptions) => {\n const newOptions = {};\n // Cycle through old-structured options\n for (const [key, value] of Object.entries(oldOptions)) {\n const propertiesChain = nestedArgs[key] ? nestedArgs[key].split('.') : [];\n\n // Populate object in correct properties levels\n propertiesChain.reduce(\n (obj, prop, index) =>\n (obj[prop] =\n propertiesChain.length - 1 === index ? value : obj[prop] || {}),\n newOptions\n );\n }\n return newOptions;\n};\n\n/**\n * Merges the new options to the options object. It omits undefined values.\n *\n * @param {object} options - Old options.\n * @param {object} newOptions - New options.\n * @param {string[]} absoluteProps - Array of object names that should be force\n * merged.\n */\nexport const mergeConfigOptions = (options, newOptions, absoluteProps = []) => {\n const mergedOptions = deepCopy(options);\n\n for (const [key, value] of Object.entries(newOptions)) {\n mergedOptions[key] =\n isObject(value) &&\n !absoluteProps.includes(key) &&\n mergedOptions[key] !== undefined\n ? mergeConfigOptions(mergedOptions[key], value, absoluteProps)\n : value !== undefined\n ? value\n : mergedOptions[key];\n }\n\n return mergedOptions;\n};\n\n/**\n * Initializes options for the `startExport` method by merging user options\n * with the general options.\n *\n * @param {any} exportOptions - User options for exporting.\n * @param {any} generalOptions - General options are used for the export server.\n * @return {object} - User options merged with default options.\n */\nexport const initExportSettings = (exportOptions, generalOptions = {}) => {\n let options = {};\n\n if (exportOptions.svg) {\n options = deepCopy(generalOptions);\n options.export.type = exportOptions.type || exportOptions.export.type;\n options.export.scale = exportOptions.scale || exportOptions.export.scale;\n options.export.outfile =\n exportOptions.outfile || exportOptions.export.outfile;\n options.payload = {\n svg: exportOptions.svg\n };\n } else {\n options = mergeConfigOptions(\n generalOptions,\n exportOptions,\n // Omit going down recursively with the belows\n absoluteProps\n );\n }\n\n options.export.outfile =\n options.export?.outfile || `chart.${options.export?.type || 'png'}`;\n return options;\n};\n\n/**\n * Loads the configuration from a custom JSON file.\n *\n * @param {string[]} args - CLI arguments.\n * @return {object} - Options object from the JSON file.\n */\nfunction loadConfigFile(args) {\n // Check if the --loadConfig option was used\n const configIndex = args.findIndex(\n (arg) => arg.replace(/-/g, '') === 'loadConfig'\n );\n\n // Check if the --loadConfig has a value\n if (configIndex > -1 && args[configIndex + 1]) {\n const fileName = args[configIndex + 1];\n try {\n // Check if an additional config file is a correct JSON file\n if (fileName && fileName.endsWith('.json')) {\n // Load an optional custom JSON config file\n return JSON.parse(readFileSync(fileName));\n }\n } catch (error) {\n log(1, `[config] Unable to load config from the ${fileName}: ${error}`);\n }\n }\n\n // No additional options to return\n return {};\n}\n\n/**\n * Setting correct values of the options from the default config.\n *\n * @param {object} configObj - The config object based on which the initial\n * configuration be made.\n * @param {object} customObj - The custom object which can contain additional\n * option values to set.\n * @param {string} propChain - Required for creating a string chain of\n * properties for nested arguments.\n */\nfunction updateDefaultConfig(configObj, customObj = {}, propChain = '') {\n Object.keys(configObj).forEach((key) => {\n if (!['puppeteer', 'highcharts'].includes(key)) {\n const entry = configObj[key];\n const customValue = customObj && customObj[key];\n let numEnvVal;\n\n if (typeof entry.value === 'undefined') {\n updateDefaultConfig(entry, customValue, `${propChain}.${key}`);\n } else {\n // If a value from a custom JSON exists, it take precedence\n if (customValue !== undefined) {\n entry.value = customValue;\n }\n\n // If a value from an env variable exists, it take precedence\n if (entry.envLink) {\n // Load the env var\n if (entry.type === 'boolean') {\n entry.value = toBoolean(\n [process.env[entry.envLink], entry.value].find(\n (el) => el || el === 'false'\n )\n );\n } else if (entry.type === 'number') {\n numEnvVal = +process.env[entry.envLink];\n entry.value = numEnvVal >= 0 ? numEnvVal : entry.value;\n } else if (\n entry.type.indexOf(']') >= 0 &&\n process.env[entry.envLink]\n ) {\n entry.value = process.env[entry.envLink].split(',');\n } else {\n entry.value = process.env[entry.envLink] || entry.value;\n }\n }\n }\n }\n });\n}\n\n/**\n * Inits options recursively.\n *\n * @param {any} items - Items to update options from.\n * @return {object} - Updated options object.\n */\nfunction initOptions(items) {\n let options = {};\n for (const [name, item] of Object.entries(items)) {\n options[name] = Object.prototype.hasOwnProperty.call(item, 'value')\n ? item.value\n : initOptions(item);\n }\n return options;\n}\n\n/**\n * Pairs argument with a corresponding value.\n *\n * @param {object} options - All server options.\n * @param {string[]} args - Array of arguments from a user.\n * @param {object} defaultConfig - The default config object.\n */\nfunction pairArgumentValue(options, args, defaultConfig) {\n for (let i = 0; i < args.length; i++) {\n let option = args[i].replace(/-/g, '');\n\n // Find the right place for property's value\n const propertiesChain = nestedArgs[option]\n ? nestedArgs[option].split('.')\n : [];\n\n propertiesChain.reduce((obj, prop, index) => {\n if (propertiesChain.length - 1 === index) {\n // Finds an option and set a corresponding value\n if (typeof obj[prop] !== 'undefined') {\n if (args[++i]) {\n obj[prop] = args[i] || obj[prop];\n } else {\n console.log(`Missing argument value for ${option}!`.red, '\\n');\n options = printUsage(defaultConfig);\n }\n }\n }\n return obj[prop];\n }, options);\n }\n\n return options;\n}\n\n/**\n * Recursively sets a property in a correct indentation level based on the\n * array of nested properties names.\n *\n * @param {object} objectToUpdate - Object where a property must be set on a\n * correct level.\n * @param {string[]}nestedNames - Array of nasted names that indicates\n * indentation level.\n * @param {any} value - A value to assign to the property.\n * @return {object} - Updated options object.\n */\nfunction recursiveProps(objectToUpdate, nestedNames, value) {\n while (nestedNames.length > 1) {\n const propName = nestedNames.shift();\n\n // Create a property in object if it doesn't exist\n if (!Object.prototype.hasOwnProperty.call(objectToUpdate, propName)) {\n objectToUpdate[propName] = {};\n }\n\n // Call function again if there still names to go\n objectToUpdate[propName] = recursiveProps(\n Object.assign({}, objectToUpdate[propName]),\n nestedNames,\n value\n );\n\n return objectToUpdate;\n }\n\n // Assign the final value\n objectToUpdate[nestedNames[0]] = value;\n return objectToUpdate;\n}\n\nexport default {\n getOptions,\n setOptions,\n manualConfig,\n mapToNewConfig,\n mergeConfigOptions,\n initExportSettings\n};\n","/*******************************************************************************\n\nHighcharts Export Server\n\nCopyright (c) 2016-2023, Highsoft\n\nLicenced under the MIT licence.\n\nAdditionally a valid Highcharts license is required for use.\n\nSee LICENSE file in root for details.\n\n*******************************************************************************/\n\nimport { readFile, readFileSync, writeFileSync } from 'fs';\n\nimport { log } from './logger.js';\nimport { killPool, postWork } from './pool.js';\nimport {\n clearText,\n fixType,\n handleResources,\n isCorrectJSON,\n optionsStringify,\n roundNumber,\n toBoolean,\n wrapAround\n} from './utils.js';\nimport { initExportSettings, getOptions } from './config.js';\n\nlet allowCodeExecution = false;\n\nexport const startExport = async (settings, endCallback) => {\n // Starting exporting process message\n log(4, '[chart] Starting exporting process.');\n\n // Initialize options\n const options = initExportSettings(settings, getOptions());\n\n // Get the export options\n const exportOptions = options.export;\n\n // If SVG is an input (argument can be sent only by the request)\n if (options.payload?.svg && options.payload.svg !== '') {\n return exportAsString(options.payload.svg.trim(), options, endCallback);\n }\n\n // Export using options from the file\n if (exportOptions.infile && exportOptions.infile.length) {\n log(4, '[chart] Attempting to export from an input file.');\n\n // Try to read the file\n return readFile(exportOptions.infile, 'utf8', (error, infile) => {\n if (error) {\n return log(1, `[chart] Error loading input file: ${error}.`);\n }\n\n // Get the string representation\n options.export.instr = infile;\n return exportAsString(options.export.instr.trim(), options, endCallback);\n });\n }\n\n // Export with options from the raw representation\n if (\n (exportOptions.instr && exportOptions.instr !== '') ||\n (exportOptions.options && exportOptions.options !== '')\n ) {\n log(4, '[chart] Attempting to export from a raw input.');\n\n // Perform a direct inject when forced\n if (toBoolean(options.customCode?.allowCodeExecution)) {\n return doStraightInject(options, endCallback);\n }\n\n // Either try to parse to JSON first or do the direct export\n return typeof exportOptions.instr === 'string'\n ? exportAsString(exportOptions.instr.trim(), options, endCallback)\n : doExport(\n options,\n exportOptions.instr || exportOptions.options,\n endCallback\n );\n }\n\n // No input specified, pass an error message to the callback\n log(\n 1,\n clearText(\n `[chart] No input specified.\n ${JSON.stringify(exportOptions, undefined, ' ')}.`\n )\n );\n\n return (\n endCallback &&\n endCallback(false, {\n error: true,\n message: 'No input specified.'\n })\n );\n};\n\nexport const batchExport = (options) => {\n const batchFunctions = [];\n\n // Split and pair the --batch arguments\n for (let pair of options.export.batch.split(';')) {\n pair = pair.split('=');\n if (pair.length === 2) {\n batchFunctions.push(\n new Promise((resolve, reject) => {\n startExport(\n {\n ...options,\n export: {\n ...options.export,\n infile: pair[0],\n outfile: pair[1]\n }\n },\n (info, error) => {\n // Throw an error\n if (error) {\n return reject(error);\n }\n\n // Save the base64 from a buffer to a correct image file\n writeFileSync(\n info.options.export.outfile,\n Buffer.from(info.data, 'base64')\n );\n\n resolve();\n }\n );\n })\n );\n }\n }\n\n // Kill the pool after all exports are done\n Promise.all(batchFunctions)\n .then(() => {\n killPool();\n })\n .catch((error) => {\n log(1, `[chart] Error encountered during batch export: ${error}`);\n killPool();\n });\n};\n\nexport const singleExport = (options) => {\n // Use instr or its alias, options\n options.export.instr = options.export.instr || options.export.options;\n\n // Perform an export\n startExport(options, (info, error) => {\n // Exit process when error\n if (error) {\n log(1, `[cli] ${error.message}`);\n process.exit(1);\n }\n\n const { outfile, type } = info.options.export;\n\n // Save the base64 from a buffer to a correct image file\n writeFileSync(\n outfile || `chart.${type}`,\n type !== 'svg' ? Buffer.from(info.data, 'base64') : info.data\n );\n\n // Kill the pool\n killPool();\n });\n};\n\n/**\n * Function for choosing chart size and scale based on options prioritization.\n *\n * @param {object} options - All options object.\n * @return {object} - An object with updated size and scale for a chart.\n */\nexport const findChartSize = (options) => {\n const { chart, exporting } =\n options.export?.options || isCorrectJSON(options.export?.instr);\n\n // See if globalOptions holds chart or exporting size\n const globalOptions = isCorrectJSON(options.export?.globalOptions);\n\n // Secure scale value\n let scale =\n options.export?.scale ||\n exporting?.scale ||\n globalOptions?.exporting?.scale ||\n options.export?.defaultScale ||\n 1;\n\n // the scale cannot be lower than 0.1 and cannot be higher than 5.0\n scale = Math.max(0.1, Math.min(scale, 5.0));\n\n // we want to round the numbers like 0.23234 -> 0.23\n scale = roundNumber(scale, 2);\n\n // Find chart size and scale\n return {\n height:\n options.export?.height ||\n exporting?.sourceHeight ||\n chart?.height ||\n globalOptions?.exporting?.sourceHeight ||\n globalOptions?.chart?.height ||\n options.export?.defaultHeight ||\n 400,\n width:\n options.export?.width ||\n exporting?.sourceWidth ||\n chart?.width ||\n globalOptions?.exporting?.sourceWidth ||\n globalOptions?.chart?.width ||\n options.export?.defaultWidth ||\n 600,\n scale\n };\n};\n\n/**\n * Function for final options preparation before export.\n *\n * @param {object} options - All options object.\n * @param {object} chartJson - Chart JSON.\n * @param {function} endCallback - The end callback.\n * @param {string} svg - The SVG representation.\n */\nconst doExport = (options, chartJson, endCallback, svg) => {\n let { export: exportOptions, customCode: customCodeOptions } = options;\n\n const allowCodeExecutionScoped =\n typeof customCodeOptions.allowCodeExecution === 'boolean'\n ? customCodeOptions.allowCodeExecution\n : allowCodeExecution;\n\n if (!customCodeOptions) {\n customCodeOptions = options.customCode = {};\n } else if (allowCodeExecutionScoped) {\n if (typeof options.customCode.resources === 'string') {\n // Process resources\n options.customCode.resources = handleResources(\n options.customCode.resources,\n toBoolean(options.customCode.allowFileResources)\n );\n } else if (!options.customCode.resources) {\n try {\n const resources = readFileSync('resources.json', 'utf8');\n options.customCode.resources = handleResources(\n resources,\n toBoolean(options.customCode.allowFileResources)\n );\n } catch (err) {\n log(3, `[chart] The default resources.json file not found.`);\n }\n }\n }\n\n // If the allowCodeExecution flag isn't set, we should refuse the usage\n // of callback, resources, and custom code. Additionally, the worker will\n // refuse to run arbitrary JavaScript. Prioritized should be the scoped\n // option, then we should take a look at the overall pool option.\n if (!allowCodeExecutionScoped && customCodeOptions) {\n if (\n customCodeOptions.callback ||\n customCodeOptions.resources ||\n customCodeOptions.customCode\n ) {\n // Send back a friendly message saying that the exporter does not support\n // these settings.\n return (\n endCallback &&\n endCallback(false, {\n error: true,\n message: clearText(\n `The callback, resources and customCode have been disabled for this\n server.`\n )\n })\n );\n }\n\n // Reset all additional custom code\n customCodeOptions.callback = false;\n customCodeOptions.resources = false;\n customCodeOptions.customCode = false;\n }\n\n // Clean properties to keep it lean and mean\n if (chartJson) {\n chartJson.chart = chartJson.chart || {};\n chartJson.exporting = chartJson.exporting || {};\n chartJson.exporting.enabled = false;\n }\n\n exportOptions.constr = exportOptions.constr || 'chart';\n exportOptions.type = fixType(exportOptions.type, exportOptions.outfile);\n if (exportOptions.type === 'svg') {\n exportOptions.width = false;\n }\n\n // Prepare global and theme options\n ['globalOptions', 'themeOptions'].forEach((optionsName) => {\n try {\n if (exportOptions && exportOptions[optionsName]) {\n if (\n typeof exportOptions[optionsName] === 'string' &&\n exportOptions[optionsName].endsWith('.json')\n ) {\n exportOptions[optionsName] = isCorrectJSON(\n readFileSync(exportOptions[optionsName], 'utf8'),\n true\n );\n } else {\n exportOptions[optionsName] = isCorrectJSON(\n exportOptions[optionsName],\n true\n );\n }\n }\n } catch (error) {\n exportOptions[optionsName] = {};\n log(1, `[chart] The ${optionsName} not found.`);\n }\n });\n\n // Prepare customCode\n if (customCodeOptions.allowCodeExecution) {\n customCodeOptions.customCode = wrapAround(\n customCodeOptions.customCode,\n customCodeOptions.allowFileResources\n );\n }\n\n // Get the callback\n if (\n customCodeOptions &&\n customCodeOptions.callback &&\n customCodeOptions.callback?.indexOf('{') < 0\n ) {\n // The allowFileResources is always set to false for HTTP requests to avoid\n // injecting arbitrary files from the fs\n if (customCodeOptions.allowFileResources) {\n try {\n customCodeOptions.callback = readFileSync(\n customCodeOptions.callback,\n 'utf8'\n );\n } catch (error) {\n log(2, `[chart] Error loading callback: ${error}.`);\n customCodeOptions.callback = false;\n }\n } else {\n customCodeOptions.callback = false;\n }\n }\n\n // Size search\n options.export = {\n ...options.export,\n ...findChartSize(options)\n };\n\n // Post the work to the pool\n postWork(exportOptions.strInj || chartJson || svg, options)\n .then((result) => endCallback(result))\n .catch((error) => {\n log(0, '[chart] When posting work:', error);\n return endCallback(false, error);\n });\n};\n\n/**\n * Function for straight injecting the code.\n * Dangerous and must be used deliberately by someone who sets up a server\n * (see --allowCodeExecution).\n *\n * @param {object} options - All options object.\n * @param {function} endCallback - The function to call when exporting is done.\n */\nconst doStraightInject = (options, endCallback) => {\n try {\n let strInj;\n let instr = options.export.instr || options.export.options;\n\n if (typeof instr !== 'string') {\n // Try to stringify options\n strInj = instr = optionsStringify(\n instr,\n options.customCode?.allowCodeExecution\n );\n }\n strInj = instr.replaceAll(/\\t|\\n|\\r/g, '').trim();\n\n // Get rid of the ;\n if (strInj[strInj.length - 1] === ';') {\n strInj = strInj.substring(0, strInj.length - 1);\n }\n\n // Save as stright inject string\n options.export.strInj = strInj;\n return doExport(options, false, endCallback);\n } catch (error) {\n const message = clearText(\n `Malformed input detected for ${options.export?.requestId || '?'}:\n Please make sure that your JSON/JavaScript options\n are sent using the \"options\" attribute, and that if you're using\n SVG, it is unescaped.`\n );\n\n log(1, message);\n return (\n endCallback &&\n endCallback(\n false,\n JSON.stringify({\n error: true,\n message\n })\n )\n );\n }\n};\n\n/**\n * Prepares an input before exporting.\n *\n * @param {string} stringToExport - String representation of SVG/export options.\n * @param {object} options - All options object.\n * @param {function} endCallback - The function to call when exporting is done.\n */\nconst exportAsString = (stringToExport, options, endCallback) => {\n const { allowCodeExecution } = options.customCode;\n\n // Check if it is SVG\n if (\n stringToExport.indexOf('= 0 ||\n stringToExport.indexOf('= 0\n ) {\n log(4, '[chart] Parsing input as SVG.');\n return doExport(options, false, endCallback, stringToExport);\n }\n\n try {\n // Try to parse to JSON and call the doExport function\n const chartJSON = JSON.parse(stringToExport.replaceAll(/\\t|\\n|\\r/g, ' '));\n\n // If a correct JSON, do the export\n return doExport(options, chartJSON, endCallback);\n } catch (error) {\n // Not a valid JSON\n if (toBoolean(allowCodeExecution)) {\n return doStraightInject(options, endCallback);\n } else {\n // Do not allow straight injection without the allowCodeExecution flag\n return (\n endCallback &&\n endCallback(false, {\n error: true,\n message: clearText(\n `Only JSON configurations and SVG is allowed for this server. If\n this is your server, JavaScript exporting can be enabled by starting\n the server with the --allowCodeExecution flag.`\n )\n })\n );\n }\n }\n};\n\nexport const getAllowCodeExecution = () => allowCodeExecution;\n\nexport const setAllowCodeExecution = (value) => {\n allowCodeExecution = toBoolean(value);\n};\n\n/**\n * Starts an exporting process\n *\n * @param {object} settings - Settings for export.\n * @param {function} endCallback - The function to call when exporting is done.\n */\nexport default {\n batchExport,\n singleExport,\n getAllowCodeExecution,\n setAllowCodeExecution,\n startExport,\n findChartSize\n};\n","/*******************************************************************************\n\nHighcharts Export Server\n\nCopyright (c) 2016-2023, Highsoft\n\nLicenced under the MIT licence.\n\nAdditionally a valid Highcharts license is required for use.\n\nSee LICENSE file in root for details.\n\n*******************************************************************************/\n\nimport { v4 as uuid } from 'uuid';\n\nimport { getAllowCodeExecution, startExport } from '../../chart.js';\nimport { getOptions, mergeConfigOptions } from '../../config.js';\nimport { log } from '../../logger.js';\nimport {\n clearText,\n fixType,\n isCorrectJSON,\n isPrivateRangeUrlFound,\n optionsStringify,\n measureTime\n} from '../../utils.js';\n\n// Reversed MIME types\nconst reversedMime = {\n png: 'image/png',\n jpeg: 'image/jpeg',\n gif: 'image/gif',\n pdf: 'application/pdf',\n svg: 'image/svg+xml'\n};\n\n// The requests counter\nlet requestsCounter = 0;\n\nconst benchmark = false;\n\n// The array of callbacks to call before a request\nconst beforeRequest = [];\n\n// The array of callbacks to call after a request\nconst afterRequest = [];\n\n/**\n * Calls callbacks.\n *\n * @param {Array} callbacks - An array of callbacks.\n * @param {object} request - The request.\n * @param {object} response - The response.\n * @param {object} data - The data to send to callbacks.\n * @return {object} - The result from a callback.\n */\nconst doCallbacks = (callbacks, request, response, data) => {\n let result = true;\n const { id, uniqueId, type, body } = data;\n\n callbacks.some((callback) => {\n if (callback) {\n let callResponse = callback(request, response, id, uniqueId, type, body);\n\n if (callResponse !== undefined && callResponse !== true) {\n result = callResponse;\n }\n\n return true;\n }\n });\n\n return result;\n};\n\n/**\n * Handles an export.\n *\n * @param {object} request - The request.\n * @param {object} response - The response.\n */\nconst exportHandler = (request, response) => {\n // Start counting time\n const stopCounter = measureTime();\n\n // Get the current server's general options\n const defaultOptions = getOptions();\n\n // Init default options\n if (benchmark) {\n console.log('Init default options:', stopCounter(), 'ms.');\n }\n\n const body = request.body;\n const id = ++requestsCounter;\n const uniqueId = uuid().replace(/-/g, '');\n let type = fixType(body.type);\n\n // Fix type\n if (benchmark) {\n console.log('Fix type:', stopCounter(), 'ms.');\n }\n\n // Throw 'Bad Request' if there's no body\n if (!body) {\n return response.status(400).send(\n clearText(\n `Body is required. Sending a body? Make sure your Content-type header\n is correct. Accepted is application/json and multipart/form-data.`\n )\n );\n }\n\n // All of the below can be used\n let instr = isCorrectJSON(body.infile || body.options || body.data);\n\n // Is correct JSON\n if (benchmark) {\n console.log('Is correct JSON:', stopCounter(), 'ms.');\n }\n\n // Throw 'Bad Request' if there's no JSON or SVG to export\n if (!instr && !body.svg) {\n log(\n 2,\n clearText(\n `Request ${uniqueId} from ${\n request.headers['x-forwarded-for'] || request.connection.remoteAddress\n } was incorrect. Check your payload.`\n )\n );\n\n return response.status(400).send(\n clearText(\n `No correct chart data found. Please make sure you are using\n application/json or multipart/form-data headers, and that the chart\n data is in the 'infile', 'options' or 'data' attribute if sending\n JSON or in the 'svg' if sending SVG.`\n )\n );\n }\n\n let callResponse = false;\n\n // Call the before request functions\n callResponse = doCallbacks(beforeRequest, request, response, {\n id,\n uniqueId,\n type,\n body\n });\n\n // Do callbacks\n if (benchmark) {\n console.log('Do callbacks:', stopCounter(), 'ms.');\n }\n\n // Block the request if one of a callbacks failed\n if (callResponse !== true) {\n return response.send(callResponse);\n }\n\n let connectionAborted = false;\n\n // In case the connection is closed, force to abort further actions\n request.socket.on('close', () => {\n connectionAborted = true;\n });\n\n log(4, `[export] Got an incoming HTTP request ${uniqueId}.`);\n\n body.constr = (typeof body.constr === 'string' && body.constr) || 'chart';\n\n // Gather and organize options from the payload\n const requestOptions = {\n export: {\n instr,\n type,\n constr: body.constr[0].toLowerCase() + body.constr.substr(1),\n height: body.height,\n width: body.width,\n scale: body.scale || defaultOptions.export.scale,\n globalOptions: isCorrectJSON(body.globalOptions, true),\n themeOptions: isCorrectJSON(body.themeOptions, true)\n },\n customCode: {\n allowCodeExecution: getAllowCodeExecution(),\n allowFileResources: false,\n resources: isCorrectJSON(body.resources, true),\n callback: body.callback,\n customCode: body.customCode\n }\n };\n\n // Organize options\n if (benchmark) {\n console.log('Organize options:', stopCounter(), 'ms.');\n }\n\n if (instr) {\n // Stringify JSON with options\n requestOptions.export.instr = optionsStringify(\n instr,\n requestOptions.customCode.allowCodeExecution\n );\n\n // Stringify JSON with options\n if (benchmark) {\n console.log('Stringify JSON with options:', stopCounter(), 'ms.');\n }\n }\n\n // Merge the request options into default ones\n const options = mergeConfigOptions(defaultOptions, requestOptions);\n\n // Merge config options\n if (benchmark) {\n console.log('Merge config options:', stopCounter(), 'ms.');\n }\n\n // Save the JSON if exists\n options.export.options = instr;\n\n // Lastly, add the server specific arguments into options as payload\n options.payload = {\n svg: body.svg || false,\n b64: body.b64 || false,\n dataOptions: isCorrectJSON(body.dataOptions, true),\n noDownload: body.noDownload || false,\n requestId: uniqueId\n };\n\n // Setting payload\n if (benchmark) {\n console.log('Setting payload:', stopCounter(), 'ms.');\n }\n\n // Test xlink:href elements from payload's SVG\n if (body.svg && isPrivateRangeUrlFound(options.payload.svg)) {\n return response\n .status(400)\n .send(\n 'SVG potentially contain at least one forbidden URL in xlink:href element.'\n );\n }\n\n // Check URL range\n if (benchmark) {\n console.log('Check URL range:', stopCounter(), 'ms.');\n }\n\n // Start the export process\n startExport(options, (info, error) => {\n // Remove the close event from the socket\n request.socket.removeAllListeners('close');\n\n // After Puppeteer exporting\n if (benchmark) {\n console.log('After Puppeteer exporting:', stopCounter(), 'ms.', '\\n');\n }\n\n // If the connection was closed, do nothing\n if (connectionAborted) {\n return log(\n 3,\n clearText(\n `[export] The client closed the connection before the chart was done\n processing.`\n )\n );\n }\n\n // If error, return it\n if (error) {\n log(\n 1,\n clearText(\n `[export] Work: ${uniqueId} could not be completed, sending:\n ${error}`\n )\n );\n return response.status(400).send(error.message);\n }\n\n // If data is missing, return the error\n if (!info || !info.data) {\n log(\n 1,\n clearText(\n `[export] Unexpected return from chart generation, please check your\n data Request: ${uniqueId} is ${info.data}.`\n )\n );\n return response\n .status(400)\n .send(\n 'Unexpected return from chart generation, please check your data.'\n );\n }\n\n // Get the type from options\n type = info.options.export.type;\n\n // The after request callbacks\n doCallbacks(afterRequest, request, response, { id, body: info.data });\n\n if (info.data) {\n // If only base64 is required, return it\n if (body.b64) {\n // Check if it is already base64 or a raw SVG\n if (type === 'pdf') {\n return response.send(\n Buffer.from(info.data, 'utf8').toString('base64')\n );\n }\n return response.send(info.data);\n }\n\n // Set correct content type\n response.header('Content-Type', reversedMime[type] || 'image/png');\n\n // Decide whether to download or not chart file\n if (!body.noDownload) {\n response.attachment(\n `${request.params.filename || request.body.filename || 'chart'}.${\n type || 'png'\n }`\n );\n }\n\n // If SVG, return plain content\n return type === 'svg'\n ? response.send(info.data)\n : response.send(Buffer.from(info.data, 'base64'));\n }\n });\n};\n\nexport default (app) => {\n app.post('/', exportHandler);\n app.post('/:filename', exportHandler);\n};\n","/*******************************************************************************\n\nHighcharts Export Server\n\nCopyright (c) 2016-2023, Highsoft\n\nLicenced under the MIT licence.\n\nAdditionally a valid Highcharts license is required for use.\n\nSee LICENSE file in root for details.\n\n*******************************************************************************/\n\nimport { promises as fsPromises } from 'fs';\nimport { posix } from 'path';\n\nimport bodyParser from 'body-parser';\nimport cors from 'cors';\nimport express from 'express';\nimport multer from 'multer';\nimport http from 'http';\nimport https from 'https';\n\nimport { log } from '../logger.js';\nimport rateLimit from './rate_limit.js';\nimport { __dirname } from '../utils.js';\n\nimport healthRoute from './routes/health.js';\nimport exportRoutes from './routes/export.js';\nimport vswitchRoute from './routes/change_hc_version.js';\nimport uiRoute from './routes/ui.js';\n\n// Create express app\nconst app = express();\n\n// Disable the X-Powered-By header\napp.disable('x-powered-by');\n\n// Enable CORS support\napp.use(cors());\n\n// Enable parsing of form data (files) with Multer package\nconst storage = multer.memoryStorage();\nconst upload = multer({\n storage,\n limits: {\n fieldsSize: '50MB'\n }\n});\n\napp.use(upload.any());\n\n// Enable body parser\napp.use(bodyParser.json({ limit: '50mb' }));\napp.use(bodyParser.urlencoded({ extended: true, limit: '50mb' }));\napp.use(bodyParser.urlencoded({ extended: false, limit: '50mb' }));\n\n/**\n * Error handler function.\n *\n * @param {object} error - An error object.\n * @return {string} - An error message.\n */\nconst errorHandler = (error) => log(1, `[server] Socket error: ${error}`);\n\n/**\n * Attaches error handlers for a server.\n *\n * @param {object} server - The http/https server.\n */\nconst attachErrorHandlers = (server) => {\n server.on('clientError', errorHandler);\n server.on('error', errorHandler);\n server.on('connection', (socket) =>\n socket.on('error', (error) => errorHandler(error, socket))\n );\n};\n\nexport const startServer = async (serverConfig) => {\n // Stop if not enabled\n if (!serverConfig.enable) {\n return false;\n }\n\n // // Get the pool\n // const pool = getPool();\n\n // // Try to create browser instance before starting the server\n // const resource = await pool.acquire();\n\n // // If not found, throw an error\n // if (!resource.browser) {\n // log(1, `[server] Could not acquire browser instance.`);\n // process.exit(1);\n // }\n\n // // Release the resource\n // pool.release(resource);\n\n // Listen HTTP server\n if (!serverConfig.ssl.enable && !serverConfig.ssl.force) {\n // Main server instance (HTTP)\n const httpServer = http.createServer(app);\n // Attach error handlers and listen to the server\n attachErrorHandlers(httpServer);\n // Listen\n httpServer.listen(serverConfig.port, serverConfig.host);\n\n log(\n 3,\n `[server] Started HTTP server on ${serverConfig.host}:${serverConfig.port}.`\n );\n }\n\n // Listen HTTPS server\n if (serverConfig.ssl.enable) {\n // Set up an SSL server also\n let key, cert;\n\n try {\n // Get the SSL key\n key = await fsPromises.readFile(\n posix.join(serverConfig.ssl.certPath, 'server.key'),\n 'utf8'\n );\n\n // Get the SSL certificate\n cert = await fsPromises.readFile(\n posix.join(serverConfig.ssl.certPath, 'server.crt'),\n 'utf8'\n );\n } catch (error) {\n log(\n 1,\n `[server] Unable to load key/certificate from ${serverConfig.ssl.certPath}.`\n );\n }\n\n if (key && cert) {\n // Main server instance (HTTPS)\n const httpsServer = https.createServer(app);\n // Attach error handlers and listen to the server\n attachErrorHandlers(httpsServer);\n // Listen\n httpsServer.listen(serverConfig.ssl.port, serverConfig.host);\n\n log(\n 3,\n `[server] Started HTTPS server on ${serverConfig.host}:${serverConfig.ssl.port}.`\n );\n }\n }\n\n // Enable the rate limiter if config says so\n if (\n serverConfig.rateLimiting &&\n serverConfig.rateLimiting.enable &&\n ![0, NaN].includes(serverConfig.rateLimiting.maxRequests)\n ) {\n rateLimit(app, serverConfig.rateLimiting);\n }\n\n // Set up static folder's route\n app.use(express.static(posix.join(__dirname, 'public')));\n\n // Set up routes\n healthRoute(app);\n exportRoutes(app);\n uiRoute(app);\n vswitchRoute(app);\n};\n\n/**\n * Returns the express instance.\n */\nexport const getExpress = () => {\n return express;\n};\n\n/**\n * Returns the app instance.\n */\nexport const getApp = () => {\n return app;\n};\n\n/**\n * Adds a middleware to the server.\n *\n * @param {object} path - An endpoint path to add middlewares to.\n * @param {Array} middlewares - An unlimited number of middlewares to use\n * against the specific endpoint.\n */\nexport const use = (path, ...middlewares) => {\n app.use(path, ...middlewares);\n};\n\n/**\n * Adds a get route to the server.\n *\n * @param {object} path - An endpoint path to add middlewares to.\n * @param {Array} middlewares - An unlimited number of middlewares to use\n * against the specific endpoint for GET method.\n */\nexport const get = (path, ...middlewares) => {\n app.get(path, ...middlewares);\n};\n\n/**\n * Adds a post route to the server.\n *\n * @param {object} path - An endpoint path to add middlewares to.\n * @param {Array} middlewares - An unlimited number of middlewares to use\n * against the specific endpoint for POST method.\n */\nexport const post = (path, ...middlewares) => {\n app.post(path, ...middlewares);\n};\n\n/**\n * Forcefully enables rate limiting.\n *\n * @param {object} limitConfig - The options object for the rate limiter\n * configuration.\n */\nexport const enableRateLimiting = (limitConfig) => {\n return rateLimit(app, limitConfig);\n};\n\nexport default {\n startServer,\n getExpress,\n getApp,\n use,\n get,\n post,\n enableRateLimiting\n};\n","/*******************************************************************************\n\nHighcharts Export Server\n\nCopyright (c) 2016-2023, Highsoft\n\nLicenced under the MIT licence.\n\nAdditionally a valid Highcharts license is required for use.\n\nSee LICENSE file in root for details.\n\n*******************************************************************************/\n\nimport { join } from 'path';\n\nimport { __dirname } from '../../utils.js';\n/**\n * Adds the / route for a UI when enabled for the export server\n */\nexport default (app) =>\n !app\n ? false\n : app.get('/', (request, response) => {\n response.sendFile(join(__dirname, 'public', 'index.html'));\n });\n","/*******************************************************************************\n\nHighcharts Export Server\n\nCopyright (c) 2016-2023, Highsoft\n\nLicenced under the MIT licence.\n\nAdditionally a valid Highcharts license is required for use.\n\nSee LICENSE file in root for details.\n\n*******************************************************************************/\n\nimport cache from '../../cache.js';\n\n/**\n * Adds a route that can be used to change the HC version on the server\n * TODO: Add auth token and connect to API\n */\nexport default (app) =>\n !app\n ? false\n : app.post('/change_hc_version/:newVersion', async (request, response) => {\n const ctoken = process.env.HIGHCHARTS_ADMIN_TOKEN;\n\n if (!ctoken || !ctoken.length) {\n return response.send({\n error: true,\n message:\n 'Server not configured to do run-time version changes: HIGHCHARTS_ADMIN_TOKEN not set'\n });\n }\n\n const token = request.get('hc-auth');\n\n if (!token || token !== ctoken) {\n return response.send({\n error: true,\n message: 'Invalid or missing token: set token in the hc-auth header'\n });\n }\n\n const newVersion = request.params.newVersion;\n\n if (newVersion) {\n try {\n // eslint-disable-next-line import/no-named-as-default-member\n await cache.updateVersion(newVersion);\n } catch (e) {\n response.send({\n error: true,\n message: e\n });\n }\n\n response.send({\n version: cache.version()\n });\n } else {\n response.send({\n error: true,\n message: 'No new version supplied'\n });\n }\n });\n","/*******************************************************************************\n\nHighcharts Export Server\n\nCopyright (c) 2016-2023, Highsoft\n\nLicenced under the MIT licence.\n\nAdditionally a valid Highcharts license is required for use.\n\nSee LICENSE file in root for details.\n\n*******************************************************************************/\n\n// Add the main directory in the global object\nimport 'colors';\n\nimport server, { startServer } from './server/server.js';\nimport {\n setAllowCodeExecution,\n batchExport,\n singleExport,\n startExport\n} from './chart.js';\nimport { mapToNewConfig, setOptions } from './config.js';\nimport { log, setLogLevel, enableFileLogging } from './logger.js';\nimport { killPool, init } from './pool.js';\nimport { checkCache } from './cache.js';\n\nexport default {\n log,\n mapToNewConfig,\n setOptions,\n singleExport,\n startExport,\n batchExport,\n server,\n startServer,\n killPool,\n initPool: async (options = {}) => {\n // Set the allowCodeExecution per export module scope\n setAllowCodeExecution(\n options.customCode && options.customCode.allowCodeExecution\n );\n\n // Set the log level\n setLogLevel(options.logging && parseInt(options.logging.level));\n\n // Set the log file path and name\n if (options.logging && options.logging.dest) {\n enableFileLogging(\n options.logging.dest,\n options.logging.file || 'highcharts-export-server.log'\n );\n }\n\n // Check if cache needs to be updated\n await checkCache(options.highcharts || { version: 'latest' });\n\n // Init the pool\n await init({\n pool: options.pool || {\n initialWorkers: 1,\n maxWorkers: 1\n },\n puppeteerArgs: options.puppeteer?.args || []\n });\n\n // Return updated options\n return options;\n }\n};\n"],"names":["dotenv","config","defaultConfig","puppeteer","args","value","type","description","highcharts","version","envLink","cdnURL","coreScripts","modules","indicators","scripts","forceFetch","export","infile","instr","options","outfile","constr","defaultHeight","defaultWidth","defaultScale","height","width","scale","globalOptions","themeOptions","batch","customCode","allowCodeExecution","allowFileResources","callback","resources","loadConfig","createConfig","server","enable","cliName","host","port","ssl","force","certPath","rateLimiting","maxRequests","window","delay","trustProxy","skipKey","skipToken","pool","initialWorkers","maxWorkers","workLimit","queueSize","timeoutThreshold","acquireTimeout","reaper","benchmarking","listenToProcessExits","logging","level","file","dest","ui","route","other","noLogo","payload","join","absoluteProps","nestedArgs","createNestedArgs","obj","propChain","Object","keys","forEach","k","includes","entry","substring","toConsole","toFile","pathCreated","levelsDesc","title","color","listeners","key","option","entries","log","newLevel","texts","length","prefix","Date","toString","split","trim","fn","existsSync","mkdirSync","appendFile","concat","error","console","apply","undefined","__dirname","fileURLToPath","URL","url","clearText","text","rule","replacer","replaceAll","fixType","formats","outType","pop","find","t","handleResources","allowedProps","handledResources","correctResources","endsWith","isCorrectJSON","readFileSync","notice","files","propName","map","item","data","parsedData","JSON","parse","stringify","deepCopy","copy","Array","isArray","prototype","hasOwnProperty","call","optionsStringify","allowFunctions","name","startsWith","printUsage","bold","yellow","cycleCategories","categories","descName","green","i","blue","category","toUpperCase","red","toBoolean","wrapAround","replace","rateLimit","app","limitConfig","msg","rateOptions","max","limiter","windowMs","delayMs","handler","request","response","format","json","status","send","message","default","skip","query","access_token","use","async","fetch","requestOptions","Promise","resolve","reject","protocol","https","http","getProtocol","get","res","on","chunk","cachePath","cache","activeManifest","sources","hcVersion","appliedConfig","extractVersion","substr","indexOf","fetchScript","script","proxyAgent","agent","timeout","process","env","statusCode","updateCache","sourcePath","customScripts","allScripts","c","m","proxyHost","proxyPort","HttpsProxyAgent","fetchedModules","all","writeFileSync","checkCache","manifestPath","requestUpdate","manifest","moduleMap","numberOfModules","some","moduleName","newManifest","saveConfigToManifest","cache$1","newVersion","assign","RANDOM_PID","randomBytes","PUPPETEER_DIR","path","minimalArgs","template","fs","browser","newPage","p","setContent","addScriptTag","evaluate","setupHighcharts","err","$eval","element","errorMessage","_displayErrors","innerHTML","close","connected","__basedir","setAsConfig","page","chart","triggerExport","puppeteerExport","injectedResources","clearInjected","dispose","scriptsToRemove","document","getElementsByTagName","stylesToRemove","linksToRemove","remove","exportBench","exportOptions","requestAnimationFrame","displayErrors","debugger","d","svgBench","isSVG","setPageBench","svgTemplate","strInj","setContentBench","resBench","js","push","content","isLocal","cssBench","css","cssImports","match","cssImportPath","addStyleTag","size","chartHeight","baseVal","chartWidth","parseFloat","Highcharts","charts","vpBench","viewportHeight","Math","ceil","viewportWidth","setViewport","deviceScaleFactor","zoomCallback","body","style","zoom","margin","x","y","getBoundingClientRect","trunc","getClipRegion","round","expBenchmark","outerHTML","createSVG","encoding","clip","race","screenshot","omitBackground","setTimeout","Error","createImage","pdf","createPDF","oldCharts","oldChart","destroy","shift","puppeteerArgs","performedExports","exportAttempts","timeSpent","droppedExports","spentAverage","poolConfig","factory","create","id","uuid","s","getTime","browserNewPage","isClosed","workCount","random","validate","workerHandle","logLevel","init","allArgs","tryCount","open","launch","headless","userDataDir","e","createBrowser","killPool","code","exit","Pool","min","createRetryIntervalMillis","createTimeoutMillis","acquireTimeoutMillis","destroyTimeoutMillis","idleTimeoutMillis","reapIntervalMillis","propagateCreateError","eventId","resource","initialResources","acquire","promise","release","destroyed","postWork","fail","getPoolInfo","workStart","result","exportTime","available","borrowed","pending","spareResourceCapacity","pool$1","packageVersion","npm_package_version","serverStartTime","generalOptions","getOptions","mergeConfigOptions","newOptions","mergedOptions","updateDefaultConfig","configObj","customObj","customValue","numEnvVal","el","initOptions","items","startExport","settings","endCallback","svg","initExportSettings","exportAsString","readFile","doStraightInject","doExport","findChartSize","exporting","precision","multiplier","pow","roundNumber","sourceHeight","sourceWidth","chartJson","customCodeOptions","allowCodeExecutionScoped","enabled","optionsName","then","catch","requestId","stringToExport","chartJSON","reversedMime","png","jpeg","gif","requestsCounter","beforeRequest","afterRequest","doCallbacks","callbacks","uniqueId","callResponse","exportHandler","start","hrtime","bigint","measureTime","defaultOptions","headers","connection","remoteAddress","connectionAborted","socket","toLowerCase","b64","dataOptions","noDownload","ipRegEx","info","removeAllListeners","Buffer","from","header","attachment","params","filename","express","disable","cors","storage","multer","memoryStorage","upload","limits","fieldsSize","any","bodyParser","limit","urlencoded","extended","errorHandler","attachErrorHandlers","startServer","serverConfig","httpServer","createServer","listen","cert","fsPromises","posix","httpsServer","NaN","static","bootTime","uptime","floor","highchartsVersion","averageProcessingTime","failedExports","sucessRatio","healthRoute","post","exportRoutes","sendFile","uiRoute","ctoken","HIGHCHARTS_ADMIN_TOKEN","token","vswitchRoute","getExpress","getApp","middlewares","enableRateLimiting","index","mapToNewConfig","oldOptions","propertiesChain","reduce","prop","setOptions","userOptions","configIndex","findIndex","arg","fileName","loadConfigFile","pairArgumentValue","singleExport","batchExport","batchFunctions","pair","initPool","parseInt","logDest","logFile","enableFileLogging"],"mappings":"snBAiBAA,EAAOC,SAIA,MAAMC,EAAgB,CAC3BC,UAAW,CACTC,KAAM,CACJC,MAAO,GACPC,KAAM,WACNC,YAAa,6CAGjBC,WAAY,CACVC,QAAS,CACPJ,MAAO,SACPK,QAAS,qBACTJ,KAAM,SACNC,YAAa,8BAEfI,OAAQ,CACNN,MAAO,+BACPK,QAAS,iBACTJ,KAAM,SACNC,YAAa,6CAEfK,YAAa,CACXF,QAAS,0BACTL,MAAO,CAAC,aAAc,kBAAmB,iBACzCC,KAAM,WACNC,YAAa,qCAEfM,QAAS,CACPH,QAAS,qBACTL,MAAO,CACL,QACA,MACA,QACA,YACA,cACA,uBACA,gBACA,uBACA,eACA,QACA,OACA,mBACA,eACA,cACA,UACA,UACA,WACA,UACA,YACA,cACA,YACA,sBACA,SACA,SACA,WACA,YACA,eACA,SACA,eACA,YACA,kBACA,SACA,cACA,mBACA,eACA,cACA,eACA,cACA,cACA,WACA,eACA,WACA,SACA,OACA,WACA,YACA,SACA,qBACA,aACA,WACA,WACA,WACA,WACA,eACA,UACA,kBACA,oBACA,cAEFC,KAAM,WACNC,YAAa,gCAEfO,WAAY,CACVJ,QAAS,wBACTL,MAAO,CAAC,kBACRC,KAAM,WACNC,YAAa,mCAEfQ,QAAS,CACPV,MAAO,CACL,wEACA,kGAEFC,KAAM,WACNC,YACE,qEAEJS,WAAY,CACVN,QAAS,yBACTL,OAAO,EACPC,KAAM,UACNC,YACE,oEAGNU,OAAQ,CACNC,OAAQ,CACNb,OAAO,EACPC,KAAM,SACNC,YACE,8FAEJY,MAAO,CACLd,OAAO,EACPC,KAAM,SACNC,YACE,iFAEJa,QAAS,CACPf,OAAO,EACPC,KAAM,SACNC,YAAa,oCAEfc,QAAS,CACPhB,OAAO,EACPC,KAAM,SACNC,YACE,2FAEJD,KAAM,CACJI,QAAS,sBACTL,MAAO,MACPC,KAAM,SACNC,YACE,sEAEJe,OAAQ,CACNZ,QAAS,wBACTL,MAAO,QACPC,KAAM,SACNC,YACE,6EAEJgB,cAAe,CACbb,QAAS,wBACTL,MAAO,IACPC,KAAM,SACNC,YACE,gFAEJiB,aAA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"} \ No newline at end of file +{"version":3,"file":"index.esm.js","sources":["../lib/schemas/config.js","../lib/logger.js","../lib/utils.js","../lib/server/rate_limit.js","../lib/fetch.js","../lib/cache.js","../lib/browser.js","../lib/export.js","../lib/benchmark.js","../templates/svg_export/svg_export.js","../lib/pool.js","../lib/server/routes/health.js","../lib/config.js","../lib/chart.js","../lib/server/routes/export.js","../lib/server/server.js","../lib/server/routes/ui.js","../lib/server/routes/change_hc_version.js","../lib/index.js"],"sourcesContent":["/*******************************************************************************\r\n\r\nHighcharts Export Server\r\n\r\nCopyright (c) 2016-2023, Highsoft\r\n\r\nLicenced under the MIT licence.\r\n\r\nAdditionally a valid Highcharts license is required for use.\r\n\r\nSee LICENSE file in root for details.\r\n\r\n*******************************************************************************/\r\n\r\n// Load .env into environment variables\r\nimport dotenv from 'dotenv';\r\n\r\ndotenv.config();\r\n\r\n// This is the configuration object with all options and their default values,\r\n// also from the .env file if one exists\r\nexport const defaultConfig = {\r\n puppeteer: {\r\n args: {\r\n value: [],\r\n type: 'string[]',\r\n description: 'Array of arguments to send to puppeteer.'\r\n }\r\n },\r\n highcharts: {\r\n version: {\r\n value: 'latest',\r\n envLink: 'HIGHCHARTS_VERSION',\r\n type: 'string',\r\n description: 'Highcharts version to use.'\r\n },\r\n cdnURL: {\r\n value: 'https://code.highcharts.com/',\r\n envLink: 'HIGHCHARTS_CDN',\r\n type: 'string',\r\n description: 'The CDN URL of Highcharts scripts to use.'\r\n },\r\n coreScripts: {\r\n envLink: 'HIGHCHARTS_CORE_SCRIPTS',\r\n value: ['highcharts', 'highcharts-more', 'highcharts-3d'],\r\n type: 'string[]',\r\n description: 'Highcharts core scripts to fetch.'\r\n },\r\n modules: {\r\n envLink: 'HIGHCHARTS_MODULES',\r\n value: [\r\n 'stock',\r\n 'map',\r\n 'gantt',\r\n 'exporting',\r\n 'export-data',\r\n 'parallel-coordinates',\r\n 'accessibility',\r\n 'annotations-advanced',\r\n 'boost-canvas',\r\n 'boost',\r\n 'data',\r\n 'draggable-points',\r\n 'static-scale',\r\n 'broken-axis',\r\n 'heatmap',\r\n 'tilemap',\r\n 'timeline',\r\n 'treemap',\r\n 'treegraph',\r\n 'item-series',\r\n 'drilldown',\r\n 'histogram-bellcurve',\r\n 'bullet',\r\n 'funnel',\r\n 'funnel3d',\r\n 'pyramid3d',\r\n 'networkgraph',\r\n 'pareto',\r\n 'pattern-fill',\r\n 'pictorial',\r\n 'price-indicator',\r\n 'sankey',\r\n 'arc-diagram',\r\n 'dependency-wheel',\r\n 'series-label',\r\n 'solid-gauge',\r\n 'sonification',\r\n 'stock-tools',\r\n 'streamgraph',\r\n 'sunburst',\r\n 'variable-pie',\r\n 'variwide',\r\n 'vector',\r\n 'venn',\r\n 'windbarb',\r\n 'wordcloud',\r\n 'xrange',\r\n 'no-data-to-display',\r\n 'drag-panes',\r\n 'debugger',\r\n 'dumbbell',\r\n 'lollipop',\r\n 'cylinder',\r\n 'organization',\r\n 'dotplot',\r\n 'marker-clusters',\r\n 'hollowcandlestick',\r\n 'heikinashi'\r\n ],\r\n type: 'string[]',\r\n description: 'Highcharts modules to fetch.'\r\n },\r\n indicators: {\r\n envLink: 'HIGHCHARTS_INDICATORS',\r\n value: ['indicators-all'],\r\n type: 'string[]',\r\n description: 'Highcharts indicators to fetch.'\r\n },\r\n scripts: {\r\n value: [\r\n 'https://cdnjs.cloudflare.com/ajax/libs/moment.js/2.29.4/moment.min.js',\r\n 'https://cdnjs.cloudflare.com/ajax/libs/moment-timezone/0.5.34/moment-timezone-with-data.min.js'\r\n ],\r\n type: 'string[]',\r\n description:\r\n 'Additional direct scripts/optional dependencies (e.g. moment.js).'\r\n },\r\n forceFetch: {\r\n envLink: 'HIGHCHARTS_FORCE_FETCH',\r\n value: false,\r\n type: 'boolean',\r\n description:\r\n 'Should all the scripts be refetched after rerunning the server.'\r\n }\r\n },\r\n export: {\r\n infile: {\r\n value: false,\r\n type: 'string',\r\n description:\r\n 'The input file name along with a type (json or svg). It can be a correct JSON or SVG file.'\r\n },\r\n instr: {\r\n value: false,\r\n type: 'string',\r\n description:\r\n 'An input in a form of a stringified JSON or SVG file. Overrides the --infile.'\r\n },\r\n options: {\r\n value: false,\r\n type: 'string',\r\n description: 'An alias for the --instr option.'\r\n },\r\n outfile: {\r\n value: false,\r\n type: 'string',\r\n description:\r\n 'The output filename along with a type (jpeg, png, pdf or svg). Ignores the --type flag.'\r\n },\r\n type: {\r\n envLink: 'EXPORT_DEFAULT_TYPE',\r\n value: 'png',\r\n type: 'string',\r\n description:\r\n 'The format of the file to export to. Can be jpeg, png, pdf or svg.'\r\n },\r\n constr: {\r\n envLink: 'EXPORT_DEFAULT_CONSTR',\r\n value: 'chart',\r\n type: 'string',\r\n description:\r\n 'The constructor to use. Can be chart, stockChart, mapChart or ganttChart.'\r\n },\r\n defaultHeight: {\r\n envLink: 'EXPORT_DEFAULT_HEIGHT',\r\n value: 400,\r\n type: 'number',\r\n description:\r\n 'The default height of the exported chart. Used when not found any value set.'\r\n },\r\n defaultWidth: {\r\n envLink: 'EXPORT_DEFAULT_WIDTH',\r\n value: 600,\r\n type: 'number',\r\n description:\r\n 'The default width of the exported chart. Used when not found any value set.'\r\n },\r\n defaultScale: {\r\n envLink: 'EXPORT_DEFAULT_SCALE',\r\n value: 1,\r\n type: 'number',\r\n description:\r\n 'The default scale of the exported chart. Ranges between 1 and 5.'\r\n },\r\n height: {\r\n type: 'number',\r\n value: false,\r\n description:\r\n 'The default height of the exported chart. Overrides the option in the chart settings.'\r\n },\r\n width: {\r\n type: 'number',\r\n value: false,\r\n description:\r\n 'The width of the exported chart. Overrides the option in the chart settings.'\r\n },\r\n scale: {\r\n value: false,\r\n type: 'number',\r\n description: 'The scale of the exported chart. Ranges between 1 and 5.'\r\n },\r\n globalOptions: {\r\n value: false,\r\n type: 'string',\r\n description:\r\n 'A stringified JSON or a filename with options to be passed into the Highcharts.setOptions.'\r\n },\r\n themeOptions: {\r\n value: false,\r\n type: 'string',\r\n description:\r\n 'A stringified JSON or a filename with theme options to be passed into the Highcharts.setOptions.'\r\n },\r\n batch: {\r\n value: false,\r\n type: 'string',\r\n description:\r\n 'Starts a batch job. A string that contains input/output pairs: \"in=out;in=out;..\".'\r\n }\r\n },\r\n customCode: {\r\n allowCodeExecution: {\r\n envLink: 'HIGHCHARTS_ALLOW_CODE_EXECUTION',\r\n value: false,\r\n type: 'boolean',\r\n description:\r\n 'If set to true, allow for the execution of arbitrary code when exporting.'\r\n },\r\n allowFileResources: {\r\n envLink: 'HIGHCHARTS_ALLOW_FILE_RESOURCES',\r\n value: true,\r\n type: 'boolean',\r\n description:\r\n 'Allow injecting resources from the filesystem. Has no effect when running as a server.'\r\n },\r\n customCode: {\r\n value: false,\r\n type: 'string',\r\n description:\r\n 'A function to be called before chart initialization. Can be a filename with the js extension.'\r\n },\r\n callback: {\r\n value: false,\r\n type: 'string',\r\n description: 'A JavaScript file with a function to run on construction.'\r\n },\r\n resources: {\r\n value: false,\r\n type: 'string',\r\n description:\r\n 'An additional resource in a form of stringified JSON. It can contain files, js and css sections.'\r\n },\r\n loadConfig: {\r\n value: false,\r\n type: 'string',\r\n description: 'A file that contains a pre-defined config to use.'\r\n },\r\n createConfig: {\r\n value: false,\r\n type: 'string',\r\n description:\r\n 'Allows to set options through a prompt and save in a provided config file.'\r\n }\r\n },\r\n server: {\r\n enable: {\r\n envLink: 'HIGHCHARTS_SERVER_ENABLE',\r\n value: false,\r\n type: 'boolean',\r\n cliName: 'enableServer',\r\n description: 'If set to true, starts a server on 0.0.0.0.'\r\n },\r\n host: {\r\n envLink: 'HIGHCHARTS_SERVER_HOST',\r\n value: '0.0.0.0',\r\n type: 'string',\r\n description:\r\n 'The hostname of the server. Also starts a server listening on the supplied hostname.'\r\n },\r\n port: {\r\n envLink: 'HIGHCHARTS_SERVER_PORT',\r\n value: 7801,\r\n type: 'number',\r\n description: 'The port to use for the server. Defaults to 7801.'\r\n },\r\n ssl: {\r\n enable: {\r\n envLink: 'HIGHCHARTS_SERVER_SSL_ENABLE',\r\n value: false,\r\n type: 'boolean',\r\n cliName: 'enableSsl',\r\n description: 'Enables the SSL protocol.'\r\n },\r\n force: {\r\n envLink: 'HIGHCHARTS_SERVER_SSL_FORCE',\r\n value: false,\r\n type: 'boolean',\r\n cliName: 'sslForced',\r\n description:\r\n 'If set to true, forces the server to only serve over HTTPS.'\r\n },\r\n port: {\r\n envLink: 'HIGHCHARTS_SERVER_SSL_PORT',\r\n value: 443,\r\n type: 'number',\r\n cliName: 'sslPort',\r\n description: 'The port on which to run the SSL server.'\r\n },\r\n certPath: {\r\n envLink: 'HIGHCHARTS_SSL_CERT_PATH',\r\n value: '',\r\n type: 'string',\r\n description: 'The path to the SSL certificate/key.'\r\n }\r\n },\r\n rateLimiting: {\r\n enable: {\r\n envLink: 'HIGHCHARTS_RATE_LIMIT_ENABLE',\r\n value: false,\r\n type: 'boolean',\r\n cliName: 'enableRateLimiting',\r\n description: 'Enables rate limiting.'\r\n },\r\n maxRequests: {\r\n envLink: 'HIGHCHARTS_RATE_LIMIT_MAX',\r\n value: 10,\r\n type: 'number',\r\n description: 'Max requests allowed in a one minute.'\r\n },\r\n window: {\r\n envLink: 'HIGHCHARTS_RATE_LIMIT_WINDOW',\r\n value: 1,\r\n type: 'number',\r\n description: 'The time window in minutes for rate limiting.'\r\n },\r\n delay: {\r\n envLink: 'HIGHCHARTS_RATE_LIMIT_DELAY',\r\n value: 0,\r\n type: 'number',\r\n description:\r\n 'The amount to delay each successive request before hitting the max.'\r\n },\r\n trustProxy: {\r\n envLink: 'HIGHCHARTS_RATE_LIMIT_TRUST_PROXY',\r\n value: false,\r\n type: 'boolean',\r\n description: 'Set this to true if behind a load balancer.'\r\n },\r\n skipKey: {\r\n envLink: 'HIGHCHARTS_RATE_LIMIT_SKIP_KEY',\r\n value: '',\r\n type: 'number|string',\r\n description:\r\n 'Allows bypassing the rate limiter and should be provided with skipToken argument.'\r\n },\r\n skipToken: {\r\n envLink: 'HIGHCHARTS_RATE_LIMIT_SKIP_TOKEN',\r\n value: '',\r\n type: 'number|string',\r\n description:\r\n 'Allows bypassing the rate limiter and should be provided with skipKey argument.'\r\n }\r\n }\r\n },\r\n pool: {\r\n minWorkers: {\r\n envLink: 'HIGHCHARTS_POOL_MIN_WORKERS',\r\n value: 4,\r\n type: 'number',\r\n description: 'The number of initial workers to spawn.'\r\n },\r\n maxWorkers: {\r\n envLink: 'HIGHCHARTS_POOL_MAX_WORKERS',\r\n value: 8,\r\n type: 'number',\r\n description: 'The number of max workers to spawn.'\r\n },\r\n workLimit: {\r\n envLink: 'HIGHCHARTS_POOL_WORK_LIMIT',\r\n value: 40,\r\n type: 'number',\r\n description:\r\n 'The pieces of work that can be performed before restarting process.'\r\n },\r\n acquireTimeout: {\r\n envLink: 'HIGHCHARTS_POOL_ACQUIRE_TIMEOUT',\r\n value: 5000,\r\n type: 'number',\r\n description:\r\n 'The number of milliseconds to wait for acquiring a resource.'\r\n },\r\n createTimeout: {\r\n envLink: 'HIGHCHARTS_POOL_CREATE_TIMEOUT',\r\n value: 5000,\r\n type: 'number',\r\n description: 'The number of milliseconds to wait for creating a resource.'\r\n },\r\n destroyTimeout: {\r\n envLink: 'HIGHCHARTS_POOL_DESTROY_TIMEOUT',\r\n value: 5000,\r\n type: 'number',\r\n description:\r\n 'The number of milliseconds to wait for destroying a resource.'\r\n },\r\n idleTimeout: {\r\n envLink: 'HIGHCHARTS_POOL_IDLE_TIMEOUT',\r\n value: 30000,\r\n type: 'number',\r\n description:\r\n 'The number of milliseconds after an idle resource is destroyed.'\r\n },\r\n rasterizationTimeout: {\r\n envLink: 'HIGHCHARTS_POOL_RASTERIZATION_TIMEOUT',\r\n value: 1500,\r\n type: 'number',\r\n description: 'The number of milliseconds to wait for rendering a webpage.'\r\n },\r\n createRetryInterval: {\r\n envLink: 'HIGHCHARTS_POOL_CREATE_RETRY_INTERVAL',\r\n value: 200,\r\n type: 'number',\r\n description:\r\n 'The number of milliseconds after the create process is retried in case of fail.'\r\n },\r\n reaperInterval: {\r\n envLink: 'HIGHCHARTS_POOL_REAPER_INTERVAL',\r\n value: 1000,\r\n type: 'number',\r\n description:\r\n 'The number of milliseconds after the check for idle resources to destroy is triggered.'\r\n },\r\n benchmarking: {\r\n envLink: 'HIGHCHARTS_POOL_BENCHMARKING',\r\n value: false,\r\n type: 'boolean',\r\n description: 'Enable benchmarking.'\r\n },\r\n listenToProcessExits: {\r\n envLink: 'HIGHCHARTS_POOL_LISTEN_TO_PROCESS_EXITS',\r\n value: true,\r\n type: 'boolean',\r\n description:\r\n 'Set to false in order to skip attaching process.exit handlers.'\r\n }\r\n },\r\n logging: {\r\n level: {\r\n envLink: 'HIGHCHARTS_LOG_LEVEL',\r\n value: 4,\r\n type: 'number',\r\n cliName: 'logLevel',\r\n description:\r\n 'The log level (0: silent, 1: error, 2: warning, 3: notice, 4: verbose).'\r\n },\r\n file: {\r\n envLink: 'HIGHCHARTS_LOG_FILE',\r\n value: 'highcharts-export-server.log',\r\n type: 'string',\r\n cliName: 'logFile',\r\n description:\r\n 'A name of a log file. The --logDest also needs to be set to enable file logging.'\r\n },\r\n dest: {\r\n envLink: 'HIGHCHARTS_LOG_DEST',\r\n value: 'log/',\r\n type: 'string',\r\n cliName: 'logDest',\r\n description: 'The path to store log files. Also enables file logging.'\r\n }\r\n },\r\n ui: {\r\n enable: {\r\n envLink: 'HIGHCHARTS_UI_ENABLE',\r\n value: false,\r\n type: 'boolean',\r\n cliName: 'enableUi',\r\n description: 'Enables the UI for the export server.'\r\n },\r\n route: {\r\n envLink: 'HIGHCHARTS_UI_ROUTE',\r\n value: '/',\r\n type: 'string',\r\n cliName: 'uiRoute',\r\n description: 'The route to attach the UI to.'\r\n }\r\n },\r\n other: {\r\n noLogo: {\r\n envLink: 'HIGHCHARTS_NO_LOGO',\r\n value: false,\r\n type: 'boolean',\r\n description:\r\n 'Skip printing the logo on a startup. Will be replaced by a simple text.'\r\n }\r\n },\r\n payload: {}\r\n};\r\n\r\n// The config descriptions object for the prompts functionality. It contains\r\n// information like:\r\n// * Type of a prompt\r\n// * Name of an option\r\n// * Short description of a chosen option\r\n// * Initial value\r\nexport const promptsConfig = {\r\n puppeteer: [\r\n {\r\n type: 'list',\r\n name: 'args',\r\n message: 'Puppeteer arguments',\r\n initial: defaultConfig.puppeteer.args.value.join(','),\r\n separator: ','\r\n }\r\n ],\r\n highcharts: [\r\n {\r\n type: 'text',\r\n name: 'version',\r\n message: 'Highcharts version',\r\n initial: defaultConfig.highcharts.version.value\r\n },\r\n {\r\n type: 'text',\r\n name: 'cdnURL',\r\n message: 'The url of CDN',\r\n initial: defaultConfig.highcharts.cdnURL.value\r\n },\r\n {\r\n type: 'multiselect',\r\n name: 'modules',\r\n message: 'Available modules',\r\n instructions: 'Space: Select specific, A: Select all, Enter: Confirm.',\r\n choices: defaultConfig.highcharts.modules.value\r\n },\r\n {\r\n type: 'list',\r\n name: 'scripts',\r\n message: 'Custom scripts',\r\n initial: defaultConfig.highcharts.scripts.value.join(','),\r\n separator: ','\r\n },\r\n {\r\n type: 'toggle',\r\n name: 'forceFetch',\r\n message: 'Should refetch all the scripts after each server rerun',\r\n initial: defaultConfig.highcharts.forceFetch.value\r\n }\r\n ],\r\n export: [\r\n {\r\n type: 'select',\r\n name: 'type',\r\n message: 'The default type of a file to export to',\r\n hint: `Default: ${defaultConfig.export.type.value}`,\r\n initial: 0,\r\n choices: ['png', 'jpeg', 'pdf', 'svg']\r\n },\r\n {\r\n type: 'select',\r\n name: 'constr',\r\n message: 'The default constructor for Highcharts to use',\r\n hint: `Default: ${defaultConfig.export.constr.value}`,\r\n initial: 0,\r\n choices: ['chart', 'stockChart', 'mapChart', 'ganttChart']\r\n },\r\n {\r\n type: 'number',\r\n name: 'defaultHeight',\r\n message: 'The default fallback height of the exported chart',\r\n initial: defaultConfig.export.defaultHeight.value\r\n },\r\n {\r\n type: 'number',\r\n name: 'defaultWidth',\r\n message: 'The default fallback width of the exported chart',\r\n initial: defaultConfig.export.defaultWidth.value\r\n },\r\n {\r\n type: 'number',\r\n name: 'defaultScale',\r\n message: 'The default fallback scale of the exported chart',\r\n initial: defaultConfig.export.defaultScale.value,\r\n min: 0.1,\r\n max: 5\r\n }\r\n ],\r\n customCode: [\r\n {\r\n type: 'toggle',\r\n name: 'allowCodeExecution',\r\n message: 'Allow to execute custom code',\r\n initial: defaultConfig.customCode.allowCodeExecution.value\r\n },\r\n {\r\n type: 'toggle',\r\n name: 'allowFileResources',\r\n message: 'Allow file resources',\r\n initial: defaultConfig.customCode.allowFileResources.value\r\n }\r\n ],\r\n server: [\r\n {\r\n type: 'toggle',\r\n name: 'enable',\r\n message: 'Starts a server on 0.0.0.0',\r\n initial: defaultConfig.server.enable.value\r\n },\r\n {\r\n type: 'text',\r\n name: 'host',\r\n message: 'A hostname of a server',\r\n initial: defaultConfig.server.host.value\r\n },\r\n {\r\n type: 'number',\r\n name: 'port',\r\n message: 'A port of a server',\r\n initial: defaultConfig.server.port.value\r\n },\r\n {\r\n type: 'toggle',\r\n name: 'ssl.enable',\r\n message: 'Enable SSL protocol',\r\n initial: defaultConfig.server.ssl.enable.value\r\n },\r\n {\r\n type: 'toggle',\r\n name: 'ssl.force',\r\n message: 'Force to only serve over HTTPS',\r\n initial: defaultConfig.server.ssl.force.value\r\n },\r\n {\r\n type: 'number',\r\n name: 'ssl.port',\r\n message: 'Port on which to run the SSL server',\r\n initial: defaultConfig.server.ssl.port.value\r\n },\r\n {\r\n type: 'text',\r\n name: 'ssl.certPath',\r\n message: 'A path where to find the SSL certificate/key',\r\n initial: defaultConfig.server.ssl.certPath.value\r\n },\r\n {\r\n type: 'toggle',\r\n name: 'rateLimiting.enable',\r\n message: 'Enable rate limiting',\r\n initial: defaultConfig.server.rateLimiting.enable.value\r\n },\r\n {\r\n type: 'number',\r\n name: 'rateLimiting.maxRequests',\r\n message: 'Max requests allowed in a one minute',\r\n initial: defaultConfig.server.rateLimiting.maxRequests.value\r\n },\r\n {\r\n type: 'number',\r\n name: 'rateLimiting.window',\r\n message: 'The time window in minutes for rate limiting',\r\n initial: defaultConfig.server.rateLimiting.window.value\r\n },\r\n {\r\n type: 'number',\r\n name: 'rateLimiting.delay',\r\n message:\r\n 'The amount to delay each successive request before hitting the max',\r\n initial: defaultConfig.server.rateLimiting.delay.value\r\n },\r\n {\r\n type: 'toggle',\r\n name: 'rateLimiting.trustProxy',\r\n message: 'Set this to true if behind a load balancer',\r\n initial: defaultConfig.server.rateLimiting.trustProxy.value\r\n },\r\n {\r\n type: 'text',\r\n name: 'rateLimiting.skipKey',\r\n message:\r\n 'Allows bypassing the rate limiter and should be provided with skipToken argument',\r\n initial: defaultConfig.server.rateLimiting.skipKey.value\r\n },\r\n {\r\n type: 'text',\r\n name: 'rateLimiting.skipToken',\r\n message:\r\n 'Allows bypassing the rate limiter and should be provided with skipKey argument',\r\n initial: defaultConfig.server.rateLimiting.skipToken.value\r\n }\r\n ],\r\n pool: [\r\n {\r\n type: 'number',\r\n name: 'minWorkers',\r\n message: 'The number of initial workers to spawn',\r\n initial: defaultConfig.pool.minWorkers.value\r\n },\r\n {\r\n type: 'number',\r\n name: 'maxWorkers',\r\n message: 'The number of max workers to spawn',\r\n initial: defaultConfig.pool.maxWorkers.value\r\n },\r\n {\r\n type: 'number',\r\n name: 'workLimit',\r\n message:\r\n 'The pieces of work that can be performed before restarting a puppeteer process',\r\n initial: defaultConfig.pool.workLimit.value\r\n },\r\n {\r\n type: 'number',\r\n name: 'acquireTimeout',\r\n message: 'The number of milliseconds to wait for acquiring a resource',\r\n initial: defaultConfig.pool.acquireTimeout.value\r\n },\r\n {\r\n type: 'number',\r\n name: 'createTimeout',\r\n message: 'The number of milliseconds to wait for creating a resource',\r\n initial: defaultConfig.pool.createTimeout.value\r\n },\r\n {\r\n type: 'number',\r\n name: 'destroyTimeout',\r\n message: 'The number of milliseconds to wait for destroying a resource',\r\n initial: defaultConfig.pool.destroyTimeout.value\r\n },\r\n {\r\n type: 'number',\r\n name: 'idleTimeout',\r\n message: 'The number of milliseconds after an idle resource is destroyed',\r\n initial: defaultConfig.pool.idleTimeout.value\r\n },\r\n {\r\n type: 'number',\r\n name: 'rasterizationTimeout',\r\n message: 'The number of milliseconds to wait for rendering a webpage',\r\n initial: defaultConfig.pool.rasterizationTimeout.value\r\n },\r\n {\r\n type: 'number',\r\n name: 'createRetryInterval',\r\n message:\r\n 'The number of milliseconds after the create process is retried in case of fail',\r\n initial: defaultConfig.pool.createRetryInterval.value\r\n },\r\n {\r\n type: 'number',\r\n name: 'reaperInterval',\r\n message:\r\n 'The number of milliseconds after the check for idle resources to destroy is triggered',\r\n initial: defaultConfig.pool.reaperInterval.value\r\n },\r\n {\r\n type: 'toggle',\r\n name: 'benchmarking',\r\n message: 'Set benchmarking',\r\n initial: defaultConfig.pool.benchmarking.value\r\n },\r\n {\r\n type: 'toggle',\r\n name: 'listenToProcessExits',\r\n message: 'Set to false in order to skip attaching process.exit handlers',\r\n initial: defaultConfig.pool.listenToProcessExits.value\r\n }\r\n ],\r\n logging: [\r\n {\r\n type: 'number',\r\n name: 'level',\r\n message:\r\n 'The log level (0: silent, 1: error, 2: warning, 3: notice, 4: verbose)',\r\n initial: defaultConfig.logging.level.value,\r\n round: 0,\r\n min: 0,\r\n max: 4\r\n },\r\n {\r\n type: 'text',\r\n name: 'file',\r\n message:\r\n 'A name of a log file. The --logDest also needs to be set to enable file logging',\r\n initial: defaultConfig.logging.file.value\r\n },\r\n {\r\n type: 'text',\r\n name: 'dest',\r\n message: 'A path to log files. It enables file logging',\r\n initial: defaultConfig.logging.dest.value\r\n }\r\n ],\r\n ui: [\r\n {\r\n type: 'toggle',\r\n name: 'enable',\r\n message: 'Enable UI for the export server',\r\n initial: defaultConfig.ui.enable.value\r\n },\r\n {\r\n type: 'text',\r\n name: 'route',\r\n message: 'A route to attach the UI to',\r\n initial: defaultConfig.ui.route.value\r\n }\r\n ],\r\n other: [\r\n {\r\n type: 'toggle',\r\n name: 'noLogo',\r\n message:\r\n 'Skip printing the logo on a startup. Will be replaced by a simple text',\r\n initial: defaultConfig.other.noLogo.value\r\n }\r\n ]\r\n};\r\n\r\n// Absolute props that, in case of merging recursively, need to be force merged\r\nexport const absoluteProps = [\r\n 'options',\r\n 'globalOptions',\r\n 'themeOptions',\r\n 'resources',\r\n 'payload'\r\n];\r\n\r\n// Argument nesting level of all export server options\r\nexport const nestedArgs = {};\r\n\r\n/**\r\n * Creates nested arguments chain for all options\r\n *\r\n * @param {object} obj - The object based on which the initial configuration be\r\n * made.\r\n * @param {string } propChain - Required for creating a string chain of\r\n * properties for nested arguments.\r\n */\r\nconst createNestedArgs = (obj, propChain = '') => {\r\n Object.keys(obj).forEach((k) => {\r\n if (!['puppeteer', 'highcharts'].includes(k)) {\r\n const entry = obj[k];\r\n if (typeof entry.value === 'undefined') {\r\n // Go deeper in the nested arguments\r\n createNestedArgs(entry, `${propChain}.${k}`);\r\n } else {\r\n // Create the chain of nested arguments\r\n nestedArgs[entry.cliName || k] = `${propChain}.${k}`.substring(1);\r\n }\r\n }\r\n });\r\n};\r\n\r\ncreateNestedArgs(defaultConfig);\r\n","/*******************************************************************************\r\n\r\nHighcharts Export Server\r\n\r\nCopyright (c) 2016-2023, Highsoft\r\n\r\nLicenced under the MIT licence.\r\n\r\nAdditionally a valid Highcharts license is required for use.\r\n\r\nSee LICENSE file in root for details.\r\n\r\n*******************************************************************************/\r\n\r\nimport { appendFile, existsSync, mkdirSync } from 'fs';\r\n\r\nimport { defaultConfig } from './schemas/config.js';\r\n\r\n// The default logging config\r\nlet logging = {\r\n // Flags for logging status\r\n toConsole: true,\r\n toFile: false,\r\n pathCreated: false,\r\n // Log levels\r\n levelsDesc: [\r\n {\r\n title: 'error',\r\n color: 'red'\r\n },\r\n {\r\n title: 'warning',\r\n color: 'yellow'\r\n },\r\n {\r\n title: 'notice',\r\n color: 'blue'\r\n },\r\n {\r\n title: 'verbose',\r\n color: 'gray'\r\n }\r\n ],\r\n // Log listeners\r\n listeners: []\r\n};\r\n\r\n// Gather init logging options\r\nfor (const [key, option] of Object.entries(defaultConfig.logging)) {\r\n logging[key] = option.value;\r\n}\r\n\r\n/**\r\n * Logs a message. Accepts a variable amount of arguments. Arguments after\r\n * `level` will be passed directly to console.log, and/or will be joined\r\n * and appended to the log file.\r\n *\r\n * @param {any} args - An array of arguments where the first is the log level\r\n * and the rest are strings to build a message with.\r\n */\r\nexport const log = (...args) => {\r\n const [newLevel, ...texts] = args;\r\n\r\n // Current logging options\r\n const { level, levelsDesc } = logging;\r\n\r\n // Check if log level is within a correct range\r\n if (newLevel === 0 || newLevel > level || level > levelsDesc.length) {\r\n return;\r\n }\r\n\r\n // Get rid of the GMT text information\r\n const newDate = new Date().toString().split('(')[0].trim();\r\n\r\n // Create a message's prefix\r\n const prefix = `${newDate} [${levelsDesc[newLevel - 1].title}] -`;\r\n\r\n // Call available log listeners\r\n logging.listeners.forEach((fn) => {\r\n fn(prefix, texts.join(' '));\r\n });\r\n\r\n // Log to file\r\n if (logging.toFile) {\r\n if (!logging.pathCreated) {\r\n // Create if does not exist\r\n !existsSync(logging.dest) && mkdirSync(logging.dest);\r\n\r\n // We now assume the path is available, e.g. it's the responsibility\r\n // of the user to create the path with the correct access rights.\r\n logging.pathCreated = true;\r\n }\r\n\r\n // Add the content to a file\r\n appendFile(\r\n `${logging.dest}${logging.file}`,\r\n [prefix].concat(texts).join(' ') + '\\n',\r\n (error) => {\r\n if (error) {\r\n console.log(`[logger] Unable to write to log file: ${error}`);\r\n logging.toFile = false;\r\n }\r\n }\r\n );\r\n }\r\n\r\n // Log to console\r\n if (logging.toConsole) {\r\n console.log.apply(\r\n undefined,\r\n [prefix.toString()[logging.levelsDesc[newLevel - 1].color]].concat(texts)\r\n );\r\n }\r\n};\r\n\r\n/**\r\n * Sets the file logging configuration.\r\n *\r\n * @param {string} logDest - A path to log to.\r\n * @param {string} logFile - The name of the log file.\r\n */\r\nexport const enableFileLogging = (logDest, logFile) => {\r\n // Update logging options\r\n logging = {\r\n ...logging,\r\n dest: logDest || logging.dest,\r\n file: logFile || logging.file,\r\n toFile: true\r\n };\r\n\r\n if (logging.dest.length === 0) {\r\n return log(1, '[logger] File logging init: no path supplied.');\r\n }\r\n\r\n if (!logging.dest.endsWith('/')) {\r\n logging.dest += '/';\r\n }\r\n};\r\n\r\n/**\r\n * Adds a log listener.\r\n *\r\n * @param {function} fn - The function to call when getting a log event.\r\n */\r\nexport const listen = (fn) => {\r\n logging.listeners.push(fn);\r\n};\r\n\r\n/**\r\n * Sets the current log level. Log levels are:\r\n * - 0 = no logging\r\n * - 1 = error\r\n * - 2 = warning\r\n * - 3 = notice\r\n * - 4 = verbose\r\n *\r\n * @param {number} newLevel - The new log level (0 - 4).\r\n */\r\nexport const setLogLevel = (newLevel) => {\r\n if (newLevel >= 0 && newLevel <= logging.levelsDesc.length) {\r\n logging.level = newLevel;\r\n }\r\n};\r\n\r\n/**\r\n * Enables or disables logging to the stdout.\r\n *\r\n * @param {boolean} enabled - Whether log to console or not.\r\n */\r\nexport const toggleSTDOut = (enabled) => {\r\n logging.toConsole = enabled;\r\n};\r\n\r\nexport default {\r\n log,\r\n enableFileLogging,\r\n listen,\r\n setLogLevel,\r\n toggleSTDOut\r\n};\r\n","/*******************************************************************************\r\n\r\nHighcharts Export Server\r\n\r\nCopyright (c) 2016-2023, Highsoft\r\n\r\nLicenced under the MIT licence.\r\n\r\nAdditionally a valid Highcharts license is required for use.\r\n\r\nSee LICENSE file in root for details.\r\n\r\n*******************************************************************************/\r\n\r\nimport { readFileSync } from 'fs';\r\nimport { fileURLToPath } from 'url';\r\n\r\nimport { defaultConfig } from '../lib/schemas/config.js';\r\nimport { log } from './logger.js';\r\n\r\nconst MAX_BACKOFF_ATTEMPTS = 6;\r\n\r\nexport const __dirname = fileURLToPath(new URL('../.', import.meta.url));\r\n\r\n/**\r\n * Clears text from whitespaces with a regex rule.\r\n *\r\n * @param {string} rule - The rule for clearing a string, default to /\\s\\s+/g.\r\n * @return {string} - Cleared text.\r\n */\r\nexport const clearText = (text, rule = /\\s\\s+/g, replacer = ' ') =>\r\n text.replaceAll(rule, replacer).trim();\r\n\r\n/**\r\n * Delays calling the function by time calculated based on the backoff\r\n * algorithm.\r\n *\r\n * @param {function} fn - A function to try to call with the backoff algorithm\r\n * on.\r\n * @param {number} attempt - The number of an attempt, where the first one is 0.\r\n */\r\nexport const expBackoff = async (fn, attempt = 0, ...args) => {\r\n try {\r\n // Try to call the function\r\n return await fn(...args);\r\n } catch (error) {\r\n // Calculate delay in ms\r\n const delayInMs = 2 ** attempt * 1000;\r\n\r\n // If the attempt exceeds the maximum attempts of reapeat, throw an error\r\n if (++attempt >= MAX_BACKOFF_ATTEMPTS) {\r\n throw error;\r\n }\r\n\r\n // Wait given amount of time\r\n await new Promise((response) => setTimeout(response, delayInMs));\r\n log(\r\n 3,\r\n `[pool] Waited ${delayInMs}ms until next call for the resource id: ${args[0]}.`\r\n );\r\n\r\n // Try again\r\n return expBackoff(fn, attempt, ...args);\r\n }\r\n};\r\n\r\n/**\r\n * Fixes to supported type format if MIME.\r\n *\r\n * @param {string} type - Type to be corrected.\r\n * @param {string} outfile - Name of the outfile.\r\n */\r\nexport const fixType = (type, outfile) => {\r\n // MIME types\r\n const mimeTypes = {\r\n 'image/png': 'png',\r\n 'image/jpeg': 'jpeg',\r\n 'application/pdf': 'pdf',\r\n 'image/svg+xml': 'svg'\r\n };\r\n\r\n // Formats\r\n const formats = ['png', 'jpeg', 'pdf', 'svg'];\r\n\r\n // Check if type and outfile's extensions are the same\r\n if (outfile) {\r\n const outType = outfile.split('.').pop();\r\n\r\n // Check if extension has a correct type\r\n if (formats.includes(outType) && type !== outType) {\r\n type = outType;\r\n }\r\n }\r\n\r\n // Return a correct type\r\n return mimeTypes[type] || formats.find((t) => t === type) || 'png';\r\n};\r\n\r\n/**\r\n * Handles the provided resources.\r\n *\r\n * @param {string} resources - The stringified resources.\r\n * @param {string} allowFileResources - Decide if resources from file are\r\n * allowed.\r\n */\r\nexport const handleResources = (resources = false, allowFileResources) => {\r\n const allowedProps = ['js', 'css', 'files'];\r\n\r\n let handledResources = resources;\r\n let correctResources = false;\r\n\r\n // Try to load resources from a file\r\n if (allowFileResources && resources.endsWith('.json')) {\r\n try {\r\n if (!resources) {\r\n handledResources = isCorrectJSON(\r\n readFileSync('resources.json', 'utf8')\r\n );\r\n } else if (resources && resources.endsWith('.json')) {\r\n handledResources = isCorrectJSON(readFileSync(resources, 'utf8'));\r\n } else {\r\n handledResources = isCorrectJSON(resources);\r\n if (handledResources === true) {\r\n handledResources = isCorrectJSON(\r\n readFileSync('resources.json', 'utf8')\r\n );\r\n }\r\n }\r\n } catch (notice) {\r\n return log(3, `[cli] No resources found.`);\r\n }\r\n } else {\r\n // Try to get JSON\r\n handledResources = isCorrectJSON(resources);\r\n\r\n // Get rid of the files section\r\n if (!allowFileResources) {\r\n delete handledResources.files;\r\n }\r\n }\r\n\r\n // Filter from unnecessary properties\r\n for (const propName in handledResources) {\r\n if (!allowedProps.includes(propName)) {\r\n delete handledResources[propName];\r\n } else if (!correctResources) {\r\n correctResources = true;\r\n }\r\n }\r\n\r\n // Check if at least one of allowed properties is present\r\n if (!correctResources) {\r\n return log(3, `[cli] No resources found.`);\r\n }\r\n\r\n // Handle files section\r\n if (handledResources.files) {\r\n handledResources.files = handledResources.files.map((item) => item.trim());\r\n if (!handledResources.files || handledResources.files.length <= 0) {\r\n delete handledResources.files;\r\n }\r\n }\r\n\r\n // Return resources\r\n return handledResources;\r\n};\r\n\r\n/**\r\n * Checks if provided data is or can be a correct JSON.\r\n *\r\n * @param {any} data - Data to be checked.\r\n * @param {boolean} toString - If true, return stringified representation.\r\n */\r\nexport function isCorrectJSON(data, toString) {\r\n try {\r\n // Get the string representation if not already before parsing\r\n const parsedData = JSON.parse(\r\n typeof data !== 'string' ? JSON.stringify(data) : data\r\n );\r\n\r\n // Return a stringified representation of a JSON if required\r\n if (typeof parsedData !== 'string' && toString) {\r\n return JSON.stringify(parsedData);\r\n }\r\n\r\n // Return a JSON\r\n return parsedData;\r\n } catch (error) {\r\n return false;\r\n }\r\n}\r\n\r\n/**\r\n * Checks if item is an object.\r\n *\r\n * @param {any} item - Item to be checked.\r\n */\r\nexport const isObject = (item) =>\r\n typeof item === 'object' && !Array.isArray(item) && item !== null;\r\n\r\n/**\r\n * Checks if string contains private range urls.\r\n *\r\n * @export utils\r\n * @param item {string} item to be checked\r\n */\r\nexport const isPrivateRangeUrlFound = (item) => {\r\n return [\r\n 'localhost',\r\n '(10).(.*).(.*).(.*)',\r\n '(127).(.*).(.*).(.*)',\r\n '(172).(1[6-9]|2[0-9]|3[0-1]).(.*).(.*)',\r\n '(192).(168).(.*).(.*)'\r\n ].some((ipRegEx) =>\r\n item.match(`xlink:href=\"(?:(http://|https://))?${ipRegEx}`)\r\n );\r\n};\r\n\r\n/**\r\n * Creates and returns a deep copy of the given object.\r\n *\r\n * @param {object} object - Object to copy.\r\n * @return {object} - Deep copy of the object.\r\n */\r\nexport const deepCopy = (obj) => {\r\n if (obj === null || typeof obj !== 'object') {\r\n return obj;\r\n }\r\n\r\n const copy = Array.isArray(obj) ? [] : {};\r\n\r\n for (const key in obj) {\r\n if (Object.prototype.hasOwnProperty.call(obj, key)) {\r\n copy[key] = deepCopy(obj[key]);\r\n }\r\n }\r\n\r\n return copy;\r\n};\r\n\r\n/**\r\n * Stringifies object with options. Possible to preserve functions with\r\n * allowFunctions flag.\r\n *\r\n * @param {object} options - Options to stringify.\r\n * @param {boolean} allowFunctions - Flag for keeping functions.\r\n */\r\nexport const optionsStringify = (options, allowFunctions) => {\r\n const replacerCallback = (name, value) => {\r\n if (typeof value === 'string') {\r\n value = value.trim();\r\n\r\n // If allowFunctions is set to true, preserve functions\r\n if (\r\n (value.startsWith('function(') || value.startsWith('function (')) &&\r\n value.endsWith('}')\r\n ) {\r\n value = allowFunctions\r\n ? `EXP_FUN${(value + '').replaceAll(/\\n|\\t|\\r/g, ' ')}EXP_FUN`\r\n : undefined;\r\n }\r\n }\r\n\r\n return typeof value === 'function'\r\n ? `EXP_FUN${(value + '').replaceAll(/\\n|\\t|\\r/g, ' ')}EXP_FUN`\r\n : value;\r\n };\r\n\r\n // Stringify options and if required, replace special functions marks\r\n return JSON.stringify(options, replacerCallback).replaceAll(\r\n /\"EXP_FUN|EXP_FUN\"/g,\r\n ''\r\n );\r\n};\r\n\r\n/**\r\n * Prints the export server logo.\r\n *\r\n * @param {boolean} noLogo - Whether to display logo or text.\r\n */\r\nexport const printLogo = (noLogo) => {\r\n // Get package version either from env or from package.json\r\n const packageVersion =\r\n process.env.npm_package_version ||\r\n JSON.parse(readFileSync(new URL('../package.json', import.meta.url)))\r\n .version;\r\n\r\n // Print text only\r\n if (noLogo) {\r\n console.log(`Starting highcharts export server v${packageVersion}...`);\r\n return;\r\n }\r\n\r\n // Print the logo\r\n console.log(\r\n readFileSync(__dirname + '/msg/startup.msg').toString().bold.yellow,\r\n `v${packageVersion}`\r\n );\r\n};\r\n\r\n/**\r\n * Prints the CLI usage. If required, it can list properties recursively\r\n */\r\nexport function printUsage() {\r\n const pad = 48;\r\n const readme = 'https://github.com/highcharts/node-export-server#readme';\r\n\r\n // Display readme information\r\n console.log(\r\n 'Usage of CLI arguments:'.bold,\r\n '\\n------',\r\n `\\nFor more detailed information visit readme at: ${readme.bold.yellow}.`\r\n );\r\n\r\n const cycleCategories = (categories) => {\r\n for (const [name, option] of Object.entries(categories)) {\r\n // If category has more levels, go further\r\n if (!Object.prototype.hasOwnProperty.call(option, 'value')) {\r\n cycleCategories(option);\r\n } else {\r\n let descName = ` --${option.cliName || name} ${\r\n ('<' + option.type + '>').green\r\n } `;\r\n if (descName.length < pad) {\r\n for (let i = descName.length; i < pad; i++) {\r\n descName += '.';\r\n }\r\n }\r\n\r\n // Display correctly aligned messages\r\n console.log(\r\n descName,\r\n option.description,\r\n `[Default: ${option.value.toString().bold}]`.blue\r\n );\r\n }\r\n }\r\n };\r\n\r\n // Cycle through options of each categories and display the usage info\r\n Object.keys(defaultConfig).forEach((category) => {\r\n // Only puppeteer and highcharts categories cannot be configured through CLI\r\n if (!['puppeteer', 'highcharts'].includes(category)) {\r\n console.log(`\\n${category.toUpperCase()}`.red);\r\n cycleCategories(defaultConfig[category]);\r\n }\r\n });\r\n console.log('\\n');\r\n}\r\n\r\n/**\r\n * Rounds number to passed precision.\r\n *\r\n * @param {number} value - Number to round.\r\n * @param {number} precision - A precision of rounding.\r\n */\r\nexport const roundNumber = (value, precision = 1) => {\r\n const multiplier = Math.pow(10, precision || 0);\r\n return Math.round(+value * multiplier) / multiplier;\r\n};\r\n\r\n/**\r\n * Casts the item to boolean.\r\n *\r\n * @param {any} item - Item to be cast.\r\n */\r\nexport const toBoolean = (item) =>\r\n ['false', 'undefined', 'null', 'NaN', '0', ''].includes(item)\r\n ? false\r\n : !!item;\r\n\r\n/**\r\n * If necessary, places a custom code inside a function.\r\n *\r\n * @param {any} customCode - The customCode.\r\n */\r\nexport const wrapAround = (customCode, allowFileResources) => {\r\n if (customCode && typeof customCode === 'string') {\r\n customCode = customCode.trim();\r\n\r\n if (customCode.endsWith('.js')) {\r\n return allowFileResources\r\n ? wrapAround(readFileSync(customCode, 'utf8'))\r\n : false;\r\n } else if (\r\n customCode.startsWith('function()') ||\r\n customCode.startsWith('function ()') ||\r\n customCode.startsWith('()=>') ||\r\n customCode.startsWith('() =>')\r\n ) {\r\n return `(${customCode})()`;\r\n }\r\n return customCode.replace(/;$/, '');\r\n }\r\n};\r\n\r\n/**\r\n * Utility to measure time.\r\n */\r\nexport const measureTime = () => {\r\n const start = process.hrtime.bigint();\r\n return () => Number(process.hrtime.bigint() - start) / 1000000;\r\n};\r\n\r\nexport default {\r\n __dirname,\r\n clearText,\r\n expBackoff,\r\n fixType,\r\n handleResources,\r\n isCorrectJSON,\r\n isObject,\r\n isPrivateRangeUrlFound,\r\n optionsStringify,\r\n printLogo,\r\n printUsage,\r\n roundNumber,\r\n toBoolean,\r\n wrapAround,\r\n measureTime\r\n};\r\n","/*******************************************************************************\r\n\r\nHighcharts Export Server\r\n\r\nCopyright (c) 2016-2023, Highsoft\r\n\r\nLicenced under the MIT licence.\r\n\r\nAdditionally a valid Highcharts license is required for use.\r\n\r\nSee LICENSE file in root for details.\r\n\r\n*******************************************************************************/\r\n\r\nimport rateLimit from 'express-rate-limit';\r\n\r\nimport { clearText } from '../utils.js';\r\nimport { log } from '../logger.js';\r\n\r\n/**\r\n * Enables rate limiting for a given app.\r\n *\r\n * @param {object} app - The express app.\r\n * @param {object} limitConfig - The options for the rate limiting.\r\n */\r\nexport default (app, limitConfig) => {\r\n const msg =\r\n 'Too many requests, you have been rate limited. Please try again later.';\r\n\r\n // Options for the rate limiter\r\n const rateOptions = {\r\n max: limitConfig.maxRequests || 30,\r\n window: limitConfig.window || 1,\r\n delay: limitConfig.delay || 0,\r\n trustProxy: limitConfig.trustProxy || false,\r\n skipKey: limitConfig.skipKey || false,\r\n skipToken: limitConfig.skipToken || false\r\n };\r\n\r\n // Set if behind a proxy\r\n if (rateOptions.trustProxy) {\r\n app.enable('trust proxy');\r\n }\r\n\r\n // Create a limiter\r\n const limiter = rateLimit({\r\n windowMs: rateOptions.window * 60 * 1000,\r\n // Limit each IP to 100 requests per windowMs\r\n max: rateOptions.max,\r\n // Disable delaying, full speed until the max limit is reached\r\n delayMs: rateOptions.delay,\r\n handler: (request, response) => {\r\n response.format({\r\n json: () => {\r\n response.status(429).send({ message: msg });\r\n },\r\n default: () => {\r\n response.status(429).send(msg);\r\n }\r\n });\r\n },\r\n skip: (request) => {\r\n // Allow bypassing the limiter if a valid key/token has been sent\r\n if (\r\n rateOptions.skipKey !== false &&\r\n rateOptions.skipToken !== false &&\r\n request.query.key === rateOptions.skipKey &&\r\n request.query.access_token === rateOptions.skipToken\r\n ) {\r\n log(4, '[rate-limiting] Skipping rate limiter.');\r\n return true;\r\n }\r\n return false;\r\n }\r\n });\r\n\r\n // Use a limiter as a middleware\r\n app.use(limiter);\r\n\r\n log(\r\n 3,\r\n clearText(\r\n `[rate-limiting] Enabled rate limiting: ${rateOptions.max} requests\r\n per ${rateOptions.window} minute per IP, trusting proxy:\r\n ${rateOptions.trustProxy}.`\r\n )\r\n );\r\n};\r\n","/**\r\n * This module exports two functions: fetch (for GET requests) and post (for POST requests).\r\n */\r\n\r\nimport http from 'http';\r\nimport https from 'https';\r\n\r\n/**\r\n * Determines the protocol of the given URL (either `http` or `https`).\r\n *\r\n * @function\r\n * @param {string} url - The URL whose protocol needs to be determined.\r\n * @returns {Object} Returns the `https` module if the URL starts with 'https',\r\n * otherwise returns the `http` module.\r\n * @private\r\n *\r\n * @example\r\n *\r\n * const protocol = getProtocol('https://example.com');\r\n * console.log(protocol); // Outputs the 'https' module\r\n */\r\nconst getProtocol = (url) => {\r\n return url.startsWith('https') ? https : http;\r\n};\r\n\r\n/**\r\n * Sends a GET request to the specified URL with optional request options.\r\n *\r\n * @function\r\n * @async\r\n * @param {string} url - The URL to fetch.\r\n * @param {Object} [requestOptions={}] - Optional request options and headers.\r\n * @returns {Promise} Returns a promise that resolves with the response object.\r\n * The response object contains a `.text` property with the raw response data.\r\n * @throws {Error} Throws an error if the request fails or if no data is fetched from the URL.\r\n *\r\n * @example\r\n *\r\n * async function getData() {\r\n * try {\r\n * const response = await fetch('https://api.example.com/data');\r\n * console.log(response.text);\r\n * } catch (error) {\r\n * console.error('Error fetching data:', error);\r\n * }\r\n * }\r\n *\r\n * getData();\r\n */\r\nasync function fetch(url, requestOptions = {}) {\r\n return new Promise((resolve, reject) => {\r\n const protocol = getProtocol(url);\r\n\r\n protocol\r\n .get(url, requestOptions, (res) => {\r\n let data = '';\r\n\r\n // A chunk of data has been received.\r\n res.on('data', (chunk) => {\r\n data += chunk;\r\n });\r\n\r\n // The whole response has been received.\r\n res.on('end', () => {\r\n if (!data) {\r\n reject('Nothing was fetched from the URL.');\r\n }\r\n\r\n res.text = data;\r\n resolve(res);\r\n });\r\n })\r\n .on('error', (error) => {\r\n reject(error);\r\n });\r\n });\r\n}\r\n\r\n/**\r\n * Sends a POST request to the specified URL with the given body and request options.\r\n *\r\n * @function\r\n * @async\r\n * @param {string} url - The URL to which the request should be sent.\r\n * @param {Object} [body={}] - The data to be sent as the request body, in JSON format.\r\n * @param {Object} [requestOptions={}] - Optional request options and headers.\r\n * @returns {Promise} - Returns a promise that resolves with the parsed JSON response.\r\n * @throws {Error} Throws an error if the request fails or if the response cannot be parsed.\r\n *\r\n * @example\r\n *\r\n * async function sendData() {\r\n * const dataToSend = {\r\n * key1: 'value1',\r\n * key2: 'value2',\r\n * };\r\n * try {\r\n * const response = await post('https://api.example.com/data', dataToSend);\r\n * console.log(response);\r\n * } catch (error) {\r\n * console.error('Error sending data:', error);\r\n * }\r\n * }\r\n *\r\n * sendData();\r\n */\r\nasync function post(url, body = {}, requestOptions = {}) {\r\n return new Promise((resolve, reject) => {\r\n const protocol = getProtocol(url);\r\n const data = JSON.stringify(body);\r\n\r\n // Set default headers and merge with requestOptions\r\n const options = Object.assign(\r\n {\r\n method: 'POST',\r\n headers: {\r\n 'Content-Type': 'application/json',\r\n 'Content-Length': data.length\r\n }\r\n },\r\n requestOptions\r\n );\r\n\r\n const req = protocol\r\n .request(url, options, (res) => {\r\n let responseData = '';\r\n\r\n // A chunk of data has been received.\r\n res.on('data', (chunk) => {\r\n responseData += chunk;\r\n });\r\n\r\n // The whole response has been received.\r\n res.on('end', () => {\r\n try {\r\n res.text = responseData;\r\n resolve(res);\r\n } catch (error) {\r\n reject(error);\r\n }\r\n });\r\n })\r\n .on('error', (error) => {\r\n reject(error);\r\n });\r\n\r\n // Write the request body and end the request.\r\n req.write(data);\r\n req.end();\r\n });\r\n}\r\n\r\nexport default fetch;\r\nexport { fetch, post };\r\n","/*******************************************************************************\r\n\r\nHighcharts Export Server\r\n\r\nCopyright (c) 2016-2023, Highsoft\r\n\r\nLicenced under the MIT licence.\r\n\r\nAdditionally a valid Highcharts license is required for use.\r\n\r\nSee LICENSE file in root for details.\r\n\r\n*******************************************************************************/\r\n\r\n// The cache manager manages the Highcharts library and its dependencies.\r\n// The cache itself is stored in .cache, and is checked by the config system\r\n// before starting the service\r\n\r\nimport { existsSync, mkdirSync, readFileSync, writeFileSync } from 'fs';\r\nimport { join } from 'path';\r\n\r\nimport dotenv from 'dotenv';\r\nimport HttpsProxyAgent from 'https-proxy-agent';\r\nimport { fetch } from './fetch.js';\r\n\r\nimport { log } from './logger.js';\r\nimport { __dirname } from '../lib/utils.js';\r\n\r\ndotenv.config();\r\n\r\nconst cachePath = join(__dirname, '.cache');\r\n\r\nconst cache = {\r\n cdnURL: 'https://code.highcharts.com/',\r\n activeManifest: {},\r\n sources: '',\r\n hcVersion: ''\r\n};\r\n\r\n// TODO: The config should be accesssible globally so we don't have to do this sort of thing..\r\nlet appliedConfig = false;\r\n\r\n/**\r\n * Extracts the Highcharts version from the cache\r\n */\r\nconst extractVersion = () =>\r\n (cache.hcVersion = cache.sources\r\n .substr(0, cache.sources.indexOf('*/'))\r\n .replace('/*', '')\r\n .replace('*/', '')\r\n .replace(/\\n/g, '')\r\n .trim());\r\n\r\n/**\r\n * Saves the Highcharts part of a config to a manifest file in the cache\r\n *\r\n * @param {object} config - Highcharts related configuration object.\r\n * @param {object} fetchedModules - An object that contains mapped names of\r\n * fetched Highcharts modules to use.\r\n */\r\nconst saveConfigToManifest = async (config, fetchedModules) => {\r\n const newManifest = {\r\n version: config.version,\r\n modules: fetchedModules || {}\r\n };\r\n\r\n // Update cache object with the current modules\r\n cache.activeManifest = newManifest;\r\n\r\n log(4, '[cache] writing new manifest');\r\n\r\n try {\r\n writeFileSync(\r\n join(cachePath, 'manifest.json'),\r\n JSON.stringify(newManifest),\r\n 'utf8'\r\n );\r\n } catch (error) {\r\n log(1, `[cache] Error writing cache manifest: ${error}.`);\r\n }\r\n};\r\n\r\n/**\r\n * Fetches a single script.\r\n *\r\n * @param {string} script - A path to script to get.\r\n * @param {object} proxyAgent - The proxy agent to use for a request.\r\n */\r\nconst fetchScript = async (script, proxyAgent) => {\r\n try {\r\n // Get rid of the .js from the custom strings\r\n if (script.endsWith('.js')) {\r\n script = script.substring(0, script.length - 3);\r\n }\r\n\r\n log(4, `[cache] Fetching script - ${script}.js`);\r\n\r\n // If exists, add proxy agent to request options\r\n const requestOptions = proxyAgent\r\n ? {\r\n agent: proxyAgent,\r\n timeout: +process.env['PROXY_SERVER_TIMEOUT'] || 5000\r\n }\r\n : {};\r\n\r\n // Fetch the script\r\n const response = await fetch(`${script}.js`, requestOptions);\r\n\r\n // If OK, return its text representation\r\n if (response.statusCode === 200) {\r\n return response.text;\r\n }\r\n\r\n throw `${response.statusCode}`;\r\n } catch (error) {\r\n log(1, `[cache] Error fetching script ${script}.js: ${error}.`);\r\n throw error;\r\n }\r\n};\r\n\r\n/**\r\n * Updates the Highcharts cache.\r\n *\r\n * @param {object} config - Highcharts related configuration object.\r\n * @param {string} sourcePath - A path to the file where save updated sources.\r\n * @return {object} An object that contains mapped names of fetched Highcharts\r\n * modules to use.\r\n */\r\nconst updateCache = async (config, sourcePath) => {\r\n const { coreScripts, modules, indicators, scripts: customScripts } = config;\r\n const hcVersion =\r\n config.version === 'latest' || !config.version ? '' : `${config.version}/`;\r\n\r\n log(3, '[cache] Updating cache to Highcharts ', hcVersion);\r\n\r\n // Gather all scripts to fetch\r\n const allScripts = [\r\n ...coreScripts.map((c) => `${hcVersion}${c}`),\r\n ...modules.map((m) =>\r\n m === 'map' ? `maps/${hcVersion}modules/${m}` : `${hcVersion}modules/${m}`\r\n ),\r\n ...indicators.map((i) => `stock/${hcVersion}indicators/${i}`)\r\n ];\r\n\r\n // Configure proxy if exists\r\n let proxyAgent;\r\n const proxyHost = process.env['PROXY_SERVER_HOST'];\r\n const proxyPort = process.env['PROXY_SERVER_PORT'];\r\n\r\n if (proxyHost && proxyPort) {\r\n proxyAgent = new HttpsProxyAgent({\r\n host: proxyHost,\r\n port: +proxyPort\r\n });\r\n }\r\n\r\n const fetchedModules = {};\r\n try {\r\n cache.sources = // TODO: convert to for loop\r\n (\r\n await Promise.all([\r\n ...allScripts.map(async (script) => {\r\n const text = await fetchScript(\r\n `${config.cdnURL || cache.cdnURL}${script}`,\r\n proxyAgent\r\n );\r\n\r\n // If fetched correctly, set it\r\n if (typeof text === 'string') {\r\n fetchedModules[\r\n script.replace(\r\n /(.*)\\/|(.*)modules\\/|stock\\/(.*)indicators\\/|maps\\/(.*)modules\\//gi,\r\n ''\r\n )\r\n ] = 1;\r\n }\r\n\r\n return text;\r\n }),\r\n ...customScripts.map((script) => fetchScript(script, proxyAgent))\r\n ])\r\n ).join(';\\n');\r\n extractVersion();\r\n\r\n // Save the fetched modules into caches' source JSON\r\n writeFileSync(sourcePath, cache.sources);\r\n return fetchedModules;\r\n } catch (error) {\r\n log(1, '[cache] Unable to update local Highcharts cache.');\r\n }\r\n};\r\n\r\nexport const updateVersion = async (newVersion) =>\r\n appliedConfig\r\n ? await checkCache(\r\n Object.assign(appliedConfig, {\r\n version: newVersion\r\n })\r\n )\r\n : false;\r\n\r\n/**\r\n * Fetches any missing Highcharts and dependencies\r\n *\r\n * @param {object} config - Highcharts related configuration object.\r\n */\r\nexport const checkCache = async (config) => {\r\n let fetchedModules;\r\n // Prepare paths to manifest and sources from the .cache folder\r\n const manifestPath = join(cachePath, 'manifest.json');\r\n const sourcePath = join(cachePath, 'sources.js');\r\n\r\n // TODO: deal with trying to switch to the running version\r\n // const activeVersion = appliedConfig ? appliedConfig.version : false;\r\n\r\n appliedConfig = config;\r\n\r\n // Create the .cache destination if it doesn't exist already\r\n !existsSync(cachePath) && mkdirSync(cachePath);\r\n\r\n // Fetch all the scripts either if manifest.json does not exist\r\n // or if the forceFetch option is enabled\r\n if (!existsSync(manifestPath) || config.forceFetch) {\r\n log(3, '[cache] Fetching and caching Highcharts dependencies.');\r\n fetchedModules = await updateCache(config, sourcePath);\r\n } else {\r\n let requestUpdate = false;\r\n\r\n // Read the manifest JSON\r\n const manifest = JSON.parse(readFileSync(manifestPath));\r\n\r\n // Check if the modules is an array, if so, we rewrite it to a map to make\r\n // it easier to resolve modules.\r\n if (manifest.modules && Array.isArray(manifest.modules)) {\r\n const moduleMap = {};\r\n manifest.modules.forEach((m) => (moduleMap[m] = 1));\r\n manifest.modules = moduleMap;\r\n }\r\n\r\n const { modules, coreScripts, indicators } = config;\r\n const numberOfModules =\r\n modules.length + coreScripts.length + indicators.length;\r\n\r\n // Compare the loaded config with the contents in .cache.\r\n // If there are changes, fetch requested modules and products,\r\n // and bake them into a giant blob. Save the blob.\r\n if (manifest.version !== config.version) {\r\n log(3, '[cache] Highcharts version mismatch in cache, need to re-fetch.');\r\n requestUpdate = true;\r\n } else if (Object.keys(manifest.modules || {}).length !== numberOfModules) {\r\n log(\r\n 3,\r\n '[cache] Cache and requested modules does not match, need to re-fetch.'\r\n );\r\n requestUpdate = true;\r\n } else {\r\n // Check each module, if anything is missing refetch everything\r\n requestUpdate = (config.modules || []).some((moduleName) => {\r\n if (!manifest.modules[moduleName]) {\r\n log(\r\n 3,\r\n `[cache] The ${moduleName} missing in cache, need to re-fetch.`\r\n );\r\n return true;\r\n }\r\n });\r\n }\r\n\r\n if (requestUpdate) {\r\n fetchedModules = await updateCache(config, sourcePath);\r\n } else {\r\n log(3, '[cache] Dependency cache is up to date, proceeding.');\r\n\r\n // Load the sources\r\n cache.sources = readFileSync(sourcePath, 'utf8');\r\n\r\n // Get current modules map\r\n fetchedModules = manifest.modules;\r\n extractVersion();\r\n }\r\n }\r\n\r\n // Finally, save the new manifest, which is basically our current config\r\n // in a slightly different format\r\n await saveConfigToManifest(config, fetchedModules);\r\n};\r\n\r\nexport default {\r\n checkCache,\r\n updateVersion,\r\n getCache: () => cache,\r\n highcharts: () => cache.sources,\r\n version: () => cache.hcVersion\r\n};\r\n","/*******************************************************************************\r\n\r\nHighcharts Export Server\r\n\r\nCopyright (c) 2016-2023, Highsoft\r\n\r\nLicenced under the MIT licence.\r\n\r\nAdditionally a valid Highcharts license is required for use.\r\n\r\nSee LICENSE file in root for details.\r\n\r\n*******************************************************************************/\r\n\r\nimport puppeteer from 'puppeteer';\r\nimport fs from 'fs';\r\nimport * as url from 'url';\r\nimport { log } from './logger.js';\r\nimport path from 'node:path';\r\n\r\n// Workaround for https://bugs.chromium.org/p/chromium/issues/detail?id=1463328\r\n// Not ideal - leaves trash in the FS\r\nimport { randomBytes } from 'node:crypto';\r\n\r\nconst RANDOM_PID = randomBytes(64).toString('base64url');\r\nconst PUPPETEER_DIR = path.join('tmp', `puppeteer-${RANDOM_PID}`);\r\nconst DATA_DIR = path.join(PUPPETEER_DIR, 'profile');\r\n\r\n// The minimal args to speed up the browser\r\nconst minimalArgs = [\r\n `--user-data-dir=${DATA_DIR}`,\r\n '--autoplay-policy=user-gesture-required',\r\n '--disable-background-networking',\r\n '--disable-background-timer-throttling',\r\n '--disable-backgrounding-occluded-windows',\r\n '--disable-breakpad',\r\n '--disable-client-side-phishing-detection',\r\n '--disable-component-update',\r\n '--disable-default-apps',\r\n '--disable-dev-shm-usage',\r\n '--disable-domain-reliability',\r\n '--disable-extensions',\r\n '--disable-features=AudioServiceOutOfProcess',\r\n '--disable-hang-monitor',\r\n '--disable-ipc-flooding-protection',\r\n '--disable-notifications',\r\n '--disable-offer-store-unmasked-wallet-cards',\r\n '--disable-popup-blocking',\r\n '--disable-print-preview',\r\n '--disable-prompt-on-repost',\r\n '--disable-renderer-backgrounding',\r\n '--disable-session-crashed-bubble',\r\n '--disable-setuid-sandbox',\r\n '--disable-speech-api',\r\n '--disable-sync',\r\n '--hide-crash-restore-bubble',\r\n '--hide-scrollbars',\r\n '--ignore-gpu-blacklist',\r\n '--metrics-recording-only',\r\n '--mute-audio',\r\n '--no-default-browser-check',\r\n '--no-first-run',\r\n '--no-pings',\r\n '--no-sandbox',\r\n '--no-zygote',\r\n '--password-store=basic',\r\n '--use-mock-keychain'\r\n];\r\n\r\nconst __dirname = url.fileURLToPath(new URL('.', import.meta.url));\r\n\r\nconst template = fs.readFileSync(\r\n __dirname + '/../templates/template.html',\r\n 'utf8'\r\n);\r\n\r\nlet browser;\r\n\r\nconst setPageContent = async (page) => {\r\n await page.setContent(template);\r\n await page.addScriptTag({ path: __dirname + '/../.cache/sources.js' });\r\n // eslint-disable-next-line no-undef\r\n await page.evaluate(() => window.setupHighcharts());\r\n\r\n page.on('pageerror', async (err) => {\r\n // TODO: Consider adding a switch here that turns on log(0) logging\r\n // on page errors.\r\n log(1, '[page error]', err);\r\n await page.$eval(\r\n '#container',\r\n (element, errorMessage) => {\r\n // eslint-disable-next-line no-undef\r\n if (window._displayErrors) {\r\n element.innerHTML = errorMessage;\r\n }\r\n },\r\n `

Chart input data error

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

Chart input data error

${err.toString()}` ); }); +}; + +export const newPage = async () => { + if (!browser) return false; - return p; + const page = await browser.newPage(); + + // Set the content + await setPageContent(page); + return page; +}; + +export const clearPage = async (page) => { + try { + // Navigate to about:blank + await page.goto('about:blank'); + + // Set the content and and scripts again + await setPageContent(page); + } catch (error) { + log(3, '[browser] Could not clear page'); + } }; export const create = async (puppeteerArgs) => { @@ -169,7 +186,8 @@ export const close = async () => { }; export default { + newPage, + clearPage, get, - close, - newPage + close }; diff --git a/lib/export.js b/lib/export.js index 28fe5d57..b34c44ba 100644 --- a/lib/export.js +++ b/lib/export.js @@ -52,9 +52,10 @@ const getClipRegion = (page) => * @param {string} type - The type of a result image. * @param {string} encoding - The type of encoding used. * @param {string} clip - The clip region. + * @param {number} rasterizationTimeout - The rasterization timeout in milliseconds. * @returns {string} - A string representation of a screenshot. */ -const createImage = async (page, type, encoding, clip) => +const createImage = async (page, type, encoding, clip, rasterizationTimeout) => await Promise.race([ page.screenshot({ type, @@ -66,7 +67,10 @@ const createImage = async (page, type, encoding, clip) => omitBackground: type == 'png' }), new Promise((resolve, reject) => - setTimeout(() => reject(new Error('Rasterization timeout')), 1500) + setTimeout( + () => reject(new Error('Rasterization timeout')), + rasterizationTimeout || 1500 + ) ) ]); @@ -399,12 +403,18 @@ export default async (page, chart, options) => { data = await createSVG(page); } else if (exportOptions.type === 'png' || exportOptions.type === 'jpeg') { // PNG or JPEG - data = await createImage(page, exportOptions.type, 'base64', { - width: viewportWidth, - height: viewportHeight, - x, - y - }); + data = await createImage( + page, + exportOptions.type, + 'base64', + { + width: viewportWidth, + height: viewportHeight, + x, + y + }, + options.pool.rasterizationTimeout + ); } else if (exportOptions.type === 'pdf') { // PDF data = await createPDF(page, viewportHeight, viewportWidth, 'base64'); diff --git a/lib/index.js b/lib/index.js index e07b3da5..99d652e4 100644 --- a/lib/index.js +++ b/lib/index.js @@ -60,7 +60,7 @@ export default { // Init the pool await init({ pool: options.pool || { - initialWorkers: 1, + minWorkers: 1, maxWorkers: 1 }, puppeteerArgs: options.puppeteer?.args || [] diff --git a/lib/pool.js b/lib/pool.js index 36997409..db1332ba 100644 --- a/lib/pool.js +++ b/lib/pool.js @@ -16,8 +16,9 @@ import { v4 as uuid } from 'uuid'; import { Pool } from 'tarn'; import { close, + create as createBrowser, newPage as browserNewPage, - create as createBrowser + clearPage } from './browser.js'; import { log } from './logger.js'; @@ -53,7 +54,7 @@ const factory = { page = await browserNewPage(); if (!page || page.isClosed()) { - throw 'invalid page'; + throw '[pool] Invalid page'; } log( @@ -86,7 +87,7 @@ const factory = { * * @return {boolean} - Bool that indicates if a resource is valid or not. */ - validate: (workerHandle) => { + validate: async (workerHandle) => { if ( poolConfig.workLimit && ++workerHandle.workCount > poolConfig.workLimit @@ -98,6 +99,9 @@ const factory = { ); return false; } + + // Clear page + await clearPage(workerHandle.page); return true; }, @@ -142,7 +146,7 @@ export const init = async (config) => { log( 3, '[pool] Initializing pool:', - `min ${poolConfig.initialWorkers}, max ${poolConfig.maxWorkers}.` + `min ${poolConfig.minWorkers}, max ${poolConfig.maxWorkers}.` ); if (pool) { @@ -162,14 +166,14 @@ export const init = async (config) => { pool = new Pool({ // Get the create/validate/destroy/log functions ...factory, - min: poolConfig.initialWorkers, + min: poolConfig.minWorkers, max: poolConfig.maxWorkers, - createRetryIntervalMillis: 200, - createTimeoutMillis: poolConfig.acquireTimeout, acquireTimeoutMillis: poolConfig.acquireTimeout, - destroyTimeoutMillis: poolConfig.acquireTimeout, - idleTimeoutMillis: poolConfig.timeoutThreshold, - reapIntervalMillis: 1000, // poolConfig.reaper ? 120000 : 0, for now + createTimeoutMillis: poolConfig.createTimeout, + destroyTimeoutMillis: poolConfig.destroyTimeout, + idleTimeoutMillis: poolConfig.idleTimeout, + createRetryIntervalMillis: poolConfig.createRetryInterval, + reapIntervalMillis: poolConfig.reaperInterval, propagateCreateError: false }); @@ -208,8 +212,13 @@ export const init = async (config) => { const initialResources = []; // Create an initial number of resources - for (let i = 0; i < poolConfig.initialWorkers; i++) { - initialResources.push(await pool.acquire().promise); + for (let i = 0; i < poolConfig.minWorkers; i++) { + try { + const resource = await pool.acquire().promise; + initialResources.push(resource); + } catch (error) { + log(1, `[pool] Couldn't create an initial resource ${error}`); + } } // Release the initial number of resources back to the pool @@ -219,7 +228,7 @@ export const init = async (config) => { log( 3, - `[pool] The pool is ready with ${poolConfig.initialWorkers} initial resources waiting.` + `[pool] The pool is ready with ${poolConfig.minWorkers} initial resources waiting.` ); } catch (error) { log(1, `[pool] Couldn't create the worker pool ${error}`); diff --git a/lib/schemas/config.js b/lib/schemas/config.js index 4380b8f5..a47809a9 100644 --- a/lib/schemas/config.js +++ b/lib/schemas/config.js @@ -374,7 +374,7 @@ export const defaultConfig = { } }, pool: { - initialWorkers: { + minWorkers: { envLink: 'HIGHCHARTS_POOL_MIN_WORKERS', value: 4, type: 'number', @@ -393,31 +393,52 @@ export const defaultConfig = { description: 'The pieces of work that can be performed before restarting process.' }, - queueSize: { - envLink: 'HIGHCHARTS_POOL_QUEUE_SIZE', - value: 5, + acquireTimeout: { + envLink: 'HIGHCHARTS_POOL_ACQUIRE_TIMEOUT', + value: 5000, type: 'number', - description: 'The size of the request overflow queue.' + description: + 'The number of milliseconds to wait for acquiring a resource.' }, - timeoutThreshold: { - envLink: 'HIGHCHARTS_POOL_TIMEOUT', + createTimeout: { + envLink: 'HIGHCHARTS_POOL_CREATE_TIMEOUT', value: 5000, type: 'number', - description: 'The number of milliseconds before timing out.' + description: 'The number of milliseconds to wait for creating a resource.' }, - acquireTimeout: { - envLink: 'HIGHCHARTS_POOL_ACQUIRE_TIMEOUT', + destroyTimeout: { + envLink: 'HIGHCHARTS_POOL_DESTROY_TIMEOUT', value: 5000, type: 'number', description: - 'The number of milliseconds to wait for acquiring a resource.' + 'The number of milliseconds to wait for destroying a resource.' }, - reaper: { - envLink: 'HIGHCHARTS_POOL_ENABLE_REAPER', - value: true, - type: 'boolean', + idleTimeout: { + envLink: 'HIGHCHARTS_POOL_IDLE_TIMEOUT', + value: 30000, + type: 'number', + description: + 'The number of milliseconds after an idle resource is destroyed.' + }, + rasterizationTimeout: { + envLink: 'HIGHCHARTS_POOL_RASTERIZATION_TIMEOUT', + value: 1500, + type: 'number', + description: 'The number of milliseconds to wait for rendering a webpage.' + }, + createRetryInterval: { + envLink: 'HIGHCHARTS_POOL_CREATE_RETRY_INTERVAL', + value: 200, + type: 'number', + description: + 'The number of milliseconds after the create process is retried in case of fail.' + }, + reaperInterval: { + envLink: 'HIGHCHARTS_POOL_REAPER_INTERVAL', + value: 1000, + type: 'number', description: - 'Whether or not to evict workers after a certain time period.' + 'The number of milliseconds after the check for idle resources to destroy is triggered.' }, benchmarking: { envLink: 'HIGHCHARTS_POOL_BENCHMARKING', @@ -680,9 +701,9 @@ export const promptsConfig = { pool: [ { type: 'number', - name: 'initialWorkers', + name: 'minWorkers', message: 'The number of initial workers to spawn', - initial: defaultConfig.pool.initialWorkers.value + initial: defaultConfig.pool.minWorkers.value }, { type: 'number', @@ -699,27 +720,47 @@ export const promptsConfig = { }, { type: 'number', - name: 'queueSize', - message: 'The size of the request overflow queue', - initial: defaultConfig.pool.queueSize.value + name: 'acquireTimeout', + message: 'The number of milliseconds to wait for acquiring a resource', + initial: defaultConfig.pool.acquireTimeout.value }, { type: 'number', - name: 'timeoutThreshold', - message: 'The number of seconds before timing out', - initial: defaultConfig.pool.timeoutThreshold.value + name: 'createTimeout', + message: 'The number of milliseconds to wait for creating a resource', + initial: defaultConfig.pool.createTimeout.value }, { type: 'number', - name: 'acquireTimeout', - message: 'The number of milliseconds to wait for acquiring a resource', - initial: defaultConfig.pool.acquireTimeout.value + name: 'destroyTimeout', + message: 'The number of milliseconds to wait for destroying a resource', + initial: defaultConfig.pool.destroyTimeout.value }, { - type: 'toggle', - name: 'reaper', - message: 'The reaper to remove hanging processes', - initial: defaultConfig.pool.reaper.value + type: 'number', + name: 'idleTimeout', + message: 'The number of milliseconds after an idle resource is destroyed', + initial: defaultConfig.pool.idleTimeout.value + }, + { + type: 'number', + name: 'rasterizationTimeout', + message: 'The number of milliseconds to wait for rendering a webpage', + initial: defaultConfig.pool.rasterizationTimeout.value + }, + { + type: 'number', + name: 'createRetryInterval', + message: + 'The number of milliseconds after the create process is retried in case of fail', + initial: defaultConfig.pool.createRetryInterval.value + }, + { + type: 'number', + name: 'reaperInterval', + message: + 'The number of milliseconds after the check for idle resources to destroy is triggered', + initial: defaultConfig.pool.reaperInterval.value }, { type: 'toggle', diff --git a/lib/server/server.js b/lib/server/server.js index 57d84df2..1fe58b93 100644 --- a/lib/server/server.js +++ b/lib/server/server.js @@ -28,7 +28,7 @@ import { __dirname } from '../utils.js'; import healthRoute from './routes/health.js'; import exportRoutes from './routes/export.js'; -import vswitchRoute from './routes/change_hc_version.js'; +import vSwitchRoute from './routes/change_hc_version.js'; import uiRoute from './routes/ui.js'; // Create express app @@ -168,7 +168,7 @@ export const startServer = async (serverConfig) => { healthRoute(app); exportRoutes(app); uiRoute(app); - vswitchRoute(app); + vSwitchRoute(app); }; /** diff --git a/package-lock.json b/package-lock.json index 498385b5..83cac869 100644 --- a/package-lock.json +++ b/package-lock.json @@ -1,12 +1,12 @@ { "name": "highcharts-export-server", - "version": "3.0.3", + "version": "3.0.5", "lockfileVersion": 2, "requires": true, "packages": { "": { "name": "highcharts-export-server", - "version": "3.0.3", + "version": "3.0.5", "hasInstallScript": true, "license": "MIT", "dependencies": { diff --git a/samples/module/options_puppeteer.js b/samples/module/options_puppeteer.js index 996b5637..da11512d 100644 --- a/samples/module/options_puppeteer.js +++ b/samples/module/options_puppeteer.js @@ -5,7 +5,7 @@ import exporter from '../../lib/index.js'; // Export settings with new options structure (Puppeteer) const exportSettings = { pool: { - initialWorkers: 1, + minWorkers: 1, maxWorkers: 1 }, export: { diff --git a/tests/node/node_test_runner_single.js b/tests/node/node_test_runner_single.js index 0bff947c..83b24a58 100644 --- a/tests/node/node_test_runner_single.js +++ b/tests/node/node_test_runner_single.js @@ -100,7 +100,7 @@ const exportChart = () => { // Set options const options = exporter.setOptions({ pool: { - initialWorkers: 1, + minWorkers: 1, maxWorkers: 1 }, logging: { From 82aa7bb97eae299f8f738a46c8036c2f09af60f6 Mon Sep 17 00:00:00 2001 From: jakubSzuminski Date: Tue, 16 Jan 2024 11:02:08 +0100 Subject: [PATCH 2/8] Fixed envLink bug with Highcharts and Puppeteer. --- lib/config.js | 59 +++++++++++++++++++++++---------------------------- 1 file changed, 27 insertions(+), 32 deletions(-) diff --git a/lib/config.js b/lib/config.js index 24dd5df9..41b34315 100644 --- a/lib/config.js +++ b/lib/config.js @@ -284,39 +284,34 @@ function loadConfigFile(args) { */ function updateDefaultConfig(configObj, customObj = {}, propChain = '') { Object.keys(configObj).forEach((key) => { - if (!['puppeteer', 'highcharts'].includes(key)) { - const entry = configObj[key]; - const customValue = customObj && customObj[key]; - let numEnvVal; - - if (typeof entry.value === 'undefined') { - updateDefaultConfig(entry, customValue, `${propChain}.${key}`); - } else { - // If a value from a custom JSON exists, it take precedence - if (customValue !== undefined) { - entry.value = customValue; - } + const entry = configObj[key]; + const customValue = customObj && customObj[key]; + let numEnvVal; + + if (typeof entry.value === 'undefined') { + updateDefaultConfig(entry, customValue, `${propChain}.${key}`); + } else { + // If a value from a custom JSON exists, it take precedence + if (customValue !== undefined) { + entry.value = customValue; + } - // If a value from an env variable exists, it take precedence - if (entry.envLink) { - // Load the env var - if (entry.type === 'boolean') { - entry.value = toBoolean( - [process.env[entry.envLink], entry.value].find( - (el) => el || el === 'false' - ) - ); - } else if (entry.type === 'number') { - numEnvVal = +process.env[entry.envLink]; - entry.value = numEnvVal >= 0 ? numEnvVal : entry.value; - } else if ( - entry.type.indexOf(']') >= 0 && - process.env[entry.envLink] - ) { - entry.value = process.env[entry.envLink].split(','); - } else { - entry.value = process.env[entry.envLink] || entry.value; - } + // If a value from an env variable exists, it take precedence + if (entry.envLink) { + // Load the env var + if (entry.type === 'boolean') { + entry.value = toBoolean( + [process.env[entry.envLink], entry.value].find( + (el) => el || el === 'false' + ) + ); + } else if (entry.type === 'number') { + numEnvVal = +process.env[entry.envLink]; + entry.value = numEnvVal >= 0 ? numEnvVal : entry.value; + } else if (entry.type.indexOf(']') >= 0 && process.env[entry.envLink]) { + entry.value = process.env[entry.envLink].split(','); + } else { + entry.value = process.env[entry.envLink] || entry.value; } } } From d917f714caf4ef3da42a3834920abd52f50088b6 Mon Sep 17 00:00:00 2001 From: PawelDalek Date: Tue, 16 Jan 2024 19:45:06 +0100 Subject: [PATCH 3/8] Update README.md --- README.md | 180 +++++++++++++++++++++++++++++++++++++++++++++--------- 1 file changed, 151 insertions(+), 29 deletions(-) diff --git a/README.md b/README.md index 4b9441f2..006f777c 100644 --- a/README.md +++ b/README.md @@ -113,6 +113,7 @@ The format, with its default values are as follows (using the below ordering of "highcharts": { "version": "latest", "cdnURL": "https://code.highcharts.com/", + "forceFetch": false, "coreScripts": [ "highcharts", "highcharts-more", @@ -137,6 +138,7 @@ The format, with its default values are as follows (using the below ordering of "tilemap", "timeline", "treemap", + "treegraph", "item-series", "drilldown", "histogram-bellcurve", @@ -182,8 +184,7 @@ The format, with its default values are as follows (using the below ordering of ], "scripts": [ "https://cdnjs.cloudflare.com/ajax/libs/moment.js/2.29.4/moment.min.js" - ], - "forceFetch": false + ] }, "export": { "infile": false, @@ -252,14 +253,150 @@ The format, with its default values are as follows (using the below ordering of } ``` -_Library fetches_ +## Loading Custom JSON Config + +Loading an additional JSON configuration file can be done by using the `--loadConfig ` option. Such a JSON can be created manually or through a prompt called by the `--createConfig` option. + +## Environment Variables + +These are set as variables in your environment. They take precedence over options from the `lib/schemas/config.js` file. On Linux, use e.g. `export`. + +### Export config +- `EXPORT_DEFAULT_TYPE`: The format of the file to export to. Can be jpeg, png, pdf or svg. +- `EXPORT_DEFAULT_CONSTR`: The constructor to use. Can be chart, stockChart, mapChart or ganttChart. +- `EXPORT_DEFAULT_HEIGHT`: The height of the exported chart. Overrides the option in the chart settings. +- `EXPORT_DEFAULT_WIDTH`: The width of the exported chart. Overrides the option in the chart settings. +- `EXPORT_DEFAULT_SCALE`: The scale of the exported chart. Ranges between 0.1 and 5.0. + +### Highcharts config +- `HIGHCHARTS_VERSION`: Highcharts version to use. +- `HIGHCHARTS_CDN`: The CDN URL of Highcharts scripts to use. +- `HIGHCHARTS_FORCE_FETCH`: Should refetch all the scripts after each server rerun. +- `HIGHCHARTS_CORE_SCRIPTS`: Highcharts core scripts to fetch. +- `HIGHCHARTS_MODULES`: Highcharts modules to fetch. +- `HIGHCHARTS_INDICATORS`: Highcharts indicators to fetch. + +### Custom code config +- `HIGHCHARTS_ALLOW_CODE_EXECUTION`: If set to true, allow for the execution of arbitrary code when exporting. +- `HIGHCHARTS_ALLOW_FILE_RESOURCES`: Allow injecting resources from the filesystem. Has no effect when running as a server. + +### Server config +- `HIGHCHARTS_SERVER_ENABLE`: If set to true, starts a server on 0.0.0.0. +- `HIGHCHARTS_SERVER_HOST`: The hostname of the server. Also starts a server listening on the supplied hostname. +- `HIGHCHARTS_SERVER_PORT`: The port to use for the server. Defaults to 7801. + +### Server SSL config +- `HIGHCHARTS_SERVER_SSL_ENABLE`: Enables the SSL protocol. +- `HIGHCHARTS_SERVER_SSL_FORCE`: If set to true, forces the server to only serve over HTTPS. +- `HIGHCHARTS_SERVER_SSL_PORT`: The port on which to run the SSL server. +- `HIGHCHARTS_SERVER_SSL_CERT_PATH`: The path to the SSL certificate/key. + +### Server rate limiting config +- `HIGHCHARTS_RATE_LIMIT_ENABLE`: Enables rate limiting. +- `HIGHCHARTS_RATE_LIMIT_MAX`: Max requests allowed in a one minute. +- `HIGHCHARTS_RATE_LIMIT_WINDOW`: The time window in minutes for rate limiting. +- `HIGHCHARTS_RATE_LIMIT_DELAY`: The amount to delay each successive request before hitting the max. +- `HIGHCHARTS_RATE_LIMIT_TRUST_PROXY`: Set this to true if behind a load balancer. +- `HIGHCHARTS_RATE_LIMIT_SKIP_KEY`: Allows bypassing the rate limiter and should be provided with skipToken argument. +- `HIGHCHARTS_RATE_LIMIT_SKIP_TOKEN`: Allows bypassing the rate limiter and should be provided with skipKey argument. + +### Pool config +- `HIGHCHARTS_POOL_MIN_WORKERS`: The number of initial workers to spawn. +- `HIGHCHARTS_POOL_MAX_WORKERS`: The number of max workers to spawn. +- `HIGHCHARTS_POOL_WORK_LIMIT`: The pieces of work that can be performed before restarting process. +- `HIGHCHARTS_POOL_ACQUIRE_TIMEOUT`: The number of milliseconds to wait for acquiring a resource. +- `HIGHCHARTS_POOL_CREATE_TIMEOUT`: The number of milliseconds to wait for creating a resource. +- `HIGHCHARTS_POOL_DESTROY_TIMEOUT`: The number of milliseconds to wait for destroying a resource. +- `HIGHCHARTS_POOL_IDLE_TIMEOUT`: The number of milliseconds after an idle resource is destroyed. +- `HIGHCHARTS_POOL_RASTERIZATION_TIMEOUT`: The number of milliseconds to wait for rendering a webpage. +- `HIGHCHARTS_POOL_CREATE_RETRY_INTERVAL`: The number of milliseconds after the create process is retried in case of fail. +- `HIGHCHARTS_POOL_REAPER_INTERVAL`: The number of milliseconds after the check for idle resources to destroy is triggered. +- `HIGHCHARTS_POOL_BENCHMARKING`: Enable benchmarking. +- `HIGHCHARTS_POOL_LISTEN_TO_PROCESS_EXITS`: Set to false in order to skip attaching process.exit handlers. + +### Logging config +- `HIGHCHARTS_LOG_LEVEL`: The log level (0: silent, 1: error, 2: warning, 3: notice, 4: verbose). +- `HIGHCHARTS_LOG_FILE`: A name of a log file. The --logDest also needs to be set to enable file logging. +- `HIGHCHARTS_LOG_DEST`: The path to store log files. Also enables file logging. + +### UI config +- `HIGHCHARTS_UI_ENABLE`: Enables the UI for the export server. +- `HIGHCHARTS_UI_ROUTE`: The route to attach the UI to. + +### Other config +- `HIGHCHARTS_NO_LOGO`: Skip printing the logo on a startup. Will be replaced by a simple text. + +### Proxy config +- `PROXY_SERVER_HOST`: The host of the proxy server to use if exists. +- `PROXY_SERVER_PORT`: The port of the proxy server to use if exists. +- `PROXY_SERVER_TIMEOUT`: The timeout for the proxy server to use if exists. + +## Command Line Arguments + +To supply command line arguments, add them as flags when running the application: +`highcharts-export-server --flag1 value --flag2 value ...` + +_Available options:_ + +- `--infile`: The input file name along with a type (json or svg). It can be a correct JSON or SVG file (defaults to `false`). +- `--instr`: An input in a form of a stringified JSON or SVG file. Overrides the --infile (defaults to `false`). +- `--options`: An alias for the --instr option (defaults to `false`). +- `--outfile`: The output filename along with a type (jpeg, png, pdf or svg). Ignores the --type flag (defaults to `false`). +- `--type`: The format of the file to export to. Can be jpeg, png, pdf or svg (defaults to `png`). +- `--constr`: The constructor to use. Can be chart, stockChart, mapChart or ganttChart (defaults to `chart`). +- `--height`: The height of the exported chart. Overrides the option in the chart settings (defaults to `600`). +- `--width`: The width of the exported chart. Overrides the option in the chart settings (defaults to `400`). +- `--scale`: The scale of the exported chart. Ranges between 0.1 and 5.0 (defaults to `1`). +- `--globalOptions`: A stringified JSON or a filename with options to be passed into the Highcharts.setOptions (defaults to `false`). +- `--themeOptions`: A stringified JSON or a filename with theme options to be passed into the Highcharts.setOptions (defaults to `false`). +- `--batch`: Starts a batch job. A string that contains input/output pairs: "in=out;in=out;.." (defaults to `false`). +- `--allowCodeExecution`: If set to true, allow for the execution of arbitrary code when exporting (defaults to `false`). +- `--allowFileResources`: Allow injecting resources from the filesystem. Has no effect when running as a server (defaults to `true`). +- `--customCode`: Custom code to be called before chart initialization. Can be a function, a code that will be wrapped within a function or a filename with the js extension (defaults to `false`). +- `--callback`: A JavaScript function to run on construction. Can be a function or a filename with the js extension (defaults to `false`). +- `--resources`: An additional resource in a form of stringified JSON. It can contains files, js and css sections (defaults to `false`). +- `--loadConfig`: A file that contains a pre-defined config to use (defaults to `false`). +- `--createConfig`: Allows to set options through a prompt and save in a provided config file (defaults to `false`). +- `--enableServer`: If set to true, starts a server on 0.0.0.0 (defaults to `false`). +- `--host`: The hostname of the server. Also starts a server listening on the supplied hostname (defaults to `0.0.0.0`). +- `--port`: The port to use for the server. Defaults to 7801 (defaults to `7801`). +- `--enableSsl`: Enables the SSL protocol (defaults to `false`). +- `--sslForced`: If set to true, forces the server to only serve over HTTPS (defaults to `false`). +- `--sslPort`: The port on which to run the SSL server (defaults to `443`). +- `--certPath`: The path to the SSL certificate/key (defaults to ``). +- `--enableRateLimiting`: Enables rate limiting (defaults to `false`). +- `--maxRequests`: Max requests allowed in a one minute (defaults to `10`). +- `--skipKey`: Allows bypassing the rate limiter and should be provided with skipToken argument (defaults to ``). +- `--skipToken`: Allows bypassing the rate limiter and should be provided with skipKey argument (defaults to ``). +- `--minWorkers`: The number of initial workers to spawn (defaults to `4`). +- `--maxWorkers`: The number of max workers to spawn (defaults to `8`). +- `--workLimit`: The pieces of work that can be performed before restarting process (defaults to `60`). +- `--acquireTimeout`: The number of milliseconds to wait for acquiring a resource (defaults to `5000`). +- `--createTimeout`: The number of milliseconds to wait for creating a resource (defaults to `5000`). +- `--destroyTimeout`: The number of milliseconds to wait for destroying a resource (defaults to `5000`). +- `--idleTimeout`: The number of milliseconds after an idle resource is destroyed (defaults to `30000`). +- `--rasterizationTimeout`: The number of milliseconds to wait for rendering a webpage (defaults to `1500`). +- `--createRetryInterval`: The number of milliseconds after the create process is retried in case of fail (defaults to `200`). +- `--reaperInterval`: The number of milliseconds after the check for idle resources to destroy is triggered (defaults to `1000`). +- `--benchmarking`: Enable benchmarking (defaults to `true`). +- `--listenToProcessExits`: Set to false in order to skip attaching process.exit handlers (defaults to `true`). +- `--logLevel`: The log level (0: silent, 1: error, 2: warning, 3: notice, 4: verbose) (defaults to `4`). +- `--logFile`: A name of a log file. The --logDest also needs to be set to enable file logging (defaults to `highcharts-export-server.log`). +- `--logDest`: The path to store log files. Also enables file logging (defaults to `log/`). +- `--enableUi`: Enables the UI for the export server (defaults to `false`). +- `--uiRoute`: The route to attach the UI to (defaults to `/`). +- `--noLogo`: Skip printing the logo on a startup. Will be replaced by a simple text (defaults to `false`). + +# Tips, Tricks & Notes + +## Note about chart size The `width` argument is mostly to set a zoom factor rather than an absolute width. If you need to set the _height_ of the chart, it can be done in two ways: -This is done by setting `HIGHCHARTS_CDN` to `npm` in addition to setting -the afformentioned `ACCEPT_HIGHCHARTS_LICENSE` to `YES`. +- Set it in the chart config under [`chart.height`](https://api.highcharts.com/highcharts/chart.height). +- Set it in the chart config under [`exporting.sourceHeight`](https://api.highcharts.com/highcharts/exporting.sourceHeight). The latter is prefered, as it lets you set a separate sizing when exporting and when displaying the chart in your web page. @@ -288,26 +425,9 @@ If `--resources` is not set, and a file `resources.json` exist in the folder fro ## Note on Worker Count & Work Limit -The server accepts the following arguments: +The export server utilizes a pool of workers, where each worker is a Puppeteer process (browser instance's page) responsible for the actual chart rasterization. The pool size can be set with the --initialWorkers and --maxWorkers options, and should be tweaked to fit the hardware on which you're running the server. -- `infile`: A string containing JSON or SVG for the chart -- `options`: Alias for `infile` -- `svg`: A string containing SVG to render -- `type`: The format: `png`, `jpeg`, `pdf`, `svg`. Mimetypes can also be used. -- `scale`: The scale factor. Use it to improve resolution in PNG and JPG, for example setting scale to 2 on a 600px chart will result in a 1200px output. -- `width`: The chart width (overrides scale) -- `callback`: Javascript to execute in the highcharts constructor. -- `resources`: Additional resources. -- `constr`: The constructor to use. Either `Chart` or `Stock`. -- `b64`: Bool, set to true to get base64 back instead of binary. -- ~~`async`: Get a download link instead of the file data. Note that the `b64` option overrides the `async` option.~~ This option is deprecated and will be removed as of December 1st 2021. Read the [announcement article on how to replace async](https://www.highcharts.com/docs/export-module/deprecated-async-option). -- `noDownload`: Bool, set to true to not send attachment headers on the response. -- `asyncRendering`: Wait for the included scripts to call `highexp.done()` before rendering the chart. -- `globalOptions`: A JSON object with options to be passed to `Highcharts.setOptions`. -- `dataOptions`: Passed to `Highcharts.data(..)` -- `customCode`: When `dataOptions` is supplied, this is a function to be called with the after applying the data options. Its only argument is the complete options object which will be passed to the Highcharts constructor on return. - -It responds to `application/json`, `multipart/form-data`, and URL encoded requests. +It's recommended that you start with the default (4), and work your way up (or down if 8 is too many for your setup, and things are unstable) gradually. The tests/other/stress-test.js script can be used to test the server and expects the server to be running on port 7801. Each of the workers has a maximum number of requests it can handle before it restarts itself to keep everything responsive. This number is 40 by default, and can be tweaked with `--workLimit`. As with `--minWorkers` and `--maxWorkers`, this number should also be tweaked to fit your use case. Also, the `--acquireTimeout` option is worth to mention as well, in case there would be problems with acquiring resources. It is set in miliseconds with 5000 as a default value. Lastly, the `--createTimeout` and `--destroyTimeout` options are similar to the `--acquireTimeout` but for resource's create and destroy actions. @@ -512,11 +632,13 @@ This package supports both CommonJS and ES modules. - `minWorkers` (default 4) - Min and initial worker process count. - `maxWorkers` (default 8) - Max worker processes count. - `workLimit` (default 40) - How many task can be performed by a worker process before it's automatically restarted. - - - `acquireTimeout` (default 5000) - the maximum allowed time for each resource acquire, in milliseconds. - - `createTimeout` (default 5000) - the maximum allowed time for each resource create, in milliseconds. - - `destroyTimeout` (default 5000) - the maximum allowed time for each resource destroy, in milliseconds. - - `idleTimeout` (default 30000) - the maximum allowed time after an idle resource is destroyed, in milliseconds. + - `acquireTimeout` (default 5000) - The maximum allowed time for each resource acquire, in milliseconds. + - `createTimeout` (default 5000) - The maximum allowed time for each resource create, in milliseconds. + - `destroyTimeout` (default 5000) - The maximum allowed time for each resource destroy, in milliseconds. + - `idleTimeout` (default 30000) - The maximum allowed time after an idle resource is destroyed, in milliseconds. + - `rasterizationTimeout` (default 1500) - The number of milliseconds to wait for rendering a webpage. + - `createRetryInterval` (default 200) - The number of milliseconds after the create process is retried in case of fail. + - `reaperInterval` (default 1000) - The number of milliseconds after the check for idle resources to destroy is triggered. - `benchmarking` (default false) - Enable benchmarking. - `listenToProcessExits` (default true) - Set to false in order to skip attaching process.exit handlers. From ebff4761b60a90b27926c097e00141734b55e7c5 Mon Sep 17 00:00:00 2001 From: PaulDalek Date: Tue, 16 Jan 2024 19:48:34 +0100 Subject: [PATCH 4/8] Some other minor corrections. --- .env.sample | 1 + lib/server.js | 504 -------------------------------------------------- 2 files changed, 1 insertion(+), 504 deletions(-) delete mode 100644 lib/server.js diff --git a/.env.sample b/.env.sample index a6846111..25498824 100644 --- a/.env.sample +++ b/.env.sample @@ -8,6 +8,7 @@ EXPORT_DEFAULT_SCALE = 1 # Highcharts config HIGHCHARTS_VERSION = latest HIGHCHARTS_CDN = https://code.highcharts.com/ +HIGHCHARTS_FORCE_FETCH = false HIGHCHARTS_CORE_SCRIPTS = HIGHCHARTS_MODULES = HIGHCHARTS_INDICATORS = diff --git a/lib/server.js b/lib/server.js deleted file mode 100644 index 56e8eb2b..00000000 --- a/lib/server.js +++ /dev/null @@ -1,504 +0,0 @@ -/* - -Highcharts Export Server - -Copyright (c) 2016, Highsoft - -Permission is hereby granted, free of charge, to any person obtaining -a copy of this software and associated documentation files (the -"Software"), to deal in the Software without restriction, including -without limitation the rights to use, copy, modify, merge, publish, -distribute, sublicense, and/or sell copies of the Software, and to -permit persons to whom the Software is furnished to do so, subject to -the following conditions: - -The above copyright notice and this permission notice shall be -included in all copies or substantial portions of the Software. - -THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, -EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF -MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. -IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY -CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, -TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE -SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. - -*/ - -const fs = require("fs"); -const log = require("./logger.js").log; -const express = require("express"); -const http = require("http"); -const https = require("https"); -const app = express(); -const bodyParser = require("body-parser"); -const chart = require("./chart.js"); -const formData = require("express-form-data"); -const cors = require("cors"); -const uuid = require("uuid/v4"); -const RateLimit = require("express-rate-limit"); - -const mime = { - "image/png": "png", - "image/jpeg": "jpeg", - "image/gif": "gif", - "application/pdf": "pdf", - "image/svg+xml": "svg" -}; - -const mimeReverse = { - png: "image/png", - jpeg: "image/jpeg", - gif: "image/gif", - pdf: "application/pdf", - svg: "image/svg+xml" -}; - -var rc = 0, - allowCodeExecution = false, - allowAsync = true, - beforeRequest = [], - afterRequest = [], - tmpDir = false, - limiter = function (req, res, next) { - next(); - }; - -function doCallbacks(which, req, res, data, id, uniqueid, type) { - var res = true; - - which.some(function (fn) { - var cres; - if (fn) { - cres = fn(req, res, data, id, uniqueid, type); - if (cres !== undefined && cres !== true) { - res = cres; - return true; - } else { - return true; - } - } - }); - - return res; -} - -app.use(cors()); -app.use( - formData.parse({ - maxFieldsSize: "50mb" - }) -); -app.use(bodyParser.urlencoded({ extended: true, limit: "50mb" })); -//application/x-www-form-urlencoded -app.use(bodyParser.urlencoded({ extended: false, limit: "50mb" })); -app.use( - bodyParser.urlencoded({ - type: "multipart/form-data", - extended: false, - limit: "50mb" - }) -); -app.use(bodyParser.json({ limit: "50mb" })); - -app.use("/charts", express.static("tmp/")); - -app.get("/health", function (req, res) { - res.send("OK"); -}); - -function enableRateLimiting(options) { - var msg = - "Too many requests, you have been rate limited. Please try again later.", - op = { - max: options.max || 30, - window: options.window || 1, - delay: options.delay || 0, - trustProxy: options.trustProxy || false, - skipKey: options.skipKey || false, - skipToken: options.skipToken || false - }; - log( - 3, - "enabling rate limiting:", - op.max, - "requests per.", - op.window, - "minute per. IP" - ); - - if (op.trustProxy) { - log(3, "trusting proxy"); - app.enable("trust proxy"); - } - - limiter = new RateLimit({ - windowMs: op.window * 60 * 1000, - max: op.max, // limit each IP to 100 requests per windowMs - delayMs: op.delay, // disable delaying - full speed until the max limit is reached - handler: function (req, res) { - res.format({ - json: function () { - res.status(429).send({ message: msg }); - }, - default: function () { - res.status(429).send(msg); - } - }); - }, - skip: function (req, res) { - //We allow bypassing the limiter if a valid key/token has been sent - if ( - op.skipKey !== false && - op.skipToken !== false && - req.query.key === op.skipKey && - req.query.access_token === op.skipToken - ) { - log(4, "skipping rate limiter"); - return true; - } - return false; - } - }); -} - -//Flush temporary files in t minutes -function flushIn(t, filename) { - if (!t) { - return; - } - - setTimeout(function () { - if (filename) { - fs.unlink(filename, function (err) { - if (err) return log(1, "error when deleting temporary file:", err); - }); - } - }, t * 60 * 1000); -} - -function handlePost(req, res) { - var cres = false; - var id = ++rc; - var uniqueid = uuid().replace(/\-/g, ""); - var type = mime[req.body.type] || "png"; - var connectionAborted = false; - - if (!req.body) - return res - .status(400) - .send( - "Body is required. Sending a body? Make sure your Content-type header is correct. Accepted is application/json and multipart/form-data." - ); - - if ( - !req.body.infile && - !req.body.options && - !req.body.svg && - !req.body.data - ) { - log( - 2, - "request", - uniqueid, - "from", - req.headers["x-forwarded-for"] || req.connection.remoteAddress, - "was empty:", - JSON.stringify(req.body, undefined, " "), - "headers:", - JSON.stringify(req.headers, undefined, " ") - ); - - return res - .status(400) - .send( - "No chart data found. Please make sure you are using application/json or multipart/form-data headers, and that the chart data is in the options attribute if sending JSON." - ); - } - - // Disable async option by config - if (req.body.async && !allowAsync) { - return res - .status(410) - .send('The async option has been disabled.\nSee https://www.highcharts.com/docs/export-module/deprecated-async-option for alternatives'); - } - - cres = doCallbacks(beforeRequest, req, res, req.body, id, uniqueid, type); - if (cres !== true) { - //Block request - return res.send(cres); - } - - log(4, "got incoming HTTP request", uniqueid); - - req.socket.on("close", function () { - connectionAborted = true; - }); - - - chart( - { - outfile: - "tmp/" + (req.params.filename || "chart." + uniqueid + "." + type), - allowCodeExecution: allowCodeExecution, - instr: req.body.infile || req.body.options || req.body.data, - constr: req.body.constr, - type: req.body.type || "png", - scale: req.body.scale, - width: req.body.width || false, - svg: req.body.svg, - resources: req.body.resources || false, - callback: req.body.callback || false, - styledMode: req.body.styledMode || false, - asyncRendering: req.body.asyncRendering || false, - globalOptions: req.body.globaloptions || req.body.globalOptions || false, - themeOptions: req.body.themeoptions || req.body.themeOptions || false, - customCode: req.body.customcode || req.body.customCode || false, - dataOptions: req.body.dataoptions || req.body.dataOptions || false, - tmpdir: tmpDir, - async: req.body.async || false, - reqID: uniqueid - }, - function (err, info, status) { - if (info && info.filename) { - flushIn(15, info.filename); - } - - if (connectionAborted) { - // If the connection was closed, do nothing - log( - 3, - "the client closed the connection before the chart was done processing" - ); - return; - } - - if (err) { - log(1, "work", uniqueid, "could not be completed, sending:", err); - return res.status(status || 400).send(err); - } - - if (!info || (!info.filename && !info.data)) { - log( - 1, - "return from chart is not even wrong. request", - uniqueid, - "is", - info - ); - return res - .status(400) - .send( - "unexpected return from chart generation - please check your input data" - ); - } - - doCallbacks( - afterRequest, - req, - res, - { - data: req.body.data, - filename: info.filename - }, - id - ); - - if (req.body.async && info.filename) { - return res.send( - "charts/" + info.filename.substr(info.filename.lastIndexOf("/") + 1) - ); - } else if (info.data) { - if (!req.body.noDownload) { - res.attachment( - (req.body.filename || "chart") + - "." + - (mime[req.body.type] || req.body.type || "png") - ); - } - - if (req.body.b64) { - return res.send(info.data); - } - - res.header("Content-Type", req.body.type || "image/png"); - return res.send(Buffer.from(info.data, "base64")); - } - - //need to convert to using pipes, this is silly. - fs.readFile(info.filename, function (err, data) { - if (err) { - log(1, uniqueid, err); - return res.status(400).send("error generating - check your json"); - } - - if (!req.body.noDownload) { - res.attachment( - (req.body.filename || "chart") + - "." + - (mime[req.body.type] || req.body.type || "png") - ); - } - - if (req.body.b64) { - res.send(data.toString("base64")); - } else { - res.header("Content-Type", req.body.type || "image/png"); - res.send(data); - } - }); - } - ); -} - -//Sets up route handling for chart conversions -function setup() { - //Main route - app.post("/", limiter, handlePost); - - //Compatibility route - app.post("/json/:filename", limiter, handlePost); - - //Shorthand - :filename becomes the filename to return - app.post("/:filename", limiter, handlePost); -} - -module.exports = { - //Start the server - start: function ( - port, - sslPort, - sslPath, - fn, - sslOnly, - newTmpDir, - host, - allowCodeEval, - allowAsyncOption = true - ) { - var httpServer = http.createServer(app), - httpsServer; - - tmpDir = newTmpDir || tmpDir; - allowCodeExecution = allowCodeEval; - allowAsync = allowAsyncOption; - - function errorHandler(err, socket) { - log(1, "socket error:", err); - // socket.end('HTTP/1.1 400 Bad request\r\n\r\n'); - } - - httpServer.on("clientError", errorHandler); - httpServer.on("error", errorHandler); - - httpServer.on("connection", function (socket) { - socket.on("error", function (err) { - errorHandler(err, socket); - }); - }); - - setup(); - - port = port || 80; - sslPort = sslPort || 443; - - if (!sslOnly) { - httpServer.listen(port, host); - } - - function handleSSLLoadError(err) { - log(1, "error loading SSL certs:", err); - if (sslOnly) { - log( - 1, - "running in SSL mode only, but failed to load certs - No server started!" - ); - } else { - log(1, "HTTPS server NOT started"); - } - } - - //Try to start the https server too - if (sslPath) { - if (sslPath && sslPath[sslPath.length - 1] !== "/") { - sslPath += "/"; - } - - fs.readFile(sslPath + "server.key", function (err, skey) { - if (err) return handleSSLLoadError(err); - fs.readFile(sslPath + "server.crt", function (err, cert) { - if (err) return handleSSLLoadError(err); - httpsServer = https.createServer( - { - key: skey, - cert: cert - }, - app - ); - - httpsServer.on("clientError", errorHandler); - httpsServer.on("error", errorHandler); - - httpsServer.listen(sslPort); - log(3, "https server started on port", sslPort); - - if (fn && sslOnly) { - fn(httpServer); - } - }); - }); - } - - if (!sslOnly) { - if (fn) { - fn(httpServer); - } - - log(3, "http server started on", (host || "0.0.0.0") + ":" + port); - } - - if (sslOnly && !sslPath) { - log(1, "no server started!"); - log( - 1, - "running in SSL mode only, but no path to certificates set! use --sslPath " - ); - } - }, - - setup: setup, - - express: function () { - return express; - }, - - app: function () { - return app; - }, - - use: function (a, b) { - app.use(a, b); - }, - - get: function (path, fn) { - app.get(path, fn); - }, - - post: function (path, fn) { - app.post(path, fn); - }, - - setTempDir: function (ndir) { - tmpDir = ndir; - }, - - enableRateLimiting: enableRateLimiting, - - useFilter: function (when, fn) { - if (when === "beforeRequest") { - beforeRequest.push(fn); - } else { - afterRequest.push(fn); - } - } -}; From b74a5fcf28ed5abae55e32f816f1903770bd15ec Mon Sep 17 00:00:00 2001 From: jakubSzuminski Date: Wed, 17 Jan 2024 13:42:54 +0100 Subject: [PATCH 5/8] Added an exception for the base64 svg export issue. --- lib/server/routes/export.js | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/lib/server/routes/export.js b/lib/server/routes/export.js index 4ccd8c25..cdb3a7e5 100644 --- a/lib/server/routes/export.js +++ b/lib/server/routes/export.js @@ -308,12 +308,13 @@ const exportHandler = (request, response) => { if (info.data) { // If only base64 is required, return it if (body.b64) { - // Check if it is already base64 or a raw SVG - if (type === 'pdf') { + // SVG Exception for the Highcharts 11.3.0 version + if (type === 'pdf' || type == 'svg') { return response.send( Buffer.from(info.data, 'utf8').toString('base64') ); } + return response.send(info.data); } From c8a360ab8e8bdf5cb7f6566040b5afeab0589a74 Mon Sep 17 00:00:00 2001 From: PaulDalek Date: Wed, 17 Jan 2024 19:56:15 +0100 Subject: [PATCH 6/8] Further pool related fixes. --- .env.sample | 2 +- .gitignore | 1 + README.md | 16 +++++++++++++--- dist/index.cjs | 2 -- dist/index.esm.js | 2 -- dist/index.esm.js.map | 1 - lib/browser.js | 25 +++++++++++++++++++------ lib/export.js | 5 +++-- lib/pool.js | 6 ++++-- lib/schemas/config.js | 24 ++++++++++++------------ package-lock.json | 4 ++-- package.json | 2 +- templates/template.html | 35 +++++++++++++++++++---------------- 13 files changed, 75 insertions(+), 50 deletions(-) delete mode 100644 dist/index.cjs delete mode 100644 dist/index.esm.js delete mode 100644 dist/index.esm.js.map diff --git a/.env.sample b/.env.sample index 25498824..69ae794a 100644 --- a/.env.sample +++ b/.env.sample @@ -4,6 +4,7 @@ EXPORT_DEFAULT_CONSTR = chart EXPORT_DEFAULT_HEIGHT = 400 EXPORT_DEFAULT_WIDTH = 600 EXPORT_DEFAULT_SCALE = 1 +EXPORT_RASTERIZATION_TIMEOUT = 1500 # Highcharts config HIGHCHARTS_VERSION = latest @@ -45,7 +46,6 @@ HIGHCHARTS_POOL_ACQUIRE_TIMEOUT = 5000 HIGHCHARTS_POOL_CREATE_TIMEOUT = 5000 HIGHCHARTS_POOL_DESTROY_TIMEOUT = 5000 HIGHCHARTS_POOL_IDLE_TIMEOUT = 30000 -HIGHCHARTS_POOL_RASTERIZATION_TIMEOUT = 1500 HIGHCHARTS_POOL_CREATE_RETRY_INTERVAL = 200 HIGHCHARTS_POOL_REAPER_INTERVAL = 1000 HIGHCHARTS_POOL_BENCHMARKING = false diff --git a/.gitignore b/.gitignore index 6d240d33..44d1686c 100644 --- a/.gitignore +++ b/.gitignore @@ -2,6 +2,7 @@ node_modules/ log/ tests/_temp tmp/ +dist/ .DS_Store .cache diff --git a/README.md b/README.md index 006f777c..ba8a8255 100644 --- a/README.md +++ b/README.md @@ -262,13 +262,16 @@ Loading an additional JSON configuration file can be done by using the `--loadCo These are set as variables in your environment. They take precedence over options from the `lib/schemas/config.js` file. On Linux, use e.g. `export`. ### Export config + - `EXPORT_DEFAULT_TYPE`: The format of the file to export to. Can be jpeg, png, pdf or svg. - `EXPORT_DEFAULT_CONSTR`: The constructor to use. Can be chart, stockChart, mapChart or ganttChart. - `EXPORT_DEFAULT_HEIGHT`: The height of the exported chart. Overrides the option in the chart settings. - `EXPORT_DEFAULT_WIDTH`: The width of the exported chart. Overrides the option in the chart settings. - `EXPORT_DEFAULT_SCALE`: The scale of the exported chart. Ranges between 0.1 and 5.0. +- `EXPORT_RASTERIZATION_TIMEOUT`: The number of milliseconds to wait for rendering a webpage. ### Highcharts config + - `HIGHCHARTS_VERSION`: Highcharts version to use. - `HIGHCHARTS_CDN`: The CDN URL of Highcharts scripts to use. - `HIGHCHARTS_FORCE_FETCH`: Should refetch all the scripts after each server rerun. @@ -277,21 +280,25 @@ These are set as variables in your environment. They take precedence over option - `HIGHCHARTS_INDICATORS`: Highcharts indicators to fetch. ### Custom code config + - `HIGHCHARTS_ALLOW_CODE_EXECUTION`: If set to true, allow for the execution of arbitrary code when exporting. - `HIGHCHARTS_ALLOW_FILE_RESOURCES`: Allow injecting resources from the filesystem. Has no effect when running as a server. ### Server config + - `HIGHCHARTS_SERVER_ENABLE`: If set to true, starts a server on 0.0.0.0. - `HIGHCHARTS_SERVER_HOST`: The hostname of the server. Also starts a server listening on the supplied hostname. - `HIGHCHARTS_SERVER_PORT`: The port to use for the server. Defaults to 7801. ### Server SSL config + - `HIGHCHARTS_SERVER_SSL_ENABLE`: Enables the SSL protocol. - `HIGHCHARTS_SERVER_SSL_FORCE`: If set to true, forces the server to only serve over HTTPS. - `HIGHCHARTS_SERVER_SSL_PORT`: The port on which to run the SSL server. - `HIGHCHARTS_SERVER_SSL_CERT_PATH`: The path to the SSL certificate/key. ### Server rate limiting config + - `HIGHCHARTS_RATE_LIMIT_ENABLE`: Enables rate limiting. - `HIGHCHARTS_RATE_LIMIT_MAX`: Max requests allowed in a one minute. - `HIGHCHARTS_RATE_LIMIT_WINDOW`: The time window in minutes for rate limiting. @@ -301,6 +308,7 @@ These are set as variables in your environment. They take precedence over option - `HIGHCHARTS_RATE_LIMIT_SKIP_TOKEN`: Allows bypassing the rate limiter and should be provided with skipKey argument. ### Pool config + - `HIGHCHARTS_POOL_MIN_WORKERS`: The number of initial workers to spawn. - `HIGHCHARTS_POOL_MAX_WORKERS`: The number of max workers to spawn. - `HIGHCHARTS_POOL_WORK_LIMIT`: The pieces of work that can be performed before restarting process. @@ -308,25 +316,28 @@ These are set as variables in your environment. They take precedence over option - `HIGHCHARTS_POOL_CREATE_TIMEOUT`: The number of milliseconds to wait for creating a resource. - `HIGHCHARTS_POOL_DESTROY_TIMEOUT`: The number of milliseconds to wait for destroying a resource. - `HIGHCHARTS_POOL_IDLE_TIMEOUT`: The number of milliseconds after an idle resource is destroyed. -- `HIGHCHARTS_POOL_RASTERIZATION_TIMEOUT`: The number of milliseconds to wait for rendering a webpage. - `HIGHCHARTS_POOL_CREATE_RETRY_INTERVAL`: The number of milliseconds after the create process is retried in case of fail. - `HIGHCHARTS_POOL_REAPER_INTERVAL`: The number of milliseconds after the check for idle resources to destroy is triggered. - `HIGHCHARTS_POOL_BENCHMARKING`: Enable benchmarking. - `HIGHCHARTS_POOL_LISTEN_TO_PROCESS_EXITS`: Set to false in order to skip attaching process.exit handlers. ### Logging config + - `HIGHCHARTS_LOG_LEVEL`: The log level (0: silent, 1: error, 2: warning, 3: notice, 4: verbose). - `HIGHCHARTS_LOG_FILE`: A name of a log file. The --logDest also needs to be set to enable file logging. - `HIGHCHARTS_LOG_DEST`: The path to store log files. Also enables file logging. ### UI config + - `HIGHCHARTS_UI_ENABLE`: Enables the UI for the export server. - `HIGHCHARTS_UI_ROUTE`: The route to attach the UI to. ### Other config + - `HIGHCHARTS_NO_LOGO`: Skip printing the logo on a startup. Will be replaced by a simple text. ### Proxy config + - `PROXY_SERVER_HOST`: The host of the proxy server to use if exists. - `PROXY_SERVER_PORT`: The port of the proxy server to use if exists. - `PROXY_SERVER_TIMEOUT`: The timeout for the proxy server to use if exists. @@ -350,6 +361,7 @@ _Available options:_ - `--globalOptions`: A stringified JSON or a filename with options to be passed into the Highcharts.setOptions (defaults to `false`). - `--themeOptions`: A stringified JSON or a filename with theme options to be passed into the Highcharts.setOptions (defaults to `false`). - `--batch`: Starts a batch job. A string that contains input/output pairs: "in=out;in=out;.." (defaults to `false`). +- `--rasterizationTimeout`: The number of milliseconds to wait for rendering a webpage (defaults to `1500`). - `--allowCodeExecution`: If set to true, allow for the execution of arbitrary code when exporting (defaults to `false`). - `--allowFileResources`: Allow injecting resources from the filesystem. Has no effect when running as a server (defaults to `true`). - `--customCode`: Custom code to be called before chart initialization. Can be a function, a code that will be wrapped within a function or a filename with the js extension (defaults to `false`). @@ -375,7 +387,6 @@ _Available options:_ - `--createTimeout`: The number of milliseconds to wait for creating a resource (defaults to `5000`). - `--destroyTimeout`: The number of milliseconds to wait for destroying a resource (defaults to `5000`). - `--idleTimeout`: The number of milliseconds after an idle resource is destroyed (defaults to `30000`). -- `--rasterizationTimeout`: The number of milliseconds to wait for rendering a webpage (defaults to `1500`). - `--createRetryInterval`: The number of milliseconds after the create process is retried in case of fail (defaults to `200`). - `--reaperInterval`: The number of milliseconds after the check for idle resources to destroy is triggered (defaults to `1000`). - `--benchmarking`: Enable benchmarking (defaults to `true`). @@ -636,7 +647,6 @@ This package supports both CommonJS and ES modules. - `createTimeout` (default 5000) - The maximum allowed time for each resource create, in milliseconds. - `destroyTimeout` (default 5000) - The maximum allowed time for each resource destroy, in milliseconds. - `idleTimeout` (default 30000) - The maximum allowed time after an idle resource is destroyed, in milliseconds. - - `rasterizationTimeout` (default 1500) - The number of milliseconds to wait for rendering a webpage. - `createRetryInterval` (default 200) - The number of milliseconds after the create process is retried in case of fail. - `reaperInterval` (default 1000) - The number of milliseconds after the check for idle resources to destroy is triggered. - `benchmarking` (default false) - Enable benchmarking. diff --git a/dist/index.cjs b/dist/index.cjs deleted file mode 100644 index 0a8ea4f4..00000000 --- a/dist/index.cjs +++ /dev/null @@ -1,2 +0,0 @@ -"use strict";require("colors");var e=require("fs"),t=require("path"),o=require("body-parser"),r=require("cors"),i=require("express"),n=require("multer"),s=require("http"),a=require("https"),l=require("dotenv"),c=require("express-rate-limit"),p=require("url"),u=require("https-proxy-agent"),d=require("uuid"),h=require("tarn"),g=require("puppeteer"),m=require("node:path"),f=require("node:crypto");require("prompts");var v="undefined"!=typeof document?document.currentScript:null;function y(e){var t=Object.create(null);return e&&Object.keys(e).forEach((function(o){if("default"!==o){var r=Object.getOwnPropertyDescriptor(e,o);Object.defineProperty(t,o,r.get?r:{enumerable:!0,get:function(){return e[o]}})}})),t.default=e,Object.freeze(t)}var b=y(p);l.config();const w={puppeteer:{args:{value:[],type:"string[]",description:"Array of arguments to send to puppeteer."}},highcharts:{version:{value:"latest",envLink:"HIGHCHARTS_VERSION",type:"string",description:"Highcharts version to use."},cdnURL:{value:"https://code.highcharts.com/",envLink:"HIGHCHARTS_CDN",type:"string",description:"The CDN URL of Highcharts scripts to use."},coreScripts:{envLink:"HIGHCHARTS_CORE_SCRIPTS",value:["highcharts","highcharts-more","highcharts-3d"],type:"string[]",description:"Highcharts core scripts to fetch."},modules:{envLink:"HIGHCHARTS_MODULES",value:["stock","map","gantt","exporting","export-data","parallel-coordinates","accessibility","annotations-advanced","boost-canvas","boost","data","draggable-points","static-scale","broken-axis","heatmap","tilemap","timeline","treemap","treegraph","item-series","drilldown","histogram-bellcurve","bullet","funnel","funnel3d","pyramid3d","networkgraph","pareto","pattern-fill","pictorial","price-indicator","sankey","arc-diagram","dependency-wheel","series-label","solid-gauge","sonification","stock-tools","streamgraph","sunburst","variable-pie","variwide","vector","venn","windbarb","wordcloud","xrange","no-data-to-display","drag-panes","debugger","dumbbell","lollipop","cylinder","organization","dotplot","marker-clusters","hollowcandlestick","heikinashi"],type:"string[]",description:"Highcharts modules to fetch."},indicators:{envLink:"HIGHCHARTS_INDICATORS",value:["indicators-all"],type:"string[]",description:"Highcharts indicators to fetch."},scripts:{value:["https://cdnjs.cloudflare.com/ajax/libs/moment.js/2.29.4/moment.min.js","https://cdnjs.cloudflare.com/ajax/libs/moment-timezone/0.5.34/moment-timezone-with-data.min.js"],type:"string[]",description:"Additional direct scripts/optional dependencies (e.g. moment.js)."},forceFetch:{envLink:"HIGHCHARTS_FORCE_FETCH",value:!1,type:"boolean",description:"Should all the scripts be refetched after rerunning the server."}},export:{infile:{value:!1,type:"string",description:"The input file name along with a type (json or svg). It can be a correct JSON or SVG file."},instr:{value:!1,type:"string",description:"An input in a form of a stringified JSON or SVG file. Overrides the --infile."},options:{value:!1,type:"string",description:"An alias for the --instr option."},outfile:{value:!1,type:"string",description:"The output filename along with a type (jpeg, png, pdf or svg). Ignores the --type flag."},type:{envLink:"EXPORT_DEFAULT_TYPE",value:"png",type:"string",description:"The format of the file to export to. Can be jpeg, png, pdf or svg."},constr:{envLink:"EXPORT_DEFAULT_CONSTR",value:"chart",type:"string",description:"The constructor to use. Can be chart, stockChart, mapChart or ganttChart."},defaultHeight:{envLink:"EXPORT_DEFAULT_HEIGHT",value:400,type:"number",description:"The default height of the exported chart. Used when not found any value set."},defaultWidth:{envLink:"EXPORT_DEFAULT_WIDTH",value:600,type:"number",description:"The default width of the exported chart. Used when not found any value set."},defaultScale:{envLink:"EXPORT_DEFAULT_SCALE",value:1,type:"number",description:"The default scale of the exported chart. Ranges between 1 and 5."},height:{type:"number",value:!1,description:"The default height of the exported chart. Overrides the option in the chart settings."},width:{type:"number",value:!1,description:"The width of the exported chart. Overrides the option in the chart settings."},scale:{value:!1,type:"number",description:"The scale of the exported chart. Ranges between 1 and 5."},globalOptions:{value:!1,type:"string",description:"A stringified JSON or a filename with options to be passed into the Highcharts.setOptions."},themeOptions:{value:!1,type:"string",description:"A stringified JSON or a filename with theme options to be passed into the Highcharts.setOptions."},batch:{value:!1,type:"string",description:'Starts a batch job. A string that contains input/output pairs: "in=out;in=out;..".'}},customCode:{allowCodeExecution:{envLink:"HIGHCHARTS_ALLOW_CODE_EXECUTION",value:!1,type:"boolean",description:"If set to true, allow for the execution of arbitrary code when exporting."},allowFileResources:{envLink:"HIGHCHARTS_ALLOW_FILE_RESOURCES",value:!0,type:"boolean",description:"Allow injecting resources from the filesystem. Has no effect when running as a server."},customCode:{value:!1,type:"string",description:"A function to be called before chart initialization. Can be a filename with the js extension."},callback:{value:!1,type:"string",description:"A JavaScript file with a function to run on construction."},resources:{value:!1,type:"string",description:"An additional resource in a form of stringified JSON. It can contain files, js and css sections."},loadConfig:{value:!1,type:"string",description:"A file that contains a pre-defined config to use."},createConfig:{value:!1,type:"string",description:"Allows to set options through a prompt and save in a provided config file."}},server:{enable:{envLink:"HIGHCHARTS_SERVER_ENABLE",value:!1,type:"boolean",cliName:"enableServer",description:"If set to true, starts a server on 0.0.0.0."},host:{envLink:"HIGHCHARTS_SERVER_HOST",value:"0.0.0.0",type:"string",description:"The hostname of the server. Also starts a server listening on the supplied hostname."},port:{envLink:"HIGHCHARTS_SERVER_PORT",value:7801,type:"number",description:"The port to use for the server. Defaults to 7801."},ssl:{enable:{envLink:"HIGHCHARTS_SERVER_SSL_ENABLE",value:!1,type:"boolean",cliName:"enableSsl",description:"Enables the SSL protocol."},force:{envLink:"HIGHCHARTS_SERVER_SSL_FORCE",value:!1,type:"boolean",cliName:"sslForced",description:"If set to true, forces the server to only serve over HTTPS."},port:{envLink:"HIGHCHARTS_SERVER_SSL_PORT",value:443,type:"number",cliName:"sslPort",description:"The port on which to run the SSL server."},certPath:{envLink:"HIGHCHARTS_SSL_CERT_PATH",value:"",type:"string",description:"The path to the SSL certificate/key."}},rateLimiting:{enable:{envLink:"HIGHCHARTS_RATE_LIMIT_ENABLE",value:!1,type:"boolean",cliName:"enableRateLimiting",description:"Enables rate limiting."},maxRequests:{envLink:"HIGHCHARTS_RATE_LIMIT_MAX",value:10,type:"number",description:"Max requests allowed in a one minute."},window:{envLink:"HIGHCHARTS_RATE_LIMIT_WINDOW",value:1,type:"number",description:"The time window in minutes for rate limiting."},delay:{envLink:"HIGHCHARTS_RATE_LIMIT_DELAY",value:0,type:"number",description:"The amount to delay each successive request before hitting the max."},trustProxy:{envLink:"HIGHCHARTS_RATE_LIMIT_TRUST_PROXY",value:!1,type:"boolean",description:"Set this to true if behind a load balancer."},skipKey:{envLink:"HIGHCHARTS_RATE_LIMIT_SKIP_KEY",value:"",type:"number|string",description:"Allows bypassing the rate limiter and should be provided with skipToken argument."},skipToken:{envLink:"HIGHCHARTS_RATE_LIMIT_SKIP_TOKEN",value:"",type:"number|string",description:"Allows bypassing the rate limiter and should be provided with skipKey argument."}}},pool:{minWorkers:{envLink:"HIGHCHARTS_POOL_MIN_WORKERS",value:4,type:"number",description:"The number of initial workers to spawn."},maxWorkers:{envLink:"HIGHCHARTS_POOL_MAX_WORKERS",value:8,type:"number",description:"The number of max workers to spawn."},workLimit:{envLink:"HIGHCHARTS_POOL_WORK_LIMIT",value:40,type:"number",description:"The pieces of work that can be performed before restarting process."},acquireTimeout:{envLink:"HIGHCHARTS_POOL_ACQUIRE_TIMEOUT",value:5e3,type:"number",description:"The number of milliseconds to wait for acquiring a resource."},createTimeout:{envLink:"HIGHCHARTS_POOL_CREATE_TIMEOUT",value:5e3,type:"number",description:"The number of milliseconds to wait for creating a resource."},destroyTimeout:{envLink:"HIGHCHARTS_POOL_DESTROY_TIMEOUT",value:5e3,type:"number",description:"The number of milliseconds to wait for destroying a resource."},idleTimeout:{envLink:"HIGHCHARTS_POOL_IDLE_TIMEOUT",value:3e4,type:"number",description:"The number of milliseconds after an idle resource is destroyed."},rasterizationTimeout:{envLink:"HIGHCHARTS_POOL_RASTERIZATION_TIMEOUT",value:1500,type:"number",description:"The number of milliseconds to wait for rendering a webpage."},createRetryInterval:{envLink:"HIGHCHARTS_POOL_CREATE_RETRY_INTERVAL",value:200,type:"number",description:"The number of milliseconds after the create process is retried in case of fail."},reaperInterval:{envLink:"HIGHCHARTS_POOL_REAPER_INTERVAL",value:1e3,type:"number",description:"The number of milliseconds after the check for idle resources to destroy is triggered."},benchmarking:{envLink:"HIGHCHARTS_POOL_BENCHMARKING",value:!1,type:"boolean",description:"Enable benchmarking."},listenToProcessExits:{envLink:"HIGHCHARTS_POOL_LISTEN_TO_PROCESS_EXITS",value:!0,type:"boolean",description:"Set to false in order to skip attaching process.exit handlers."}},logging:{level:{envLink:"HIGHCHARTS_LOG_LEVEL",value:4,type:"number",cliName:"logLevel",description:"The log level (0: silent, 1: error, 2: warning, 3: notice, 4: verbose)."},file:{envLink:"HIGHCHARTS_LOG_FILE",value:"highcharts-export-server.log",type:"string",cliName:"logFile",description:"A name of a log file. The --logDest also needs to be set to enable file logging."},dest:{envLink:"HIGHCHARTS_LOG_DEST",value:"log/",type:"string",cliName:"logDest",description:"The path to store log files. Also enables file logging."}},ui:{enable:{envLink:"HIGHCHARTS_UI_ENABLE",value:!1,type:"boolean",cliName:"enableUi",description:"Enables the UI for the export server."},route:{envLink:"HIGHCHARTS_UI_ROUTE",value:"/",type:"string",cliName:"uiRoute",description:"The route to attach the UI to."}},other:{noLogo:{envLink:"HIGHCHARTS_NO_LOGO",value:!1,type:"boolean",description:"Skip printing the logo on a startup. Will be replaced by a simple text."}},payload:{}};w.puppeteer.args.value.join(","),w.highcharts.version.value,w.highcharts.cdnURL.value,w.highcharts.modules.value,w.highcharts.scripts.value.join(","),w.highcharts.forceFetch.value,w.export.type.value,w.export.constr.value,w.export.defaultHeight.value,w.export.defaultWidth.value,w.export.defaultScale.value,w.customCode.allowCodeExecution.value,w.customCode.allowFileResources.value,w.server.enable.value,w.server.host.value,w.server.port.value,w.server.ssl.enable.value,w.server.ssl.force.value,w.server.ssl.port.value,w.server.ssl.certPath.value,w.server.rateLimiting.enable.value,w.server.rateLimiting.maxRequests.value,w.server.rateLimiting.window.value,w.server.rateLimiting.delay.value,w.server.rateLimiting.trustProxy.value,w.server.rateLimiting.skipKey.value,w.server.rateLimiting.skipToken.value,w.pool.minWorkers.value,w.pool.maxWorkers.value,w.pool.workLimit.value,w.pool.acquireTimeout.value,w.pool.createTimeout.value,w.pool.destroyTimeout.value,w.pool.idleTimeout.value,w.pool.rasterizationTimeout.value,w.pool.createRetryInterval.value,w.pool.reaperInterval.value,w.pool.benchmarking.value,w.pool.listenToProcessExits.value,w.logging.level.value,w.logging.file.value,w.logging.dest.value,w.ui.enable.value,w.ui.route.value,w.other.noLogo.value;const T=["options","globalOptions","themeOptions","resources","payload"],x={},k=(e,t="")=>{Object.keys(e).forEach((o=>{if(!["puppeteer","highcharts"].includes(o)){const r=e[o];void 0===r.value?k(r,`${t}.${o}`):x[r.cliName||o]=`${t}.${o}`.substring(1)}}))};k(w);let S={toConsole:!0,toFile:!1,pathCreated:!1,levelsDesc:[{title:"error",color:"red"},{title:"warning",color:"yellow"},{title:"notice",color:"blue"},{title:"verbose",color:"gray"}],listeners:[]};for(const[e,t]of Object.entries(w.logging))S[e]=t.value;const H=(...t)=>{const[o,...r]=t,{level:i,levelsDesc:n}=S;if(0===o||o>i||i>n.length)return;const s=`${(new Date).toString().split("(")[0].trim()} [${n[o-1].title}] -`;S.listeners.forEach((e=>{e(s,r.join(" "))})),S.toFile&&(S.pathCreated||(!e.existsSync(S.dest)&&e.mkdirSync(S.dest),S.pathCreated=!0),e.appendFile(`${S.dest}${S.file}`,[s].concat(r).join(" ")+"\n",(e=>{e&&(console.log(`[logger] Unable to write to log file: ${e}`),S.toFile=!1)}))),S.toConsole&&console.log.apply(void 0,[s.toString()[S.levelsDesc[o-1].color]].concat(r))},R=p.fileURLToPath(new URL("../.","undefined"==typeof document?require("url").pathToFileURL(__filename).href:v&&v.src||new URL("index.cjs",document.baseURI).href)),E=(e,t=/\s\s+/g,o=" ")=>e.replaceAll(t,o).trim(),L=(e,t)=>{const o=["png","jpeg","pdf","svg"];if(t){const r=t.split(".").pop();o.includes(r)&&e!==r&&(e=r)}return{"image/png":"png","image/jpeg":"jpeg","application/pdf":"pdf","image/svg+xml":"svg"}[e]||o.find((t=>t===e))||"png"},O=(t=!1,o)=>{const r=["js","css","files"];let i=t,n=!1;if(o&&t.endsWith(".json"))try{t?t&&t.endsWith(".json")?i=_(e.readFileSync(t,"utf8")):(i=_(t),!0===i&&(i=_(e.readFileSync("resources.json","utf8")))):i=_(e.readFileSync("resources.json","utf8"))}catch(e){return H(3,"[cli] No resources found.")}else i=_(t),o||delete i.files;for(const e in i)r.includes(e)?n||(n=!0):delete i[e];return n?(i.files&&(i.files=i.files.map((e=>e.trim())),(!i.files||i.files.length<=0)&&delete i.files),i):H(3,"[cli] No resources found.")};function _(e,t){try{const o=JSON.parse("string"!=typeof e?JSON.stringify(e):e);return"string"!=typeof o&&t?JSON.stringify(o):o}catch(e){return!1}}const C=e=>{if(null===e||"object"!=typeof e)return e;const t=Array.isArray(e)?[]:{};for(const o in e)Object.prototype.hasOwnProperty.call(e,o)&&(t[o]=C(e[o]));return t},I=(e,t)=>JSON.stringify(e,((e,o)=>("string"==typeof o&&((o=o.trim()).startsWith("function(")||o.startsWith("function ("))&&o.endsWith("}")&&(o=t?`EXP_FUN${(o+"").replaceAll(/\n|\t|\r/g," ")}EXP_FUN`:void 0),"function"==typeof o?`EXP_FUN${(o+"").replaceAll(/\n|\t|\r/g," ")}EXP_FUN`:o))).replaceAll(/"EXP_FUN|EXP_FUN"/g,"");function A(){console.log("Usage of CLI arguments:".bold,"\n------",`\nFor more detailed information visit readme at: ${"https://github.com/highcharts/node-export-server#readme".bold.yellow}.`);const e=t=>{for(const[o,r]of Object.entries(t))if(Object.prototype.hasOwnProperty.call(r,"value")){let e=` --${r.cliName||o} ${("<"+r.type+">").green} `;if(e.length<48)for(let t=e.length;t<48;t++)e+=".";console.log(e,r.description,`[Default: ${r.value.toString().bold}]`.blue)}else e(r)};Object.keys(w).forEach((t=>{["puppeteer","highcharts"].includes(t)||(console.log(`\n${t.toUpperCase()}`.red),e(w[t]))})),console.log("\n")}const $=e=>!["false","undefined","null","NaN","0",""].includes(e)&&!!e,j=(t,o)=>{if(t&&"string"==typeof t)return(t=t.trim()).endsWith(".js")?!!o&&j(e.readFileSync(t,"utf8")):t.startsWith("function()")||t.startsWith("function ()")||t.startsWith("()=>")||t.startsWith("() =>")?`(${t})()`:t.replace(/;$/,"")};var P=(e,t)=>{const o="Too many requests, you have been rate limited. Please try again later.",r={max:t.maxRequests||30,window:t.window||1,delay:t.delay||0,trustProxy:t.trustProxy||!1,skipKey:t.skipKey||!1,skipToken:t.skipToken||!1};r.trustProxy&&e.enable("trust proxy");const i=c({windowMs:60*r.window*1e3,max:r.max,delayMs:r.delay,handler:(e,t)=>{t.format({json:()=>{t.status(429).send({message:o})},default:()=>{t.status(429).send(o)}})},skip:e=>!1!==r.skipKey&&!1!==r.skipToken&&e.query.key===r.skipKey&&e.query.access_token===r.skipToken&&(H(4,"[rate-limiting] Skipping rate limiter."),!0)});e.use(i),H(3,E(`[rate-limiting] Enabled rate limiting: ${r.max} requests\n per ${r.window} minute per IP, trusting proxy:\n ${r.trustProxy}.`))};async function N(e,t={}){return new Promise(((o,r)=>{const i=(e=>e.startsWith("https")?a:s)(e);i.get(e,t,(e=>{let t="";e.on("data",(e=>{t+=e})),e.on("end",(()=>{t||r("Nothing was fetched from the URL."),e.text=t,o(e)}))})).on("error",(e=>{r(e)}))}))}l.config();const F=t.join(R,".cache"),U={cdnURL:"https://code.highcharts.com/",activeManifest:{},sources:"",hcVersion:""};let G=!1;const q=()=>U.hcVersion=U.sources.substr(0,U.sources.indexOf("*/")).replace("/*","").replace("*/","").replace(/\n/g,"").trim(),W=async(e,t)=>{try{e.endsWith(".js")&&(e=e.substring(0,e.length-3)),H(4,`[cache] Fetching script - ${e}.js`);const o=t?{agent:t,timeout:+process.env.PROXY_SERVER_TIMEOUT||5e3}:{},r=await N(`${e}.js`,o);if(200===r.statusCode)return r.text;throw`${r.statusCode}`}catch(t){throw H(1,`[cache] Error fetching script ${e}.js: ${t}.`),t}},M=async(t,o)=>{const{coreScripts:r,modules:i,indicators:n,scripts:s}=t,a="latest"!==t.version&&t.version?`${t.version}/`:"";H(3,"[cache] Updating cache to Highcharts ",a);const l=[...r.map((e=>`${a}${e}`)),...i.map((e=>"map"===e?`maps/${a}modules/${e}`:`${a}modules/${e}`)),...n.map((e=>`stock/${a}indicators/${e}`))];let c;const p=process.env.PROXY_SERVER_HOST,d=process.env.PROXY_SERVER_PORT;p&&d&&(c=new u({host:p,port:+d}));const h={};try{return U.sources=(await Promise.all([...l.map((async e=>{const o=await W(`${t.cdnURL||U.cdnURL}${e}`,c);return"string"==typeof o&&(h[e.replace(/(.*)\/|(.*)modules\/|stock\/(.*)indicators\/|maps\/(.*)modules\//gi,"")]=1),o})),...s.map((e=>W(e,c)))])).join(";\n"),q(),e.writeFileSync(o,U.sources),h}catch(e){H(1,"[cache] Unable to update local Highcharts cache.")}},D=async o=>{let r;const i=t.join(F,"manifest.json"),n=t.join(F,"sources.js");if(G=o,!e.existsSync(F)&&e.mkdirSync(F),!e.existsSync(i)||o.forceFetch)H(3,"[cache] Fetching and caching Highcharts dependencies."),r=await M(o,n);else{let t=!1;const s=JSON.parse(e.readFileSync(i));if(s.modules&&Array.isArray(s.modules)){const e={};s.modules.forEach((t=>e[t]=1)),s.modules=e}const{modules:a,coreScripts:l,indicators:c}=o,p=a.length+l.length+c.length;s.version!==o.version?(H(3,"[cache] Highcharts version mismatch in cache, need to re-fetch."),t=!0):Object.keys(s.modules||{}).length!==p?(H(3,"[cache] Cache and requested modules does not match, need to re-fetch."),t=!0):t=(o.modules||[]).some((e=>{if(!s.modules[e])return H(3,`[cache] The ${e} missing in cache, need to re-fetch.`),!0})),t?r=await M(o,n):(H(3,"[cache] Dependency cache is up to date, proceeding."),U.sources=e.readFileSync(n,"utf8"),r=s.modules,q())}await(async(o,r)=>{const i={version:o.version,modules:r||{}};U.activeManifest=i,H(4,"[cache] writing new manifest");try{e.writeFileSync(t.join(F,"manifest.json"),JSON.stringify(i),"utf8")}catch(e){H(1,`[cache] Error writing cache manifest: ${e}.`)}})(o,r)};var V=async e=>!!G&&await D(Object.assign(G,{version:e})),z=()=>U,J=()=>U.hcVersion;const K=f.randomBytes(64).toString("base64url"),X=m.join("tmp",`puppeteer-${K}`),B=[`--user-data-dir=${m.join(X,"profile")}`,"--autoplay-policy=user-gesture-required","--disable-background-networking","--disable-background-timer-throttling","--disable-backgrounding-occluded-windows","--disable-breakpad","--disable-client-side-phishing-detection","--disable-component-update","--disable-default-apps","--disable-dev-shm-usage","--disable-domain-reliability","--disable-extensions","--disable-features=AudioServiceOutOfProcess","--disable-hang-monitor","--disable-ipc-flooding-protection","--disable-notifications","--disable-offer-store-unmasked-wallet-cards","--disable-popup-blocking","--disable-print-preview","--disable-prompt-on-repost","--disable-renderer-backgrounding","--disable-session-crashed-bubble","--disable-setuid-sandbox","--disable-speech-api","--disable-sync","--hide-crash-restore-bubble","--hide-scrollbars","--ignore-gpu-blacklist","--metrics-recording-only","--mute-audio","--no-default-browser-check","--no-first-run","--no-pings","--no-sandbox","--no-zygote","--password-store=basic","--use-mock-keychain"],Y=b.fileURLToPath(new URL(".","undefined"==typeof document?require("url").pathToFileURL(__filename).href:v&&v.src||new URL("index.cjs",document.baseURI).href)),Q=e.readFileSync(Y+"/../templates/template.html","utf8");let Z;const ee=async e=>{await e.setContent(Q),await e.addScriptTag({path:Y+"/../.cache/sources.js"}),await e.evaluate((()=>window.setupHighcharts())),e.on("pageerror",(async t=>{H(1,"[page error]",t),await e.$eval("#container",((e,t)=>{window._displayErrors&&(e.innerHTML=t)}),`

Chart input data error

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

Chart input data error

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

Chart input data error

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