diff --git a/.env.sample b/.env.sample index aef40f74..52401fdc 100644 --- a/.env.sample +++ b/.env.sample @@ -1,5 +1,6 @@ # PUPPETEER CONFIG PUPPETEER_TEMP_DIR = ./tmp/ +PUPPETEER_LAUNCH_RETRY_WINDOW = 30000 # HIGHCHARTS CONFIG HIGHCHARTS_VERSION = latest @@ -29,6 +30,7 @@ SERVER_ENABLE = false SERVER_HOST = 0.0.0.0 SERVER_PORT = 7801 SERVER_MAX_UPLOAD_SIZE = 3 +SERVER_KEEP_ALIVE_TIMEOUT = 65000 SERVER_BENCHMARKING = false # SERVER PROXY CONFIG @@ -42,7 +44,6 @@ SERVER_PROXY_TIMEOUT = 5000 SERVER_RATE_LIMITING_ENABLE = false SERVER_RATE_LIMITING_MAX_REQUESTS = 10 SERVER_RATE_LIMITING_WINDOW = 1 -SERVER_RATE_LIMITING_DELAY = 0 SERVER_RATE_LIMITING_TRUST_PROXY = false SERVER_RATE_LIMITING_SKIP_KEY = SERVER_RATE_LIMITING_SKIP_TOKEN = @@ -57,6 +58,8 @@ SERVER_SSL_CERT_PATH = POOL_MIN_WORKERS = 4 POOL_MAX_WORKERS = 8 POOL_WORK_LIMIT = 40 +POOL_QUEUE_LIMIT = 0 +POOL_QUEUE_REJECT_DELAY = 500 POOL_ACQUIRE_TIMEOUT = 5000 POOL_CREATE_TIMEOUT = 5000 POOL_DESTROY_TIMEOUT = 5000 @@ -77,6 +80,7 @@ UI_ENABLE = true UI_ROUTE = / # OTHER CONFIG +OTHER_SHUTDOWN_DRAIN_TIMEOUT = 30000 OTHER_NODE_ENV = production OTHER_LISTEN_TO_PROCESS_EXITS = true OTHER_NO_LOGO = false diff --git a/.eslintignore b/.eslintignore deleted file mode 100644 index c052660a..00000000 --- a/.eslintignore +++ /dev/null @@ -1,2 +0,0 @@ -node_modules/ -dist/* diff --git a/.eslintrc.cjs b/.eslintrc.cjs deleted file mode 100644 index 3b8e2752..00000000 --- a/.eslintrc.cjs +++ /dev/null @@ -1,36 +0,0 @@ -module.exports = { - env: { - browser: true, - es2021: true, - node: true - }, - root: true, - parserOptions: { - ecmaVersion: 'latest', - sourceType: 'module' - }, - plugins: ['import', 'prettier'], - extends: [ - 'eslint:recommended', - 'plugin:import/recommended', - 'plugin:prettier/recommended' - ], - overrides: [ - { - files: ['*.test.js', '*.spec.js'], - env: { - jest: true - } - } - ], - rules: { - 'no-unused-vars': 0, - 'import/no-cycle': 2, - 'prettier/prettier': [ - 'error', - { - endOfLine: require('os').EOL === '\r\n' ? 'crlf' : 'lf' - } - ] - } -}; diff --git a/.github/workflows/build-and-push.yml b/.github/workflows/build-and-push.yml index 5eb3ef6a..3a14f8e4 100644 --- a/.github/workflows/build-and-push.yml +++ b/.github/workflows/build-and-push.yml @@ -16,7 +16,7 @@ jobs: - name: Set up Node.js uses: actions/setup-node@v4 with: - node-version: '18.x.x' + node-version: '24.x' - name: Install Dependencies run: npm ci diff --git a/.github/workflows/eslint-check.yml b/.github/workflows/eslint-check.yml index 4bf3adf6..92c8a6bb 100644 --- a/.github/workflows/eslint-check.yml +++ b/.github/workflows/eslint-check.yml @@ -12,7 +12,7 @@ jobs: - name: Set up Node.js uses: actions/setup-node@v4 with: - node-version: '18.x.x' + node-version: '24.x' - name: Install Dependencies run: npm install @@ -21,7 +21,7 @@ jobs: id: eslint continue-on-error: true run: | - ESLINT_OUTPUT=$(npx eslint . --ext .js,.jsx,.ts,.tsx) + ESLINT_OUTPUT=$(npx eslint .) echo "::set-output name=result::$ESLINT_OUTPUT" if [ -z "$ESLINT_OUTPUT" ]; then echo "ESLint found no issues :white_check_mark:" diff --git a/.github/workflows/unit-tests.yml b/.github/workflows/unit-tests.yml index e8142dbe..6db627a8 100644 --- a/.github/workflows/unit-tests.yml +++ b/.github/workflows/unit-tests.yml @@ -8,6 +8,13 @@ jobs: testing: runs-on: ubuntu-latest + strategy: + fail-fast: false + matrix: + node-version: [ '22.x', '24.x' ] + + name: Unit tests (Node ${{ matrix.node-version }}) + steps: - name: Checkout Repository uses: actions/checkout@v6 @@ -15,7 +22,7 @@ jobs: - name: Set up Node.js uses: actions/setup-node@v4 with: - node-version: '18.x.x' + node-version: ${{ matrix.node-version }} - name: Install dependencies run: npm ci diff --git a/.lintstagedrc b/.lintstagedrc deleted file mode 100644 index 9893929d..00000000 --- a/.lintstagedrc +++ /dev/null @@ -1,3 +0,0 @@ -{ - "lib/**/\*.{js,json}": ["eslint", "npx prettier --write"] -} diff --git a/CHANGELOG.md b/CHANGELOG.md index bba5c2fe..565876c7 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,3 +1,48 @@ +# 6.0.0 + +_Breaking Changes:_ + +- Raised the minimum supported Node.js version to `^22.22.2 || ^24.15.0 || >=26.0.0`. Node.js 18 and 20 are end of life; both 22 and 24 are tested. +- Updated Puppeteer from 22 to 25, which advances the bundled Chrome several major versions. Chart layout was verified unchanged, though font hinting and antialiasing may differ slightly, as with any browser update. +- Updated Express from 4 to 5. This matters to anyone mounting their own middleware or routes onto the exported Express app, as route matching, `request.query` and the `extended` default of `express.urlencoded` all changed upstream. +- Updated Highcharts from 12 to 13, which only affects deployments using the `useNpm` option. Where x axis labels are rotated, the leftmost label no longer extends to the very edge of the image. +- Updated jsdom from 24 to 30, Multer from 1 to 2, zod from 3 to 4, `uuid` from 10 to 14, `dotenv` from 16 to 17, `https-proxy-agent` from 7 to 9 and `express-rate-limit` from 7 to 8. +- Removed the `delay`/`SERVER_RATE_LIMITING_DELAY` rate limiting option, which has had no effect since `express-rate-limit` v7. +- Exports now queue only up to `queueLimit`, 32 by default, and requests arriving beyond it are refused rather than queued. The queue was previously unbounded, so a saturated server accepted far more work than it could complete and then failed most of it on timeout. +- Changed the default `keepAliveTimeout` from the Node.js default of 5 seconds to 65. A value below the idle timeout of a proxy in front of the server causes sporadic gateway errors, as the proxy sends requests into connections the server has just closed. + +_Fixes:_ + +- Fixed the server never recovering when the browser process died, for example after being killed by an out of memory reaper. The browser was launched once at startup and could never be relaunched, so every export failed from that point on while the pool continued to report healthy workers. +- Fixed exports whose client had already disconnected still taking a place in the queue and then a worker. An export already being rendered still runs to completion, as the underlying browser operations cannot be cancelled. +- Fixed a clean client disconnect, such as a proxy idle timeout, not being detected as an abandoned request. Only a socket closing with an error was. +- Fixed a rasterization timeout leaking the page it timed out on, leaving a renderer process behind for the lifetime of the browser. +- Fixed the browser process being able to outlive the call that closes it, leaving it holding Chrome's lock on the user data directory and preventing any later browser from starting. +- Fixed a browser page being left open when configuring it failed, which leaked one on every retry. +- Fixed a worker's page being handed to the next export before it had finished being cleared. +- Fixed the shutdown sequence exiting before the HTTP servers had closed, which dropped exports still being served on every restart, deployment and scale-in. +- Fixed the reported export success ratio counting exports abandoned by their client as failures. +- Fixed the rasterization timeout keeping its timer alive after a successful image export. +- Fixed error responses being able to carry a status outside the 1xx to 4xx range, which could happen when the status came from a wrapped error. +- Fixed the `PUPPETEER_TEMP_DIR` validation message never being shown. +- Removed a call that stripped every `close` listener from the request socket, including those belonging to Node.js and Express. + +_New Features:_ + +- Added the `POOL_QUEUE_LIMIT`/`--queueLimit`/`queueLimit` option, capping how many exports may wait for a worker, defaulting to four times `maxWorkers`. Requests beyond the limit are refused before their body is parsed. +- Added the `POOL_QUEUE_REJECT_DELAY`/`--queueRejectDelay`/`queueRejectDelay` option, defaulting to 500ms, being how long the server waits before refusing a request for capacity. This is deliberate backpressure, as answering instantly lets clients that retry immediately consume the event loop being refused. +- Added the `SERVER_KEEP_ALIVE_TIMEOUT`/`--keepAliveTimeout`/`keepAliveTimeout` option, defaulting to 65 seconds. The `headersTimeout` is kept 5 seconds above it automatically. +- Added the `OTHER_SHUTDOWN_DRAIN_TIMEOUT`/`--shutdownDrainTimeout`/`shutdownDrainTimeout` option, defaulting to 30 seconds, bounding how long a shutdown lets exports already being served finish. +- Added the `PUPPETEER_LAUNCH_RETRY_WINDOW`/`--launchRetryWindow`/`launchRetryWindow` option, defaulting to 30 seconds, bounding how long a browser launch is retried before being reported as failed. +- Added an `errorCode` property to error responses, so a request refused for capacity can be told apart from one refused as malformed. The codes are `EXPORT_INVALID_REQUEST`, `EXPORT_QUEUE_FULL`, `EXPORT_ACQUIRE_TIMEOUT`, `EXPORT_RASTERIZATION_TIMEOUT` and `EXPORT_FAILED`. +- Added `browserConnected`, `consecutiveCreateFailures`, `abandonedExports` and `rejectedForCapacity` to the `/health` response. Existing properties are unchanged. + +_Enhancements:_ + +- Reduced the cost of sanitizing incoming SVGs by around ten times, by reusing the DOM and purifier between requests instead of building them on every export. +- Reduced the time taken to report a browser that cannot be launched, from around 100 seconds to a configurable window defaulting to 30, using growing delays with jitter. +- Added tooling for load, saturation, page isolation, browser recovery and render layout comparison tests, in the `tests/other` folder. + # 5.1.0 _New Features:_ diff --git a/README.md b/README.md index a5839cd1..85254c16 100644 --- a/README.md +++ b/README.md @@ -6,6 +6,22 @@ Convert Highcharts.JS charts into static image files. ## Upgrade Notes +## v5.x.x to v6.x.x + +There are several breaking changes in v6.x.x: + +- `Node.js v22.22.2` or higher is now required. Node.js 18 and 20 are end of life, and the dependencies in this release no longer support them. Node.js 24 is the recommended target. +- Exports now queue only up to a limit, `32` with default pool settings, and requests arriving beyond it are refused rather than queued. Configure with `POOL_QUEUE_LIMIT`/`--queueLimit`/`queueLimit`, and see the note about worker count below. +- Idle connections are now held open for 65 seconds rather than the 5 second Node.js default, which avoids sporadic gateway errors behind a proxy. Configure with `SERVER_KEEP_ALIVE_TIMEOUT`/`--keepAliveTimeout`/`keepAliveTimeout`. +- The `delay`/`SERVER_RATE_LIMITING_DELAY` rate limiting option has been removed, as it has had no effect since `express-rate-limit` v7. +- Express has been updated from v4 to v5. This only matters if you mount your own middleware or routes onto the Express app returned by `server.getApp()`, as route matching and some request properties changed upstream. +- Highcharts has been updated from v12 to v13, which only affects deployments using the `useNpm` option. Everywhere else the runtime version is chosen by `HIGHCHARTS_VERSION` and fetched from the CDN. +- Puppeteer has been updated from v22 to v25, advancing the bundled browser several major versions. Chart layout is unchanged, but font hinting and antialiasing may differ slightly. + +If you build your own container image, note that a newer Puppeteer needs `unzip` present to install its browser, and that the browser process needs to be able to use its sandbox. + +For other changes and fixes, please see the [changelog](CHANGELOG.md). + ## v4.x.x to v5.x.x There are two breaking changes in v5.x.x: @@ -145,6 +161,7 @@ The format, along with its default values, is as follows (using the recommended { "puppeteer": { "args": [], + "launchRetryWindow": 30000, "tempDir": "./tmp/" }, "highcharts": { @@ -263,6 +280,7 @@ The format, along with its default values, is as follows (using the recommended "port": 7801, "benchmarking": false, "maxUploadSize": 3, + "keepAliveTimeout": 65000, "proxy": { "host": "", "port": 8080, @@ -274,7 +292,6 @@ The format, along with its default values, is as follows (using the recommended "enable": false, "maxRequests": 10, "window": 1, - "delay": 0, "trustProxy": false, "skipKey": "", "skipToken": "" @@ -290,6 +307,8 @@ The format, along with its default values, is as follows (using the recommended "minWorkers": 4, "maxWorkers": 8, "workLimit": 40, + "queueLimit": 0, + "queueRejectDelay": 500, "acquireTimeout": 5000, "createTimeout": 5000, "destroyTimeout": 5000, @@ -314,7 +333,8 @@ The format, along with its default values, is as follows (using the recommended "listenToProcessExits": true, "noLogo": false, "hardResetPage": false, - "browserShellMode": true + "browserShellMode": true, + "shutdownDrainTimeout": 30000 }, "debug": { "enable": false, @@ -339,6 +359,7 @@ These variables are set in your environment and take precedence over options fro ### Puppeteer Config - `PUPPETEER_TEMP_DIR`: The directory for Puppeteer to store temporary files (defaults to `./tmp/`). +- `PUPPETEER_LAUNCH_RETRY_WINDOW`: The duration, in milliseconds, to keep retrying a browser launch before reporting it as failed (defaults to `30000`). ### Highcharts Config @@ -373,6 +394,7 @@ These variables are set in your environment and take precedence over options fro - `SERVER_PORT`: The port to be used for the server when enabled (defaults to `7801`). - `SERVER_BENCHMARKING`: Indicates whether to display a message with the duration, in milliseconds, of specific actions that occur on the server while serving a request (defaults to `false`). - `SERVER_MAX_UPLOAD_SIZE`: The maximum size, in MB, of files uploaded through the server (defaults to `3`). +- `SERVER_KEEP_ALIVE_TIMEOUT`: The duration, in milliseconds, an idle keep-alive connection is held open. Keep this above the idle timeout of any proxy in front of the server (defaults to `65000`). ### Server Proxy Config @@ -387,7 +409,6 @@ These variables are set in your environment and take precedence over options fro - `SERVER_RATE_LIMITING_ENABLE`: Enables rate limiting for the server (defaults to `false`). - `SERVER_RATE_LIMITING_MAX_REQUESTS`: The maximum number of requests allowed in one minute (defaults to `10`). - `SERVER_RATE_LIMITING_WINDOW`: The time window, in minutes, for the rate limiting (defaults to `1`). -- `SERVER_RATE_LIMITING_DELAY`: The delay duration for each successive request before reaching the maximum limit (defaults to `0`). - `SERVER_RATE_LIMITING_TRUST_PROXY`: Set this to **true** if the server is behind a load balancer (defaults to `false`). - `SERVER_RATE_LIMITING_SKIP_KEY`: Allows bypassing the rate limiter and should be provided with the `skipToken` argument (defaults to ``). - `SERVER_RATE_LIMITING_SKIP_TOKEN`: Allows bypassing the rate limiter and should be provided with the `skipKey` argument (defaults to ``). @@ -404,6 +425,8 @@ These variables are set in your environment and take precedence over options fro - `POOL_MIN_WORKERS`: The number of minimum and initial pool workers to spawn (defaults to `4`). - `POOL_MAX_WORKERS`: The number of maximum pool workers to spawn (defaults to `8`). - `POOL_WORK_LIMIT`: The number of work pieces that can be performed before restarting the worker process (defaults to `40`). +- `POOL_QUEUE_LIMIT`: The maximum number of exports allowed to wait for a worker. Requests beyond this are refused rather than queued. Set to `0` to derive it as four times `maxWorkers` (defaults to `0`). +- `POOL_QUEUE_REJECT_DELAY`: The duration, in milliseconds, to wait before refusing a request for capacity. This is deliberate backpressure; set to `0` to refuse immediately (defaults to `500`). - `POOL_ACQUIRE_TIMEOUT`: The duration, in milliseconds, to wait for acquiring a resource (defaults to `5000`). - `POOL_CREATE_TIMEOUT`: The duration, in milliseconds, to wait for creating a resource (defaults to `5000`). - `POOL_DESTROY_TIMEOUT`: The duration, in milliseconds, to wait for destroying a resource (defaults to `5000`). @@ -433,6 +456,7 @@ These variables are set in your environment and take precedence over options fro - `OTHER_HARD_RESET_PAGE`: Determines whether the page's content should be reset from scratch, including Highcharts scripts (defaults to `false`). - `OTHER_BROWSER_SHELL_MODE`: Decides whether to enable older but much more performant _shell_ mode for the browser (defaults to `true`). - `OTHER_ALLOW_XLINK`: If set to true, allow `xlink:href` in incoming SVG (defaults to `false`). +- `OTHER_SHUTDOWN_DRAIN_TIMEOUT`: The duration, in milliseconds, a shutdown lets exports already being served finish. Set it above the deregistration delay of anything routing traffic to the server (defaults to `30000`). ### Debugging Config - `DEBUG_ENABLE`: Enables or disables debug mode for the underlying browser (defaults to `false`). @@ -450,6 +474,7 @@ To supply command line arguments, add them as flags when running the application _Available options:_ +- `--launchRetryWindow`: The duration, in milliseconds, to keep retrying a browser launch before reporting it as failed (defaults to `30000`). - `--useNpm`: The flag that determines whether to use Highcharts scripts from CDN or NPM package (defaults to `false`). - `--infile`: The input file should include a name and a type (**.json** or **.svg**) and must be a correctly formatted JSON or SVG file (defaults to `false`). - `--instr`: An input in a form of a stringified JSON or SVG file. Overrides the `--infile` option (defaults to `false`). @@ -475,6 +500,7 @@ _Available options:_ - `--host`: The hostname of the server. Additionally, it starts a server listening on the provided hostname (defaults to `0.0.0.0`). - `--port`: The port to be used for the server when enabled (defaults to `7801`). - `--maxUploadSize`: The maximum size, in MB, of files uploaded through the server (defaults to `3`). +- `--keepAliveTimeout`: The duration, in milliseconds, an idle keep-alive connection is held open. Keep this above the idle timeout of any proxy in front of the server (defaults to `65000`). - `--serverBenchmarking`: Indicates whether to display the duration, in milliseconds, of specific actions that occur on the server while serving a request (defaults to `false`). - `--proxyHost`: The host of the proxy server to use, if it exists (defaults to `false`). - `--proxyPort`: The port of the proxy server to use, if it exists (defaults to `false`). @@ -484,7 +510,6 @@ _Available options:_ - `--enableRateLimiting`: Enables rate limiting for the server (defaults to `false`). - `--maxRequests`: The maximum number of requests allowed in one minute (defaults to `10`). - `--window`: The time window, in minutes, for the rate limiting (defaults to `1`). -- `--delay`: The delay duration for each successive request before reaching the maximum limit (defaults to `0`). - `--trustProxy`: Set this to **true** if the server is behind a load balancer (defaults to `false`). - `--skipKey`: Allows bypassing the rate limiter and should be provided with the `--skipToken` argument (defaults to ``). - `--skipToken`: Allows bypassing the rate limiter and should be provided with the `--skipKey` argument (defaults to ``). @@ -495,6 +520,8 @@ _Available options:_ - `--minWorkers`: The number of minimum and initial pool workers to spawn (defaults to `4`). - `--maxWorkers`: The number of maximum pool workers to spawn (defaults to `8`). - `--workLimit`: The number of work pieces that can be performed before restarting the worker process (defaults to `40`). +- `--queueLimit`: The maximum number of exports allowed to wait for a worker. Requests beyond this are refused rather than queued. Set to `0` to derive it as four times `maxWorkers` (defaults to `0`). +- `--queueRejectDelay`: The duration, in milliseconds, to wait before refusing a request for capacity. Set to `0` to refuse immediately (defaults to `500`). - `--acquireTimeout`: The duration, in milliseconds, to wait for acquiring a resource (defaults to `5000`). - `--createTimeout`: The duration, in milliseconds, to wait for creating a resource (defaults to `5000`). - `--destroyTimeout`: The duration, in milliseconds, to wait for destroying a resource (defaults to `5000`). @@ -514,6 +541,7 @@ _Available options:_ - `--noLogo`: Skip printing the logo on a startup. Will be replaced by a simple text (defaults to `false`). - `--hardResetPage`: Determines whether the page's content should be reset from scratch, including Highcharts scripts (defaults to `false`). - `--browserShellMode`: Decides whether to enable older but much more performant _shell_ mode for the browser (defaults to `true`). +- `--shutdownDrainTimeout`: The duration, in milliseconds, a shutdown lets exports already being served finish. Set it above the deregistration delay of anything routing traffic to the server (defaults to `30000`). - `--enableDebug`: Enables or disables debug mode for the underlying browser (defaults to `false`). - `--headless`: Controls the mode in which the browser is launched when in the debug mode (defaults to `true`). - `--devtools`: Decides whether to enable DevTools when the browser is in a headful state (defaults to `false`). @@ -572,6 +600,16 @@ CORS is enabled for the server. It is recommended to run the server using [pm2](https://www.npmjs.com/package/pm2) unless running in a managed environment/container. Please refer to the pm2 documentation for details on how to set this up. +## Error Responses + +A request that cannot be served is answered with a JSON body containing `statusCode`, `message` and, where one applies, an `errorCode`. The server does not respond with 5xx status codes, so the `errorCode` is what distinguishes a request that was refused because the server was busy from one that was refused because it was malformed: + +- `EXPORT_INVALID_REQUEST`: The request itself was not usable, such as a missing body or no chart data. Retrying it unchanged will not help. +- `EXPORT_QUEUE_FULL`: The server was already holding as many queued exports as it is willing to and refused this one without starting work on it. Retrying later, with backoff, is appropriate. +- `EXPORT_ACQUIRE_TIMEOUT`: No worker became available in time. The same meaning for a caller as `EXPORT_QUEUE_FULL`. +- `EXPORT_RASTERIZATION_TIMEOUT`: The chart was too large or complex to render in the allotted time. +- `EXPORT_FAILED`: The export failed for some other reason. + ## Available Endpoints - POST @@ -581,7 +619,7 @@ It is recommended to run the server using [pm2](https://www.npmjs.com/package/pm - GET - `/`: An endpoint to perform exports through the user interface the server allows it. - - `/health`: An endpoint for outputting basic statistics for the server. + - `/health`: An endpoint for outputting basic statistics for the server. Alongside the export counts it reports `browserConnected`, `consecutiveCreateFailures`, `abandonedExports` and `rejectedForCapacity`, which distinguish a server that has lost its browser from one that is merely busy or being abandoned by its callers. ## Switching Highcharts Version at Runtime @@ -817,7 +855,9 @@ If `--resources` argument is not set and a file named `resources.json` exists in 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 `--minWorkers` and `--maxWorkers` options, and should be tweaked to fit the hardware on which you are running the server. -It is 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`. +It is 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/load_test.js` script can be used to drive concurrent load at the server and report throughput, latency percentiles and the peak number of exports waiting for a worker. It expects the server to be running on port `7801`. + +Beyond the pool size, `--queueLimit` caps how many exports may wait for a worker, and requests arriving beyond it are refused rather than queued. Throughput does not improve past the pool size, so a deeper queue only adds latency and memory use; the default of four times `maxWorkers` is a reasonable starting point, and lowering it sheds load sooner. Refusals are delayed by `--queueRejectDelay` on purpose, since answering instantly lets clients that retry immediately consume the server refusing them. 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. diff --git a/dist/index.cjs b/dist/index.cjs index 24a4ad8b..a85563c0 100644 --- a/dist/index.cjs +++ b/dist/index.cjs @@ -1,2 +1,2 @@ -"use strict";require("colors");var e=require("fs"),t=require("path"),r=require("https-proxy-agent"),o=require("prompts"),i=require("dotenv"),s=require("zod"),n=require("module"),a=require("url"),l=require("http"),c=require("https"),p=require("tarn"),h=require("uuid"),u=require("puppeteer"),d=require("jsdom"),g=require("dompurify"),m=require("cors"),f=require("express"),v=require("multer"),y=require("express-rate-limit"),b="undefined"!=typeof document?document.currentScript:null;const w={core:["highcharts","highcharts-more","highcharts-3d"],modules:["stock","map","gantt","exporting","parallel-coordinates","accessibility","boost-canvas","boost","data","data-tools","draggable-points","static-scale","broken-axis","heatmap","tilemap","tiledwebmap","timeline","treemap","treegraph","item-series","drilldown","histogram-bellcurve","bullet","funnel","funnel3d","geoheatmap","pyramid3d","networkgraph","pareto","pattern-fill","pictorial","price-indicator","sankey","arc-diagram","dependency-wheel","series-label","series-on-point","solid-gauge","sonification","streamgraph","sunburst","variable-pie","variwide","vector","venn","windbarb","wordcloud","xrange","no-data-to-display","drag-panes","debugger","dumbbell","lollipop","cylinder","organization","dotplot","marker-clusters","hollowcandlestick","heikinashi","flowmap","export-data","navigator","textpath"],indicators:["indicators-all"],custom:["https://cdnjs.cloudflare.com/ajax/libs/moment.js/2.30.1/moment.min.js","https://cdnjs.cloudflare.com/ajax/libs/moment-timezone/0.5.45/moment-timezone-with-data.min.js"]},E={puppeteer:{args:{value:["--allow-running-insecure-content","--ash-no-nudges","--autoplay-policy=user-gesture-required","--block-new-web-contents","--disable-accelerated-2d-canvas","--disable-background-networking","--disable-background-timer-throttling","--disable-backgrounding-occluded-windows","--disable-breakpad","--disable-checker-imaging","--disable-client-side-phishing-detection","--disable-component-extensions-with-background-pages","--disable-component-update","--disable-default-apps","--disable-dev-shm-usage","--disable-domain-reliability","--disable-extensions","--disable-features=CalculateNativeWinOcclusion,InterestFeedContentSuggestions,WebOTP","--disable-hang-monitor","--disable-ipc-flooding-protection","--disable-logging","--disable-notifications","--disable-offer-store-unmasked-wallet-cards","--disable-popup-blocking","--disable-print-preview","--disable-prompt-on-repost","--disable-renderer-backgrounding","--disable-search-engine-choice-screen","--disable-session-crashed-bubble","--disable-setuid-sandbox","--disable-site-isolation-trials","--disable-speech-api","--disable-sync","--enable-unsafe-webgpu","--hide-crash-restore-bubble","--hide-scrollbars","--metrics-recording-only","--mute-audio","--no-default-browser-check","--no-first-run","--no-pings","--pipe","--no-startup-window","--password-store=basic","--process-per-tab","--use-mock-keychain"],type:"string[]",description:"Arguments array to send to Puppeteer."},tempDir:{value:"./tmp/",type:"string",envLink:"PUPPETEER_TEMP_DIR",description:"The directory for Puppeteer to store temporary files."}},highcharts:{version:{value:"latest",type:"string",envLink:"HIGHCHARTS_VERSION",description:"The Highcharts version to be used."},cdnURL:{value:"https://code.highcharts.com/",type:"string",envLink:"HIGHCHARTS_CDN_URL",description:"The CDN URL for Highcharts scripts to be used."},useNpm:{value:!1,type:"boolean",envLink:"HIGHCHARTS_USE_NPM",description:"Flag to use Highcharts scripts from NPM package"},coreScripts:{value:w.core,type:"string[]",envLink:"HIGHCHARTS_CORE_SCRIPTS",description:"The core Highcharts scripts to fetch."},moduleScripts:{value:w.modules,type:"string[]",envLink:"HIGHCHARTS_MODULE_SCRIPTS",description:"The modules of Highcharts to fetch."},indicatorScripts:{value:w.indicators,type:"string[]",envLink:"HIGHCHARTS_INDICATOR_SCRIPTS",description:"The indicators of Highcharts to fetch."},customScripts:{value:w.custom,type:"string[]",description:"Additional custom scripts or dependencies to fetch."},forceFetch:{value:!1,type:"boolean",envLink:"HIGHCHARTS_FORCE_FETCH",description:"The flag to determine whether to refetch all scripts after each server rerun."},cachePath:{value:".cache",type:"string",envLink:"HIGHCHARTS_CACHE_PATH",description:"The path to the cache directory. It is used to store the Highcharts scripts and custom scripts."}},export:{infile:{value:!1,type:"string",description:"The input file should include a name and a type (json or svg). It must be correctly formatted as a JSON or SVG file."},instr:{value:!1,type:"string",description:"Input, provided in the form of a stringified JSON or SVG file, will override the --infile option."},options:{value:!1,type:"string",description:"An alias for the --instr option."},outfile:{value:!1,type:"string",description:"The output filename along with a type (jpeg, png, pdf, or svg). This will ignore the --type flag."},type:{value:"png",type:"string",envLink:"EXPORT_TYPE",description:"The file export format. It can be jpeg, png, pdf, or svg."},constr:{value:"chart",type:"string",envLink:"EXPORT_CONSTR",description:"The constructor to use. Can be chart, stockChart, mapChart, or ganttChart."},defaultHeight:{value:400,type:"number",envLink:"EXPORT_DEFAULT_HEIGHT",description:"the default height of the exported chart. Used when no value is set."},defaultWidth:{value:600,type:"number",envLink:"EXPORT_DEFAULT_WIDTH",description:"The default width of the exported chart. Used when no value is set."},defaultScale:{value:1,type:"number",envLink:"EXPORT_DEFAULT_SCALE",description:"The default scale of the exported chart. Used when no value is set."},height:{value:!1,type:"number",description:"The height of the exported chart, overriding the option in the chart settings."},width:{value:!1,type:"number",description:"The width of the exported chart, overriding the option in the chart settings."},scale:{value:!1,type:"number",description:"The scale of the exported chart, overriding the option in the chart settings. Ranges between 0.1 and 5.0."},globalOptions:{value:!1,type:"string",description:"Either a stringified JSON or a filename containing options to be passed into the Highcharts.setOptions."},themeOptions:{value:!1,type:"string",description:"Either a stringified JSON or a filename containing theme options to be passed into the Highcharts.setOptions."},batch:{value:!1,type:"string",description:'Initiates a batch job with a string containing input/output pairs: "in=out;in=out;...".'},rasterizationTimeout:{value:1500,type:"number",envLink:"EXPORT_RASTERIZATION_TIMEOUT",description:"The duration in milliseconds to wait for rendering a webpage."}},customLogic:{allowCodeExecution:{value:!1,type:"boolean",envLink:"CUSTOM_LOGIC_ALLOW_CODE_EXECUTION",description:"Controls whether the execution of arbitrary code is allowed during the exporting process."},allowFileResources:{value:!1,type:"boolean",envLink:"CUSTOM_LOGIC_ALLOW_FILE_RESOURCES",description:"Controls the ability to inject resources from the filesystem. This setting has no effect when running as a server."},customCode:{value:!1,type:"string",description:"Custom code to execute before chart initialization. It can be a function, code wrapped within a function, or a filename with the .js extension."},callback:{value:!1,type:"string",description:"JavaScript code to run during construction. It can be a function or a filename with the .js extension."},resources:{value:!1,type:"string",description:"Additional resource in the form of a stringified JSON, which may contain files, js, and css sections."},loadConfig:{value:!1,type:"string",legacyName:"fromFile",description:"A file containing a pre-defined configuration to use."},createConfig:{value:!1,type:"string",description:"Enables setting options through a prompt and saving them in a provided config file."}},server:{maxUploadSize:{value:3,type:"number",envLink:"SERVER_MAX_UPLOAD_SIZE",description:"The maximum upload size, in MB, for the server."},enable:{value:!1,type:"boolean",envLink:"SERVER_ENABLE",cliName:"enableServer",description:"When set to true, the server starts on the local IP address 0.0.0.0."},host:{value:"0.0.0.0",type:"string",envLink:"SERVER_HOST",description:"The hostname of the server. Additionally, it starts a server on the provided hostname."},port:{value:7801,type:"number",envLink:"SERVER_PORT",description:"The server port when enabled."},benchmarking:{value:!1,type:"boolean",envLink:"SERVER_BENCHMARKING",cliName:"serverBenchmarking",description:"Indicates whether to display the duration, in milliseconds, of specific actions that occur on the server while serving a request."},proxy:{host:{value:!1,type:"string",envLink:"SERVER_PROXY_HOST",cliName:"proxyHost",description:"The host of the proxy server to use, if it exists."},port:{value:8080,type:"number",envLink:"SERVER_PROXY_PORT",cliName:"proxyPort",description:"The port of the proxy server to use, if it exists."},username:{value:!1,type:"string",envLink:"SERVER_PROXY_USERNAME",cliName:"proxyUsername",description:"The username for the proxy server, if it exists."},password:{value:!1,type:"string",envLink:"SERVER_PROXY_PASSWORD",cliName:"proxyPassword",description:"The password for the proxy server, if it exists."},timeout:{value:5e3,type:"number",envLink:"SERVER_PROXY_TIMEOUT",cliName:"proxyTimeout",description:"The timeout for the proxy server to use, if it exists."}},rateLimiting:{enable:{value:!1,type:"boolean",envLink:"SERVER_RATE_LIMITING_ENABLE",cliName:"enableRateLimiting",description:"Enables rate limiting for the server."},maxRequests:{value:10,type:"number",envLink:"SERVER_RATE_LIMITING_MAX_REQUESTS",legacyName:"rateLimit",description:"The maximum number of requests allowed in one minute."},window:{value:1,type:"number",envLink:"SERVER_RATE_LIMITING_WINDOW",description:"The time window, in minutes, for the rate limiting."},delay:{value:0,type:"number",envLink:"SERVER_RATE_LIMITING_DELAY",description:"The delay duration for each successive request before reaching the maximum limit."},trustProxy:{value:!1,type:"boolean",envLink:"SERVER_RATE_LIMITING_TRUST_PROXY",description:"Set this to true if the server is behind a load balancer."},skipKey:{value:!1,type:"string",envLink:"SERVER_RATE_LIMITING_SKIP_KEY",description:"Allows bypassing the rate limiter and should be provided with the skipToken argument."},skipToken:{value:!1,type:"string",envLink:"SERVER_RATE_LIMITING_SKIP_TOKEN",description:"Allows bypassing the rate limiter and should be provided with the skipKey argument."}},ssl:{enable:{value:!1,type:"boolean",envLink:"SERVER_SSL_ENABLE",cliName:"enableSsl",description:"Enables or disables the SSL protocol."},force:{value:!1,type:"boolean",envLink:"SERVER_SSL_FORCE",cliName:"sslForce",legacyName:"sslOnly",description:"When set to true, the server is forced to serve only over HTTPS."},port:{value:443,type:"number",envLink:"SERVER_SSL_PORT",cliName:"sslPort",description:"The port on which to run the SSL server."},certPath:{value:!1,type:"string",envLink:"SERVER_SSL_CERT_PATH",legacyName:"sslPath",description:"The path to the SSL certificate/key file."}}},pool:{minWorkers:{value:4,type:"number",envLink:"POOL_MIN_WORKERS",description:"The number of minimum and initial pool workers to spawn."},maxWorkers:{value:8,type:"number",envLink:"POOL_MAX_WORKERS",legacyName:"workers",description:"The number of maximum pool workers to spawn."},workLimit:{value:40,type:"number",envLink:"POOL_WORK_LIMIT",description:"The number of work pieces that can be performed before restarting the worker process."},acquireTimeout:{value:5e3,type:"number",envLink:"POOL_ACQUIRE_TIMEOUT",description:"The duration, in milliseconds, to wait for acquiring a resource."},createTimeout:{value:5e3,type:"number",envLink:"POOL_CREATE_TIMEOUT",description:"The duration, in milliseconds, to wait for creating a resource."},destroyTimeout:{value:5e3,type:"number",envLink:"POOL_DESTROY_TIMEOUT",description:"The duration, in milliseconds, to wait for destroying a resource."},idleTimeout:{value:3e4,type:"number",envLink:"POOL_IDLE_TIMEOUT",description:"The duration, in milliseconds, after which an idle resource is destroyed."},createRetryInterval:{value:200,type:"number",envLink:"POOL_CREATE_RETRY_INTERVAL",description:"The duration, in milliseconds, to wait before retrying the create process in case of a failure."},reaperInterval:{value:1e3,type:"number",envLink:"POOL_REAPER_INTERVAL",description:"The duration, in milliseconds, after which the check for idle resources to destroy is triggered."},benchmarking:{value:!1,type:"boolean",envLink:"POOL_BENCHMARKING",cliName:"poolBenchmarking",description:"Indicate whether to show statistics for the pool of resources or not."}},logging:{level:{value:4,type:"number",envLink:"LOGGING_LEVEL",cliName:"logLevel",description:"The logging level to be used."},file:{value:"highcharts-export-server.log",type:"string",envLink:"LOGGING_FILE",cliName:"logFile",description:"The name of a log file. The `logToFile` and `logDest` options also need to be set to enable file logging."},dest:{value:"log/",type:"string",envLink:"LOGGING_DEST",cliName:"logDest",description:"The path to store log files. The `logToFile` option also needs to be set to enable file logging."},toConsole:{value:!0,type:"boolean",envLink:"LOGGING_TO_CONSOLE",cliName:"logToConsole",description:"Enables or disables showing logs in the console."},toFile:{value:!0,type:"boolean",envLink:"LOGGING_TO_FILE",cliName:"logToFile",description:"Enables or disables creation of the log directory and saving the log into a .log file."}},ui:{enable:{value:!1,type:"boolean",envLink:"UI_ENABLE",cliName:"enableUi",description:"Enables or disables the user interface (UI) for the export server."},route:{value:"/",type:"string",envLink:"UI_ROUTE",cliName:"uiRoute",description:"The endpoint route to which the user interface (UI) should be attached."}},other:{nodeEnv:{value:"production",type:"string",envLink:"OTHER_NODE_ENV",description:"The type of Node.js environment."},listenToProcessExits:{value:!0,type:"boolean",envLink:"OTHER_LISTEN_TO_PROCESS_EXITS",description:"Decides whether or not to attach process.exit handlers."},noLogo:{value:!1,type:"boolean",envLink:"OTHER_NO_LOGO",description:"Skip printing the logo on a startup. Will be replaced by a simple text."},hardResetPage:{value:!1,type:"boolean",envLink:"OTHER_HARD_RESET_PAGE",description:"Decides if the page content should be reset entirely."},browserShellMode:{value:!0,type:"boolean",envLink:"OTHER_BROWSER_SHELL_MODE",description:"Decides if the browser runs in the shell mode."}},debug:{enable:{value:!1,type:"boolean",envLink:"DEBUG_ENABLE",cliName:"enableDebug",description:"Enables or disables debug mode for the underlying browser."},headless:{value:!0,type:"boolean",envLink:"DEBUG_HEADLESS",description:"Controls the mode in which the browser is launched when in the debug mode."},devtools:{value:!1,type:"boolean",envLink:"DEBUG_DEVTOOLS",description:"Decides whether to enable DevTools when the browser is in a headful state."},listenToConsole:{value:!1,type:"boolean",envLink:"DEBUG_LISTEN_TO_CONSOLE",description:"Decides whether to enable a listener for console messages sent from the browser."},dumpio:{value:!1,type:"boolean",envLink:"DEBUG_DUMPIO",description:"Redirects browser process stdout and stderr to process.stdout and process.stderr."},slowMo:{value:0,type:"number",envLink:"DEBUG_SLOW_MO",description:"Slows down Puppeteer operations by the specified number of milliseconds."},debuggingPort:{value:9222,type:"number",envLink:"DEBUG_DEBUGGING_PORT",description:"Specifies the debugging port."}}},T={puppeteer:[{type:"list",name:"args",message:"Puppeteer arguments",initial:E.puppeteer.args.value.join(","),separator:","}],highcharts:[{type:"text",name:"version",message:"Highcharts version",initial:E.highcharts.version.value},{type:"text",name:"cdnURL",message:"The URL of CDN",initial:E.highcharts.cdnURL.value},{type:"toggle",name:"useNpm",message:"Flag to use Highcharts scripts from NPM package",initial:E.highcharts.useNpm.value},{type:"multiselect",name:"coreScripts",message:"Available core scripts",instructions:"Space: Select specific, A: Select all, Enter: Confirm.",choices:E.highcharts.coreScripts.value},{type:"multiselect",name:"moduleScripts",message:"Available module scripts",instructions:"Space: Select specific, A: Select all, Enter: Confirm.",choices:E.highcharts.moduleScripts.value},{type:"multiselect",name:"indicatorScripts",message:"Available indicator scripts",instructions:"Space: Select specific, A: Select all, Enter: Confirm.",choices:E.highcharts.indicatorScripts.value},{type:"list",name:"customScripts",message:"Custom scripts",initial:E.highcharts.customScripts.value.join(","),separator:","},{type:"toggle",name:"forceFetch",message:"Force re-fetch the scripts",initial:E.highcharts.forceFetch.value},{type:"text",name:"cachePath",message:"The path to the cache directory",initial:E.highcharts.cachePath.value}],export:[{type:"select",name:"type",message:"The default export file type",hint:`Default: ${E.export.type.value}`,initial:0,choices:["png","jpeg","pdf","svg"]},{type:"select",name:"constr",message:"The default constructor for Highcharts",hint:`Default: ${E.export.constr.value}`,initial:0,choices:["chart","stockChart","mapChart","ganttChart"]},{type:"number",name:"defaultHeight",message:"The default fallback height of the exported chart",initial:E.export.defaultHeight.value},{type:"number",name:"defaultWidth",message:"The default fallback width of the exported chart",initial:E.export.defaultWidth.value},{type:"number",name:"defaultScale",message:"The default fallback scale of the exported chart",initial:E.export.defaultScale.value,min:.1,max:5},{type:"number",name:"rasterizationTimeout",message:"The rendering webpage timeout in milliseconds",initial:E.export.rasterizationTimeout.value}],customLogic:[{type:"toggle",name:"allowCodeExecution",message:"Enable execution of custom code",initial:E.customLogic.allowCodeExecution.value},{type:"toggle",name:"allowFileResources",message:"Enable file resources",initial:E.customLogic.allowFileResources.value}],server:[{type:"toggle",name:"enable",message:"Starts the server on 0.0.0.0",initial:E.server.enable.value},{type:"text",name:"host",message:"Server hostname",initial:E.server.host.value},{type:"number",name:"port",message:"Server port",initial:E.server.port.value},{type:"toggle",name:"benchmarking",message:"Enable server benchmarking",initial:E.server.benchmarking.value},{type:"text",name:"proxy.host",message:"The host of the proxy server to use",initial:E.server.proxy.host.value},{type:"number",name:"proxy.port",message:"The port of the proxy server to use",initial:E.server.proxy.port.value},{type:"number",name:"proxy.timeout",message:"The timeout for the proxy server to use",initial:E.server.proxy.timeout.value},{type:"toggle",name:"rateLimiting.enable",message:"Enable rate limiting",initial:E.server.rateLimiting.enable.value},{type:"number",name:"rateLimiting.maxRequests",message:"The maximum requests allowed per minute",initial:E.server.rateLimiting.maxRequests.value},{type:"number",name:"rateLimiting.window",message:"The rate-limiting time window in minutes",initial:E.server.rateLimiting.window.value},{type:"number",name:"rateLimiting.delay",message:"The delay for each successive request before reaching the maximum",initial:E.server.rateLimiting.delay.value},{type:"toggle",name:"rateLimiting.trustProxy",message:"Set to true if behind a load balancer",initial:E.server.rateLimiting.trustProxy.value},{type:"text",name:"rateLimiting.skipKey",message:"Allows bypassing the rate limiter when provided with the skipToken argument",initial:E.server.rateLimiting.skipKey.value},{type:"text",name:"rateLimiting.skipToken",message:"Allows bypassing the rate limiter when provided with the skipKey argument",initial:E.server.rateLimiting.skipToken.value},{type:"toggle",name:"ssl.enable",message:"Enable SSL protocol",initial:E.server.ssl.enable.value},{type:"toggle",name:"ssl.force",message:"Force serving only over HTTPS",initial:E.server.ssl.force.value},{type:"number",name:"ssl.port",message:"SSL server port",initial:E.server.ssl.port.value},{type:"text",name:"ssl.certPath",message:"The path to find the SSL certificate/key",initial:E.server.ssl.certPath.value}],pool:[{type:"number",name:"minWorkers",message:"The initial number of workers to spawn",initial:E.pool.minWorkers.value},{type:"number",name:"maxWorkers",message:"The maximum number of workers to spawn",initial:E.pool.maxWorkers.value},{type:"number",name:"workLimit",message:"The pieces of work that can be performed before restarting a Puppeteer process",initial:E.pool.workLimit.value},{type:"number",name:"acquireTimeout",message:"The number of milliseconds to wait for acquiring a resource",initial:E.pool.acquireTimeout.value},{type:"number",name:"createTimeout",message:"The number of milliseconds to wait for creating a resource",initial:E.pool.createTimeout.value},{type:"number",name:"destroyTimeout",message:"The number of milliseconds to wait for destroying a resource",initial:E.pool.destroyTimeout.value},{type:"number",name:"idleTimeout",message:"The number of milliseconds after an idle resource is destroyed",initial:E.pool.idleTimeout.value},{type:"number",name:"createRetryInterval",message:"The retry interval in milliseconds after a create process fails",initial:E.pool.createRetryInterval.value},{type:"number",name:"reaperInterval",message:"The reaper interval in milliseconds after triggering the check for idle resources to destroy",initial:E.pool.reaperInterval.value},{type:"toggle",name:"benchmarking",message:"Enable benchmarking for a resource pool",initial:E.pool.benchmarking.value}],logging:[{type:"number",name:"level",message:"The log level (0: silent, 1: error, 2: warning, 3: notice, 4: verbose, 5: benchmark)",initial:E.logging.level.value,round:0,min:0,max:5},{type:"text",name:"file",message:"A log file name. Set with --toFile and --logDest to enable file logging",initial:E.logging.file.value},{type:"text",name:"dest",message:"The path to a log file when the file logging is enabled",initial:E.logging.dest.value},{type:"toggle",name:"toConsole",message:"Enable logging to the console",initial:E.logging.toConsole.value},{type:"toggle",name:"toFile",message:"Enables logging to a file",initial:E.logging.toFile.value}],ui:[{type:"toggle",name:"enable",message:"Enable UI for the export server",initial:E.ui.enable.value},{type:"text",name:"route",message:"A route to attach the UI",initial:E.ui.route.value}],other:[{type:"text",name:"nodeEnv",message:"The type of Node.js environment",initial:E.other.nodeEnv.value},{type:"toggle",name:"listenToProcessExits",message:"Set to false to skip attaching process.exit handlers",initial:E.other.listenToProcessExits.value},{type:"toggle",name:"noLogo",message:"Skip printing the logo on startup. Replaced by simple text",initial:E.other.noLogo.value},{type:"toggle",name:"hardResetPage",message:"Decides if the page content should be reset entirely",initial:E.other.hardResetPage.value},{type:"toggle",name:"browserShellMode",message:"Decides if the browser runs in the shell mode",initial:E.other.browserShellMode.value}],debug:[{type:"toggle",name:"enable",message:"Enables debug mode for the browser instance",initial:E.debug.enable.value},{type:"toggle",name:"headless",message:"The mode setting for the browser",initial:E.debug.headless.value},{type:"toggle",name:"devtools",message:"The DevTools for the headful browser",initial:E.debug.devtools.value},{type:"toggle",name:"listenToConsole",message:"The event listener for console messages from the browser",initial:E.debug.listenToConsole.value},{type:"toggle",name:"dumpio",message:"Redirects the browser stdout and stderr to NodeJS process",initial:E.debug.dumpio.value},{type:"number",name:"slowMo",message:"Puppeteer operations slow down in milliseconds",initial:E.debug.slowMo.value},{type:"number",name:"debuggingPort",message:"The port number for debugging",initial:E.debug.debuggingPort.value}]},S=["options","globalOptions","themeOptions","resources","payload"],R={},x=(e,t="")=>{Object.keys(e).forEach((r=>{if(!["puppeteer","highcharts"].includes(r)){const o=e[r];void 0===o.value?x(o,`${t}.${r}`):(R[o.cliName||r]=`${t}.${r}`.substring(1),void 0!==o.legacyName&&(R[o.legacyName]=`${t}.${r}`.substring(1)))}}))};x(E),i.config();const _=e=>s.z.string().transform((t=>t.split(",").map((e=>e.trim())).filter((t=>e.includes(t))))).transform((e=>e.length?e:void 0)),L=()=>s.z.enum(["true","false",""]).transform((e=>""!==e?"true"===e:void 0)),O=e=>s.z.enum([...e,""]).transform((e=>""!==e?e:void 0)),k=()=>s.z.string().trim().refine((e=>!["false","undefined","null","NaN"].includes(e)||""===e),(e=>({message:`The string contains forbidden values, received '${e}'`}))).transform((e=>""!==e?e:void 0)),I=()=>s.z.string().trim().refine((e=>/^(\.\/|\.\.\/|\/|[a-zA-Z]:\\|[a-zA-Z]:\/)?((?:[\w-]+)[\\/]?)+$/.test(e)),{},{message:"The string is an invalid path directory string."}),C=()=>s.z.string().trim().refine((e=>""===e||!isNaN(parseFloat(e))&&parseFloat(e)>0),(e=>({message:`The value must be numeric and positive, received '${e}'`}))).transform((e=>""!==e?parseFloat(e):void 0)),N=()=>s.z.string().trim().refine((e=>""===e||!isNaN(parseFloat(e))&&parseFloat(e)>=0),(e=>({message:`The value must be numeric and non-negative, received '${e}'`}))).transform((e=>""!==e?parseFloat(e):void 0)),A=s.z.object({PUPPETEER_TEMP_DIR:I(),HIGHCHARTS_VERSION:s.z.string().trim().refine((e=>/^(latest|\d+(\.\d+){0,2})$/.test(e)||""===e),(e=>({message:`HIGHCHARTS_VERSION must be 'latest', a major version, or in the form XX.YY.ZZ, received '${e}'`}))).transform((e=>""!==e?e:void 0)),HIGHCHARTS_CDN_URL:s.z.string().trim().refine((e=>e.startsWith("https://")||e.startsWith("http://")||""===e),(e=>({message:`Invalid value for HIGHCHARTS_CDN_URL. It should start with http:// or https://, received '${e}'`}))).transform((e=>""!==e?e:void 0)),HIGHCHARTS_USE_NPM:L(),HIGHCHARTS_CORE_SCRIPTS:_(w.core),HIGHCHARTS_MODULE_SCRIPTS:_(w.modules),HIGHCHARTS_INDICATOR_SCRIPTS:_(w.indicators),HIGHCHARTS_FORCE_FETCH:L(),HIGHCHARTS_CACHE_PATH:k(),HIGHCHARTS_ADMIN_TOKEN:k(),EXPORT_TYPE:O(["jpeg","png","pdf","svg"]),EXPORT_CONSTR:O(["chart","stockChart","mapChart","ganttChart"]),EXPORT_DEFAULT_HEIGHT:C(),EXPORT_DEFAULT_WIDTH:C(),EXPORT_DEFAULT_SCALE:C(),EXPORT_RASTERIZATION_TIMEOUT:N(),CUSTOM_LOGIC_ALLOW_CODE_EXECUTION:L(),CUSTOM_LOGIC_ALLOW_FILE_RESOURCES:L(),SERVER_ENABLE:L(),SERVER_HOST:k(),SERVER_PORT:C(),SERVER_MAX_UPLOAD_SIZE:C(),SERVER_BENCHMARKING:L(),SERVER_PROXY_HOST:k(),SERVER_PROXY_PORT:C(),SERVER_PROXY_USERNAME:k(),SERVER_PROXY_PASSWORD:k(),SERVER_PROXY_TIMEOUT:N(),SERVER_RATE_LIMITING_ENABLE:L(),SERVER_RATE_LIMITING_MAX_REQUESTS:N(),SERVER_RATE_LIMITING_WINDOW:N(),SERVER_RATE_LIMITING_DELAY:N(),SERVER_RATE_LIMITING_TRUST_PROXY:L(),SERVER_RATE_LIMITING_SKIP_KEY:k(),SERVER_RATE_LIMITING_SKIP_TOKEN:k(),SERVER_SSL_ENABLE:L(),SERVER_SSL_FORCE:L(),SERVER_SSL_PORT:C(),SERVER_SSL_CERT_PATH:k(),POOL_MIN_WORKERS:N(),POOL_MAX_WORKERS:N(),POOL_WORK_LIMIT:C(),POOL_ACQUIRE_TIMEOUT:N(),POOL_CREATE_TIMEOUT:N(),POOL_DESTROY_TIMEOUT:N(),POOL_IDLE_TIMEOUT:N(),POOL_CREATE_RETRY_INTERVAL:N(),POOL_REAPER_INTERVAL:N(),POOL_BENCHMARKING:L(),LOGGING_LEVEL:s.z.string().trim().refine((e=>""===e||!isNaN(parseFloat(e))&&parseFloat(e)>=0&&parseFloat(e)<=5),(e=>({message:`Invalid value for LOGGING_LEVEL. We only accept values from 0 to 5 as logging levels, received '${e}'`}))).transform((e=>""!==e?parseFloat(e):void 0)),LOGGING_FILE:k(),LOGGING_DEST:k(),LOGGING_TO_CONSOLE:L(),LOGGING_TO_FILE:L(),UI_ENABLE:L(),UI_ROUTE:k(),OTHER_NODE_ENV:O(["development","production","test"]),OTHER_LISTEN_TO_PROCESS_EXITS:L(),OTHER_NO_LOGO:L(),OTHER_HARD_RESET_PAGE:L(),OTHER_BROWSER_SHELL_MODE:L(),OTHER_ALLOW_XLINK:L(),DEBUG_ENABLE:L(),DEBUG_HEADLESS:L(),DEBUG_DEVTOOLS:L(),DEBUG_LISTEN_TO_CONSOLE:L(),DEBUG_DUMPIO:L(),DEBUG_SLOW_MO:N(),DEBUG_DEBUGGING_PORT:C()}).partial().parse(process.env),P=["red","yellow","blue","gray","green"];let H={toConsole:!0,toFile:!1,pathCreated:!1,levelsDesc:[{title:"error",color:P[0]},{title:"warning",color:P[1]},{title:"notice",color:P[2]},{title:"verbose",color:P[3]},{title:"benchmark",color:P[4]}],listeners:[]};const $=(t,r)=>{H.pathCreated||(!e.existsSync(H.dest)&&e.mkdirSync(H.dest),H.pathCreated=!0),e.appendFile(`${H.dest}${H.file}`,[r].concat(t).join(" ")+"\n",(e=>{e&&(console.log(`[logger] Unable to write to log file: ${e}`),H.toFile=!1)}))},U=(...e)=>{const[t,...r]=e,{levelsDesc:o,level:i}=H;if(5!==t&&(0===t||t>i||i>o.length))return;const s=`${(new Date).toString().split("(")[0].trim()} [${o[t-1].title}] -`;H.listeners.forEach((e=>{e(s,r.join(" "))})),H.toConsole&&console.log.apply(void 0,[s.toString()[H.levelsDesc[t-1].color]].concat(r)),H.toFile&&$(r,s)},D=(e,t,r)=>{const o=r||t.message,{level:i,levelsDesc:s}=H;if(0===e||e>i||i>s.length)return;const n=`${(new Date).toString().split("(")[0].trim()} [${s[e-1].title}] -`,a=t.message!==t.stackMessage||void 0===t.stackMessage?t.stack:t.stack.split("\n").slice(1).join("\n"),l=[o,"\n",a];H.toConsole&&console.log.apply(void 0,[n.toString()[H.levelsDesc[e-1].color]].concat([o[P[e-1]],"\n",a])),H.listeners.forEach((e=>{e(n,l.join(" "))})),H.toFile&&$(l,n)},G=e=>{e>=0&&e<=H.levelsDesc.length&&(H.level=e)},F=(e,t)=>{if(H={...H,dest:e||H.dest,file:t||H.file,toFile:!0},0===H.dest.length)return U(1,"[logger] File logging initialization: no path supplied.");H.dest.endsWith("/")||(H.dest+="/")},j=t.dirname(n.createRequire("undefined"==typeof document?require("url").pathToFileURL(__filename).href:b&&"SCRIPT"===b.tagName.toUpperCase()&&b.src||new URL("index.cjs",document.baseURI).href).resolve("highcharts/package.json")),M=a.fileURLToPath(new URL("../.","undefined"==typeof document?require("url").pathToFileURL(__filename).href:b&&"SCRIPT"===b.tagName.toUpperCase()&&b.src||new URL("index.cjs",document.baseURI).href)),W=(e,t)=>{const r=["png","jpeg","pdf","svg"];if(t){const o=t.split(".").pop();"jpg"===o?e="jpeg":r.includes(o)&&e!==o&&(e=o)}return{"image/png":"png","image/jpeg":"jpeg","application/pdf":"pdf","image/svg+xml":"svg"}[e]||r.find((t=>t===e))||"png"},q=(t=!1,r)=>{const o=["js","css","files"];let i=t,s=!1;if(r&&t.endsWith(".json"))try{i=V(e.readFileSync(t,"utf8"))}catch(e){return D(2,e,"[cli] No resources found.")}else i=V(t),i&&!r&&delete i.files;for(const e in i)o.includes(e)?s||(s=!0):delete i[e];return s?(i.files&&(i.files=i.files.map((e=>e.trim())),(!i.files||i.files.length<=0)&&delete i.files),i):U(3,"[cli] No resources found.")};function V(e,t){try{const r=JSON.parse("string"!=typeof e?JSON.stringify(e):e);return"string"!=typeof r&&t?JSON.stringify(r):r}catch{return!1}}const B=e=>{if(null===e||"object"!=typeof e)return e;const t=Array.isArray(e)?[]:{};for(const r in e)Object.prototype.hasOwnProperty.call(e,r)&&(t[r]=B(e[r]));return t},X=(e,t)=>JSON.stringify(e,((e,r)=>("string"==typeof r&&((r=r.trim()).startsWith("function(")||r.startsWith("function ("))&&r.endsWith("}")&&(r=t?`EXP_FUN${(r+"").replaceAll(/\n|\t|\r/g," ")}EXP_FUN`:void 0),"function"==typeof r?`EXP_FUN${(r+"").replaceAll(/\n|\t|\r/g," ")}EXP_FUN`:r))).replaceAll(/"EXP_FUN|EXP_FUN"/g,"");function z(){console.log("\nUsage of CLI arguments:".bold,"\n------",`\nFor more detailed information, visit the readme at: ${"https://github.com/highcharts/node-export-server#readme".bold.yellow}.`);const e=t=>{for(const[r,o]of Object.entries(t))if(Object.prototype.hasOwnProperty.call(o,"value")){let e=` --${o.cliName||r} ${("<"+o.type+">").green} `;if(e.length<48)for(let t=e.length;t<48;t++)e+=".";console.log(e,o.description,`[Default: ${o.value.toString().bold}]`.blue)}else e(o)};Object.keys(E).forEach((t=>{["puppeteer","highcharts"].includes(t)||(console.log(`\n${t.toUpperCase()}`.red),e(E[t]))})),console.log("\n")}const K=e=>!["false","undefined","null","NaN","0",""].includes(e)&&!!e,J=(t,r)=>{if(t&&"string"==typeof t)return(t=t.trim()).endsWith(".js")?!!r&&J(e.readFileSync(t,"utf8")):t.startsWith("function()")||t.startsWith("function ()")||t.startsWith("()=>")||t.startsWith("() =>")?`(${t})()`:t.replace(/;$/,"")},Y=()=>{const e=process.hrtime.bigint();return()=>Number(process.hrtime.bigint()-e)/1e6};let Z={};const Q=()=>Z,ee=(e,t,r=[])=>{const o=B(e);for(const[e,s]of Object.entries(t))o[e]="object"!=typeof(i=s)||Array.isArray(i)||null===i||r.includes(e)||void 0===o[e]?void 0!==s?s:o[e]:ee(o[e],s,r);var i;return o};function te(e,t={},r=""){Object.keys(e).forEach((o=>{const i=e[o],s=t&&t[o];void 0===i.value?te(i,s,`${r}.${o}`):(void 0!==s&&(i.value=s),i.envLink in A&&void 0!==A[i.envLink]&&(i.value=A[i.envLink]))}))}function re(e){let t={};for(const[r,o]of Object.entries(e))t[r]=Object.prototype.hasOwnProperty.call(o,"value")?o.value:re(o);return t}function oe(e,t,r){for(;t.length>1;){const o=t.shift();return Object.prototype.hasOwnProperty.call(e,o)||(e[o]={}),e[o]=oe(Object.assign({},e[o]),t,r),e}return e[t[0]]=r,e}async function ie(e,t={}){return new Promise(((r,o)=>{const i=(e=>e.startsWith("https")?c:l)(e);i.get(e,Object.assign({headers:{"User-Agent":"highcharts/export",Referer:"highcharts.export"}},t||{}),(e=>{let t="";e.on("data",(e=>{t+=e})),e.on("end",(()=>{t||o("Nothing was fetched from the URL."),e.text=t,r(e)}))})).on("error",(e=>{o(e)}))}))}class se extends Error{constructor(e){super(),this.message=e,this.stackMessage=e}setError(e){return this.error=e,e.name&&(this.name=e.name),e.statusCode&&(this.statusCode=e.statusCode),e.stack&&(this.stackMessage=e.message,this.stack=e.stack),this}}const ne={cdnURL:"https://code.highcharts.com/",activeManifest:{},sources:"",hcVersion:""},ae=e=>e.sources.substring(0,e.sources.indexOf("*/")).replace("/*","").replace("*/","").replace(/\n/g,"").trim(),le=e=>e.replace(/\\/g,"/").split("/").pop().replace(/\.js$/i,""),ce=async(r,o,i,s=!1,n=!1)=>{let a;if(r.endsWith(".js")||(r=`${r}.js`),s)try{U(4,`[cache] Fetching script from NPM - ${t.join("node_modules","highcharts",r)}`);const o=t.resolve(j,r);if(!o.startsWith(t.resolve(j)+t.sep))throw new se(`[cache] Invalid script path detected for '${r}'. Directory traversal attempt or bad version input.`,403);return a=e.readFileSync(o,"utf8"),i&&a&&(i[le(r)]=1),a}catch{}else if(U(4,`[cache] Fetching script from CDN - ${r}`),a=await ie(r,o),200===a.statusCode&&"string"==typeof a.text)return i&&(i[le(r)]=1),a.text;if(n)throw new se(`[cache] Could not fetch the mandatory ${r}. The script might not exist in the requested version.`,404);return U(2,`[cache] Could not fetch the ${r}. The script might not exist in the requested version.`),""},pe=async(o,i,s)=>{try{const n={};return ne.sources=await(async(e,o,i)=>{const s=e.version,n="latest"!==s&&s?`${s}/`:"",a=e.cdnURL||ne.cdnURL;U(3,`[cache] Updating cache version to Highcharts: ${n||"latest"}.`);const l=e.useNpm;let c;const{host:p,port:h,username:u,password:d}=o;if(p&&h)try{c=new r.HttpsProxyAgent({host:p,port:h,...u&&d?{username:u,password:d}:{}})}catch(e){throw new se("[cache] Could not create a Proxy Agent.").setError(e)}const g=c?{agent:c,timeout:A.SERVER_PROXY_TIMEOUT}:{};return(await Promise.all([...e.coreScripts.map((e=>ce(l&&e||`${a}${n}${e}`,g,i,l,!0))),...e.moduleScripts.map((e=>ce(l&&t.join("modules",e)||("map"===e?`${a}maps/${n}modules/${e}`:`${a}${n}modules/${e}`),g,i,l))),...e.indicatorScripts.map((e=>ce(l&&t.join("indicators",e)||`${a}stock/${n}indicators/${e}`,g,i,l))),...e.customScripts.map((e=>ce(`${e}`,g)))])).join(";\n")})(o,i,n),ne.hcVersion=ae(ne),e.writeFileSync(s,ne.sources),n}catch(e){throw new se("[cache] Unable to update the local Highcharts cache.").setError(e)}},he=async r=>{const{highcharts:o,server:i}=r,s=t.join(M,o.cachePath);let n;const a=t.join(s,"manifest.json"),l=t.join(s,"sources.js");if(!e.existsSync(s)&&e.mkdirSync(s),!e.existsSync(a)||o.forceFetch)U(3,"[cache] Fetching and caching Highcharts dependencies."),n=await pe(o,i.proxy,l);else{let t=!1;const r=JSON.parse(e.readFileSync(a));if(r.modules&&Array.isArray(r.modules)){const e={};r.modules.forEach((t=>e[t]=1)),r.modules=e}const{coreScripts:s,moduleScripts:c,indicatorScripts:p}=o,h=s.length+c.length+p.length;r.version!==o.version?(U(2,"[cache] A Highcharts version mismatch in the cache, need to re-fetch."),t=!0):Object.keys(r.modules||{}).length!==h?(U(2,"[cache] The cache and the requested modules do not match, need to re-fetch."),t=!0):t=(c||[]).some((e=>{if(!r.modules[e])return U(2,`[cache] The ${e} is missing in the cache, need to re-fetch.`),!0})),t?n=await pe(o,i.proxy,l):(U(3,"[cache] Dependency cache is up to date, proceeding."),ne.sources=e.readFileSync(l,"utf8"),n=r.modules,ne.hcVersion=ae(ne))}await(async(r,o)=>{const i={version:r.version,modules:o||{}};ne.activeManifest=i,U(3,"[cache] Writing a new manifest.");try{e.writeFileSync(t.join(M,r.cachePath,"manifest.json"),JSON.stringify(i),"utf8")}catch(e){throw new se("[cache] Error writing the cache manifest.").setError(e)}})(o,n)},ue=()=>t.join(M,Q().highcharts.cachePath),de=()=>ne.hcVersion;function ge(){Highcharts.animObject=function(){return{duration:0}}}async function me(e,t,r){window._displayErrors=r;const{getOptions:o,merge:i,setOptions:s,wrap:n}=Highcharts;Highcharts.setOptionsObj=i(!1,{},o());const a={animation:!1};t.export.strInj&&(a.height=e.chart.height,a.width=e.chart.width),window.isRenderComplete=!1,n(Highcharts.Chart.prototype,"init",(function(e,t,r){((t=i(t,{exporting:{enabled:!1},plotOptions:{series:{label:{enabled:!1}}},tooltip:{}})).series||[]).forEach((function(e){e.animation=!1})),window.onHighchartsRender||(window.onHighchartsRender=Highcharts.addEvent(this,"render",(()=>{window.isRenderComplete=!0}))),e.apply(this,[t,r])})),n(Highcharts.Series.prototype,"init",(function(e,t,r){e.apply(this,[t,r])}));const l=t.export.strInj?new Function(`return ${t.export.strInj}`)():e;t.customLogic.customCode&&new Function("options",t.customLogic.customCode)(l);const c=i(!1,JSON.parse(t.export.themeOptions),l,{chart:a}),p=t.customLogic.callback?new Function(`return ${t.customLogic.callback}`)():void 0,h=JSON.parse(t.export.globalOptions);h&&s(h);let u=t.export.constr||"chart";u=void 0!==Highcharts[u]?u:"chart",Highcharts[u]("container",c,p);const d=o();for(const e in d)"function"!=typeof d[e]&&delete d[e];s(Highcharts.setOptionsObj),Highcharts.setOptionsObj={}}const fe=e.readFileSync(M+"/templates/template.html","utf8");let ve;async function ye(){if(!ve)return!1;const e=await ve.newPage();return await e.setCacheEnabled(!1),await we(e),function(e){const{debug:t}=Q();t.enable&&t.listenToConsole&&e.on("console",(e=>{console.log(`[debug] ${e.text()}`)}));e.on("pageerror",(async t=>{e.isClosed()||await e.$eval("#container",((e,t)=>{window._displayErrors&&(e.innerHTML=t)}),`

Chart input data error:

${t.toString()}`)}))}(e),e}async function be(e,t){try{for(const e of t)await e.dispose();await e.evaluate((()=>{if("undefined"!=typeof Highcharts){const e=Highcharts.charts;if(Array.isArray(e)&&e.length)for(const t of e)t&&t.destroy(),Highcharts.charts.shift()}const[...e]=document.getElementsByTagName("script"),[,...t]=document.getElementsByTagName("style"),[...r]=document.getElementsByTagName("link");for(const o of[...e,...t,...r])o.remove()}))}catch(e){D(2,e,"[browser] Could not clear page's resources.")}}async function we(e){await e.setContent(fe,{waitUntil:"domcontentloaded"}),await e.addScriptTag({path:`${ue()}/sources.js`}),await e.evaluate(ge)}const Ee=async(e,t,r,o)=>{r.export.instr=null,r.export.infile=null;const i=Buffer.byteLength(r.export?.strInj?r.export?.strInj:JSON.stringify(t),"utf-8");if(U(4,`[export] The current total size of data passed to a page is around ${(i/1048576).toFixed(2)} MB`),i>=104857600)throw new se("[export] The data passed to a page exceeded 100MB.");return e.evaluate(me,t,r,o)};var Te=async(r,o,i)=>{let s=[];try{U(4,"[export] Determining export path.");const n=i.export,a=n?.options?.chart?.displayErrors&&ne.activeManifest.modules.debugger;let l;if(o.indexOf&&(o.indexOf("=0||o.indexOf("=0)){if(U(4,"[export] Treating as SVG."),"svg"===n.type)return o;l=!0,await r.setContent((e=>`\n\n\n \n \n Highcharts Export\n \n \n \n
\n ${e}\n
\n \n\n\n`)(o),{waitUntil:"domcontentloaded"})}else U(4,"[export] Treating as config."),n.strInj?await Ee(r,{chart:{height:n.height,width:n.width}},i,a):(o.chart.height=n.height,o.chart.width=n.width,await Ee(r,o,i,a));s=await async function(r,o){const i=[],s=o.customLogic.resources;if(s){const n=[];if(s.js&&n.push({content:s.js}),s.files)for(const t of s.files){const r=!t.startsWith("http");n.push(r?{content:e.readFileSync(t,"utf8")}:{url:t})}for(const e of n)try{i.push(await r.addScriptTag(e))}catch(e){D(2,e,"[export] The JS resource cannot be loaded.")}n.length=0;const a=[];if(s.css){let e=s.css.match(/@import\s*([^;]*);/g);if(e)for(let r of e)r&&(r=r.replace("url(","").replace("@import","").replace(/"/g,"").replace(/'/g,"").replace(/;/,"").replace(/\)/g,"").trim(),r.startsWith("http")?a.push({url:r}):o.customLogic.allowFileResources&&a.push({path:t.join(M,r)}));a.push({content:s.css.replace(/@import\s*([^;]*);/g,"")||" "});for(const e of a)try{i.push(await r.addStyleTag(e))}catch(e){D(2,e,"[export] The CSS resource cannot be loaded.")}a.length=0}}return i}(r,i);const c=l?await r.evaluate((e=>{const t=document.querySelector("#chart-container svg:first-of-type"),r=t.height.baseVal.value*e,o=t.width.baseVal.value*e;return document.body.style.zoom=e,document.body.style.margin="0px",{chartHeight:r,chartWidth:o}}),parseFloat(n.scale)):await r.evaluate((()=>{const{chartHeight:e,chartWidth:t}=window.Highcharts.charts[0];return document.body.style.zoom=1,{chartHeight:e,chartWidth:t}})),p=Math.abs(Math.ceil(c.chartHeight||n.height)),h=Math.abs(Math.ceil(c.chartWidth||n.width)),{x:u,y:d}=await(e=>e.$eval("#chart-container",(e=>{const{x:t,y:r,width:o,height:i}=e.getBoundingClientRect();return{x:t,y:r,width:o,height:Math.trunc(i>1?i:500)}})))(r);let g;if(await r.setViewport({height:p,width:h,deviceScaleFactor:l?1:parseFloat(n.scale)}),"svg"===n.type)g=await(e=>e.$eval("#container svg:first-of-type",(e=>e.outerHTML)))(r);else if(["png","jpeg"].includes(n.type))g=await((e,t,r,o,i)=>Promise.race([e.screenshot({type:t,encoding:r,clip:o,captureBeyondViewport:!0,fullPage:!1,optimizeForSpeed:!0,..."png"!==t?{quality:80}:{},omitBackground:"png"==t}),new Promise(((e,t)=>setTimeout((()=>t(new se("Rasterization timeout"))),i||1500)))]))(r,n.type,"base64",{width:h,height:p,x:u,y:d},n.rasterizationTimeout);else{if("pdf"!==n.type)throw new se(`[export] Unsupported output format ${n.type}.`);g=await(async(e,t,r,o,i)=>(await e.emulateMediaType("screen"),e.pdf({height:t+1,width:r,encoding:o,timeout:i||1500})))(r,p,h,"base64",n.rasterizationTimeout)}return await be(r,s),g}catch(e){return await be(r,s),e}};let Se=!1;const Re={performedExports:0,exportAttempts:0,exportFromSvgAttempts:0,timeSpent:0,droppedExports:0,spentAverage:0};let xe={};const _e={create:async()=>{let e=!1;const t=h.v4(),r=(new Date).getTime();try{if(e=await ye(),!e||e.isClosed())throw new se("The page is invalid or closed.");U(3,`[pool] Successfully created a worker ${t} - took ${(new Date).getTime()-r} ms.`)}catch(e){throw new se("Error encountered when creating a new page.").setError(e)}return{id:t,page:e,workCount:Math.round(Math.random()*(xe.workLimit/2))}},validate:async e=>!(!e.page||e.page?.isClosed())&&(!(xe.workLimit&&++e.workCount>xe.workLimit)||(U(3,`[pool] Worker failed validation: exceeded work limit (limit is ${xe.workLimit}).`),!1)),destroy:async e=>{U(3,`[pool] Destroying pool entry ${e.id}.`),e.page&&!e.page.isClosed()&&await e.page.close()}},Le=async e=>{if(xe=e&&e.pool?{...e.pool}:{},await async function(e){const{puppeteer:t,debug:r,other:o}=Q(),{enable:i,...s}=r,n={headless:!o.browserShellMode||"shell",userDataDir:t.tempDir||"./tmp/",args:e,handleSIGINT:!1,handleSIGTERM:!1,handleSIGHUP:!1,waitForInitialPage:!1,defaultViewport:null,...i&&s};if(!ve){const e=25;let t=0;const r=async()=>{try{U(3,`[browser] Attempting to get a browser instance (try ${++t}).`),ve=await u.launch(n)}catch(o){if(D(2,o,`[browser] Failed to launch a browser instance - retrying (attempt ${t}/${e}).`),!(t<25))throw o;U(3,`[browser] Retry to open a browser (attempt ${t}/${e}).`),await new Promise((e=>setTimeout(e,4e3))),await r()}};try{await r(),"shell"===n.headless&&U(3,"[browser] Launched browser in shell mode."),i&&U(3,"[browser] Launched browser in debug mode.")}catch(e){throw new se("[browser] Maximum retries to open a browser instance reached.").setError(e)}if(!ve)throw new se("[browser] Cannot find a browser to open.")}return ve}(e.puppeteerArgs),U(3,`[pool] Initializing pool with workers: min ${xe.minWorkers}, max ${xe.maxWorkers}.`),Se)return U(4,"[pool] Already initialized, please kill it before creating a new one.");parseInt(xe.minWorkers)>parseInt(xe.maxWorkers)&&(xe.minWorkers=xe.maxWorkers);try{Se=new p.Pool({..._e,min:parseInt(xe.minWorkers),max:parseInt(xe.maxWorkers),acquireTimeoutMillis:xe.acquireTimeout,createTimeoutMillis:xe.createTimeout,destroyTimeoutMillis:xe.destroyTimeout,idleTimeoutMillis:xe.idleTimeout,createRetryIntervalMillis:xe.createRetryInterval,reapIntervalMillis:xe.reaperInterval,propagateCreateError:!1}),Se.on("release",(async e=>{const t=await async function(e,t=!1){try{if(e&&!e.isClosed())return t?(await e.goto("about:blank",{waitUntil:"domcontentloaded"}),await we(e)):await e.evaluate((()=>{document.body.innerHTML='
'})),!0}catch(e){D(2,e,"[browser] Could not clear the content of the page.")}return!1}(e.page,!1);U(4,`[pool] Releasing a worker with ID ${e.id}. Clear page status: ${t}.`)})),Se.on("destroySuccess",((e,t)=>{U(4,`[pool] Destroyed a worker with ID ${t.id}.`),t.page=null}));const e=[];for(let t=0;t{Se.release(e)})),U(3,"[pool] The pool is ready"+(e.length?` with ${e.length} initial resources waiting.`:"."))}catch(e){throw new se("[pool] Could not create the pool of workers.").setError(e)}};async function Oe(){if(U(3,"[pool] Killing pool with all workers and closing browser."),Se){for(const e of Se.used)Se.release(e.resource);Se.destroyed||(await Se.destroy(),U(4,"[browser] Destroyed the pool of resources."))}await async function(){ve?.connected&&await ve.close(),U(4,"[browser] Closed the browser.")}()}const ke=async(e,t)=>{let r;try{if(U(4,"[pool] Work received, starting to process."),++Re.exportAttempts,xe.benchmarking&&Ce(),!Se)throw new se("Work received, but pool has not been started.");const o=Y();try{U(4,"[pool] Acquiring a worker handle."),r=await Se.acquire().promise,t.server.benchmarking&&U(5,t.payload?.requestId?`[benchmark] Request with ID ${t.payload?.requestId} -`:"[benchmark]",`Acquired a worker handle: ${o()}ms.`)}catch(e){throw new se((t.payload?.requestId?`For request with ID ${t.payload?.requestId} - `:"")+`Error encountered when acquiring an available entry: ${o()}ms.`).setError(e)}if(U(4,"[pool] Acquired a worker handle."),!r.page)throw new se("Resolved worker page is invalid: the pool setup is wonky.");let i=(new Date).getTime();U(4,`[pool] Starting work on pool entry with ID ${r.id}.`);const s=Y(),n=await Te(r.page,e,t);if(n instanceof Error)throw"Rasterization timeout"===n.message&&(r.workCount=xe.workLimit+1,r.page=null),"TimeoutError"===n.name||"Rasterization timeout"===n.message?new se("Rasterization timeout: your chart may be too complex or large, and failed to render within the allotted time.").setError(n):new se((t.payload?.requestId?`For request with ID ${t.payload?.requestId} - `:"")+`Error encountered during export: ${s()}ms.`).setError(n);t.server.benchmarking&&U(5,t.payload?.requestId?`[benchmark] Request with ID ${t.payload?.requestId} -`:"[benchmark]",`Exported a chart sucessfully: ${s()}ms.`),Se.release(r);const a=(new Date).getTime()-i;return Re.timeSpent+=a,Re.spentAverage=Re.timeSpent/++Re.performedExports,U(4,`[pool] Work completed in ${a} ms.`),{result:n,options:t}}catch(e){throw++Re.droppedExports,r&&Se.release(r),new se(`[pool] In pool.postWork: ${e.message}`).setError(e)}},Ie=()=>({min:Se.min,max:Se.max,all:Se.numFree()+Se.numUsed(),available:Se.numFree(),used:Se.numUsed(),pending:Se.numPendingAcquires()});function Ce(){const{min:e,max:t,all:r,available:o,used:i,pending:s}=Ie();U(5,`[pool] The minimum number of resources allowed by pool: ${e}.`),U(5,`[pool] The maximum number of resources allowed by pool: ${t}.`),U(5,`[pool] The number of all created resources: ${r}.`),U(5,`[pool] The number of available resources: ${o}.`),U(5,`[pool] The number of acquired resources: ${i}.`),U(5,`[pool] The number of resources waiting to be acquired: ${s}.`)}var Ne=Ie,Ae=()=>Re;let Pe=!1;const He=async(t,r)=>{U(4,"[chart] Starting the exporting process.");const o=((e,t={})=>{let r={};return e.svg?(r=B(t),r.export.type=e.type||e.export.type,r.export.scale=e.scale||e.export.scale,r.export.outfile=e.outfile||e.export.outfile,r.payload={svg:e.svg}):r=ee(t,e,S),r.export.outfile=r.export?.outfile||`chart.${r.export?.type||"png"}`,r})(t,Q()),i=o.export;if(o.payload?.svg&&""!==o.payload.svg)try{U(4,"[chart] Attempting to export from a SVG input.");const e=Ge(function(e){const t=[];A.OTHER_ALLOW_XLINK||t.push("xlink:href");const r=new d.JSDOM("").window;return g(r).sanitize(e,{ADD_TAGS:["foreignObject"],FORBID_ATTR:t})}(o.payload.svg),o,r);return++Re.exportFromSvgAttempts,e}catch(e){return r(new se("[chart] Error loading SVG input.").setError(e))}if(i.infile&&i.infile.length)try{return U(4,"[chart] Attempting to export from an input file."),o.export.instr=e.readFileSync(i.infile,"utf8"),Ge(o.export.instr.trim(),o,r)}catch(e){return r(new se("[chart] Error loading input file.").setError(e))}if(i.instr&&""!==i.instr||i.options&&""!==i.options)try{return U(4,"[chart] Attempting to export from a raw input."),i.instr=i.instr||i.options,K(o.customLogic?.allowCodeExecution)?De(o,r):"string"==typeof i.instr?Ge(i.instr.trim(),o,r):Ue(o,i.instr||i.options,r)}catch(e){return r(new se("[chart] Error loading raw input.").setError(e))}return r(new se("[chart] No valid input specified. Check if at least one of the following parameters is correctly set: 'infile', 'instr', 'options', or 'svg'."))},$e=e=>{const{chart:t,exporting:r}=e.export?.options||V(e.export?.instr),o=V(e.export?.globalOptions);let i=e.export?.scale||r?.scale||o?.exporting?.scale||e.export?.defaultScale||1;i=Math.max(.1,Math.min(i,5)),i=((e,t=1)=>{const r=Math.pow(10,t||0);return Math.round(+e*r)/r})(i,2);const s={height:e.export?.height||r?.sourceHeight||t?.height||o?.exporting?.sourceHeight||o?.chart?.height||e.export?.defaultHeight||400,width:e.export?.width||r?.sourceWidth||t?.width||o?.exporting?.sourceWidth||o?.chart?.width||e.export?.defaultWidth||600,scale:i};for(let[e,t]of Object.entries(s))s[e]="string"==typeof t?+t.replace(/px|%/gi,""):t;return s},Ue=async(t,r,o,i)=>{let{export:s,customLogic:n}=t;const a="boolean"==typeof n.allowCodeExecution?n.allowCodeExecution:Pe;if(n){if(a)if("string"==typeof t.customLogic.resources)t.customLogic.resources=q(t.customLogic.resources,K(t.customLogic.allowFileResources));else if(!t.customLogic.resources)try{const r=e.readFileSync("resources.json","utf8");t.customLogic.resources=q(r,K(t.customLogic.allowFileResources))}catch(e){U(2,"[chart] Unable to load the default resources.json file.")}}else n=t.customLogic={};if(!a&&n){if(n.callback||n.resources||n.customCode)return o(new se("[chart] The 'callback', 'resources' and 'customCode' options have been disabled for this server."));n.callback=!1,n.resources=!1,n.customCode=!1}if(r&&(r.chart=r.chart||{},r.exporting=r.exporting||{},r.exporting.enabled=!1),s.constr=s.constr||"chart",s.type=W(s.type,s.outfile),"svg"===s.type&&(s.width=!1),["globalOptions","themeOptions"].forEach((t=>{try{s&&s[t]&&("string"==typeof s[t]&&s[t].endsWith(".json")?s[t]=V(e.readFileSync(s[t],"utf8"),!0):s[t]=V(s[t],!0))}catch(e){s[t]={},D(2,e,`[chart] The '${t}' cannot be loaded.`)}})),n.allowCodeExecution)try{n.customCode=J(n.customCode,n.allowFileResources)}catch(e){D(2,e,"[chart] The 'customCode' cannot be loaded.")}if(n&&n.callback&&n.callback?.indexOf("{")<0)if(n.allowFileResources)try{n.callback=e.readFileSync(n.callback,"utf8")}catch(e){n.callback=!1,D(2,e,"[chart] The 'callback' cannot be loaded.")}else n.callback=!1;t.export={...t.export,...$e(t)};try{return o(!1,await ke(s.strInj||r||i,t))}catch(e){return o(e)}},De=(e,t)=>{try{let r,o=e.export.instr||e.export.options;return"string"!=typeof o&&(r=o=X(o,e.customLogic?.allowCodeExecution)),r=o.replaceAll(/\t|\n|\r/g,"").trim(),";"===r[r.length-1]&&(r=r.substring(0,r.length-1)),e.export.strInj=r,Ue(e,!1,t)}catch(r){return t(new se(`[chart] Malformed input detected for ${e.export?.requestId||"?"}. Please make sure that your JSON/JavaScript options are sent using the "options" attribute, and that if you're using SVG, it is unescaped.`).setError(r))}},Ge=(e,t,r)=>{const{allowCodeExecution:o}=t.customLogic;if(e.indexOf("=0||e.indexOf("=0)return U(4,"[chart] Parsing input as SVG."),Ue(t,!1,r,e);try{const o=JSON.parse(e.replaceAll(/\t|\n|\r/g," "));return Ue(t,o,r)}catch(e){return K(o)?De(t,r):r(new se("[chart] Only JSON configurations and SVG are allowed for this server. If this is your server, JavaScript custom code can be enabled by starting the server with the --allowCodeExecution flag.").setError(e))}},Fe=[],je=()=>{U(4,"[server] Clearing all registered intervals.");for(const e of Fe)clearInterval(e)},Me=(e,t,r,o)=>{D(1,e),"development"!==A.OTHER_NODE_ENV&&delete e.stack,o(e)},We=(e,t,r,o)=>{const{statusCode:i,status:s,message:n,stack:a}=e,l=i||s||400;r.status(l).json({statusCode:l,message:n,stack:a})};var qe=(e,t)=>{const r="Too many requests, you have been rate limited. Please try again later.",o={max:t.maxRequests||30,window:t.window||1,delay:t.delay||0,trustProxy:t.trustProxy||!1,skipKey:t.skipKey||!1,skipToken:t.skipToken||!1};o.trustProxy&&e.enable("trust proxy");const i=y({windowMs:60*o.window*1e3,max:o.max,delayMs:o.delay,handler:(e,t)=>{t.format({json:()=>{t.status(429).send({message:r})},default:()=>{t.status(429).send(r)}})},skip:e=>!1!==o.skipKey&&!1!==o.skipToken&&e.query.key===o.skipKey&&e.query.access_token===o.skipToken&&(U(4,"[rate limiting] Skipping rate limiter."),!0)});e.use(i),U(3,`[rate limiting] Enabled rate limiting with ${o.max} requests per ${o.window} minute for each IP, trusting proxy: ${o.trustProxy}.`)};class Ve extends se{constructor(e,t){super(e),this.status=this.statusCode=t}setStatus(e){return this.status=e,this}}var Be=e=>!!e&&e.post("/version/change/:newVersion",(async(e,t,r)=>{try{const r=A.HIGHCHARTS_ADMIN_TOKEN;if(!r||!r.length)throw new Ve("The server is not configured to perform run-time version changes: HIGHCHARTS_ADMIN_TOKEN is not set.",401);const o=e.get("hc-auth");if(!o||o!==r)throw new Ve("Invalid or missing token: Set the token in the hc-auth header.",401);const i=e.params.newVersion;if(!i||!/^[a-zA-Z0-9.-]+$/.test(i))throw new Ve("No new version supplied.",400);try{await(async e=>{const t=Q();t?.highcharts&&(t.highcharts.version=e),await he(t)})(i)}catch(e){throw new Ve(`Version change: ${e.message}`,e.statusCode).setError(e)}t.status(200).send({statusCode:200,version:de(),message:`Successfully updated Highcharts to version: ${i}.`})}catch(e){r(e)}}));const Xe={png:"image/png",jpeg:"image/jpeg",gif:"image/gif",pdf:"application/pdf",svg:"image/svg+xml"};let ze=0;const Ke=[],Je=[],Ye=(e,t,r,o)=>{let i=!0;const{id:s,uniqueId:n,type:a,body:l}=o;return e.some((e=>{if(e){let o=e(t,r,s,n,a,l);return void 0!==o&&!0!==o&&(i=o),!0}})),i},Ze=async(e,t,r)=>{try{const r=Y(),i=h.v4().replace(/-/g,""),s=Q(),n=e.body,a=++ze;let l=W(n.type);if(!n||"object"==typeof(o=n)&&!Array.isArray(o)&&null!==o&&0===Object.keys(o).length)throw new Ve("The request body is required. Please ensure that your Content-Type header is correct (accepted types are application/json and multipart/form-data).",400);let c=V(n.infile||n.options||n.data);if(!c&&!n.svg)throw U(2,`The request with ID ${i} from ${e.headers["x-forwarded-for"]||e.connection.remoteAddress} was incorrect:\n Content-Type: ${e.headers["content-type"]}. \n Chart constructor: ${n.constr}.\n Dimensions: ${n.width}x${n.height} @ ${n.scale} scale.\n Type: ${l}.\n Is SVG set? ${void 0!==n.svg}.\n B64? ${void 0!==n.b64}.\n No download? ${void 0!==n.noDownload}.\n\n Payload received: ${JSON.stringify(n.infile||n.options||n.data||n.svg)}\n\n `),new Ve("No correct chart data found. Ensure that you are using either application/json or multipart/form-data headers. If sending JSON, make sure the chart data is in the 'infile', 'options', or 'data' attribute. If sending SVG, ensure it is in the 'svg' attribute.",400);let p=!1;if(p=Ye(Ke,e,t,{id:a,uniqueId:i,type:l,body:n}),!0!==p)return t.send(p);let u=!1;e.socket.on("close",(e=>{e&&(u=!0)})),U(4,`[export] Got an incoming HTTP request with ID ${i}.`),n.constr="string"==typeof n.constr&&n.constr||"chart";const d={export:{instr:c,type:l,constr:n.constr[0].toLowerCase()+n.constr.substr(1),height:n.height,width:n.width,scale:n.scale||s.export.scale,globalOptions:V(n.globalOptions,!0),themeOptions:V(n.themeOptions,!0)},customLogic:{allowCodeExecution:Pe,allowFileResources:!1,resources:V(n.resources,!0),callback:n.callback,customCode:n.customCode}};c&&(d.export.instr=X(c,d.customLogic.allowCodeExecution));const g=ee(s,d);if(g.export.options=c,g.payload={svg:n.svg||!1,b64:n.b64||!1,noDownload:n.noDownload||!1,requestId:i},n.svg&&(e=>[/xlink:href="(?:http:\/\/|https:\/\/)?localhost\b/,/xlink:href="(?:http:\/\/|https:\/\/)?10\.\d{1,3}\.\d{1,3}\.\d{1,3}\b/,/xlink:href="(?:http:\/\/|https:\/\/)?127\.\d{1,3}\.\d{1,3}\.\d{1,3}\b/,/xlink:href="(?:http:\/\/|https:\/\/)?172\.(1[6-9]|2[0-9]|3[0-1])\.\d{1,3}\.\d{1,3}\b/,/xlink:href="(?:http:\/\/|https:\/\/)?192\.168\.\d{1,3}\.\d{1,3}\b/].some((t=>t.test(e))))(g.payload.svg))throw new Ve("SVG potentially contain at least one forbidden URL in xlink:href element. Please review the SVG content and ensure that all referenced URLs comply with security policies.",400);await He(g,((o,c)=>{if(e.socket.removeAllListeners("close"),s.server.benchmarking&&U(5,`[benchmark] Request with ID ${i} - After the whole exporting process: ${r()}ms.`),u)return U(3,"[export] The client closed the connection before the chart finished processing.");if(o)throw o;if(!c||!c.result)throw new Ve(`Unexpected return from chart generation. Please check your request data. For the request with ID ${i}, the result is ${c.result}.`,400);return l=c.options.export.type,Ye(Je,e,t,{id:a,body:c.result}),c.result?n.b64?"pdf"===l||"svg"==l?t.send(Buffer.from(c.result,"utf8").toString("base64")):t.send(c.result):(t.header("Content-Type",Xe[l]||"image/png"),n.noDownload||t.attachment(`${e.params.filename||e.body.filename||"chart"}.${l||"png"}`),"svg"===l?t.send(c.result):t.send(Buffer.from(c.result,"base64"))):void 0}))}catch(e){r(e)}var o};const Qe=JSON.parse(e.readFileSync(t.join(M,"package.json"))),et=new Date,tt=[];function rt(e){if(!e)return!1;var t;t=setInterval((()=>{const e=Ae(),t=0===e.exportAttempts?1:e.performedExports/e.exportAttempts*100;tt.push(t),tt.length>30&&tt.shift()}),6e4),Fe.push(t),e.get("/health",((e,t)=>{const r=Ae(),o=tt.length,i=tt.reduce(((e,t)=>e+t),0)/tt.length;U(4,"[health.js] GET /health [200] - returning server health."),t.send({status:"OK",bootTime:et,uptime:Math.floor(((new Date).getTime()-et.getTime())/1e3/60)+" minutes",version:Qe.version,highchartsVersion:de(),averageProcessingTime:r.spentAverage,performedExports:r.performedExports,failedExports:r.droppedExports,exportAttempts:r.exportAttempts,sucessRatio:r.performedExports/r.exportAttempts*100,pool:Ne(),period:o,movingAverage:i,message:isNaN(i)||!tt.length?"Too early to report. No exports made yet. Please check back soon.":`Last ${o} minutes had a success rate of ${i.toFixed(2)}%.`,svgExportAttempts:r.exportFromSvgAttempts,jsonExportAttempts:r.performedExports-r.exportFromSvgAttempts})}))}const ot=new Map,it=f();it.disable("x-powered-by"),it.use(m()),it.use(((e,t,r)=>{t.set("Accept-Ranges","none"),r()}));const st=e=>{e.on("clientError",((e,t)=>{D(1,e,`[server] Client error: ${e.message}, destroying socket.`),t.destroy()})),e.on("error",(e=>{D(1,e,`[server] Server error: ${e.message}`)})),e.on("connection",(e=>{e.on("error",(e=>{D(1,e,`[server] Socket error: ${e.message}`)}))}))},nt=async r=>{try{const o=1024*(r.maxUploadSize||3)*1024,i=v.memoryStorage(),s=v({storage:i,limits:{fieldSize:o}});if(it.use(f.json({limit:o})),it.use(f.urlencoded({extended:!0,limit:o})),it.use(s.none()),!r.enable)return!1;if(!r.ssl.force){const e=l.createServer(it);st(e),e.listen(r.port,r.host),ot.set(r.port,e),U(3,`[server] Started HTTP server on ${r.host}:${r.port}.`)}if(r.ssl.enable){let o,i;try{o=await e.promises.readFile(t.posix.join(r.ssl.certPath,"server.key"),"utf8"),i=await e.promises.readFile(t.posix.join(r.ssl.certPath,"server.crt"),"utf8")}catch(e){U(2,`[server] Unable to load key/certificate from the '${r.ssl.certPath}' path. Could not run secured layer server.`)}if(o&&i){const e=c.createServer({key:o,cert:i},it);st(e),e.listen(r.ssl.port,r.host),ot.set(r.ssl.port,e),U(3,`[server] Started HTTPS server on ${r.host}:${r.ssl.port}.`)}}r.rateLimiting&&r.rateLimiting.enable&&![0,NaN].includes(r.rateLimiting.maxRequests)&&qe(it,r.rateLimiting),it.use(f.static(t.posix.join(M,"public"))),rt(it),(e=>{e.post("/",Ze),e.post("/:filename",Ze)})(it),(e=>{!!e&&e.get("/",((e,r)=>{r.sendFile(t.join(M,"public","index.html"),{acceptRanges:!1})}))})(it),Be(it),(e=>{e.use(Me),e.use(We)})(it)}catch(e){throw new se("[server] Could not configure and start the server.").setError(e)}},at=()=>{U(4,"[server] Closing all servers.");for(const[e,t]of ot)t.close((()=>{ot.delete(e),U(4,`[server] Closed server on port: ${e}.`)}))};var lt={startServer:nt,closeServers:at,getServers:()=>ot,enableRateLimiting:e=>qe(it,e),getExpress:()=>f,getApp:()=>it,use:(e,...t)=>{it.use(e,...t)},get:(e,...t)=>{it.get(e,...t)},post:(e,...t)=>{it.post(e,...t)}};const ct=async e=>{await Promise.allSettled([je(),at(),Oe()]),process.exit(e)};var pt={server:lt,startServer:nt,initExport:async e=>{var t;return t=e.customLogic&&e.customLogic.allowCodeExecution,Pe=K(t),(e=>{for(const[t,r]of Object.entries(e))H[t]=r;G(e&&parseInt(e.level)),e&&e.dest&&e.toFile&&F(e.dest,e.file||"highcharts-export-server.log")})(e.logging),e.other.listenToProcessExits&&(U(3,"[process] Attaching exit listeners to the process."),process.on("exit",(e=>{U(4,`Process exited with code ${e}.`)})),process.on("SIGINT",(async(e,t)=>{U(4,`The ${e} event with code: ${t}.`),await ct(0)})),process.on("SIGTERM",(async(e,t)=>{U(4,`The ${e} event with code: ${t}.`),await ct(0)})),process.on("SIGHUP",(async(e,t)=>{U(4,`The ${e} event with code: ${t}.`),await ct(0)})),process.on("uncaughtException",(async(e,t)=>{D(1,e,`The ${t} error.`),await ct(1)}))),await he(e),await Le({pool:e.pool||{minWorkers:1,maxWorkers:1},puppeteerArgs:e.puppeteer.args||[]}),e},singleExport:async t=>{t.export.instr=t.export.instr||t.export.options,await He(t,(async(t,r)=>{if(t)throw t;const{outfile:o,type:i}=r.options.export;e.writeFileSync(o||`chart.${i}`,"svg"!==i?Buffer.from(r.result,"base64"):r.result),await Oe()}))},batchExport:async t=>{const r=[];for(let o of t.export.batch.split(";"))o=o.split("="),2===o.length&&r.push(He({...t,export:{...t.export,infile:o[0],outfile:o[1]}},((t,r)=>{if(t)throw t;e.writeFileSync(r.options.export.outfile,"svg"!==r.options.export.type?Buffer.from(r.result,"base64"):r.result)})));try{await Promise.all(r),await Oe()}catch(e){throw new se("[chart] Error encountered during batch export.").setError(e)}},startExport:He,initPool:Le,killPool:Oe,setOptions:(t,r)=>(r?.length&&(Z=function(t){const r=t.findIndex((e=>"loadConfig"===e.replace(/-/g,"")));if(r>-1&&t[r+1]){const o=t[r+1];try{if(o&&o.endsWith(".json"))return JSON.parse(e.readFileSync(o))}catch(e){D(2,e,`[config] Unable to load the configuration from the ${o} file.`)}}return{}}(r)),te(E,Z),Z=re(E),t&&(Z=ee(Z,t,S)),r?.length&&(Z=function(e,t,r){let o=!1;for(let i=0;i(n.length-1===r&&(a=e[t].type),e[t])),r),n.reduce(((e,r,l)=>(n.length-1===l&&void 0!==e[r]&&(t[++i]?"boolean"===a?e[r]=K(t[i]):"number"===a?e[r]=+t[i]:a.indexOf("]")>=0?e[r]=t[i].split(","):e[r]=t[i]:(U(2,`[config] Missing value for the '${s}' argument. Using the default value.`),o=!0)),e[r])),e)}o&&z();return e}(Z,r,E)),Z),shutdownCleanUp:ct,log:U,logWithStack:D,setLogLevel:G,enableFileLogging:F,mapToNewConfig:e=>{const t={};for(const[r,o]of Object.entries(e)){const e=R[r]?R[r].split("."):[];e.reduce(((t,r,i)=>t[r]=e.length-1===i?o:t[r]||{}),t)}return t},manualConfig:async t=>{let r={};e.existsSync(t)&&(r=JSON.parse(e.readFileSync(t,"utf8")));const i=Object.keys(T).map((e=>({title:`${e} options`,value:e})));return o({type:"multiselect",name:"category",message:"Which category do you want to configure?",hint:"Space: Select specific, A: Select all, Enter: Confirm.",instructions:"",choices:i},{onSubmit:async(i,s)=>{let n=0,a=[];for(const e of s)T[e]=T[e].map((t=>({...t,section:e}))),a=[...a,...T[e]];return await o(a,{onSubmit:async(o,i)=>{if("moduleScripts"===o.name?(i=i.length?i.map((e=>o.choices[e])):o.choices,r[o.section][o.name]=i):r[o.section]=oe(Object.assign({},r[o.section]||{}),o.name.split("."),o.choices?o.choices[i]:i),++n===a.length){try{await e.promises.writeFile(t,JSON.stringify(r,null,2),"utf8")}catch(e){D(1,e,`[config] An error occurred while creating the ${t} file.`)}return!0}}}),!0}})},printLogo:r=>{const o=JSON.parse(e.readFileSync(t.join(M,"package.json"))).version;r?console.log(`Starting Highcharts Export Server v${o}...`):console.log(e.readFileSync(M+"/msg/startup.msg").toString().bold.yellow,`v${o}\n`.bold)},printUsage:z};module.exports=pt; -//# sourceMappingURL=data:application/json;charset=utf-8;base64,eyJ2ZXJzaW9uIjozLCJmaWxlIjoiaW5kZXguY2pzIiwic291cmNlcyI6WyIuLi9saWIvc2NoZW1hcy9jb25maWcuanMiLCIuLi9saWIvZW52cy5qcyIsIi4uL2xpYi9sb2dnZXIuanMiLCIuLi9saWIvdXRpbHMuanMiLCIuLi9saWIvY29uZmlnLmpzIiwiLi4vbGliL2ZldGNoLmpzIiwiLi4vbGliL2Vycm9ycy9FeHBvcnRFcnJvci5qcyIsIi4uL2xpYi9jYWNoZS5qcyIsIi4uL2xpYi9oaWdoY2hhcnRzLmpzIiwiLi4vbGliL2Jyb3dzZXIuanMiLCIuLi9saWIvZXhwb3J0LmpzIiwiLi4vdGVtcGxhdGVzL3N2Z19leHBvcnQvc3ZnX2V4cG9ydC5qcyIsIi4uL2xpYi9wb29sLmpzIiwiLi4vbGliL2NoYXJ0LmpzIiwiLi4vbGliL3Nhbml0aXplLmpzIiwiLi4vbGliL2ludGVydmFscy5qcyIsIi4uL2xpYi9zZXJ2ZXIvZXJyb3IuanMiLCIuLi9saWIvc2VydmVyL3JhdGVfbGltaXQuanMiLCIuLi9saWIvZXJyb3JzL0h0dHBFcnJvci5qcyIsIi4uL2xpYi9zZXJ2ZXIvcm91dGVzL2NoYW5nZV9oY192ZXJzaW9uLmpzIiwiLi4vbGliL3NlcnZlci9yb3V0ZXMvZXhwb3J0LmpzIiwiLi4vbGliL3NlcnZlci9yb3V0ZXMvaGVhbHRoLmpzIiwiLi4vbGliL3NlcnZlci9zZXJ2ZXIuanMiLCIuLi9saWIvc2VydmVyL3JvdXRlcy91aS5qcyIsIi4uL2xpYi9yZXNvdXJjZV9yZWxlYXNlLmpzIiwiLi4vbGliL2luZGV4LmpzIl0sInNvdXJjZXNDb250ZW50IjpbIi8qKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqXHJcblxyXG5IaWdoY2hhcnRzIEV4cG9ydCBTZXJ2ZXJcclxuXHJcbkNvcHlyaWdodCAoYykgMjAxNi0yMDI0LCBIaWdoc29mdFxyXG5cclxuTGljZW5jZWQgdW5kZXIgdGhlIE1JVCBsaWNlbmNlLlxyXG5cclxuQWRkaXRpb25hbGx5IGEgdmFsaWQgSGlnaGNoYXJ0cyBsaWNlbnNlIGlzIHJlcXVpcmVkIGZvciB1c2UuXHJcblxyXG5TZWUgTElDRU5TRSBmaWxlIGluIHJvb3QgZm9yIGRldGFpbHMuXHJcblxyXG4qKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqL1xyXG5cclxuLy8gUG9zc2libGUgbmFtZXMgZm9yIEhpZ2hjaGFydHMgc2NyaXB0c1xyXG5leHBvcnQgY29uc3Qgc2NyaXB0c05hbWVzID0ge1xyXG4gIGNvcmU6IFsnaGlnaGNoYXJ0cycsICdoaWdoY2hhcnRzLW1vcmUnLCAnaGlnaGNoYXJ0cy0zZCddLFxyXG4gIG1vZHVsZXM6IFtcclxuICAgICdzdG9jaycsXHJcbiAgICAnbWFwJyxcclxuICAgICdnYW50dCcsXHJcbiAgICAnZXhwb3J0aW5nJyxcclxuICAgICdwYXJhbGxlbC1jb29yZGluYXRlcycsXHJcbiAgICAnYWNjZXNzaWJpbGl0eScsXHJcbiAgICAvLyAnYW5ub3RhdGlvbnMtYWR2YW5jZWQnLFxyXG4gICAgJ2Jvb3N0LWNhbnZhcycsXHJcbiAgICAnYm9vc3QnLFxyXG4gICAgJ2RhdGEnLFxyXG4gICAgJ2RhdGEtdG9vbHMnLFxyXG4gICAgJ2RyYWdnYWJsZS1wb2ludHMnLFxyXG4gICAgJ3N0YXRpYy1zY2FsZScsXHJcbiAgICAnYnJva2VuLWF4aXMnLFxyXG4gICAgJ2hlYXRtYXAnLFxyXG4gICAgJ3RpbGVtYXAnLFxyXG4gICAgJ3RpbGVkd2VibWFwJyxcclxuICAgICd0aW1lbGluZScsXHJcbiAgICAndHJlZW1hcCcsXHJcbiAgICAndHJlZWdyYXBoJyxcclxuICAgICdpdGVtLXNlcmllcycsXHJcbiAgICAnZHJpbGxkb3duJyxcclxuICAgICdoaXN0b2dyYW0tYmVsbGN1cnZlJyxcclxuICAgICdidWxsZXQnLFxyXG4gICAgJ2Z1bm5lbCcsXHJcbiAgICAnZnVubmVsM2QnLFxyXG4gICAgJ2dlb2hlYXRtYXAnLFxyXG4gICAgJ3B5cmFtaWQzZCcsXHJcbiAgICAnbmV0d29ya2dyYXBoJyxcclxuICAgIC8vICdvdmVybGFwcGluZy1kYXRhbGFiZWxzJyxcclxuICAgICdwYXJldG8nLFxyXG4gICAgJ3BhdHRlcm4tZmlsbCcsXHJcbiAgICAncGljdG9yaWFsJyxcclxuICAgICdwcmljZS1pbmRpY2F0b3InLFxyXG4gICAgJ3NhbmtleScsXHJcbiAgICAnYXJjLWRpYWdyYW0nLFxyXG4gICAgJ2RlcGVuZGVuY3ktd2hlZWwnLFxyXG4gICAgJ3Nlcmllcy1sYWJlbCcsXHJcbiAgICAnc2VyaWVzLW9uLXBvaW50JyxcclxuICAgICdzb2xpZC1nYXVnZScsXHJcbiAgICAnc29uaWZpY2F0aW9uJyxcclxuICAgIC8vICdzdG9jay10b29scycsXHJcbiAgICAnc3RyZWFtZ3JhcGgnLFxyXG4gICAgJ3N1bmJ1cnN0JyxcclxuICAgICd2YXJpYWJsZS1waWUnLFxyXG4gICAgJ3Zhcml3aWRlJyxcclxuICAgICd2ZWN0b3InLFxyXG4gICAgJ3Zlbm4nLFxyXG4gICAgJ3dpbmRiYXJiJyxcclxuICAgICd3b3JkY2xvdWQnLFxyXG4gICAgJ3hyYW5nZScsXHJcbiAgICAnbm8tZGF0YS10by1kaXNwbGF5JyxcclxuICAgICdkcmFnLXBhbmVzJyxcclxuICAgICdkZWJ1Z2dlcicsXHJcbiAgICAnZHVtYmJlbGwnLFxyXG4gICAgJ2xvbGxpcG9wJyxcclxuICAgICdjeWxpbmRlcicsXHJcbiAgICAnb3JnYW5pemF0aW9uJyxcclxuICAgICdkb3RwbG90JyxcclxuICAgICdtYXJrZXItY2x1c3RlcnMnLFxyXG4gICAgJ2hvbGxvd2NhbmRsZXN0aWNrJyxcclxuICAgICdoZWlraW5hc2hpJyxcclxuICAgICdmbG93bWFwJyxcclxuICAgICdleHBvcnQtZGF0YScsXHJcbiAgICAnbmF2aWdhdG9yJyxcclxuICAgICd0ZXh0cGF0aCdcclxuICBdLFxyXG4gIGluZGljYXRvcnM6IFsnaW5kaWNhdG9ycy1hbGwnXSxcclxuICBjdXN0b206IFtcclxuICAgICdodHRwczovL2NkbmpzLmNsb3VkZmxhcmUuY29tL2FqYXgvbGlicy9tb21lbnQuanMvMi4zMC4xL21vbWVudC5taW4uanMnLFxyXG4gICAgJ2h0dHBzOi8vY2RuanMuY2xvdWRmbGFyZS5jb20vYWpheC9saWJzL21vbWVudC10aW1lem9uZS8wLjUuNDUvbW9tZW50LXRpbWV6b25lLXdpdGgtZGF0YS5taW4uanMnXHJcbiAgXVxyXG59O1xyXG5cclxuLy8gVGhpcyBpcyB0aGUgY29uZmlndXJhdGlvbiBvYmplY3Qgd2l0aCBhbGwgb3B0aW9ucyBhbmQgdGhlaXIgZGVmYXVsdCB2YWx1ZXMsXHJcbi8vIGFsc28gZnJvbSB0aGUgLmVudiBmaWxlIGlmIG9uZSBleGlzdHNcclxuZXhwb3J0IGNvbnN0IGRlZmF1bHRDb25maWcgPSB7XHJcbiAgcHVwcGV0ZWVyOiB7XHJcbiAgICBhcmdzOiB7XHJcbiAgICAgIHZhbHVlOiBbXHJcbiAgICAgICAgJy0tYWxsb3ctcnVubmluZy1pbnNlY3VyZS1jb250ZW50JyxcclxuICAgICAgICAnLS1hc2gtbm8tbnVkZ2VzJyxcclxuICAgICAgICAnLS1hdXRvcGxheS1wb2xpY3k9dXNlci1nZXN0dXJlLXJlcXVpcmVkJyxcclxuICAgICAgICAnLS1ibG9jay1uZXctd2ViLWNvbnRlbnRzJyxcclxuICAgICAgICAnLS1kaXNhYmxlLWFjY2VsZXJhdGVkLTJkLWNhbnZhcycsXHJcbiAgICAgICAgJy0tZGlzYWJsZS1iYWNrZ3JvdW5kLW5ldHdvcmtpbmcnLFxyXG4gICAgICAgICctLWRpc2FibGUtYmFja2dyb3VuZC10aW1lci10aHJvdHRsaW5nJyxcclxuICAgICAgICAnLS1kaXNhYmxlLWJhY2tncm91bmRpbmctb2NjbHVkZWQtd2luZG93cycsXHJcbiAgICAgICAgJy0tZGlzYWJsZS1icmVha3BhZCcsXHJcbiAgICAgICAgJy0tZGlzYWJsZS1jaGVja2VyLWltYWdpbmcnLFxyXG4gICAgICAgICctLWRpc2FibGUtY2xpZW50LXNpZGUtcGhpc2hpbmctZGV0ZWN0aW9uJyxcclxuICAgICAgICAnLS1kaXNhYmxlLWNvbXBvbmVudC1leHRlbnNpb25zLXdpdGgtYmFja2dyb3VuZC1wYWdlcycsXHJcbiAgICAgICAgJy0tZGlzYWJsZS1jb21wb25lbnQtdXBkYXRlJyxcclxuICAgICAgICAnLS1kaXNhYmxlLWRlZmF1bHQtYXBwcycsXHJcbiAgICAgICAgJy0tZGlzYWJsZS1kZXYtc2htLXVzYWdlJyxcclxuICAgICAgICAnLS1kaXNhYmxlLWRvbWFpbi1yZWxpYWJpbGl0eScsXHJcbiAgICAgICAgJy0tZGlzYWJsZS1leHRlbnNpb25zJyxcclxuICAgICAgICAnLS1kaXNhYmxlLWZlYXR1cmVzPUNhbGN1bGF0ZU5hdGl2ZVdpbk9jY2x1c2lvbixJbnRlcmVzdEZlZWRDb250ZW50U3VnZ2VzdGlvbnMsV2ViT1RQJyxcclxuICAgICAgICAnLS1kaXNhYmxlLWhhbmctbW9uaXRvcicsXHJcbiAgICAgICAgJy0tZGlzYWJsZS1pcGMtZmxvb2RpbmctcHJvdGVjdGlvbicsXHJcbiAgICAgICAgJy0tZGlzYWJsZS1sb2dnaW5nJyxcclxuICAgICAgICAnLS1kaXNhYmxlLW5vdGlmaWNhdGlvbnMnLFxyXG4gICAgICAgICctLWRpc2FibGUtb2ZmZXItc3RvcmUtdW5tYXNrZWQtd2FsbGV0LWNhcmRzJyxcclxuICAgICAgICAnLS1kaXNhYmxlLXBvcHVwLWJsb2NraW5nJyxcclxuICAgICAgICAnLS1kaXNhYmxlLXByaW50LXByZXZpZXcnLFxyXG4gICAgICAgICctLWRpc2FibGUtcHJvbXB0LW9uLXJlcG9zdCcsXHJcbiAgICAgICAgJy0tZGlzYWJsZS1yZW5kZXJlci1iYWNrZ3JvdW5kaW5nJyxcclxuICAgICAgICAnLS1kaXNhYmxlLXNlYXJjaC1lbmdpbmUtY2hvaWNlLXNjcmVlbicsXHJcbiAgICAgICAgJy0tZGlzYWJsZS1zZXNzaW9uLWNyYXNoZWQtYnViYmxlJyxcclxuICAgICAgICAnLS1kaXNhYmxlLXNldHVpZC1zYW5kYm94JyxcclxuICAgICAgICAnLS1kaXNhYmxlLXNpdGUtaXNvbGF0aW9uLXRyaWFscycsXHJcbiAgICAgICAgJy0tZGlzYWJsZS1zcGVlY2gtYXBpJyxcclxuICAgICAgICAnLS1kaXNhYmxlLXN5bmMnLFxyXG4gICAgICAgICctLWVuYWJsZS11bnNhZmUtd2ViZ3B1JyxcclxuICAgICAgICAnLS1oaWRlLWNyYXNoLXJlc3RvcmUtYnViYmxlJyxcclxuICAgICAgICAnLS1oaWRlLXNjcm9sbGJhcnMnLFxyXG4gICAgICAgICctLW1ldHJpY3MtcmVjb3JkaW5nLW9ubHknLFxyXG4gICAgICAgICctLW11dGUtYXVkaW8nLFxyXG4gICAgICAgICctLW5vLWRlZmF1bHQtYnJvd3Nlci1jaGVjaycsXHJcbiAgICAgICAgJy0tbm8tZmlyc3QtcnVuJyxcclxuICAgICAgICAnLS1uby1waW5ncycsXHJcbiAgICAgICAgJy0tcGlwZScsXHJcbiAgICAgICAgLy8gJy0tbm8tc2FuZGJveCcsXHJcbiAgICAgICAgJy0tbm8tc3RhcnR1cC13aW5kb3cnLFxyXG4gICAgICAgIC8vICctLW5vLXp5Z290ZScsXHJcbiAgICAgICAgJy0tcGFzc3dvcmQtc3RvcmU9YmFzaWMnLFxyXG4gICAgICAgICctLXByb2Nlc3MtcGVyLXRhYicsXHJcbiAgICAgICAgJy0tdXNlLW1vY2sta2V5Y2hhaW4nXHJcbiAgICAgIF0sXHJcbiAgICAgIHR5cGU6ICdzdHJpbmdbXScsXHJcbiAgICAgIGRlc2NyaXB0aW9uOiAnQXJndW1lbnRzIGFycmF5IHRvIHNlbmQgdG8gUHVwcGV0ZWVyLidcclxuICAgIH0sXHJcbiAgICB0ZW1wRGlyOiB7XHJcbiAgICAgIHZhbHVlOiAnLi90bXAvJyxcclxuICAgICAgdHlwZTogJ3N0cmluZycsXHJcbiAgICAgIGVudkxpbms6ICdQVVBQRVRFRVJfVEVNUF9ESVInLFxyXG4gICAgICBkZXNjcmlwdGlvbjogJ1RoZSBkaXJlY3RvcnkgZm9yIFB1cHBldGVlciB0byBzdG9yZSB0ZW1wb3JhcnkgZmlsZXMuJ1xyXG4gICAgfVxyXG4gIH0sXHJcbiAgaGlnaGNoYXJ0czoge1xyXG4gICAgdmVyc2lvbjoge1xyXG4gICAgICB2YWx1ZTogJ2xhdGVzdCcsXHJcbiAgICAgIHR5cGU6ICdzdHJpbmcnLFxyXG4gICAgICBlbnZMaW5rOiAnSElHSENIQVJUU19WRVJTSU9OJyxcclxuICAgICAgZGVzY3JpcHRpb246ICdUaGUgSGlnaGNoYXJ0cyB2ZXJzaW9uIHRvIGJlIHVzZWQuJ1xyXG4gICAgfSxcclxuICAgIGNkblVSTDoge1xyXG4gICAgICB2YWx1ZTogJ2h0dHBzOi8vY29kZS5oaWdoY2hhcnRzLmNvbS8nLFxyXG4gICAgICB0eXBlOiAnc3RyaW5nJyxcclxuICAgICAgZW52TGluazogJ0hJR0hDSEFSVFNfQ0ROX1VSTCcsXHJcbiAgICAgIGRlc2NyaXB0aW9uOiAnVGhlIENETiBVUkwgZm9yIEhpZ2hjaGFydHMgc2NyaXB0cyB0byBiZSB1c2VkLidcclxuICAgIH0sXHJcbiAgICB1c2VOcG06IHtcclxuICAgICAgdmFsdWU6IGZhbHNlLFxyXG4gICAgICB0eXBlOiAnYm9vbGVhbicsXHJcbiAgICAgIGVudkxpbms6ICdISUdIQ0hBUlRTX1VTRV9OUE0nLFxyXG4gICAgICBkZXNjcmlwdGlvbjogJ0ZsYWcgdG8gdXNlIEhpZ2hjaGFydHMgc2NyaXB0cyBmcm9tIE5QTSBwYWNrYWdlJ1xyXG4gICAgfSxcclxuICAgIGNvcmVTY3JpcHRzOiB7XHJcbiAgICAgIHZhbHVlOiBzY3JpcHRzTmFtZXMuY29yZSxcclxuICAgICAgdHlwZTogJ3N0cmluZ1tdJyxcclxuICAgICAgZW52TGluazogJ0hJR0hDSEFSVFNfQ09SRV9TQ1JJUFRTJyxcclxuICAgICAgZGVzY3JpcHRpb246ICdUaGUgY29yZSBIaWdoY2hhcnRzIHNjcmlwdHMgdG8gZmV0Y2guJ1xyXG4gICAgfSxcclxuICAgIG1vZHVsZVNjcmlwdHM6IHtcclxuICAgICAgdmFsdWU6IHNjcmlwdHNOYW1lcy5tb2R1bGVzLFxyXG4gICAgICB0eXBlOiAnc3RyaW5nW10nLFxyXG4gICAgICBlbnZMaW5rOiAnSElHSENIQVJUU19NT0RVTEVfU0NSSVBUUycsXHJcbiAgICAgIGRlc2NyaXB0aW9uOiAnVGhlIG1vZHVsZXMgb2YgSGlnaGNoYXJ0cyB0byBmZXRjaC4nXHJcbiAgICB9LFxyXG4gICAgaW5kaWNhdG9yU2NyaXB0czoge1xyXG4gICAgICB2YWx1ZTogc2NyaXB0c05hbWVzLmluZGljYXRvcnMsXHJcbiAgICAgIHR5cGU6ICdzdHJpbmdbXScsXHJcbiAgICAgIGVudkxpbms6ICdISUdIQ0hBUlRTX0lORElDQVRPUl9TQ1JJUFRTJyxcclxuICAgICAgZGVzY3JpcHRpb246ICdUaGUgaW5kaWNhdG9ycyBvZiBIaWdoY2hhcnRzIHRvIGZldGNoLidcclxuICAgIH0sXHJcbiAgICBjdXN0b21TY3JpcHRzOiB7XHJcbiAgICAgIHZhbHVlOiBzY3JpcHRzTmFtZXMuY3VzdG9tLFxyXG4gICAgICB0eXBlOiAnc3RyaW5nW10nLFxyXG4gICAgICBkZXNjcmlwdGlvbjogJ0FkZGl0aW9uYWwgY3VzdG9tIHNjcmlwdHMgb3IgZGVwZW5kZW5jaWVzIHRvIGZldGNoLidcclxuICAgIH0sXHJcbiAgICBmb3JjZUZldGNoOiB7XHJcbiAgICAgIHZhbHVlOiBmYWxzZSxcclxuICAgICAgdHlwZTogJ2Jvb2xlYW4nLFxyXG4gICAgICBlbnZMaW5rOiAnSElHSENIQVJUU19GT1JDRV9GRVRDSCcsXHJcbiAgICAgIGRlc2NyaXB0aW9uOlxyXG4gICAgICAgICdUaGUgZmxhZyB0byBkZXRlcm1pbmUgd2hldGhlciB0byByZWZldGNoIGFsbCBzY3JpcHRzIGFmdGVyIGVhY2ggc2VydmVyIHJlcnVuLidcclxuICAgIH0sXHJcbiAgICBjYWNoZVBhdGg6IHtcclxuICAgICAgdmFsdWU6ICcuY2FjaGUnLFxyXG4gICAgICB0eXBlOiAnc3RyaW5nJyxcclxuICAgICAgZW52TGluazogJ0hJR0hDSEFSVFNfQ0FDSEVfUEFUSCcsXHJcbiAgICAgIGRlc2NyaXB0aW9uOlxyXG4gICAgICAgICdUaGUgcGF0aCB0byB0aGUgY2FjaGUgZGlyZWN0b3J5LiBJdCBpcyB1c2VkIHRvIHN0b3JlIHRoZSBIaWdoY2hhcnRzIHNjcmlwdHMgYW5kIGN1c3RvbSBzY3JpcHRzLidcclxuICAgIH1cclxuICB9LFxyXG4gIGV4cG9ydDoge1xyXG4gICAgaW5maWxlOiB7XHJcbiAgICAgIHZhbHVlOiBmYWxzZSxcclxuICAgICAgdHlwZTogJ3N0cmluZycsXHJcbiAgICAgIGRlc2NyaXB0aW9uOlxyXG4gICAgICAgICdUaGUgaW5wdXQgZmlsZSBzaG91bGQgaW5jbHVkZSBhIG5hbWUgYW5kIGEgdHlwZSAoanNvbiBvciBzdmcpLiBJdCBtdXN0IGJlIGNvcnJlY3RseSBmb3JtYXR0ZWQgYXMgYSBKU09OIG9yIFNWRyBmaWxlLidcclxuICAgIH0sXHJcbiAgICBpbnN0cjoge1xyXG4gICAgICB2YWx1ZTogZmFsc2UsXHJcbiAgICAgIHR5cGU6ICdzdHJpbmcnLFxyXG4gICAgICBkZXNjcmlwdGlvbjpcclxuICAgICAgICAnSW5wdXQsIHByb3ZpZGVkIGluIHRoZSBmb3JtIG9mIGEgc3RyaW5naWZpZWQgSlNPTiBvciBTVkcgZmlsZSwgd2lsbCBvdmVycmlkZSB0aGUgLS1pbmZpbGUgb3B0aW9uLidcclxuICAgIH0sXHJcbiAgICBvcHRpb25zOiB7XHJcbiAgICAgIHZhbHVlOiBmYWxzZSxcclxuICAgICAgdHlwZTogJ3N0cmluZycsXHJcbiAgICAgIGRlc2NyaXB0aW9uOiAnQW4gYWxpYXMgZm9yIHRoZSAtLWluc3RyIG9wdGlvbi4nXHJcbiAgICB9LFxyXG4gICAgb3V0ZmlsZToge1xyXG4gICAgICB2YWx1ZTogZmFsc2UsXHJcbiAgICAgIHR5cGU6ICdzdHJpbmcnLFxyXG4gICAgICBkZXNjcmlwdGlvbjpcclxuICAgICAgICAnVGhlIG91dHB1dCBmaWxlbmFtZSBhbG9uZyB3aXRoIGEgdHlwZSAoanBlZywgcG5nLCBwZGYsIG9yIHN2ZykuIFRoaXMgd2lsbCBpZ25vcmUgdGhlIC0tdHlwZSBmbGFnLidcclxuICAgIH0sXHJcbiAgICB0eXBlOiB7XHJcbiAgICAgIHZhbHVlOiAncG5nJyxcclxuICAgICAgdHlwZTogJ3N0cmluZycsXHJcbiAgICAgIGVudkxpbms6ICdFWFBPUlRfVFlQRScsXHJcbiAgICAgIGRlc2NyaXB0aW9uOiAnVGhlIGZpbGUgZXhwb3J0IGZvcm1hdC4gSXQgY2FuIGJlIGpwZWcsIHBuZywgcGRmLCBvciBzdmcuJ1xyXG4gICAgfSxcclxuICAgIGNvbnN0cjoge1xyXG4gICAgICB2YWx1ZTogJ2NoYXJ0JyxcclxuICAgICAgdHlwZTogJ3N0cmluZycsXHJcbiAgICAgIGVudkxpbms6ICdFWFBPUlRfQ09OU1RSJyxcclxuICAgICAgZGVzY3JpcHRpb246XHJcbiAgICAgICAgJ1RoZSBjb25zdHJ1Y3RvciB0byB1c2UuIENhbiBiZSBjaGFydCwgc3RvY2tDaGFydCwgbWFwQ2hhcnQsIG9yIGdhbnR0Q2hhcnQuJ1xyXG4gICAgfSxcclxuICAgIGRlZmF1bHRIZWlnaHQ6IHtcclxuICAgICAgdmFsdWU6IDQwMCxcclxuICAgICAgdHlwZTogJ251bWJlcicsXHJcbiAgICAgIGVudkxpbms6ICdFWFBPUlRfREVGQVVMVF9IRUlHSFQnLFxyXG4gICAgICBkZXNjcmlwdGlvbjpcclxuICAgICAgICAndGhlIGRlZmF1bHQgaGVpZ2h0IG9mIHRoZSBleHBvcnRlZCBjaGFydC4gVXNlZCB3aGVuIG5vIHZhbHVlIGlzIHNldC4nXHJcbiAgICB9LFxyXG4gICAgZGVmYXVsdFdpZHRoOiB7XHJcbiAgICAgIHZhbHVlOiA2MDAsXHJcbiAgICAgIHR5cGU6ICdudW1iZXInLFxyXG4gICAgICBlbnZMaW5rOiAnRVhQT1JUX0RFRkFVTFRfV0lEVEgnLFxyXG4gICAgICBkZXNjcmlwdGlvbjpcclxuICAgICAgICAnVGhlIGRlZmF1bHQgd2lkdGggb2YgdGhlIGV4cG9ydGVkIGNoYXJ0LiBVc2VkIHdoZW4gbm8gdmFsdWUgaXMgc2V0LidcclxuICAgIH0sXHJcbiAgICBkZWZhdWx0U2NhbGU6IHtcclxuICAgICAgdmFsdWU6IDEsXHJcbiAgICAgIHR5cGU6ICdudW1iZXInLFxyXG4gICAgICBlbnZMaW5rOiAnRVhQT1JUX0RFRkFVTFRfU0NBTEUnLFxyXG4gICAgICBkZXNjcmlwdGlvbjpcclxuICAgICAgICAnVGhlIGRlZmF1bHQgc2NhbGUgb2YgdGhlIGV4cG9ydGVkIGNoYXJ0LiBVc2VkIHdoZW4gbm8gdmFsdWUgaXMgc2V0LidcclxuICAgIH0sXHJcbiAgICBoZWlnaHQ6IHtcclxuICAgICAgdmFsdWU6IGZhbHNlLFxyXG4gICAgICB0eXBlOiAnbnVtYmVyJyxcclxuICAgICAgZGVzY3JpcHRpb246XHJcbiAgICAgICAgJ1RoZSBoZWlnaHQgb2YgdGhlIGV4cG9ydGVkIGNoYXJ0LCBvdmVycmlkaW5nIHRoZSBvcHRpb24gaW4gdGhlIGNoYXJ0IHNldHRpbmdzLidcclxuICAgIH0sXHJcbiAgICB3aWR0aDoge1xyXG4gICAgICB2YWx1ZTogZmFsc2UsXHJcbiAgICAgIHR5cGU6ICdudW1iZXInLFxyXG4gICAgICBkZXNjcmlwdGlvbjpcclxuICAgICAgICAnVGhlIHdpZHRoIG9mIHRoZSBleHBvcnRlZCBjaGFydCwgb3ZlcnJpZGluZyB0aGUgb3B0aW9uIGluIHRoZSBjaGFydCBzZXR0aW5ncy4nXHJcbiAgICB9LFxyXG4gICAgc2NhbGU6IHtcclxuICAgICAgdmFsdWU6IGZhbHNlLFxyXG4gICAgICB0eXBlOiAnbnVtYmVyJyxcclxuICAgICAgZGVzY3JpcHRpb246XHJcbiAgICAgICAgJ1RoZSBzY2FsZSBvZiB0aGUgZXhwb3J0ZWQgY2hhcnQsIG92ZXJyaWRpbmcgdGhlIG9wdGlvbiBpbiB0aGUgY2hhcnQgc2V0dGluZ3MuIFJhbmdlcyBiZXR3ZWVuIDAuMSBhbmQgNS4wLidcclxuICAgIH0sXHJcbiAgICBnbG9iYWxPcHRpb25zOiB7XHJcbiAgICAgIHZhbHVlOiBmYWxzZSxcclxuICAgICAgdHlwZTogJ3N0cmluZycsXHJcbiAgICAgIGRlc2NyaXB0aW9uOlxyXG4gICAgICAgICdFaXRoZXIgYSBzdHJpbmdpZmllZCBKU09OIG9yIGEgZmlsZW5hbWUgY29udGFpbmluZyBvcHRpb25zIHRvIGJlIHBhc3NlZCBpbnRvIHRoZSBIaWdoY2hhcnRzLnNldE9wdGlvbnMuJ1xyXG4gICAgfSxcclxuICAgIHRoZW1lT3B0aW9uczoge1xyXG4gICAgICB2YWx1ZTogZmFsc2UsXHJcbiAgICAgIHR5cGU6ICdzdHJpbmcnLFxyXG4gICAgICBkZXNjcmlwdGlvbjpcclxuICAgICAgICAnRWl0aGVyIGEgc3RyaW5naWZpZWQgSlNPTiBvciBhIGZpbGVuYW1lIGNvbnRhaW5pbmcgdGhlbWUgb3B0aW9ucyB0byBiZSBwYXNzZWQgaW50byB0aGUgSGlnaGNoYXJ0cy5zZXRPcHRpb25zLidcclxuICAgIH0sXHJcbiAgICBiYXRjaDoge1xyXG4gICAgICB2YWx1ZTogZmFsc2UsXHJcbiAgICAgIHR5cGU6ICdzdHJpbmcnLFxyXG4gICAgICBkZXNjcmlwdGlvbjpcclxuICAgICAgICAnSW5pdGlhdGVzIGEgYmF0Y2ggam9iIHdpdGggYSBzdHJpbmcgY29udGFpbmluZyBpbnB1dC9vdXRwdXQgcGFpcnM6IFwiaW49b3V0O2luPW91dDsuLi5cIi4nXHJcbiAgICB9LFxyXG4gICAgcmFzdGVyaXphdGlvblRpbWVvdXQ6IHtcclxuICAgICAgdmFsdWU6IDE1MDAsXHJcbiAgICAgIHR5cGU6ICdudW1iZXInLFxyXG4gICAgICBlbnZMaW5rOiAnRVhQT1JUX1JBU1RFUklaQVRJT05fVElNRU9VVCcsXHJcbiAgICAgIGRlc2NyaXB0aW9uOlxyXG4gICAgICAgICdUaGUgZHVyYXRpb24gaW4gbWlsbGlzZWNvbmRzIHRvIHdhaXQgZm9yIHJlbmRlcmluZyBhIHdlYnBhZ2UuJ1xyXG4gICAgfVxyXG4gIH0sXHJcbiAgY3VzdG9tTG9naWM6IHtcclxuICAgIGFsbG93Q29kZUV4ZWN1dGlvbjoge1xyXG4gICAgICB2YWx1ZTogZmFsc2UsXHJcbiAgICAgIHR5cGU6ICdib29sZWFuJyxcclxuICAgICAgZW52TGluazogJ0NVU1RPTV9MT0dJQ19BTExPV19DT0RFX0VYRUNVVElPTicsXHJcbiAgICAgIGRlc2NyaXB0aW9uOlxyXG4gICAgICAgICdDb250cm9scyB3aGV0aGVyIHRoZSBleGVjdXRpb24gb2YgYXJiaXRyYXJ5IGNvZGUgaXMgYWxsb3dlZCBkdXJpbmcgdGhlIGV4cG9ydGluZyBwcm9jZXNzLidcclxuICAgIH0sXHJcbiAgICBhbGxvd0ZpbGVSZXNvdXJjZXM6IHtcclxuICAgICAgdmFsdWU6IGZhbHNlLFxyXG4gICAgICB0eXBlOiAnYm9vbGVhbicsXHJcbiAgICAgIGVudkxpbms6ICdDVVNUT01fTE9HSUNfQUxMT1dfRklMRV9SRVNPVVJDRVMnLFxyXG4gICAgICBkZXNjcmlwdGlvbjpcclxuICAgICAgICAnQ29udHJvbHMgdGhlIGFiaWxpdHkgdG8gaW5qZWN0IHJlc291cmNlcyBmcm9tIHRoZSBmaWxlc3lzdGVtLiBUaGlzIHNldHRpbmcgaGFzIG5vIGVmZmVjdCB3aGVuIHJ1bm5pbmcgYXMgYSBzZXJ2ZXIuJ1xyXG4gICAgfSxcclxuICAgIGN1c3RvbUNvZGU6IHtcclxuICAgICAgdmFsdWU6IGZhbHNlLFxyXG4gICAgICB0eXBlOiAnc3RyaW5nJyxcclxuICAgICAgZGVzY3JpcHRpb246XHJcbiAgICAgICAgJ0N1c3RvbSBjb2RlIHRvIGV4ZWN1dGUgYmVmb3JlIGNoYXJ0IGluaXRpYWxpemF0aW9uLiBJdCBjYW4gYmUgYSBmdW5jdGlvbiwgY29kZSB3cmFwcGVkIHdpdGhpbiBhIGZ1bmN0aW9uLCBvciBhIGZpbGVuYW1lIHdpdGggdGhlIC5qcyBleHRlbnNpb24uJ1xyXG4gICAgfSxcclxuICAgIGNhbGxiYWNrOiB7XHJcbiAgICAgIHZhbHVlOiBmYWxzZSxcclxuICAgICAgdHlwZTogJ3N0cmluZycsXHJcbiAgICAgIGRlc2NyaXB0aW9uOlxyXG4gICAgICAgICdKYXZhU2NyaXB0IGNvZGUgdG8gcnVuIGR1cmluZyBjb25zdHJ1Y3Rpb24uIEl0IGNhbiBiZSBhIGZ1bmN0aW9uIG9yIGEgZmlsZW5hbWUgd2l0aCB0aGUgLmpzIGV4dGVuc2lvbi4nXHJcbiAgICB9LFxyXG4gICAgcmVzb3VyY2VzOiB7XHJcbiAgICAgIHZhbHVlOiBmYWxzZSxcclxuICAgICAgdHlwZTogJ3N0cmluZycsXHJcbiAgICAgIGRlc2NyaXB0aW9uOlxyXG4gICAgICAgICdBZGRpdGlvbmFsIHJlc291cmNlIGluIHRoZSBmb3JtIG9mIGEgc3RyaW5naWZpZWQgSlNPTiwgd2hpY2ggbWF5IGNvbnRhaW4gZmlsZXMsIGpzLCBhbmQgY3NzIHNlY3Rpb25zLidcclxuICAgIH0sXHJcbiAgICBsb2FkQ29uZmlnOiB7XHJcbiAgICAgIHZhbHVlOiBmYWxzZSxcclxuICAgICAgdHlwZTogJ3N0cmluZycsXHJcbiAgICAgIGxlZ2FjeU5hbWU6ICdmcm9tRmlsZScsXHJcbiAgICAgIGRlc2NyaXB0aW9uOiAnQSBmaWxlIGNvbnRhaW5pbmcgYSBwcmUtZGVmaW5lZCBjb25maWd1cmF0aW9uIHRvIHVzZS4nXHJcbiAgICB9LFxyXG4gICAgY3JlYXRlQ29uZmlnOiB7XHJcbiAgICAgIHZhbHVlOiBmYWxzZSxcclxuICAgICAgdHlwZTogJ3N0cmluZycsXHJcbiAgICAgIGRlc2NyaXB0aW9uOlxyXG4gICAgICAgICdFbmFibGVzIHNldHRpbmcgb3B0aW9ucyB0aHJvdWdoIGEgcHJvbXB0IGFuZCBzYXZpbmcgdGhlbSBpbiBhIHByb3ZpZGVkIGNvbmZpZyBmaWxlLidcclxuICAgIH1cclxuICB9LFxyXG4gIHNlcnZlcjoge1xyXG4gICAgbWF4VXBsb2FkU2l6ZToge1xyXG4gICAgICB2YWx1ZTogMyxcclxuICAgICAgdHlwZTogJ251bWJlcicsXHJcbiAgICAgIGVudkxpbms6ICdTRVJWRVJfTUFYX1VQTE9BRF9TSVpFJyxcclxuICAgICAgZGVzY3JpcHRpb246ICdUaGUgbWF4aW11bSB1cGxvYWQgc2l6ZSwgaW4gTUIsIGZvciB0aGUgc2VydmVyLidcclxuICAgIH0sXHJcbiAgICBlbmFibGU6IHtcclxuICAgICAgdmFsdWU6IGZhbHNlLFxyXG4gICAgICB0eXBlOiAnYm9vbGVhbicsXHJcbiAgICAgIGVudkxpbms6ICdTRVJWRVJfRU5BQkxFJyxcclxuICAgICAgY2xpTmFtZTogJ2VuYWJsZVNlcnZlcicsXHJcbiAgICAgIGRlc2NyaXB0aW9uOlxyXG4gICAgICAgICdXaGVuIHNldCB0byB0cnVlLCB0aGUgc2VydmVyIHN0YXJ0cyBvbiB0aGUgbG9jYWwgSVAgYWRkcmVzcyAwLjAuMC4wLidcclxuICAgIH0sXHJcbiAgICBob3N0OiB7XHJcbiAgICAgIHZhbHVlOiAnMC4wLjAuMCcsXHJcbiAgICAgIHR5cGU6ICdzdHJpbmcnLFxyXG4gICAgICBlbnZMaW5rOiAnU0VSVkVSX0hPU1QnLFxyXG4gICAgICBkZXNjcmlwdGlvbjpcclxuICAgICAgICAnVGhlIGhvc3RuYW1lIG9mIHRoZSBzZXJ2ZXIuIEFkZGl0aW9uYWxseSwgaXQgc3RhcnRzIGEgc2VydmVyIG9uIHRoZSBwcm92aWRlZCBob3N0bmFtZS4nXHJcbiAgICB9LFxyXG4gICAgcG9ydDoge1xyXG4gICAgICB2YWx1ZTogNzgwMSxcclxuICAgICAgdHlwZTogJ251bWJlcicsXHJcbiAgICAgIGVudkxpbms6ICdTRVJWRVJfUE9SVCcsXHJcbiAgICAgIGRlc2NyaXB0aW9uOiAnVGhlIHNlcnZlciBwb3J0IHdoZW4gZW5hYmxlZC4nXHJcbiAgICB9LFxyXG4gICAgYmVuY2htYXJraW5nOiB7XHJcbiAgICAgIHZhbHVlOiBmYWxzZSxcclxuICAgICAgdHlwZTogJ2Jvb2xlYW4nLFxyXG4gICAgICBlbnZMaW5rOiAnU0VSVkVSX0JFTkNITUFSS0lORycsXHJcbiAgICAgIGNsaU5hbWU6ICdzZXJ2ZXJCZW5jaG1hcmtpbmcnLFxyXG4gICAgICBkZXNjcmlwdGlvbjpcclxuICAgICAgICAnSW5kaWNhdGVzIHdoZXRoZXIgdG8gZGlzcGxheSB0aGUgZHVyYXRpb24sIGluIG1pbGxpc2Vjb25kcywgb2Ygc3BlY2lmaWMgYWN0aW9ucyB0aGF0IG9jY3VyIG9uIHRoZSBzZXJ2ZXIgd2hpbGUgc2VydmluZyBhIHJlcXVlc3QuJ1xyXG4gICAgfSxcclxuICAgIHByb3h5OiB7XHJcbiAgICAgIGhvc3Q6IHtcclxuICAgICAgICB2YWx1ZTogZmFsc2UsXHJcbiAgICAgICAgdHlwZTogJ3N0cmluZycsXHJcbiAgICAgICAgZW52TGluazogJ1NFUlZFUl9QUk9YWV9IT1NUJyxcclxuICAgICAgICBjbGlOYW1lOiAncHJveHlIb3N0JyxcclxuICAgICAgICBkZXNjcmlwdGlvbjogJ1RoZSBob3N0IG9mIHRoZSBwcm94eSBzZXJ2ZXIgdG8gdXNlLCBpZiBpdCBleGlzdHMuJ1xyXG4gICAgICB9LFxyXG4gICAgICBwb3J0OiB7XHJcbiAgICAgICAgdmFsdWU6IDgwODAsXHJcbiAgICAgICAgdHlwZTogJ251bWJlcicsXHJcbiAgICAgICAgZW52TGluazogJ1NFUlZFUl9QUk9YWV9QT1JUJyxcclxuICAgICAgICBjbGlOYW1lOiAncHJveHlQb3J0JyxcclxuICAgICAgICBkZXNjcmlwdGlvbjogJ1RoZSBwb3J0IG9mIHRoZSBwcm94eSBzZXJ2ZXIgdG8gdXNlLCBpZiBpdCBleGlzdHMuJ1xyXG4gICAgICB9LFxyXG4gICAgICB1c2VybmFtZToge1xyXG4gICAgICAgIHZhbHVlOiBmYWxzZSxcclxuICAgICAgICB0eXBlOiAnc3RyaW5nJyxcclxuICAgICAgICBlbnZMaW5rOiAnU0VSVkVSX1BST1hZX1VTRVJOQU1FJyxcclxuICAgICAgICBjbGlOYW1lOiAncHJveHlVc2VybmFtZScsXHJcbiAgICAgICAgZGVzY3JpcHRpb246ICdUaGUgdXNlcm5hbWUgZm9yIHRoZSBwcm94eSBzZXJ2ZXIsIGlmIGl0IGV4aXN0cy4nXHJcbiAgICAgIH0sXHJcbiAgICAgIHBhc3N3b3JkOiB7XHJcbiAgICAgICAgdmFsdWU6IGZhbHNlLFxyXG4gICAgICAgIHR5cGU6ICdzdHJpbmcnLFxyXG4gICAgICAgIGVudkxpbms6ICdTRVJWRVJfUFJPWFlfUEFTU1dPUkQnLFxyXG4gICAgICAgIGNsaU5hbWU6ICdwcm94eVBhc3N3b3JkJyxcclxuICAgICAgICBkZXNjcmlwdGlvbjogJ1RoZSBwYXNzd29yZCBmb3IgdGhlIHByb3h5IHNlcnZlciwgaWYgaXQgZXhpc3RzLidcclxuICAgICAgfSxcclxuICAgICAgdGltZW91dDoge1xyXG4gICAgICAgIHZhbHVlOiA1MDAwLFxyXG4gICAgICAgIHR5cGU6ICdudW1iZXInLFxyXG4gICAgICAgIGVudkxpbms6ICdTRVJWRVJfUFJPWFlfVElNRU9VVCcsXHJcbiAgICAgICAgY2xpTmFtZTogJ3Byb3h5VGltZW91dCcsXHJcbiAgICAgICAgZGVzY3JpcHRpb246ICdUaGUgdGltZW91dCBmb3IgdGhlIHByb3h5IHNlcnZlciB0byB1c2UsIGlmIGl0IGV4aXN0cy4nXHJcbiAgICAgIH1cclxuICAgIH0sXHJcbiAgICByYXRlTGltaXRpbmc6IHtcclxuICAgICAgZW5hYmxlOiB7XHJcbiAgICAgICAgdmFsdWU6IGZhbHNlLFxyXG4gICAgICAgIHR5cGU6ICdib29sZWFuJyxcclxuICAgICAgICBlbnZMaW5rOiAnU0VSVkVSX1JBVEVfTElNSVRJTkdfRU5BQkxFJyxcclxuICAgICAgICBjbGlOYW1lOiAnZW5hYmxlUmF0ZUxpbWl0aW5nJyxcclxuICAgICAgICBkZXNjcmlwdGlvbjogJ0VuYWJsZXMgcmF0ZSBsaW1pdGluZyBmb3IgdGhlIHNlcnZlci4nXHJcbiAgICAgIH0sXHJcbiAgICAgIG1heFJlcXVlc3RzOiB7XHJcbiAgICAgICAgdmFsdWU6IDEwLFxyXG4gICAgICAgIHR5cGU6ICdudW1iZXInLFxyXG4gICAgICAgIGVudkxpbms6ICdTRVJWRVJfUkFURV9MSU1JVElOR19NQVhfUkVRVUVTVFMnLFxyXG4gICAgICAgIGxlZ2FjeU5hbWU6ICdyYXRlTGltaXQnLFxyXG4gICAgICAgIGRlc2NyaXB0aW9uOiAnVGhlIG1heGltdW0gbnVtYmVyIG9mIHJlcXVlc3RzIGFsbG93ZWQgaW4gb25lIG1pbnV0ZS4nXHJcbiAgICAgIH0sXHJcbiAgICAgIHdpbmRvdzoge1xyXG4gICAgICAgIHZhbHVlOiAxLFxyXG4gICAgICAgIHR5cGU6ICdudW1iZXInLFxyXG4gICAgICAgIGVudkxpbms6ICdTRVJWRVJfUkFURV9MSU1JVElOR19XSU5ET1cnLFxyXG4gICAgICAgIGRlc2NyaXB0aW9uOiAnVGhlIHRpbWUgd2luZG93LCBpbiBtaW51dGVzLCBmb3IgdGhlIHJhdGUgbGltaXRpbmcuJ1xyXG4gICAgICB9LFxyXG4gICAgICBkZWxheToge1xyXG4gICAgICAgIHZhbHVlOiAwLFxyXG4gICAgICAgIHR5cGU6ICdudW1iZXInLFxyXG4gICAgICAgIGVudkxpbms6ICdTRVJWRVJfUkFURV9MSU1JVElOR19ERUxBWScsXHJcbiAgICAgICAgZGVzY3JpcHRpb246XHJcbiAgICAgICAgICAnVGhlIGRlbGF5IGR1cmF0aW9uIGZvciBlYWNoIHN1Y2Nlc3NpdmUgcmVxdWVzdCBiZWZvcmUgcmVhY2hpbmcgdGhlIG1heGltdW0gbGltaXQuJ1xyXG4gICAgICB9LFxyXG4gICAgICB0cnVzdFByb3h5OiB7XHJcbiAgICAgICAgdmFsdWU6IGZhbHNlLFxyXG4gICAgICAgIHR5cGU6ICdib29sZWFuJyxcclxuICAgICAgICBlbnZMaW5rOiAnU0VSVkVSX1JBVEVfTElNSVRJTkdfVFJVU1RfUFJPWFknLFxyXG4gICAgICAgIGRlc2NyaXB0aW9uOiAnU2V0IHRoaXMgdG8gdHJ1ZSBpZiB0aGUgc2VydmVyIGlzIGJlaGluZCBhIGxvYWQgYmFsYW5jZXIuJ1xyXG4gICAgICB9LFxyXG4gICAgICBza2lwS2V5OiB7XHJcbiAgICAgICAgdmFsdWU6IGZhbHNlLFxyXG4gICAgICAgIHR5cGU6ICdzdHJpbmcnLFxyXG4gICAgICAgIGVudkxpbms6ICdTRVJWRVJfUkFURV9MSU1JVElOR19TS0lQX0tFWScsXHJcbiAgICAgICAgZGVzY3JpcHRpb246XHJcbiAgICAgICAgICAnQWxsb3dzIGJ5cGFzc2luZyB0aGUgcmF0ZSBsaW1pdGVyIGFuZCBzaG91bGQgYmUgcHJvdmlkZWQgd2l0aCB0aGUgc2tpcFRva2VuIGFyZ3VtZW50LidcclxuICAgICAgfSxcclxuICAgICAgc2tpcFRva2VuOiB7XHJcbiAgICAgICAgdmFsdWU6IGZhbHNlLFxyXG4gICAgICAgIHR5cGU6ICdzdHJpbmcnLFxyXG4gICAgICAgIGVudkxpbms6ICdTRVJWRVJfUkFURV9MSU1JVElOR19TS0lQX1RPS0VOJyxcclxuICAgICAgICBkZXNjcmlwdGlvbjpcclxuICAgICAgICAgICdBbGxvd3MgYnlwYXNzaW5nIHRoZSByYXRlIGxpbWl0ZXIgYW5kIHNob3VsZCBiZSBwcm92aWRlZCB3aXRoIHRoZSBza2lwS2V5IGFyZ3VtZW50LidcclxuICAgICAgfVxyXG4gICAgfSxcclxuICAgIHNzbDoge1xyXG4gICAgICBlbmFibGU6IHtcclxuICAgICAgICB2YWx1ZTogZmFsc2UsXHJcbiAgICAgICAgdHlwZTogJ2Jvb2xlYW4nLFxyXG4gICAgICAgIGVudkxpbms6ICdTRVJWRVJfU1NMX0VOQUJMRScsXHJcbiAgICAgICAgY2xpTmFtZTogJ2VuYWJsZVNzbCcsXHJcbiAgICAgICAgZGVzY3JpcHRpb246ICdFbmFibGVzIG9yIGRpc2FibGVzIHRoZSBTU0wgcHJvdG9jb2wuJ1xyXG4gICAgICB9LFxyXG4gICAgICBmb3JjZToge1xyXG4gICAgICAgIHZhbHVlOiBmYWxzZSxcclxuICAgICAgICB0eXBlOiAnYm9vbGVhbicsXHJcbiAgICAgICAgZW52TGluazogJ1NFUlZFUl9TU0xfRk9SQ0UnLFxyXG4gICAgICAgIGNsaU5hbWU6ICdzc2xGb3JjZScsXHJcbiAgICAgICAgbGVnYWN5TmFtZTogJ3NzbE9ubHknLFxyXG4gICAgICAgIGRlc2NyaXB0aW9uOlxyXG4gICAgICAgICAgJ1doZW4gc2V0IHRvIHRydWUsIHRoZSBzZXJ2ZXIgaXMgZm9yY2VkIHRvIHNlcnZlIG9ubHkgb3ZlciBIVFRQUy4nXHJcbiAgICAgIH0sXHJcbiAgICAgIHBvcnQ6IHtcclxuICAgICAgICB2YWx1ZTogNDQzLFxyXG4gICAgICAgIHR5cGU6ICdudW1iZXInLFxyXG4gICAgICAgIGVudkxpbms6ICdTRVJWRVJfU1NMX1BPUlQnLFxyXG4gICAgICAgIGNsaU5hbWU6ICdzc2xQb3J0JyxcclxuICAgICAgICBkZXNjcmlwdGlvbjogJ1RoZSBwb3J0IG9uIHdoaWNoIHRvIHJ1biB0aGUgU1NMIHNlcnZlci4nXHJcbiAgICAgIH0sXHJcbiAgICAgIGNlcnRQYXRoOiB7XHJcbiAgICAgICAgdmFsdWU6IGZhbHNlLFxyXG4gICAgICAgIHR5cGU6ICdzdHJpbmcnLFxyXG4gICAgICAgIGVudkxpbms6ICdTRVJWRVJfU1NMX0NFUlRfUEFUSCcsXHJcbiAgICAgICAgbGVnYWN5TmFtZTogJ3NzbFBhdGgnLFxyXG4gICAgICAgIGRlc2NyaXB0aW9uOiAnVGhlIHBhdGggdG8gdGhlIFNTTCBjZXJ0aWZpY2F0ZS9rZXkgZmlsZS4nXHJcbiAgICAgIH1cclxuICAgIH1cclxuICB9LFxyXG4gIHBvb2w6IHtcclxuICAgIG1pbldvcmtlcnM6IHtcclxuICAgICAgdmFsdWU6IDQsXHJcbiAgICAgIHR5cGU6ICdudW1iZXInLFxyXG4gICAgICBlbnZMaW5rOiAnUE9PTF9NSU5fV09SS0VSUycsXHJcbiAgICAgIGRlc2NyaXB0aW9uOiAnVGhlIG51bWJlciBvZiBtaW5pbXVtIGFuZCBpbml0aWFsIHBvb2wgd29ya2VycyB0byBzcGF3bi4nXHJcbiAgICB9LFxyXG4gICAgbWF4V29ya2Vyczoge1xyXG4gICAgICB2YWx1ZTogOCxcclxuICAgICAgdHlwZTogJ251bWJlcicsXHJcbiAgICAgIGVudkxpbms6ICdQT09MX01BWF9XT1JLRVJTJyxcclxuICAgICAgbGVnYWN5TmFtZTogJ3dvcmtlcnMnLFxyXG4gICAgICBkZXNjcmlwdGlvbjogJ1RoZSBudW1iZXIgb2YgbWF4aW11bSBwb29sIHdvcmtlcnMgdG8gc3Bhd24uJ1xyXG4gICAgfSxcclxuICAgIHdvcmtMaW1pdDoge1xyXG4gICAgICB2YWx1ZTogNDAsXHJcbiAgICAgIHR5cGU6ICdudW1iZXInLFxyXG4gICAgICBlbnZMaW5rOiAnUE9PTF9XT1JLX0xJTUlUJyxcclxuICAgICAgZGVzY3JpcHRpb246XHJcbiAgICAgICAgJ1RoZSBudW1iZXIgb2Ygd29yayBwaWVjZXMgdGhhdCBjYW4gYmUgcGVyZm9ybWVkIGJlZm9yZSByZXN0YXJ0aW5nIHRoZSB3b3JrZXIgcHJvY2Vzcy4nXHJcbiAgICB9LFxyXG4gICAgYWNxdWlyZVRpbWVvdXQ6IHtcclxuICAgICAgdmFsdWU6IDUwMDAsXHJcbiAgICAgIHR5cGU6ICdudW1iZXInLFxyXG4gICAgICBlbnZMaW5rOiAnUE9PTF9BQ1FVSVJFX1RJTUVPVVQnLFxyXG4gICAgICBkZXNjcmlwdGlvbjpcclxuICAgICAgICAnVGhlIGR1cmF0aW9uLCBpbiBtaWxsaXNlY29uZHMsIHRvIHdhaXQgZm9yIGFjcXVpcmluZyBhIHJlc291cmNlLidcclxuICAgIH0sXHJcbiAgICBjcmVhdGVUaW1lb3V0OiB7XHJcbiAgICAgIHZhbHVlOiA1MDAwLFxyXG4gICAgICB0eXBlOiAnbnVtYmVyJyxcclxuICAgICAgZW52TGluazogJ1BPT0xfQ1JFQVRFX1RJTUVPVVQnLFxyXG4gICAgICBkZXNjcmlwdGlvbjpcclxuICAgICAgICAnVGhlIGR1cmF0aW9uLCBpbiBtaWxsaXNlY29uZHMsIHRvIHdhaXQgZm9yIGNyZWF0aW5nIGEgcmVzb3VyY2UuJ1xyXG4gICAgfSxcclxuICAgIGRlc3Ryb3lUaW1lb3V0OiB7XHJcbiAgICAgIHZhbHVlOiA1MDAwLFxyXG4gICAgICB0eXBlOiAnbnVtYmVyJyxcclxuICAgICAgZW52TGluazogJ1BPT0xfREVTVFJPWV9USU1FT1VUJyxcclxuICAgICAgZGVzY3JpcHRpb246XHJcbiAgICAgICAgJ1RoZSBkdXJhdGlvbiwgaW4gbWlsbGlzZWNvbmRzLCB0byB3YWl0IGZvciBkZXN0cm95aW5nIGEgcmVzb3VyY2UuJ1xyXG4gICAgfSxcclxuICAgIGlkbGVUaW1lb3V0OiB7XHJcbiAgICAgIHZhbHVlOiAzMDAwMCxcclxuICAgICAgdHlwZTogJ251bWJlcicsXHJcbiAgICAgIGVudkxpbms6ICdQT09MX0lETEVfVElNRU9VVCcsXHJcbiAgICAgIGRlc2NyaXB0aW9uOlxyXG4gICAgICAgICdUaGUgZHVyYXRpb24sIGluIG1pbGxpc2Vjb25kcywgYWZ0ZXIgd2hpY2ggYW4gaWRsZSByZXNvdXJjZSBpcyBkZXN0cm95ZWQuJ1xyXG4gICAgfSxcclxuICAgIGNyZWF0ZVJldHJ5SW50ZXJ2YWw6IHtcclxuICAgICAgdmFsdWU6IDIwMCxcclxuICAgICAgdHlwZTogJ251bWJlcicsXHJcbiAgICAgIGVudkxpbms6ICdQT09MX0NSRUFURV9SRVRSWV9JTlRFUlZBTCcsXHJcbiAgICAgIGRlc2NyaXB0aW9uOlxyXG4gICAgICAgICdUaGUgZHVyYXRpb24sIGluIG1pbGxpc2Vjb25kcywgdG8gd2FpdCBiZWZvcmUgcmV0cnlpbmcgdGhlIGNyZWF0ZSBwcm9jZXNzIGluIGNhc2Ugb2YgYSBmYWlsdXJlLidcclxuICAgIH0sXHJcbiAgICByZWFwZXJJbnRlcnZhbDoge1xyXG4gICAgICB2YWx1ZTogMTAwMCxcclxuICAgICAgdHlwZTogJ251bWJlcicsXHJcbiAgICAgIGVudkxpbms6ICdQT09MX1JFQVBFUl9JTlRFUlZBTCcsXHJcbiAgICAgIGRlc2NyaXB0aW9uOlxyXG4gICAgICAgICdUaGUgZHVyYXRpb24sIGluIG1pbGxpc2Vjb25kcywgYWZ0ZXIgd2hpY2ggdGhlIGNoZWNrIGZvciBpZGxlIHJlc291cmNlcyB0byBkZXN0cm95IGlzIHRyaWdnZXJlZC4nXHJcbiAgICB9LFxyXG4gICAgYmVuY2htYXJraW5nOiB7XHJcbiAgICAgIHZhbHVlOiBmYWxzZSxcclxuICAgICAgdHlwZTogJ2Jvb2xlYW4nLFxyXG4gICAgICBlbnZMaW5rOiAnUE9PTF9CRU5DSE1BUktJTkcnLFxyXG4gICAgICBjbGlOYW1lOiAncG9vbEJlbmNobWFya2luZycsXHJcbiAgICAgIGRlc2NyaXB0aW9uOlxyXG4gICAgICAgICdJbmRpY2F0ZSB3aGV0aGVyIHRvIHNob3cgc3RhdGlzdGljcyBmb3IgdGhlIHBvb2wgb2YgcmVzb3VyY2VzIG9yIG5vdC4nXHJcbiAgICB9XHJcbiAgfSxcclxuICBsb2dnaW5nOiB7XHJcbiAgICBsZXZlbDoge1xyXG4gICAgICB2YWx1ZTogNCxcclxuICAgICAgdHlwZTogJ251bWJlcicsXHJcbiAgICAgIGVudkxpbms6ICdMT0dHSU5HX0xFVkVMJyxcclxuICAgICAgY2xpTmFtZTogJ2xvZ0xldmVsJyxcclxuICAgICAgZGVzY3JpcHRpb246ICdUaGUgbG9nZ2luZyBsZXZlbCB0byBiZSB1c2VkLidcclxuICAgIH0sXHJcbiAgICBmaWxlOiB7XHJcbiAgICAgIHZhbHVlOiAnaGlnaGNoYXJ0cy1leHBvcnQtc2VydmVyLmxvZycsXHJcbiAgICAgIHR5cGU6ICdzdHJpbmcnLFxyXG4gICAgICBlbnZMaW5rOiAnTE9HR0lOR19GSUxFJyxcclxuICAgICAgY2xpTmFtZTogJ2xvZ0ZpbGUnLFxyXG4gICAgICBkZXNjcmlwdGlvbjpcclxuICAgICAgICAnVGhlIG5hbWUgb2YgYSBsb2cgZmlsZS4gVGhlIGBsb2dUb0ZpbGVgIGFuZCBgbG9nRGVzdGAgb3B0aW9ucyBhbHNvIG5lZWQgdG8gYmUgc2V0IHRvIGVuYWJsZSBmaWxlIGxvZ2dpbmcuJ1xyXG4gICAgfSxcclxuICAgIGRlc3Q6IHtcclxuICAgICAgdmFsdWU6ICdsb2cvJyxcclxuICAgICAgdHlwZTogJ3N0cmluZycsXHJcbiAgICAgIGVudkxpbms6ICdMT0dHSU5HX0RFU1QnLFxyXG4gICAgICBjbGlOYW1lOiAnbG9nRGVzdCcsXHJcbiAgICAgIGRlc2NyaXB0aW9uOlxyXG4gICAgICAgICdUaGUgcGF0aCB0byBzdG9yZSBsb2cgZmlsZXMuIFRoZSBgbG9nVG9GaWxlYCBvcHRpb24gYWxzbyBuZWVkcyB0byBiZSBzZXQgdG8gZW5hYmxlIGZpbGUgbG9nZ2luZy4nXHJcbiAgICB9LFxyXG4gICAgdG9Db25zb2xlOiB7XHJcbiAgICAgIHZhbHVlOiB0cnVlLFxyXG4gICAgICB0eXBlOiAnYm9vbGVhbicsXHJcbiAgICAgIGVudkxpbms6ICdMT0dHSU5HX1RPX0NPTlNPTEUnLFxyXG4gICAgICBjbGlOYW1lOiAnbG9nVG9Db25zb2xlJyxcclxuICAgICAgZGVzY3JpcHRpb246ICdFbmFibGVzIG9yIGRpc2FibGVzIHNob3dpbmcgbG9ncyBpbiB0aGUgY29uc29sZS4nXHJcbiAgICB9LFxyXG4gICAgdG9GaWxlOiB7XHJcbiAgICAgIHZhbHVlOiB0cnVlLFxyXG4gICAgICB0eXBlOiAnYm9vbGVhbicsXHJcbiAgICAgIGVudkxpbms6ICdMT0dHSU5HX1RPX0ZJTEUnLFxyXG4gICAgICBjbGlOYW1lOiAnbG9nVG9GaWxlJyxcclxuICAgICAgZGVzY3JpcHRpb246XHJcbiAgICAgICAgJ0VuYWJsZXMgb3IgZGlzYWJsZXMgY3JlYXRpb24gb2YgdGhlIGxvZyBkaXJlY3RvcnkgYW5kIHNhdmluZyB0aGUgbG9nIGludG8gYSAubG9nIGZpbGUuJ1xyXG4gICAgfVxyXG4gIH0sXHJcbiAgdWk6IHtcclxuICAgIGVuYWJsZToge1xyXG4gICAgICB2YWx1ZTogZmFsc2UsXHJcbiAgICAgIHR5cGU6ICdib29sZWFuJyxcclxuICAgICAgZW52TGluazogJ1VJX0VOQUJMRScsXHJcbiAgICAgIGNsaU5hbWU6ICdlbmFibGVVaScsXHJcbiAgICAgIGRlc2NyaXB0aW9uOlxyXG4gICAgICAgICdFbmFibGVzIG9yIGRpc2FibGVzIHRoZSB1c2VyIGludGVyZmFjZSAoVUkpIGZvciB0aGUgZXhwb3J0IHNlcnZlci4nXHJcbiAgICB9LFxyXG4gICAgcm91dGU6IHtcclxuICAgICAgdmFsdWU6ICcvJyxcclxuICAgICAgdHlwZTogJ3N0cmluZycsXHJcbiAgICAgIGVudkxpbms6ICdVSV9ST1VURScsXHJcbiAgICAgIGNsaU5hbWU6ICd1aVJvdXRlJyxcclxuICAgICAgZGVzY3JpcHRpb246XHJcbiAgICAgICAgJ1RoZSBlbmRwb2ludCByb3V0ZSB0byB3aGljaCB0aGUgdXNlciBpbnRlcmZhY2UgKFVJKSBzaG91bGQgYmUgYXR0YWNoZWQuJ1xyXG4gICAgfVxyXG4gIH0sXHJcbiAgb3RoZXI6IHtcclxuICAgIG5vZGVFbnY6IHtcclxuICAgICAgdmFsdWU6ICdwcm9kdWN0aW9uJyxcclxuICAgICAgdHlwZTogJ3N0cmluZycsXHJcbiAgICAgIGVudkxpbms6ICdPVEhFUl9OT0RFX0VOVicsXHJcbiAgICAgIGRlc2NyaXB0aW9uOiAnVGhlIHR5cGUgb2YgTm9kZS5qcyBlbnZpcm9ubWVudC4nXHJcbiAgICB9LFxyXG4gICAgbGlzdGVuVG9Qcm9jZXNzRXhpdHM6IHtcclxuICAgICAgdmFsdWU6IHRydWUsXHJcbiAgICAgIHR5cGU6ICdib29sZWFuJyxcclxuICAgICAgZW52TGluazogJ09USEVSX0xJU1RFTl9UT19QUk9DRVNTX0VYSVRTJyxcclxuICAgICAgZGVzY3JpcHRpb246ICdEZWNpZGVzIHdoZXRoZXIgb3Igbm90IHRvIGF0dGFjaCBwcm9jZXNzLmV4aXQgaGFuZGxlcnMuJ1xyXG4gICAgfSxcclxuICAgIG5vTG9nbzoge1xyXG4gICAgICB2YWx1ZTogZmFsc2UsXHJcbiAgICAgIHR5cGU6ICdib29sZWFuJyxcclxuICAgICAgZW52TGluazogJ09USEVSX05PX0xPR08nLFxyXG4gICAgICBkZXNjcmlwdGlvbjpcclxuICAgICAgICAnU2tpcCBwcmludGluZyB0aGUgbG9nbyBvbiBhIHN0YXJ0dXAuIFdpbGwgYmUgcmVwbGFjZWQgYnkgYSBzaW1wbGUgdGV4dC4nXHJcbiAgICB9LFxyXG4gICAgaGFyZFJlc2V0UGFnZToge1xyXG4gICAgICB2YWx1ZTogZmFsc2UsXHJcbiAgICAgIHR5cGU6ICdib29sZWFuJyxcclxuICAgICAgZW52TGluazogJ09USEVSX0hBUkRfUkVTRVRfUEFHRScsXHJcbiAgICAgIGRlc2NyaXB0aW9uOiAnRGVjaWRlcyBpZiB0aGUgcGFnZSBjb250ZW50IHNob3VsZCBiZSByZXNldCBlbnRpcmVseS4nXHJcbiAgICB9LFxyXG4gICAgYnJvd3NlclNoZWxsTW9kZToge1xyXG4gICAgICB2YWx1ZTogdHJ1ZSxcclxuICAgICAgdHlwZTogJ2Jvb2xlYW4nLFxyXG4gICAgICBlbnZMaW5rOiAnT1RIRVJfQlJPV1NFUl9TSEVMTF9NT0RFJyxcclxuICAgICAgZGVzY3JpcHRpb246ICdEZWNpZGVzIGlmIHRoZSBicm93c2VyIHJ1bnMgaW4gdGhlIHNoZWxsIG1vZGUuJ1xyXG4gICAgfVxyXG4gIH0sXHJcbiAgZGVidWc6IHtcclxuICAgIGVuYWJsZToge1xyXG4gICAgICB2YWx1ZTogZmFsc2UsXHJcbiAgICAgIHR5cGU6ICdib29sZWFuJyxcclxuICAgICAgZW52TGluazogJ0RFQlVHX0VOQUJMRScsXHJcbiAgICAgIGNsaU5hbWU6ICdlbmFibGVEZWJ1ZycsXHJcbiAgICAgIGRlc2NyaXB0aW9uOiAnRW5hYmxlcyBvciBkaXNhYmxlcyBkZWJ1ZyBtb2RlIGZvciB0aGUgdW5kZXJseWluZyBicm93c2VyLidcclxuICAgIH0sXHJcbiAgICBoZWFkbGVzczoge1xyXG4gICAgICB2YWx1ZTogdHJ1ZSxcclxuICAgICAgdHlwZTogJ2Jvb2xlYW4nLFxyXG4gICAgICBlbnZMaW5rOiAnREVCVUdfSEVBRExFU1MnLFxyXG4gICAgICBkZXNjcmlwdGlvbjpcclxuICAgICAgICAnQ29udHJvbHMgdGhlIG1vZGUgaW4gd2hpY2ggdGhlIGJyb3dzZXIgaXMgbGF1bmNoZWQgd2hlbiBpbiB0aGUgZGVidWcgbW9kZS4nXHJcbiAgICB9LFxyXG4gICAgZGV2dG9vbHM6IHtcclxuICAgICAgdmFsdWU6IGZhbHNlLFxyXG4gICAgICB0eXBlOiAnYm9vbGVhbicsXHJcbiAgICAgIGVudkxpbms6ICdERUJVR19ERVZUT09MUycsXHJcbiAgICAgIGRlc2NyaXB0aW9uOlxyXG4gICAgICAgICdEZWNpZGVzIHdoZXRoZXIgdG8gZW5hYmxlIERldlRvb2xzIHdoZW4gdGhlIGJyb3dzZXIgaXMgaW4gYSBoZWFkZnVsIHN0YXRlLidcclxuICAgIH0sXHJcbiAgICBsaXN0ZW5Ub0NvbnNvbGU6IHtcclxuICAgICAgdmFsdWU6IGZhbHNlLFxyXG4gICAgICB0eXBlOiAnYm9vbGVhbicsXHJcbiAgICAgIGVudkxpbms6ICdERUJVR19MSVNURU5fVE9fQ09OU09MRScsXHJcbiAgICAgIGRlc2NyaXB0aW9uOlxyXG4gICAgICAgICdEZWNpZGVzIHdoZXRoZXIgdG8gZW5hYmxlIGEgbGlzdGVuZXIgZm9yIGNvbnNvbGUgbWVzc2FnZXMgc2VudCBmcm9tIHRoZSBicm93c2VyLidcclxuICAgIH0sXHJcbiAgICBkdW1waW86IHtcclxuICAgICAgdmFsdWU6IGZhbHNlLFxyXG4gICAgICB0eXBlOiAnYm9vbGVhbicsXHJcbiAgICAgIGVudkxpbms6ICdERUJVR19EVU1QSU8nLFxyXG4gICAgICBkZXNjcmlwdGlvbjpcclxuICAgICAgICAnUmVkaXJlY3RzIGJyb3dzZXIgcHJvY2VzcyBzdGRvdXQgYW5kIHN0ZGVyciB0byBwcm9jZXNzLnN0ZG91dCBhbmQgcHJvY2Vzcy5zdGRlcnIuJ1xyXG4gICAgfSxcclxuICAgIHNsb3dNbzoge1xyXG4gICAgICB2YWx1ZTogMCxcclxuICAgICAgdHlwZTogJ251bWJlcicsXHJcbiAgICAgIGVudkxpbms6ICdERUJVR19TTE9XX01PJyxcclxuICAgICAgZGVzY3JpcHRpb246XHJcbiAgICAgICAgJ1Nsb3dzIGRvd24gUHVwcGV0ZWVyIG9wZXJhdGlvbnMgYnkgdGhlIHNwZWNpZmllZCBudW1iZXIgb2YgbWlsbGlzZWNvbmRzLidcclxuICAgIH0sXHJcbiAgICBkZWJ1Z2dpbmdQb3J0OiB7XHJcbiAgICAgIHZhbHVlOiA5MjIyLFxyXG4gICAgICB0eXBlOiAnbnVtYmVyJyxcclxuICAgICAgZW52TGluazogJ0RFQlVHX0RFQlVHR0lOR19QT1JUJyxcclxuICAgICAgZGVzY3JpcHRpb246ICdTcGVjaWZpZXMgdGhlIGRlYnVnZ2luZyBwb3J0LidcclxuICAgIH1cclxuICB9XHJcbn07XHJcblxyXG4vLyBUaGUgY29uZmlnIGRlc2NyaXB0aW9ucyBvYmplY3QgZm9yIHRoZSBwcm9tcHRzIGZ1bmN0aW9uYWxpdHkuIEl0IGNvbnRhaW5zXHJcbi8vIGluZm9ybWF0aW9uIGxpa2U6XHJcbi8vICogVHlwZSBvZiBhIHByb21wdFxyXG4vLyAqIE5hbWUgb2YgYW4gb3B0aW9uXHJcbi8vICogU2hvcnQgZGVzY3JpcHRpb24gb2YgYSBjaG9zZW4gb3B0aW9uXHJcbi8vICogSW5pdGlhbCB2YWx1ZVxyXG5leHBvcnQgY29uc3QgcHJvbXB0c0NvbmZpZyA9IHtcclxuICBwdXBwZXRlZXI6IFtcclxuICAgIHtcclxuICAgICAgdHlwZTogJ2xpc3QnLFxyXG4gICAgICBuYW1lOiAnYXJncycsXHJcbiAgICAgIG1lc3NhZ2U6ICdQdXBwZXRlZXIgYXJndW1lbnRzJyxcclxuICAgICAgaW5pdGlhbDogZGVmYXVsdENvbmZpZy5wdXBwZXRlZXIuYXJncy52YWx1ZS5qb2luKCcsJyksXHJcbiAgICAgIHNlcGFyYXRvcjogJywnXHJcbiAgICB9XHJcbiAgXSxcclxuICBoaWdoY2hhcnRzOiBbXHJcbiAgICB7XHJcbiAgICAgIHR5cGU6ICd0ZXh0JyxcclxuICAgICAgbmFtZTogJ3ZlcnNpb24nLFxyXG4gICAgICBtZXNzYWdlOiAnSGlnaGNoYXJ0cyB2ZXJzaW9uJyxcclxuICAgICAgaW5pdGlhbDogZGVmYXVsdENvbmZpZy5oaWdoY2hhcnRzLnZlcnNpb24udmFsdWVcclxuICAgIH0sXHJcbiAgICB7XHJcbiAgICAgIHR5cGU6ICd0ZXh0JyxcclxuICAgICAgbmFtZTogJ2NkblVSTCcsXHJcbiAgICAgIG1lc3NhZ2U6ICdUaGUgVVJMIG9mIENETicsXHJcbiAgICAgIGluaXRpYWw6IGRlZmF1bHRDb25maWcuaGlnaGNoYXJ0cy5jZG5VUkwudmFsdWVcclxuICAgIH0sXHJcbiAgICB7XHJcbiAgICAgIHR5cGU6ICd0b2dnbGUnLFxyXG4gICAgICBuYW1lOiAndXNlTnBtJyxcclxuICAgICAgbWVzc2FnZTogJ0ZsYWcgdG8gdXNlIEhpZ2hjaGFydHMgc2NyaXB0cyBmcm9tIE5QTSBwYWNrYWdlJyxcclxuICAgICAgaW5pdGlhbDogZGVmYXVsdENvbmZpZy5oaWdoY2hhcnRzLnVzZU5wbS52YWx1ZVxyXG4gICAgfSxcclxuICAgIHtcclxuICAgICAgdHlwZTogJ211bHRpc2VsZWN0JyxcclxuICAgICAgbmFtZTogJ2NvcmVTY3JpcHRzJyxcclxuICAgICAgbWVzc2FnZTogJ0F2YWlsYWJsZSBjb3JlIHNjcmlwdHMnLFxyXG4gICAgICBpbnN0cnVjdGlvbnM6ICdTcGFjZTogU2VsZWN0IHNwZWNpZmljLCBBOiBTZWxlY3QgYWxsLCBFbnRlcjogQ29uZmlybS4nLFxyXG4gICAgICBjaG9pY2VzOiBkZWZhdWx0Q29uZmlnLmhpZ2hjaGFydHMuY29yZVNjcmlwdHMudmFsdWVcclxuICAgIH0sXHJcbiAgICB7XHJcbiAgICAgIHR5cGU6ICdtdWx0aXNlbGVjdCcsXHJcbiAgICAgIG5hbWU6ICdtb2R1bGVTY3JpcHRzJyxcclxuICAgICAgbWVzc2FnZTogJ0F2YWlsYWJsZSBtb2R1bGUgc2NyaXB0cycsXHJcbiAgICAgIGluc3RydWN0aW9uczogJ1NwYWNlOiBTZWxlY3Qgc3BlY2lmaWMsIEE6IFNlbGVjdCBhbGwsIEVudGVyOiBDb25maXJtLicsXHJcbiAgICAgIGNob2ljZXM6IGRlZmF1bHRDb25maWcuaGlnaGNoYXJ0cy5tb2R1bGVTY3JpcHRzLnZhbHVlXHJcbiAgICB9LFxyXG4gICAge1xyXG4gICAgICB0eXBlOiAnbXVsdGlzZWxlY3QnLFxyXG4gICAgICBuYW1lOiAnaW5kaWNhdG9yU2NyaXB0cycsXHJcbiAgICAgIG1lc3NhZ2U6ICdBdmFpbGFibGUgaW5kaWNhdG9yIHNjcmlwdHMnLFxyXG4gICAgICBpbnN0cnVjdGlvbnM6ICdTcGFjZTogU2VsZWN0IHNwZWNpZmljLCBBOiBTZWxlY3QgYWxsLCBFbnRlcjogQ29uZmlybS4nLFxyXG4gICAgICBjaG9pY2VzOiBkZWZhdWx0Q29uZmlnLmhpZ2hjaGFydHMuaW5kaWNhdG9yU2NyaXB0cy52YWx1ZVxyXG4gICAgfSxcclxuICAgIHtcclxuICAgICAgdHlwZTogJ2xpc3QnLFxyXG4gICAgICBuYW1lOiAnY3VzdG9tU2NyaXB0cycsXHJcbiAgICAgIG1lc3NhZ2U6ICdDdXN0b20gc2NyaXB0cycsXHJcbiAgICAgIGluaXRpYWw6IGRlZmF1bHRDb25maWcuaGlnaGNoYXJ0cy5jdXN0b21TY3JpcHRzLnZhbHVlLmpvaW4oJywnKSxcclxuICAgICAgc2VwYXJhdG9yOiAnLCdcclxuICAgIH0sXHJcbiAgICB7XHJcbiAgICAgIHR5cGU6ICd0b2dnbGUnLFxyXG4gICAgICBuYW1lOiAnZm9yY2VGZXRjaCcsXHJcbiAgICAgIG1lc3NhZ2U6ICdGb3JjZSByZS1mZXRjaCB0aGUgc2NyaXB0cycsXHJcbiAgICAgIGluaXRpYWw6IGRlZmF1bHRDb25maWcuaGlnaGNoYXJ0cy5mb3JjZUZldGNoLnZhbHVlXHJcbiAgICB9LFxyXG4gICAge1xyXG4gICAgICB0eXBlOiAndGV4dCcsXHJcbiAgICAgIG5hbWU6ICdjYWNoZVBhdGgnLFxyXG4gICAgICBtZXNzYWdlOiAnVGhlIHBhdGggdG8gdGhlIGNhY2hlIGRpcmVjdG9yeScsXHJcbiAgICAgIGluaXRpYWw6IGRlZmF1bHRDb25maWcuaGlnaGNoYXJ0cy5jYWNoZVBhdGgudmFsdWVcclxuICAgIH1cclxuICBdLFxyXG4gIGV4cG9ydDogW1xyXG4gICAge1xyXG4gICAgICB0eXBlOiAnc2VsZWN0JyxcclxuICAgICAgbmFtZTogJ3R5cGUnLFxyXG4gICAgICBtZXNzYWdlOiAnVGhlIGRlZmF1bHQgZXhwb3J0IGZpbGUgdHlwZScsXHJcbiAgICAgIGhpbnQ6IGBEZWZhdWx0OiAke2RlZmF1bHRDb25maWcuZXhwb3J0LnR5cGUudmFsdWV9YCxcclxuICAgICAgaW5pdGlhbDogMCxcclxuICAgICAgY2hvaWNlczogWydwbmcnLCAnanBlZycsICdwZGYnLCAnc3ZnJ11cclxuICAgIH0sXHJcbiAgICB7XHJcbiAgICAgIHR5cGU6ICdzZWxlY3QnLFxyXG4gICAgICBuYW1lOiAnY29uc3RyJyxcclxuICAgICAgbWVzc2FnZTogJ1RoZSBkZWZhdWx0IGNvbnN0cnVjdG9yIGZvciBIaWdoY2hhcnRzJyxcclxuICAgICAgaGludDogYERlZmF1bHQ6ICR7ZGVmYXVsdENvbmZpZy5leHBvcnQuY29uc3RyLnZhbHVlfWAsXHJcbiAgICAgIGluaXRpYWw6IDAsXHJcbiAgICAgIGNob2ljZXM6IFsnY2hhcnQnLCAnc3RvY2tDaGFydCcsICdtYXBDaGFydCcsICdnYW50dENoYXJ0J11cclxuICAgIH0sXHJcbiAgICB7XHJcbiAgICAgIHR5cGU6ICdudW1iZXInLFxyXG4gICAgICBuYW1lOiAnZGVmYXVsdEhlaWdodCcsXHJcbiAgICAgIG1lc3NhZ2U6ICdUaGUgZGVmYXVsdCBmYWxsYmFjayBoZWlnaHQgb2YgdGhlIGV4cG9ydGVkIGNoYXJ0JyxcclxuICAgICAgaW5pdGlhbDogZGVmYXVsdENvbmZpZy5leHBvcnQuZGVmYXVsdEhlaWdodC52YWx1ZVxyXG4gICAgfSxcclxuICAgIHtcclxuICAgICAgdHlwZTogJ251bWJlcicsXHJcbiAgICAgIG5hbWU6ICdkZWZhdWx0V2lkdGgnLFxyXG4gICAgICBtZXNzYWdlOiAnVGhlIGRlZmF1bHQgZmFsbGJhY2sgd2lkdGggb2YgdGhlIGV4cG9ydGVkIGNoYXJ0JyxcclxuICAgICAgaW5pdGlhbDogZGVmYXVsdENvbmZpZy5leHBvcnQuZGVmYXVsdFdpZHRoLnZhbHVlXHJcbiAgICB9LFxyXG4gICAge1xyXG4gICAgICB0eXBlOiAnbnVtYmVyJyxcclxuICAgICAgbmFtZTogJ2RlZmF1bHRTY2FsZScsXHJcbiAgICAgIG1lc3NhZ2U6ICdUaGUgZGVmYXVsdCBmYWxsYmFjayBzY2FsZSBvZiB0aGUgZXhwb3J0ZWQgY2hhcnQnLFxyXG4gICAgICBpbml0aWFsOiBkZWZhdWx0Q29uZmlnLmV4cG9ydC5kZWZhdWx0U2NhbGUudmFsdWUsXHJcbiAgICAgIG1pbjogMC4xLFxyXG4gICAgICBtYXg6IDVcclxuICAgIH0sXHJcbiAgICB7XHJcbiAgICAgIHR5cGU6ICdudW1iZXInLFxyXG4gICAgICBuYW1lOiAncmFzdGVyaXphdGlvblRpbWVvdXQnLFxyXG4gICAgICBtZXNzYWdlOiAnVGhlIHJlbmRlcmluZyB3ZWJwYWdlIHRpbWVvdXQgaW4gbWlsbGlzZWNvbmRzJyxcclxuICAgICAgaW5pdGlhbDogZGVmYXVsdENvbmZpZy5leHBvcnQucmFzdGVyaXphdGlvblRpbWVvdXQudmFsdWVcclxuICAgIH1cclxuICBdLFxyXG4gIGN1c3RvbUxvZ2ljOiBbXHJcbiAgICB7XHJcbiAgICAgIHR5cGU6ICd0b2dnbGUnLFxyXG4gICAgICBuYW1lOiAnYWxsb3dDb2RlRXhlY3V0aW9uJyxcclxuICAgICAgbWVzc2FnZTogJ0VuYWJsZSBleGVjdXRpb24gb2YgY3VzdG9tIGNvZGUnLFxyXG4gICAgICBpbml0aWFsOiBkZWZhdWx0Q29uZmlnLmN1c3RvbUxvZ2ljLmFsbG93Q29kZUV4ZWN1dGlvbi52YWx1ZVxyXG4gICAgfSxcclxuICAgIHtcclxuICAgICAgdHlwZTogJ3RvZ2dsZScsXHJcbiAgICAgIG5hbWU6ICdhbGxvd0ZpbGVSZXNvdXJjZXMnLFxyXG4gICAgICBtZXNzYWdlOiAnRW5hYmxlIGZpbGUgcmVzb3VyY2VzJyxcclxuICAgICAgaW5pdGlhbDogZGVmYXVsdENvbmZpZy5jdXN0b21Mb2dpYy5hbGxvd0ZpbGVSZXNvdXJjZXMudmFsdWVcclxuICAgIH1cclxuICBdLFxyXG4gIHNlcnZlcjogW1xyXG4gICAge1xyXG4gICAgICB0eXBlOiAndG9nZ2xlJyxcclxuICAgICAgbmFtZTogJ2VuYWJsZScsXHJcbiAgICAgIG1lc3NhZ2U6ICdTdGFydHMgdGhlIHNlcnZlciBvbiAwLjAuMC4wJyxcclxuICAgICAgaW5pdGlhbDogZGVmYXVsdENvbmZpZy5zZXJ2ZXIuZW5hYmxlLnZhbHVlXHJcbiAgICB9LFxyXG4gICAge1xyXG4gICAgICB0eXBlOiAndGV4dCcsXHJcbiAgICAgIG5hbWU6ICdob3N0JyxcclxuICAgICAgbWVzc2FnZTogJ1NlcnZlciBob3N0bmFtZScsXHJcbiAgICAgIGluaXRpYWw6IGRlZmF1bHRDb25maWcuc2VydmVyLmhvc3QudmFsdWVcclxuICAgIH0sXHJcbiAgICB7XHJcbiAgICAgIHR5cGU6ICdudW1iZXInLFxyXG4gICAgICBuYW1lOiAncG9ydCcsXHJcbiAgICAgIG1lc3NhZ2U6ICdTZXJ2ZXIgcG9ydCcsXHJcbiAgICAgIGluaXRpYWw6IGRlZmF1bHRDb25maWcuc2VydmVyLnBvcnQudmFsdWVcclxuICAgIH0sXHJcbiAgICB7XHJcbiAgICAgIHR5cGU6ICd0b2dnbGUnLFxyXG4gICAgICBuYW1lOiAnYmVuY2htYXJraW5nJyxcclxuICAgICAgbWVzc2FnZTogJ0VuYWJsZSBzZXJ2ZXIgYmVuY2htYXJraW5nJyxcclxuICAgICAgaW5pdGlhbDogZGVmYXVsdENvbmZpZy5zZXJ2ZXIuYmVuY2htYXJraW5nLnZhbHVlXHJcbiAgICB9LFxyXG4gICAge1xyXG4gICAgICB0eXBlOiAndGV4dCcsXHJcbiAgICAgIG5hbWU6ICdwcm94eS5ob3N0JyxcclxuICAgICAgbWVzc2FnZTogJ1RoZSBob3N0IG9mIHRoZSBwcm94eSBzZXJ2ZXIgdG8gdXNlJyxcclxuICAgICAgaW5pdGlhbDogZGVmYXVsdENvbmZpZy5zZXJ2ZXIucHJveHkuaG9zdC52YWx1ZVxyXG4gICAgfSxcclxuICAgIHtcclxuICAgICAgdHlwZTogJ251bWJlcicsXHJcbiAgICAgIG5hbWU6ICdwcm94eS5wb3J0JyxcclxuICAgICAgbWVzc2FnZTogJ1RoZSBwb3J0IG9mIHRoZSBwcm94eSBzZXJ2ZXIgdG8gdXNlJyxcclxuICAgICAgaW5pdGlhbDogZGVmYXVsdENvbmZpZy5zZXJ2ZXIucHJveHkucG9ydC52YWx1ZVxyXG4gICAgfSxcclxuICAgIHtcclxuICAgICAgdHlwZTogJ251bWJlcicsXHJcbiAgICAgIG5hbWU6ICdwcm94eS50aW1lb3V0JyxcclxuICAgICAgbWVzc2FnZTogJ1RoZSB0aW1lb3V0IGZvciB0aGUgcHJveHkgc2VydmVyIHRvIHVzZScsXHJcbiAgICAgIGluaXRpYWw6IGRlZmF1bHRDb25maWcuc2VydmVyLnByb3h5LnRpbWVvdXQudmFsdWVcclxuICAgIH0sXHJcbiAgICB7XHJcbiAgICAgIHR5cGU6ICd0b2dnbGUnLFxyXG4gICAgICBuYW1lOiAncmF0ZUxpbWl0aW5nLmVuYWJsZScsXHJcbiAgICAgIG1lc3NhZ2U6ICdFbmFibGUgcmF0ZSBsaW1pdGluZycsXHJcbiAgICAgIGluaXRpYWw6IGRlZmF1bHRDb25maWcuc2VydmVyLnJhdGVMaW1pdGluZy5lbmFibGUudmFsdWVcclxuICAgIH0sXHJcbiAgICB7XHJcbiAgICAgIHR5cGU6ICdudW1iZXInLFxyXG4gICAgICBuYW1lOiAncmF0ZUxpbWl0aW5nLm1heFJlcXVlc3RzJyxcclxuICAgICAgbWVzc2FnZTogJ1RoZSBtYXhpbXVtIHJlcXVlc3RzIGFsbG93ZWQgcGVyIG1pbnV0ZScsXHJcbiAgICAgIGluaXRpYWw6IGRlZmF1bHRDb25maWcuc2VydmVyLnJhdGVMaW1pdGluZy5tYXhSZXF1ZXN0cy52YWx1ZVxyXG4gICAgfSxcclxuICAgIHtcclxuICAgICAgdHlwZTogJ251bWJlcicsXHJcbiAgICAgIG5hbWU6ICdyYXRlTGltaXRpbmcud2luZG93JyxcclxuICAgICAgbWVzc2FnZTogJ1RoZSByYXRlLWxpbWl0aW5nIHRpbWUgd2luZG93IGluIG1pbnV0ZXMnLFxyXG4gICAgICBpbml0aWFsOiBkZWZhdWx0Q29uZmlnLnNlcnZlci5yYXRlTGltaXRpbmcud2luZG93LnZhbHVlXHJcbiAgICB9LFxyXG4gICAge1xyXG4gICAgICB0eXBlOiAnbnVtYmVyJyxcclxuICAgICAgbmFtZTogJ3JhdGVMaW1pdGluZy5kZWxheScsXHJcbiAgICAgIG1lc3NhZ2U6XHJcbiAgICAgICAgJ1RoZSBkZWxheSBmb3IgZWFjaCBzdWNjZXNzaXZlIHJlcXVlc3QgYmVmb3JlIHJlYWNoaW5nIHRoZSBtYXhpbXVtJyxcclxuICAgICAgaW5pdGlhbDogZGVmYXVsdENvbmZpZy5zZXJ2ZXIucmF0ZUxpbWl0aW5nLmRlbGF5LnZhbHVlXHJcbiAgICB9LFxyXG4gICAge1xyXG4gICAgICB0eXBlOiAndG9nZ2xlJyxcclxuICAgICAgbmFtZTogJ3JhdGVMaW1pdGluZy50cnVzdFByb3h5JyxcclxuICAgICAgbWVzc2FnZTogJ1NldCB0byB0cnVlIGlmIGJlaGluZCBhIGxvYWQgYmFsYW5jZXInLFxyXG4gICAgICBpbml0aWFsOiBkZWZhdWx0Q29uZmlnLnNlcnZlci5yYXRlTGltaXRpbmcudHJ1c3RQcm94eS52YWx1ZVxyXG4gICAgfSxcclxuICAgIHtcclxuICAgICAgdHlwZTogJ3RleHQnLFxyXG4gICAgICBuYW1lOiAncmF0ZUxpbWl0aW5nLnNraXBLZXknLFxyXG4gICAgICBtZXNzYWdlOlxyXG4gICAgICAgICdBbGxvd3MgYnlwYXNzaW5nIHRoZSByYXRlIGxpbWl0ZXIgd2hlbiBwcm92aWRlZCB3aXRoIHRoZSBza2lwVG9rZW4gYXJndW1lbnQnLFxyXG4gICAgICBpbml0aWFsOiBkZWZhdWx0Q29uZmlnLnNlcnZlci5yYXRlTGltaXRpbmcuc2tpcEtleS52YWx1ZVxyXG4gICAgfSxcclxuICAgIHtcclxuICAgICAgdHlwZTogJ3RleHQnLFxyXG4gICAgICBuYW1lOiAncmF0ZUxpbWl0aW5nLnNraXBUb2tlbicsXHJcbiAgICAgIG1lc3NhZ2U6XHJcbiAgICAgICAgJ0FsbG93cyBieXBhc3NpbmcgdGhlIHJhdGUgbGltaXRlciB3aGVuIHByb3ZpZGVkIHdpdGggdGhlIHNraXBLZXkgYXJndW1lbnQnLFxyXG4gICAgICBpbml0aWFsOiBkZWZhdWx0Q29uZmlnLnNlcnZlci5yYXRlTGltaXRpbmcuc2tpcFRva2VuLnZhbHVlXHJcbiAgICB9LFxyXG4gICAge1xyXG4gICAgICB0eXBlOiAndG9nZ2xlJyxcclxuICAgICAgbmFtZTogJ3NzbC5lbmFibGUnLFxyXG4gICAgICBtZXNzYWdlOiAnRW5hYmxlIFNTTCBwcm90b2NvbCcsXHJcbiAgICAgIGluaXRpYWw6IGRlZmF1bHRDb25maWcuc2VydmVyLnNzbC5lbmFibGUudmFsdWVcclxuICAgIH0sXHJcbiAgICB7XHJcbiAgICAgIHR5cGU6ICd0b2dnbGUnLFxyXG4gICAgICBuYW1lOiAnc3NsLmZvcmNlJyxcclxuICAgICAgbWVzc2FnZTogJ0ZvcmNlIHNlcnZpbmcgb25seSBvdmVyIEhUVFBTJyxcclxuICAgICAgaW5pdGlhbDogZGVmYXVsdENvbmZpZy5zZXJ2ZXIuc3NsLmZvcmNlLnZhbHVlXHJcbiAgICB9LFxyXG4gICAge1xyXG4gICAgICB0eXBlOiAnbnVtYmVyJyxcclxuICAgICAgbmFtZTogJ3NzbC5wb3J0JyxcclxuICAgICAgbWVzc2FnZTogJ1NTTCBzZXJ2ZXIgcG9ydCcsXHJcbiAgICAgIGluaXRpYWw6IGRlZmF1bHRDb25maWcuc2VydmVyLnNzbC5wb3J0LnZhbHVlXHJcbiAgICB9LFxyXG4gICAge1xyXG4gICAgICB0eXBlOiAndGV4dCcsXHJcbiAgICAgIG5hbWU6ICdzc2wuY2VydFBhdGgnLFxyXG4gICAgICBtZXNzYWdlOiAnVGhlIHBhdGggdG8gZmluZCB0aGUgU1NMIGNlcnRpZmljYXRlL2tleScsXHJcbiAgICAgIGluaXRpYWw6IGRlZmF1bHRDb25maWcuc2VydmVyLnNzbC5jZXJ0UGF0aC52YWx1ZVxyXG4gICAgfVxyXG4gIF0sXHJcbiAgcG9vbDogW1xyXG4gICAge1xyXG4gICAgICB0eXBlOiAnbnVtYmVyJyxcclxuICAgICAgbmFtZTogJ21pbldvcmtlcnMnLFxyXG4gICAgICBtZXNzYWdlOiAnVGhlIGluaXRpYWwgbnVtYmVyIG9mIHdvcmtlcnMgdG8gc3Bhd24nLFxyXG4gICAgICBpbml0aWFsOiBkZWZhdWx0Q29uZmlnLnBvb2wubWluV29ya2Vycy52YWx1ZVxyXG4gICAgfSxcclxuICAgIHtcclxuICAgICAgdHlwZTogJ251bWJlcicsXHJcbiAgICAgIG5hbWU6ICdtYXhXb3JrZXJzJyxcclxuICAgICAgbWVzc2FnZTogJ1RoZSBtYXhpbXVtIG51bWJlciBvZiB3b3JrZXJzIHRvIHNwYXduJyxcclxuICAgICAgaW5pdGlhbDogZGVmYXVsdENvbmZpZy5wb29sLm1heFdvcmtlcnMudmFsdWVcclxuICAgIH0sXHJcbiAgICB7XHJcbiAgICAgIHR5cGU6ICdudW1iZXInLFxyXG4gICAgICBuYW1lOiAnd29ya0xpbWl0JyxcclxuICAgICAgbWVzc2FnZTpcclxuICAgICAgICAnVGhlIHBpZWNlcyBvZiB3b3JrIHRoYXQgY2FuIGJlIHBlcmZvcm1lZCBiZWZvcmUgcmVzdGFydGluZyBhIFB1cHBldGVlciBwcm9jZXNzJyxcclxuICAgICAgaW5pdGlhbDogZGVmYXVsdENvbmZpZy5wb29sLndvcmtMaW1pdC52YWx1ZVxyXG4gICAgfSxcclxuICAgIHtcclxuICAgICAgdHlwZTogJ251bWJlcicsXHJcbiAgICAgIG5hbWU6ICdhY3F1aXJlVGltZW91dCcsXHJcbiAgICAgIG1lc3NhZ2U6ICdUaGUgbnVtYmVyIG9mIG1pbGxpc2Vjb25kcyB0byB3YWl0IGZvciBhY3F1aXJpbmcgYSByZXNvdXJjZScsXHJcbiAgICAgIGluaXRpYWw6IGRlZmF1bHRDb25maWcucG9vbC5hY3F1aXJlVGltZW91dC52YWx1ZVxyXG4gICAgfSxcclxuICAgIHtcclxuICAgICAgdHlwZTogJ251bWJlcicsXHJcbiAgICAgIG5hbWU6ICdjcmVhdGVUaW1lb3V0JyxcclxuICAgICAgbWVzc2FnZTogJ1RoZSBudW1iZXIgb2YgbWlsbGlzZWNvbmRzIHRvIHdhaXQgZm9yIGNyZWF0aW5nIGEgcmVzb3VyY2UnLFxyXG4gICAgICBpbml0aWFsOiBkZWZhdWx0Q29uZmlnLnBvb2wuY3JlYXRlVGltZW91dC52YWx1ZVxyXG4gICAgfSxcclxuICAgIHtcclxuICAgICAgdHlwZTogJ251bWJlcicsXHJcbiAgICAgIG5hbWU6ICdkZXN0cm95VGltZW91dCcsXHJcbiAgICAgIG1lc3NhZ2U6ICdUaGUgbnVtYmVyIG9mIG1pbGxpc2Vjb25kcyB0byB3YWl0IGZvciBkZXN0cm95aW5nIGEgcmVzb3VyY2UnLFxyXG4gICAgICBpbml0aWFsOiBkZWZhdWx0Q29uZmlnLnBvb2wuZGVzdHJveVRpbWVvdXQudmFsdWVcclxuICAgIH0sXHJcbiAgICB7XHJcbiAgICAgIHR5cGU6ICdudW1iZXInLFxyXG4gICAgICBuYW1lOiAnaWRsZVRpbWVvdXQnLFxyXG4gICAgICBtZXNzYWdlOiAnVGhlIG51bWJlciBvZiBtaWxsaXNlY29uZHMgYWZ0ZXIgYW4gaWRsZSByZXNvdXJjZSBpcyBkZXN0cm95ZWQnLFxyXG4gICAgICBpbml0aWFsOiBkZWZhdWx0Q29uZmlnLnBvb2wuaWRsZVRpbWVvdXQudmFsdWVcclxuICAgIH0sXHJcbiAgICB7XHJcbiAgICAgIHR5cGU6ICdudW1iZXInLFxyXG4gICAgICBuYW1lOiAnY3JlYXRlUmV0cnlJbnRlcnZhbCcsXHJcbiAgICAgIG1lc3NhZ2U6XHJcbiAgICAgICAgJ1RoZSByZXRyeSBpbnRlcnZhbCBpbiBtaWxsaXNlY29uZHMgYWZ0ZXIgYSBjcmVhdGUgcHJvY2VzcyBmYWlscycsXHJcbiAgICAgIGluaXRpYWw6IGRlZmF1bHRDb25maWcucG9vbC5jcmVhdGVSZXRyeUludGVydmFsLnZhbHVlXHJcbiAgICB9LFxyXG4gICAge1xyXG4gICAgICB0eXBlOiAnbnVtYmVyJyxcclxuICAgICAgbmFtZTogJ3JlYXBlckludGVydmFsJyxcclxuICAgICAgbWVzc2FnZTpcclxuICAgICAgICAnVGhlIHJlYXBlciBpbnRlcnZhbCBpbiBtaWxsaXNlY29uZHMgYWZ0ZXIgdHJpZ2dlcmluZyB0aGUgY2hlY2sgZm9yIGlkbGUgcmVzb3VyY2VzIHRvIGRlc3Ryb3knLFxyXG4gICAgICBpbml0aWFsOiBkZWZhdWx0Q29uZmlnLnBvb2wucmVhcGVySW50ZXJ2YWwudmFsdWVcclxuICAgIH0sXHJcbiAgICB7XHJcbiAgICAgIHR5cGU6ICd0b2dnbGUnLFxyXG4gICAgICBuYW1lOiAnYmVuY2htYXJraW5nJyxcclxuICAgICAgbWVzc2FnZTogJ0VuYWJsZSBiZW5jaG1hcmtpbmcgZm9yIGEgcmVzb3VyY2UgcG9vbCcsXHJcbiAgICAgIGluaXRpYWw6IGRlZmF1bHRDb25maWcucG9vbC5iZW5jaG1hcmtpbmcudmFsdWVcclxuICAgIH1cclxuICBdLFxyXG4gIGxvZ2dpbmc6IFtcclxuICAgIHtcclxuICAgICAgdHlwZTogJ251bWJlcicsXHJcbiAgICAgIG5hbWU6ICdsZXZlbCcsXHJcbiAgICAgIG1lc3NhZ2U6XHJcbiAgICAgICAgJ1RoZSBsb2cgbGV2ZWwgKDA6IHNpbGVudCwgMTogZXJyb3IsIDI6IHdhcm5pbmcsIDM6IG5vdGljZSwgNDogdmVyYm9zZSwgNTogYmVuY2htYXJrKScsXHJcbiAgICAgIGluaXRpYWw6IGRlZmF1bHRDb25maWcubG9nZ2luZy5sZXZlbC52YWx1ZSxcclxuICAgICAgcm91bmQ6IDAsXHJcbiAgICAgIG1pbjogMCxcclxuICAgICAgbWF4OiA1XHJcbiAgICB9LFxyXG4gICAge1xyXG4gICAgICB0eXBlOiAndGV4dCcsXHJcbiAgICAgIG5hbWU6ICdmaWxlJyxcclxuICAgICAgbWVzc2FnZTpcclxuICAgICAgICAnQSBsb2cgZmlsZSBuYW1lLiBTZXQgd2l0aCAtLXRvRmlsZSBhbmQgLS1sb2dEZXN0IHRvIGVuYWJsZSBmaWxlIGxvZ2dpbmcnLFxyXG4gICAgICBpbml0aWFsOiBkZWZhdWx0Q29uZmlnLmxvZ2dpbmcuZmlsZS52YWx1ZVxyXG4gICAgfSxcclxuICAgIHtcclxuICAgICAgdHlwZTogJ3RleHQnLFxyXG4gICAgICBuYW1lOiAnZGVzdCcsXHJcbiAgICAgIG1lc3NhZ2U6ICdUaGUgcGF0aCB0byBhIGxvZyBmaWxlIHdoZW4gdGhlIGZpbGUgbG9nZ2luZyBpcyBlbmFibGVkJyxcclxuICAgICAgaW5pdGlhbDogZGVmYXVsdENvbmZpZy5sb2dnaW5nLmRlc3QudmFsdWVcclxuICAgIH0sXHJcbiAgICB7XHJcbiAgICAgIHR5cGU6ICd0b2dnbGUnLFxyXG4gICAgICBuYW1lOiAndG9Db25zb2xlJyxcclxuICAgICAgbWVzc2FnZTogJ0VuYWJsZSBsb2dnaW5nIHRvIHRoZSBjb25zb2xlJyxcclxuICAgICAgaW5pdGlhbDogZGVmYXVsdENvbmZpZy5sb2dnaW5nLnRvQ29uc29sZS52YWx1ZVxyXG4gICAgfSxcclxuICAgIHtcclxuICAgICAgdHlwZTogJ3RvZ2dsZScsXHJcbiAgICAgIG5hbWU6ICd0b0ZpbGUnLFxyXG4gICAgICBtZXNzYWdlOiAnRW5hYmxlcyBsb2dnaW5nIHRvIGEgZmlsZScsXHJcbiAgICAgIGluaXRpYWw6IGRlZmF1bHRDb25maWcubG9nZ2luZy50b0ZpbGUudmFsdWVcclxuICAgIH1cclxuICBdLFxyXG4gIHVpOiBbXHJcbiAgICB7XHJcbiAgICAgIHR5cGU6ICd0b2dnbGUnLFxyXG4gICAgICBuYW1lOiAnZW5hYmxlJyxcclxuICAgICAgbWVzc2FnZTogJ0VuYWJsZSBVSSBmb3IgdGhlIGV4cG9ydCBzZXJ2ZXInLFxyXG4gICAgICBpbml0aWFsOiBkZWZhdWx0Q29uZmlnLnVpLmVuYWJsZS52YWx1ZVxyXG4gICAgfSxcclxuICAgIHtcclxuICAgICAgdHlwZTogJ3RleHQnLFxyXG4gICAgICBuYW1lOiAncm91dGUnLFxyXG4gICAgICBtZXNzYWdlOiAnQSByb3V0ZSB0byBhdHRhY2ggdGhlIFVJJyxcclxuICAgICAgaW5pdGlhbDogZGVmYXVsdENvbmZpZy51aS5yb3V0ZS52YWx1ZVxyXG4gICAgfVxyXG4gIF0sXHJcbiAgb3RoZXI6IFtcclxuICAgIHtcclxuICAgICAgdHlwZTogJ3RleHQnLFxyXG4gICAgICBuYW1lOiAnbm9kZUVudicsXHJcbiAgICAgIG1lc3NhZ2U6ICdUaGUgdHlwZSBvZiBOb2RlLmpzIGVudmlyb25tZW50JyxcclxuICAgICAgaW5pdGlhbDogZGVmYXVsdENvbmZpZy5vdGhlci5ub2RlRW52LnZhbHVlXHJcbiAgICB9LFxyXG4gICAge1xyXG4gICAgICB0eXBlOiAndG9nZ2xlJyxcclxuICAgICAgbmFtZTogJ2xpc3RlblRvUHJvY2Vzc0V4aXRzJyxcclxuICAgICAgbWVzc2FnZTogJ1NldCB0byBmYWxzZSB0byBza2lwIGF0dGFjaGluZyBwcm9jZXNzLmV4aXQgaGFuZGxlcnMnLFxyXG4gICAgICBpbml0aWFsOiBkZWZhdWx0Q29uZmlnLm90aGVyLmxpc3RlblRvUHJvY2Vzc0V4aXRzLnZhbHVlXHJcbiAgICB9LFxyXG4gICAge1xyXG4gICAgICB0eXBlOiAndG9nZ2xlJyxcclxuICAgICAgbmFtZTogJ25vTG9nbycsXHJcbiAgICAgIG1lc3NhZ2U6ICdTa2lwIHByaW50aW5nIHRoZSBsb2dvIG9uIHN0YXJ0dXAuIFJlcGxhY2VkIGJ5IHNpbXBsZSB0ZXh0JyxcclxuICAgICAgaW5pdGlhbDogZGVmYXVsdENvbmZpZy5vdGhlci5ub0xvZ28udmFsdWVcclxuICAgIH0sXHJcbiAgICB7XHJcbiAgICAgIHR5cGU6ICd0b2dnbGUnLFxyXG4gICAgICBuYW1lOiAnaGFyZFJlc2V0UGFnZScsXHJcbiAgICAgIG1lc3NhZ2U6ICdEZWNpZGVzIGlmIHRoZSBwYWdlIGNvbnRlbnQgc2hvdWxkIGJlIHJlc2V0IGVudGlyZWx5JyxcclxuICAgICAgaW5pdGlhbDogZGVmYXVsdENvbmZpZy5vdGhlci5oYXJkUmVzZXRQYWdlLnZhbHVlXHJcbiAgICB9LFxyXG4gICAge1xyXG4gICAgICB0eXBlOiAndG9nZ2xlJyxcclxuICAgICAgbmFtZTogJ2Jyb3dzZXJTaGVsbE1vZGUnLFxyXG4gICAgICBtZXNzYWdlOiAnRGVjaWRlcyBpZiB0aGUgYnJvd3NlciBydW5zIGluIHRoZSBzaGVsbCBtb2RlJyxcclxuICAgICAgaW5pdGlhbDogZGVmYXVsdENvbmZpZy5vdGhlci5icm93c2VyU2hlbGxNb2RlLnZhbHVlXHJcbiAgICB9XHJcbiAgXSxcclxuICBkZWJ1ZzogW1xyXG4gICAge1xyXG4gICAgICB0eXBlOiAndG9nZ2xlJyxcclxuICAgICAgbmFtZTogJ2VuYWJsZScsXHJcbiAgICAgIG1lc3NhZ2U6ICdFbmFibGVzIGRlYnVnIG1vZGUgZm9yIHRoZSBicm93c2VyIGluc3RhbmNlJyxcclxuICAgICAgaW5pdGlhbDogZGVmYXVsdENvbmZpZy5kZWJ1Zy5lbmFibGUudmFsdWVcclxuICAgIH0sXHJcbiAgICB7XHJcbiAgICAgIHR5cGU6ICd0b2dnbGUnLFxyXG4gICAgICBuYW1lOiAnaGVhZGxlc3MnLFxyXG4gICAgICBtZXNzYWdlOiAnVGhlIG1vZGUgc2V0dGluZyBmb3IgdGhlIGJyb3dzZXInLFxyXG4gICAgICBpbml0aWFsOiBkZWZhdWx0Q29uZmlnLmRlYnVnLmhlYWRsZXNzLnZhbHVlXHJcbiAgICB9LFxyXG4gICAge1xyXG4gICAgICB0eXBlOiAndG9nZ2xlJyxcclxuICAgICAgbmFtZTogJ2RldnRvb2xzJyxcclxuICAgICAgbWVzc2FnZTogJ1RoZSBEZXZUb29scyBmb3IgdGhlIGhlYWRmdWwgYnJvd3NlcicsXHJcbiAgICAgIGluaXRpYWw6IGRlZmF1bHRDb25maWcuZGVidWcuZGV2dG9vbHMudmFsdWVcclxuICAgIH0sXHJcbiAgICB7XHJcbiAgICAgIHR5cGU6ICd0b2dnbGUnLFxyXG4gICAgICBuYW1lOiAnbGlzdGVuVG9Db25zb2xlJyxcclxuICAgICAgbWVzc2FnZTogJ1RoZSBldmVudCBsaXN0ZW5lciBmb3IgY29uc29sZSBtZXNzYWdlcyBmcm9tIHRoZSBicm93c2VyJyxcclxuICAgICAgaW5pdGlhbDogZGVmYXVsdENvbmZpZy5kZWJ1Zy5saXN0ZW5Ub0NvbnNvbGUudmFsdWVcclxuICAgIH0sXHJcbiAgICB7XHJcbiAgICAgIHR5cGU6ICd0b2dnbGUnLFxyXG4gICAgICBuYW1lOiAnZHVtcGlvJyxcclxuICAgICAgbWVzc2FnZTogJ1JlZGlyZWN0cyB0aGUgYnJvd3NlciBzdGRvdXQgYW5kIHN0ZGVyciB0byBOb2RlSlMgcHJvY2VzcycsXHJcbiAgICAgIGluaXRpYWw6IGRlZmF1bHRDb25maWcuZGVidWcuZHVtcGlvLnZhbHVlXHJcbiAgICB9LFxyXG4gICAge1xyXG4gICAgICB0eXBlOiAnbnVtYmVyJyxcclxuICAgICAgbmFtZTogJ3Nsb3dNbycsXHJcbiAgICAgIG1lc3NhZ2U6ICdQdXBwZXRlZXIgb3BlcmF0aW9ucyBzbG93IGRvd24gaW4gbWlsbGlzZWNvbmRzJyxcclxuICAgICAgaW5pdGlhbDogZGVmYXVsdENvbmZpZy5kZWJ1Zy5zbG93TW8udmFsdWVcclxuICAgIH0sXHJcbiAgICB7XHJcbiAgICAgIHR5cGU6ICdudW1iZXInLFxyXG4gICAgICBuYW1lOiAnZGVidWdnaW5nUG9ydCcsXHJcbiAgICAgIG1lc3NhZ2U6ICdUaGUgcG9ydCBudW1iZXIgZm9yIGRlYnVnZ2luZycsXHJcbiAgICAgIGluaXRpYWw6IGRlZmF1bHRDb25maWcuZGVidWcuZGVidWdnaW5nUG9ydC52YWx1ZVxyXG4gICAgfVxyXG4gIF1cclxufTtcclxuXHJcbi8vIEFic29sdXRlIHByb3BzIHRoYXQsIGluIGNhc2Ugb2YgbWVyZ2luZyByZWN1cnNpdmVseSwgbmVlZCB0byBiZSBmb3JjZSBtZXJnZWRcclxuZXhwb3J0IGNvbnN0IGFic29sdXRlUHJvcHMgPSBbXHJcbiAgJ29wdGlvbnMnLFxyXG4gICdnbG9iYWxPcHRpb25zJyxcclxuICAndGhlbWVPcHRpb25zJyxcclxuICAncmVzb3VyY2VzJyxcclxuICAncGF5bG9hZCdcclxuXTtcclxuXHJcbi8vIEFyZ3VtZW50IG5lc3RpbmcgbGV2ZWwgb2YgYWxsIGV4cG9ydCBzZXJ2ZXIgb3B0aW9uc1xyXG5leHBvcnQgY29uc3QgbmVzdGVkQXJncyA9IHt9O1xyXG5cclxuLyoqXHJcbiAqIFJlY3Vyc2l2ZWx5IGNyZWF0ZXMgYSBjaGFpbiBvZiBuZXN0ZWQgYXJndW1lbnRzIGZyb20gYW4gb2JqZWN0LlxyXG4gKlxyXG4gKiBAcGFyYW0ge09iamVjdH0gb2JqIC0gVGhlIG9iamVjdCBjb250YWluaW5nIG5lc3RlZCBhcmd1bWVudHMuXHJcbiAqIEBwYXJhbSB7c3RyaW5nfSBwcm9wQ2hhaW4gLSBUaGUgY3VycmVudCBjaGFpbiBvZiBuZXN0ZWQgcHJvcGVydGllc1xyXG4gKiAodXNlZCBpbnRlcm5hbGx5IGR1cmluZyByZWN1cnNpb24pLlxyXG4gKi9cclxuY29uc3QgY3JlYXRlTmVzdGVkQXJncyA9IChvYmosIHByb3BDaGFpbiA9ICcnKSA9PiB7XHJcbiAgT2JqZWN0LmtleXMob2JqKS5mb3JFYWNoKChrKSA9PiB7XHJcbiAgICBpZiAoIVsncHVwcGV0ZWVyJywgJ2hpZ2hjaGFydHMnXS5pbmNsdWRlcyhrKSkge1xyXG4gICAgICBjb25zdCBlbnRyeSA9IG9ialtrXTtcclxuICAgICAgaWYgKHR5cGVvZiBlbnRyeS52YWx1ZSA9PT0gJ3VuZGVmaW5lZCcpIHtcclxuICAgICAgICAvLyBHbyBkZWVwZXIgaW4gdGhlIG5lc3RlZCBhcmd1bWVudHNcclxuICAgICAgICBjcmVhdGVOZXN0ZWRBcmdzKGVudHJ5LCBgJHtwcm9wQ2hhaW59LiR7a31gKTtcclxuICAgICAgfSBlbHNlIHtcclxuICAgICAgICAvLyBDcmVhdGUgdGhlIGNoYWluIG9mIG5lc3RlZCBhcmd1bWVudHNcclxuICAgICAgICBuZXN0ZWRBcmdzW2VudHJ5LmNsaU5hbWUgfHwga10gPSBgJHtwcm9wQ2hhaW59LiR7a31gLnN1YnN0cmluZygxKTtcclxuXHJcbiAgICAgICAgLy8gU3VwcG9ydCBmb3IgdGhlIGxlZ2FjeSwgUGhhbnRvbUpTIHByb3BlcnRpZXMgbmFtZXNcclxuICAgICAgICBpZiAoZW50cnkubGVnYWN5TmFtZSAhPT0gdW5kZWZpbmVkKSB7XHJcbiAgICAgICAgICBuZXN0ZWRBcmdzW2VudHJ5LmxlZ2FjeU5hbWVdID0gYCR7cHJvcENoYWlufS4ke2t9YC5zdWJzdHJpbmcoMSk7XHJcbiAgICAgICAgfVxyXG4gICAgICB9XHJcbiAgICB9XHJcbiAgfSk7XHJcbn07XHJcblxyXG5jcmVhdGVOZXN0ZWRBcmdzKGRlZmF1bHRDb25maWcpO1xyXG4iLCIvKipcclxuICogQGZpbGVvdmVydmlld1xyXG4gKiBUaGlzIGZpbGUgaXMgcmVzcG9uc2libGUgZm9yIHBhcnNpbmcgdGhlIGVudmlyb25tZW50IHZhcmlhYmxlcyB3aXRoIHRoZSAnem9kJ1xyXG4gKiBsaWJyYXJ5LiBUaGUgcGFyc2VkIGVudmlyb25tZW50IHZhcmlhYmxlcyBhcmUgdGhlbiBleHBvcnRlZCB0byBiZSB1c2VkXHJcbiAqIGluIHRoZSBhcHBsaWNhdGlvbiBhcyBcImVudnNcIi4gV2Ugc2hvdWxkIG5vdCB1c2UgcHJvY2Vzcy5lbnYgZGlyZWN0bHlcclxuICogaW4gdGhlIGFwcGxpY2F0aW9uIGFzIHRoZXNlIHdvdWxkIG5vdCBiZSBwYXJzZWQgcHJvcGVybHkuXHJcbiAqXHJcbiAqIFRoZSBlbnZpcm9ubWVudCB2YXJpYWJsZXMgYXJlIHBhcnNlZCBhbmQgdmFsaWRhdGVkIG9ubHkgb25jZSB3aGVuXHJcbiAqIHRoZSBhcHBsaWNhdGlvbiBzdGFydHMuIFdlIHNob3VsZCB3cml0ZSBhIGN1c3RvbSB2YWxpZGF0b3Igb3IgYSB0cmFuc2Zvcm1lclxyXG4gKiBmb3IgZWFjaCBvZiB0aGUgb3B0aW9ucy5cclxuICovXHJcblxyXG5pbXBvcnQgZG90ZW52IGZyb20gJ2RvdGVudic7XHJcbmltcG9ydCB7IHogfSBmcm9tICd6b2QnO1xyXG5cclxuaW1wb3J0IHsgc2NyaXB0c05hbWVzIH0gZnJvbSAnLi9zY2hlbWFzL2NvbmZpZy5qcyc7XHJcblxyXG4vLyBMb2FkIC5lbnYgaW50byBlbnZpcm9ubWVudCB2YXJpYWJsZXNcclxuZG90ZW52LmNvbmZpZygpO1xyXG5cclxuLy8gT2JqZWN0IHdpdGggY3VzdG9tIHZhbGlkYXRvcnMgYW5kIHRyYW5zZm9ybWVycywgdG8gYXZvaWQgcmVwZXRpdGlvblxyXG4vLyBpbiB0aGUgQ29uZmlnIG9iamVjdFxyXG5jb25zdCB2ID0ge1xyXG4gIC8vIFNwbGl0cyBzdHJpbmcgdmFsdWUgaW50byBlbGVtZW50cyBpbiBhbiBhcnJheSwgdHJpbXMgZXZlcnkgZWxlbWVudCwgY2hlY2tzXHJcbiAgLy8gaWYgYW4gYXJyYXkgaXMgY29ycmVjdCwgaWYgaXQgaXMgZW1wdHksIGFuZCBpZiBpdCBpcywgcmV0dXJucyB1bmRlZmluZWRcclxuICBhcnJheTogKGZpbHRlckFycmF5KSA9PlxyXG4gICAgelxyXG4gICAgICAuc3RyaW5nKClcclxuICAgICAgLnRyYW5zZm9ybSgodmFsdWUpID0+XHJcbiAgICAgICAgdmFsdWVcclxuICAgICAgICAgIC5zcGxpdCgnLCcpXHJcbiAgICAgICAgICAubWFwKCh2YWx1ZSkgPT4gdmFsdWUudHJpbSgpKVxyXG4gICAgICAgICAgLmZpbHRlcigodmFsdWUpID0+IGZpbHRlckFycmF5LmluY2x1ZGVzKHZhbHVlKSlcclxuICAgICAgKVxyXG4gICAgICAudHJhbnNmb3JtKCh2YWx1ZSkgPT4gKHZhbHVlLmxlbmd0aCA/IHZhbHVlIDogdW5kZWZpbmVkKSksXHJcblxyXG4gIC8vIEFsbG93cyBvbmx5IHRydWUsIGZhbHNlIGFuZCBjb3JyZWN0bHkgcGFyc2UgdGhlIHZhbHVlIHRvIGJvb2xlYW5cclxuICAvLyBvciBubyB2YWx1ZSBpbiB3aGljaCBjYXNlIHRoZSByZXR1cm5lZCB2YWx1ZSB3aWxsIGJlIHVuZGVmaW5lZFxyXG4gIGJvb2xlYW46ICgpID0+XHJcbiAgICB6XHJcbiAgICAgIC5lbnVtKFsndHJ1ZScsICdmYWxzZScsICcnXSlcclxuICAgICAgLnRyYW5zZm9ybSgodmFsdWUpID0+ICh2YWx1ZSAhPT0gJycgPyB2YWx1ZSA9PT0gJ3RydWUnIDogdW5kZWZpbmVkKSksXHJcblxyXG4gIC8vIEFsbG93cyBwYXNzZWQgdmFsdWVzIG9yIG5vIHZhbHVlIGluIHdoaWNoIGNhc2UgdGhlIHJldHVybmVkIHZhbHVlIHdpbGxcclxuICAvLyBiZSB1bmRlZmluZWRcclxuICBlbnVtOiAodmFsdWVzKSA9PlxyXG4gICAgelxyXG4gICAgICAuZW51bShbLi4udmFsdWVzLCAnJ10pXHJcbiAgICAgIC50cmFuc2Zvcm0oKHZhbHVlKSA9PiAodmFsdWUgIT09ICcnID8gdmFsdWUgOiB1bmRlZmluZWQpKSxcclxuXHJcbiAgLy8gVHJpbXMgdGhlIHN0cmluZyB2YWx1ZSBhbmQgY2hlY2tzIGlmIGl0IGlzIGVtcHR5IG9yIGNvbnRhaW5zIHN0cmluZ2lmaWVkXHJcbiAgLy8gdmFsdWVzIHN1Y2ggYXMgZmFsc2UsIHVuZGVmaW5lZCwgbnVsbCwgTmFOLCBpZiBpdCBkb2VzLCByZXR1cm5zIHVuZGVmaW5lZFxyXG4gIHN0cmluZzogKCkgPT5cclxuICAgIHpcclxuICAgICAgLnN0cmluZygpXHJcbiAgICAgIC50cmltKClcclxuICAgICAgLnJlZmluZShcclxuICAgICAgICAodmFsdWUpID0+XHJcbiAgICAgICAgICAhWydmYWxzZScsICd1bmRlZmluZWQnLCAnbnVsbCcsICdOYU4nXS5pbmNsdWRlcyh2YWx1ZSkgfHxcclxuICAgICAgICAgIHZhbHVlID09PSAnJyxcclxuICAgICAgICAodmFsdWUpID0+ICh7XHJcbiAgICAgICAgICBtZXNzYWdlOiBgVGhlIHN0cmluZyBjb250YWlucyBmb3JiaWRkZW4gdmFsdWVzLCByZWNlaXZlZCAnJHt2YWx1ZX0nYFxyXG4gICAgICAgIH0pXHJcbiAgICAgIClcclxuICAgICAgLnRyYW5zZm9ybSgodmFsdWUpID0+ICh2YWx1ZSAhPT0gJycgPyB2YWx1ZSA6IHVuZGVmaW5lZCkpLFxyXG5cclxuICAvLyBDaGVja3MgaWYgdGhlIHN0cmluZyBpcyBhIHZhbGlkIHBhdGggZGlyZWN0b3J5IChwYXRoIGZvcm1hdClcclxuICBwYXRoOiAoKSA9PlxyXG4gICAgelxyXG4gICAgICAuc3RyaW5nKClcclxuICAgICAgLnRyaW0oKVxyXG4gICAgICAucmVmaW5lKFxyXG4gICAgICAgICh2YWx1ZSkgPT4ge1xyXG4gICAgICAgICAgLy8gU2ltcGxpZmllZCByZWdleCB0byBtYXRjaCBib3RoIGFic29sdXRlIGFuZCByZWxhdGl2ZSBwYXRoc1xyXG4gICAgICAgICAgcmV0dXJuIC9eKFxcLlxcL3xcXC5cXC5cXC98XFwvfFthLXpBLVpdOlxcXFx8W2EtekEtWl06XFwvKT8oKD86W1xcdy1dKylbXFxcXC9dPykrJC8udGVzdChcclxuICAgICAgICAgICAgdmFsdWVcclxuICAgICAgICAgICk7XHJcbiAgICAgICAgfSxcclxuICAgICAgICB7fSxcclxuICAgICAgICB7XHJcbiAgICAgICAgICBtZXNzYWdlOiAnVGhlIHN0cmluZyBpcyBhbiBpbnZhbGlkIHBhdGggZGlyZWN0b3J5IHN0cmluZy4nXHJcbiAgICAgICAgfVxyXG4gICAgICApLFxyXG5cclxuICAvLyBBbGxvd3MgcG9zaXRpdmUgbnVtYmVycyBvciBubyB2YWx1ZSBpbiB3aGljaCBjYXNlIHRoZSByZXR1cm5lZCB2YWx1ZSB3aWxsXHJcbiAgLy8gYmUgdW5kZWZpbmVkXHJcbiAgcG9zaXRpdmVOdW06ICgpID0+XHJcbiAgICB6XHJcbiAgICAgIC5zdHJpbmcoKVxyXG4gICAgICAudHJpbSgpXHJcbiAgICAgIC5yZWZpbmUoXHJcbiAgICAgICAgKHZhbHVlKSA9PlxyXG4gICAgICAgICAgdmFsdWUgPT09ICcnIHx8ICghaXNOYU4ocGFyc2VGbG9hdCh2YWx1ZSkpICYmIHBhcnNlRmxvYXQodmFsdWUpID4gMCksXHJcbiAgICAgICAgKHZhbHVlKSA9PiAoe1xyXG4gICAgICAgICAgbWVzc2FnZTogYFRoZSB2YWx1ZSBtdXN0IGJlIG51bWVyaWMgYW5kIHBvc2l0aXZlLCByZWNlaXZlZCAnJHt2YWx1ZX0nYFxyXG4gICAgICAgIH0pXHJcbiAgICAgIClcclxuICAgICAgLnRyYW5zZm9ybSgodmFsdWUpID0+ICh2YWx1ZSAhPT0gJycgPyBwYXJzZUZsb2F0KHZhbHVlKSA6IHVuZGVmaW5lZCkpLFxyXG5cclxuICAvLyBBbGxvd3Mgbm9uLW5lZ2F0aXZlIG51bWJlcnMgb3Igbm8gdmFsdWUgaW4gd2hpY2ggY2FzZSB0aGUgcmV0dXJuZWQgdmFsdWVcclxuICAvLyB3aWxsIGJlIHVuZGVmaW5lZFxyXG4gIG5vbk5lZ2F0aXZlTnVtOiAoKSA9PlxyXG4gICAgelxyXG4gICAgICAuc3RyaW5nKClcclxuICAgICAgLnRyaW0oKVxyXG4gICAgICAucmVmaW5lKFxyXG4gICAgICAgICh2YWx1ZSkgPT5cclxuICAgICAgICAgIHZhbHVlID09PSAnJyB8fCAoIWlzTmFOKHBhcnNlRmxvYXQodmFsdWUpKSAmJiBwYXJzZUZsb2F0KHZhbHVlKSA+PSAwKSxcclxuICAgICAgICAodmFsdWUpID0+ICh7XHJcbiAgICAgICAgICBtZXNzYWdlOiBgVGhlIHZhbHVlIG11c3QgYmUgbnVtZXJpYyBhbmQgbm9uLW5lZ2F0aXZlLCByZWNlaXZlZCAnJHt2YWx1ZX0nYFxyXG4gICAgICAgIH0pXHJcbiAgICAgIClcclxuICAgICAgLnRyYW5zZm9ybSgodmFsdWUpID0+ICh2YWx1ZSAhPT0gJycgPyBwYXJzZUZsb2F0KHZhbHVlKSA6IHVuZGVmaW5lZCkpXHJcbn07XHJcblxyXG5leHBvcnQgY29uc3QgQ29uZmlnID0gei5vYmplY3Qoe1xyXG4gIC8vIHB1cHBldGVlclxyXG4gIFBVUFBFVEVFUl9URU1QX0RJUjogdi5wYXRoKCksXHJcblxyXG4gIC8vIGhpZ2hjaGFydHNcclxuICBISUdIQ0hBUlRTX1ZFUlNJT046IHpcclxuICAgIC5zdHJpbmcoKVxyXG4gICAgLnRyaW0oKVxyXG4gICAgLnJlZmluZShcclxuICAgICAgKHZhbHVlKSA9PiAvXihsYXRlc3R8XFxkKyhcXC5cXGQrKXswLDJ9KSQvLnRlc3QodmFsdWUpIHx8IHZhbHVlID09PSAnJyxcclxuICAgICAgKHZhbHVlKSA9PiAoe1xyXG4gICAgICAgIG1lc3NhZ2U6IGBISUdIQ0hBUlRTX1ZFUlNJT04gbXVzdCBiZSAnbGF0ZXN0JywgYSBtYWpvciB2ZXJzaW9uLCBvciBpbiB0aGUgZm9ybSBYWC5ZWS5aWiwgcmVjZWl2ZWQgJyR7dmFsdWV9J2BcclxuICAgICAgfSlcclxuICAgIClcclxuICAgIC50cmFuc2Zvcm0oKHZhbHVlKSA9PiAodmFsdWUgIT09ICcnID8gdmFsdWUgOiB1bmRlZmluZWQpKSxcclxuICBISUdIQ0hBUlRTX0NETl9VUkw6IHpcclxuICAgIC5zdHJpbmcoKVxyXG4gICAgLnRyaW0oKVxyXG4gICAgLnJlZmluZShcclxuICAgICAgKHZhbHVlKSA9PlxyXG4gICAgICAgIHZhbHVlLnN0YXJ0c1dpdGgoJ2h0dHBzOi8vJykgfHxcclxuICAgICAgICB2YWx1ZS5zdGFydHNXaXRoKCdodHRwOi8vJykgfHxcclxuICAgICAgICB2YWx1ZSA9PT0gJycsXHJcbiAgICAgICh2YWx1ZSkgPT4gKHtcclxuICAgICAgICBtZXNzYWdlOiBgSW52YWxpZCB2YWx1ZSBmb3IgSElHSENIQVJUU19DRE5fVVJMLiBJdCBzaG91bGQgc3RhcnQgd2l0aCBodHRwOi8vIG9yIGh0dHBzOi8vLCByZWNlaXZlZCAnJHt2YWx1ZX0nYFxyXG4gICAgICB9KVxyXG4gICAgKVxyXG4gICAgLnRyYW5zZm9ybSgodmFsdWUpID0+ICh2YWx1ZSAhPT0gJycgPyB2YWx1ZSA6IHVuZGVmaW5lZCkpLFxyXG4gIEhJR0hDSEFSVFNfVVNFX05QTTogdi5ib29sZWFuKCksXHJcbiAgSElHSENIQVJUU19DT1JFX1NDUklQVFM6IHYuYXJyYXkoc2NyaXB0c05hbWVzLmNvcmUpLFxyXG4gIEhJR0hDSEFSVFNfTU9EVUxFX1NDUklQVFM6IHYuYXJyYXkoc2NyaXB0c05hbWVzLm1vZHVsZXMpLFxyXG4gIEhJR0hDSEFSVFNfSU5ESUNBVE9SX1NDUklQVFM6IHYuYXJyYXkoc2NyaXB0c05hbWVzLmluZGljYXRvcnMpLFxyXG4gIEhJR0hDSEFSVFNfRk9SQ0VfRkVUQ0g6IHYuYm9vbGVhbigpLFxyXG4gIEhJR0hDSEFSVFNfQ0FDSEVfUEFUSDogdi5zdHJpbmcoKSxcclxuICBISUdIQ0hBUlRTX0FETUlOX1RPS0VOOiB2LnN0cmluZygpLFxyXG5cclxuICAvLyBleHBvcnRcclxuICBFWFBPUlRfVFlQRTogdi5lbnVtKFsnanBlZycsICdwbmcnLCAncGRmJywgJ3N2ZyddKSxcclxuICBFWFBPUlRfQ09OU1RSOiB2LmVudW0oWydjaGFydCcsICdzdG9ja0NoYXJ0JywgJ21hcENoYXJ0JywgJ2dhbnR0Q2hhcnQnXSksXHJcbiAgRVhQT1JUX0RFRkFVTFRfSEVJR0hUOiB2LnBvc2l0aXZlTnVtKCksXHJcbiAgRVhQT1JUX0RFRkFVTFRfV0lEVEg6IHYucG9zaXRpdmVOdW0oKSxcclxuICBFWFBPUlRfREVGQVVMVF9TQ0FMRTogdi5wb3NpdGl2ZU51bSgpLFxyXG4gIEVYUE9SVF9SQVNURVJJWkFUSU9OX1RJTUVPVVQ6IHYubm9uTmVnYXRpdmVOdW0oKSxcclxuXHJcbiAgLy8gY3VzdG9tXHJcbiAgQ1VTVE9NX0xPR0lDX0FMTE9XX0NPREVfRVhFQ1VUSU9OOiB2LmJvb2xlYW4oKSxcclxuICBDVVNUT01fTE9HSUNfQUxMT1dfRklMRV9SRVNPVVJDRVM6IHYuYm9vbGVhbigpLFxyXG5cclxuICAvLyBzZXJ2ZXJcclxuICBTRVJWRVJfRU5BQkxFOiB2LmJvb2xlYW4oKSxcclxuICBTRVJWRVJfSE9TVDogdi5zdHJpbmcoKSxcclxuICBTRVJWRVJfUE9SVDogdi5wb3NpdGl2ZU51bSgpLFxyXG4gIFNFUlZFUl9NQVhfVVBMT0FEX1NJWkU6IHYucG9zaXRpdmVOdW0oKSxcclxuICBTRVJWRVJfQkVOQ0hNQVJLSU5HOiB2LmJvb2xlYW4oKSxcclxuXHJcbiAgLy8gc2VydmVyIHByb3h5XHJcbiAgU0VSVkVSX1BST1hZX0hPU1Q6IHYuc3RyaW5nKCksXHJcbiAgU0VSVkVSX1BST1hZX1BPUlQ6IHYucG9zaXRpdmVOdW0oKSxcclxuICBTRVJWRVJfUFJPWFlfVVNFUk5BTUU6IHYuc3RyaW5nKCksXHJcbiAgU0VSVkVSX1BST1hZX1BBU1NXT1JEOiB2LnN0cmluZygpLFxyXG4gIFNFUlZFUl9QUk9YWV9USU1FT1VUOiB2Lm5vbk5lZ2F0aXZlTnVtKCksXHJcblxyXG4gIC8vIHNlcnZlciByYXRlIGxpbWl0aW5nXHJcbiAgU0VSVkVSX1JBVEVfTElNSVRJTkdfRU5BQkxFOiB2LmJvb2xlYW4oKSxcclxuICBTRVJWRVJfUkFURV9MSU1JVElOR19NQVhfUkVRVUVTVFM6IHYubm9uTmVnYXRpdmVOdW0oKSxcclxuICBTRVJWRVJfUkFURV9MSU1JVElOR19XSU5ET1c6IHYubm9uTmVnYXRpdmVOdW0oKSxcclxuICBTRVJWRVJfUkFURV9MSU1JVElOR19ERUxBWTogdi5ub25OZWdhdGl2ZU51bSgpLFxyXG4gIFNFUlZFUl9SQVRFX0xJTUlUSU5HX1RSVVNUX1BST1hZOiB2LmJvb2xlYW4oKSxcclxuICBTRVJWRVJfUkFURV9MSU1JVElOR19TS0lQX0tFWTogdi5zdHJpbmcoKSxcclxuICBTRVJWRVJfUkFURV9MSU1JVElOR19TS0lQX1RPS0VOOiB2LnN0cmluZygpLFxyXG5cclxuICAvLyBzZXJ2ZXIgc3NsXHJcbiAgU0VSVkVSX1NTTF9FTkFCTEU6IHYuYm9vbGVhbigpLFxyXG4gIFNFUlZFUl9TU0xfRk9SQ0U6IHYuYm9vbGVhbigpLFxyXG4gIFNFUlZFUl9TU0xfUE9SVDogdi5wb3NpdGl2ZU51bSgpLFxyXG4gIFNFUlZFUl9TU0xfQ0VSVF9QQVRIOiB2LnN0cmluZygpLFxyXG5cclxuICAvLyBwb29sXHJcbiAgUE9PTF9NSU5fV09SS0VSUzogdi5ub25OZWdhdGl2ZU51bSgpLFxyXG4gIFBPT0xfTUFYX1dPUktFUlM6IHYubm9uTmVnYXRpdmVOdW0oKSxcclxuICBQT09MX1dPUktfTElNSVQ6IHYucG9zaXRpdmVOdW0oKSxcclxuICBQT09MX0FDUVVJUkVfVElNRU9VVDogdi5ub25OZWdhdGl2ZU51bSgpLFxyXG4gIFBPT0xfQ1JFQVRFX1RJTUVPVVQ6IHYubm9uTmVnYXRpdmVOdW0oKSxcclxuICBQT09MX0RFU1RST1lfVElNRU9VVDogdi5ub25OZWdhdGl2ZU51bSgpLFxyXG4gIFBPT0xfSURMRV9USU1FT1VUOiB2Lm5vbk5lZ2F0aXZlTnVtKCksXHJcbiAgUE9PTF9DUkVBVEVfUkVUUllfSU5URVJWQUw6IHYubm9uTmVnYXRpdmVOdW0oKSxcclxuICBQT09MX1JFQVBFUl9JTlRFUlZBTDogdi5ub25OZWdhdGl2ZU51bSgpLFxyXG4gIFBPT0xfQkVOQ0hNQVJLSU5HOiB2LmJvb2xlYW4oKSxcclxuXHJcbiAgLy8gbG9nZ2VyXHJcbiAgTE9HR0lOR19MRVZFTDogelxyXG4gICAgLnN0cmluZygpXHJcbiAgICAudHJpbSgpXHJcbiAgICAucmVmaW5lKFxyXG4gICAgICAodmFsdWUpID0+XHJcbiAgICAgICAgdmFsdWUgPT09ICcnIHx8XHJcbiAgICAgICAgKCFpc05hTihwYXJzZUZsb2F0KHZhbHVlKSkgJiZcclxuICAgICAgICAgIHBhcnNlRmxvYXQodmFsdWUpID49IDAgJiZcclxuICAgICAgICAgIHBhcnNlRmxvYXQodmFsdWUpIDw9IDUpLFxyXG4gICAgICAodmFsdWUpID0+ICh7XHJcbiAgICAgICAgbWVzc2FnZTogYEludmFsaWQgdmFsdWUgZm9yIExPR0dJTkdfTEVWRUwuIFdlIG9ubHkgYWNjZXB0IHZhbHVlcyBmcm9tIDAgdG8gNSBhcyBsb2dnaW5nIGxldmVscywgcmVjZWl2ZWQgJyR7dmFsdWV9J2BcclxuICAgICAgfSlcclxuICAgIClcclxuICAgIC50cmFuc2Zvcm0oKHZhbHVlKSA9PiAodmFsdWUgIT09ICcnID8gcGFyc2VGbG9hdCh2YWx1ZSkgOiB1bmRlZmluZWQpKSxcclxuICBMT0dHSU5HX0ZJTEU6IHYuc3RyaW5nKCksXHJcbiAgTE9HR0lOR19ERVNUOiB2LnN0cmluZygpLFxyXG4gIExPR0dJTkdfVE9fQ09OU09MRTogdi5ib29sZWFuKCksXHJcbiAgTE9HR0lOR19UT19GSUxFOiB2LmJvb2xlYW4oKSxcclxuXHJcbiAgLy8gdWlcclxuICBVSV9FTkFCTEU6IHYuYm9vbGVhbigpLFxyXG4gIFVJX1JPVVRFOiB2LnN0cmluZygpLFxyXG5cclxuICAvLyBvdGhlclxyXG4gIE9USEVSX05PREVfRU5WOiB2LmVudW0oWydkZXZlbG9wbWVudCcsICdwcm9kdWN0aW9uJywgJ3Rlc3QnXSksXHJcbiAgT1RIRVJfTElTVEVOX1RPX1BST0NFU1NfRVhJVFM6IHYuYm9vbGVhbigpLFxyXG4gIE9USEVSX05PX0xPR086IHYuYm9vbGVhbigpLFxyXG4gIE9USEVSX0hBUkRfUkVTRVRfUEFHRTogdi5ib29sZWFuKCksXHJcbiAgT1RIRVJfQlJPV1NFUl9TSEVMTF9NT0RFOiB2LmJvb2xlYW4oKSxcclxuICBPVEhFUl9BTExPV19YTElOSzogdi5ib29sZWFuKCksXHJcblxyXG4gIC8vIGRlYnVnZ2VyXHJcbiAgREVCVUdfRU5BQkxFOiB2LmJvb2xlYW4oKSxcclxuICBERUJVR19IRUFETEVTUzogdi5ib29sZWFuKCksXHJcbiAgREVCVUdfREVWVE9PTFM6IHYuYm9vbGVhbigpLFxyXG4gIERFQlVHX0xJU1RFTl9UT19DT05TT0xFOiB2LmJvb2xlYW4oKSxcclxuICBERUJVR19EVU1QSU86IHYuYm9vbGVhbigpLFxyXG4gIERFQlVHX1NMT1dfTU86IHYubm9uTmVnYXRpdmVOdW0oKSxcclxuICBERUJVR19ERUJVR0dJTkdfUE9SVDogdi5wb3NpdGl2ZU51bSgpXHJcbn0pO1xyXG5cclxuZXhwb3J0IGNvbnN0IGVudnMgPSBDb25maWcucGFydGlhbCgpLnBhcnNlKHByb2Nlc3MuZW52KTtcclxuIiwiLyoqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKipcclxuXHJcbkhpZ2hjaGFydHMgRXhwb3J0IFNlcnZlclxyXG5cclxuQ29weXJpZ2h0IChjKSAyMDE2LTIwMjQsIEhpZ2hzb2Z0XHJcblxyXG5MaWNlbmNlZCB1bmRlciB0aGUgTUlUIGxpY2VuY2UuXHJcblxyXG5BZGRpdGlvbmFsbHkgYSB2YWxpZCBIaWdoY2hhcnRzIGxpY2Vuc2UgaXMgcmVxdWlyZWQgZm9yIHVzZS5cclxuXHJcblNlZSBMSUNFTlNFIGZpbGUgaW4gcm9vdCBmb3IgZGV0YWlscy5cclxuXHJcbioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKiovXHJcblxyXG5pbXBvcnQgeyBhcHBlbmRGaWxlLCBleGlzdHNTeW5jLCBta2RpclN5bmMgfSBmcm9tICdmcyc7XHJcblxyXG4vLyBUaGUgYXZhaWxhYmxlIGNvbG9yc1xyXG5jb25zdCBjb2xvcnMgPSBbJ3JlZCcsICd5ZWxsb3cnLCAnYmx1ZScsICdncmF5JywgJ2dyZWVuJ107XHJcblxyXG4vLyBUaGUgZGVmYXVsdCBsb2dnaW5nIGNvbmZpZ1xyXG5sZXQgbG9nZ2luZyA9IHtcclxuICAvLyBGbGFncyBmb3IgbG9nZ2luZyBzdGF0dXNcclxuICB0b0NvbnNvbGU6IHRydWUsXHJcbiAgdG9GaWxlOiBmYWxzZSxcclxuICBwYXRoQ3JlYXRlZDogZmFsc2UsXHJcbiAgLy8gTG9nIGxldmVsc1xyXG4gIGxldmVsc0Rlc2M6IFtcclxuICAgIHtcclxuICAgICAgdGl0bGU6ICdlcnJvcicsXHJcbiAgICAgIGNvbG9yOiBjb2xvcnNbMF1cclxuICAgIH0sXHJcbiAgICB7XHJcbiAgICAgIHRpdGxlOiAnd2FybmluZycsXHJcbiAgICAgIGNvbG9yOiBjb2xvcnNbMV1cclxuICAgIH0sXHJcbiAgICB7XHJcbiAgICAgIHRpdGxlOiAnbm90aWNlJyxcclxuICAgICAgY29sb3I6IGNvbG9yc1syXVxyXG4gICAgfSxcclxuICAgIHtcclxuICAgICAgdGl0bGU6ICd2ZXJib3NlJyxcclxuICAgICAgY29sb3I6IGNvbG9yc1szXVxyXG4gICAgfSxcclxuICAgIHtcclxuICAgICAgdGl0bGU6ICdiZW5jaG1hcmsnLFxyXG4gICAgICBjb2xvcjogY29sb3JzWzRdXHJcbiAgICB9XHJcbiAgXSxcclxuICAvLyBMb2cgbGlzdGVuZXJzXHJcbiAgbGlzdGVuZXJzOiBbXVxyXG59O1xyXG5cclxuLyoqXHJcbiAqIExvZ3MgdGhlIHByb3ZpZGVkIHRleHRzIHRvIGEgZmlsZSwgaWYgZmlsZSBsb2dnaW5nIGlzIGVuYWJsZWQuIEl0IGNyZWF0ZXNcclxuICogdGhlIG5lY2Vzc2FyeSBkaXJlY3Rvcnkgc3RydWN0dXJlIGlmIG5vdCBhbHJlYWR5IGNyZWF0ZWQgYW5kIGFwcGVuZHMgdGhlXHJcbiAqIGNvbnRlbnQsIGluY2x1ZGluZyBhbiBvcHRpb25hbCBwcmVmaXgsIHRvIHRoZSBzcGVjaWZpZWQgbG9nIGZpbGUuXHJcbiAqXHJcbiAqIEBwYXJhbSB7c3RyaW5nW119IHRleHRzIC0gQW4gYXJyYXkgb2YgdGV4dHMgdG8gYmUgbG9nZ2VkLlxyXG4gKiBAcGFyYW0ge3N0cmluZ30gcHJlZml4IC0gQW4gb3B0aW9uYWwgcHJlZml4IHRvIGJlIGFkZGVkIHRvIGVhY2ggbG9nIGVudHJ5LlxyXG4gKi9cclxuY29uc3QgbG9nVG9GaWxlID0gKHRleHRzLCBwcmVmaXgpID0+IHtcclxuICBpZiAoIWxvZ2dpbmcucGF0aENyZWF0ZWQpIHtcclxuICAgIC8vIENyZWF0ZSBpZiBkb2VzIG5vdCBleGlzdFxyXG4gICAgIWV4aXN0c1N5bmMobG9nZ2luZy5kZXN0KSAmJiBta2RpclN5bmMobG9nZ2luZy5kZXN0KTtcclxuXHJcbiAgICAvLyBXZSBub3cgYXNzdW1lIHRoZSBwYXRoIGlzIGF2YWlsYWJsZSwgZS5nLiBpdCdzIHRoZSByZXNwb25zaWJpbGl0eVxyXG4gICAgLy8gb2YgdGhlIHVzZXIgdG8gY3JlYXRlIHRoZSBwYXRoIHdpdGggdGhlIGNvcnJlY3QgYWNjZXNzIHJpZ2h0cy5cclxuICAgIGxvZ2dpbmcucGF0aENyZWF0ZWQgPSB0cnVlO1xyXG4gIH1cclxuXHJcbiAgLy8gQWRkIHRoZSBjb250ZW50IHRvIGEgZmlsZVxyXG4gIGFwcGVuZEZpbGUoXHJcbiAgICBgJHtsb2dnaW5nLmRlc3R9JHtsb2dnaW5nLmZpbGV9YCxcclxuICAgIFtwcmVmaXhdLmNvbmNhdCh0ZXh0cykuam9pbignICcpICsgJ1xcbicsXHJcbiAgICAoZXJyb3IpID0+IHtcclxuICAgICAgaWYgKGVycm9yKSB7XHJcbiAgICAgICAgY29uc29sZS5sb2coYFtsb2dnZXJdIFVuYWJsZSB0byB3cml0ZSB0byBsb2cgZmlsZTogJHtlcnJvcn1gKTtcclxuICAgICAgICBsb2dnaW5nLnRvRmlsZSA9IGZhbHNlO1xyXG4gICAgICB9XHJcbiAgICB9XHJcbiAgKTtcclxufTtcclxuXHJcbi8qKlxyXG4gKiBMb2dzIGEgbWVzc2FnZS4gQWNjZXB0cyBhIHZhcmlhYmxlIGFtb3VudCBvZiBhcmd1bWVudHMuIEFyZ3VtZW50cyBhZnRlclxyXG4gKiBgbGV2ZWxgIHdpbGwgYmUgcGFzc2VkIGRpcmVjdGx5IHRvIGNvbnNvbGUubG9nLCBhbmQvb3Igd2lsbCBiZSBqb2luZWRcclxuICogYW5kIGFwcGVuZGVkIHRvIHRoZSBsb2cgZmlsZS5cclxuICpcclxuICogQHBhcmFtIHthbnl9IGFyZ3MgLSBBbiBhcnJheSBvZiBhcmd1bWVudHMgd2hlcmUgdGhlIGZpcnN0IGlzIHRoZSBsb2cgbGV2ZWxcclxuICogYW5kIHRoZSByZXN0IGFyZSBzdHJpbmdzIHRvIGJ1aWxkIGEgbWVzc2FnZSB3aXRoLlxyXG4gKi9cclxuZXhwb3J0IGNvbnN0IGxvZyA9ICguLi5hcmdzKSA9PiB7XHJcbiAgY29uc3QgW25ld0xldmVsLCAuLi50ZXh0c10gPSBhcmdzO1xyXG5cclxuICAvLyBDdXJyZW50IGxvZ2dpbmcgb3B0aW9uc1xyXG4gIGNvbnN0IHsgbGV2ZWxzRGVzYywgbGV2ZWwgfSA9IGxvZ2dpbmc7XHJcblxyXG4gIC8vIENoZWNrIGlmIGxvZyBsZXZlbCBpcyB3aXRoaW4gYSBjb3JyZWN0IHJhbmdlIG9yIGlzIGEgYmVuY2htYXJrIGxvZ1xyXG4gIGlmIChcclxuICAgIG5ld0xldmVsICE9PSA1ICYmXHJcbiAgICAobmV3TGV2ZWwgPT09IDAgfHwgbmV3TGV2ZWwgPiBsZXZlbCB8fCBsZXZlbCA+IGxldmVsc0Rlc2MubGVuZ3RoKVxyXG4gICkge1xyXG4gICAgcmV0dXJuO1xyXG4gIH1cclxuXHJcbiAgLy8gR2V0IHJpZCBvZiB0aGUgR01UIHRleHQgaW5mb3JtYXRpb25cclxuICBjb25zdCBuZXdEYXRlID0gbmV3IERhdGUoKS50b1N0cmluZygpLnNwbGl0KCcoJylbMF0udHJpbSgpO1xyXG5cclxuICAvLyBDcmVhdGUgYSBtZXNzYWdlJ3MgcHJlZml4XHJcbiAgY29uc3QgcHJlZml4ID0gYCR7bmV3RGF0ZX0gWyR7bGV2ZWxzRGVzY1tuZXdMZXZlbCAtIDFdLnRpdGxlfV0gLWA7XHJcblxyXG4gIC8vIENhbGwgYXZhaWxhYmxlIGxvZyBsaXN0ZW5lcnNcclxuICBsb2dnaW5nLmxpc3RlbmVycy5mb3JFYWNoKChmbikgPT4ge1xyXG4gICAgZm4ocHJlZml4LCB0ZXh0cy5qb2luKCcgJykpO1xyXG4gIH0pO1xyXG5cclxuICAvLyBMb2cgdG8gY29uc29sZVxyXG4gIGlmIChsb2dnaW5nLnRvQ29uc29sZSkge1xyXG4gICAgY29uc29sZS5sb2cuYXBwbHkoXHJcbiAgICAgIHVuZGVmaW5lZCxcclxuICAgICAgW3ByZWZpeC50b1N0cmluZygpW2xvZ2dpbmcubGV2ZWxzRGVzY1tuZXdMZXZlbCAtIDFdLmNvbG9yXV0uY29uY2F0KHRleHRzKVxyXG4gICAgKTtcclxuICB9XHJcblxyXG4gIC8vIExvZyB0byBmaWxlXHJcbiAgaWYgKGxvZ2dpbmcudG9GaWxlKSB7XHJcbiAgICBsb2dUb0ZpbGUodGV4dHMsIHByZWZpeCk7XHJcbiAgfVxyXG59O1xyXG5cclxuLyoqXHJcbiAqIExvZ3MgYW4gZXJyb3IgbWVzc2FnZSB3aXRoIGl0cyBzdGFjayB0cmFjZS4gT3B0aW9uYWxseSwgYSBjdXN0b20gbWVzc2FnZVxyXG4gKiBjYW4gYmUgcHJvdmlkZWQuXHJcbiAqXHJcbiAqIEBwYXJhbSB7bnVtYmVyfSBsZXZlbCAtIFRoZSBsb2cgbGV2ZWwuXHJcbiAqIEBwYXJhbSB7RXJyb3J9IGVycm9yIC0gVGhlIGVycm9yIG9iamVjdC5cclxuICogQHBhcmFtIHtzdHJpbmd9IGN1c3RvbU1lc3NhZ2UgLSBBbiBvcHRpb25hbCBjdXN0b20gbWVzc2FnZSB0byBiZSBsb2dnZWQgYWxvbmdcclxuICogd2l0aCB0aGUgZXJyb3IuXHJcbiAqL1xyXG5leHBvcnQgY29uc3QgbG9nV2l0aFN0YWNrID0gKG5ld0xldmVsLCBlcnJvciwgY3VzdG9tTWVzc2FnZSkgPT4ge1xyXG4gIC8vIEdldCB0aGUgbWFpbiBtZXNzYWdlXHJcbiAgY29uc3QgbWFpbk1lc3NhZ2UgPSBjdXN0b21NZXNzYWdlIHx8IGVycm9yLm1lc3NhZ2U7XHJcblxyXG4gIC8vIEN1cnJlbnQgbG9nZ2luZyBvcHRpb25zXHJcbiAgY29uc3QgeyBsZXZlbCwgbGV2ZWxzRGVzYyB9ID0gbG9nZ2luZztcclxuXHJcbiAgLy8gQ2hlY2sgaWYgbG9nIGxldmVsIGlzIHdpdGhpbiBhIGNvcnJlY3QgcmFuZ2VcclxuICBpZiAobmV3TGV2ZWwgPT09IDAgfHwgbmV3TGV2ZWwgPiBsZXZlbCB8fCBsZXZlbCA+IGxldmVsc0Rlc2MubGVuZ3RoKSB7XHJcbiAgICByZXR1cm47XHJcbiAgfVxyXG5cclxuICAvLyBHZXQgcmlkIG9mIHRoZSBHTVQgdGV4dCBpbmZvcm1hdGlvblxyXG4gIGNvbnN0IG5ld0RhdGUgPSBuZXcgRGF0ZSgpLnRvU3RyaW5nKCkuc3BsaXQoJygnKVswXS50cmltKCk7XHJcblxyXG4gIC8vIENyZWF0ZSBhIG1lc3NhZ2UncyBwcmVmaXhcclxuICBjb25zdCBwcmVmaXggPSBgJHtuZXdEYXRlfSBbJHtsZXZlbHNEZXNjW25ld0xldmVsIC0gMV0udGl0bGV9XSAtYDtcclxuXHJcbiAgLy8gSWYgdGhlIGN1c3RvbU1lc3NhZ2UgZXhpc3RzLCB3ZSB3YW50IHRvIGRpc3BsYXkgdGhlIHdob2xlIHN0YWNrIG1lc3NhZ2VcclxuICBjb25zdCBzdGFja01lc3NhZ2UgPVxyXG4gICAgZXJyb3IubWVzc2FnZSAhPT0gZXJyb3Iuc3RhY2tNZXNzYWdlIHx8IGVycm9yLnN0YWNrTWVzc2FnZSA9PT0gdW5kZWZpbmVkXHJcbiAgICAgID8gZXJyb3Iuc3RhY2tcclxuICAgICAgOiBlcnJvci5zdGFjay5zcGxpdCgnXFxuJykuc2xpY2UoMSkuam9pbignXFxuJyk7XHJcblxyXG4gIC8vIENvbWJpbmUgY3VzdG9tIG1lc3NhZ2Ugb3IgZXJyb3IgbWVzc2FnZSB3aXRoIGVycm9yIHN0YWNrIG1lc3NhZ2VcclxuICBjb25zdCB0ZXh0cyA9IFttYWluTWVzc2FnZSwgJ1xcbicsIHN0YWNrTWVzc2FnZV07XHJcblxyXG4gIC8vIExvZyB0byBjb25zb2xlXHJcbiAgaWYgKGxvZ2dpbmcudG9Db25zb2xlKSB7XHJcbiAgICBjb25zb2xlLmxvZy5hcHBseShcclxuICAgICAgdW5kZWZpbmVkLFxyXG4gICAgICBbcHJlZml4LnRvU3RyaW5nKClbbG9nZ2luZy5sZXZlbHNEZXNjW25ld0xldmVsIC0gMV0uY29sb3JdXS5jb25jYXQoW1xyXG4gICAgICAgIG1haW5NZXNzYWdlW2NvbG9yc1tuZXdMZXZlbCAtIDFdXSxcclxuICAgICAgICAnXFxuJyxcclxuICAgICAgICBzdGFja01lc3NhZ2VcclxuICAgICAgXSlcclxuICAgICk7XHJcbiAgfVxyXG5cclxuICAvLyBDYWxsIGF2YWlsYWJsZSBsb2cgbGlzdGVuZXJzXHJcbiAgbG9nZ2luZy5saXN0ZW5lcnMuZm9yRWFjaCgoZm4pID0+IHtcclxuICAgIGZuKHByZWZpeCwgdGV4dHMuam9pbignICcpKTtcclxuICB9KTtcclxuXHJcbiAgLy8gTG9nIHRvIGZpbGVcclxuICBpZiAobG9nZ2luZy50b0ZpbGUpIHtcclxuICAgIGxvZ1RvRmlsZSh0ZXh0cywgcHJlZml4KTtcclxuICB9XHJcbn07XHJcblxyXG4vKipcclxuICogU2V0cyB0aGUgbG9nIGxldmVsIHRvIHRoZSBzcGVjaWZpZWQgdmFsdWUuIExvZyBsZXZlbHMgYXJlICgwID0gbm8gbG9nZ2luZyxcclxuICogMSA9IGVycm9yLCAyID0gd2FybmluZywgMyA9IG5vdGljZSwgNCA9IHZlcmJvc2Ugb3IgNSA9IGJlbmNobWFyaylcclxuICpcclxuICogQHBhcmFtIHtudW1iZXJ9IG5ld0xldmVsIC0gVGhlIG5ldyBsb2cgbGV2ZWwgdG8gYmUgc2V0LlxyXG4gKi9cclxuZXhwb3J0IGNvbnN0IHNldExvZ0xldmVsID0gKG5ld0xldmVsKSA9PiB7XHJcbiAgaWYgKG5ld0xldmVsID49IDAgJiYgbmV3TGV2ZWwgPD0gbG9nZ2luZy5sZXZlbHNEZXNjLmxlbmd0aCkge1xyXG4gICAgbG9nZ2luZy5sZXZlbCA9IG5ld0xldmVsO1xyXG4gIH1cclxufTtcclxuXHJcbi8qKlxyXG4gKiBFbmFibGVzIGZpbGUgbG9nZ2luZyB3aXRoIHRoZSBzcGVjaWZpZWQgZGVzdGluYXRpb24gYW5kIGxvZyBmaWxlLlxyXG4gKlxyXG4gKiBAcGFyYW0ge3N0cmluZ30gbG9nRGVzdCAtIFRoZSBkZXN0aW5hdGlvbiBwYXRoIGZvciBsb2cgZmlsZXMuXHJcbiAqIEBwYXJhbSB7c3RyaW5nfSBsb2dGaWxlIC0gVGhlIGxvZyBmaWxlIG5hbWUuXHJcbiAqL1xyXG5leHBvcnQgY29uc3QgZW5hYmxlRmlsZUxvZ2dpbmcgPSAobG9nRGVzdCwgbG9nRmlsZSkgPT4ge1xyXG4gIC8vIFVwZGF0ZSBsb2dnaW5nIG9wdGlvbnNcclxuICBsb2dnaW5nID0ge1xyXG4gICAgLi4ubG9nZ2luZyxcclxuICAgIGRlc3Q6IGxvZ0Rlc3QgfHwgbG9nZ2luZy5kZXN0LFxyXG4gICAgZmlsZTogbG9nRmlsZSB8fCBsb2dnaW5nLmZpbGUsXHJcbiAgICB0b0ZpbGU6IHRydWVcclxuICB9O1xyXG5cclxuICBpZiAobG9nZ2luZy5kZXN0Lmxlbmd0aCA9PT0gMCkge1xyXG4gICAgcmV0dXJuIGxvZygxLCAnW2xvZ2dlcl0gRmlsZSBsb2dnaW5nIGluaXRpYWxpemF0aW9uOiBubyBwYXRoIHN1cHBsaWVkLicpO1xyXG4gIH1cclxuXHJcbiAgaWYgKCFsb2dnaW5nLmRlc3QuZW5kc1dpdGgoJy8nKSkge1xyXG4gICAgbG9nZ2luZy5kZXN0ICs9ICcvJztcclxuICB9XHJcbn07XHJcblxyXG4vKipcclxuICogSW5pdGlhbGl6ZXMgbG9nZ2luZyB3aXRoIHRoZSBzcGVjaWZpZWQgbG9nZ2luZyBjb25maWd1cmF0aW9uLlxyXG4gKlxyXG4gKiBAcGFyYW0ge09iamVjdH0gbG9nZ2luZ09wdGlvbnMgLSBUaGUgbG9nZ2luZyBjb25maWd1cmF0aW9uIG9iamVjdC5cclxuICovXHJcbmV4cG9ydCBjb25zdCBpbml0TG9nZ2luZyA9IChsb2dnaW5nT3B0aW9ucykgPT4ge1xyXG4gIC8vIFNldCBhbGwgdGhlIGxvZ2dpbmcgb3B0aW9ucyBvbiBvdXIgbG9nZ2luZyBtb2R1bGUgb2JqZWN0XHJcbiAgZm9yIChjb25zdCBba2V5LCB2YWx1ZV0gb2YgT2JqZWN0LmVudHJpZXMobG9nZ2luZ09wdGlvbnMpKSB7XHJcbiAgICBsb2dnaW5nW2tleV0gPSB2YWx1ZTtcclxuICB9XHJcblxyXG4gIC8vIFNldCB0aGUgbG9nIGxldmVsXHJcbiAgc2V0TG9nTGV2ZWwobG9nZ2luZ09wdGlvbnMgJiYgcGFyc2VJbnQobG9nZ2luZ09wdGlvbnMubGV2ZWwpKTtcclxuXHJcbiAgLy8gU2V0IHRoZSBsb2cgZmlsZSBwYXRoIGFuZCBuYW1lXHJcbiAgaWYgKGxvZ2dpbmdPcHRpb25zICYmIGxvZ2dpbmdPcHRpb25zLmRlc3QgJiYgbG9nZ2luZ09wdGlvbnMudG9GaWxlKSB7XHJcbiAgICBlbmFibGVGaWxlTG9nZ2luZyhcclxuICAgICAgbG9nZ2luZ09wdGlvbnMuZGVzdCxcclxuICAgICAgbG9nZ2luZ09wdGlvbnMuZmlsZSB8fCAnaGlnaGNoYXJ0cy1leHBvcnQtc2VydmVyLmxvZydcclxuICAgICk7XHJcbiAgfVxyXG59O1xyXG5cclxuLyoqXHJcbiAqIEFkZHMgYSBsaXN0ZW5lciBmdW5jdGlvbiB0byB0aGUgbG9nZ2luZyBzeXN0ZW0uXHJcbiAqXHJcbiAqIEBwYXJhbSB7ZnVuY3Rpb259IGZuIC0gVGhlIGxpc3RlbmVyIGZ1bmN0aW9uIHRvIGJlIGFkZGVkLlxyXG4gKi9cclxuZXhwb3J0IGNvbnN0IGxpc3RlbiA9IChmbikgPT4ge1xyXG4gIGxvZ2dpbmcubGlzdGVuZXJzLnB1c2goZm4pO1xyXG59O1xyXG5cclxuZXhwb3J0IGRlZmF1bHQge1xyXG4gIGxvZyxcclxuICBsb2dXaXRoU3RhY2ssXHJcbiAgc2V0TG9nTGV2ZWwsXHJcbiAgZW5hYmxlRmlsZUxvZ2dpbmcsXHJcbiAgaW5pdExvZ2dpbmcsXHJcbiAgbGlzdGVuXHJcbn07XHJcbiIsIi8qKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqXHJcblxyXG5IaWdoY2hhcnRzIEV4cG9ydCBTZXJ2ZXJcclxuXHJcbkNvcHlyaWdodCAoYykgMjAxNi0yMDI0LCBIaWdoc29mdFxyXG5cclxuTGljZW5jZWQgdW5kZXIgdGhlIE1JVCBsaWNlbmNlLlxyXG5cclxuQWRkaXRpb25hbGx5IGEgdmFsaWQgSGlnaGNoYXJ0cyBsaWNlbnNlIGlzIHJlcXVpcmVkIGZvciB1c2UuXHJcblxyXG5TZWUgTElDRU5TRSBmaWxlIGluIHJvb3QgZm9yIGRldGFpbHMuXHJcblxyXG4qKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqL1xyXG5cclxuaW1wb3J0IHsgcmVhZEZpbGVTeW5jIH0gZnJvbSAnZnMnO1xyXG5pbXBvcnQgeyBjcmVhdGVSZXF1aXJlIH0gZnJvbSAnbW9kdWxlJztcclxuaW1wb3J0IHsgZGlybmFtZSwgam9pbiB9IGZyb20gJ3BhdGgnO1xyXG5pbXBvcnQgeyBmaWxlVVJMVG9QYXRoIH0gZnJvbSAndXJsJztcclxuXHJcbmltcG9ydCB7IGRlZmF1bHRDb25maWcgfSBmcm9tICcuLi9saWIvc2NoZW1hcy9jb25maWcuanMnO1xyXG5pbXBvcnQgeyBsb2csIGxvZ1dpdGhTdGFjayB9IGZyb20gJy4vbG9nZ2VyLmpzJztcclxuXHJcbmNvbnN0IE1BWF9CQUNLT0ZGX0FUVEVNUFRTID0gNjtcclxuXHJcbi8vIFRoZSBoaWdoY2hhcnRzIGRlcGVuZGVuY3kgZGlyZWN0b3J5IHBhdGhcclxuZXhwb3J0IGNvbnN0IF9faGlnaGNoYXJ0c0RpciA9IGRpcm5hbWUoXHJcbiAgY3JlYXRlUmVxdWlyZShpbXBvcnQubWV0YS51cmwpLnJlc29sdmUoJ2hpZ2hjaGFydHMvcGFja2FnZS5qc29uJylcclxuKTtcclxuXHJcbmV4cG9ydCBjb25zdCBfX2Rpcm5hbWUgPSBmaWxlVVJMVG9QYXRoKG5ldyBVUkwoJy4uLy4nLCBpbXBvcnQubWV0YS51cmwpKTtcclxuXHJcbi8qKlxyXG4gKiBDbGVhcnMgYW5kIHN0YW5kYXJkaXplcyB0ZXh0IGJ5IHJlcGxhY2luZyBtdWx0aXBsZSBjb25zZWN1dGl2ZSB3aGl0ZXNwYWNlXHJcbiAqIGNoYXJhY3RlcnMgd2l0aCBhIHNpbmdsZSBzcGFjZSBhbmQgdHJpbW1pbmcgYW55IGxlYWRpbmcgb3IgdHJhaWxpbmdcclxuICogd2hpdGVzcGFjZS5cclxuICpcclxuICogQHBhcmFtIHtzdHJpbmd9IHRleHQgLSBUaGUgaW5wdXQgdGV4dCB0byBiZSBjbGVhcmVkLlxyXG4gKiBAcGFyYW0ge1JlZ0V4cH0gW3J1bGU9L1xcc1xccysvZ10gLSBUaGUgcmVndWxhciBleHByZXNzaW9uIHJ1bGUgdG8gbWF0Y2hcclxuICogbXVsdGlwbGUgY29uc2VjdXRpdmUgd2hpdGVzcGFjZSBjaGFyYWN0ZXJzLlxyXG4gKiBAcGFyYW0ge3N0cmluZ30gW3JlcGxhY2VyPScgJ10gLSBUaGUgc3RyaW5nIHVzZWQgdG8gcmVwbGFjZSBtdWx0aXBsZVxyXG4gKiBjb25zZWN1dGl2ZSB3aGl0ZXNwYWNlIGNoYXJhY3RlcnMuXHJcbiAqXHJcbiAqIEByZXR1cm5zIHtzdHJpbmd9IC0gVGhlIGNsZWFyZWQgYW5kIHN0YW5kYXJkaXplZCB0ZXh0LlxyXG4gKi9cclxuZXhwb3J0IGNvbnN0IGNsZWFyVGV4dCA9ICh0ZXh0LCBydWxlID0gL1xcc1xccysvZywgcmVwbGFjZXIgPSAnICcpID0+XHJcbiAgdGV4dC5yZXBsYWNlQWxsKHJ1bGUsIHJlcGxhY2VyKS50cmltKCk7XHJcblxyXG4vKipcclxuICogSW1wbGVtZW50cyBhbiBleHBvbmVudGlhbCBiYWNrb2ZmIHN0cmF0ZWd5IGZvciByZXRyeWluZyBhIGZ1bmN0aW9uIHVudGlsXHJcbiAqIGEgY2VydGFpbiBudW1iZXIgb2YgYXR0ZW1wdHMgYXJlIHJlYWNoZWQuXHJcbiAqXHJcbiAqIEBwYXJhbSB7RnVuY3Rpb259IGZuIC0gVGhlIGZ1bmN0aW9uIHRvIGJlIHJldHJpZWQuXHJcbiAqIEBwYXJhbSB7bnVtYmVyfSBbYXR0ZW1wdD0wXSAtIFRoZSBjdXJyZW50IGF0dGVtcHQgbnVtYmVyLlxyXG4gKiBAcGFyYW0gey4uLmFueX0gYXJncyAtIEFyZ3VtZW50cyB0byBiZSBwYXNzZWQgdG8gdGhlIGZ1bmN0aW9uLlxyXG4gKlxyXG4gKiBAcmV0dXJucyB7UHJvbWlzZX0gLSBBIHByb21pc2UgdGhhdCByZXNvbHZlcyB0byB0aGUgcmVzdWx0IG9mIHRoZSBmdW5jdGlvblxyXG4gKiBpZiBzdWNjZXNzZnVsLlxyXG4gKlxyXG4gKiBAdGhyb3dzIHtFcnJvcn0gLSBUaHJvd3MgYW4gZXJyb3IgaWYgdGhlIG1heGltdW0gbnVtYmVyIG9mIGF0dGVtcHRzXHJcbiAqIGlzIHJlYWNoZWQuXHJcbiAqL1xyXG5leHBvcnQgY29uc3QgZXhwQmFja29mZiA9IGFzeW5jIChmbiwgYXR0ZW1wdCA9IDAsIC4uLmFyZ3MpID0+IHtcclxuICB0cnkge1xyXG4gICAgLy8gVHJ5IHRvIGNhbGwgdGhlIGZ1bmN0aW9uXHJcbiAgICByZXR1cm4gYXdhaXQgZm4oLi4uYXJncyk7XHJcbiAgfSBjYXRjaCAoZXJyb3IpIHtcclxuICAgIC8vIENhbGN1bGF0ZSBkZWxheSBpbiBtc1xyXG4gICAgY29uc3QgZGVsYXlJbk1zID0gMiAqKiBhdHRlbXB0ICogMTAwMDtcclxuXHJcbiAgICAvLyBJZiB0aGUgYXR0ZW1wdCBleGNlZWRzIHRoZSBtYXhpbXVtIGF0dGVtcHRzIG9mIHJlYXBlYXQsIHRocm93IGFuIGVycm9yXHJcbiAgICBpZiAoKythdHRlbXB0ID49IE1BWF9CQUNLT0ZGX0FUVEVNUFRTKSB7XHJcbiAgICAgIHRocm93IGVycm9yO1xyXG4gICAgfVxyXG5cclxuICAgIC8vIFdhaXQgZ2l2ZW4gYW1vdW50IG9mIHRpbWVcclxuICAgIGF3YWl0IG5ldyBQcm9taXNlKChyZXNwb25zZSkgPT4gc2V0VGltZW91dChyZXNwb25zZSwgZGVsYXlJbk1zKSk7XHJcbiAgICBsb2coXHJcbiAgICAgIDMsXHJcbiAgICAgIGBbcG9vbF0gV2FpdGVkICR7ZGVsYXlJbk1zfW1zIHVudGlsIG5leHQgY2FsbCBmb3IgdGhlIHJlc291cmNlIGlkOiAke2FyZ3NbMF19LmBcclxuICAgICk7XHJcblxyXG4gICAgLy8gVHJ5IGFnYWluXHJcbiAgICByZXR1cm4gZXhwQmFja29mZihmbiwgYXR0ZW1wdCwgLi4uYXJncyk7XHJcbiAgfVxyXG59O1xyXG5cclxuLyoqXHJcbiAqIEZpeGVzIHRoZSBleHBvcnQgdHlwZSBiYXNlZCBvbiBNSU1FIHR5cGVzIGFuZCBmaWxlIGV4dGVuc2lvbnMuXHJcbiAqXHJcbiAqIEBwYXJhbSB7c3RyaW5nfSB0eXBlIC0gVGhlIG9yaWdpbmFsIGV4cG9ydCB0eXBlLlxyXG4gKiBAcGFyYW0ge3N0cmluZ30gb3V0ZmlsZSAtIFRoZSBmaWxlIHBhdGggb3IgbmFtZS5cclxuICpcclxuICogQHJldHVybnMge3N0cmluZ30gLSBUaGUgY29ycmVjdGVkIGV4cG9ydCB0eXBlLlxyXG4gKi9cclxuZXhwb3J0IGNvbnN0IGZpeFR5cGUgPSAodHlwZSwgb3V0ZmlsZSkgPT4ge1xyXG4gIC8vIE1JTUUgdHlwZXNcclxuICBjb25zdCBtaW1lVHlwZXMgPSB7XHJcbiAgICAnaW1hZ2UvcG5nJzogJ3BuZycsXHJcbiAgICAnaW1hZ2UvanBlZyc6ICdqcGVnJyxcclxuICAgICdhcHBsaWNhdGlvbi9wZGYnOiAncGRmJyxcclxuICAgICdpbWFnZS9zdmcreG1sJzogJ3N2ZydcclxuICB9O1xyXG5cclxuICAvLyBGb3JtYXRzXHJcbiAgY29uc3QgZm9ybWF0cyA9IFsncG5nJywgJ2pwZWcnLCAncGRmJywgJ3N2ZyddO1xyXG5cclxuICAvLyBDaGVjayBpZiB0eXBlIGFuZCBvdXRmaWxlJ3MgZXh0ZW5zaW9ucyBhcmUgdGhlIHNhbWVcclxuICBpZiAob3V0ZmlsZSkge1xyXG4gICAgY29uc3Qgb3V0VHlwZSA9IG91dGZpbGUuc3BsaXQoJy4nKS5wb3AoKTtcclxuXHJcbiAgICBpZiAob3V0VHlwZSA9PT0gJ2pwZycpIHtcclxuICAgICAgdHlwZSA9ICdqcGVnJztcclxuICAgIH0gZWxzZSBpZiAoZm9ybWF0cy5pbmNsdWRlcyhvdXRUeXBlKSAmJiB0eXBlICE9PSBvdXRUeXBlKSB7XHJcbiAgICAgIHR5cGUgPSBvdXRUeXBlO1xyXG4gICAgfVxyXG4gIH1cclxuXHJcbiAgLy8gUmV0dXJuIGEgY29ycmVjdCB0eXBlXHJcbiAgcmV0dXJuIG1pbWVUeXBlc1t0eXBlXSB8fCBmb3JtYXRzLmZpbmQoKHQpID0+IHQgPT09IHR5cGUpIHx8ICdwbmcnO1xyXG59O1xyXG5cclxuLyoqXHJcbiAqIEhhbmRsZXMgYW5kIHZhbGlkYXRlcyByZXNvdXJjZXMgZm9yIGV4cG9ydC5cclxuICpcclxuICogQHBhcmFtIHtPYmplY3R8c3RyaW5nfSByZXNvdXJjZXMgLSBUaGUgcmVzb3VyY2VzIHRvIGJlIGhhbmRsZWQuIENhbiBiZSBlaXRoZXJcclxuICogYSBKU09OIG9iamVjdCwgc3RyaW5naWZpZWQgSlNPTiBvciBhIHBhdGggdG8gYSBKU09OIGZpbGUuXHJcbiAqIEBwYXJhbSB7Ym9vbGVhbn0gYWxsb3dGaWxlUmVzb3VyY2VzIC0gV2hldGhlciB0byBhbGxvdyBsb2FkaW5nIHJlc291cmNlcyBmcm9tXHJcbiAqIGZpbGVzLlxyXG4gKlxyXG4gKiBAcmV0dXJucyB7T2JqZWN0fHVuZGVmaW5lZH0gLSBUaGUgaGFuZGxlZCByZXNvdXJjZXMgb3IgdW5kZWZpbmVkIGlmIG5vIHZhbGlkXHJcbiAqIHJlc291cmNlcyBhcmUgZm91bmQuXHJcbiAqL1xyXG5leHBvcnQgY29uc3QgaGFuZGxlUmVzb3VyY2VzID0gKHJlc291cmNlcyA9IGZhbHNlLCBhbGxvd0ZpbGVSZXNvdXJjZXMpID0+IHtcclxuICBjb25zdCBhbGxvd2VkUHJvcHMgPSBbJ2pzJywgJ2NzcycsICdmaWxlcyddO1xyXG5cclxuICBsZXQgaGFuZGxlZFJlc291cmNlcyA9IHJlc291cmNlcztcclxuICBsZXQgY29ycmVjdFJlc291cmNlcyA9IGZhbHNlO1xyXG5cclxuICAvLyBUcnkgdG8gbG9hZCByZXNvdXJjZXMgZnJvbSBhIGZpbGVcclxuICBpZiAoYWxsb3dGaWxlUmVzb3VyY2VzICYmIHJlc291cmNlcy5lbmRzV2l0aCgnLmpzb24nKSkge1xyXG4gICAgdHJ5IHtcclxuICAgICAgaGFuZGxlZFJlc291cmNlcyA9IGlzQ29ycmVjdEpTT04ocmVhZEZpbGVTeW5jKHJlc291cmNlcywgJ3V0ZjgnKSk7XHJcbiAgICB9IGNhdGNoIChlcnJvcikge1xyXG4gICAgICByZXR1cm4gbG9nV2l0aFN0YWNrKDIsIGVycm9yLCBgW2NsaV0gTm8gcmVzb3VyY2VzIGZvdW5kLmApO1xyXG4gICAgfVxyXG4gIH0gZWxzZSB7XHJcbiAgICAvLyBUcnkgdG8gZ2V0IEpTT05cclxuICAgIGhhbmRsZWRSZXNvdXJjZXMgPSBpc0NvcnJlY3RKU09OKHJlc291cmNlcyk7XHJcblxyXG4gICAgLy8gR2V0IHJpZCBvZiB0aGUgZmlsZXMgc2VjdGlvblxyXG4gICAgaWYgKGhhbmRsZWRSZXNvdXJjZXMgJiYgIWFsbG93RmlsZVJlc291cmNlcykge1xyXG4gICAgICBkZWxldGUgaGFuZGxlZFJlc291cmNlcy5maWxlcztcclxuICAgIH1cclxuICB9XHJcblxyXG4gIC8vIEZpbHRlciBmcm9tIHVubmVjZXNzYXJ5IHByb3BlcnRpZXNcclxuICBmb3IgKGNvbnN0IHByb3BOYW1lIGluIGhhbmRsZWRSZXNvdXJjZXMpIHtcclxuICAgIGlmICghYWxsb3dlZFByb3BzLmluY2x1ZGVzKHByb3BOYW1lKSkge1xyXG4gICAgICBkZWxldGUgaGFuZGxlZFJlc291cmNlc1twcm9wTmFtZV07XHJcbiAgICB9IGVsc2UgaWYgKCFjb3JyZWN0UmVzb3VyY2VzKSB7XHJcbiAgICAgIGNvcnJlY3RSZXNvdXJjZXMgPSB0cnVlO1xyXG4gICAgfVxyXG4gIH1cclxuXHJcbiAgLy8gQ2hlY2sgaWYgYXQgbGVhc3Qgb25lIG9mIGFsbG93ZWQgcHJvcGVydGllcyBpcyBwcmVzZW50XHJcbiAgaWYgKCFjb3JyZWN0UmVzb3VyY2VzKSB7XHJcbiAgICByZXR1cm4gbG9nKDMsIGBbY2xpXSBObyByZXNvdXJjZXMgZm91bmQuYCk7XHJcbiAgfVxyXG5cclxuICAvLyBIYW5kbGUgZmlsZXMgc2VjdGlvblxyXG4gIGlmIChoYW5kbGVkUmVzb3VyY2VzLmZpbGVzKSB7XHJcbiAgICBoYW5kbGVkUmVzb3VyY2VzLmZpbGVzID0gaGFuZGxlZFJlc291cmNlcy5maWxlcy5tYXAoKGl0ZW0pID0+IGl0ZW0udHJpbSgpKTtcclxuICAgIGlmICghaGFuZGxlZFJlc291cmNlcy5maWxlcyB8fCBoYW5kbGVkUmVzb3VyY2VzLmZpbGVzLmxlbmd0aCA8PSAwKSB7XHJcbiAgICAgIGRlbGV0ZSBoYW5kbGVkUmVzb3VyY2VzLmZpbGVzO1xyXG4gICAgfVxyXG4gIH1cclxuXHJcbiAgLy8gUmV0dXJuIHJlc291cmNlc1xyXG4gIHJldHVybiBoYW5kbGVkUmVzb3VyY2VzO1xyXG59O1xyXG5cclxuLyoqXHJcbiAqIFZhbGlkYXRlcyBhbmQgcGFyc2VzIEpTT04gZGF0YS4gQ2hlY2tzIGlmIHByb3ZpZGVkIGRhdGEgaXMgb3IgY2FuXHJcbiAqIGJlIGEgY29ycmVjdCBKU09OLiBJZiBhIHByaW1pdGl2ZSBpcyBwcm92aWRlZCwgaXQgaXMgc3RyaW5naWZpZWQgYW5kIHJldHVybmVkLlxyXG4gKlxyXG4gKiBAcGFyYW0ge09iamVjdHxzdHJpbmd9IGRhdGEgLSBUaGUgSlNPTiBkYXRhIHRvIGJlIHZhbGlkYXRlZCBhbmQgcGFyc2VkLlxyXG4gKiBAcGFyYW0ge2Jvb2xlYW59IHRvU3RyaW5nIC0gV2hldGhlciB0byByZXR1cm4gYSBzdHJpbmdpZmllZCByZXByZXNlbnRhdGlvblxyXG4gKiBvZiB0aGUgcGFyc2VkIEpTT04uXHJcbiAqXHJcbiAqIEByZXR1cm5zIHtPYmplY3R8c3RyaW5nfGJvb2xlYW59IC0gVGhlIHBhcnNlZCBKU09OIG9iamVjdCwgc3RyaW5naWZpZWQgSlNPTixcclxuICogb3IgZmFsc2UgaWYgdmFsaWRhdGlvbiBmYWlscy5cclxuICovXHJcbmV4cG9ydCBmdW5jdGlvbiBpc0NvcnJlY3RKU09OKGRhdGEsIHRvU3RyaW5nKSB7XHJcbiAgdHJ5IHtcclxuICAgIC8vIEdldCB0aGUgc3RyaW5nIHJlcHJlc2VudGF0aW9uIGlmIG5vdCBhbHJlYWR5IGJlZm9yZSBwYXJzaW5nXHJcbiAgICBjb25zdCBwYXJzZWREYXRhID0gSlNPTi5wYXJzZShcclxuICAgICAgdHlwZW9mIGRhdGEgIT09ICdzdHJpbmcnID8gSlNPTi5zdHJpbmdpZnkoZGF0YSkgOiBkYXRhXHJcbiAgICApO1xyXG5cclxuICAgIC8vIFJldHVybiBhIHN0cmluZ2lmaWVkIHJlcHJlc2VudGF0aW9uIG9mIGEgSlNPTiBpZiByZXF1aXJlZFxyXG4gICAgaWYgKHR5cGVvZiBwYXJzZWREYXRhICE9PSAnc3RyaW5nJyAmJiB0b1N0cmluZykge1xyXG4gICAgICByZXR1cm4gSlNPTi5zdHJpbmdpZnkocGFyc2VkRGF0YSk7XHJcbiAgICB9XHJcblxyXG4gICAgLy8gUmV0dXJuIGEgSlNPTlxyXG4gICAgcmV0dXJuIHBhcnNlZERhdGE7XHJcbiAgfSBjYXRjaCB7XHJcbiAgICByZXR1cm4gZmFsc2U7XHJcbiAgfVxyXG59XHJcblxyXG4vKipcclxuICogQ2hlY2tzIGlmIHRoZSBnaXZlbiBpdGVtIGlzIGFuIG9iamVjdC5cclxuICpcclxuICogQHBhcmFtIHthbnl9IGl0ZW0gLSBUaGUgaXRlbSB0byBiZSBjaGVja2VkLlxyXG4gKlxyXG4gKiBAcmV0dXJucyB7Ym9vbGVhbn0gLSBUcnVlIGlmIHRoZSBpdGVtIGlzIGFuIG9iamVjdCwgZmFsc2Ugb3RoZXJ3aXNlLlxyXG4gKi9cclxuZXhwb3J0IGNvbnN0IGlzT2JqZWN0ID0gKGl0ZW0pID0+XHJcbiAgdHlwZW9mIGl0ZW0gPT09ICdvYmplY3QnICYmICFBcnJheS5pc0FycmF5KGl0ZW0pICYmIGl0ZW0gIT09IG51bGw7XHJcblxyXG4vKipcclxuICogQ2hlY2tzIGlmIHRoZSBnaXZlbiBvYmplY3QgaXMgZW1wdHkuXHJcbiAqXHJcbiAqIEBwYXJhbSB7T2JqZWN0fSBpdGVtIC0gVGhlIG9iamVjdCB0byBiZSBjaGVja2VkLlxyXG4gKlxyXG4gKiBAcmV0dXJucyB7Ym9vbGVhbn0gLSBUcnVlIGlmIHRoZSBvYmplY3QgaXMgZW1wdHksIGZhbHNlIG90aGVyd2lzZS5cclxuICovXHJcbmV4cG9ydCBjb25zdCBpc09iamVjdEVtcHR5ID0gKGl0ZW0pID0+XHJcbiAgdHlwZW9mIGl0ZW0gPT09ICdvYmplY3QnICYmXHJcbiAgIUFycmF5LmlzQXJyYXkoaXRlbSkgJiZcclxuICBpdGVtICE9PSBudWxsICYmXHJcbiAgT2JqZWN0LmtleXMoaXRlbSkubGVuZ3RoID09PSAwO1xyXG5cclxuLyoqXHJcbiAqIENoZWNrcyBpZiBhIHByaXZhdGUgSVAgcmFuZ2UgVVJMIGlzIGZvdW5kIGluIHRoZSBnaXZlbiBzdHJpbmcuXHJcbiAqXHJcbiAqIEBwYXJhbSB7c3RyaW5nfSBpdGVtIC0gVGhlIHN0cmluZyB0byBiZSBjaGVja2VkIGZvciBhIHByaXZhdGUgSVAgcmFuZ2UgVVJMLlxyXG4gKlxyXG4gKiBAcmV0dXJucyB7Ym9vbGVhbn0gLSBUcnVlIGlmIGEgcHJpdmF0ZSBJUCByYW5nZSBVUkwgaXMgZm91bmQsIGZhbHNlXHJcbiAqIG90aGVyd2lzZS5cclxuICovXHJcbmV4cG9ydCBjb25zdCBpc1ByaXZhdGVSYW5nZVVybEZvdW5kID0gKGl0ZW0pID0+IHtcclxuICBjb25zdCByZWdleFBhdHRlcm5zID0gW1xyXG4gICAgL3hsaW5rOmhyZWY9XCIoPzpodHRwOlxcL1xcL3xodHRwczpcXC9cXC8pP2xvY2FsaG9zdFxcYi8sXHJcbiAgICAveGxpbms6aHJlZj1cIig/Omh0dHA6XFwvXFwvfGh0dHBzOlxcL1xcLyk/MTBcXC5cXGR7MSwzfVxcLlxcZHsxLDN9XFwuXFxkezEsM31cXGIvLFxyXG4gICAgL3hsaW5rOmhyZWY9XCIoPzpodHRwOlxcL1xcL3xodHRwczpcXC9cXC8pPzEyN1xcLlxcZHsxLDN9XFwuXFxkezEsM31cXC5cXGR7MSwzfVxcYi8sXHJcbiAgICAveGxpbms6aHJlZj1cIig/Omh0dHA6XFwvXFwvfGh0dHBzOlxcL1xcLyk/MTcyXFwuKDFbNi05XXwyWzAtOV18M1swLTFdKVxcLlxcZHsxLDN9XFwuXFxkezEsM31cXGIvLFxyXG4gICAgL3hsaW5rOmhyZWY9XCIoPzpodHRwOlxcL1xcL3xodHRwczpcXC9cXC8pPzE5MlxcLjE2OFxcLlxcZHsxLDN9XFwuXFxkezEsM31cXGIvXHJcbiAgXTtcclxuXHJcbiAgcmV0dXJuIHJlZ2V4UGF0dGVybnMuc29tZSgocGF0dGVybikgPT4gcGF0dGVybi50ZXN0KGl0ZW0pKTtcclxufTtcclxuXHJcbi8qKlxyXG4gKiBDcmVhdGVzIGEgZGVlcCBjb3B5IG9mIHRoZSBnaXZlbiBvYmplY3Qgb3IgYXJyYXkuXHJcbiAqXHJcbiAqIEBwYXJhbSB7T2JqZWN0fEFycmF5fSBvYmogLSBUaGUgb2JqZWN0IG9yIGFycmF5IHRvIGJlIGRlZXBseSBjb3BpZWQuXHJcbiAqXHJcbiAqIEByZXR1cm5zIHtPYmplY3R8QXJyYXl9IC0gVGhlIGRlZXAgY29weSBvZiB0aGUgcHJvdmlkZWQgb2JqZWN0IG9yIGFycmF5LlxyXG4gKi9cclxuZXhwb3J0IGNvbnN0IGRlZXBDb3B5ID0gKG9iaikgPT4ge1xyXG4gIGlmIChvYmogPT09IG51bGwgfHwgdHlwZW9mIG9iaiAhPT0gJ29iamVjdCcpIHtcclxuICAgIHJldHVybiBvYmo7XHJcbiAgfVxyXG5cclxuICBjb25zdCBjb3B5ID0gQXJyYXkuaXNBcnJheShvYmopID8gW10gOiB7fTtcclxuXHJcbiAgZm9yIChjb25zdCBrZXkgaW4gb2JqKSB7XHJcbiAgICBpZiAoT2JqZWN0LnByb3RvdHlwZS5oYXNPd25Qcm9wZXJ0eS5jYWxsKG9iaiwga2V5KSkge1xyXG4gICAgICBjb3B5W2tleV0gPSBkZWVwQ29weShvYmpba2V5XSk7XHJcbiAgICB9XHJcbiAgfVxyXG5cclxuICByZXR1cm4gY29weTtcclxufTtcclxuXHJcbi8qKlxyXG4gKiBDb252ZXJ0cyB0aGUgcHJvdmlkZWQgb3B0aW9ucyBvYmplY3QgdG8gYSBKU09OLWZvcm1hdHRlZCBzdHJpbmcgd2l0aCB0aGVcclxuICogb3B0aW9uIHRvIHByZXNlcnZlIGZ1bmN0aW9ucy5cclxuICpcclxuICogQHBhcmFtIHtPYmplY3R9IG9wdGlvbnMgLSBUaGUgb3B0aW9ucyBvYmplY3QgdG8gYmUgY29udmVydGVkIHRvIGEgc3RyaW5nLlxyXG4gKiBAcGFyYW0ge2Jvb2xlYW59IGFsbG93RnVuY3Rpb25zIC0gSWYgc2V0IHRvIHRydWUsIGZ1bmN0aW9ucyBhcmUgcHJlc2VydmVkXHJcbiAqIGluIHRoZSBvdXRwdXQuXHJcbiAqXHJcbiAqIEByZXR1cm5zIHtzdHJpbmd9IC0gVGhlIEpTT04tZm9ybWF0dGVkIHN0cmluZyByZXByZXNlbnRpbmcgdGhlIG9wdGlvbnMuXHJcbiAqL1xyXG5leHBvcnQgY29uc3Qgb3B0aW9uc1N0cmluZ2lmeSA9IChvcHRpb25zLCBhbGxvd0Z1bmN0aW9ucykgPT4ge1xyXG4gIGNvbnN0IHJlcGxhY2VyQ2FsbGJhY2sgPSAobmFtZSwgdmFsdWUpID0+IHtcclxuICAgIGlmICh0eXBlb2YgdmFsdWUgPT09ICdzdHJpbmcnKSB7XHJcbiAgICAgIHZhbHVlID0gdmFsdWUudHJpbSgpO1xyXG5cclxuICAgICAgLy8gSWYgYWxsb3dGdW5jdGlvbnMgaXMgc2V0IHRvIHRydWUsIHByZXNlcnZlIGZ1bmN0aW9uc1xyXG4gICAgICBpZiAoXHJcbiAgICAgICAgKHZhbHVlLnN0YXJ0c1dpdGgoJ2Z1bmN0aW9uKCcpIHx8IHZhbHVlLnN0YXJ0c1dpdGgoJ2Z1bmN0aW9uICgnKSkgJiZcclxuICAgICAgICB2YWx1ZS5lbmRzV2l0aCgnfScpXHJcbiAgICAgICkge1xyXG4gICAgICAgIHZhbHVlID0gYWxsb3dGdW5jdGlvbnNcclxuICAgICAgICAgID8gYEVYUF9GVU4keyh2YWx1ZSArICcnKS5yZXBsYWNlQWxsKC9cXG58XFx0fFxcci9nLCAnICcpfUVYUF9GVU5gXHJcbiAgICAgICAgICA6IHVuZGVmaW5lZDtcclxuICAgICAgfVxyXG4gICAgfVxyXG5cclxuICAgIHJldHVybiB0eXBlb2YgdmFsdWUgPT09ICdmdW5jdGlvbidcclxuICAgICAgPyBgRVhQX0ZVTiR7KHZhbHVlICsgJycpLnJlcGxhY2VBbGwoL1xcbnxcXHR8XFxyL2csICcgJyl9RVhQX0ZVTmBcclxuICAgICAgOiB2YWx1ZTtcclxuICB9O1xyXG5cclxuICAvLyBTdHJpbmdpZnkgb3B0aW9ucyBhbmQgaWYgcmVxdWlyZWQsIHJlcGxhY2Ugc3BlY2lhbCBmdW5jdGlvbnMgbWFya3NcclxuICByZXR1cm4gSlNPTi5zdHJpbmdpZnkob3B0aW9ucywgcmVwbGFjZXJDYWxsYmFjaykucmVwbGFjZUFsbChcclxuICAgIC9cIkVYUF9GVU58RVhQX0ZVTlwiL2csXHJcbiAgICAnJ1xyXG4gICk7XHJcbn07XHJcblxyXG4vKipcclxuICogUHJpbnRzIHRoZSBIaWdoY2hhcnRzIEV4cG9ydCBTZXJ2ZXIgbG9nbyBhbmQgdmVyc2lvbiBpbmZvcm1hdGlvbi5cclxuICpcclxuICogQHBhcmFtIHtib29sZWFufSBub0xvZ28gLSBJZiB0cnVlLCBvbmx5IHByaW50cyB2ZXJzaW9uIGluZm9ybWF0aW9uIHdpdGhvdXRcclxuICogdGhlIGxvZ28uXHJcbiAqL1xyXG5leHBvcnQgY29uc3QgcHJpbnRMb2dvID0gKG5vTG9nbykgPT4ge1xyXG4gIC8vIEdldCBwYWNrYWdlIHZlcnNpb24gZWl0aGVyIGZyb20gZW52IG9yIGZyb20gcGFja2FnZS5qc29uXHJcbiAgY29uc3QgcGFja2FnZVZlcnNpb24gPSBKU09OLnBhcnNlKFxyXG4gICAgcmVhZEZpbGVTeW5jKGpvaW4oX19kaXJuYW1lLCAncGFja2FnZS5qc29uJykpXHJcbiAgKS52ZXJzaW9uO1xyXG5cclxuICAvLyBQcmludCB0ZXh0IG9ubHlcclxuICBpZiAobm9Mb2dvKSB7XHJcbiAgICBjb25zb2xlLmxvZyhgU3RhcnRpbmcgSGlnaGNoYXJ0cyBFeHBvcnQgU2VydmVyIHYke3BhY2thZ2VWZXJzaW9ufS4uLmApO1xyXG4gICAgcmV0dXJuO1xyXG4gIH1cclxuXHJcbiAgLy8gUHJpbnQgdGhlIGxvZ29cclxuICBjb25zb2xlLmxvZyhcclxuICAgIHJlYWRGaWxlU3luYyhfX2Rpcm5hbWUgKyAnL21zZy9zdGFydHVwLm1zZycpLnRvU3RyaW5nKCkuYm9sZC55ZWxsb3csXHJcbiAgICBgdiR7cGFja2FnZVZlcnNpb259XFxuYC5ib2xkXHJcbiAgKTtcclxufTtcclxuXHJcbi8qKlxyXG4gKiBQcmludHMgdGhlIHVzYWdlIGluZm9ybWF0aW9uIGZvciBDTEkgYXJndW1lbnRzLiBJZiByZXF1aXJlZCwgaXQgY2FuIGxpc3RcclxuICogcHJvcGVydGllcyByZWN1cnNpdmVseVxyXG4gKi9cclxuZXhwb3J0IGZ1bmN0aW9uIHByaW50VXNhZ2UoKSB7XHJcbiAgY29uc3QgcGFkID0gNDg7XHJcbiAgY29uc3QgcmVhZG1lID0gJ2h0dHBzOi8vZ2l0aHViLmNvbS9oaWdoY2hhcnRzL25vZGUtZXhwb3J0LXNlcnZlciNyZWFkbWUnO1xyXG5cclxuICAvLyBEaXNwbGF5IHJlYWRtZSBpbmZvcm1hdGlvblxyXG4gIGNvbnNvbGUubG9nKFxyXG4gICAgJ1xcblVzYWdlIG9mIENMSSBhcmd1bWVudHM6Jy5ib2xkLFxyXG4gICAgJ1xcbi0tLS0tLScsXHJcbiAgICBgXFxuRm9yIG1vcmUgZGV0YWlsZWQgaW5mb3JtYXRpb24sIHZpc2l0IHRoZSByZWFkbWUgYXQ6ICR7cmVhZG1lLmJvbGQueWVsbG93fS5gXHJcbiAgKTtcclxuXHJcbiAgY29uc3QgY3ljbGVDYXRlZ29yaWVzID0gKG9wdGlvbnMpID0+IHtcclxuICAgIGZvciAoY29uc3QgW25hbWUsIG9wdGlvbl0gb2YgT2JqZWN0LmVudHJpZXMob3B0aW9ucykpIHtcclxuICAgICAgLy8gSWYgY2F0ZWdvcnkgaGFzIG1vcmUgbGV2ZWxzLCBnbyBmdXJ0aGVyXHJcbiAgICAgIGlmICghT2JqZWN0LnByb3RvdHlwZS5oYXNPd25Qcm9wZXJ0eS5jYWxsKG9wdGlvbiwgJ3ZhbHVlJykpIHtcclxuICAgICAgICBjeWNsZUNhdGVnb3JpZXMob3B0aW9uKTtcclxuICAgICAgfSBlbHNlIHtcclxuICAgICAgICBsZXQgZGVzY05hbWUgPSBgICAtLSR7b3B0aW9uLmNsaU5hbWUgfHwgbmFtZX0gJHtcclxuICAgICAgICAgICgnPCcgKyBvcHRpb24udHlwZSArICc+JykuZ3JlZW5cclxuICAgICAgICB9IGA7XHJcbiAgICAgICAgaWYgKGRlc2NOYW1lLmxlbmd0aCA8IHBhZCkge1xyXG4gICAgICAgICAgZm9yIChsZXQgaSA9IGRlc2NOYW1lLmxlbmd0aDsgaSA8IHBhZDsgaSsrKSB7XHJcbiAgICAgICAgICAgIGRlc2NOYW1lICs9ICcuJztcclxuICAgICAgICAgIH1cclxuICAgICAgICB9XHJcblxyXG4gICAgICAgIC8vIERpc3BsYXkgY29ycmVjdGx5IGFsaWduZWQgbWVzc2FnZXNcclxuICAgICAgICBjb25zb2xlLmxvZyhcclxuICAgICAgICAgIGRlc2NOYW1lLFxyXG4gICAgICAgICAgb3B0aW9uLmRlc2NyaXB0aW9uLFxyXG4gICAgICAgICAgYFtEZWZhdWx0OiAke29wdGlvbi52YWx1ZS50b1N0cmluZygpLmJvbGR9XWAuYmx1ZVxyXG4gICAgICAgICk7XHJcbiAgICAgIH1cclxuICAgIH1cclxuICB9O1xyXG5cclxuICAvLyBDeWNsZSB0aHJvdWdoIG9wdGlvbnMgb2YgZWFjaCBjYXRlZ29yaWVzIGFuZCBkaXNwbGF5IHRoZSB1c2FnZSBpbmZvXHJcbiAgT2JqZWN0LmtleXMoZGVmYXVsdENvbmZpZykuZm9yRWFjaCgoY2F0ZWdvcnkpID0+IHtcclxuICAgIC8vIE9ubHkgcHVwcGV0ZWVyIGFuZCBoaWdoY2hhcnRzIGNhdGVnb3JpZXMgY2Fubm90IGJlIGNvbmZpZ3VyZWQgdGhyb3VnaCBDTElcclxuICAgIGlmICghWydwdXBwZXRlZXInLCAnaGlnaGNoYXJ0cyddLmluY2x1ZGVzKGNhdGVnb3J5KSkge1xyXG4gICAgICBjb25zb2xlLmxvZyhgXFxuJHtjYXRlZ29yeS50b1VwcGVyQ2FzZSgpfWAucmVkKTtcclxuICAgICAgY3ljbGVDYXRlZ29yaWVzKGRlZmF1bHRDb25maWdbY2F0ZWdvcnldKTtcclxuICAgIH1cclxuICB9KTtcclxuICBjb25zb2xlLmxvZygnXFxuJyk7XHJcbn1cclxuXHJcbi8qKlxyXG4gKiBSb3VuZHMgYSBudW1iZXIgdG8gdGhlIHNwZWNpZmllZCBwcmVjaXNpb24uXHJcbiAqXHJcbiAqIEBwYXJhbSB7bnVtYmVyfSB2YWx1ZSAtIFRoZSBudW1iZXIgdG8gYmUgcm91bmRlZC5cclxuICogQHBhcmFtIHtudW1iZXJ9IHByZWNpc2lvbiAtIFRoZSBudW1iZXIgb2YgZGVjaW1hbCBwbGFjZXMgdG8gcm91bmQgdG8uXHJcbiAqXHJcbiAqIEByZXR1cm5zIHtudW1iZXJ9IC0gVGhlIHJvdW5kZWQgbnVtYmVyLlxyXG4gKi9cclxuZXhwb3J0IGNvbnN0IHJvdW5kTnVtYmVyID0gKHZhbHVlLCBwcmVjaXNpb24gPSAxKSA9PiB7XHJcbiAgY29uc3QgbXVsdGlwbGllciA9IE1hdGgucG93KDEwLCBwcmVjaXNpb24gfHwgMCk7XHJcbiAgcmV0dXJuIE1hdGgucm91bmQoK3ZhbHVlICogbXVsdGlwbGllcikgLyBtdWx0aXBsaWVyO1xyXG59O1xyXG5cclxuLyoqXHJcbiAqIENvbnZlcnRzIGEgdmFsdWUgdG8gYSBib29sZWFuLlxyXG4gKlxyXG4gKiBAcGFyYW0ge2FueX0gaXRlbSAtIFRoZSB2YWx1ZSB0byBiZSBjb252ZXJ0ZWQgdG8gYSBib29sZWFuLlxyXG4gKlxyXG4gKiBAcmV0dXJucyB7Ym9vbGVhbn0gLSBUaGUgYm9vbGVhbiByZXByZXNlbnRhdGlvbiBvZiB0aGUgaW5wdXQgdmFsdWUuXHJcbiAqL1xyXG5leHBvcnQgY29uc3QgdG9Cb29sZWFuID0gKGl0ZW0pID0+XHJcbiAgWydmYWxzZScsICd1bmRlZmluZWQnLCAnbnVsbCcsICdOYU4nLCAnMCcsICcnXS5pbmNsdWRlcyhpdGVtKVxyXG4gICAgPyBmYWxzZVxyXG4gICAgOiAhIWl0ZW07XHJcblxyXG4vKipcclxuICogV3JhcHMgY3VzdG9tIGNvZGUgdG8gZXhlY3V0ZSBpdCBzYWZlbHkuXHJcbiAqXHJcbiAqIEBwYXJhbSB7c3RyaW5nfSBjdXN0b21Db2RlIC0gVGhlIGN1c3RvbSBjb2RlIHRvIGJlIHdyYXBwZWQuXHJcbiAqIEBwYXJhbSB7Ym9vbGVhbn0gYWxsb3dGaWxlUmVzb3VyY2VzIC0gRmxhZyB0byBhbGxvdyBsb2FkaW5nIGNvZGUgZnJvbSBhIGZpbGUuXHJcbiAqXHJcbiAqIEByZXR1cm5zIHtzdHJpbmd8Ym9vbGVhbn0gLSBUaGUgd3JhcHBlZCBjdXN0b20gY29kZSBvciBmYWxzZSBpZiB3cmFwcGluZ1xyXG4gKiBmYWlscy5cclxuICovXHJcbmV4cG9ydCBjb25zdCB3cmFwQXJvdW5kID0gKGN1c3RvbUNvZGUsIGFsbG93RmlsZVJlc291cmNlcykgPT4ge1xyXG4gIGlmIChjdXN0b21Db2RlICYmIHR5cGVvZiBjdXN0b21Db2RlID09PSAnc3RyaW5nJykge1xyXG4gICAgY3VzdG9tQ29kZSA9IGN1c3RvbUNvZGUudHJpbSgpO1xyXG5cclxuICAgIGlmIChjdXN0b21Db2RlLmVuZHNXaXRoKCcuanMnKSkge1xyXG4gICAgICByZXR1cm4gYWxsb3dGaWxlUmVzb3VyY2VzXHJcbiAgICAgICAgPyB3cmFwQXJvdW5kKHJlYWRGaWxlU3luYyhjdXN0b21Db2RlLCAndXRmOCcpKVxyXG4gICAgICAgIDogZmFsc2U7XHJcbiAgICB9IGVsc2UgaWYgKFxyXG4gICAgICBjdXN0b21Db2RlLnN0YXJ0c1dpdGgoJ2Z1bmN0aW9uKCknKSB8fFxyXG4gICAgICBjdXN0b21Db2RlLnN0YXJ0c1dpdGgoJ2Z1bmN0aW9uICgpJykgfHxcclxuICAgICAgY3VzdG9tQ29kZS5zdGFydHNXaXRoKCcoKT0+JykgfHxcclxuICAgICAgY3VzdG9tQ29kZS5zdGFydHNXaXRoKCcoKSA9PicpXHJcbiAgICApIHtcclxuICAgICAgcmV0dXJuIGAoJHtjdXN0b21Db2RlfSkoKWA7XHJcbiAgICB9XHJcbiAgICByZXR1cm4gY3VzdG9tQ29kZS5yZXBsYWNlKC87JC8sICcnKTtcclxuICB9XHJcbn07XHJcblxyXG4vKipcclxuICogVXRpbGl0eSB0byBtZWFzdXJlIGVsYXBzZWQgdGltZSB1c2luZyB0aGUgTm9kZS5qcyBwcm9jZXNzLmhydGltZSgpIG1ldGhvZC5cclxuICpcclxuICogQHJldHVybnMge2Z1bmN0aW9uKCk6IG51bWJlcn0gLSBBIGZ1bmN0aW9uIHRvIGNhbGN1bGF0ZSB0aGUgZWxhcHNlZCB0aW1lXHJcbiAqIGluIG1pbGxpc2Vjb25kcy5cclxuICovXHJcbmV4cG9ydCBjb25zdCBtZWFzdXJlVGltZSA9ICgpID0+IHtcclxuICBjb25zdCBzdGFydCA9IHByb2Nlc3MuaHJ0aW1lLmJpZ2ludCgpO1xyXG4gIHJldHVybiAoKSA9PiBOdW1iZXIocHJvY2Vzcy5ocnRpbWUuYmlnaW50KCkgLSBzdGFydCkgLyAxMDAwMDAwO1xyXG59O1xyXG5cclxuZXhwb3J0IGRlZmF1bHQge1xyXG4gIF9faGlnaGNoYXJ0c0RpcixcclxuICBfX2Rpcm5hbWUsXHJcbiAgY2xlYXJUZXh0LFxyXG4gIGV4cEJhY2tvZmYsXHJcbiAgZml4VHlwZSxcclxuICBoYW5kbGVSZXNvdXJjZXMsXHJcbiAgaXNDb3JyZWN0SlNPTixcclxuICBpc09iamVjdCxcclxuICBpc09iamVjdEVtcHR5LFxyXG4gIGlzUHJpdmF0ZVJhbmdlVXJsRm91bmQsXHJcbiAgb3B0aW9uc1N0cmluZ2lmeSxcclxuICBwcmludExvZ28sXHJcbiAgcHJpbnRVc2FnZSxcclxuICByb3VuZE51bWJlcixcclxuICB0b0Jvb2xlYW4sXHJcbiAgd3JhcEFyb3VuZCxcclxuICBtZWFzdXJlVGltZVxyXG59O1xyXG4iLCIvKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKlxyXG5cclxuSGlnaGNoYXJ0cyBFeHBvcnQgU2VydmVyXHJcblxyXG5Db3B5cmlnaHQgKGMpIDIwMTYtMjAyNCwgSGlnaHNvZnRcclxuXHJcbkxpY2VuY2VkIHVuZGVyIHRoZSBNSVQgbGljZW5jZS5cclxuXHJcbkFkZGl0aW9uYWxseSBhIHZhbGlkIEhpZ2hjaGFydHMgbGljZW5zZSBpcyByZXF1aXJlZCBmb3IgdXNlLlxyXG5cclxuU2VlIExJQ0VOU0UgZmlsZSBpbiByb290IGZvciBkZXRhaWxzLlxyXG5cclxuKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKi9cclxuXHJcbmltcG9ydCB7IGV4aXN0c1N5bmMsIHJlYWRGaWxlU3luYywgcHJvbWlzZXMgYXMgZnNQcm9taXNlcyB9IGZyb20gJ2ZzJztcclxuXHJcbmltcG9ydCBwcm9tcHRzIGZyb20gJ3Byb21wdHMnO1xyXG5cclxuaW1wb3J0IHtcclxuICBhYnNvbHV0ZVByb3BzLFxyXG4gIGRlZmF1bHRDb25maWcsXHJcbiAgbmVzdGVkQXJncyxcclxuICBwcm9tcHRzQ29uZmlnXHJcbn0gZnJvbSAnLi9zY2hlbWFzL2NvbmZpZy5qcyc7XHJcbmltcG9ydCB7IGVudnMgfSBmcm9tICcuL2VudnMuanMnO1xyXG5pbXBvcnQgeyBsb2csIGxvZ1dpdGhTdGFjayB9IGZyb20gJy4vbG9nZ2VyLmpzJztcclxuaW1wb3J0IHsgZGVlcENvcHksIGlzT2JqZWN0LCBwcmludFVzYWdlLCB0b0Jvb2xlYW4gfSBmcm9tICcuL3V0aWxzLmpzJztcclxuXHJcbmxldCBnZW5lcmFsT3B0aW9ucyA9IHt9O1xyXG5cclxuLyoqXHJcbiAqIFJldHJpZXZlcyBhbmQgcmV0dXJucyB0aGUgZ2VuZXJhbCBvcHRpb25zIGZvciB0aGUgZXhwb3J0IHByb2Nlc3MuXHJcbiAqXHJcbiAqIEByZXR1cm5zIHtPYmplY3R9IFRoZSBnZW5lcmFsIG9wdGlvbnMgb2JqZWN0LlxyXG4gKi9cclxuZXhwb3J0IGNvbnN0IGdldE9wdGlvbnMgPSAoKSA9PiBnZW5lcmFsT3B0aW9ucztcclxuXHJcbi8qKlxyXG4gKiBJbml0aWFsaXplcyBhbmQgc2V0cyB0aGUgZ2VuZXJhbCBvcHRpb25zIGZvciB0aGUgc2VydmVyIGluc3RhY2UsIGtlZXBpbmdcclxuICogdGhlIHByaW5jaXBsZSBvZiB0aGUgb3B0aW9ucyBsb2FkIHByaW9yaXR5LiBJdCBhY2NlcHRzIG9wdGlvbmFsIHVzZXJPcHRpb25zXHJcbiAqIGFuZCBhcmdzIGZyb20gdGhlIENMSS5cclxuICpcclxuICogQHBhcmFtIHtPYmplY3R9IHVzZXJPcHRpb25zIC0gVXNlci1wcm92aWRlZCBvcHRpb25zIGZvciBjdXN0b21pemF0aW9uLlxyXG4gKiBAcGFyYW0ge0FycmF5fSBhcmdzIC0gQ29tbWFuZC1saW5lIGFyZ3VtZW50cyBmb3IgYWRkaXRpb25hbCBjb25maWd1cmF0aW9uXHJcbiAqIChDTEkgdXNhZ2UpLlxyXG4gKlxyXG4gKiBAcmV0dXJucyB7T2JqZWN0fSBUaGUgdXBkYXRlZCBnZW5lcmFsIG9wdGlvbnMgb2JqZWN0LlxyXG4gKi9cclxuZXhwb3J0IGNvbnN0IHNldE9wdGlvbnMgPSAodXNlck9wdGlvbnMsIGFyZ3MpID0+IHtcclxuICAvLyBPbmx5IGZvciB0aGUgQ0xJIHVzYWdlXHJcbiAgaWYgKGFyZ3M/Lmxlbmd0aCkge1xyXG4gICAgLy8gR2V0IHRoZSBhZGRpdGlvbmFsIG9wdGlvbnMgZnJvbSB0aGUgY3VzdG9tIEpTT04gZmlsZVxyXG4gICAgZ2VuZXJhbE9wdGlvbnMgPSBsb2FkQ29uZmlnRmlsZShhcmdzKTtcclxuICB9XHJcblxyXG4gIC8vIFVwZGF0ZSB0aGUgZGVmYXVsdCBjb25maWcgd2l0aCBhIGNvcnJlY3Qgb3B0aW9uIHZhbHVlc1xyXG4gIHVwZGF0ZURlZmF1bHRDb25maWcoZGVmYXVsdENvbmZpZywgZ2VuZXJhbE9wdGlvbnMpO1xyXG5cclxuICAvLyBTZXQgdmFsdWVzIGZvciBzZXJ2ZXIncyBvcHRpb25zIGFuZCByZXR1cm5zIHRoZW1cclxuICBnZW5lcmFsT3B0aW9ucyA9IGluaXRPcHRpb25zKGRlZmF1bHRDb25maWcpO1xyXG5cclxuICAvLyBBcHBseSB1c2VyIG9wdGlvbnMgaWYgdGhlcmUgYXJlIGFueVxyXG4gIGlmICh1c2VyT3B0aW9ucykge1xyXG4gICAgLy8gTWVyZ2UgdXNlciBvcHRpb25zXHJcbiAgICBnZW5lcmFsT3B0aW9ucyA9IG1lcmdlQ29uZmlnT3B0aW9ucyhcclxuICAgICAgZ2VuZXJhbE9wdGlvbnMsXHJcbiAgICAgIHVzZXJPcHRpb25zLFxyXG4gICAgICBhYnNvbHV0ZVByb3BzXHJcbiAgICApO1xyXG4gIH1cclxuXHJcbiAgLy8gT25seSBmb3IgdGhlIENMSSB1c2FnZVxyXG4gIGlmIChhcmdzPy5sZW5ndGgpIHtcclxuICAgIC8vIFBhaXIgcHJvdmlkZWQgYXJndW1lbnRzXHJcbiAgICBnZW5lcmFsT3B0aW9ucyA9IHBhaXJBcmd1bWVudFZhbHVlKGdlbmVyYWxPcHRpb25zLCBhcmdzLCBkZWZhdWx0Q29uZmlnKTtcclxuICB9XHJcblxyXG4gIC8vIFJldHVybiBmaW5hbCBnZW5lcmFsIG9wdGlvbnNcclxuICByZXR1cm4gZ2VuZXJhbE9wdGlvbnM7XHJcbn07XHJcblxyXG4vKipcclxuICogQWxsb3dzIG1hbnVhbCBjb25maWd1cmF0aW9uIGJhc2VkIG9uIHNwZWNpZmllZCBwcm9tcHRzIGFuZCBzYXZlc1xyXG4gKiB0aGUgY29uZmlndXJhdGlvbiB0byBhIGZpbGUuXHJcbiAqXHJcbiAqIEBwYXJhbSB7c3RyaW5nfSBjb25maWdGaWxlTmFtZSAtIFRoZSBuYW1lIG9mIHRoZSBjb25maWd1cmF0aW9uIGZpbGUuXHJcbiAqXHJcbiAqIEByZXR1cm5zIHtQcm9taXNlPGJvb2xlYW4+fSBBIFByb21pc2UgdGhhdCByZXNvbHZlcyB0byB0cnVlIG9uY2UgdGhlIG1hbnVhbFxyXG4gKiBjb25maWd1cmF0aW9uIGlzIGNvbXBsZXRlZCBhbmQgc2F2ZWQuXHJcbiAqL1xyXG5leHBvcnQgY29uc3QgbWFudWFsQ29uZmlnID0gYXN5bmMgKGNvbmZpZ0ZpbGVOYW1lKSA9PiB7XHJcbiAgLy8gUHJlcGFyZSBhIGNvbmZpZyBvYmplY3RcclxuICBsZXQgY29uZmlnRmlsZSA9IHt9O1xyXG5cclxuICAvLyBDaGVjayBpZiBwcm92aWRlZCBjb25maWcgZmlsZSBleGlzdHNcclxuICBpZiAoZXhpc3RzU3luYyhjb25maWdGaWxlTmFtZSkpIHtcclxuICAgIGNvbmZpZ0ZpbGUgPSBKU09OLnBhcnNlKHJlYWRGaWxlU3luYyhjb25maWdGaWxlTmFtZSwgJ3V0ZjgnKSk7XHJcbiAgfVxyXG5cclxuICAvLyBRdWVzdGlvbiBhYm91dCBhIGNvbmZpZ3VyYXRpb24gY2F0ZWdvcnlcclxuICBjb25zdCBvblN1Ym1pdCA9IGFzeW5jIChwLCBjYXRlZ29yaWVzKSA9PiB7XHJcbiAgICBsZXQgcXVlc3Rpb25zQ291bnRlciA9IDA7XHJcbiAgICBsZXQgYWxsUXVlc3Rpb25zID0gW107XHJcblxyXG4gICAgLy8gQ3JlYXRlIGEgY29ycmVzcG9uZGluZyBwcm9wZXJ0eSBpbiB0aGUgbWFudWFsQ29uZmlnIG9iamVjdFxyXG4gICAgZm9yIChjb25zdCBzZWN0aW9uIG9mIGNhdGVnb3JpZXMpIHtcclxuICAgICAgLy8gTWFyayBlYWNoIG9wdGlvbiB3aXRoIGEgc2VjdGlvblxyXG4gICAgICBwcm9tcHRzQ29uZmlnW3NlY3Rpb25dID0gcHJvbXB0c0NvbmZpZ1tzZWN0aW9uXS5tYXAoKG9wdGlvbikgPT4gKHtcclxuICAgICAgICAuLi5vcHRpb24sXHJcbiAgICAgICAgc2VjdGlvblxyXG4gICAgICB9KSk7XHJcblxyXG4gICAgICAvLyBDb2xsZWN0IHRoZSBxdWVzdGlvbnNcclxuICAgICAgYWxsUXVlc3Rpb25zID0gWy4uLmFsbFF1ZXN0aW9ucywgLi4ucHJvbXB0c0NvbmZpZ1tzZWN0aW9uXV07XHJcbiAgICB9XHJcblxyXG4gICAgYXdhaXQgcHJvbXB0cyhhbGxRdWVzdGlvbnMsIHtcclxuICAgICAgb25TdWJtaXQ6IGFzeW5jIChwcm9tcHQsIGFuc3dlcikgPT4ge1xyXG4gICAgICAgIC8vIEdldCB0aGUgZGVmYXVsdCBtb2R1bGUgc2NyaXB0c1xyXG4gICAgICAgIGlmIChwcm9tcHQubmFtZSA9PT0gJ21vZHVsZVNjcmlwdHMnKSB7XHJcbiAgICAgICAgICBhbnN3ZXIgPSBhbnN3ZXIubGVuZ3RoXHJcbiAgICAgICAgICAgID8gYW5zd2VyLm1hcCgobW9kdWxlKSA9PiBwcm9tcHQuY2hvaWNlc1ttb2R1bGVdKVxyXG4gICAgICAgICAgICA6IHByb21wdC5jaG9pY2VzO1xyXG5cclxuICAgICAgICAgIGNvbmZpZ0ZpbGVbcHJvbXB0LnNlY3Rpb25dW3Byb21wdC5uYW1lXSA9IGFuc3dlcjtcclxuICAgICAgICB9IGVsc2Uge1xyXG4gICAgICAgICAgY29uZmlnRmlsZVtwcm9tcHQuc2VjdGlvbl0gPSByZWN1cnNpdmVQcm9wcyhcclxuICAgICAgICAgICAgT2JqZWN0LmFzc2lnbih7fSwgY29uZmlnRmlsZVtwcm9tcHQuc2VjdGlvbl0gfHwge30pLFxyXG4gICAgICAgICAgICBwcm9tcHQubmFtZS5zcGxpdCgnLicpLFxyXG4gICAgICAgICAgICBwcm9tcHQuY2hvaWNlcyA/IHByb21wdC5jaG9pY2VzW2Fuc3dlcl0gOiBhbnN3ZXJcclxuICAgICAgICAgICk7XHJcbiAgICAgICAgfVxyXG5cclxuICAgICAgICBpZiAoKytxdWVzdGlvbnNDb3VudGVyID09PSBhbGxRdWVzdGlvbnMubGVuZ3RoKSB7XHJcbiAgICAgICAgICB0cnkge1xyXG4gICAgICAgICAgICBhd2FpdCBmc1Byb21pc2VzLndyaXRlRmlsZShcclxuICAgICAgICAgICAgICBjb25maWdGaWxlTmFtZSxcclxuICAgICAgICAgICAgICBKU09OLnN0cmluZ2lmeShjb25maWdGaWxlLCBudWxsLCAyKSxcclxuICAgICAgICAgICAgICAndXRmOCdcclxuICAgICAgICAgICAgKTtcclxuICAgICAgICAgIH0gY2F0Y2ggKGVycm9yKSB7XHJcbiAgICAgICAgICAgIGxvZ1dpdGhTdGFjayhcclxuICAgICAgICAgICAgICAxLFxyXG4gICAgICAgICAgICAgIGVycm9yLFxyXG4gICAgICAgICAgICAgIGBbY29uZmlnXSBBbiBlcnJvciBvY2N1cnJlZCB3aGlsZSBjcmVhdGluZyB0aGUgJHtjb25maWdGaWxlTmFtZX0gZmlsZS5gXHJcbiAgICAgICAgICAgICk7XHJcbiAgICAgICAgICB9XHJcbiAgICAgICAgICByZXR1cm4gdHJ1ZTtcclxuICAgICAgICB9XHJcbiAgICAgIH1cclxuICAgIH0pO1xyXG5cclxuICAgIHJldHVybiB0cnVlO1xyXG4gIH07XHJcblxyXG4gIC8vIEZpbmQgdGhlIGNhdGVnb3JpZXNcclxuICBjb25zdCBjaG9pY2VzID0gT2JqZWN0LmtleXMocHJvbXB0c0NvbmZpZykubWFwKChjaG9pY2UpID0+ICh7XHJcbiAgICB0aXRsZTogYCR7Y2hvaWNlfSBvcHRpb25zYCxcclxuICAgIHZhbHVlOiBjaG9pY2VcclxuICB9KSk7XHJcblxyXG4gIC8vIENhdGVnb3J5IHByb21wdFxyXG4gIHJldHVybiBwcm9tcHRzKFxyXG4gICAge1xyXG4gICAgICB0eXBlOiAnbXVsdGlzZWxlY3QnLFxyXG4gICAgICBuYW1lOiAnY2F0ZWdvcnknLFxyXG4gICAgICBtZXNzYWdlOiAnV2hpY2ggY2F0ZWdvcnkgZG8geW91IHdhbnQgdG8gY29uZmlndXJlPycsXHJcbiAgICAgIGhpbnQ6ICdTcGFjZTogU2VsZWN0IHNwZWNpZmljLCBBOiBTZWxlY3QgYWxsLCBFbnRlcjogQ29uZmlybS4nLFxyXG4gICAgICBpbnN0cnVjdGlvbnM6ICcnLFxyXG4gICAgICBjaG9pY2VzXHJcbiAgICB9LFxyXG4gICAgeyBvblN1Ym1pdCB9XHJcbiAgKTtcclxufTtcclxuXHJcbi8qKlxyXG4gKiBNYXBzIG9sZC1zdHJ1Y3R1cmVkIChQaGFudG9tSlMpIG9wdGlvbnMgdG8gYSBuZXcgY29uZmlndXJhdGlvbiBmb3JtYXRcclxuICogKFB1cHBldGVlcikuXHJcbiAqXHJcbiAqIEBwYXJhbSB7T2JqZWN0fSBvbGRPcHRpb25zIC0gT2xkLXN0cnVjdHVyZWQgb3B0aW9ucyB0byBiZSBtYXBwZWQuXHJcbiAqXHJcbiAqIEByZXR1cm5zIHtPYmplY3R9IE5ldyBvcHRpb25zIHN0cnVjdHVyZWQgYmFzZWQgb24gdGhlIGRlZmluZWQgbmVzdGVkQXJnc1xyXG4gKiBtYXBwaW5nLlxyXG4gKi9cclxuZXhwb3J0IGNvbnN0IG1hcFRvTmV3Q29uZmlnID0gKG9sZE9wdGlvbnMpID0+IHtcclxuICBjb25zdCBuZXdPcHRpb25zID0ge307XHJcbiAgLy8gQ3ljbGUgdGhyb3VnaCBvbGQtc3RydWN0dXJlZCBvcHRpb25zXHJcbiAgZm9yIChjb25zdCBba2V5LCB2YWx1ZV0gb2YgT2JqZWN0LmVudHJpZXMob2xkT3B0aW9ucykpIHtcclxuICAgIGNvbnN0IHByb3BlcnRpZXNDaGFpbiA9IG5lc3RlZEFyZ3Nba2V5XSA/IG5lc3RlZEFyZ3Nba2V5XS5zcGxpdCgnLicpIDogW107XHJcblxyXG4gICAgLy8gUG9wdWxhdGUgb2JqZWN0IGluIGNvcnJlY3QgcHJvcGVydGllcyBsZXZlbHNcclxuICAgIHByb3BlcnRpZXNDaGFpbi5yZWR1Y2UoXHJcbiAgICAgIChvYmosIHByb3AsIGluZGV4KSA9PlxyXG4gICAgICAgIChvYmpbcHJvcF0gPVxyXG4gICAgICAgICAgcHJvcGVydGllc0NoYWluLmxlbmd0aCAtIDEgPT09IGluZGV4ID8gdmFsdWUgOiBvYmpbcHJvcF0gfHwge30pLFxyXG4gICAgICBuZXdPcHRpb25zXHJcbiAgICApO1xyXG4gIH1cclxuICByZXR1cm4gbmV3T3B0aW9ucztcclxufTtcclxuXHJcbi8qKlxyXG4gKiBNZXJnZXMgdHdvIHNldHMgb2YgY29uZmlndXJhdGlvbiBvcHRpb25zLCBjb25zaWRlcmluZyBhYnNvbHV0ZSBwcm9wZXJ0aWVzLlxyXG4gKlxyXG4gKiBAcGFyYW0ge09iamVjdH0gb3B0aW9ucyAtIE9yaWdpbmFsIGNvbmZpZ3VyYXRpb24gb3B0aW9ucy5cclxuICogQHBhcmFtIHtPYmplY3R9IG5ld09wdGlvbnMgLSBOZXcgY29uZmlndXJhdGlvbiBvcHRpb25zIHRvIGJlIG1lcmdlZC5cclxuICogQHBhcmFtIHtBcnJheX0gYWJzb2x1dGVQcm9wcyAtIExpc3Qgb2YgcHJvcGVydGllcyB0aGF0IHNob3VsZFxyXG4gKiBub3QgYmUgcmVjdXJzaXZlbHkgbWVyZ2VkLlxyXG4gKlxyXG4gKiBAcmV0dXJucyB7T2JqZWN0fSBNZXJnZWQgY29uZmlndXJhdGlvbiBvcHRpb25zLlxyXG4gKi9cclxuZXhwb3J0IGNvbnN0IG1lcmdlQ29uZmlnT3B0aW9ucyA9IChvcHRpb25zLCBuZXdPcHRpb25zLCBhYnNvbHV0ZVByb3BzID0gW10pID0+IHtcclxuICBjb25zdCBtZXJnZWRPcHRpb25zID0gZGVlcENvcHkob3B0aW9ucyk7XHJcblxyXG4gIGZvciAoY29uc3QgW2tleSwgdmFsdWVdIG9mIE9iamVjdC5lbnRyaWVzKG5ld09wdGlvbnMpKSB7XHJcbiAgICBtZXJnZWRPcHRpb25zW2tleV0gPVxyXG4gICAgICBpc09iamVjdCh2YWx1ZSkgJiZcclxuICAgICAgIWFic29sdXRlUHJvcHMuaW5jbHVkZXMoa2V5KSAmJlxyXG4gICAgICBtZXJnZWRPcHRpb25zW2tleV0gIT09IHVuZGVmaW5lZFxyXG4gICAgICAgID8gbWVyZ2VDb25maWdPcHRpb25zKG1lcmdlZE9wdGlvbnNba2V5XSwgdmFsdWUsIGFic29sdXRlUHJvcHMpXHJcbiAgICAgICAgOiB2YWx1ZSAhPT0gdW5kZWZpbmVkXHJcbiAgICAgICAgICA/IHZhbHVlXHJcbiAgICAgICAgICA6IG1lcmdlZE9wdGlvbnNba2V5XTtcclxuICB9XHJcblxyXG4gIHJldHVybiBtZXJnZWRPcHRpb25zO1xyXG59O1xyXG5cclxuLyoqXHJcbiAqIEluaXRpYWxpemVzIGV4cG9ydCBzZXR0aW5ncyBiYXNlZCBvbiBwcm92aWRlZCBleHBvcnRPcHRpb25zXHJcbiAqIGFuZCBnZW5lcmFsT3B0aW9ucy5cclxuICpcclxuICogQHBhcmFtIHtPYmplY3R9IGV4cG9ydE9wdGlvbnMgLSBPcHRpb25zIHNwZWNpZmljIHRvIHRoZSBleHBvcnQgcHJvY2Vzcy5cclxuICogQHBhcmFtIHtPYmplY3R9IGdlbmVyYWxPcHRpb25zIC0gR2VuZXJhbCBjb25maWd1cmF0aW9uIG9wdGlvbnMuXHJcbiAqXHJcbiAqIEByZXR1cm5zIHtPYmplY3R9IEluaXRpYWxpemVkIGV4cG9ydCBzZXR0aW5ncy5cclxuICovXHJcbmV4cG9ydCBjb25zdCBpbml0RXhwb3J0U2V0dGluZ3MgPSAoZXhwb3J0T3B0aW9ucywgZ2VuZXJhbE9wdGlvbnMgPSB7fSkgPT4ge1xyXG4gIGxldCBvcHRpb25zID0ge307XHJcblxyXG4gIGlmIChleHBvcnRPcHRpb25zLnN2Zykge1xyXG4gICAgb3B0aW9ucyA9IGRlZXBDb3B5KGdlbmVyYWxPcHRpb25zKTtcclxuICAgIG9wdGlvbnMuZXhwb3J0LnR5cGUgPSBleHBvcnRPcHRpb25zLnR5cGUgfHwgZXhwb3J0T3B0aW9ucy5leHBvcnQudHlwZTtcclxuICAgIG9wdGlvbnMuZXhwb3J0LnNjYWxlID0gZXhwb3J0T3B0aW9ucy5zY2FsZSB8fCBleHBvcnRPcHRpb25zLmV4cG9ydC5zY2FsZTtcclxuICAgIG9wdGlvbnMuZXhwb3J0Lm91dGZpbGUgPVxyXG4gICAgICBleHBvcnRPcHRpb25zLm91dGZpbGUgfHwgZXhwb3J0T3B0aW9ucy5leHBvcnQub3V0ZmlsZTtcclxuICAgIG9wdGlvbnMucGF5bG9hZCA9IHtcclxuICAgICAgc3ZnOiBleHBvcnRPcHRpb25zLnN2Z1xyXG4gICAgfTtcclxuICB9IGVsc2Uge1xyXG4gICAgb3B0aW9ucyA9IG1lcmdlQ29uZmlnT3B0aW9ucyhcclxuICAgICAgZ2VuZXJhbE9wdGlvbnMsXHJcbiAgICAgIGV4cG9ydE9wdGlvbnMsXHJcbiAgICAgIC8vIE9taXQgZ29pbmcgZG93biByZWN1cnNpdmVseSB3aXRoIHRoZSBiZWxvd3NcclxuICAgICAgYWJzb2x1dGVQcm9wc1xyXG4gICAgKTtcclxuICB9XHJcblxyXG4gIG9wdGlvbnMuZXhwb3J0Lm91dGZpbGUgPVxyXG4gICAgb3B0aW9ucy5leHBvcnQ/Lm91dGZpbGUgfHwgYGNoYXJ0LiR7b3B0aW9ucy5leHBvcnQ/LnR5cGUgfHwgJ3BuZyd9YDtcclxuICByZXR1cm4gb3B0aW9ucztcclxufTtcclxuXHJcbi8qKlxyXG4gKiBMb2FkcyBhZGRpdGlvbmFsIGNvbmZpZ3VyYXRpb24gZnJvbSBhIHNwZWNpZmllZCBmaWxlIHVzaW5nXHJcbiAqIHRoZSAtLWxvYWRDb25maWcgb3B0aW9uLlxyXG4gKlxyXG4gKiBAcGFyYW0ge0FycmF5fSBhcmdzIC0gQ29tbWFuZC1saW5lIGFyZ3VtZW50cyB0byBjaGVjayBmb3JcclxuICogdGhlIC0tbG9hZENvbmZpZyBvcHRpb24uXHJcbiAqXHJcbiAqIEByZXR1cm5zIHtPYmplY3R9IEFkZGl0aW9uYWwgY29uZmlndXJhdGlvbiBsb2FkZWQgZnJvbSB0aGUgc3BlY2lmaWVkIGZpbGUsXHJcbiAqIG9yIGFuIGVtcHR5IG9iamVjdCBpZiBub3QgZm91bmQgb3IgaW52YWxpZC5cclxuICovXHJcbmZ1bmN0aW9uIGxvYWRDb25maWdGaWxlKGFyZ3MpIHtcclxuICAvLyBDaGVjayBpZiB0aGUgLS1sb2FkQ29uZmlnIG9wdGlvbiB3YXMgdXNlZFxyXG4gIGNvbnN0IGNvbmZpZ0luZGV4ID0gYXJncy5maW5kSW5kZXgoXHJcbiAgICAoYXJnKSA9PiBhcmcucmVwbGFjZSgvLS9nLCAnJykgPT09ICdsb2FkQ29uZmlnJ1xyXG4gICk7XHJcblxyXG4gIC8vIENoZWNrIGlmIHRoZSAtLWxvYWRDb25maWcgaGFzIGEgdmFsdWVcclxuICBpZiAoY29uZmlnSW5kZXggPiAtMSAmJiBhcmdzW2NvbmZpZ0luZGV4ICsgMV0pIHtcclxuICAgIGNvbnN0IGZpbGVOYW1lID0gYXJnc1tjb25maWdJbmRleCArIDFdO1xyXG4gICAgdHJ5IHtcclxuICAgICAgLy8gQ2hlY2sgaWYgYW4gYWRkaXRpb25hbCBjb25maWcgZmlsZSBpcyBhIGNvcnJlY3QgSlNPTiBmaWxlXHJcbiAgICAgIGlmIChmaWxlTmFtZSAmJiBmaWxlTmFtZS5lbmRzV2l0aCgnLmpzb24nKSkge1xyXG4gICAgICAgIC8vIExvYWQgYW4gb3B0aW9uYWwgY3VzdG9tIEpTT04gY29uZmlnIGZpbGVcclxuICAgICAgICByZXR1cm4gSlNPTi5wYXJzZShyZWFkRmlsZVN5bmMoZmlsZU5hbWUpKTtcclxuICAgICAgfVxyXG4gICAgfSBjYXRjaCAoZXJyb3IpIHtcclxuICAgICAgbG9nV2l0aFN0YWNrKFxyXG4gICAgICAgIDIsXHJcbiAgICAgICAgZXJyb3IsXHJcbiAgICAgICAgYFtjb25maWddIFVuYWJsZSB0byBsb2FkIHRoZSBjb25maWd1cmF0aW9uIGZyb20gdGhlICR7ZmlsZU5hbWV9IGZpbGUuYFxyXG4gICAgICApO1xyXG4gICAgfVxyXG4gIH1cclxuXHJcbiAgLy8gTm8gYWRkaXRpb25hbCBvcHRpb25zIHRvIHJldHVyblxyXG4gIHJldHVybiB7fTtcclxufVxyXG5cclxuLyoqXHJcbiAqIFVwZGF0ZXMgdGhlIGRlZmF1bHQgY29uZmlndXJhdGlvbiBvYmplY3Qgd2l0aCB2YWx1ZXMgZnJvbSBhIGN1c3RvbSBvYmplY3RcclxuICogYW5kIGVudmlyb25tZW50IHZhcmlhYmxlcy5cclxuICpcclxuICogQHBhcmFtIHtPYmplY3R9IGNvbmZpZ09iaiAtIFRoZSBkZWZhdWx0IGNvbmZpZ3VyYXRpb24gb2JqZWN0LlxyXG4gKiBAcGFyYW0ge09iamVjdH0gY3VzdG9tT2JqIC0gQ3VzdG9tIGNvbmZpZ3VyYXRpb24gb2JqZWN0IHRvIG92ZXJyaWRlIGRlZmF1bHRzLlxyXG4gKiBAcGFyYW0ge3N0cmluZ30gcHJvcENoYWluIC0gUHJvcGVydHkgY2hhaW4gZm9yIHRyYWNraW5nIG5lc3RlZCBwcm9wZXJ0aWVzXHJcbiAqIGR1cmluZyByZWN1cnNpb24uXHJcbiAqL1xyXG5mdW5jdGlvbiB1cGRhdGVEZWZhdWx0Q29uZmlnKGNvbmZpZ09iaiwgY3VzdG9tT2JqID0ge30sIHByb3BDaGFpbiA9ICcnKSB7XHJcbiAgT2JqZWN0LmtleXMoY29uZmlnT2JqKS5mb3JFYWNoKChrZXkpID0+IHtcclxuICAgIGNvbnN0IGVudHJ5ID0gY29uZmlnT2JqW2tleV07XHJcbiAgICBjb25zdCBjdXN0b21WYWx1ZSA9IGN1c3RvbU9iaiAmJiBjdXN0b21PYmpba2V5XTtcclxuXHJcbiAgICBpZiAodHlwZW9mIGVudHJ5LnZhbHVlID09PSAndW5kZWZpbmVkJykge1xyXG4gICAgICB1cGRhdGVEZWZhdWx0Q29uZmlnKGVudHJ5LCBjdXN0b21WYWx1ZSwgYCR7cHJvcENoYWlufS4ke2tleX1gKTtcclxuICAgIH0gZWxzZSB7XHJcbiAgICAgIC8vIElmIGEgdmFsdWUgZnJvbSBhIGN1c3RvbSBKU09OIGV4aXN0cywgaXQgdGFrZSBwcmVjZWRlbmNlXHJcbiAgICAgIGlmIChjdXN0b21WYWx1ZSAhPT0gdW5kZWZpbmVkKSB7XHJcbiAgICAgICAgZW50cnkudmFsdWUgPSBjdXN0b21WYWx1ZTtcclxuICAgICAgfVxyXG5cclxuICAgICAgLy8gSWYgYSB2YWx1ZSBmcm9tIGFuIGVudiB2YXJpYWJsZSBleGlzdHMsIGl0IHRha2UgcHJlY2VkZW5jZVxyXG4gICAgICBpZiAoZW50cnkuZW52TGluayBpbiBlbnZzICYmIGVudnNbZW50cnkuZW52TGlua10gIT09IHVuZGVmaW5lZCkge1xyXG4gICAgICAgIGVudHJ5LnZhbHVlID0gZW52c1tlbnRyeS5lbnZMaW5rXTtcclxuICAgICAgfVxyXG4gICAgfVxyXG4gIH0pO1xyXG59XHJcblxyXG4vKipcclxuICogSW5pdGlhbGl6ZXMgb3B0aW9ucyBvYmplY3QgYmFzZWQgb24gcHJvdmlkZWQgaXRlbXMsIHNldHRpbmcgdmFsdWVzIGZyb21cclxuICogbmVzdGVkIHByb3BlcnRpZXMgcmVjdXJzaXZlbHkuXHJcbiAqXHJcbiAqIEBwYXJhbSB7T2JqZWN0fSBpdGVtcyAtIENvbmZpZ3VyYXRpb24gaXRlbXMgdG8gYmUgdXNlZCBmb3IgaW5pdGlhbGl6aW5nXHJcbiAqIG9wdGlvbnMuXHJcbiAqXHJcbiAqIEByZXR1cm5zIHtPYmplY3R9IEluaXRpYWxpemVkIG9wdGlvbnMgb2JqZWN0LlxyXG4gKi9cclxuZnVuY3Rpb24gaW5pdE9wdGlvbnMoaXRlbXMpIHtcclxuICBsZXQgb3B0aW9ucyA9IHt9O1xyXG4gIGZvciAoY29uc3QgW25hbWUsIGl0ZW1dIG9mIE9iamVjdC5lbnRyaWVzKGl0ZW1zKSkge1xyXG4gICAgb3B0aW9uc1tuYW1lXSA9IE9iamVjdC5wcm90b3R5cGUuaGFzT3duUHJvcGVydHkuY2FsbChpdGVtLCAndmFsdWUnKVxyXG4gICAgICA/IGl0ZW0udmFsdWVcclxuICAgICAgOiBpbml0T3B0aW9ucyhpdGVtKTtcclxuICB9XHJcbiAgcmV0dXJuIG9wdGlvbnM7XHJcbn1cclxuXHJcbi8qKlxyXG4gKiBQYWlycyBhcmd1bWVudCB2YWx1ZXMgd2l0aCBjb3JyZXNwb25kaW5nIG9wdGlvbnMgaW4gdGhlIGNvbmZpZ3VyYXRpb24sXHJcbiAqIHVwZGF0aW5nIHRoZSBvcHRpb25zIG9iamVjdC5cclxuICpcclxuICogQHBhcmFtIHtPYmplY3R9IG9wdGlvbnMgLSBDb25maWd1cmF0aW9uIG9wdGlvbnMgb2JqZWN0IHRvIGJlIHVwZGF0ZWQuXHJcbiAqIEBwYXJhbSB7QXJyYXl9IGFyZ3MgLSBDb21tYW5kLWxpbmUgYXJndW1lbnRzIGNvbnRhaW5pbmcgdmFsdWVzIGZvciBzcGVjaWZpY1xyXG4gKiBvcHRpb25zLlxyXG4gKiBAcGFyYW0ge09iamVjdH0gZGVmYXVsdENvbmZpZyAtIERlZmF1bHQgY29uZmlndXJhdGlvbiBvYmplY3QgZm9yIHJlZmVyZW5jZS5cclxuICpcclxuICogQHJldHVybnMge09iamVjdH0gVXBkYXRlZCBvcHRpb25zIG9iamVjdC5cclxuICovXHJcbmZ1bmN0aW9uIHBhaXJBcmd1bWVudFZhbHVlKG9wdGlvbnMsIGFyZ3MsIGRlZmF1bHRDb25maWcpIHtcclxuICBsZXQgc2hvd1VzYWdlID0gZmFsc2U7XHJcbiAgZm9yIChsZXQgaSA9IDA7IGkgPCBhcmdzLmxlbmd0aDsgaSsrKSB7XHJcbiAgICBjb25zdCBvcHRpb24gPSBhcmdzW2ldLnJlcGxhY2UoLy0vZywgJycpO1xyXG5cclxuICAgIC8vIEZpbmQgdGhlIHJpZ2h0IHBsYWNlIGZvciBwcm9wZXJ0eSdzIHZhbHVlXHJcbiAgICBjb25zdCBwcm9wZXJ0aWVzQ2hhaW4gPSBuZXN0ZWRBcmdzW29wdGlvbl1cclxuICAgICAgPyBuZXN0ZWRBcmdzW29wdGlvbl0uc3BsaXQoJy4nKVxyXG4gICAgICA6IFtdO1xyXG5cclxuICAgIC8vIEdldCB0aGUgY29ycmVjdCB0eXBlIGZvciBDTEkgYXJncyB3aGljaCBhcmUgcGFzc2VkIGFzIHN0cmluZ3NcclxuICAgIGxldCBhcmd1bWVudFR5cGU7XHJcbiAgICBwcm9wZXJ0aWVzQ2hhaW4ucmVkdWNlKChvYmosIHByb3AsIGluZGV4KSA9PiB7XHJcbiAgICAgIGlmIChwcm9wZXJ0aWVzQ2hhaW4ubGVuZ3RoIC0gMSA9PT0gaW5kZXgpIHtcclxuICAgICAgICBhcmd1bWVudFR5cGUgPSBvYmpbcHJvcF0udHlwZTtcclxuICAgICAgfVxyXG4gICAgICByZXR1cm4gb2JqW3Byb3BdO1xyXG4gICAgfSwgZGVmYXVsdENvbmZpZyk7XHJcblxyXG4gICAgcHJvcGVydGllc0NoYWluLnJlZHVjZSgob2JqLCBwcm9wLCBpbmRleCkgPT4ge1xyXG4gICAgICBpZiAocHJvcGVydGllc0NoYWluLmxlbmd0aCAtIDEgPT09IGluZGV4KSB7XHJcbiAgICAgICAgLy8gRmluZHMgYW4gb3B0aW9uIGFuZCBzZXQgYSBjb3JyZXNwb25kaW5nIHZhbHVlXHJcbiAgICAgICAgaWYgKHR5cGVvZiBvYmpbcHJvcF0gIT09ICd1bmRlZmluZWQnKSB7XHJcbiAgICAgICAgICBpZiAoYXJnc1srK2ldKSB7XHJcbiAgICAgICAgICAgIGlmIChhcmd1bWVudFR5cGUgPT09ICdib29sZWFuJykge1xyXG4gICAgICAgICAgICAgIG9ialtwcm9wXSA9IHRvQm9vbGVhbihhcmdzW2ldKTtcclxuICAgICAgICAgICAgfSBlbHNlIGlmIChhcmd1bWVudFR5cGUgPT09ICdudW1iZXInKSB7XHJcbiAgICAgICAgICAgICAgb2JqW3Byb3BdID0gK2FyZ3NbaV07XHJcbiAgICAgICAgICAgIH0gZWxzZSBpZiAoYXJndW1lbnRUeXBlLmluZGV4T2YoJ10nKSA+PSAwKSB7XHJcbiAgICAgICAgICAgICAgb2JqW3Byb3BdID0gYXJnc1tpXS5zcGxpdCgnLCcpO1xyXG4gICAgICAgICAgICB9IGVsc2Uge1xyXG4gICAgICAgICAgICAgIG9ialtwcm9wXSA9IGFyZ3NbaV07XHJcbiAgICAgICAgICAgIH1cclxuICAgICAgICAgIH0gZWxzZSB7XHJcbiAgICAgICAgICAgIGxvZyhcclxuICAgICAgICAgICAgICAyLFxyXG4gICAgICAgICAgICAgIGBbY29uZmlnXSBNaXNzaW5nIHZhbHVlIGZvciB0aGUgJyR7b3B0aW9ufScgYXJndW1lbnQuIFVzaW5nIHRoZSBkZWZhdWx0IHZhbHVlLmBcclxuICAgICAgICAgICAgKTtcclxuICAgICAgICAgICAgc2hvd1VzYWdlID0gdHJ1ZTtcclxuICAgICAgICAgIH1cclxuICAgICAgICB9XHJcbiAgICAgIH1cclxuICAgICAgcmV0dXJuIG9ialtwcm9wXTtcclxuICAgIH0sIG9wdGlvbnMpO1xyXG4gIH1cclxuXHJcbiAgLy8gRGlzcGxheSB0aGUgdXNhZ2UgZm9yIHRoZSByZWZlcmVuY2UgaWYgbmVlZGVkXHJcbiAgaWYgKHNob3dVc2FnZSkge1xyXG4gICAgcHJpbnRVc2FnZShkZWZhdWx0Q29uZmlnKTtcclxuICB9XHJcblxyXG4gIHJldHVybiBvcHRpb25zO1xyXG59XHJcblxyXG4vKipcclxuICogUmVjdXJzaXZlbHkgdXBkYXRlcyBwcm9wZXJ0aWVzIGluIGFuIG9iamVjdCBiYXNlZCBvbiBuZXN0ZWQgbmFtZXMgYW5kIGFzc2lnbnNcclxuICogdGhlIGZpbmFsIHZhbHVlLlxyXG4gKlxyXG4gKiBAcGFyYW0ge09iamVjdH0gb2JqZWN0VG9VcGRhdGUgLSBUaGUgb2JqZWN0IHRvIGJlIHVwZGF0ZWQuXHJcbiAqIEBwYXJhbSB7QXJyYXl9IG5lc3RlZE5hbWVzIC0gQXJyYXkgb2YgbmVzdGVkIHByb3BlcnR5IG5hbWVzLlxyXG4gKiBAcGFyYW0ge2FueX0gdmFsdWUgLSBUaGUgZmluYWwgdmFsdWUgdG8gYmUgYXNzaWduZWQuXHJcbiAqXHJcbiAqIEByZXR1cm5zIHtPYmplY3R9IFVwZGF0ZWQgb2JqZWN0IHdpdGggYXNzaWduZWQgdmFsdWVzLlxyXG4gKi9cclxuZnVuY3Rpb24gcmVjdXJzaXZlUHJvcHMob2JqZWN0VG9VcGRhdGUsIG5lc3RlZE5hbWVzLCB2YWx1ZSkge1xyXG4gIHdoaWxlIChuZXN0ZWROYW1lcy5sZW5ndGggPiAxKSB7XHJcbiAgICBjb25zdCBwcm9wTmFtZSA9IG5lc3RlZE5hbWVzLnNoaWZ0KCk7XHJcblxyXG4gICAgLy8gQ3JlYXRlIGEgcHJvcGVydHkgaW4gb2JqZWN0IGlmIGl0IGRvZXNuJ3QgZXhpc3RcclxuICAgIGlmICghT2JqZWN0LnByb3RvdHlwZS5oYXNPd25Qcm9wZXJ0eS5jYWxsKG9iamVjdFRvVXBkYXRlLCBwcm9wTmFtZSkpIHtcclxuICAgICAgb2JqZWN0VG9VcGRhdGVbcHJvcE5hbWVdID0ge307XHJcbiAgICB9XHJcblxyXG4gICAgLy8gQ2FsbCBmdW5jdGlvbiBhZ2FpbiBpZiB0aGVyZSBzdGlsbCBuYW1lcyB0byBnb1xyXG4gICAgb2JqZWN0VG9VcGRhdGVbcHJvcE5hbWVdID0gcmVjdXJzaXZlUHJvcHMoXHJcbiAgICAgIE9iamVjdC5hc3NpZ24oe30sIG9iamVjdFRvVXBkYXRlW3Byb3BOYW1lXSksXHJcbiAgICAgIG5lc3RlZE5hbWVzLFxyXG4gICAgICB2YWx1ZVxyXG4gICAgKTtcclxuXHJcbiAgICByZXR1cm4gb2JqZWN0VG9VcGRhdGU7XHJcbiAgfVxyXG5cclxuICAvLyBBc3NpZ24gdGhlIGZpbmFsIHZhbHVlXHJcbiAgb2JqZWN0VG9VcGRhdGVbbmVzdGVkTmFtZXNbMF1dID0gdmFsdWU7XHJcbiAgcmV0dXJuIG9iamVjdFRvVXBkYXRlO1xyXG59XHJcblxyXG5leHBvcnQgZGVmYXVsdCB7XHJcbiAgZ2V0T3B0aW9ucyxcclxuICBzZXRPcHRpb25zLFxyXG4gIG1hbnVhbENvbmZpZyxcclxuICBtYXBUb05ld0NvbmZpZyxcclxuICBtZXJnZUNvbmZpZ09wdGlvbnMsXHJcbiAgaW5pdEV4cG9ydFNldHRpbmdzXHJcbn07XHJcbiIsIi8qKlxyXG4gKiBUaGlzIG1vZHVsZSBleHBvcnRzIHR3byBmdW5jdGlvbnM6IGZldGNoIChmb3IgR0VUIHJlcXVlc3RzKSBhbmQgcG9zdCAoZm9yIFBPU1QgcmVxdWVzdHMpLlxyXG4gKi9cclxuXHJcbmltcG9ydCBodHRwIGZyb20gJ2h0dHAnO1xyXG5pbXBvcnQgaHR0cHMgZnJvbSAnaHR0cHMnO1xyXG5cclxuLyoqXHJcbiAqIFJldHVybnMgdGhlIEhUVFAgb3IgSFRUUFMgcHJvdG9jb2wgbW9kdWxlIGJhc2VkIG9uIHRoZSBwcm92aWRlZCBVUkwuXHJcbiAqXHJcbiAqIEBwYXJhbSB7c3RyaW5nfSB1cmwgLSBUaGUgVVJMIHRvIGRldGVybWluZSB0aGUgcHJvdG9jb2wuXHJcbiAqXHJcbiAqIEByZXR1cm5zIHtPYmplY3R9IFRoZSBIVFRQIG9yIEhUVFBTIHByb3RvY29sIG1vZHVsZSAoaHR0cCBvciBodHRwcykuXHJcbiAqL1xyXG5jb25zdCBnZXRQcm90b2NvbCA9ICh1cmwpID0+ICh1cmwuc3RhcnRzV2l0aCgnaHR0cHMnKSA/IGh0dHBzIDogaHR0cCk7XHJcblxyXG4vKipcclxuICogRmV0Y2hlcyBkYXRhIGZyb20gdGhlIHNwZWNpZmllZCBVUkwgdXNpbmcgZWl0aGVyIEhUVFAgb3IgSFRUUFMgcHJvdG9jb2wuXHJcbiAqXHJcbiAqIEBwYXJhbSB7c3RyaW5nfSB1cmwgLSBUaGUgVVJMIHRvIGZldGNoIGRhdGEgZnJvbS5cclxuICogQHBhcmFtIHtPYmplY3R9IHJlcXVlc3RPcHRpb25zIC0gT3B0aW9ucyBmb3IgdGhlIEhUVFAgcmVxdWVzdCAob3B0aW9uYWwpLlxyXG4gKlxyXG4gKiBAcmV0dXJucyB7UHJvbWlzZTxPYmplY3Q+fSBQcm9taXNlIHJlc29sdmluZyB0byB0aGUgSFRUUCByZXNwb25zZSBvYmplY3RcclxuICogd2l0aCBhZGRlZCAndGV4dCcgcHJvcGVydHkgb3IgcmVqZWN0aW5nIHdpdGggYW4gZXJyb3IuXHJcbiAqL1xyXG5hc3luYyBmdW5jdGlvbiBmZXRjaCh1cmwsIHJlcXVlc3RPcHRpb25zID0ge30pIHtcclxuICByZXR1cm4gbmV3IFByb21pc2UoKHJlc29sdmUsIHJlamVjdCkgPT4ge1xyXG4gICAgY29uc3QgcHJvdG9jb2wgPSBnZXRQcm90b2NvbCh1cmwpO1xyXG5cclxuICAgIHByb3RvY29sXHJcbiAgICAgIC5nZXQoXHJcbiAgICAgICAgdXJsLFxyXG4gICAgICAgIE9iamVjdC5hc3NpZ24oXHJcbiAgICAgICAgICB7XHJcbiAgICAgICAgICAgIGhlYWRlcnM6IHtcclxuICAgICAgICAgICAgICAnVXNlci1BZ2VudCc6ICdoaWdoY2hhcnRzL2V4cG9ydCcsXHJcbiAgICAgICAgICAgICAgUmVmZXJlcjogJ2hpZ2hjaGFydHMuZXhwb3J0J1xyXG4gICAgICAgICAgICB9XHJcbiAgICAgICAgICB9LFxyXG4gICAgICAgICAgcmVxdWVzdE9wdGlvbnMgfHwge31cclxuICAgICAgICApLFxyXG4gICAgICAgIChyZXMpID0+IHtcclxuICAgICAgICAgIGxldCBkYXRhID0gJyc7XHJcblxyXG4gICAgICAgICAgLy8gQSBjaHVuayBvZiBkYXRhIGhhcyBiZWVuIHJlY2VpdmVkLlxyXG4gICAgICAgICAgcmVzLm9uKCdkYXRhJywgKGNodW5rKSA9PiB7XHJcbiAgICAgICAgICAgIGRhdGEgKz0gY2h1bms7XHJcbiAgICAgICAgICB9KTtcclxuXHJcbiAgICAgICAgICAvLyBUaGUgd2hvbGUgcmVzcG9uc2UgaGFzIGJlZW4gcmVjZWl2ZWQuXHJcbiAgICAgICAgICByZXMub24oJ2VuZCcsICgpID0+IHtcclxuICAgICAgICAgICAgaWYgKCFkYXRhKSB7XHJcbiAgICAgICAgICAgICAgcmVqZWN0KCdOb3RoaW5nIHdhcyBmZXRjaGVkIGZyb20gdGhlIFVSTC4nKTtcclxuICAgICAgICAgICAgfVxyXG5cclxuICAgICAgICAgICAgcmVzLnRleHQgPSBkYXRhO1xyXG4gICAgICAgICAgICByZXNvbHZlKHJlcyk7XHJcbiAgICAgICAgICB9KTtcclxuICAgICAgICB9XHJcbiAgICAgIClcclxuICAgICAgLm9uKCdlcnJvcicsIChlcnJvcikgPT4ge1xyXG4gICAgICAgIHJlamVjdChlcnJvcik7XHJcbiAgICAgIH0pO1xyXG4gIH0pO1xyXG59XHJcblxyXG4vKipcclxuICogU2VuZHMgYSBQT1NUIHJlcXVlc3QgdG8gdGhlIHNwZWNpZmllZCBVUkwgd2l0aCB0aGUgcHJvdmlkZWQgSlNPTiBib2R5IHVzaW5nXHJcbiAqIGVpdGhlciBIVFRQIG9yIEhUVFBTIHByb3RvY29sLlxyXG4gKlxyXG4gKiBAcGFyYW0ge3N0cmluZ30gdXJsIC0gVGhlIFVSTCB0byBzZW5kIHRoZSBQT1NUIHJlcXVlc3QgdG8uXHJcbiAqIEBwYXJhbSB7T2JqZWN0fSBib2R5IC0gVGhlIEpTT04gYm9keSB0byBpbmNsdWRlIGluIHRoZSBQT1NUIHJlcXVlc3RcclxuICogKG9wdGlvbmFsLCBkZWZhdWx0IGlzIGFuIGVtcHR5IG9iamVjdCkuXHJcbiAqIEBwYXJhbSB7T2JqZWN0fSByZXF1ZXN0T3B0aW9ucyAtIE9wdGlvbnMgZm9yIHRoZSBIVFRQIHJlcXVlc3QgKG9wdGlvbmFsKS5cclxuICpcclxuICogQHJldHVybnMge1Byb21pc2U8T2JqZWN0Pn0gUHJvbWlzZSByZXNvbHZpbmcgdG8gdGhlIEhUVFAgcmVzcG9uc2Ugb2JqZWN0IHdpdGhcclxuICogYWRkZWQgJ3RleHQnIHByb3BlcnR5IG9yIHJlamVjdGluZyB3aXRoIGFuIGVycm9yLlxyXG4gKi9cclxuYXN5bmMgZnVuY3Rpb24gcG9zdCh1cmwsIGJvZHkgPSB7fSwgcmVxdWVzdE9wdGlvbnMgPSB7fSkge1xyXG4gIHJldHVybiBuZXcgUHJvbWlzZSgocmVzb2x2ZSwgcmVqZWN0KSA9PiB7XHJcbiAgICBjb25zdCBwcm90b2NvbCA9IGdldFByb3RvY29sKHVybCk7XHJcbiAgICBjb25zdCBkYXRhID0gSlNPTi5zdHJpbmdpZnkoYm9keSk7XHJcblxyXG4gICAgLy8gU2V0IGRlZmF1bHQgaGVhZGVycyBhbmQgbWVyZ2Ugd2l0aCByZXF1ZXN0T3B0aW9uc1xyXG4gICAgY29uc3Qgb3B0aW9ucyA9IE9iamVjdC5hc3NpZ24oXHJcbiAgICAgIHtcclxuICAgICAgICBtZXRob2Q6ICdQT1NUJyxcclxuICAgICAgICBoZWFkZXJzOiB7XHJcbiAgICAgICAgICAnQ29udGVudC1UeXBlJzogJ2FwcGxpY2F0aW9uL2pzb24nLFxyXG4gICAgICAgICAgJ0NvbnRlbnQtTGVuZ3RoJzogZGF0YS5sZW5ndGhcclxuICAgICAgICB9XHJcbiAgICAgIH0sXHJcbiAgICAgIHJlcXVlc3RPcHRpb25zXHJcbiAgICApO1xyXG5cclxuICAgIGNvbnN0IHJlcSA9IHByb3RvY29sXHJcbiAgICAgIC5yZXF1ZXN0KHVybCwgb3B0aW9ucywgKHJlcykgPT4ge1xyXG4gICAgICAgIGxldCByZXNwb25zZURhdGEgPSAnJztcclxuXHJcbiAgICAgICAgLy8gQSBjaHVuayBvZiBkYXRhIGhhcyBiZWVuIHJlY2VpdmVkLlxyXG4gICAgICAgIHJlcy5vbignZGF0YScsIChjaHVuaykgPT4ge1xyXG4gICAgICAgICAgcmVzcG9uc2VEYXRhICs9IGNodW5rO1xyXG4gICAgICAgIH0pO1xyXG5cclxuICAgICAgICAvLyBUaGUgd2hvbGUgcmVzcG9uc2UgaGFzIGJlZW4gcmVjZWl2ZWQuXHJcbiAgICAgICAgcmVzLm9uKCdlbmQnLCAoKSA9PiB7XHJcbiAgICAgICAgICB0cnkge1xyXG4gICAgICAgICAgICByZXMudGV4dCA9IHJlc3BvbnNlRGF0YTtcclxuICAgICAgICAgICAgcmVzb2x2ZShyZXMpO1xyXG4gICAgICAgICAgfSBjYXRjaCAoZXJyb3IpIHtcclxuICAgICAgICAgICAgcmVqZWN0KGVycm9yKTtcclxuICAgICAgICAgIH1cclxuICAgICAgICB9KTtcclxuICAgICAgfSlcclxuICAgICAgLm9uKCdlcnJvcicsIChlcnJvcikgPT4ge1xyXG4gICAgICAgIHJlamVjdChlcnJvcik7XHJcbiAgICAgIH0pO1xyXG5cclxuICAgIC8vIFdyaXRlIHRoZSByZXF1ZXN0IGJvZHkgYW5kIGVuZCB0aGUgcmVxdWVzdC5cclxuICAgIHJlcS53cml0ZShkYXRhKTtcclxuICAgIHJlcS5lbmQoKTtcclxuICB9KTtcclxufVxyXG5cclxuZXhwb3J0IGRlZmF1bHQgZmV0Y2g7XHJcbmV4cG9ydCB7IGZldGNoLCBwb3N0IH07XHJcbiIsImNsYXNzIEV4cG9ydEVycm9yIGV4dGVuZHMgRXJyb3Ige1xyXG4gIGNvbnN0cnVjdG9yKG1lc3NhZ2UpIHtcclxuICAgIHN1cGVyKCk7XHJcbiAgICB0aGlzLm1lc3NhZ2UgPSBtZXNzYWdlO1xyXG4gICAgdGhpcy5zdGFja01lc3NhZ2UgPSBtZXNzYWdlO1xyXG4gIH1cclxuXHJcbiAgc2V0RXJyb3IoZXJyb3IpIHtcclxuICAgIHRoaXMuZXJyb3IgPSBlcnJvcjtcclxuICAgIGlmIChlcnJvci5uYW1lKSB7XHJcbiAgICAgIHRoaXMubmFtZSA9IGVycm9yLm5hbWU7XHJcbiAgICB9XHJcbiAgICBpZiAoZXJyb3Iuc3RhdHVzQ29kZSkge1xyXG4gICAgICB0aGlzLnN0YXR1c0NvZGUgPSBlcnJvci5zdGF0dXNDb2RlO1xyXG4gICAgfVxyXG4gICAgaWYgKGVycm9yLnN0YWNrKSB7XHJcbiAgICAgIHRoaXMuc3RhY2tNZXNzYWdlID0gZXJyb3IubWVzc2FnZTtcclxuICAgICAgdGhpcy5zdGFjayA9IGVycm9yLnN0YWNrO1xyXG4gICAgfVxyXG4gICAgcmV0dXJuIHRoaXM7XHJcbiAgfVxyXG59XHJcblxyXG5leHBvcnQgZGVmYXVsdCBFeHBvcnRFcnJvcjtcclxuIiwiLyoqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKipcclxuXHJcbkhpZ2hjaGFydHMgRXhwb3J0IFNlcnZlclxyXG5cclxuQ29weXJpZ2h0IChjKSAyMDE2LTIwMjQsIEhpZ2hzb2Z0XHJcblxyXG5MaWNlbmNlZCB1bmRlciB0aGUgTUlUIGxpY2VuY2UuXHJcblxyXG5BZGRpdGlvbmFsbHkgYSB2YWxpZCBIaWdoY2hhcnRzIGxpY2Vuc2UgaXMgcmVxdWlyZWQgZm9yIHVzZS5cclxuXHJcblNlZSBMSUNFTlNFIGZpbGUgaW4gcm9vdCBmb3IgZGV0YWlscy5cclxuXHJcbioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKiovXHJcblxyXG4vLyBUaGUgY2FjaGUgbWFuYWdlciBtYW5hZ2VzIHRoZSBIaWdoY2hhcnRzIGxpYnJhcnkgYW5kIGl0cyBkZXBlbmRlbmNpZXMuXHJcbi8vIFRoZSBjYWNoZSBpdHNlbGYgaXMgc3RvcmVkIGluIC5jYWNoZSwgYW5kIGlzIGNoZWNrZWQgYnkgdGhlIGNvbmZpZyBzeXN0ZW1cclxuLy8gYmVmb3JlIHN0YXJ0aW5nIHRoZSBzZXJ2aWNlXHJcblxyXG5pbXBvcnQgeyBleGlzdHNTeW5jLCBta2RpclN5bmMsIHJlYWRGaWxlU3luYywgd3JpdGVGaWxlU3luYyB9IGZyb20gJ2ZzJztcclxuaW1wb3J0IHsgam9pbiwgcmVzb2x2ZSwgc2VwIH0gZnJvbSAncGF0aCc7XHJcblxyXG5pbXBvcnQgeyBIdHRwc1Byb3h5QWdlbnQgfSBmcm9tICdodHRwcy1wcm94eS1hZ2VudCc7XHJcblxyXG5pbXBvcnQgeyBnZXRPcHRpb25zIH0gZnJvbSAnLi9jb25maWcuanMnO1xyXG5pbXBvcnQgeyBlbnZzIH0gZnJvbSAnLi9lbnZzLmpzJztcclxuaW1wb3J0IHsgZmV0Y2ggfSBmcm9tICcuL2ZldGNoLmpzJztcclxuaW1wb3J0IHsgbG9nIH0gZnJvbSAnLi9sb2dnZXIuanMnO1xyXG5pbXBvcnQgeyBfX2Rpcm5hbWUsIF9faGlnaGNoYXJ0c0RpciB9IGZyb20gJy4vdXRpbHMuanMnO1xyXG5cclxuaW1wb3J0IEV4cG9ydEVycm9yIGZyb20gJy4vZXJyb3JzL0V4cG9ydEVycm9yLmpzJztcclxuXHJcbmNvbnN0IGNhY2hlID0ge1xyXG4gIGNkblVSTDogJ2h0dHBzOi8vY29kZS5oaWdoY2hhcnRzLmNvbS8nLFxyXG4gIGFjdGl2ZU1hbmlmZXN0OiB7fSxcclxuICBzb3VyY2VzOiAnJyxcclxuICBoY1ZlcnNpb246ICcnXHJcbn07XHJcblxyXG4vKipcclxuICogRXh0cmFjdHMgYW5kIGNhY2hlcyB0aGUgSGlnaGNoYXJ0cyB2ZXJzaW9uIGZyb20gdGhlIHNvdXJjZXMgc3RyaW5nLlxyXG4gKlxyXG4gKiBAcmV0dXJucyB7c3RyaW5nfSBUaGUgZXh0cmFjdGVkIEhpZ2hjaGFydHMgdmVyc2lvbi5cclxuICovXHJcbmV4cG9ydCBjb25zdCBleHRyYWN0VmVyc2lvbiA9IChjYWNoZSkgPT4ge1xyXG4gIHJldHVybiBjYWNoZS5zb3VyY2VzXHJcbiAgICAuc3Vic3RyaW5nKDAsIGNhY2hlLnNvdXJjZXMuaW5kZXhPZignKi8nKSlcclxuICAgIC5yZXBsYWNlKCcvKicsICcnKVxyXG4gICAgLnJlcGxhY2UoJyovJywgJycpXHJcbiAgICAucmVwbGFjZSgvXFxuL2csICcnKVxyXG4gICAgLnRyaW0oKTtcclxufTtcclxuXHJcbi8qKlxyXG4gKiBFeHRyYWN0cyB0aGUgSGlnaGNoYXJ0cyBtb2R1bGUgbmFtZSBiYXNlZCBvbiB0aGUgc2NyaXB0UGF0aC5cclxuICpcclxuICogQHBhcmFtIHtzdHJpbmd9IHNjcmlwdFBhdGggLSBUaGUgcGF0aCB0byB0aGUgbW9kdWxlLlxyXG4gKlxyXG4gKiBAcmV0dXJucyB7c3RyaW5nfSBUaGUgbmFtZSBvZiBhIG1vZHVsZS5cclxuICovXHJcbmV4cG9ydCBjb25zdCBleHRyYWN0TW9kdWxlTmFtZSA9IChzY3JpcHRQYXRoKSA9PiB7XHJcbiAgLy8gTm9ybWFsaXplIHNsYXNoZXMsIGdldCBwYXJ0IGFmdGVyIHRoZSBsYXN0ICcvJyBhbmQgcmVtb3ZlIC5qcyBleHRlbnNpb25cclxuICByZXR1cm4gc2NyaXB0UGF0aC5yZXBsYWNlKC9cXFxcL2csICcvJykuc3BsaXQoJy8nKS5wb3AoKS5yZXBsYWNlKC9cXC5qcyQvaSwgJycpO1xyXG59O1xyXG5cclxuLyoqXHJcbiAqIFNhdmVzIHRoZSBwcm92aWRlZCBjb25maWd1cmF0aW9uIGFuZCBmZXRjaGVkIG1vZHVsZXMgdG8gdGhlIGNhY2hlIG1hbmlmZXN0XHJcbiAqIGZpbGUuXHJcbiAqXHJcbiAqIEBwYXJhbSB7b2JqZWN0fSBjb25maWcgLSBIaWdoY2hhcnRzLXJlbGF0ZWQgY29uZmlndXJhdGlvbiBvYmplY3QuXHJcbiAqIEBwYXJhbSB7b2JqZWN0fSBmZXRjaGVkTW9kdWxlcyAtIEFuIG9iamVjdCB0aGF0IGNvbnRhaW5zIG1hcHBlZCBuYW1lcyBvZlxyXG4gKiBmZXRjaGVkIEhpZ2hjaGFydHMgbW9kdWxlcyB0byB1c2UuXHJcbiAqXHJcbiAqIEB0aHJvd3Mge0V4cG9ydEVycm9yfSBUaHJvd3MgYW4gRXhwb3J0RXJyb3IgaWYgYW4gZXJyb3Igb2NjdXJzIHdoaWxlIHdyaXRpbmdcclxuICogdGhlIGNhY2hlIG1hbmlmZXN0LlxyXG4gKi9cclxuZXhwb3J0IGNvbnN0IHNhdmVDb25maWdUb01hbmlmZXN0ID0gYXN5bmMgKGNvbmZpZywgZmV0Y2hlZE1vZHVsZXMpID0+IHtcclxuICBjb25zdCBuZXdNYW5pZmVzdCA9IHtcclxuICAgIHZlcnNpb246IGNvbmZpZy52ZXJzaW9uLFxyXG4gICAgbW9kdWxlczogZmV0Y2hlZE1vZHVsZXMgfHwge31cclxuICB9O1xyXG5cclxuICAvLyBVcGRhdGUgY2FjaGUgb2JqZWN0IHdpdGggdGhlIGN1cnJlbnQgbW9kdWxlc1xyXG4gIGNhY2hlLmFjdGl2ZU1hbmlmZXN0ID0gbmV3TWFuaWZlc3Q7XHJcblxyXG4gIGxvZygzLCAnW2NhY2hlXSBXcml0aW5nIGEgbmV3IG1hbmlmZXN0LicpO1xyXG4gIHRyeSB7XHJcbiAgICB3cml0ZUZpbGVTeW5jKFxyXG4gICAgICBqb2luKF9fZGlybmFtZSwgY29uZmlnLmNhY2hlUGF0aCwgJ21hbmlmZXN0Lmpzb24nKSxcclxuICAgICAgSlNPTi5zdHJpbmdpZnkobmV3TWFuaWZlc3QpLFxyXG4gICAgICAndXRmOCdcclxuICAgICk7XHJcbiAgfSBjYXRjaCAoZXJyb3IpIHtcclxuICAgIHRocm93IG5ldyBFeHBvcnRFcnJvcignW2NhY2hlXSBFcnJvciB3cml0aW5nIHRoZSBjYWNoZSBtYW5pZmVzdC4nKS5zZXRFcnJvcihcclxuICAgICAgZXJyb3JcclxuICAgICk7XHJcbiAgfVxyXG59O1xyXG5cclxuLyoqXHJcbiAqIEZldGNoZXMgYSBzaW5nbGUgc2NyaXB0IGFuZCB1cGRhdGVzIHRoZSBmZXRjaGVkTW9kdWxlcyBhY2NvcmRpbmdseS5cclxuICpcclxuICogQHBhcmFtIHtzdHJpbmd9IHNjcmlwdCAtIEEgcGF0aCB0byBzY3JpcHQgdG8gZ2V0LlxyXG4gKiBAcGFyYW0ge09iamVjdH0gcmVxdWVzdE9wdGlvbnMgLSBBZGRpdGlvbmFsIG9wdGlvbnMgZm9yIHRoZSBwcm94eSBhZ2VudFxyXG4gKiB0byB1c2UgZm9yIGEgcmVxdWVzdC5cclxuICogQHBhcmFtIHtPYmplY3R9IGZldGNoZWRNb2R1bGVzIC0gQW4gb2JqZWN0IHdoaWNoIHRyYWNrcyB3aGljaCBIaWdoY2hhcnRzXHJcbiAqIG1vZHVsZXMgaGF2ZSBiZWVuIGZldGNoZWQuXHJcbiAqIEBwYXJhbSB7Ym9vbGVhbn0gW3VzZU5wbT1mYWxzZV0gLSBBIGZsYWcgdG8gaW5kaWNhdGUgaWYgdGhlIHNjcmlwdCBzaG91bGQgYmVcclxuICogZ2V0IGZyb20gTlBNIHBhY2thZ2Ugb3IgZmV0Y2hlZCBmcm9tIENETi4gVGhlIGRlZmF1bHQgdmFsdWUgaXMgYGZhbHNlYC5cclxuICogQHBhcmFtIHtib29sZWFufSBbc2hvdWxkVGhyb3dFcnJvcj1mYWxzZV0gLSBBIGZsYWcgdG8gaW5kaWNhdGUgaWYgdGhlIGVycm9yXHJcbiAqIHNob3VsZCBiZSB0aHJvd24uIFRoaXMgc2hvdWxkIGJlIHVzZWQgb25seSBmb3IgdGhlIGNvcmUgc2NyaXB0cy5cclxuICpcclxuICogQHJldHVybnMge1Byb21pc2U8c3RyaW5nPn0gQSBQcm9taXNlIHJlc29sdmluZyB0byB0aGUgdGV4dCByZXByZXNlbnRhdGlvblxyXG4gKiBvZiB0aGUgZmV0Y2hlZCBzY3JpcHQuXHJcbiAqXHJcbiAqIEB0aHJvd3Mge0V4cG9ydEVycm9yfSBUaHJvd3MgYW4gRXhwb3J0RXJyb3IgaWYgdGhlcmUgaXMgYSBwcm9ibGVtIHdpdGhcclxuICogZmV0Y2hpbmcgdGhlIHNjcmlwdC5cclxuICovXHJcbmV4cG9ydCBjb25zdCBmZXRjaEFuZFByb2Nlc3NTY3JpcHQgPSBhc3luYyAoXHJcbiAgc2NyaXB0LFxyXG4gIHJlcXVlc3RPcHRpb25zLFxyXG4gIGZldGNoZWRNb2R1bGVzLFxyXG4gIHVzZU5wbSA9IGZhbHNlLFxyXG4gIHNob3VsZFRocm93RXJyb3IgPSBmYWxzZVxyXG4pID0+IHtcclxuICBsZXQgcmVzcG9uc2U7XHJcblxyXG4gIC8vIEFkZCB0aGUgbWlzc2luZyAuanMgdG8gdGhlIHN0cmluZ3NcclxuICBpZiAoIXNjcmlwdC5lbmRzV2l0aCgnLmpzJykpIHtcclxuICAgIHNjcmlwdCA9IGAke3NjcmlwdH0uanNgO1xyXG4gIH1cclxuXHJcbiAgLy8gV2hldGhlciB0byB1c2UgTlBNIHBhY2thZ2Ugc2NyaXB0cyBvciBmZXRjaCBpdCBmcm9tIENETlxyXG4gIGlmICh1c2VOcG0pIHtcclxuICAgIHRyeSB7XHJcbiAgICAgIC8vIExvZyBmZXRjaGVkIHNjcmlwdFxyXG4gICAgICBsb2coXHJcbiAgICAgICAgNCxcclxuICAgICAgICBgW2NhY2hlXSBGZXRjaGluZyBzY3JpcHQgZnJvbSBOUE0gLSAke2pvaW4oJ25vZGVfbW9kdWxlcycsICdoaWdoY2hhcnRzJywgc2NyaXB0KX1gXHJcbiAgICAgICk7XHJcblxyXG4gICAgICAvLyBTYW5pdGl6ZSBhbmQgdmFsaWRhdGUgcGF0aFxyXG4gICAgICBjb25zdCByZXNvbHZlZFNjcmlwdFBhdGggPSByZXNvbHZlKF9faGlnaGNoYXJ0c0Rpciwgc2NyaXB0KTtcclxuICAgICAgaWYgKCFyZXNvbHZlZFNjcmlwdFBhdGguc3RhcnRzV2l0aChyZXNvbHZlKF9faGlnaGNoYXJ0c0RpcikgKyBzZXApKSB7XHJcbiAgICAgICAgdGhyb3cgbmV3IEV4cG9ydEVycm9yKFxyXG4gICAgICAgICAgYFtjYWNoZV0gSW52YWxpZCBzY3JpcHQgcGF0aCBkZXRlY3RlZCBmb3IgJyR7c2NyaXB0fScuIERpcmVjdG9yeSB0cmF2ZXJzYWwgYXR0ZW1wdCBvciBiYWQgdmVyc2lvbiBpbnB1dC5gLFxyXG4gICAgICAgICAgNDAzXHJcbiAgICAgICAgKTtcclxuICAgICAgfVxyXG5cclxuICAgICAgLy8gRmV0Y2ggdGhlIHNjcmlwdCBmcm9tIE5QTVxyXG4gICAgICByZXNwb25zZSA9IHJlYWRGaWxlU3luYyhyZXNvbHZlZFNjcmlwdFBhdGgsICd1dGY4Jyk7XHJcblxyXG4gICAgICAvLyBJZiBPSywgcmV0dXJuIGl0cyB0ZXh0IHJlcHJlc2VudGF0aW9uXHJcbiAgICAgIGlmIChmZXRjaGVkTW9kdWxlcyAmJiByZXNwb25zZSkge1xyXG4gICAgICAgIGZldGNoZWRNb2R1bGVzW2V4dHJhY3RNb2R1bGVOYW1lKHNjcmlwdCldID0gMTtcclxuICAgICAgfVxyXG4gICAgICByZXR1cm4gcmVzcG9uc2U7XHJcbiAgICB9IGNhdGNoIHtcclxuICAgICAgLy8gUHJvY2VlZFxyXG4gICAgfVxyXG4gIH0gZWxzZSB7XHJcbiAgICAvLyBMb2cgZmV0Y2hlZCBzY3JpcHRcclxuICAgIGxvZyg0LCBgW2NhY2hlXSBGZXRjaGluZyBzY3JpcHQgZnJvbSBDRE4gLSAke3NjcmlwdH1gKTtcclxuXHJcbiAgICAvLyBGZXRjaCB0aGUgc2NyaXB0IGZyb20gQ0ROXHJcbiAgICByZXNwb25zZSA9IGF3YWl0IGZldGNoKHNjcmlwdCwgcmVxdWVzdE9wdGlvbnMpO1xyXG5cclxuICAgIC8vIElmIE9LLCByZXR1cm4gaXRzIHRleHQgcmVwcmVzZW50YXRpb25cclxuICAgIGlmIChyZXNwb25zZS5zdGF0dXNDb2RlID09PSAyMDAgJiYgdHlwZW9mIHJlc3BvbnNlLnRleHQgPT0gJ3N0cmluZycpIHtcclxuICAgICAgaWYgKGZldGNoZWRNb2R1bGVzKSB7XHJcbiAgICAgICAgZmV0Y2hlZE1vZHVsZXNbZXh0cmFjdE1vZHVsZU5hbWUoc2NyaXB0KV0gPSAxO1xyXG4gICAgICB9XHJcbiAgICAgIHJldHVybiByZXNwb25zZS50ZXh0O1xyXG4gICAgfVxyXG4gIH1cclxuXHJcbiAgLy8gQmFzZWQgb24gdGhlIGBzaG91bGRUaHJvd0Vycm9yYCBmbGFnLCBkZWNpZGUgaG93IHRvIHNlcnZlIGVycm9yIG1lc3NhZ2VcclxuICBpZiAoc2hvdWxkVGhyb3dFcnJvcikge1xyXG4gICAgdGhyb3cgbmV3IEV4cG9ydEVycm9yKFxyXG4gICAgICBgW2NhY2hlXSBDb3VsZCBub3QgZmV0Y2ggdGhlIG1hbmRhdG9yeSAke3NjcmlwdH0uIFRoZSBzY3JpcHQgbWlnaHQgbm90IGV4aXN0IGluIHRoZSByZXF1ZXN0ZWQgdmVyc2lvbi5gLFxyXG4gICAgICA0MDRcclxuICAgICk7XHJcbiAgfSBlbHNlIHtcclxuICAgIGxvZyhcclxuICAgICAgMixcclxuICAgICAgYFtjYWNoZV0gQ291bGQgbm90IGZldGNoIHRoZSAke3NjcmlwdH0uIFRoZSBzY3JpcHQgbWlnaHQgbm90IGV4aXN0IGluIHRoZSByZXF1ZXN0ZWQgdmVyc2lvbi5gXHJcbiAgICApO1xyXG4gIH1cclxuXHJcbiAgcmV0dXJuICcnO1xyXG59O1xyXG5cclxuLyoqXHJcbiAqIEZldGNoZXMgSGlnaGNoYXJ0cyBzY3JpcHRzIGFuZCBjdXN0b21TY3JpcHRzIGZyb20gdGhlIGdpdmVuIENETnMuXHJcbiAqXHJcbiAqIEBwYXJhbSB7T2JqZWN0fSBoaWdoY2hhcnRzT3B0aW9ucyAtIE9iamVjdCBjb250YWluaW5nIGFsbCBoaWdoY2hhcnRzIG9wdGlvbnMuXHJcbiAqIEBwYXJhbSB7b2JqZWN0fSBwcm94eU9wdGlvbnMgLSBPcHRpb25zIGZvciB0aGUgcHJveHkgYWdlbnQgdG8gdXNlIGZvclxyXG4gKiBhIHJlcXVlc3QuXHJcbiAqIEBwYXJhbSB7b2JqZWN0fSBmZXRjaGVkTW9kdWxlcyAtIEFuIG9iamVjdCB3aGljaCB0cmFja3Mgd2hpY2ggSGlnaGNoYXJ0c1xyXG4gKiBtb2R1bGVzIGhhdmUgYmVlbiBmZXRjaGVkLlxyXG4gKlxyXG4gKiBAcmV0dXJucyB7UHJvbWlzZTxzdHJpbmc+fSBUaGUgZmV0Y2hlZCBzY3JpcHRzIGNvbnRlbnQgam9pbmVkLlxyXG4gKi9cclxuZXhwb3J0IGNvbnN0IGZldGNoU2NyaXB0cyA9IGFzeW5jIChcclxuICBoaWdoY2hhcnRzT3B0aW9ucyxcclxuICBwcm94eU9wdGlvbnMsXHJcbiAgZmV0Y2hlZE1vZHVsZXNcclxuKSA9PiB7XHJcbiAgY29uc3QgdmVyc2lvbiA9IGhpZ2hjaGFydHNPcHRpb25zLnZlcnNpb247XHJcbiAgY29uc3QgaGNWZXJzaW9uID0gdmVyc2lvbiA9PT0gJ2xhdGVzdCcgfHwgIXZlcnNpb24gPyAnJyA6IGAke3ZlcnNpb259L2A7XHJcbiAgY29uc3QgY2RuVVJMID0gaGlnaGNoYXJ0c09wdGlvbnMuY2RuVVJMIHx8IGNhY2hlLmNkblVSTDtcclxuXHJcbiAgbG9nKFxyXG4gICAgMyxcclxuICAgIGBbY2FjaGVdIFVwZGF0aW5nIGNhY2hlIHZlcnNpb24gdG8gSGlnaGNoYXJ0czogJHtoY1ZlcnNpb24gfHwgJ2xhdGVzdCd9LmBcclxuICApO1xyXG5cclxuICAvLyBXaGV0aGVyIHRvIHVzZSBOUE0gb3IgQ0ROXHJcbiAgY29uc3QgdXNlTnBtID0gaGlnaGNoYXJ0c09wdGlvbnMudXNlTnBtO1xyXG5cclxuICAvLyBDb25maWd1cmUgcHJveHkgaWYgZXhpc3RzXHJcbiAgbGV0IHByb3h5QWdlbnQ7XHJcbiAgY29uc3QgeyBob3N0LCBwb3J0LCB1c2VybmFtZSwgcGFzc3dvcmQgfSA9IHByb3h5T3B0aW9ucztcclxuXHJcbiAgLy8gVHJ5IHRvIGNyZWF0ZSBhIFByb3h5IEFnZW50XHJcbiAgaWYgKGhvc3QgJiYgcG9ydCkge1xyXG4gICAgdHJ5IHtcclxuICAgICAgcHJveHlBZ2VudCA9IG5ldyBIdHRwc1Byb3h5QWdlbnQoe1xyXG4gICAgICAgIGhvc3QsXHJcbiAgICAgICAgcG9ydCxcclxuICAgICAgICAuLi4odXNlcm5hbWUgJiYgcGFzc3dvcmQgPyB7IHVzZXJuYW1lLCBwYXNzd29yZCB9IDoge30pXHJcbiAgICAgIH0pO1xyXG4gICAgfSBjYXRjaCAoZXJyb3IpIHtcclxuICAgICAgdGhyb3cgbmV3IEV4cG9ydEVycm9yKCdbY2FjaGVdIENvdWxkIG5vdCBjcmVhdGUgYSBQcm94eSBBZ2VudC4nKS5zZXRFcnJvcihcclxuICAgICAgICBlcnJvclxyXG4gICAgICApO1xyXG4gICAgfVxyXG4gIH1cclxuXHJcbiAgLy8gSWYgZXhpc3RzLCBhZGQgcHJveHkgYWdlbnQgdG8gcmVxdWVzdCBvcHRpb25zXHJcbiAgY29uc3QgcmVxdWVzdE9wdGlvbnMgPSBwcm94eUFnZW50XHJcbiAgICA/IHtcclxuICAgICAgICBhZ2VudDogcHJveHlBZ2VudCxcclxuICAgICAgICB0aW1lb3V0OiBlbnZzLlNFUlZFUl9QUk9YWV9USU1FT1VUXHJcbiAgICAgIH1cclxuICAgIDoge307XHJcblxyXG4gIGNvbnN0IGZldGNoZWRTY3JpcHRzID0gYXdhaXQgUHJvbWlzZS5hbGwoW1xyXG4gICAgLi4uaGlnaGNoYXJ0c09wdGlvbnMuY29yZVNjcmlwdHMubWFwKChjKSA9PlxyXG4gICAgICBmZXRjaEFuZFByb2Nlc3NTY3JpcHQoXHJcbiAgICAgICAgKHVzZU5wbSAmJiBjKSB8fCBgJHtjZG5VUkx9JHtoY1ZlcnNpb259JHtjfWAsXHJcbiAgICAgICAgcmVxdWVzdE9wdGlvbnMsXHJcbiAgICAgICAgZmV0Y2hlZE1vZHVsZXMsXHJcbiAgICAgICAgdXNlTnBtLFxyXG4gICAgICAgIHRydWVcclxuICAgICAgKVxyXG4gICAgKSxcclxuICAgIC4uLmhpZ2hjaGFydHNPcHRpb25zLm1vZHVsZVNjcmlwdHMubWFwKChtKSA9PlxyXG4gICAgICBmZXRjaEFuZFByb2Nlc3NTY3JpcHQoXHJcbiAgICAgICAgKHVzZU5wbSAmJiBqb2luKCdtb2R1bGVzJywgbSkpIHx8XHJcbiAgICAgICAgICAobSA9PT0gJ21hcCdcclxuICAgICAgICAgICAgPyBgJHtjZG5VUkx9bWFwcy8ke2hjVmVyc2lvbn1tb2R1bGVzLyR7bX1gXHJcbiAgICAgICAgICAgIDogYCR7Y2RuVVJMfSR7aGNWZXJzaW9ufW1vZHVsZXMvJHttfWApLFxyXG4gICAgICAgIHJlcXVlc3RPcHRpb25zLFxyXG4gICAgICAgIGZldGNoZWRNb2R1bGVzLFxyXG4gICAgICAgIHVzZU5wbVxyXG4gICAgICApXHJcbiAgICApLFxyXG4gICAgLi4uaGlnaGNoYXJ0c09wdGlvbnMuaW5kaWNhdG9yU2NyaXB0cy5tYXAoKGkpID0+XHJcbiAgICAgIGZldGNoQW5kUHJvY2Vzc1NjcmlwdChcclxuICAgICAgICAodXNlTnBtICYmIGpvaW4oJ2luZGljYXRvcnMnLCBpKSkgfHxcclxuICAgICAgICAgIGAke2NkblVSTH1zdG9jay8ke2hjVmVyc2lvbn1pbmRpY2F0b3JzLyR7aX1gLFxyXG4gICAgICAgIHJlcXVlc3RPcHRpb25zLFxyXG4gICAgICAgIGZldGNoZWRNb2R1bGVzLFxyXG4gICAgICAgIHVzZU5wbVxyXG4gICAgICApXHJcbiAgICApLFxyXG4gICAgLi4uaGlnaGNoYXJ0c09wdGlvbnMuY3VzdG9tU2NyaXB0cy5tYXAoKGMpID0+XHJcbiAgICAgIGZldGNoQW5kUHJvY2Vzc1NjcmlwdChgJHtjfWAsIHJlcXVlc3RPcHRpb25zKVxyXG4gICAgKVxyXG4gIF0pO1xyXG5cclxuICByZXR1cm4gZmV0Y2hlZFNjcmlwdHMuam9pbignO1xcbicpO1xyXG59O1xyXG5cclxuLyoqXHJcbiAqIFVwZGF0ZXMgdGhlIGxvY2FsIGNhY2hlIHdpdGggSGlnaGNoYXJ0cyBzY3JpcHRzIGFuZCB0aGVpciB2ZXJzaW9ucy5cclxuICpcclxuICogQHBhcmFtIHtPYmplY3R9IGhpZ2hjaGFydHNPcHRpb25zIC0gT2JqZWN0IGNvbnRhaW5pbmcgYWxsIG9wdGlvbnMgZnJvbVxyXG4gKiB0aGUgaGlnaGNoYXJ0cyBzZWN0aW9uLlxyXG4gKiBAcGFyYW0ge3N0cmluZ30gc291cmNlUGF0aCAtIFRoZSBwYXRoIHRvIHRoZSBzb3VyY2UgZmlsZSBpbiB0aGUgY2FjaGUuXHJcbiAqXHJcbiAqIEByZXR1cm5zIHtQcm9taXNlPG9iamVjdD59IEEgUHJvbWlzZSByZXNvbHZpbmcgdG8gYW4gb2JqZWN0IHJlcHJlc2VudGluZ1xyXG4gKiB0aGUgZmV0Y2hlZCBtb2R1bGVzLlxyXG4gKlxyXG4gKiBAdGhyb3dzIHtFeHBvcnRFcnJvcn0gVGhyb3dzIGFuIEV4cG9ydEVycm9yIGlmIHRoZXJlIGlzIGFuIGlzc3VlIHVwZGF0aW5nXHJcbiAqIHRoZSBsb2NhbCBIaWdoY2hhcnRzIGNhY2hlLlxyXG4gKi9cclxuZXhwb3J0IGNvbnN0IHVwZGF0ZUNhY2hlID0gYXN5bmMgKFxyXG4gIGhpZ2hjaGFydHNPcHRpb25zLFxyXG4gIHByb3h5T3B0aW9ucyxcclxuICBzb3VyY2VQYXRoXHJcbikgPT4ge1xyXG4gIHRyeSB7XHJcbiAgICBjb25zdCBmZXRjaGVkTW9kdWxlcyA9IHt9O1xyXG5cclxuICAgIC8vIEdldCBzb3VyY2VzXHJcbiAgICBjYWNoZS5zb3VyY2VzID0gYXdhaXQgZmV0Y2hTY3JpcHRzKFxyXG4gICAgICBoaWdoY2hhcnRzT3B0aW9ucyxcclxuICAgICAgcHJveHlPcHRpb25zLFxyXG4gICAgICBmZXRjaGVkTW9kdWxlc1xyXG4gICAgKTtcclxuXHJcbiAgICAvLyBHZXQgc291cmNlcyB2ZXJzaW9uXHJcbiAgICBjYWNoZS5oY1ZlcnNpb24gPSBleHRyYWN0VmVyc2lvbihjYWNoZSk7XHJcblxyXG4gICAgLy8gU2F2ZSB0aGUgZmV0Y2hlZCBtb2R1bGVzIGludG8gY2FjaGVzJyBzb3VyY2UgSlNPTlxyXG4gICAgd3JpdGVGaWxlU3luYyhzb3VyY2VQYXRoLCBjYWNoZS5zb3VyY2VzKTtcclxuXHJcbiAgICByZXR1cm4gZmV0Y2hlZE1vZHVsZXM7XHJcbiAgfSBjYXRjaCAoZXJyb3IpIHtcclxuICAgIHRocm93IG5ldyBFeHBvcnRFcnJvcihcclxuICAgICAgJ1tjYWNoZV0gVW5hYmxlIHRvIHVwZGF0ZSB0aGUgbG9jYWwgSGlnaGNoYXJ0cyBjYWNoZS4nXHJcbiAgICApLnNldEVycm9yKGVycm9yKTtcclxuICB9XHJcbn07XHJcblxyXG4vKipcclxuICogVXBkYXRlcyB0aGUgSGlnaGNoYXJ0cyB2ZXJzaW9uIGluIHRoZSBhcHBsaWVkIGNvbmZpZ3VyYXRpb24gYW5kIGNoZWNrc1xyXG4gKiB0aGUgY2FjaGUgZm9yIHRoZSBuZXcgdmVyc2lvbi5cclxuICpcclxuICogQHBhcmFtIHtzdHJpbmd9IG5ld1ZlcnNpb24gLSBUaGUgbmV3IEhpZ2hjaGFydHMgdmVyc2lvbiB0byBiZSBhcHBsaWVkLlxyXG4gKlxyXG4gKiBAcmV0dXJucyB7UHJvbWlzZTwob2JqZWN0fGJvb2xlYW4pPn0gQSBQcm9taXNlIHJlc29sdmluZyB0byB0aGUgdXBkYXRlZFxyXG4gKiBjb25maWd1cmF0aW9uIHdpdGggdGhlIG5ldyB2ZXJzaW9uLCBvciBmYWxzZSBpZiBubyBhcHBsaWVkIGNvbmZpZ3VyYXRpb25cclxuICogZXhpc3RzLlxyXG4gKi9cclxuZXhwb3J0IGNvbnN0IHVwZGF0ZVZlcnNpb24gPSBhc3luYyAobmV3VmVyc2lvbikgPT4ge1xyXG4gIGNvbnN0IG9wdGlvbnMgPSBnZXRPcHRpb25zKCk7XHJcbiAgaWYgKG9wdGlvbnM/LmhpZ2hjaGFydHMpIHtcclxuICAgIG9wdGlvbnMuaGlnaGNoYXJ0cy52ZXJzaW9uID0gbmV3VmVyc2lvbjtcclxuICB9XHJcbiAgYXdhaXQgY2hlY2tBbmRVcGRhdGVDYWNoZShvcHRpb25zKTtcclxufTtcclxuXHJcbi8qKlxyXG4gKiBDaGVja3MgdGhlIGNhY2hlIGZvciBIaWdoY2hhcnRzIGRlcGVuZGVuY2llcywgdXBkYXRlcyB0aGUgY2FjaGUgaWYgbmVlZGVkLFxyXG4gKiBhbmQgbG9hZHMgdGhlIHNvdXJjZXMuXHJcbiAqXHJcbiAqIEBwYXJhbSB7T2JqZWN0fSBvcHRpb25zIC0gT2JqZWN0IGNvbnRhaW5pbmcgYWxsIG9wdGlvbnMuXHJcbiAqXHJcbiAqIEByZXR1cm5zIHtQcm9taXNlPHZvaWQ+fSBBIFByb21pc2UgdGhhdCByZXNvbHZlcyBvbmNlIHRoZSBjYWNoZSBpcyBjaGVja2VkXHJcbiAqIGFuZCB1cGRhdGVkLlxyXG4gKlxyXG4gKiBAdGhyb3dzIHtFeHBvcnRFcnJvcn0gVGhyb3dzIGFuIEV4cG9ydEVycm9yIGlmIHRoZXJlIGlzIGFuIGlzc3VlIHVwZGF0aW5nXHJcbiAqIG9yIHJlYWRpbmcgdGhlIGNhY2hlLlxyXG4gKi9cclxuZXhwb3J0IGNvbnN0IGNoZWNrQW5kVXBkYXRlQ2FjaGUgPSBhc3luYyAob3B0aW9ucykgPT4ge1xyXG4gIGNvbnN0IHsgaGlnaGNoYXJ0cywgc2VydmVyIH0gPSBvcHRpb25zO1xyXG4gIGNvbnN0IGNhY2hlUGF0aCA9IGpvaW4oX19kaXJuYW1lLCBoaWdoY2hhcnRzLmNhY2hlUGF0aCk7XHJcblxyXG4gIGxldCBmZXRjaGVkTW9kdWxlcztcclxuICAvLyBQcmVwYXJlIHBhdGhzIHRvIG1hbmlmZXN0IGFuZCBzb3VyY2VzIGZyb20gdGhlIC5jYWNoZSBmb2xkZXJcclxuICBjb25zdCBtYW5pZmVzdFBhdGggPSBqb2luKGNhY2hlUGF0aCwgJ21hbmlmZXN0Lmpzb24nKTtcclxuICBjb25zdCBzb3VyY2VQYXRoID0gam9pbihjYWNoZVBhdGgsICdzb3VyY2VzLmpzJyk7XHJcblxyXG4gIC8vIENyZWF0ZSB0aGUgY2FjaGUgZGVzdGluYXRpb24gaWYgaXQgZG9lc24ndCBleGlzdCBhbHJlYWR5XHJcbiAgIWV4aXN0c1N5bmMoY2FjaGVQYXRoKSAmJiBta2RpclN5bmMoY2FjaGVQYXRoKTtcclxuXHJcbiAgLy8gRmV0Y2ggYWxsIHRoZSBzY3JpcHRzIGVpdGhlciBpZiBtYW5pZmVzdC5qc29uIGRvZXMgbm90IGV4aXN0XHJcbiAgLy8gb3IgaWYgdGhlIGZvcmNlRmV0Y2ggb3B0aW9uIGlzIGVuYWJsZWRcclxuICBpZiAoIWV4aXN0c1N5bmMobWFuaWZlc3RQYXRoKSB8fCBoaWdoY2hhcnRzLmZvcmNlRmV0Y2gpIHtcclxuICAgIGxvZygzLCAnW2NhY2hlXSBGZXRjaGluZyBhbmQgY2FjaGluZyBIaWdoY2hhcnRzIGRlcGVuZGVuY2llcy4nKTtcclxuICAgIGZldGNoZWRNb2R1bGVzID0gYXdhaXQgdXBkYXRlQ2FjaGUoaGlnaGNoYXJ0cywgc2VydmVyLnByb3h5LCBzb3VyY2VQYXRoKTtcclxuICB9IGVsc2Uge1xyXG4gICAgbGV0IHJlcXVlc3RVcGRhdGUgPSBmYWxzZTtcclxuXHJcbiAgICAvLyBSZWFkIHRoZSBtYW5pZmVzdCBKU09OXHJcbiAgICBjb25zdCBtYW5pZmVzdCA9IEpTT04ucGFyc2UocmVhZEZpbGVTeW5jKG1hbmlmZXN0UGF0aCkpO1xyXG5cclxuICAgIC8vIENoZWNrIGlmIHRoZSBtb2R1bGVzIGlzIGFuIGFycmF5LCBpZiBzbywgd2UgcmV3cml0ZSBpdCB0byBhIG1hcCB0byBtYWtlXHJcbiAgICAvLyBpdCBlYXNpZXIgdG8gcmVzb2x2ZSBtb2R1bGVzLlxyXG4gICAgaWYgKG1hbmlmZXN0Lm1vZHVsZXMgJiYgQXJyYXkuaXNBcnJheShtYW5pZmVzdC5tb2R1bGVzKSkge1xyXG4gICAgICBjb25zdCBtb2R1bGVNYXAgPSB7fTtcclxuICAgICAgbWFuaWZlc3QubW9kdWxlcy5mb3JFYWNoKChtKSA9PiAobW9kdWxlTWFwW21dID0gMSkpO1xyXG4gICAgICBtYW5pZmVzdC5tb2R1bGVzID0gbW9kdWxlTWFwO1xyXG4gICAgfVxyXG5cclxuICAgIGNvbnN0IHsgY29yZVNjcmlwdHMsIG1vZHVsZVNjcmlwdHMsIGluZGljYXRvclNjcmlwdHMgfSA9IGhpZ2hjaGFydHM7XHJcbiAgICBjb25zdCBudW1iZXJPZk1vZHVsZXMgPVxyXG4gICAgICBjb3JlU2NyaXB0cy5sZW5ndGggKyBtb2R1bGVTY3JpcHRzLmxlbmd0aCArIGluZGljYXRvclNjcmlwdHMubGVuZ3RoO1xyXG5cclxuICAgIC8vIENvbXBhcmUgdGhlIGxvYWRlZCBoaWdoY2hhcnRzIGNvbmZpZyB3aXRoIHRoZSBjb250ZW50cyBpbiBjYWNoZS5cclxuICAgIC8vIElmIHRoZXJlIGFyZSBjaGFuZ2VzLCBmZXRjaCByZXF1ZXN0ZWQgbW9kdWxlcyBhbmQgcHJvZHVjdHMsXHJcbiAgICAvLyBhbmQgYmFrZSB0aGVtIGludG8gYSBnaWFudCBibG9iLiBTYXZlIHRoZSBibG9iLlxyXG4gICAgaWYgKG1hbmlmZXN0LnZlcnNpb24gIT09IGhpZ2hjaGFydHMudmVyc2lvbikge1xyXG4gICAgICBsb2coXHJcbiAgICAgICAgMixcclxuICAgICAgICAnW2NhY2hlXSBBIEhpZ2hjaGFydHMgdmVyc2lvbiBtaXNtYXRjaCBpbiB0aGUgY2FjaGUsIG5lZWQgdG8gcmUtZmV0Y2guJ1xyXG4gICAgICApO1xyXG4gICAgICByZXF1ZXN0VXBkYXRlID0gdHJ1ZTtcclxuICAgIH0gZWxzZSBpZiAoT2JqZWN0LmtleXMobWFuaWZlc3QubW9kdWxlcyB8fCB7fSkubGVuZ3RoICE9PSBudW1iZXJPZk1vZHVsZXMpIHtcclxuICAgICAgbG9nKFxyXG4gICAgICAgIDIsXHJcbiAgICAgICAgJ1tjYWNoZV0gVGhlIGNhY2hlIGFuZCB0aGUgcmVxdWVzdGVkIG1vZHVsZXMgZG8gbm90IG1hdGNoLCBuZWVkIHRvIHJlLWZldGNoLidcclxuICAgICAgKTtcclxuICAgICAgcmVxdWVzdFVwZGF0ZSA9IHRydWU7XHJcbiAgICB9IGVsc2Uge1xyXG4gICAgICAvLyBDaGVjayBlYWNoIG1vZHVsZSwgaWYgYW55dGhpbmcgaXMgbWlzc2luZyByZWZldGNoIGV2ZXJ5dGhpbmdcclxuICAgICAgcmVxdWVzdFVwZGF0ZSA9IChtb2R1bGVTY3JpcHRzIHx8IFtdKS5zb21lKChtb2R1bGVOYW1lKSA9PiB7XHJcbiAgICAgICAgaWYgKCFtYW5pZmVzdC5tb2R1bGVzW21vZHVsZU5hbWVdKSB7XHJcbiAgICAgICAgICBsb2coXHJcbiAgICAgICAgICAgIDIsXHJcbiAgICAgICAgICAgIGBbY2FjaGVdIFRoZSAke21vZHVsZU5hbWV9IGlzIG1pc3NpbmcgaW4gdGhlIGNhY2hlLCBuZWVkIHRvIHJlLWZldGNoLmBcclxuICAgICAgICAgICk7XHJcbiAgICAgICAgICByZXR1cm4gdHJ1ZTtcclxuICAgICAgICB9XHJcbiAgICAgIH0pO1xyXG4gICAgfVxyXG5cclxuICAgIGlmIChyZXF1ZXN0VXBkYXRlKSB7XHJcbiAgICAgIGZldGNoZWRNb2R1bGVzID0gYXdhaXQgdXBkYXRlQ2FjaGUoaGlnaGNoYXJ0cywgc2VydmVyLnByb3h5LCBzb3VyY2VQYXRoKTtcclxuICAgIH0gZWxzZSB7XHJcbiAgICAgIGxvZygzLCAnW2NhY2hlXSBEZXBlbmRlbmN5IGNhY2hlIGlzIHVwIHRvIGRhdGUsIHByb2NlZWRpbmcuJyk7XHJcblxyXG4gICAgICAvLyBMb2FkIHRoZSBzb3VyY2VzXHJcbiAgICAgIGNhY2hlLnNvdXJjZXMgPSByZWFkRmlsZVN5bmMoc291cmNlUGF0aCwgJ3V0ZjgnKTtcclxuXHJcbiAgICAgIC8vIEdldCBjdXJyZW50IG1vZHVsZXMgbWFwXHJcbiAgICAgIGZldGNoZWRNb2R1bGVzID0gbWFuaWZlc3QubW9kdWxlcztcclxuXHJcbiAgICAgIGNhY2hlLmhjVmVyc2lvbiA9IGV4dHJhY3RWZXJzaW9uKGNhY2hlKTtcclxuICAgIH1cclxuICB9XHJcblxyXG4gIC8vIEZpbmFsbHksIHNhdmUgdGhlIG5ldyBtYW5pZmVzdCwgd2hpY2ggaXMgYmFzaWNhbGx5IG91ciBjdXJyZW50IGNvbmZpZ1xyXG4gIC8vIGluIGEgc2xpZ2h0bHkgZGlmZmVyZW50IGZvcm1hdFxyXG4gIGF3YWl0IHNhdmVDb25maWdUb01hbmlmZXN0KGhpZ2hjaGFydHMsIGZldGNoZWRNb2R1bGVzKTtcclxufTtcclxuXHJcbmV4cG9ydCBjb25zdCBnZXRDYWNoZVBhdGggPSAoKSA9PlxyXG4gIGpvaW4oX19kaXJuYW1lLCBnZXRPcHRpb25zKCkuaGlnaGNoYXJ0cy5jYWNoZVBhdGgpO1xyXG5cclxuZXhwb3J0IGNvbnN0IGdldENhY2hlID0gKCkgPT4gY2FjaGU7XHJcblxyXG5leHBvcnQgY29uc3QgaGlnaGNoYXJ0cyA9ICgpID0+IGNhY2hlLnNvdXJjZXM7XHJcblxyXG5leHBvcnQgY29uc3QgdmVyc2lvbiA9ICgpID0+IGNhY2hlLmhjVmVyc2lvbjtcclxuXHJcbmV4cG9ydCBkZWZhdWx0IHtcclxuICBjaGVja0FuZFVwZGF0ZUNhY2hlLFxyXG4gIGdldENhY2hlUGF0aCxcclxuICB1cGRhdGVWZXJzaW9uLFxyXG4gIGdldENhY2hlLFxyXG4gIGhpZ2hjaGFydHMsXHJcbiAgdmVyc2lvblxyXG59O1xyXG4iLCIvKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKlxyXG5cclxuSGlnaGNoYXJ0cyBFeHBvcnQgU2VydmVyXHJcblxyXG5Db3B5cmlnaHQgKGMpIDIwMTYtMjAyNCwgSGlnaHNvZnRcclxuXHJcbkxpY2VuY2VkIHVuZGVyIHRoZSBNSVQgbGljZW5jZS5cclxuXHJcbkFkZGl0aW9uYWxseSBhIHZhbGlkIEhpZ2hjaGFydHMgbGljZW5zZSBpcyByZXF1aXJlZCBmb3IgdXNlLlxyXG5cclxuU2VlIExJQ0VOU0UgZmlsZSBpbiByb290IGZvciBkZXRhaWxzLlxyXG5cclxuKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKi9cclxuXHJcbi8qIGVzbGludC1kaXNhYmxlIG5vLXVuZGVmICovXHJcblxyXG4vKipcclxuICogU2V0dGluZyB0aGUgYW5pbU9iamVjdC4gQ2FsbGVkIHdoZW4gaW5pdGluZyB0aGUgcGFnZS5cclxuICovXHJcbmV4cG9ydCBmdW5jdGlvbiBzZXR1cEhpZ2hjaGFydHMoKSB7XHJcbiAgSGlnaGNoYXJ0cy5hbmltT2JqZWN0ID0gZnVuY3Rpb24gKCkge1xyXG4gICAgcmV0dXJuIHsgZHVyYXRpb246IDAgfTtcclxuICB9O1xyXG59XHJcblxyXG4vKipcclxuICogQ3JlYXRlcyB0aGUgYWN0dWFsIGNoYXJ0LlxyXG4gKlxyXG4gKiBAcGFyYW0ge29iamVjdH0gY2hhcnRPcHRpb25zIC0gVGhlIG9wdGlvbnMgZm9yIHRoZSBIaWdoY2hhcnRzIGNoYXJ0LlxyXG4gKiBAcGFyYW0ge29iamVjdH0gb3B0aW9ucyAtIFRoZSBleHBvcnQgb3B0aW9ucy5cclxuICogQHBhcmFtIHtib29sZWFufSBkaXNwbGF5RXJyb3JzIC0gQSBmbGFnIGluZGljYXRpbmcgd2hldGhlciB0byBkaXNwbGF5IGVycm9ycy5cclxuICovXHJcbmV4cG9ydCBhc3luYyBmdW5jdGlvbiB0cmlnZ2VyRXhwb3J0KGNoYXJ0T3B0aW9ucywgb3B0aW9ucywgZGlzcGxheUVycm9ycykge1xyXG4gIC8vIERpc3BsYXkgZXJyb3JzIGZsYWcgdGFrZW4gZnJvbSBjaGFydCBvcHRpb25zIG5hZCBkZWJ1Z2dlciBtb2R1bGVcclxuICB3aW5kb3cuX2Rpc3BsYXlFcnJvcnMgPSBkaXNwbGF5RXJyb3JzO1xyXG5cclxuICAvLyBHZXQgcmVxdWlyZWQgZnVuY3Rpb25zXHJcbiAgY29uc3QgeyBnZXRPcHRpb25zLCBtZXJnZSwgc2V0T3B0aW9ucywgd3JhcCB9ID0gSGlnaGNoYXJ0cztcclxuXHJcbiAgLy8gQ3JlYXRlIGEgc2VwYXJhdGUgb2JqZWN0IGZvciBhIHBvdGVudGlhbCBzZXRPcHRpb25zIHVzYWdlcyBpbiBvcmRlciB0b1xyXG4gIC8vIHByZXZlbnQgZnJvbSBwb2xsdXRpbmcgb3RoZXIgZXhwb3J0cyB0aGF0IGNhbiBoYXBwZW4gb24gdGhlIHNhbWUgcGFnZVxyXG4gIEhpZ2hjaGFydHMuc2V0T3B0aW9uc09iaiA9IG1lcmdlKGZhbHNlLCB7fSwgZ2V0T3B0aW9ucygpKTtcclxuXHJcbiAgLy8gQnkgZGVmYXVsdCBhbmltYXRpb24gaXMgZGlzYWJsZWRcclxuICBjb25zdCBjaGFydCA9IHtcclxuICAgIGFuaW1hdGlvbjogZmFsc2VcclxuICB9O1xyXG5cclxuICAvLyBXaGVuIHN0cmFpZ2h0IGluamVjdCwgdGhlIHNpemUgaXMgc2V0IHRocm91Z2ggQ1NTIG9ubHlcclxuICBpZiAob3B0aW9ucy5leHBvcnQuc3RySW5qKSB7XHJcbiAgICBjaGFydC5oZWlnaHQgPSBjaGFydE9wdGlvbnMuY2hhcnQuaGVpZ2h0O1xyXG4gICAgY2hhcnQud2lkdGggPSBjaGFydE9wdGlvbnMuY2hhcnQud2lkdGg7XHJcbiAgfVxyXG5cclxuICAvLyBOT1RFOiBJcyB0aGlzIHVzZWQgZm9yIGFueXRoaW5nIHVzZWZ1bD9cclxuICB3aW5kb3cuaXNSZW5kZXJDb21wbGV0ZSA9IGZhbHNlO1xyXG4gIHdyYXAoSGlnaGNoYXJ0cy5DaGFydC5wcm90b3R5cGUsICdpbml0JywgZnVuY3Rpb24gKHByb2NlZWQsIHVzZXJPcHRpb25zLCBjYikge1xyXG4gICAgLy8gT3ZlcnJpZGUgdXNlck9wdGlvbnMgd2l0aCBpbWFnZSBmcmllbmRseSBvcHRpb25zXHJcbiAgICB1c2VyT3B0aW9ucyA9IG1lcmdlKHVzZXJPcHRpb25zLCB7XHJcbiAgICAgIGV4cG9ydGluZzoge1xyXG4gICAgICAgIGVuYWJsZWQ6IGZhbHNlXHJcbiAgICAgIH0sXHJcbiAgICAgIHBsb3RPcHRpb25zOiB7XHJcbiAgICAgICAgc2VyaWVzOiB7XHJcbiAgICAgICAgICBsYWJlbDoge1xyXG4gICAgICAgICAgICBlbmFibGVkOiBmYWxzZVxyXG4gICAgICAgICAgfVxyXG4gICAgICAgIH1cclxuICAgICAgfSxcclxuICAgICAgLyogRXhwZWN0cyB0b29sdGlwIGluIHVzZXJPcHRpb25zIHdoZW4gZm9yRXhwb3J0IGlzIHRydWUuXHJcbiAgICAgICAgaHR0cHM6Ly9naXRodWIuY29tL2hpZ2hjaGFydHMvaGlnaGNoYXJ0cy9ibG9iLzNhZDQzMGEzNTNiODA1NmI5ZTc2NGFhNGU1Y2Q2ODI4YWE0NzlkYjIvanMvcGFydHMvQ2hhcnQuanMjTDI0MVxyXG4gICAgICAgICovXHJcbiAgICAgIHRvb2x0aXA6IHt9XHJcbiAgICB9KTtcclxuXHJcbiAgICAodXNlck9wdGlvbnMuc2VyaWVzIHx8IFtdKS5mb3JFYWNoKGZ1bmN0aW9uIChzZXJpZXMpIHtcclxuICAgICAgc2VyaWVzLmFuaW1hdGlvbiA9IGZhbHNlO1xyXG4gICAgfSk7XHJcblxyXG4gICAgLy8gQWRkIGZsYWcgdG8ga25vdyBpZiBjaGFydCByZW5kZXIgaGFzIGJlZW4gY2FsbGVkLlxyXG4gICAgaWYgKCF3aW5kb3cub25IaWdoY2hhcnRzUmVuZGVyKSB7XHJcbiAgICAgIHdpbmRvdy5vbkhpZ2hjaGFydHNSZW5kZXIgPSBIaWdoY2hhcnRzLmFkZEV2ZW50KHRoaXMsICdyZW5kZXInLCAoKSA9PiB7XHJcbiAgICAgICAgd2luZG93LmlzUmVuZGVyQ29tcGxldGUgPSB0cnVlO1xyXG4gICAgICB9KTtcclxuICAgIH1cclxuXHJcbiAgICBwcm9jZWVkLmFwcGx5KHRoaXMsIFt1c2VyT3B0aW9ucywgY2JdKTtcclxuICB9KTtcclxuXHJcbiAgd3JhcChIaWdoY2hhcnRzLlNlcmllcy5wcm90b3R5cGUsICdpbml0JywgZnVuY3Rpb24gKHByb2NlZWQsIGNoYXJ0LCBvcHRpb25zKSB7XHJcbiAgICBwcm9jZWVkLmFwcGx5KHRoaXMsIFtjaGFydCwgb3B0aW9uc10pO1xyXG4gIH0pO1xyXG5cclxuICAvLyBHZXQgdGhlIHVzZXIgb3B0aW9uc1xyXG4gIGNvbnN0IHVzZXJPcHRpb25zID0gb3B0aW9ucy5leHBvcnQuc3RySW5qXHJcbiAgICA/IG5ldyBGdW5jdGlvbihgcmV0dXJuICR7b3B0aW9ucy5leHBvcnQuc3RySW5qfWApKClcclxuICAgIDogY2hhcnRPcHRpb25zO1xyXG5cclxuICAvLyBUcmlnZ2VyIGN1c3RvbSBjb2RlXHJcbiAgaWYgKG9wdGlvbnMuY3VzdG9tTG9naWMuY3VzdG9tQ29kZSkge1xyXG4gICAgbmV3IEZ1bmN0aW9uKCdvcHRpb25zJywgb3B0aW9ucy5jdXN0b21Mb2dpYy5jdXN0b21Db2RlKSh1c2VyT3B0aW9ucyk7XHJcbiAgfVxyXG5cclxuICAvLyBNZXJnZSB0aGUgZ2xvYmFsT3B0aW9ucywgdGhlbWVPcHRpb25zLCBvcHRpb25zIGZyb20gdGhlIHdyYXBwZWRcclxuICAvLyBzZXRPcHRpb25zIGZ1bmN0aW9uIGFuZCB1c2VyIG9wdGlvbnMgdG8gY3JlYXRlIHRoZSBmaW5hbCBvcHRpb25zIG9iamVjdFxyXG4gIGNvbnN0IGZpbmFsT3B0aW9ucyA9IG1lcmdlKFxyXG4gICAgZmFsc2UsXHJcbiAgICBKU09OLnBhcnNlKG9wdGlvbnMuZXhwb3J0LnRoZW1lT3B0aW9ucyksXHJcbiAgICB1c2VyT3B0aW9ucyxcclxuICAgIC8vIFBsYWNlZCBpdCBoZXJlIGluc3RlYWQgaW4gdGhlIGluaXQgYmVjYXVzZSBvZiB0aGUgc2l6ZSBpc3N1ZXNcclxuICAgIHsgY2hhcnQgfVxyXG4gICk7XHJcblxyXG4gIGNvbnN0IGZpbmFsQ2FsbGJhY2sgPSBvcHRpb25zLmN1c3RvbUxvZ2ljLmNhbGxiYWNrXHJcbiAgICA/IG5ldyBGdW5jdGlvbihgcmV0dXJuICR7b3B0aW9ucy5jdXN0b21Mb2dpYy5jYWxsYmFja31gKSgpXHJcbiAgICA6IHVuZGVmaW5lZDtcclxuXHJcbiAgLy8gU2V0IHRoZSBnbG9iYWwgb3B0aW9ucyBpZiBleGlzdFxyXG4gIGNvbnN0IGdsb2JhbE9wdGlvbnMgPSBKU09OLnBhcnNlKG9wdGlvbnMuZXhwb3J0Lmdsb2JhbE9wdGlvbnMpO1xyXG4gIGlmIChnbG9iYWxPcHRpb25zKSB7XHJcbiAgICBzZXRPcHRpb25zKGdsb2JhbE9wdGlvbnMpO1xyXG4gIH1cclxuXHJcbiAgbGV0IGNvbnN0ciA9IG9wdGlvbnMuZXhwb3J0LmNvbnN0ciB8fCAnY2hhcnQnO1xyXG4gIGNvbnN0ciA9IHR5cGVvZiBIaWdoY2hhcnRzW2NvbnN0cl0gIT09ICd1bmRlZmluZWQnID8gY29uc3RyIDogJ2NoYXJ0JztcclxuXHJcbiAgSGlnaGNoYXJ0c1tjb25zdHJdKCdjb250YWluZXInLCBmaW5hbE9wdGlvbnMsIGZpbmFsQ2FsbGJhY2spO1xyXG5cclxuICAvLyBHZXQgdGhlIGN1cnJlbnQgZ2xvYmFsIG9wdGlvbnNcclxuICBjb25zdCBkZWZhdWx0T3B0aW9ucyA9IGdldE9wdGlvbnMoKTtcclxuXHJcbiAgLy8gQ2xlYXIgaXQganVzdCBpbiBjYXNlIChlLmcuIHRoZSBzZXRPcHRpb25zIHdhcyB1c2VkIGluIHRoZSBjdXN0b21Db2RlKVxyXG4gIGZvciAoY29uc3QgcHJvcCBpbiBkZWZhdWx0T3B0aW9ucykge1xyXG4gICAgaWYgKHR5cGVvZiBkZWZhdWx0T3B0aW9uc1twcm9wXSAhPT0gJ2Z1bmN0aW9uJykge1xyXG4gICAgICBkZWxldGUgZGVmYXVsdE9wdGlvbnNbcHJvcF07XHJcbiAgICB9XHJcbiAgfVxyXG5cclxuICAvLyBTZXQgdGhlIGRlZmF1bHQgb3B0aW9ucyBiYWNrXHJcbiAgc2V0T3B0aW9ucyhIaWdoY2hhcnRzLnNldE9wdGlvbnNPYmopO1xyXG5cclxuICAvLyBFbXB0eSB0aGUgY3VzdG9tIGdsb2JhbCBvcHRpb25zIG9iamVjdFxyXG4gIEhpZ2hjaGFydHMuc2V0T3B0aW9uc09iaiA9IHt9O1xyXG59XHJcbiIsIi8qKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqXHJcblxyXG5IaWdoY2hhcnRzIEV4cG9ydCBTZXJ2ZXJcclxuXHJcbkNvcHlyaWdodCAoYykgMjAxNi0yMDI0LCBIaWdoc29mdFxyXG5cclxuTGljZW5jZWQgdW5kZXIgdGhlIE1JVCBsaWNlbmNlLlxyXG5cclxuQWRkaXRpb25hbGx5IGEgdmFsaWQgSGlnaGNoYXJ0cyBsaWNlbnNlIGlzIHJlcXVpcmVkIGZvciB1c2UuXHJcblxyXG5TZWUgTElDRU5TRSBmaWxlIGluIHJvb3QgZm9yIGRldGFpbHMuXHJcblxyXG4qKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqL1xyXG5cclxuaW1wb3J0IHsgcmVhZEZpbGVTeW5jIH0gZnJvbSAnZnMnO1xyXG5pbXBvcnQgcGF0aCBmcm9tICdwYXRoJztcclxuXHJcbmltcG9ydCBwdXBwZXRlZXIgZnJvbSAncHVwcGV0ZWVyJztcclxuXHJcbmltcG9ydCB7IGdldENhY2hlUGF0aCB9IGZyb20gJy4vY2FjaGUuanMnO1xyXG5pbXBvcnQgeyBnZXRPcHRpb25zIH0gZnJvbSAnLi9jb25maWcuanMnO1xyXG5pbXBvcnQgeyBzZXR1cEhpZ2hjaGFydHMgfSBmcm9tICcuL2hpZ2hjaGFydHMuanMnO1xyXG5pbXBvcnQgeyBsb2csIGxvZ1dpdGhTdGFjayB9IGZyb20gJy4vbG9nZ2VyLmpzJztcclxuaW1wb3J0IHsgX19kaXJuYW1lIH0gZnJvbSAnLi91dGlscy5qcyc7XHJcblxyXG5pbXBvcnQgRXhwb3J0RXJyb3IgZnJvbSAnLi9lcnJvcnMvRXhwb3J0RXJyb3IuanMnO1xyXG5cclxuLy8gR2V0IHRoZSB0ZW1wbGF0ZSBmb3IgdGhlIHBhZ2VcclxuY29uc3QgdGVtcGxhdGUgPSByZWFkRmlsZVN5bmMoX19kaXJuYW1lICsgJy90ZW1wbGF0ZXMvdGVtcGxhdGUuaHRtbCcsICd1dGY4Jyk7XHJcblxyXG5sZXQgYnJvd3NlcjtcclxuXHJcbi8qKlxyXG4gKiBSZXRyaWV2ZXMgdGhlIGV4aXN0aW5nIFB1cHBldGVlciBicm93c2VyIGluc3RhbmNlLlxyXG4gKlxyXG4gKiBAcmV0dXJucyB7UHJvbWlzZTxvYmplY3Q+fSBBIFByb21pc2UgcmVzb2x2aW5nIHRvIHRoZSBQdXBwZXRlZXIgYnJvd3NlclxyXG4gKiBpbnN0YW5jZS5cclxuICpcclxuICogQHRocm93cyB7RXhwb3J0RXJyb3J9IFRocm93cyBhbiBFeHBvcnRFcnJvciBpZiBubyB2YWxpZCBicm93c2VyIGhhcyBiZWVuXHJcbiAqIGNyZWF0ZWQuXHJcbiAqL1xyXG5leHBvcnQgZnVuY3Rpb24gZ2V0KCkge1xyXG4gIGlmICghYnJvd3Nlcikge1xyXG4gICAgdGhyb3cgbmV3IEV4cG9ydEVycm9yKCdbYnJvd3Nlcl0gTm8gdmFsaWQgYnJvd3NlciBoYXMgYmVlbiBjcmVhdGVkLicpO1xyXG4gIH1cclxuICByZXR1cm4gYnJvd3NlcjtcclxufVxyXG5cclxuLyoqXHJcbiAqIENyZWF0ZXMgYSBQdXBwZXRlZXIgYnJvd3NlciBpbnN0YW5jZSB3aXRoIHRoZSBzcGVjaWZpZWQgYXJndW1lbnRzLlxyXG4gKlxyXG4gKiBAcGFyYW0ge0FycmF5fSBwdXBwZXRlZXJBcmdzIC0gQWRkaXRpb25hbCBhcmd1bWVudHMgZm9yIFB1cHBldGVlciBsYXVuY2guXHJcbiAqXHJcbiAqIEByZXR1cm5zIHtQcm9taXNlPG9iamVjdD59IEEgUHJvbWlzZSByZXNvbHZpbmcgdG8gdGhlIFB1cHBldGVlciBicm93c2VyXHJcbiAqIGluc3RhbmNlLlxyXG4gKlxyXG4gKiBAdGhyb3dzIHtFeHBvcnRFcnJvcn0gVGhyb3dzIGFuIEV4cG9ydEVycm9yIGlmIG1heCByZXRyaWVzIHRvIG9wZW4gYSBicm93c2VyXHJcbiAqIGluc3RhbmNlIGFyZSByZWFjaGVkLCBvciBpZiBubyBicm93c2VyIGluc3RhbmNlIGlzIGZvdW5kIGFmdGVyIHJldHJpZXMuXHJcbiAqL1xyXG5leHBvcnQgYXN5bmMgZnVuY3Rpb24gY3JlYXRlKHB1cHBldGVlckFyZ3MpIHtcclxuICAvLyBHZXQgZGVidWcgYW5kIG90aGVyIG9wdGlvbnNcclxuICBjb25zdCB7IHB1cHBldGVlcjogcHVwcGV0ZWVyT3B0aW9ucywgZGVidWcsIG90aGVyIH0gPSBnZXRPcHRpb25zKCk7XHJcblxyXG4gIC8vIEdldCB0aGUgZGVidWcgb3B0aW9uc1xyXG4gIGNvbnN0IHsgZW5hYmxlOiBlbmFibGVkRGVidWcsIC4uLmRlYnVnT3B0aW9ucyB9ID0gZGVidWc7XHJcblxyXG4gIGNvbnN0IGxhdW5jaE9wdGlvbnMgPSB7XHJcbiAgICBoZWFkbGVzczogb3RoZXIuYnJvd3NlclNoZWxsTW9kZSA/ICdzaGVsbCcgOiB0cnVlLFxyXG4gICAgdXNlckRhdGFEaXI6IHB1cHBldGVlck9wdGlvbnMudGVtcERpciB8fCAnLi90bXAvJyxcclxuICAgIGFyZ3M6IHB1cHBldGVlckFyZ3MsXHJcbiAgICBoYW5kbGVTSUdJTlQ6IGZhbHNlLFxyXG4gICAgaGFuZGxlU0lHVEVSTTogZmFsc2UsXHJcbiAgICBoYW5kbGVTSUdIVVA6IGZhbHNlLFxyXG4gICAgd2FpdEZvckluaXRpYWxQYWdlOiBmYWxzZSxcclxuICAgIGRlZmF1bHRWaWV3cG9ydDogbnVsbCxcclxuICAgIC4uLihlbmFibGVkRGVidWcgJiYgZGVidWdPcHRpb25zKVxyXG4gIH07XHJcblxyXG4gIC8vIENyZWF0ZSBhIGJyb3dzZXJcclxuICBpZiAoIWJyb3dzZXIpIHtcclxuICAgIGNvbnN0IG1heFRyaWVzID0gMjU7XHJcbiAgICBsZXQgdHJ5Q291bnQgPSAwO1xyXG5cclxuICAgIGNvbnN0IG9wZW4gPSBhc3luYyAoKSA9PiB7XHJcbiAgICAgIHRyeSB7XHJcbiAgICAgICAgbG9nKFxyXG4gICAgICAgICAgMyxcclxuICAgICAgICAgIGBbYnJvd3Nlcl0gQXR0ZW1wdGluZyB0byBnZXQgYSBicm93c2VyIGluc3RhbmNlICh0cnkgJHsrK3RyeUNvdW50fSkuYFxyXG4gICAgICAgICk7XHJcbiAgICAgICAgYnJvd3NlciA9IGF3YWl0IHB1cHBldGVlci5sYXVuY2gobGF1bmNoT3B0aW9ucyk7XHJcbiAgICAgIH0gY2F0Y2ggKGVycm9yKSB7XHJcbiAgICAgICAgLy8gVGhpcyBpc24ndCBhIGZ1bGwgZXJyb3IgeWV0IGFzIHB1cHBldGVlciBzb21ldGltZXMgdGFrZXMgdGltZSB0b1xyXG4gICAgICAgIC8vIGluaXRpYWxpemUgcHJvcGVybHkuXHJcbiAgICAgICAgbG9nV2l0aFN0YWNrKFxyXG4gICAgICAgICAgMixcclxuICAgICAgICAgIGVycm9yLFxyXG4gICAgICAgICAgYFticm93c2VyXSBGYWlsZWQgdG8gbGF1bmNoIGEgYnJvd3NlciBpbnN0YW5jZSAtIHJldHJ5aW5nIChhdHRlbXB0ICR7dHJ5Q291bnR9LyR7bWF4VHJpZXN9KS5gXHJcbiAgICAgICAgKTtcclxuXHJcbiAgICAgICAgLy8gUmV0cnkgdG8gbGF1bmNoIGJyb3dzZXIgdW50aWwgcmVhY2hpbmcgbWF4IGF0dGVtcHRzXHJcbiAgICAgICAgaWYgKHRyeUNvdW50IDwgMjUpIHtcclxuICAgICAgICAgIGxvZyhcclxuICAgICAgICAgICAgMyxcclxuICAgICAgICAgICAgYFticm93c2VyXSBSZXRyeSB0byBvcGVuIGEgYnJvd3NlciAoYXR0ZW1wdCAke3RyeUNvdW50fS8ke21heFRyaWVzfSkuYFxyXG4gICAgICAgICAgKTtcclxuICAgICAgICAgIGF3YWl0IG5ldyBQcm9taXNlKChyZXNwb25zZSkgPT4gc2V0VGltZW91dChyZXNwb25zZSwgNDAwMCkpO1xyXG4gICAgICAgICAgYXdhaXQgb3BlbigpO1xyXG4gICAgICAgIH0gZWxzZSB7XHJcbiAgICAgICAgICAvLy4uLiBub3cgaXQncyBhbiBlcnJvciwgd2hpY2ggaXMgY2F1Z2h0IGJ5IHRoZSBjYWxsZXJcclxuICAgICAgICAgIHRocm93IGVycm9yO1xyXG4gICAgICAgIH1cclxuICAgICAgfVxyXG4gICAgfTtcclxuXHJcbiAgICB0cnkge1xyXG4gICAgICBhd2FpdCBvcGVuKCk7XHJcblxyXG4gICAgICAvLyBTaGVsbCBtb2RlIGluZm9ybVxyXG4gICAgICBpZiAobGF1bmNoT3B0aW9ucy5oZWFkbGVzcyA9PT0gJ3NoZWxsJykge1xyXG4gICAgICAgIGxvZygzLCBgW2Jyb3dzZXJdIExhdW5jaGVkIGJyb3dzZXIgaW4gc2hlbGwgbW9kZS5gKTtcclxuICAgICAgfVxyXG5cclxuICAgICAgLy8gRGVidWcgbW9kZSBpbmZvcm1cclxuICAgICAgaWYgKGVuYWJsZWREZWJ1Zykge1xyXG4gICAgICAgIGxvZygzLCBgW2Jyb3dzZXJdIExhdW5jaGVkIGJyb3dzZXIgaW4gZGVidWcgbW9kZS5gKTtcclxuICAgICAgfVxyXG4gICAgfSBjYXRjaCAoZXJyb3IpIHtcclxuICAgICAgdGhyb3cgbmV3IEV4cG9ydEVycm9yKFxyXG4gICAgICAgICdbYnJvd3Nlcl0gTWF4aW11bSByZXRyaWVzIHRvIG9wZW4gYSBicm93c2VyIGluc3RhbmNlIHJlYWNoZWQuJ1xyXG4gICAgICApLnNldEVycm9yKGVycm9yKTtcclxuICAgIH1cclxuXHJcbiAgICBpZiAoIWJyb3dzZXIpIHtcclxuICAgICAgdGhyb3cgbmV3IEV4cG9ydEVycm9yKCdbYnJvd3Nlcl0gQ2Fubm90IGZpbmQgYSBicm93c2VyIHRvIG9wZW4uJyk7XHJcbiAgICB9XHJcbiAgfVxyXG5cclxuICAvLyBSZXR1cm4gYSBicm93c2VyIHByb21pc2VcclxuICByZXR1cm4gYnJvd3NlcjtcclxufVxyXG5cclxuLyoqXHJcbiAqIENsb3NlcyB0aGUgUHVwcGV0ZWVyIGJyb3dzZXIgaW5zdGFuY2UgaWYgaXQgaXMgY29ubmVjdGVkLlxyXG4gKlxyXG4gKiBAcmV0dXJucyB7UHJvbWlzZTxib29sZWFuPn0gQSBQcm9taXNlIHJlc29sdmluZyB0byB0cnVlIGFmdGVyIHRoZSBicm93c2VyXHJcbiAqIGlzIGNsb3NlZC5cclxuICovXHJcbmV4cG9ydCBhc3luYyBmdW5jdGlvbiBjbG9zZSgpIHtcclxuICAvLyBDbG9zZSB0aGUgYnJvd3NlciB3aGVuIGNvbm5uZWN0ZWRcclxuICBpZiAoYnJvd3Nlcj8uY29ubmVjdGVkKSB7XHJcbiAgICBhd2FpdCBicm93c2VyLmNsb3NlKCk7XHJcbiAgfVxyXG4gIGxvZyg0LCAnW2Jyb3dzZXJdIENsb3NlZCB0aGUgYnJvd3Nlci4nKTtcclxufVxyXG5cclxuLyoqXHJcbiAqIENyZWF0ZXMgYSBuZXcgUHVwcGV0ZWVyIFBhZ2Ugd2l0aGluIGFuIGV4aXN0aW5nIGJyb3dzZXIgaW5zdGFuY2UuXHJcbiAqXHJcbiAqIElmIHRoZSBicm93c2VyIGluc3RhbmNlIGlzIG5vdCBhdmFpbGFibGUsIHJldHVybnMgZmFsc2UuXHJcbiAqXHJcbiAqIFRoZSBmdW5jdGlvbiBjcmVhdGVzIGEgbmV3IHBhZ2UsIGRpc2FibGVzIGNhY2hpbmcsIHNldHMgY29udGVudCB1c2luZ1xyXG4gKiBzZXRQYWdlQ29udGVudCgpLCBhbmQgcmV0dXJucyB0aGUgY3JlYXRlZCBQdXBwZXRlZXIgUGFnZS5cclxuICpcclxuICogQHJldHVybnMgeyhib29sZWFufG9iamVjdCl9IFJldHVybnMgZmFsc2UgaWYgdGhlIGJyb3dzZXIgaW5zdGFuY2UgaXMgbm90XHJcbiAqIGF2YWlsYWJsZSwgb3IgYSBQdXBwZXRlZXIgUGFnZSBvYmplY3QgcmVwcmVzZW50aW5nIHRoZSBuZXdseSBjcmVhdGVkIHBhZ2UuXHJcbiAqL1xyXG5leHBvcnQgYXN5bmMgZnVuY3Rpb24gbmV3UGFnZSgpIHtcclxuICBpZiAoIWJyb3dzZXIpIHtcclxuICAgIHJldHVybiBmYWxzZTtcclxuICB9XHJcblxyXG4gIC8vIENyZWF0ZSBhIHBhZ2VcclxuICBjb25zdCBwYWdlID0gYXdhaXQgYnJvd3Nlci5uZXdQYWdlKCk7XHJcblxyXG4gIC8vIERpc2FibGUgY2FjaGVcclxuICBhd2FpdCBwYWdlLnNldENhY2hlRW5hYmxlZChmYWxzZSk7XHJcblxyXG4gIC8vIFNldCB0aGUgY29udGVudFxyXG4gIGF3YWl0IHNldFBhZ2VDb250ZW50KHBhZ2UpO1xyXG5cclxuICAvLyBTZXQgcGFnZSBldmVudHNcclxuICBzZXRQYWdlRXZlbnRzKHBhZ2UpO1xyXG5cclxuICByZXR1cm4gcGFnZTtcclxufVxyXG5cclxuLyoqXHJcbiAqIENsZWFycyB0aGUgY29udGVudCBvZiBhIFB1cHBldGVlciBQYWdlIGJhc2VkIG9uIHRoZSBzcGVjaWZpZWQgbW9kZS5cclxuICpcclxuICogQHBhcmFtIHtPYmplY3R9IHBhZ2UgLSBUaGUgUHVwcGV0ZWVyIFBhZ2Ugb2JqZWN0IHRvIGJlIGNsZWFyZWQuXHJcbiAqIEBwYXJhbSB7Ym9vbGVhbn0gaGFyZFJlc2V0IC0gQSBmbGFnIGluZGljYXRpbmcgdGhlIHR5cGUgb2YgY2xlYXJpbmdcclxuICogdG8gYmUgcGVyZm9ybWVkLiBJZiB0cnVlLCBuYXZpZ2F0ZXMgdG8gJ2Fib3V0OmJsYW5rJyBhbmQgcmVzZXRzIGNvbnRlbnRcclxuICogYW5kIHNjcmlwdHMuIElmIGZhbHNlLCBjbGVhcnMgdGhlIGJvZHkgY29udGVudCBieSBzZXR0aW5nIGEgcHJlZGVmaW5lZCBIVE1MXHJcbiAqIHN0cnVjdHVyZS5cclxuICpcclxuICogQHRocm93cyB7RXJyb3J9IExvZ3MgdGhyb3duIGVycm9yIGlmIGNsZWFyaW5nIHRoZSBwYWdlIGNvbnRlbnQgZmFpbHMuXHJcbiAqL1xyXG5leHBvcnQgYXN5bmMgZnVuY3Rpb24gY2xlYXJQYWdlKHBhZ2UsIGhhcmRSZXNldCA9IGZhbHNlKSB7XHJcbiAgdHJ5IHtcclxuICAgIGlmIChwYWdlICYmICFwYWdlLmlzQ2xvc2VkKCkpIHtcclxuICAgICAgaWYgKGhhcmRSZXNldCkge1xyXG4gICAgICAgIC8vIE5hdmlnYXRlIHRvIGFib3V0OmJsYW5rXHJcbiAgICAgICAgYXdhaXQgcGFnZS5nb3RvKCdhYm91dDpibGFuaycsIHsgd2FpdFVudGlsOiAnZG9tY29udGVudGxvYWRlZCcgfSk7XHJcblxyXG4gICAgICAgIC8vIFNldCB0aGUgY29udGVudCBhbmQgYW5kIHNjcmlwdHMgYWdhaW5cclxuICAgICAgICBhd2FpdCBzZXRQYWdlQ29udGVudChwYWdlKTtcclxuICAgICAgfSBlbHNlIHtcclxuICAgICAgICAvLyBDbGVhciBib2R5IGNvbnRlbnRcclxuICAgICAgICBhd2FpdCBwYWdlLmV2YWx1YXRlKCgpID0+IHtcclxuICAgICAgICAgIGRvY3VtZW50LmJvZHkuaW5uZXJIVE1MID1cclxuICAgICAgICAgICAgJzxkaXYgaWQ9XCJjaGFydC1jb250YWluZXJcIj48ZGl2IGlkPVwiY29udGFpbmVyXCI+PC9kaXY+PC9kaXY+JztcclxuICAgICAgICB9KTtcclxuICAgICAgfVxyXG4gICAgICByZXR1cm4gdHJ1ZTtcclxuICAgIH1cclxuICB9IGNhdGNoIChlcnJvcikge1xyXG4gICAgbG9nV2l0aFN0YWNrKFxyXG4gICAgICAyLFxyXG4gICAgICBlcnJvcixcclxuICAgICAgJ1ticm93c2VyXSBDb3VsZCBub3QgY2xlYXIgdGhlIGNvbnRlbnQgb2YgdGhlIHBhZ2UuJ1xyXG4gICAgKTtcclxuICB9XHJcblxyXG4gIHJldHVybiBmYWxzZTtcclxufVxyXG5cclxuLyoqXHJcbiAqIEFkZHMgY3VzdG9tIEpTIGFuZCBDU1MgcmVzb3VyY2VzIHRvIGEgUHVwcGV0ZWVyIFBhZ2UgYmFzZWQgb24gdGhlIHNwZWNpZmllZFxyXG4gKiBvcHRpb25zLlxyXG4gKlxyXG4gKiBAcGFyYW0ge09iamVjdH0gcGFnZSAtIFRoZSBQdXBwZXRlZXIgUGFnZSBvYmplY3QgdG8gd2hpY2ggcmVzb3VyY2VzIHdpbGwgYmVcclxuICogYWRkZWQuXHJcbiAqIEBwYXJhbSB7T2JqZWN0fSBvcHRpb25zIC0gQWxsIG9wdGlvbnMgYW5kIGNvbmZpZ3VyYXRpb24uXHJcbiAqXHJcbiAqIEByZXR1cm5zIHtQcm9taXNlPEFycmF5PE9iamVjdD4+fSAtIFByb21pc2UgcmVzb2x2aW5nIHRvIGFuIGFycmF5IG9mIGluamVjdGVkXHJcbiAqIHJlc291cmNlcy5cclxuICovXHJcbmV4cG9ydCBhc3luYyBmdW5jdGlvbiBhZGRQYWdlUmVzb3VyY2VzKHBhZ2UsIG9wdGlvbnMpIHtcclxuICAvLyBJbmplY3RlZCByZXNvdXJjZXMgYXJyYXlcclxuICBjb25zdCBpbmplY3RlZFJlc291cmNlcyA9IFtdO1xyXG5cclxuICAvLyBVc2UgcmVzb3VyY2VzXHJcbiAgY29uc3QgcmVzb3VyY2VzID0gb3B0aW9ucy5jdXN0b21Mb2dpYy5yZXNvdXJjZXM7XHJcbiAgaWYgKHJlc291cmNlcykge1xyXG4gICAgY29uc3QgaW5qZWN0ZWRKcyA9IFtdO1xyXG5cclxuICAgIC8vIExvYWQgY3VzdG9tIEpTIGNvZGVcclxuICAgIGlmIChyZXNvdXJjZXMuanMpIHtcclxuICAgICAgaW5qZWN0ZWRKcy5wdXNoKHtcclxuICAgICAgICBjb250ZW50OiByZXNvdXJjZXMuanNcclxuICAgICAgfSk7XHJcbiAgICB9XHJcblxyXG4gICAgLy8gTG9hZCBzY3JpcHRzIGZyb20gYWxsIGN1c3RvbSBmaWxlc1xyXG4gICAgaWYgKHJlc291cmNlcy5maWxlcykge1xyXG4gICAgICBmb3IgKGNvbnN0IGZpbGUgb2YgcmVzb3VyY2VzLmZpbGVzKSB7XHJcbiAgICAgICAgY29uc3QgaXNMb2NhbCA9ICFmaWxlLnN0YXJ0c1dpdGgoJ2h0dHAnKSA/IHRydWUgOiBmYWxzZTtcclxuXHJcbiAgICAgICAgLy8gQWRkIGVhY2ggY3VzdG9tIHNjcmlwdCBmcm9tIHJlc291cmNlcycgZmlsZXNcclxuICAgICAgICBpbmplY3RlZEpzLnB1c2goXHJcbiAgICAgICAgICBpc0xvY2FsXHJcbiAgICAgICAgICAgID8ge1xyXG4gICAgICAgICAgICAgICAgY29udGVudDogcmVhZEZpbGVTeW5jKGZpbGUsICd1dGY4JylcclxuICAgICAgICAgICAgICB9XHJcbiAgICAgICAgICAgIDoge1xyXG4gICAgICAgICAgICAgICAgdXJsOiBmaWxlXHJcbiAgICAgICAgICAgICAgfVxyXG4gICAgICAgICk7XHJcbiAgICAgIH1cclxuICAgIH1cclxuXHJcbiAgICBmb3IgKGNvbnN0IGpzUmVzb3VyY2Ugb2YgaW5qZWN0ZWRKcykge1xyXG4gICAgICB0cnkge1xyXG4gICAgICAgIGluamVjdGVkUmVzb3VyY2VzLnB1c2goYXdhaXQgcGFnZS5hZGRTY3JpcHRUYWcoanNSZXNvdXJjZSkpO1xyXG4gICAgICB9IGNhdGNoIChlcnJvcikge1xyXG4gICAgICAgIGxvZ1dpdGhTdGFjaygyLCBlcnJvciwgYFtleHBvcnRdIFRoZSBKUyByZXNvdXJjZSBjYW5ub3QgYmUgbG9hZGVkLmApO1xyXG4gICAgICB9XHJcbiAgICB9XHJcbiAgICBpbmplY3RlZEpzLmxlbmd0aCA9IDA7XHJcblxyXG4gICAgLy8gTG9hZCBDU1NcclxuICAgIGNvbnN0IGluamVjdGVkQ3NzID0gW107XHJcbiAgICBpZiAocmVzb3VyY2VzLmNzcykge1xyXG4gICAgICBsZXQgY3NzSW1wb3J0cyA9IHJlc291cmNlcy5jc3MubWF0Y2goL0BpbXBvcnRcXHMqKFteO10qKTsvZyk7XHJcbiAgICAgIGlmIChjc3NJbXBvcnRzKSB7XHJcbiAgICAgICAgLy8gSGFuZGxlIGNzcyBzZWN0aW9uXHJcbiAgICAgICAgZm9yIChsZXQgY3NzSW1wb3J0UGF0aCBvZiBjc3NJbXBvcnRzKSB7XHJcbiAgICAgICAgICBpZiAoY3NzSW1wb3J0UGF0aCkge1xyXG4gICAgICAgICAgICBjc3NJbXBvcnRQYXRoID0gY3NzSW1wb3J0UGF0aFxyXG4gICAgICAgICAgICAgIC5yZXBsYWNlKCd1cmwoJywgJycpXHJcbiAgICAgICAgICAgICAgLnJlcGxhY2UoJ0BpbXBvcnQnLCAnJylcclxuICAgICAgICAgICAgICAucmVwbGFjZSgvXCIvZywgJycpXHJcbiAgICAgICAgICAgICAgLnJlcGxhY2UoLycvZywgJycpXHJcbiAgICAgICAgICAgICAgLnJlcGxhY2UoLzsvLCAnJylcclxuICAgICAgICAgICAgICAucmVwbGFjZSgvXFwpL2csICcnKVxyXG4gICAgICAgICAgICAgIC50cmltKCk7XHJcblxyXG4gICAgICAgICAgICAvLyBBZGQgZWFjaCBjdXN0b20gY3NzIGZyb20gcmVzb3VyY2VzXHJcbiAgICAgICAgICAgIGlmIChjc3NJbXBvcnRQYXRoLnN0YXJ0c1dpdGgoJ2h0dHAnKSkge1xyXG4gICAgICAgICAgICAgIGluamVjdGVkQ3NzLnB1c2goe1xyXG4gICAgICAgICAgICAgICAgdXJsOiBjc3NJbXBvcnRQYXRoXHJcbiAgICAgICAgICAgICAgfSk7XHJcbiAgICAgICAgICAgIH0gZWxzZSBpZiAob3B0aW9ucy5jdXN0b21Mb2dpYy5hbGxvd0ZpbGVSZXNvdXJjZXMpIHtcclxuICAgICAgICAgICAgICBpbmplY3RlZENzcy5wdXNoKHtcclxuICAgICAgICAgICAgICAgIHBhdGg6IHBhdGguam9pbihfX2Rpcm5hbWUsIGNzc0ltcG9ydFBhdGgpXHJcbiAgICAgICAgICAgICAgfSk7XHJcbiAgICAgICAgICAgIH1cclxuICAgICAgICAgIH1cclxuICAgICAgICB9XHJcbiAgICAgIH1cclxuXHJcbiAgICAgIC8vIFRoZSByZXN0IG9mIHRoZSBDU1Mgc2VjdGlvbiB3aWxsIGJlIGNvbnRlbnQgYnkgbm93XHJcbiAgICAgIGluamVjdGVkQ3NzLnB1c2goe1xyXG4gICAgICAgIGNvbnRlbnQ6IHJlc291cmNlcy5jc3MucmVwbGFjZSgvQGltcG9ydFxccyooW147XSopOy9nLCAnJykgfHwgJyAnXHJcbiAgICAgIH0pO1xyXG5cclxuICAgICAgZm9yIChjb25zdCBjc3NSZXNvdXJjZSBvZiBpbmplY3RlZENzcykge1xyXG4gICAgICAgIHRyeSB7XHJcbiAgICAgICAgICBpbmplY3RlZFJlc291cmNlcy5wdXNoKGF3YWl0IHBhZ2UuYWRkU3R5bGVUYWcoY3NzUmVzb3VyY2UpKTtcclxuICAgICAgICB9IGNhdGNoIChlcnJvcikge1xyXG4gICAgICAgICAgbG9nV2l0aFN0YWNrKDIsIGVycm9yLCBgW2V4cG9ydF0gVGhlIENTUyByZXNvdXJjZSBjYW5ub3QgYmUgbG9hZGVkLmApO1xyXG4gICAgICAgIH1cclxuICAgICAgfVxyXG4gICAgICBpbmplY3RlZENzcy5sZW5ndGggPSAwO1xyXG4gICAgfVxyXG4gIH1cclxuICByZXR1cm4gaW5qZWN0ZWRSZXNvdXJjZXM7XHJcbn1cclxuXHJcbi8qKlxyXG4gKiBDbGVhcnMgb3V0IGFsbCBzdGF0ZSBzZXQgb24gdGhlIHBhZ2Ugd2l0aCBhZGRTY3JpcHRUYWcvYWRkU3R5bGVUYWcuIFJlbW92ZXNcclxuICogaW5qZWN0ZWQgcmVzb3VyY2VzIGFuZCByZXNldHMgQ1NTIGFuZCBzY3JpcHQgdGFncyBvbiB0aGUgcGFnZS4gQWRkaXRpb25hbGx5LFxyXG4gKiBpdCBkZXN0cm95cyBwcmV2aW91c2x5IGV4aXN0aW5nIGNoYXJ0cy5cclxuICpcclxuICogQHBhcmFtIHtPYmplY3R9IHBhZ2UgLSBUaGUgUHVwcGV0ZWVyIFBhZ2Ugb2JqZWN0IGZyb20gd2hpY2ggcmVzb3VyY2VzIHdpbGxcclxuICogYmUgY2xlYXJlZC5cclxuICogQHBhcmFtIHtBcnJheTxPYmplY3Q+fSBpbmplY3RlZFJlc291cmNlcyAtIEFycmF5IG9mIGluamVjdGVkIHJlc291cmNlc1xyXG4gKiB0byBiZSBjbGVhcmVkLlxyXG4gKi9cclxuZXhwb3J0IGFzeW5jIGZ1bmN0aW9uIGNsZWFyUGFnZVJlc291cmNlcyhwYWdlLCBpbmplY3RlZFJlc291cmNlcykge1xyXG4gIHRyeSB7XHJcbiAgICBmb3IgKGNvbnN0IHJlc291cmNlIG9mIGluamVjdGVkUmVzb3VyY2VzKSB7XHJcbiAgICAgIGF3YWl0IHJlc291cmNlLmRpc3Bvc2UoKTtcclxuICAgIH1cclxuXHJcbiAgICAvLyBEZXN0cm95IG9sZCBjaGFydHMgYWZ0ZXIgZXhwb3J0IGlzIGRvbmUgYW5kIHJlc2V0IGFsbCBDU1MgYW5kIHNjcmlwdCB0YWdzXHJcbiAgICBhd2FpdCBwYWdlLmV2YWx1YXRlKCgpID0+IHtcclxuICAgICAgLy8gV2UgYXJlIG5vdCBndWFyYW50ZWVkIHRoYXQgSGlnaGNoYXJ0cyBpcyBsb2FkZWQsIGUsZywgd2hlbiBkb2luZyBTVkdcclxuICAgICAgLy8gZXhwb3J0c1xyXG4gICAgICBpZiAodHlwZW9mIEhpZ2hjaGFydHMgIT09ICd1bmRlZmluZWQnKSB7XHJcbiAgICAgICAgLy8gZXNsaW50LWRpc2FibGUtbmV4dC1saW5lIG5vLXVuZGVmXHJcbiAgICAgICAgY29uc3Qgb2xkQ2hhcnRzID0gSGlnaGNoYXJ0cy5jaGFydHM7XHJcblxyXG4gICAgICAgIC8vIENoZWNrIGluIGFueSBhbHJlYWR5IGV4aXN0aW5nIGNoYXJ0c1xyXG4gICAgICAgIGlmIChBcnJheS5pc0FycmF5KG9sZENoYXJ0cykgJiYgb2xkQ2hhcnRzLmxlbmd0aCkge1xyXG4gICAgICAgICAgLy8gRGVzdHJveSBvbGQgY2hhcnRzXHJcbiAgICAgICAgICBmb3IgKGNvbnN0IG9sZENoYXJ0IG9mIG9sZENoYXJ0cykge1xyXG4gICAgICAgICAgICBvbGRDaGFydCAmJiBvbGRDaGFydC5kZXN0cm95KCk7XHJcbiAgICAgICAgICAgIC8vIGVzbGludC1kaXNhYmxlLW5leHQtbGluZSBuby11bmRlZlxyXG4gICAgICAgICAgICBIaWdoY2hhcnRzLmNoYXJ0cy5zaGlmdCgpO1xyXG4gICAgICAgICAgfVxyXG4gICAgICAgIH1cclxuICAgICAgfVxyXG5cclxuICAgICAgLy8gZXNsaW50LWRpc2FibGUtbmV4dC1saW5lIG5vLXVuZGVmXHJcbiAgICAgIGNvbnN0IFsuLi5zY3JpcHRzVG9SZW1vdmVdID0gZG9jdW1lbnQuZ2V0RWxlbWVudHNCeVRhZ05hbWUoJ3NjcmlwdCcpO1xyXG4gICAgICAvLyBlc2xpbnQtZGlzYWJsZS1uZXh0LWxpbmUgbm8tdW5kZWZcclxuICAgICAgY29uc3QgWywgLi4uc3R5bGVzVG9SZW1vdmVdID0gZG9jdW1lbnQuZ2V0RWxlbWVudHNCeVRhZ05hbWUoJ3N0eWxlJyk7XHJcbiAgICAgIC8vIGVzbGludC1kaXNhYmxlLW5leHQtbGluZSBuby11bmRlZlxyXG4gICAgICBjb25zdCBbLi4ubGlua3NUb1JlbW92ZV0gPSBkb2N1bWVudC5nZXRFbGVtZW50c0J5VGFnTmFtZSgnbGluaycpO1xyXG5cclxuICAgICAgLy8gUmVtb3ZlIHRhZ3NcclxuICAgICAgZm9yIChjb25zdCBlbGVtZW50IG9mIFtcclxuICAgICAgICAuLi5zY3JpcHRzVG9SZW1vdmUsXHJcbiAgICAgICAgLi4uc3R5bGVzVG9SZW1vdmUsXHJcbiAgICAgICAgLi4ubGlua3NUb1JlbW92ZVxyXG4gICAgICBdKSB7XHJcbiAgICAgICAgZWxlbWVudC5yZW1vdmUoKTtcclxuICAgICAgfVxyXG4gICAgfSk7XHJcbiAgfSBjYXRjaCAoZXJyb3IpIHtcclxuICAgIGxvZ1dpdGhTdGFjaygyLCBlcnJvciwgYFticm93c2VyXSBDb3VsZCBub3QgY2xlYXIgcGFnZSdzIHJlc291cmNlcy5gKTtcclxuICB9XHJcbn1cclxuXHJcbi8qKlxyXG4gKiBTZXRzIHRoZSBjb250ZW50IGZvciBhIFB1cHBldGVlciBQYWdlIHVzaW5nIGEgcHJlZGVmaW5lZCB0ZW1wbGF0ZVxyXG4gKiBhbmQgYWRkaXRpb25hbCBzY3JpcHRzLiBBbHNvLCBzZXRzIHRoZSBwYWdlZXJyb3IgaW4gb3JkZXIgdG8gY2F0Y2hcclxuICogYW5kIGRpc3BsYXkgZXJyb3JzIGZyb20gdGhlIHdpbmRvdyBjb250ZXh0LlxyXG4gKlxyXG4gKiBAcGFyYW0ge09iamVjdH0gcGFnZSAtIFRoZSBQdXBwZXRlZXIgUGFnZSBvYmplY3QgZm9yIHdoaWNoIHRoZSBjb250ZW50XHJcbiAqIGlzIGJlaW5nIHNldC5cclxuICovXHJcbmFzeW5jIGZ1bmN0aW9uIHNldFBhZ2VDb250ZW50KHBhZ2UpIHtcclxuICBhd2FpdCBwYWdlLnNldENvbnRlbnQodGVtcGxhdGUsIHsgd2FpdFVudGlsOiAnZG9tY29udGVudGxvYWRlZCcgfSk7XHJcblxyXG4gIC8vIEFkZCBhbGwgcmVnaXN0ZXJlZCBIaWdjaGFydHMgc2NyaXB0cywgcXVpdGUgZGVtYW5kaW5nXHJcbiAgYXdhaXQgcGFnZS5hZGRTY3JpcHRUYWcoeyBwYXRoOiBgJHtnZXRDYWNoZVBhdGgoKX0vc291cmNlcy5qc2AgfSk7XHJcblxyXG4gIC8vIFNldCB0aGUgaW5pdGlhbCBhbmltT2JqZWN0XHJcbiAgYXdhaXQgcGFnZS5ldmFsdWF0ZShzZXR1cEhpZ2hjaGFydHMpO1xyXG59XHJcblxyXG4vKipcclxuICogU2V0IGV2ZW50cyBmb3IgYSBQdXBwZXRlZXIgUGFnZS5cclxuICpcclxuICogQHBhcmFtIHtPYmplY3R9IHBhZ2UgLSBUaGUgUHVwcGV0ZWVyIFBhZ2Ugb2JqZWN0IHRvIHNldCBldmVudHMgdG8uXHJcbiAqL1xyXG5mdW5jdGlvbiBzZXRQYWdlRXZlbnRzKHBhZ2UpIHtcclxuICAvLyBHZXQgZGVidWcgb3B0aW9uc1xyXG4gIGNvbnN0IHsgZGVidWcgfSA9IGdldE9wdGlvbnMoKTtcclxuXHJcbiAgLy8gU2V0IHRoZSBjb25zb2xlIGxpc3RlbmVyLCBpZiBuZWVkZWRcclxuICBpZiAoZGVidWcuZW5hYmxlICYmIGRlYnVnLmxpc3RlblRvQ29uc29sZSkge1xyXG4gICAgcGFnZS5vbignY29uc29sZScsIChtZXNzYWdlKSA9PiB7XHJcbiAgICAgIGNvbnNvbGUubG9nKGBbZGVidWddICR7bWVzc2FnZS50ZXh0KCl9YCk7XHJcbiAgICB9KTtcclxuICB9XHJcblxyXG4gIC8vIFNldCB0aGUgcGFnZWVycm9yIGxpc3RlbmVyXHJcbiAgcGFnZS5vbigncGFnZWVycm9yJywgYXN5bmMgKGVycm9yKSA9PiB7XHJcbiAgICAvLyBJdCB3b3VsZCBzZWVtIGxpa2UgdGhpcyBtYXkgZmlyZSBhdCB0aGUgc2FtZSB0aW1lIG9yIHNob3J0bHkgYmVmb3JlXHJcbiAgICAvLyBhIHBhZ2UgaXMgY2xvc2VkLlxyXG4gICAgaWYgKHBhZ2UuaXNDbG9zZWQoKSkge1xyXG4gICAgICByZXR1cm47XHJcbiAgICB9XHJcblxyXG4gICAgLy8gVE9ETzogQ29uc2lkZXIgYWRkaW5nIGEgc3dpdGNoIGhlcmUgdGhhdCB0dXJucyBvbiBsb2coMCkgbG9nZ2luZ1xyXG4gICAgLy8gb24gcGFnZSBlcnJvcnMuXHJcbiAgICBhd2FpdCBwYWdlLiRldmFsKFxyXG4gICAgICAnI2NvbnRhaW5lcicsXHJcbiAgICAgIChlbGVtZW50LCBlcnJvck1lc3NhZ2UpID0+IHtcclxuICAgICAgICAvLyBlc2xpbnQtZGlzYWJsZS1uZXh0LWxpbmUgbm8tdW5kZWZcclxuICAgICAgICBpZiAod2luZG93Ll9kaXNwbGF5RXJyb3JzKSB7XHJcbiAgICAgICAgICBlbGVtZW50LmlubmVySFRNTCA9IGVycm9yTWVzc2FnZTtcclxuICAgICAgICB9XHJcbiAgICAgIH0sXHJcbiAgICAgIGA8aDE+Q2hhcnQgaW5wdXQgZGF0YSBlcnJvcjogPC9oMT4ke2Vycm9yLnRvU3RyaW5nKCl9YFxyXG4gICAgKTtcclxuICB9KTtcclxufVxyXG5cclxuZXhwb3J0IGRlZmF1bHQge1xyXG4gIGdldCxcclxuICBjcmVhdGUsXHJcbiAgY2xvc2UsXHJcbiAgbmV3UGFnZSxcclxuICBjbGVhclBhZ2UsXHJcbiAgYWRkUGFnZVJlc291cmNlcyxcclxuICBjbGVhclBhZ2VSZXNvdXJjZXNcclxufTtcclxuIiwiLyoqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKipcclxuXHJcbkhpZ2hjaGFydHMgRXhwb3J0IFNlcnZlclxyXG5cclxuQ29weXJpZ2h0IChjKSAyMDE2LTIwMjQsIEhpZ2hzb2Z0XHJcblxyXG5MaWNlbmNlZCB1bmRlciB0aGUgTUlUIGxpY2VuY2UuXHJcblxyXG5BZGRpdGlvbmFsbHkgYSB2YWxpZCBIaWdoY2hhcnRzIGxpY2Vuc2UgaXMgcmVxdWlyZWQgZm9yIHVzZS5cclxuXHJcblNlZSBMSUNFTlNFIGZpbGUgaW4gcm9vdCBmb3IgZGV0YWlscy5cclxuXHJcbioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKiovXHJcblxyXG5pbXBvcnQgeyBhZGRQYWdlUmVzb3VyY2VzLCBjbGVhclBhZ2VSZXNvdXJjZXMgfSBmcm9tICcuL2Jyb3dzZXIuanMnO1xyXG5pbXBvcnQgeyBnZXRDYWNoZSB9IGZyb20gJy4vY2FjaGUuanMnO1xyXG5pbXBvcnQgeyB0cmlnZ2VyRXhwb3J0IH0gZnJvbSAnLi9oaWdoY2hhcnRzLmpzJztcclxuaW1wb3J0IHsgbG9nIH0gZnJvbSAnLi9sb2dnZXIuanMnO1xyXG5cclxuaW1wb3J0IHN2Z1RlbXBsYXRlIGZyb20gJy4vLi4vdGVtcGxhdGVzL3N2Z19leHBvcnQvc3ZnX2V4cG9ydC5qcyc7XHJcblxyXG5pbXBvcnQgRXhwb3J0RXJyb3IgZnJvbSAnLi9lcnJvcnMvRXhwb3J0RXJyb3IuanMnO1xyXG5cclxuLyoqXHJcbiAqIFJldHJpZXZlcyB0aGUgY2xpcHBpbmcgcmVnaW9uIGNvb3JkaW5hdGVzIG9mIHRoZSBzcGVjaWZpZWQgcGFnZSBlbGVtZW50IHdpdGhcclxuICogdGhlIGlkICdjaGFydC1jb250YWluZXInLlxyXG4gKlxyXG4gKiBAcGFyYW0ge09iamVjdH0gcGFnZSAtIFB1cHBldGVlciBwYWdlIG9iamVjdC5cclxuICpcclxuICogQHJldHVybnMge1Byb21pc2U8T2JqZWN0Pn0gUHJvbWlzZSByZXNvbHZpbmcgdG8gYW4gb2JqZWN0IGNvbnRhaW5pbmdcclxuICogeCwgeSwgd2lkdGgsIGFuZCBoZWlnaHQgcHJvcGVydGllcy5cclxuICovXHJcbmNvbnN0IGdldENsaXBSZWdpb24gPSAocGFnZSkgPT5cclxuICBwYWdlLiRldmFsKCcjY2hhcnQtY29udGFpbmVyJywgKGVsZW1lbnQpID0+IHtcclxuICAgIGNvbnN0IHsgeCwgeSwgd2lkdGgsIGhlaWdodCB9ID0gZWxlbWVudC5nZXRCb3VuZGluZ0NsaWVudFJlY3QoKTtcclxuICAgIHJldHVybiB7XHJcbiAgICAgIHgsXHJcbiAgICAgIHksXHJcbiAgICAgIHdpZHRoLFxyXG4gICAgICBoZWlnaHQ6IE1hdGgudHJ1bmMoaGVpZ2h0ID4gMSA/IGhlaWdodCA6IDUwMClcclxuICAgIH07XHJcbiAgfSk7XHJcblxyXG4vKipcclxuICogQ3JlYXRlcyBhbiBpbWFnZSB1c2luZyBQdXBwZXRlZXIncyBwYWdlIHNjcmVlbnNob3QgZnVuY3Rpb25hbGl0eSB3aXRoXHJcbiAqIHNwZWNpZmllZCBvcHRpb25zLlxyXG4gKlxyXG4gKiBAcGFyYW0ge09iamVjdH0gcGFnZSAtIFB1cHBldGVlciBwYWdlIG9iamVjdC5cclxuICogQHBhcmFtIHtzdHJpbmd9IHR5cGUgLSBJbWFnZSB0eXBlLlxyXG4gKiBAcGFyYW0ge3N0cmluZ30gZW5jb2RpbmcgLSBJbWFnZSBlbmNvZGluZy5cclxuICogQHBhcmFtIHtPYmplY3R9IGNsaXAgLSBDbGlwcGluZyByZWdpb24gY29vcmRpbmF0ZXMuXHJcbiAqIEBwYXJhbSB7bnVtYmVyfSByYXN0ZXJpemF0aW9uVGltZW91dCAtIFRpbWVvdXQgZm9yIHJhc3Rlcml6YXRpb25cclxuICogaW4gbWlsbGlzZWNvbmRzLlxyXG4gKlxyXG4gKiBAcmV0dXJucyB7UHJvbWlzZTxCdWZmZXI+fSBQcm9taXNlIHJlc29sdmluZyB0byB0aGUgaW1hZ2UgYnVmZmVyIG9yIHJlamVjdGluZ1xyXG4gKiB3aXRoIGFuIEV4cG9ydEVycm9yIGZvciB0aW1lb3V0LlxyXG4gKi9cclxuY29uc3QgY3JlYXRlSW1hZ2UgPSAocGFnZSwgdHlwZSwgZW5jb2RpbmcsIGNsaXAsIHJhc3Rlcml6YXRpb25UaW1lb3V0KSA9PlxyXG4gIFByb21pc2UucmFjZShbXHJcbiAgICBwYWdlLnNjcmVlbnNob3Qoe1xyXG4gICAgICB0eXBlLFxyXG4gICAgICBlbmNvZGluZyxcclxuICAgICAgY2xpcCxcclxuICAgICAgY2FwdHVyZUJleW9uZFZpZXdwb3J0OiB0cnVlLFxyXG4gICAgICBmdWxsUGFnZTogZmFsc2UsXHJcbiAgICAgIG9wdGltaXplRm9yU3BlZWQ6IHRydWUsXHJcbiAgICAgIC4uLih0eXBlICE9PSAncG5nJyA/IHsgcXVhbGl0eTogODAgfSA6IHt9KSxcclxuXHJcbiAgICAgIC8vICM0NDcsICM0NjMgLSBhbHdheXMgcmVuZGVyIG9uIGEgdHJhbnNwYXJlbnQgcGFnZSBpZiB0aGUgZXhwZWN0ZWQgdHlwZVxyXG4gICAgICAvLyBmb3JtYXQgaXMgUE5HXHJcbiAgICAgIG9taXRCYWNrZ3JvdW5kOiB0eXBlID09ICdwbmcnXHJcbiAgICB9KSxcclxuICAgIG5ldyBQcm9taXNlKChfcmVzb2x2ZSwgcmVqZWN0KSA9PlxyXG4gICAgICBzZXRUaW1lb3V0KFxyXG4gICAgICAgICgpID0+IHJlamVjdChuZXcgRXhwb3J0RXJyb3IoJ1Jhc3Rlcml6YXRpb24gdGltZW91dCcpKSxcclxuICAgICAgICByYXN0ZXJpemF0aW9uVGltZW91dCB8fCAxNTAwXHJcbiAgICAgIClcclxuICAgIClcclxuICBdKTtcclxuXHJcbi8qKlxyXG4gKiBDcmVhdGVzIGEgUERGIHVzaW5nIFB1cHBldGVlcidzIHBhZ2UgcGRmIGZ1bmN0aW9uYWxpdHkgd2l0aCBzcGVjaWZpZWRcclxuICogb3B0aW9ucy5cclxuICpcclxuICogQHBhcmFtIHtPYmplY3R9IHBhZ2UgLSBQdXBwZXRlZXIgcGFnZSBvYmplY3QuXHJcbiAqIEBwYXJhbSB7bnVtYmVyfSBoZWlnaHQgLSBQREYgaGVpZ2h0LlxyXG4gKiBAcGFyYW0ge251bWJlcn0gd2lkdGggLSBQREYgd2lkdGguXHJcbiAqIEBwYXJhbSB7c3RyaW5nfSBlbmNvZGluZyAtIFBERiBlbmNvZGluZy5cclxuICpcclxuICogQHJldHVybnMge1Byb21pc2U8QnVmZmVyPn0gUHJvbWlzZSByZXNvbHZpbmcgdG8gdGhlIFBERiBidWZmZXIuXHJcbiAqL1xyXG5jb25zdCBjcmVhdGVQREYgPSBhc3luYyAoXHJcbiAgcGFnZSxcclxuICBoZWlnaHQsXHJcbiAgd2lkdGgsXHJcbiAgZW5jb2RpbmcsXHJcbiAgcmFzdGVyaXphdGlvblRpbWVvdXRcclxuKSA9PiB7XHJcbiAgYXdhaXQgcGFnZS5lbXVsYXRlTWVkaWFUeXBlKCdzY3JlZW4nKTtcclxuXHJcbiAgcmV0dXJuIHBhZ2UucGRmKHtcclxuICAgIC8vIFRoaXMgd2lsbCByZW1vdmUgYW4gZXh0cmEgZW1wdHkgcGFnZSBpbiBQREYgZXhwb3J0c1xyXG4gICAgaGVpZ2h0OiBoZWlnaHQgKyAxLFxyXG4gICAgd2lkdGgsXHJcbiAgICBlbmNvZGluZyxcclxuICAgIHRpbWVvdXQ6IHJhc3Rlcml6YXRpb25UaW1lb3V0IHx8IDE1MDBcclxuICB9KTtcclxufTtcclxuXHJcbi8qKlxyXG4gKiBDcmVhdGVzIGFuIFNWRyBzdHJpbmcgYnkgZXZhbHVhdGluZyB0aGUgb3V0ZXJIVE1MIG9mIHRoZSBmaXJzdCAnc3ZnJyBlbGVtZW50XHJcbiAqIGluc2lkZSBhbiBlbGVtZW50IHdpdGggdGhlIGlkICdjb250YWluZXInLlxyXG4gKlxyXG4gKiBAcGFyYW0ge09iamVjdH0gcGFnZSAtIFB1cHBldGVlciBwYWdlIG9iamVjdC5cclxuICpcclxuICogQHJldHVybnMge1Byb21pc2U8c3RyaW5nPn0gUHJvbWlzZSByZXNvbHZpbmcgdG8gdGhlIFNWRyBzdHJpbmcuXHJcbiAqL1xyXG5jb25zdCBjcmVhdGVTVkcgPSAocGFnZSkgPT5cclxuICBwYWdlLiRldmFsKCcjY29udGFpbmVyIHN2ZzpmaXJzdC1vZi10eXBlJywgKGVsZW1lbnQpID0+IGVsZW1lbnQub3V0ZXJIVE1MKTtcclxuXHJcbi8qKlxyXG4gKiBTZXRzIHRoZSBzcGVjaWZpZWQgY2hhcnQgYW5kIG9wdGlvbnMgYXMgY29uZmlndXJhdGlvbiBpbnRvIHRoZSB0cmlnZ2VyRXhwb3J0XHJcbiAqIGZ1bmN0aW9uIHdpdGhpbiB0aGUgd2luZG93IGNvbnRleHQgdXNpbmcgcGFnZS5ldmFsdWF0ZS5cclxuICpcclxuICogQHBhcmFtIHtPYmplY3R9IHBhZ2UgLSBQdXBwZXRlZXIgcGFnZSBvYmplY3QuXHJcbiAqIEBwYXJhbSB7YW55fSBjaGFydCAtIFRoZSBjaGFydCBvYmplY3QgdG8gYmUgY29uZmlndXJlZC5cclxuICogQHBhcmFtIHtPYmplY3R9IG9wdGlvbnMgLSBDb25maWd1cmF0aW9uIG9wdGlvbnMgZm9yIHRoZSBjaGFydC5cclxuICpcclxuICogQHJldHVybnMge1Byb21pc2U8dm9pZD59IFByb21pc2UgcmVzb2x2aW5nIGFmdGVyIHRoZSBjb25maWd1cmF0aW9uIGlzIHNldC5cclxuICovXHJcbmNvbnN0IHNldEFzQ29uZmlnID0gYXN5bmMgKHBhZ2UsIGNoYXJ0LCBvcHRpb25zLCBkaXNwbGF5RXJyb3JzKSA9PiB7XHJcbiAgLy8gR2V0IHJpZCBvZiB0aGUgcmVkdW5hbnQgc3RyaW5nIGRhdGFcclxuICBvcHRpb25zLmV4cG9ydC5pbnN0ciA9IG51bGw7XHJcbiAgb3B0aW9ucy5leHBvcnQuaW5maWxlID0gbnVsbDtcclxuXHJcbiAgLy8gR2V0IHRoZSBzaXplIG9mIHRoZSBleHBvcnQgaW5wdXRcclxuICBjb25zdCB0b3RhbFNpemUgPSBCdWZmZXIuYnl0ZUxlbmd0aChcclxuICAgIG9wdGlvbnMuZXhwb3J0Py5zdHJJbmogPyBvcHRpb25zLmV4cG9ydD8uc3RySW5qIDogSlNPTi5zdHJpbmdpZnkoY2hhcnQpLFxyXG4gICAgJ3V0Zi04J1xyXG4gICk7XHJcblxyXG4gIC8vIExvZyB0aGUgc2l6ZSBpbiBNQlxyXG4gIGxvZyhcclxuICAgIDQsXHJcbiAgICBgW2V4cG9ydF0gVGhlIGN1cnJlbnQgdG90YWwgc2l6ZSBvZiBkYXRhIHBhc3NlZCB0byBhIHBhZ2UgaXMgYXJvdW5kICR7KFxyXG4gICAgICB0b3RhbFNpemUgL1xyXG4gICAgICAoMTAyNCAqIDEwMjQpXHJcbiAgICApLnRvRml4ZWQoMil9IE1CYFxyXG4gICk7XHJcblxyXG4gIC8vIENoZWNrIHRoZSBzaXplIG9mIGRhdGEgcGFzc2VkIHRvIHRoZSBwYWdlXHJcbiAgaWYgKHRvdGFsU2l6ZSA+PSAxMDAgKiAxMDI0ICogMTAyNCkge1xyXG4gICAgdGhyb3cgbmV3IEV4cG9ydEVycm9yKGBbZXhwb3J0XSBUaGUgZGF0YSBwYXNzZWQgdG8gYSBwYWdlIGV4Y2VlZGVkIDEwME1CLmApO1xyXG4gIH1cclxuXHJcbiAgLy8gVHJpZ2dlciB0aGUgSGlnaGNoYXJ0cyBjaGFydCBjcmVhdGlvblxyXG4gIHJldHVybiBwYWdlLmV2YWx1YXRlKHRyaWdnZXJFeHBvcnQsIGNoYXJ0LCBvcHRpb25zLCBkaXNwbGF5RXJyb3JzKTtcclxufTtcclxuXHJcbi8qKlxyXG4gKiBFeHBvcnRzIHRvIGEgY2hhcnQgZnJvbSBhIHBhZ2UgdXNpbmcgUHVwcGV0ZWVyLlxyXG4gKlxyXG4gKiBAcGFyYW0ge09iamVjdH0gcGFnZSAtIFB1cHBldGVlciBwYWdlIG9iamVjdC5cclxuICogQHBhcmFtIHthbnl9IGNoYXJ0IC0gVGhlIGNoYXJ0IG9iamVjdCBvciBTVkcgY29uZmlndXJhdGlvbiB0byBiZSBleHBvcnRlZC5cclxuICogQHBhcmFtIHtPYmplY3R9IG9wdGlvbnMgLSBFeHBvcnQgb3B0aW9ucyBhbmQgY29uZmlndXJhdGlvbi5cclxuICpcclxuICogQHJldHVybnMge1Byb21pc2U8c3RyaW5nIHwgQnVmZmVyIHwgRXhwb3J0RXJyb3I+fSBQcm9taXNlIHJlc29sdmluZyB0b1xyXG4gKiB0aGUgZXhwb3J0ZWQgZGF0YSBvciByZWplY3Rpbmcgd2l0aCBhbiBFeHBvcnRFcnJvci5cclxuICovXHJcbmV4cG9ydCBkZWZhdWx0IGFzeW5jIChwYWdlLCBjaGFydCwgb3B0aW9ucykgPT4ge1xyXG4gIC8vIEluamVjdGVkIHJlc291cmNlcyBhcnJheSAoYWRkaXRpb25hbCBKUyBhbmQgQ1NTKVxyXG4gIGxldCBpbmplY3RlZFJlc291cmNlcyA9IFtdO1xyXG5cclxuICB0cnkge1xyXG4gICAgbG9nKDQsICdbZXhwb3J0XSBEZXRlcm1pbmluZyBleHBvcnQgcGF0aC4nKTtcclxuXHJcbiAgICBjb25zdCBleHBvcnRPcHRpb25zID0gb3B0aW9ucy5leHBvcnQ7XHJcblxyXG4gICAgLy8gRGVjaWRlIHdoZXRoZXIgZGlzcGxheSBlcnJvciBvciBkZWJidWdlciB3cmFwcGVyIGFyb3VuZCBpdFxyXG4gICAgY29uc3QgZGlzcGxheUVycm9ycyA9XHJcbiAgICAgIGV4cG9ydE9wdGlvbnM/Lm9wdGlvbnM/LmNoYXJ0Py5kaXNwbGF5RXJyb3JzICYmXHJcbiAgICAgIGdldENhY2hlKCkuYWN0aXZlTWFuaWZlc3QubW9kdWxlcy5kZWJ1Z2dlcjtcclxuXHJcbiAgICBsZXQgaXNTVkc7XHJcbiAgICBpZiAoXHJcbiAgICAgIGNoYXJ0LmluZGV4T2YgJiZcclxuICAgICAgKGNoYXJ0LmluZGV4T2YoJzxzdmcnKSA+PSAwIHx8IGNoYXJ0LmluZGV4T2YoJzw/eG1sJykgPj0gMClcclxuICAgICkge1xyXG4gICAgICAvLyBTVkcgaW5wdXQgaGFuZGxpbmdcclxuICAgICAgbG9nKDQsICdbZXhwb3J0XSBUcmVhdGluZyBhcyBTVkcuJyk7XHJcblxyXG4gICAgICAvLyBJZiBpbnB1dCBpcyBhbHNvIFNWRywganVzdCByZXR1cm4gaXRcclxuICAgICAgaWYgKGV4cG9ydE9wdGlvbnMudHlwZSA9PT0gJ3N2ZycpIHtcclxuICAgICAgICByZXR1cm4gY2hhcnQ7XHJcbiAgICAgIH1cclxuXHJcbiAgICAgIGlzU1ZHID0gdHJ1ZTtcclxuICAgICAgYXdhaXQgcGFnZS5zZXRDb250ZW50KHN2Z1RlbXBsYXRlKGNoYXJ0KSwge1xyXG4gICAgICAgIHdhaXRVbnRpbDogJ2RvbWNvbnRlbnRsb2FkZWQnXHJcbiAgICAgIH0pO1xyXG4gICAgfSBlbHNlIHtcclxuICAgICAgLy8gSlNPTiBjb25maWcgaGFuZGxpbmdcclxuICAgICAgbG9nKDQsICdbZXhwb3J0XSBUcmVhdGluZyBhcyBjb25maWcuJyk7XHJcblxyXG4gICAgICAvLyBOZWVkIHRvIHBlcmZvcm0gc3RyYWlnaHQgaW5qZWN0XHJcbiAgICAgIGlmIChleHBvcnRPcHRpb25zLnN0ckluaikge1xyXG4gICAgICAgIC8vIEluamVjdGlvbiBiYXNlZCBjb25maWd1cmF0aW9uIGV4cG9ydFxyXG4gICAgICAgIGF3YWl0IHNldEFzQ29uZmlnKFxyXG4gICAgICAgICAgcGFnZSxcclxuICAgICAgICAgIHtcclxuICAgICAgICAgICAgY2hhcnQ6IHtcclxuICAgICAgICAgICAgICBoZWlnaHQ6IGV4cG9ydE9wdGlvbnMuaGVpZ2h0LFxyXG4gICAgICAgICAgICAgIHdpZHRoOiBleHBvcnRPcHRpb25zLndpZHRoXHJcbiAgICAgICAgICAgIH1cclxuICAgICAgICAgIH0sXHJcbiAgICAgICAgICBvcHRpb25zLFxyXG4gICAgICAgICAgZGlzcGxheUVycm9yc1xyXG4gICAgICAgICk7XHJcbiAgICAgIH0gZWxzZSB7XHJcbiAgICAgICAgLy8gQmFzaWMgY29uZmlndXJhdGlvbiBleHBvcnRcclxuICAgICAgICBjaGFydC5jaGFydC5oZWlnaHQgPSBleHBvcnRPcHRpb25zLmhlaWdodDtcclxuICAgICAgICBjaGFydC5jaGFydC53aWR0aCA9IGV4cG9ydE9wdGlvbnMud2lkdGg7XHJcblxyXG4gICAgICAgIGF3YWl0IHNldEFzQ29uZmlnKHBhZ2UsIGNoYXJ0LCBvcHRpb25zLCBkaXNwbGF5RXJyb3JzKTtcclxuICAgICAgfVxyXG4gICAgfVxyXG5cclxuICAgIC8vIEtlZXBzIHRyYWNrIG9mIGFsbCByZXNvdXJjZXMgYWRkZWQgb24gdGhlIHBhZ2Ugd2l0aCBhZGRYWFhUYWcuIGV0Y1xyXG4gICAgLy8gSXQncyBWSVRBTCB0aGF0IGFsbCBhZGRlZCByZXNvdXJjZXMgZW5kcyB1cCBoZXJlIHNvIHdlIGNhbiBjbGVhciB0aGluZ3NcclxuICAgIC8vIG91dCB3aGVuIGRvaW5nIGEgbmV3IGV4cG9ydCBpbiB0aGUgc2FtZSBwYWdlIVxyXG4gICAgaW5qZWN0ZWRSZXNvdXJjZXMgPSBhd2FpdCBhZGRQYWdlUmVzb3VyY2VzKHBhZ2UsIG9wdGlvbnMpO1xyXG5cclxuICAgIC8vIEdldCB0aGUgcmVhbCBjaGFydCBzaXplIGFuZCBzZXQgdGhlIHpvb20gYWNjb3JkaW5nbHlcclxuICAgIGNvbnN0IHNpemUgPSBpc1NWR1xyXG4gICAgICA/IGF3YWl0IHBhZ2UuZXZhbHVhdGUoKHNjYWxlKSA9PiB7XHJcbiAgICAgICAgICBjb25zdCBzdmdFbGVtZW50ID0gZG9jdW1lbnQucXVlcnlTZWxlY3RvcihcclxuICAgICAgICAgICAgJyNjaGFydC1jb250YWluZXIgc3ZnOmZpcnN0LW9mLXR5cGUnXHJcbiAgICAgICAgICApO1xyXG5cclxuICAgICAgICAgIC8vIEdldCB0aGUgdmFsdWVzIGNvcnJlY3RseSBzY2FsZWRcclxuICAgICAgICAgIGNvbnN0IGNoYXJ0SGVpZ2h0ID0gc3ZnRWxlbWVudC5oZWlnaHQuYmFzZVZhbC52YWx1ZSAqIHNjYWxlO1xyXG4gICAgICAgICAgY29uc3QgY2hhcnRXaWR0aCA9IHN2Z0VsZW1lbnQud2lkdGguYmFzZVZhbC52YWx1ZSAqIHNjYWxlO1xyXG5cclxuICAgICAgICAgIC8vIEluIGNhc2Ugb2YgU1ZHIHRoZSB6b29tIG11c3QgYmUgc2V0IGRpcmVjdGx5IGZvciBib2R5XHJcbiAgICAgICAgICAvLyBTZXQgdGhlIHpvb20gYXMgc2NhbGVcclxuICAgICAgICAgIC8vIGVzbGludC1kaXNhYmxlLW5leHQtbGluZSBuby11bmRlZlxyXG4gICAgICAgICAgZG9jdW1lbnQuYm9keS5zdHlsZS56b29tID0gc2NhbGU7XHJcblxyXG4gICAgICAgICAgLy8gU2V0IHRoZSBtYXJnaW4gdG8gMHB4XHJcbiAgICAgICAgICAvLyBlc2xpbnQtZGlzYWJsZS1uZXh0LWxpbmUgbm8tdW5kZWZcclxuICAgICAgICAgIGRvY3VtZW50LmJvZHkuc3R5bGUubWFyZ2luID0gJzBweCc7XHJcblxyXG4gICAgICAgICAgcmV0dXJuIHtcclxuICAgICAgICAgICAgY2hhcnRIZWlnaHQsXHJcbiAgICAgICAgICAgIGNoYXJ0V2lkdGhcclxuICAgICAgICAgIH07XHJcbiAgICAgICAgfSwgcGFyc2VGbG9hdChleHBvcnRPcHRpb25zLnNjYWxlKSlcclxuICAgICAgOiBhd2FpdCBwYWdlLmV2YWx1YXRlKCgpID0+IHtcclxuICAgICAgICAgIC8vIGVzbGludC1kaXNhYmxlLW5leHQtbGluZSBuby11bmRlZlxyXG4gICAgICAgICAgY29uc3QgeyBjaGFydEhlaWdodCwgY2hhcnRXaWR0aCB9ID0gd2luZG93LkhpZ2hjaGFydHMuY2hhcnRzWzBdO1xyXG5cclxuICAgICAgICAgIC8vIE5vIG5lZWQgZm9yIHN1Y2ggc2NhbGUgbWFuaXB1bGF0aW9uIGluIGNhc2Ugb2Ygb3RoZXIgdHlwZXMgb2YgZXhwb3J0c1xyXG4gICAgICAgICAgLy8gUmVzZXQgdGhlIHpvb20gZm9yIG90aGVyIGV4cG9ydHMgdGhhbiB0byBTVkdzXHJcbiAgICAgICAgICAvLyBlc2xpbnQtZGlzYWJsZS1uZXh0LWxpbmUgbm8tdW5kZWZcclxuICAgICAgICAgIGRvY3VtZW50LmJvZHkuc3R5bGUuem9vbSA9IDE7XHJcblxyXG4gICAgICAgICAgcmV0dXJuIHtcclxuICAgICAgICAgICAgY2hhcnRIZWlnaHQsXHJcbiAgICAgICAgICAgIGNoYXJ0V2lkdGhcclxuICAgICAgICAgIH07XHJcbiAgICAgICAgfSk7XHJcblxyXG4gICAgLy8gU2V0IGZpbmFsIGhlaWdodCBhbmQgd2lkdGggZm9yIHZpZXdwb3J0XHJcbiAgICBjb25zdCB2aWV3cG9ydEhlaWdodCA9IE1hdGguYWJzKFxyXG4gICAgICBNYXRoLmNlaWwoc2l6ZS5jaGFydEhlaWdodCB8fCBleHBvcnRPcHRpb25zLmhlaWdodClcclxuICAgICk7XHJcbiAgICBjb25zdCB2aWV3cG9ydFdpZHRoID0gTWF0aC5hYnMoXHJcbiAgICAgIE1hdGguY2VpbChzaXplLmNoYXJ0V2lkdGggfHwgZXhwb3J0T3B0aW9ucy53aWR0aClcclxuICAgICk7XHJcblxyXG4gICAgLy8gR2V0IHRoZSBjbGlwIHJlZ2lvbiBmb3IgdGhlIHBhZ2VcclxuICAgIGNvbnN0IHsgeCwgeSB9ID0gYXdhaXQgZ2V0Q2xpcFJlZ2lvbihwYWdlKTtcclxuXHJcbiAgICAvLyBTZXQgdGhlIGZpbmFsIHZpZXdwb3J0IG5vdyB0aGF0IHdlIGhhdmUgdGhlIHJlYWwgaGVpZ2h0XHJcbiAgICBhd2FpdCBwYWdlLnNldFZpZXdwb3J0KHtcclxuICAgICAgaGVpZ2h0OiB2aWV3cG9ydEhlaWdodCxcclxuICAgICAgd2lkdGg6IHZpZXdwb3J0V2lkdGgsXHJcbiAgICAgIGRldmljZVNjYWxlRmFjdG9yOiBpc1NWRyA/IDEgOiBwYXJzZUZsb2F0KGV4cG9ydE9wdGlvbnMuc2NhbGUpXHJcbiAgICB9KTtcclxuXHJcbiAgICBsZXQgZGF0YTtcclxuICAgIC8vIFJhc3Rlcml6YXRpb24gcHJvY2Vzc1xyXG4gICAgaWYgKGV4cG9ydE9wdGlvbnMudHlwZSA9PT0gJ3N2ZycpIHtcclxuICAgICAgLy8gU1ZHXHJcbiAgICAgIGRhdGEgPSBhd2FpdCBjcmVhdGVTVkcocGFnZSk7XHJcbiAgICB9IGVsc2UgaWYgKFsncG5nJywgJ2pwZWcnXS5pbmNsdWRlcyhleHBvcnRPcHRpb25zLnR5cGUpKSB7XHJcbiAgICAgIC8vIFBORyBvciBKUEVHXHJcbiAgICAgIGRhdGEgPSBhd2FpdCBjcmVhdGVJbWFnZShcclxuICAgICAgICBwYWdlLFxyXG4gICAgICAgIGV4cG9ydE9wdGlvbnMudHlwZSxcclxuICAgICAgICAnYmFzZTY0JyxcclxuICAgICAgICB7XHJcbiAgICAgICAgICB3aWR0aDogdmlld3BvcnRXaWR0aCxcclxuICAgICAgICAgIGhlaWdodDogdmlld3BvcnRIZWlnaHQsXHJcbiAgICAgICAgICB4LFxyXG4gICAgICAgICAgeVxyXG4gICAgICAgIH0sXHJcbiAgICAgICAgZXhwb3J0T3B0aW9ucy5yYXN0ZXJpemF0aW9uVGltZW91dFxyXG4gICAgICApO1xyXG4gICAgfSBlbHNlIGlmIChleHBvcnRPcHRpb25zLnR5cGUgPT09ICdwZGYnKSB7XHJcbiAgICAgIC8vIFBERlxyXG4gICAgICBkYXRhID0gYXdhaXQgY3JlYXRlUERGKFxyXG4gICAgICAgIHBhZ2UsXHJcbiAgICAgICAgdmlld3BvcnRIZWlnaHQsXHJcbiAgICAgICAgdmlld3BvcnRXaWR0aCxcclxuICAgICAgICAnYmFzZTY0JyxcclxuICAgICAgICBleHBvcnRPcHRpb25zLnJhc3Rlcml6YXRpb25UaW1lb3V0XHJcbiAgICAgICk7XHJcbiAgICB9IGVsc2Uge1xyXG4gICAgICB0aHJvdyBuZXcgRXhwb3J0RXJyb3IoXHJcbiAgICAgICAgYFtleHBvcnRdIFVuc3VwcG9ydGVkIG91dHB1dCBmb3JtYXQgJHtleHBvcnRPcHRpb25zLnR5cGV9LmBcclxuICAgICAgKTtcclxuICAgIH1cclxuXHJcbiAgICAvLyBDbGVhciBwcmV2aW91c2x5IGluamVjdGVkIEpTIGFuZCBDU1MgcmVzb3VyY2VzXHJcbiAgICBhd2FpdCBjbGVhclBhZ2VSZXNvdXJjZXMocGFnZSwgaW5qZWN0ZWRSZXNvdXJjZXMpO1xyXG4gICAgcmV0dXJuIGRhdGE7XHJcbiAgfSBjYXRjaCAoZXJyb3IpIHtcclxuICAgIGF3YWl0IGNsZWFyUGFnZVJlc291cmNlcyhwYWdlLCBpbmplY3RlZFJlc291cmNlcyk7XHJcbiAgICByZXR1cm4gZXJyb3I7XHJcbiAgfVxyXG59O1xyXG4iLCIvKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKlxyXG5cclxuSGlnaGNoYXJ0cyBFeHBvcnQgU2VydmVyXHJcblxyXG5Db3B5cmlnaHQgKGMpIDIwMTYtMjAyNCwgSGlnaHNvZnRcclxuXHJcbkxpY2VuY2VkIHVuZGVyIHRoZSBNSVQgbGljZW5jZS5cclxuXHJcbkFkZGl0aW9uYWxseSBhIHZhbGlkIEhpZ2hjaGFydHMgbGljZW5zZSBpcyByZXF1aXJlZCBmb3IgdXNlLlxyXG5cclxuU2VlIExJQ0VOU0UgZmlsZSBpbiByb290IGZvciBkZXRhaWxzLlxyXG5cclxuKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKi9cclxuXHJcbmltcG9ydCBjc3NUZW1wbGF0ZSBmcm9tICcuL2Nzcy5qcyc7XHJcblxyXG5leHBvcnQgZGVmYXVsdCAoY2hhcnQpID0+IGBcclxuPCFET0NUWVBFIGh0bWw+XHJcbjxodG1sIGxhbmc9J2VuLVVTJz5cclxuICA8aGVhZD5cclxuICAgIDxtZXRhIGh0dHAtZXF1aXY9XCJDb250ZW50LVR5cGVcIiBjb250ZW50PVwidGV4dC9odG1sOyBjaGFyc2V0PXV0Zi04XCI+XHJcbiAgICA8dGl0bGU+SGlnaGNoYXJ0cyBFeHBvcnQ8L3RpdGxlPlxyXG4gIDwvaGVhZD5cclxuICA8c3R5bGU+XHJcbiAgICAke2Nzc1RlbXBsYXRlKCl9XHJcbiAgPC9zdHlsZT5cclxuICA8Ym9keT5cclxuICAgIDxkaXYgaWQ9XCJjaGFydC1jb250YWluZXJcIj5cclxuICAgICAgJHtjaGFydH1cclxuICAgIDwvZGl2PlxyXG4gIDwvYm9keT5cclxuPC9odG1sPlxyXG5cclxuYDtcclxuIiwiLyoqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKipcclxuXHJcbkhpZ2hjaGFydHMgRXhwb3J0IFNlcnZlclxyXG5cclxuQ29weXJpZ2h0IChjKSAyMDE2LTIwMjQsIEhpZ2hzb2Z0XHJcblxyXG5MaWNlbmNlZCB1bmRlciB0aGUgTUlUIGxpY2VuY2UuXHJcblxyXG5BZGRpdGlvbmFsbHkgYSB2YWxpZCBIaWdoY2hhcnRzIGxpY2Vuc2UgaXMgcmVxdWlyZWQgZm9yIHVzZS5cclxuXHJcblNlZSBMSUNFTlNFIGZpbGUgaW4gcm9vdCBmb3IgZGV0YWlscy5cclxuXHJcbioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKiovXHJcblxyXG5pbXBvcnQgeyBQb29sIH0gZnJvbSAndGFybic7XHJcbmltcG9ydCB7IHY0IGFzIHV1aWQgfSBmcm9tICd1dWlkJztcclxuXHJcbmltcG9ydCB7XHJcbiAgY3JlYXRlIGFzIGNyZWF0ZUJyb3dzZXIsXHJcbiAgY2xvc2UgYXMgY2xvc2VCcm93c2VyLFxyXG4gIG5ld1BhZ2UsXHJcbiAgY2xlYXJQYWdlXHJcbn0gZnJvbSAnLi9icm93c2VyLmpzJztcclxuaW1wb3J0IHB1cHBldGVlckV4cG9ydCBmcm9tICcuL2V4cG9ydC5qcyc7XHJcbmltcG9ydCB7IGxvZywgbG9nV2l0aFN0YWNrIH0gZnJvbSAnLi9sb2dnZXIuanMnO1xyXG5pbXBvcnQgeyBtZWFzdXJlVGltZSB9IGZyb20gJy4vdXRpbHMuanMnO1xyXG5cclxuaW1wb3J0IEV4cG9ydEVycm9yIGZyb20gJy4vZXJyb3JzL0V4cG9ydEVycm9yLmpzJztcclxuXHJcbi8vIFRoZSBwb29sIGluc3RhbmNlXHJcbmxldCBwb29sID0gZmFsc2U7XHJcblxyXG4vLyBQb29sIHN0YXRpc3RpY3NcclxuZXhwb3J0IGNvbnN0IHN0YXRzID0ge1xyXG4gIHBlcmZvcm1lZEV4cG9ydHM6IDAsXHJcbiAgZXhwb3J0QXR0ZW1wdHM6IDAsXHJcbiAgZXhwb3J0RnJvbVN2Z0F0dGVtcHRzOiAwLFxyXG4gIHRpbWVTcGVudDogMCxcclxuICBkcm9wcGVkRXhwb3J0czogMCxcclxuICBzcGVudEF2ZXJhZ2U6IDBcclxufTtcclxuXHJcbmxldCBwb29sQ29uZmlnID0ge307XHJcblxyXG5jb25zdCBmYWN0b3J5ID0ge1xyXG4gIC8qKlxyXG4gICAqIENyZWF0ZXMgYSBuZXcgd29ya2VyIHBhZ2UgZm9yIHRoZSBleHBvcnQgcG9vbC5cclxuICAgKlxyXG4gICAqIEByZXR1cm5zIHtPYmplY3R9IC0gQW4gb2JqZWN0IGNvbnRhaW5pbmcgdGhlIHdvcmtlciBJRCwgYSByZWZlcmVuY2UgdG8gdGhlXHJcbiAgICogYnJvd3NlciBwYWdlLCBhbmQgaW5pdGlhbCB3b3JrIGNvdW50LlxyXG4gICAqXHJcbiAgICogQHRocm93cyB7RXhwb3J0RXJyb3J9IC0gSWYgdGhlcmUncyBhbiBlcnJvciBkdXJpbmcgdGhlIGNyZWF0aW9uIG9mIHRoZSBuZXdcclxuICAgKiBwYWdlLlxyXG4gICAqL1xyXG4gIGNyZWF0ZTogYXN5bmMgKCkgPT4ge1xyXG4gICAgbGV0IHBhZ2UgPSBmYWxzZTtcclxuXHJcbiAgICBjb25zdCBpZCA9IHV1aWQoKTtcclxuICAgIGNvbnN0IHN0YXJ0RGF0ZSA9IG5ldyBEYXRlKCkuZ2V0VGltZSgpO1xyXG5cclxuICAgIHRyeSB7XHJcbiAgICAgIHBhZ2UgPSBhd2FpdCBuZXdQYWdlKCk7XHJcblxyXG4gICAgICBpZiAoIXBhZ2UgfHwgcGFnZS5pc0Nsb3NlZCgpKSB7XHJcbiAgICAgICAgdGhyb3cgbmV3IEV4cG9ydEVycm9yKCdUaGUgcGFnZSBpcyBpbnZhbGlkIG9yIGNsb3NlZC4nKTtcclxuICAgICAgfVxyXG5cclxuICAgICAgbG9nKFxyXG4gICAgICAgIDMsXHJcbiAgICAgICAgYFtwb29sXSBTdWNjZXNzZnVsbHkgY3JlYXRlZCBhIHdvcmtlciAke2lkfSAtIHRvb2sgJHtcclxuICAgICAgICAgIG5ldyBEYXRlKCkuZ2V0VGltZSgpIC0gc3RhcnREYXRlXHJcbiAgICAgICAgfSBtcy5gXHJcbiAgICAgICk7XHJcbiAgICB9IGNhdGNoIChlcnJvcikge1xyXG4gICAgICB0aHJvdyBuZXcgRXhwb3J0RXJyb3IoXHJcbiAgICAgICAgJ0Vycm9yIGVuY291bnRlcmVkIHdoZW4gY3JlYXRpbmcgYSBuZXcgcGFnZS4nXHJcbiAgICAgICkuc2V0RXJyb3IoZXJyb3IpO1xyXG4gICAgfVxyXG5cclxuICAgIHJldHVybiB7XHJcbiAgICAgIGlkLFxyXG4gICAgICBwYWdlLFxyXG4gICAgICAvLyBUcnkgdG8gZGlzdHJpYnV0ZSB0aGUgaW5pdGlhbCB3b3JrIGNvdW50XHJcbiAgICAgIHdvcmtDb3VudDogTWF0aC5yb3VuZChNYXRoLnJhbmRvbSgpICogKHBvb2xDb25maWcud29ya0xpbWl0IC8gMikpXHJcbiAgICB9O1xyXG4gIH0sXHJcblxyXG4gIC8qKlxyXG4gICAqIFZhbGlkYXRlcyBhIHdvcmtlciBwYWdlIGluIHRoZSBleHBvcnQgcG9vbCwgY2hlY2tpbmcgaWYgaXQgaGFzIGV4Y2VlZGVkXHJcbiAgICogdGhlIHdvcmsgbGltaXQuXHJcbiAgICpcclxuICAgKiBAcGFyYW0ge09iamVjdH0gd29ya2VySGFuZGxlIC0gVGhlIGhhbmRsZSB0byB0aGUgd29ya2VyLCBjb250YWluaW5nIHRoZVxyXG4gICAqIHdvcmtlcidzIElELCBhIHJlZmVyZW5jZSB0byB0aGUgYnJvd3NlciBwYWdlLCBhbmQgd29yayBjb3VudC5cclxuICAgKlxyXG4gICAqIEByZXR1cm5zIHtib29sZWFufSAtIFJldHVybnMgdHJ1ZSBpZiB0aGUgd29ya2VyIGlzIHZhbGlkIGFuZCB3aXRoaW5cclxuICAgKiB0aGUgd29yayBsaW1pdDsgb3RoZXJ3aXNlLCByZXR1cm5zIGZhbHNlLlxyXG4gICAqL1xyXG4gIHZhbGlkYXRlOiBhc3luYyAod29ya2VySGFuZGxlKSA9PiB7XHJcbiAgICAvLyBOT1RFOiBJbiBjZXJ0YWluIGNhc2VzIGFjcXVpcmluZyB0aHJvd3MgYSBUYXJnZXRDbG9zZUVycm9yLCB3aGljaCBtYXlcclxuICAgIC8vICAgICAgIGJlIGNhdXNlZCBieSB0d28gdGhpbmdzOlxyXG4gICAgLy8gICAgICAgICAtIFRoZSBwYWdlIGlzIGNsb3NlZCBhbmQgYXR0ZW1wdGVkIHRvIGJlIHJldXNlZC5cclxuICAgIC8vICAgICAgICAgLSBMb3N0IGNvbnRhY3Qgd2l0aCB0aGUgYnJvd3NlclxyXG4gICAgLy8gICAgICAgV2hhdCB3ZSdyZSBzZWVpbmcgaW4gbG9ncyBpcyB0aGF0IHN1Y2Nlc3NpdmUgZXhwb3J0cyB0eXBpY2FsbHlcclxuICAgIC8vICAgICAgIHN1Y2NlZWRzLCBhbmQgdGhlIHNlcnZlciByZWNvdmVycywgaW5kaWNhdGluZyB0aGF0IGl0J3MgbGlrZWx5XHJcbiAgICAvLyAgICAgICB0aGUgZmlyc3QgY2FzZS4gVGhpcyBpcyBhbiBhdHRlbXB0IGF0IGFsbGlldmF0aW5nIHRoZSBpc3N1ZSBieVxyXG4gICAgLy8gICAgICAgc2ltcGx5IG5vdCB2YWxpZGF0aW5nIHRoZSB3b3JrZXIgaWYgdGhlIHBhZ2UgaXMgbnVsbCBvciBjbG9zZWQuXHJcbiAgICAvL1xyXG4gICAgLy8gICAgICAgVGhlIGFjdHVhbCByZXN1bHQgZnJvbSB3aGVuIHRoaXMgaGFwcGVuZWQsIHdhcyB0aGF0IGEgd29ya2VyIHdvdWxkXHJcbiAgICAvLyAgICAgICBiZSBjb21wbGV0ZWx5IGxvY2tlZCwgc3RvcHBpbmcgaXQgZnJvbSBiZWluZyBhY3F1aXJlZCB1bnRpbFxyXG4gICAgLy8gICAgICAgaXRzIHdvcmsgY291bnQgcmVhY2hlZCB0aGUgbGltaXQuXHJcbiAgICBpZiAoIXdvcmtlckhhbmRsZS5wYWdlIHx8IHdvcmtlckhhbmRsZS5wYWdlPy5pc0Nsb3NlZCgpKSB7XHJcbiAgICAgIHJldHVybiBmYWxzZTtcclxuICAgIH1cclxuXHJcbiAgICBpZiAoXHJcbiAgICAgIHBvb2xDb25maWcud29ya0xpbWl0ICYmXHJcbiAgICAgICsrd29ya2VySGFuZGxlLndvcmtDb3VudCA+IHBvb2xDb25maWcud29ya0xpbWl0XHJcbiAgICApIHtcclxuICAgICAgbG9nKFxyXG4gICAgICAgIDMsXHJcbiAgICAgICAgYFtwb29sXSBXb3JrZXIgZmFpbGVkIHZhbGlkYXRpb246IGV4Y2VlZGVkIHdvcmsgbGltaXQgKGxpbWl0IGlzICR7cG9vbENvbmZpZy53b3JrTGltaXR9KS5gXHJcbiAgICAgICk7XHJcbiAgICAgIHJldHVybiBmYWxzZTtcclxuICAgIH1cclxuICAgIHJldHVybiB0cnVlO1xyXG4gIH0sXHJcblxyXG4gIC8qKlxyXG4gICAqIERlc3Ryb3lzIGEgd29ya2VyIGVudHJ5IGluIHRoZSBleHBvcnQgcG9vbCwgY2xvc2luZyBpdHMgYXNzb2NpYXRlZCBwYWdlLlxyXG4gICAqXHJcbiAgICogQHBhcmFtIHtPYmplY3R9IHdvcmtlckhhbmRsZSAtIFRoZSBoYW5kbGUgdG8gdGhlIHdvcmtlciwgY29udGFpbmluZ1xyXG4gICAqIHRoZSB3b3JrZXIncyBJRCBhbmQgYSByZWZlcmVuY2UgdG8gdGhlIGJyb3dzZXIgcGFnZS5cclxuICAgKi9cclxuICBkZXN0cm95OiBhc3luYyAod29ya2VySGFuZGxlKSA9PiB7XHJcbiAgICBsb2coMywgYFtwb29sXSBEZXN0cm95aW5nIHBvb2wgZW50cnkgJHt3b3JrZXJIYW5kbGUuaWR9LmApO1xyXG5cclxuICAgIGlmICh3b3JrZXJIYW5kbGUucGFnZSAmJiAhd29ya2VySGFuZGxlLnBhZ2UuaXNDbG9zZWQoKSkge1xyXG4gICAgICBhd2FpdCB3b3JrZXJIYW5kbGUucGFnZS5jbG9zZSgpO1xyXG4gICAgfVxyXG4gIH1cclxuXHJcbiAgLy8gbG9nOiAobWVzc2FnZSwgbGV2ZWwpID0+IGxvZygxLCAnW3Rhcm5dICcgKyAgbWVzc2FnZSlcclxufTtcclxuXHJcbi8qKlxyXG4gKiBJbml0aWFsaXplcyB0aGUgZXhwb3J0IHBvb2wgd2l0aCB0aGUgcHJvdmlkZWQgY29uZmlndXJhdGlvbiwgY3JlYXRpbmdcclxuICogYSBicm93c2VyIGluc3RhbmNlIGFuZCBzZXR0aW5nIHVwIHdvcmtlciByZXNvdXJjZXMuXHJcbiAqXHJcbiAqIEBwYXJhbSB7T2JqZWN0fSBjb25maWcgLSBDb25maWd1cmF0aW9uIG9wdGlvbnMgZm9yIHRoZSBleHBvcnQgcG9vbCBhbG9uZ1xyXG4gKiB3aXRoIGN1c3RvbSBwdXBwZXRlZXIgYXJndW1lbnRzIGZvciB0aGUgcHVwcGV0ZWVyLmxhdW5jaCBmdW5jdGlvbi5cclxuICovXHJcbmV4cG9ydCBjb25zdCBpbml0UG9vbCA9IGFzeW5jIChjb25maWcpID0+IHtcclxuICAvLyBGb3IgdGhlIG1vZHVsZSBzY29wZSB1c2FnZVxyXG4gIHBvb2xDb25maWcgPSBjb25maWcgJiYgY29uZmlnLnBvb2wgPyB7IC4uLmNvbmZpZy5wb29sIH0gOiB7fTtcclxuXHJcbiAgLy8gQ3JlYXRlIGEgYnJvd3NlciBpbnN0YW5jZSB3aXRoIHRoZSBwdXBwZXRlZXIgYXJndW1lbnRzXHJcbiAgYXdhaXQgY3JlYXRlQnJvd3Nlcihjb25maWcucHVwcGV0ZWVyQXJncyk7XHJcblxyXG4gIGxvZyhcclxuICAgIDMsXHJcbiAgICBgW3Bvb2xdIEluaXRpYWxpemluZyBwb29sIHdpdGggd29ya2VyczogbWluICR7cG9vbENvbmZpZy5taW5Xb3JrZXJzfSwgbWF4ICR7cG9vbENvbmZpZy5tYXhXb3JrZXJzfS5gXHJcbiAgKTtcclxuXHJcbiAgaWYgKHBvb2wpIHtcclxuICAgIHJldHVybiBsb2coXHJcbiAgICAgIDQsXHJcbiAgICAgICdbcG9vbF0gQWxyZWFkeSBpbml0aWFsaXplZCwgcGxlYXNlIGtpbGwgaXQgYmVmb3JlIGNyZWF0aW5nIGEgbmV3IG9uZS4nXHJcbiAgICApO1xyXG4gIH1cclxuXHJcbiAgaWYgKHBhcnNlSW50KHBvb2xDb25maWcubWluV29ya2VycykgPiBwYXJzZUludChwb29sQ29uZmlnLm1heFdvcmtlcnMpKSB7XHJcbiAgICBwb29sQ29uZmlnLm1pbldvcmtlcnMgPSBwb29sQ29uZmlnLm1heFdvcmtlcnM7XHJcbiAgfVxyXG5cclxuICB0cnkge1xyXG4gICAgLy8gQ3JlYXRlIGEgcG9vbCBhbG9uZyB3aXRoIGEgbWluaW1hbCBudW1iZXIgb2YgcmVzb3VyY2VzXHJcbiAgICBwb29sID0gbmV3IFBvb2woe1xyXG4gICAgICAvLyBHZXQgdGhlIGNyZWF0ZS92YWxpZGF0ZS9kZXN0cm95L2xvZyBmdW5jdGlvbnNcclxuICAgICAgLi4uZmFjdG9yeSxcclxuICAgICAgbWluOiBwYXJzZUludChwb29sQ29uZmlnLm1pbldvcmtlcnMpLFxyXG4gICAgICBtYXg6IHBhcnNlSW50KHBvb2xDb25maWcubWF4V29ya2VycyksXHJcbiAgICAgIGFjcXVpcmVUaW1lb3V0TWlsbGlzOiBwb29sQ29uZmlnLmFjcXVpcmVUaW1lb3V0LFxyXG4gICAgICBjcmVhdGVUaW1lb3V0TWlsbGlzOiBwb29sQ29uZmlnLmNyZWF0ZVRpbWVvdXQsXHJcbiAgICAgIGRlc3Ryb3lUaW1lb3V0TWlsbGlzOiBwb29sQ29uZmlnLmRlc3Ryb3lUaW1lb3V0LFxyXG4gICAgICBpZGxlVGltZW91dE1pbGxpczogcG9vbENvbmZpZy5pZGxlVGltZW91dCxcclxuICAgICAgY3JlYXRlUmV0cnlJbnRlcnZhbE1pbGxpczogcG9vbENvbmZpZy5jcmVhdGVSZXRyeUludGVydmFsLFxyXG4gICAgICByZWFwSW50ZXJ2YWxNaWxsaXM6IHBvb2xDb25maWcucmVhcGVySW50ZXJ2YWwsXHJcbiAgICAgIHByb3BhZ2F0ZUNyZWF0ZUVycm9yOiBmYWxzZVxyXG4gICAgfSk7XHJcblxyXG4gICAgLy8gU2V0IGV2ZW50c1xyXG4gICAgcG9vbC5vbigncmVsZWFzZScsIGFzeW5jIChyZXNvdXJjZSkgPT4ge1xyXG4gICAgICAvLyBDbGVhciBwYWdlXHJcbiAgICAgIGNvbnN0IHIgPSBhd2FpdCBjbGVhclBhZ2UocmVzb3VyY2UucGFnZSwgZmFsc2UpO1xyXG4gICAgICBsb2coXHJcbiAgICAgICAgNCxcclxuICAgICAgICBgW3Bvb2xdIFJlbGVhc2luZyBhIHdvcmtlciB3aXRoIElEICR7cmVzb3VyY2UuaWR9LiBDbGVhciBwYWdlIHN0YXR1czogJHtyfS5gXHJcbiAgICAgICk7XHJcbiAgICB9KTtcclxuXHJcbiAgICBwb29sLm9uKCdkZXN0cm95U3VjY2VzcycsIChldmVudElkLCByZXNvdXJjZSkgPT4ge1xyXG4gICAgICBsb2coNCwgYFtwb29sXSBEZXN0cm95ZWQgYSB3b3JrZXIgd2l0aCBJRCAke3Jlc291cmNlLmlkfS5gKTtcclxuICAgICAgcmVzb3VyY2UucGFnZSA9IG51bGw7XHJcbiAgICB9KTtcclxuXHJcbiAgICBjb25zdCBpbml0aWFsUmVzb3VyY2VzID0gW107XHJcbiAgICAvLyBDcmVhdGUgYW4gaW5pdGlhbCBudW1iZXIgb2YgcmVzb3VyY2VzXHJcbiAgICBmb3IgKGxldCBpID0gMDsgaSA8IHBvb2xDb25maWcubWluV29ya2VyczsgaSsrKSB7XHJcbiAgICAgIHRyeSB7XHJcbiAgICAgICAgY29uc3QgcmVzb3VyY2UgPSBhd2FpdCBwb29sLmFjcXVpcmUoKS5wcm9taXNlO1xyXG4gICAgICAgIGluaXRpYWxSZXNvdXJjZXMucHVzaChyZXNvdXJjZSk7XHJcbiAgICAgIH0gY2F0Y2ggKGVycm9yKSB7XHJcbiAgICAgICAgbG9nV2l0aFN0YWNrKDIsIGVycm9yLCAnW3Bvb2xdIENvdWxkIG5vdCBjcmVhdGUgYW4gaW5pdGlhbCByZXNvdXJjZS4nKTtcclxuICAgICAgfVxyXG4gICAgfVxyXG5cclxuICAgIC8vIFJlbGVhc2UgdGhlIGluaXRpYWwgbnVtYmVyIG9mIHJlc291cmNlcyBiYWNrIHRvIHRoZSBwb29sXHJcbiAgICBpbml0aWFsUmVzb3VyY2VzLmZvckVhY2goKHJlc291cmNlKSA9PiB7XHJcbiAgICAgIHBvb2wucmVsZWFzZShyZXNvdXJjZSk7XHJcbiAgICB9KTtcclxuXHJcbiAgICBsb2coXHJcbiAgICAgIDMsXHJcbiAgICAgIGBbcG9vbF0gVGhlIHBvb2wgaXMgcmVhZHkke2luaXRpYWxSZXNvdXJjZXMubGVuZ3RoID8gYCB3aXRoICR7aW5pdGlhbFJlc291cmNlcy5sZW5ndGh9IGluaXRpYWwgcmVzb3VyY2VzIHdhaXRpbmcuYCA6ICcuJ31gXHJcbiAgICApO1xyXG4gIH0gY2F0Y2ggKGVycm9yKSB7XHJcbiAgICB0aHJvdyBuZXcgRXhwb3J0RXJyb3IoXHJcbiAgICAgICdbcG9vbF0gQ291bGQgbm90IGNyZWF0ZSB0aGUgcG9vbCBvZiB3b3JrZXJzLidcclxuICAgICkuc2V0RXJyb3IoZXJyb3IpO1xyXG4gIH1cclxufTtcclxuXHJcbi8qKlxyXG4gKiBLaWxscyBhbGwgd29ya2VycyBpbiB0aGUgcG9vbCwgZGVzdHJveXMgdGhlIHBvb2wsIGFuZCBjbG9zZXMgdGhlIGJyb3dzZXJcclxuICogaW5zdGFuY2UuXHJcbiAqXHJcbiAqIEByZXR1cm5zIHtQcm9taXNlPHZvaWQ+fSBBIHByb21pc2UgdGhhdCByZXNvbHZlcyBhZnRlciB0aGUgd29ya2VycyBhcmVcclxuICoga2lsbGVkLCB0aGUgcG9vbCBpcyBkZXN0cm95ZWQsIGFuZCB0aGUgYnJvd3NlciBpcyBjbG9zZWQuXHJcbiAqL1xyXG5leHBvcnQgYXN5bmMgZnVuY3Rpb24ga2lsbFBvb2woKSB7XHJcbiAgbG9nKDMsICdbcG9vbF0gS2lsbGluZyBwb29sIHdpdGggYWxsIHdvcmtlcnMgYW5kIGNsb3NpbmcgYnJvd3Nlci4nKTtcclxuXHJcbiAgLy8gSWYgc3RpbGwgYWxpdmUsIGRlc3Ryb3kgdGhlIHBvb2wgb2YgcGFnZXMgYmVmb3JlIGNsb3NpbmcgYSBicm93c2VyXHJcbiAgaWYgKHBvb2wpIHtcclxuICAgIC8vIEZyZWUgdXAgbm90IHJlbGVhc2VkIHdvcmtlcnNcclxuICAgIGZvciAoY29uc3Qgd29ya2VyIG9mIHBvb2wudXNlZCkge1xyXG4gICAgICBwb29sLnJlbGVhc2Uod29ya2VyLnJlc291cmNlKTtcclxuICAgIH1cclxuXHJcbiAgICAvLyBEZXN0cm95IHRoZSBwb29sIGlmIGl0IGlzIHN0aWxsIGF2YWlsYWJsZVxyXG4gICAgaWYgKCFwb29sLmRlc3Ryb3llZCkge1xyXG4gICAgICBhd2FpdCBwb29sLmRlc3Ryb3koKTtcclxuICAgICAgbG9nKDQsICdbYnJvd3Nlcl0gRGVzdHJveWVkIHRoZSBwb29sIG9mIHJlc291cmNlcy4nKTtcclxuICAgIH1cclxuICB9XHJcblxyXG4gIC8vIENsb3NlIHRoZSBicm93c2VyIGluc3RhbmNlXHJcbiAgYXdhaXQgY2xvc2VCcm93c2VyKCk7XHJcbn1cclxuXHJcbi8qKlxyXG4gKiBQcm9jZXNzZXMgdGhlIGV4cG9ydCB3b3JrIHVzaW5nIGEgd29ya2VyIGZyb20gdGhlIHBvb2wuIEFjcXVpcmVzIGEgd29ya2VyXHJcbiAqIGhhbmRsZSBmcm9tIHRoZSBwb29sLCBwZXJmb3JtcyB0aGUgZXhwb3J0IHVzaW5nIHB1cHBldGVlciwgYW5kIHJlbGVhc2VzXHJcbiAqIHRoZSB3b3JrZXIgaGFuZGxlIGJhY2sgdG8gdGhlIHBvb2wuXHJcbiAqXHJcbiAqIEBwYXJhbSB7c3RyaW5nfSBjaGFydCAtIFRoZSBjaGFydCBkYXRhIG9yIGNvbmZpZ3VyYXRpb24gdG8gYmUgZXhwb3J0ZWQuXHJcbiAqIEBwYXJhbSB7T2JqZWN0fSBvcHRpb25zIC0gRXhwb3J0IG9wdGlvbnMgYW5kIGNvbmZpZ3VyYXRpb24uXHJcbiAqXHJcbiAqIEByZXR1cm5zIHtQcm9taXNlPE9iamVjdD59IEEgcHJvbWlzZSB0aGF0IHJlc29sdmVzIHdpdGggdGhlIGV4cG9ydCByZXN1bHRhbmRcclxuICogb3B0aW9ucy5cclxuICpcclxuICogQHRocm93cyB7RXhwb3J0RXJyb3J9IElmIGFuIGVycm9yIG9jY3VycyBkdXJpbmcgdGhlIGV4cG9ydCBwcm9jZXNzLlxyXG4gKi9cclxuZXhwb3J0IGNvbnN0IHBvc3RXb3JrID0gYXN5bmMgKGNoYXJ0LCBvcHRpb25zKSA9PiB7XHJcbiAgbGV0IHdvcmtlckhhbmRsZTtcclxuXHJcbiAgdHJ5IHtcclxuICAgIGxvZyg0LCAnW3Bvb2xdIFdvcmsgcmVjZWl2ZWQsIHN0YXJ0aW5nIHRvIHByb2Nlc3MuJyk7XHJcblxyXG4gICAgKytzdGF0cy5leHBvcnRBdHRlbXB0cztcclxuICAgIGlmIChwb29sQ29uZmlnLmJlbmNobWFya2luZykge1xyXG4gICAgICBnZXRQb29sSW5mbygpO1xyXG4gICAgfVxyXG5cclxuICAgIGlmICghcG9vbCkge1xyXG4gICAgICB0aHJvdyBuZXcgRXhwb3J0RXJyb3IoJ1dvcmsgcmVjZWl2ZWQsIGJ1dCBwb29sIGhhcyBub3QgYmVlbiBzdGFydGVkLicpO1xyXG4gICAgfVxyXG5cclxuICAgIC8vIEFjcXVpcmUgdGhlIHdvcmtlciBhbG9uZyB3aXRoIHRoZSBpZCBvZiByZXNvdXJjZSBhbmQgd29yayBjb3VudFxyXG4gICAgY29uc3QgYWNxdWlyZUNvdW50ZXIgPSBtZWFzdXJlVGltZSgpO1xyXG4gICAgdHJ5IHtcclxuICAgICAgbG9nKDQsICdbcG9vbF0gQWNxdWlyaW5nIGEgd29ya2VyIGhhbmRsZS4nKTtcclxuICAgICAgd29ya2VySGFuZGxlID0gYXdhaXQgcG9vbC5hY3F1aXJlKCkucHJvbWlzZTtcclxuXHJcbiAgICAgIC8vIENoZWNrIHRoZSBwYWdlIGFjcXVpcmUgdGltZVxyXG4gICAgICBpZiAob3B0aW9ucy5zZXJ2ZXIuYmVuY2htYXJraW5nKSB7XHJcbiAgICAgICAgbG9nKFxyXG4gICAgICAgICAgNSxcclxuICAgICAgICAgIG9wdGlvbnMucGF5bG9hZD8ucmVxdWVzdElkXHJcbiAgICAgICAgICAgID8gYFtiZW5jaG1hcmtdIFJlcXVlc3Qgd2l0aCBJRCAke29wdGlvbnMucGF5bG9hZD8ucmVxdWVzdElkfSAtYFxyXG4gICAgICAgICAgICA6ICdbYmVuY2htYXJrXScsXHJcbiAgICAgICAgICBgQWNxdWlyZWQgYSB3b3JrZXIgaGFuZGxlOiAke2FjcXVpcmVDb3VudGVyKCl9bXMuYFxyXG4gICAgICAgICk7XHJcbiAgICAgIH1cclxuICAgIH0gY2F0Y2ggKGVycm9yKSB7XHJcbiAgICAgIHRocm93IG5ldyBFeHBvcnRFcnJvcihcclxuICAgICAgICAob3B0aW9ucy5wYXlsb2FkPy5yZXF1ZXN0SWRcclxuICAgICAgICAgID8gYEZvciByZXF1ZXN0IHdpdGggSUQgJHtvcHRpb25zLnBheWxvYWQ/LnJlcXVlc3RJZH0gLSBgXHJcbiAgICAgICAgICA6ICcnKSArXHJcbiAgICAgICAgICBgRXJyb3IgZW5jb3VudGVyZWQgd2hlbiBhY3F1aXJpbmcgYW4gYXZhaWxhYmxlIGVudHJ5OiAke2FjcXVpcmVDb3VudGVyKCl9bXMuYFxyXG4gICAgICApLnNldEVycm9yKGVycm9yKTtcclxuICAgIH1cclxuICAgIGxvZyg0LCAnW3Bvb2xdIEFjcXVpcmVkIGEgd29ya2VyIGhhbmRsZS4nKTtcclxuXHJcbiAgICBpZiAoIXdvcmtlckhhbmRsZS5wYWdlKSB7XHJcbiAgICAgIHRocm93IG5ldyBFeHBvcnRFcnJvcihcclxuICAgICAgICAnUmVzb2x2ZWQgd29ya2VyIHBhZ2UgaXMgaW52YWxpZDogdGhlIHBvb2wgc2V0dXAgaXMgd29ua3kuJ1xyXG4gICAgICApO1xyXG4gICAgfVxyXG5cclxuICAgIC8vIFNhdmUgdGhlIHN0YXJ0IHRpbWVcclxuICAgIGxldCB3b3JrU3RhcnQgPSBuZXcgRGF0ZSgpLmdldFRpbWUoKTtcclxuXHJcbiAgICBsb2coNCwgYFtwb29sXSBTdGFydGluZyB3b3JrIG9uIHBvb2wgZW50cnkgd2l0aCBJRCAke3dvcmtlckhhbmRsZS5pZH0uYCk7XHJcblxyXG4gICAgLy8gUGVyZm9ybSBhbiBleHBvcnQgb24gYSBwdXBwZXRlZXIgbGV2ZWxcclxuICAgIGNvbnN0IGV4cG9ydENvdW50ZXIgPSBtZWFzdXJlVGltZSgpO1xyXG4gICAgY29uc3QgcmVzdWx0ID0gYXdhaXQgcHVwcGV0ZWVyRXhwb3J0KHdvcmtlckhhbmRsZS5wYWdlLCBjaGFydCwgb3B0aW9ucyk7XHJcblxyXG4gICAgLy8gQ2hlY2sgaWYgaXQncyBhbiBlcnJvclxyXG4gICAgaWYgKHJlc3VsdCBpbnN0YW5jZW9mIEVycm9yKSB7XHJcbiAgICAgIC8vIE5PVEU6IElmIHRoZXJlJ3MgYSByYXN0ZXJpemF0aW9uIHRpbWVvdXQsIHdlIHdhbnQgbmVlZCB0byBmbHVzaCB0aGUgcGFnZS5cclxuICAgICAgLy8gICAgICAgVGhpcyBpcyBiZWNhdXNlIHRoZSBwYWdlIG1heSBiZSBpbiBhIHN0YXRlIHdoZXJlIGl0J3Mgd2FpdGluZyBmb3JcclxuICAgICAgLy8gICAgICAgdGhlIHNjcmVlbnNob3QgdG8gZmluaXNoIGV2ZW4gdGhvdWdoIHRoZSB0aW1lb3V0IGhhcyBvY2N1cmVkLlxyXG4gICAgICAvLyAgICAgICBXaGljaCBvZiBjb3Vyc2UgY2F1c2VzIGEgbG90IG9mIGlzc3VlcyB3aXRoIHRoZSBldmVudCBzeXN0ZW0sXHJcbiAgICAgIC8vICAgICAgIGFuZCBwYWdlIGNvbnNpc3RlbmN5LlxyXG4gICAgICAvL1xyXG4gICAgICAvLyBOT1RFOiBPbmx5IHBhZ2Uuc2NyZWVuc2hvdCB3aWxsIHRocm93IHRoaXMsIHRpbWVvdXRzIGZvciBQREYncyBhcmVcclxuICAgICAgLy8gICAgICAgaGFuZGxlZCBieSB0aGUgcGFnZS5wZGYgZnVuY3Rpb24gaXRzZWxmLlxyXG4gICAgICAvL1xyXG4gICAgICAvLyAgICAgICAuLi55ZXMsIHRoaXMgaXMgdWdseS5cclxuICAgICAgaWYgKHJlc3VsdC5tZXNzYWdlID09PSAnUmFzdGVyaXphdGlvbiB0aW1lb3V0Jykge1xyXG4gICAgICAgIHdvcmtlckhhbmRsZS53b3JrQ291bnQgPSBwb29sQ29uZmlnLndvcmtMaW1pdCArIDE7XHJcbiAgICAgICAgd29ya2VySGFuZGxlLnBhZ2UgPSBudWxsO1xyXG4gICAgICB9XHJcblxyXG4gICAgICBpZiAoXHJcbiAgICAgICAgcmVzdWx0Lm5hbWUgPT09ICdUaW1lb3V0RXJyb3InIHx8XHJcbiAgICAgICAgcmVzdWx0Lm1lc3NhZ2UgPT09ICdSYXN0ZXJpemF0aW9uIHRpbWVvdXQnXHJcbiAgICAgICkge1xyXG4gICAgICAgIHRocm93IG5ldyBFeHBvcnRFcnJvcihcclxuICAgICAgICAgICdSYXN0ZXJpemF0aW9uIHRpbWVvdXQ6IHlvdXIgY2hhcnQgbWF5IGJlIHRvbyBjb21wbGV4IG9yIGxhcmdlLCBhbmQgZmFpbGVkIHRvIHJlbmRlciB3aXRoaW4gdGhlIGFsbG90dGVkIHRpbWUuJ1xyXG4gICAgICAgICkuc2V0RXJyb3IocmVzdWx0KTtcclxuICAgICAgfSBlbHNlIHtcclxuICAgICAgICB0aHJvdyBuZXcgRXhwb3J0RXJyb3IoXHJcbiAgICAgICAgICAob3B0aW9ucy5wYXlsb2FkPy5yZXF1ZXN0SWRcclxuICAgICAgICAgICAgPyBgRm9yIHJlcXVlc3Qgd2l0aCBJRCAke29wdGlvbnMucGF5bG9hZD8ucmVxdWVzdElkfSAtIGBcclxuICAgICAgICAgICAgOiAnJykgKyBgRXJyb3IgZW5jb3VudGVyZWQgZHVyaW5nIGV4cG9ydDogJHtleHBvcnRDb3VudGVyKCl9bXMuYFxyXG4gICAgICAgICkuc2V0RXJyb3IocmVzdWx0KTtcclxuICAgICAgfVxyXG4gICAgfVxyXG5cclxuICAgIC8vIENoZWNrIHRoZSBQdXBwZXRlZXIgZXhwb3J0IHRpbWVcclxuICAgIGlmIChvcHRpb25zLnNlcnZlci5iZW5jaG1hcmtpbmcpIHtcclxuICAgICAgbG9nKFxyXG4gICAgICAgIDUsXHJcbiAgICAgICAgb3B0aW9ucy5wYXlsb2FkPy5yZXF1ZXN0SWRcclxuICAgICAgICAgID8gYFtiZW5jaG1hcmtdIFJlcXVlc3Qgd2l0aCBJRCAke29wdGlvbnMucGF5bG9hZD8ucmVxdWVzdElkfSAtYFxyXG4gICAgICAgICAgOiAnW2JlbmNobWFya10nLFxyXG4gICAgICAgIGBFeHBvcnRlZCBhIGNoYXJ0IHN1Y2Vzc2Z1bGx5OiAke2V4cG9ydENvdW50ZXIoKX1tcy5gXHJcbiAgICAgICk7XHJcbiAgICB9XHJcblxyXG4gICAgLy8gUmVsZWFzZSB0aGUgcmVzb3VyY2UgYmFjayB0byB0aGUgcG9vbFxyXG4gICAgcG9vbC5yZWxlYXNlKHdvcmtlckhhbmRsZSk7XHJcblxyXG4gICAgLy8gVXNlZCBmb3Igc3RhdGlzdGljcyBpbiBhdmVyYWdlVGltZSBhbmQgcHJvY2Vzc2VkV29ya0NvdW50LCB3aGljaFxyXG4gICAgLy8gaW4gdHVybiBpcyB1c2VkIGJ5IHRoZSAvaGVhbHRoIHJvdXRlLlxyXG4gICAgY29uc3Qgd29ya0VuZCA9IG5ldyBEYXRlKCkuZ2V0VGltZSgpO1xyXG4gICAgY29uc3QgZXhwb3J0VGltZSA9IHdvcmtFbmQgLSB3b3JrU3RhcnQ7XHJcbiAgICBzdGF0cy50aW1lU3BlbnQgKz0gZXhwb3J0VGltZTtcclxuICAgIHN0YXRzLnNwZW50QXZlcmFnZSA9IHN0YXRzLnRpbWVTcGVudCAvICsrc3RhdHMucGVyZm9ybWVkRXhwb3J0cztcclxuXHJcbiAgICBsb2coNCwgYFtwb29sXSBXb3JrIGNvbXBsZXRlZCBpbiAke2V4cG9ydFRpbWV9IG1zLmApO1xyXG5cclxuICAgIC8vIE90aGVyd2lzZSByZXR1cm4gdGhlIHJlc3VsdFxyXG4gICAgcmV0dXJuIHtcclxuICAgICAgcmVzdWx0LFxyXG4gICAgICBvcHRpb25zXHJcbiAgICB9O1xyXG4gIH0gY2F0Y2ggKGVycm9yKSB7XHJcbiAgICArK3N0YXRzLmRyb3BwZWRFeHBvcnRzO1xyXG5cclxuICAgIGlmICh3b3JrZXJIYW5kbGUpIHtcclxuICAgICAgcG9vbC5yZWxlYXNlKHdvcmtlckhhbmRsZSk7XHJcbiAgICB9XHJcblxyXG4gICAgdGhyb3cgbmV3IEV4cG9ydEVycm9yKGBbcG9vbF0gSW4gcG9vbC5wb3N0V29yazogJHtlcnJvci5tZXNzYWdlfWApLnNldEVycm9yKFxyXG4gICAgICBlcnJvclxyXG4gICAgKTtcclxuICB9XHJcbn07XHJcblxyXG4vKipcclxuICogUmV0cmlldmVzIHRoZSBjdXJyZW50IHBvb2wgaW5zdGFuY2UuXHJcbiAqXHJcbiAqIEByZXR1cm5zIHtPYmplY3R8bnVsbH0gVGhlIGN1cnJlbnQgcG9vbCBpbnN0YW5jZSBpZiBpbml0aWFsaXplZCwgb3IgbnVsbFxyXG4gKiBpZiB0aGUgcG9vbCBoYXMgbm90IGJlZW4gY3JlYXRlZC5cclxuICovXHJcbmV4cG9ydCBjb25zdCBnZXRQb29sID0gKCkgPT4gcG9vbDtcclxuXHJcbi8qKlxyXG4gKiBSZXRyaWV2ZXMgcG9vbCBpbmZvcm1hdGlvbiBpbiBKU09OIGZvcm1hdCwgaW5jbHVkaW5nIG1pbmltdW0gYW5kIG1heGltdW1cclxuICogd29ya2VycywgYXZhaWxhYmxlIHdvcmtlcnMsIHdvcmtlcnMgaW4gdXNlLCBhbmQgcGVuZGluZyBhY3F1aXJlIHJlcXVlc3RzLlxyXG4gKlxyXG4gKiBAcmV0dXJucyB7T2JqZWN0fSBQb29sIGluZm9ybWF0aW9uIGluIEpTT04gZm9ybWF0LlxyXG4gKi9cclxuZXhwb3J0IGNvbnN0IGdldFBvb2xJbmZvSlNPTiA9ICgpID0+ICh7XHJcbiAgbWluOiBwb29sLm1pbixcclxuICBtYXg6IHBvb2wubWF4LFxyXG4gIGFsbDogcG9vbC5udW1GcmVlKCkgKyBwb29sLm51bVVzZWQoKSxcclxuICBhdmFpbGFibGU6IHBvb2wubnVtRnJlZSgpLFxyXG4gIHVzZWQ6IHBvb2wubnVtVXNlZCgpLFxyXG4gIHBlbmRpbmc6IHBvb2wubnVtUGVuZGluZ0FjcXVpcmVzKClcclxufSk7XHJcblxyXG4vKipcclxuICogTG9ncyBpbmZvcm1hdGlvbiBhYm91dCB0aGUgY3VycmVudCBzdGF0ZSBvZiB0aGUgcG9vbCwgaW5jbHVkaW5nIHRoZSBtaW5pbXVtXHJcbiAqIGFuZCBtYXhpbXVtIHdvcmtlcnMsIGF2YWlsYWJsZSB3b3JrZXJzLCB3b3JrZXJzIGluIHVzZSwgYW5kIHBlbmRpbmcgYWNxdWlyZVxyXG4gKiByZXF1ZXN0cy5cclxuICovXHJcbmV4cG9ydCBmdW5jdGlvbiBnZXRQb29sSW5mbygpIHtcclxuICBjb25zdCB7IG1pbiwgbWF4LCBhbGwsIGF2YWlsYWJsZSwgdXNlZCwgcGVuZGluZyB9ID0gZ2V0UG9vbEluZm9KU09OKCk7XHJcblxyXG4gIGxvZyg1LCBgW3Bvb2xdIFRoZSBtaW5pbXVtIG51bWJlciBvZiByZXNvdXJjZXMgYWxsb3dlZCBieSBwb29sOiAke21pbn0uYCk7XHJcbiAgbG9nKDUsIGBbcG9vbF0gVGhlIG1heGltdW0gbnVtYmVyIG9mIHJlc291cmNlcyBhbGxvd2VkIGJ5IHBvb2w6ICR7bWF4fS5gKTtcclxuICBsb2coNSwgYFtwb29sXSBUaGUgbnVtYmVyIG9mIGFsbCBjcmVhdGVkIHJlc291cmNlczogJHthbGx9LmApO1xyXG4gIGxvZyg1LCBgW3Bvb2xdIFRoZSBudW1iZXIgb2YgYXZhaWxhYmxlIHJlc291cmNlczogJHthdmFpbGFibGV9LmApO1xyXG4gIGxvZyg1LCBgW3Bvb2xdIFRoZSBudW1iZXIgb2YgYWNxdWlyZWQgcmVzb3VyY2VzOiAke3VzZWR9LmApO1xyXG4gIGxvZyg1LCBgW3Bvb2xdIFRoZSBudW1iZXIgb2YgcmVzb3VyY2VzIHdhaXRpbmcgdG8gYmUgYWNxdWlyZWQ6ICR7cGVuZGluZ30uYCk7XHJcbn1cclxuXHJcbmV4cG9ydCBkZWZhdWx0IHtcclxuICBpbml0UG9vbCxcclxuICBraWxsUG9vbCxcclxuICBwb3N0V29yayxcclxuICBnZXRQb29sLFxyXG4gIGdldFBvb2xJbmZvLFxyXG4gIGdldFBvb2xJbmZvSlNPTixcclxuICBnZXRTdGF0czogKCkgPT4gc3RhdHNcclxufTtcclxuIiwiLyoqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKipcclxuXHJcbkhpZ2hjaGFydHMgRXhwb3J0IFNlcnZlclxyXG5cclxuQ29weXJpZ2h0IChjKSAyMDE2LTIwMjQsIEhpZ2hzb2Z0XHJcblxyXG5MaWNlbmNlZCB1bmRlciB0aGUgTUlUIGxpY2VuY2UuXHJcblxyXG5BZGRpdGlvbmFsbHkgYSB2YWxpZCBIaWdoY2hhcnRzIGxpY2Vuc2UgaXMgcmVxdWlyZWQgZm9yIHVzZS5cclxuXHJcblNlZSBMSUNFTlNFIGZpbGUgaW4gcm9vdCBmb3IgZGV0YWlscy5cclxuXHJcbioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKiovXHJcblxyXG5pbXBvcnQgeyByZWFkRmlsZVN5bmMsIHdyaXRlRmlsZVN5bmMgfSBmcm9tICdmcyc7XHJcblxyXG5pbXBvcnQgeyBnZXRPcHRpb25zLCBpbml0RXhwb3J0U2V0dGluZ3MgfSBmcm9tICcuL2NvbmZpZy5qcyc7XHJcbmltcG9ydCB7IGxvZywgbG9nV2l0aFN0YWNrIH0gZnJvbSAnLi9sb2dnZXIuanMnO1xyXG5pbXBvcnQgeyBraWxsUG9vbCwgcG9zdFdvcmssIHN0YXRzIH0gZnJvbSAnLi9wb29sLmpzJztcclxuaW1wb3J0IHtcclxuICBmaXhUeXBlLFxyXG4gIGhhbmRsZVJlc291cmNlcyxcclxuICBpc0NvcnJlY3RKU09OLFxyXG4gIG9wdGlvbnNTdHJpbmdpZnksXHJcbiAgcm91bmROdW1iZXIsXHJcbiAgdG9Cb29sZWFuLFxyXG4gIHdyYXBBcm91bmRcclxufSBmcm9tICcuL3V0aWxzLmpzJztcclxuaW1wb3J0IHsgc2FuaXRpemUgfSBmcm9tICcuL3Nhbml0aXplLmpzJztcclxuaW1wb3J0IEV4cG9ydEVycm9yIGZyb20gJy4vZXJyb3JzL0V4cG9ydEVycm9yLmpzJztcclxuXHJcbmxldCBhbGxvd0NvZGVFeGVjdXRpb24gPSBmYWxzZTtcclxuXHJcbi8qKlxyXG4gKiBTdGFydHMgYW4gZXhwb3J0IHByb2Nlc3MuIFRoZSBgc2V0dGluZ3NgIGNvbnRhaW5zIGZpbmFsIG9wdGlvbnMgZ2F0aGVyZWRcclxuICogZnJvbSBhbGwgcG9zc2libGUgc291cmNlcyAoY29uZmlnLCBlbnYsIGNsaSwganNvbikuIFRoZSBgZW5kQ2FsbGJhY2tgIGlzXHJcbiAqIGNhbGxlZCB3aGVuIHRoZSBleHBvcnQgaXMgY29tcGxldGVkLCB3aXRoIGFuIGVycm9yIG9iamVjdCBhcyB0aGUgZmlyc3RcclxuICogYXJndW1lbnQgYW5kIHRoZSBzZWNvbmQgY29udGFpbmluZyB0aGUgYmFzZTY0IHJlc3ByZXNlbnRhdGlvbiBvZiBhIGNoYXJ0LlxyXG4gKlxyXG4gKiBAcGFyYW0ge09iamVjdH0gc2V0dGluZ3MgLSBUaGUgc2V0dGluZ3Mgb2JqZWN0IGNvbnRhaW5pbmcgZXhwb3J0XHJcbiAqIGNvbmZpZ3VyYXRpb24uXHJcbiAqIEBwYXJhbSB7ZnVuY3Rpb259IGVuZENhbGxiYWNrIC0gVGhlIGNhbGxiYWNrIGZ1bmN0aW9uIHRvIGJlIGludm9rZWQgdXBvblxyXG4gKiBmaW5hbGl6aW5nIHdvcmsgb3IgdXBvbiBlcnJvciBvY2N1cmFuY2Ugb2YgdGhlIGV4cG9ydGluZyBwcm9jZXNzLlxyXG4gKlxyXG4gKiBAcmV0dXJucyB7dm9pZH0gVGhpcyBmdW5jdGlvbiBkb2VzIG5vdCByZXR1cm4gYSB2YWx1ZSBkaXJlY3RseTsgaW5zdGVhZCxcclxuICogaXQgY29tbXVuaWNhdGVzIHJlc3VsdHMgdmlhIHRoZSBlbmRDYWxsYmFjay5cclxuICovXHJcbmV4cG9ydCBjb25zdCBzdGFydEV4cG9ydCA9IGFzeW5jIChzZXR0aW5ncywgZW5kQ2FsbGJhY2spID0+IHtcclxuICAvLyBTdGFydGluZyBleHBvcnRpbmcgcHJvY2VzcyBtZXNzYWdlXHJcbiAgbG9nKDQsICdbY2hhcnRdIFN0YXJ0aW5nIHRoZSBleHBvcnRpbmcgcHJvY2Vzcy4nKTtcclxuXHJcbiAgLy8gSW5pdGlhbGl6ZSBvcHRpb25zXHJcbiAgY29uc3Qgb3B0aW9ucyA9IGluaXRFeHBvcnRTZXR0aW5ncyhzZXR0aW5ncywgZ2V0T3B0aW9ucygpKTtcclxuXHJcbiAgLy8gR2V0IHRoZSBleHBvcnQgb3B0aW9uc1xyXG4gIGNvbnN0IGV4cG9ydE9wdGlvbnMgPSBvcHRpb25zLmV4cG9ydDtcclxuXHJcbiAgLy8gSWYgU1ZHIGlzIGFuIGlucHV0IChhcmd1bWVudCBjYW4gYmUgc2VudCBvbmx5IGJ5IHRoZSByZXF1ZXN0KVxyXG4gIGlmIChvcHRpb25zLnBheWxvYWQ/LnN2ZyAmJiBvcHRpb25zLnBheWxvYWQuc3ZnICE9PSAnJykge1xyXG4gICAgdHJ5IHtcclxuICAgICAgbG9nKDQsICdbY2hhcnRdIEF0dGVtcHRpbmcgdG8gZXhwb3J0IGZyb20gYSBTVkcgaW5wdXQuJyk7XHJcblxyXG4gICAgICBjb25zdCByZXN1bHQgPSBleHBvcnRBc1N0cmluZyhcclxuICAgICAgICBzYW5pdGl6ZShvcHRpb25zLnBheWxvYWQuc3ZnKSwgLy8gIzIwOVxyXG4gICAgICAgIG9wdGlvbnMsXHJcbiAgICAgICAgZW5kQ2FsbGJhY2tcclxuICAgICAgKTtcclxuXHJcbiAgICAgICsrc3RhdHMuZXhwb3J0RnJvbVN2Z0F0dGVtcHRzO1xyXG4gICAgICByZXR1cm4gcmVzdWx0O1xyXG4gICAgfSBjYXRjaCAoZXJyb3IpIHtcclxuICAgICAgcmV0dXJuIGVuZENhbGxiYWNrKFxyXG4gICAgICAgIG5ldyBFeHBvcnRFcnJvcignW2NoYXJ0XSBFcnJvciBsb2FkaW5nIFNWRyBpbnB1dC4nKS5zZXRFcnJvcihlcnJvcilcclxuICAgICAgKTtcclxuICAgIH1cclxuICB9XHJcblxyXG4gIC8vIEV4cG9ydCB1c2luZyBvcHRpb25zIGZyb20gdGhlIGZpbGVcclxuICBpZiAoZXhwb3J0T3B0aW9ucy5pbmZpbGUgJiYgZXhwb3J0T3B0aW9ucy5pbmZpbGUubGVuZ3RoKSB7XHJcbiAgICAvLyBUcnkgdG8gcmVhZCB0aGUgZmlsZSB0byBnZXQgdGhlIHN0cmluZyByZXByZXNlbnRhdGlvblxyXG4gICAgdHJ5IHtcclxuICAgICAgbG9nKDQsICdbY2hhcnRdIEF0dGVtcHRpbmcgdG8gZXhwb3J0IGZyb20gYW4gaW5wdXQgZmlsZS4nKTtcclxuICAgICAgb3B0aW9ucy5leHBvcnQuaW5zdHIgPSByZWFkRmlsZVN5bmMoZXhwb3J0T3B0aW9ucy5pbmZpbGUsICd1dGY4Jyk7XHJcbiAgICAgIHJldHVybiBleHBvcnRBc1N0cmluZyhvcHRpb25zLmV4cG9ydC5pbnN0ci50cmltKCksIG9wdGlvbnMsIGVuZENhbGxiYWNrKTtcclxuICAgIH0gY2F0Y2ggKGVycm9yKSB7XHJcbiAgICAgIHJldHVybiBlbmRDYWxsYmFjayhcclxuICAgICAgICBuZXcgRXhwb3J0RXJyb3IoJ1tjaGFydF0gRXJyb3IgbG9hZGluZyBpbnB1dCBmaWxlLicpLnNldEVycm9yKGVycm9yKVxyXG4gICAgICApO1xyXG4gICAgfVxyXG4gIH1cclxuXHJcbiAgLy8gRXhwb3J0IHdpdGggb3B0aW9ucyBmcm9tIHRoZSByYXcgcmVwcmVzZW50YXRpb25cclxuICBpZiAoXHJcbiAgICAoZXhwb3J0T3B0aW9ucy5pbnN0ciAmJiBleHBvcnRPcHRpb25zLmluc3RyICE9PSAnJykgfHxcclxuICAgIChleHBvcnRPcHRpb25zLm9wdGlvbnMgJiYgZXhwb3J0T3B0aW9ucy5vcHRpb25zICE9PSAnJylcclxuICApIHtcclxuICAgIHRyeSB7XHJcbiAgICAgIGxvZyg0LCAnW2NoYXJ0XSBBdHRlbXB0aW5nIHRvIGV4cG9ydCBmcm9tIGEgcmF3IGlucHV0LicpO1xyXG5cclxuICAgICAgLy8gVXNlIHdoaWNoZXZlciBvbmUgaXMgYXZhaWxhYmxlXHJcbiAgICAgIGV4cG9ydE9wdGlvbnMuaW5zdHIgPSBleHBvcnRPcHRpb25zLmluc3RyIHx8IGV4cG9ydE9wdGlvbnMub3B0aW9ucztcclxuXHJcbiAgICAgIC8vIFBlcmZvcm0gYSBkaXJlY3QgaW5qZWN0IHdoZW4gZm9yY2VkXHJcbiAgICAgIGlmICh0b0Jvb2xlYW4ob3B0aW9ucy5jdXN0b21Mb2dpYz8uYWxsb3dDb2RlRXhlY3V0aW9uKSkge1xyXG4gICAgICAgIHJldHVybiBkb1N0cmFpZ2h0SW5qZWN0KG9wdGlvbnMsIGVuZENhbGxiYWNrKTtcclxuICAgICAgfVxyXG5cclxuICAgICAgLy8gRWl0aGVyIHRyeSB0byBwYXJzZSB0byBKU09OIGZpcnN0IG9yIGRvIHRoZSBkaXJlY3QgZXhwb3J0XHJcbiAgICAgIHJldHVybiB0eXBlb2YgZXhwb3J0T3B0aW9ucy5pbnN0ciA9PT0gJ3N0cmluZydcclxuICAgICAgICA/IGV4cG9ydEFzU3RyaW5nKGV4cG9ydE9wdGlvbnMuaW5zdHIudHJpbSgpLCBvcHRpb25zLCBlbmRDYWxsYmFjaylcclxuICAgICAgICA6IGRvRXhwb3J0KFxyXG4gICAgICAgICAgICBvcHRpb25zLFxyXG4gICAgICAgICAgICBleHBvcnRPcHRpb25zLmluc3RyIHx8IGV4cG9ydE9wdGlvbnMub3B0aW9ucyxcclxuICAgICAgICAgICAgZW5kQ2FsbGJhY2tcclxuICAgICAgICAgICk7XHJcbiAgICB9IGNhdGNoIChlcnJvcikge1xyXG4gICAgICByZXR1cm4gZW5kQ2FsbGJhY2soXHJcbiAgICAgICAgbmV3IEV4cG9ydEVycm9yKCdbY2hhcnRdIEVycm9yIGxvYWRpbmcgcmF3IGlucHV0LicpLnNldEVycm9yKGVycm9yKVxyXG4gICAgICApO1xyXG4gICAgfVxyXG4gIH1cclxuXHJcbiAgLy8gTm8gaW5wdXQgc3BlY2lmaWVkLCBwYXNzIGFuIGVycm9yIG1lc3NhZ2UgdG8gdGhlIGNhbGxiYWNrXHJcbiAgcmV0dXJuIGVuZENhbGxiYWNrKFxyXG4gICAgbmV3IEV4cG9ydEVycm9yKFxyXG4gICAgICBgW2NoYXJ0XSBObyB2YWxpZCBpbnB1dCBzcGVjaWZpZWQuIENoZWNrIGlmIGF0IGxlYXN0IG9uZSBvZiB0aGUgZm9sbG93aW5nIHBhcmFtZXRlcnMgaXMgY29ycmVjdGx5IHNldDogJ2luZmlsZScsICdpbnN0cicsICdvcHRpb25zJywgb3IgJ3N2ZycuYFxyXG4gICAgKVxyXG4gICk7XHJcbn07XHJcblxyXG4vKipcclxuICogU3RhcnRzIGEgYmF0Y2ggZXhwb3J0IHByb2Nlc3MgZm9yIG11bHRpcGxlIGNoYXJ0cyBiYXNlZCBvbiB0aGUgaW5mb3JtYXRpb25cclxuICogaW4gdGhlIGJhdGNoIG9wdGlvbi4gVGhlIGJhdGNoIGlzIGEgc3RyaW5nIGluIHRoZSBmb2xsb3dpbmcgZm9ybWF0OlxyXG4gKiBcImluZmlsZTEuanNvbj1vdXRmaWxlMS5wbmc7aW5maWxlMi5qc29uPW91dGZpbGUyLnBuZzsuLi5cIlxyXG4gKlxyXG4gKiBAcGFyYW0ge09iamVjdH0gb3B0aW9ucyAtIFRoZSBvcHRpb25zIG9iamVjdCBjb250YWluaW5nIGNvbmZpZ3VyYXRpb24gZm9yXHJcbiAqIGEgYmF0Y2ggZXhwb3J0LlxyXG4gKlxyXG4gKiBAcmV0dXJucyB7UHJvbWlzZTx2b2lkPn0gQSBQcm9taXNlIHRoYXQgcmVzb2x2ZXMgb25jZSB0aGUgYmF0Y2ggZXhwb3J0XHJcbiAqIHByb2Nlc3MgaXMgY29tcGxldGVkLlxyXG4gKlxyXG4gKiBAdGhyb3dzIHtFeHBvcnRFcnJvcn0gVGhyb3dzIGFuIEV4cG9ydEVycm9yIGlmIGFuIGVycm9yIG9jY3VycyBkdXJpbmdcclxuICogYW55IG9mIHRoZSBiYXRjaCBleHBvcnQgcHJvY2Vzcy5cclxuICovXHJcbmV4cG9ydCBjb25zdCBiYXRjaEV4cG9ydCA9IGFzeW5jIChvcHRpb25zKSA9PiB7XHJcbiAgY29uc3QgYmF0Y2hGdW5jdGlvbnMgPSBbXTtcclxuXHJcbiAgLy8gU3BsaXQgYW5kIHBhaXIgdGhlIC0tYmF0Y2ggYXJndW1lbnRzXHJcbiAgZm9yIChsZXQgcGFpciBvZiBvcHRpb25zLmV4cG9ydC5iYXRjaC5zcGxpdCgnOycpKSB7XHJcbiAgICBwYWlyID0gcGFpci5zcGxpdCgnPScpO1xyXG4gICAgaWYgKHBhaXIubGVuZ3RoID09PSAyKSB7XHJcbiAgICAgIGJhdGNoRnVuY3Rpb25zLnB1c2goXHJcbiAgICAgICAgc3RhcnRFeHBvcnQoXHJcbiAgICAgICAgICB7XHJcbiAgICAgICAgICAgIC4uLm9wdGlvbnMsXHJcbiAgICAgICAgICAgIGV4cG9ydDoge1xyXG4gICAgICAgICAgICAgIC4uLm9wdGlvbnMuZXhwb3J0LFxyXG4gICAgICAgICAgICAgIGluZmlsZTogcGFpclswXSxcclxuICAgICAgICAgICAgICBvdXRmaWxlOiBwYWlyWzFdXHJcbiAgICAgICAgICAgIH1cclxuICAgICAgICAgIH0sXHJcbiAgICAgICAgICAoZXJyb3IsIGluZm8pID0+IHtcclxuICAgICAgICAgICAgLy8gVGhyb3cgYW4gZXJyb3JcclxuICAgICAgICAgICAgaWYgKGVycm9yKSB7XHJcbiAgICAgICAgICAgICAgdGhyb3cgZXJyb3I7XHJcbiAgICAgICAgICAgIH1cclxuXHJcbiAgICAgICAgICAgIC8vIFNhdmUgdGhlIGJhc2U2NCBmcm9tIGEgYnVmZmVyIHRvIGEgY29ycmVjdCBpbWFnZSBmaWxlXHJcbiAgICAgICAgICAgIHdyaXRlRmlsZVN5bmMoXHJcbiAgICAgICAgICAgICAgaW5mby5vcHRpb25zLmV4cG9ydC5vdXRmaWxlLFxyXG4gICAgICAgICAgICAgIGluZm8ub3B0aW9ucy5leHBvcnQudHlwZSAhPT0gJ3N2ZydcclxuICAgICAgICAgICAgICAgID8gQnVmZmVyLmZyb20oaW5mby5yZXN1bHQsICdiYXNlNjQnKVxyXG4gICAgICAgICAgICAgICAgOiBpbmZvLnJlc3VsdFxyXG4gICAgICAgICAgICApO1xyXG4gICAgICAgICAgfVxyXG4gICAgICAgIClcclxuICAgICAgKTtcclxuICAgIH1cclxuICB9XHJcblxyXG4gIHRyeSB7XHJcbiAgICAvLyBBd2FpdCBhbGwgZXhwb3J0cyBhcmUgZG9uZVxyXG4gICAgYXdhaXQgUHJvbWlzZS5hbGwoYmF0Y2hGdW5jdGlvbnMpO1xyXG5cclxuICAgIC8vIEtpbGwgcG9vbCBhbmQgY2xvc2UgYnJvd3NlciBhZnRlciBmaW5pc2hpbmcgYmF0Y2ggZXhwb3J0XHJcbiAgICBhd2FpdCBraWxsUG9vbCgpO1xyXG4gIH0gY2F0Y2ggKGVycm9yKSB7XHJcbiAgICB0aHJvdyBuZXcgRXhwb3J0RXJyb3IoXHJcbiAgICAgICdbY2hhcnRdIEVycm9yIGVuY291bnRlcmVkIGR1cmluZyBiYXRjaCBleHBvcnQuJ1xyXG4gICAgKS5zZXRFcnJvcihlcnJvcik7XHJcbiAgfVxyXG59O1xyXG5cclxuLyoqXHJcbiAqIFN0YXJ0cyBhIHNpbmdsZSBleHBvcnQgcHJvY2VzcyBiYXNlZCBvbiB0aGUgc3BlY2lmaWVkIG9wdGlvbnMuXHJcbiAqXHJcbiAqIEBwYXJhbSB7T2JqZWN0fSBvcHRpb25zIC0gVGhlIG9wdGlvbnMgb2JqZWN0IGNvbnRhaW5pbmcgY29uZmlndXJhdGlvbiBmb3JcclxuICogYSBzaW5nbGUgZXhwb3J0LlxyXG4gKlxyXG4gKiBAcmV0dXJucyB7UHJvbWlzZTx2b2lkPn0gQSBQcm9taXNlIHRoYXQgcmVzb2x2ZXMgb25jZSB0aGUgc2luZ2xlIGV4cG9ydFxyXG4gKiBwcm9jZXNzIGlzIGNvbXBsZXRlZC5cclxuICpcclxuICogQHRocm93cyB7RXhwb3J0RXJyb3J9IFRocm93cyBhbiBFeHBvcnRFcnJvciBpZiBhbiBlcnJvciBvY2N1cnMgZHVyaW5nXHJcbiAqIHRoZSBzaW5nbGUgZXhwb3J0IHByb2Nlc3MuXHJcbiAqL1xyXG5leHBvcnQgY29uc3Qgc2luZ2xlRXhwb3J0ID0gYXN5bmMgKG9wdGlvbnMpID0+IHtcclxuICAvLyBVc2UgaW5zdHIgb3IgaXRzIGFsaWFzLCBvcHRpb25zXHJcbiAgb3B0aW9ucy5leHBvcnQuaW5zdHIgPSBvcHRpb25zLmV4cG9ydC5pbnN0ciB8fCBvcHRpb25zLmV4cG9ydC5vcHRpb25zO1xyXG5cclxuICAvLyBQZXJmb3JtIGFuIGV4cG9ydFxyXG4gIGF3YWl0IHN0YXJ0RXhwb3J0KG9wdGlvbnMsIGFzeW5jIChlcnJvciwgaW5mbykgPT4ge1xyXG4gICAgLy8gRXhpdCBwcm9jZXNzIHdoZW4gZXJyb3JcclxuICAgIGlmIChlcnJvcikge1xyXG4gICAgICB0aHJvdyBlcnJvcjtcclxuICAgIH1cclxuXHJcbiAgICBjb25zdCB7IG91dGZpbGUsIHR5cGUgfSA9IGluZm8ub3B0aW9ucy5leHBvcnQ7XHJcblxyXG4gICAgLy8gU2F2ZSB0aGUgYmFzZTY0IGZyb20gYSBidWZmZXIgdG8gYSBjb3JyZWN0IGltYWdlIGZpbGVcclxuICAgIHdyaXRlRmlsZVN5bmMoXHJcbiAgICAgIG91dGZpbGUgfHwgYGNoYXJ0LiR7dHlwZX1gLFxyXG4gICAgICB0eXBlICE9PSAnc3ZnJyA/IEJ1ZmZlci5mcm9tKGluZm8ucmVzdWx0LCAnYmFzZTY0JykgOiBpbmZvLnJlc3VsdFxyXG4gICAgKTtcclxuXHJcbiAgICAvLyBLaWxsIHBvb2wgYW5kIGNsb3NlIGJyb3dzZXIgYWZ0ZXIgZmluaXNoaW5nIHNpbmdsZSBleHBvcnRcclxuICAgIGF3YWl0IGtpbGxQb29sKCk7XHJcbiAgfSk7XHJcbn07XHJcblxyXG4vKipcclxuICogRGV0ZXJtaW5lcyB0aGUgc2l6ZSBhbmQgc2NhbGUgZm9yIGNoYXJ0IGV4cG9ydCBiYXNlZCBvbiB0aGUgcHJvdmlkZWQgb3B0aW9ucy5cclxuICpcclxuICogQHBhcmFtIHtPYmplY3R9IG9wdGlvbnMgLSBUaGUgb3B0aW9ucyBvYmplY3QgY29udGFpbmluZyBjb25maWd1cmF0aW9uIGZvclxyXG4gKiBjaGFydCBleHBvcnQuXHJcbiAqXHJcbiAqIEByZXR1cm5zIHtPYmplY3R9IEFuIG9iamVjdCBjb250YWluaW5nIHRoZSBjYWxjdWxhdGVkIGhlaWdodCwgd2lkdGgsXHJcbiAqIGFuZCBzY2FsZSBmb3IgdGhlIGNoYXJ0IGV4cG9ydC5cclxuICovXHJcbmV4cG9ydCBjb25zdCBmaW5kQ2hhcnRTaXplID0gKG9wdGlvbnMpID0+IHtcclxuICBjb25zdCB7IGNoYXJ0LCBleHBvcnRpbmcgfSA9XHJcbiAgICBvcHRpb25zLmV4cG9ydD8ub3B0aW9ucyB8fCBpc0NvcnJlY3RKU09OKG9wdGlvbnMuZXhwb3J0Py5pbnN0cik7XHJcblxyXG4gIC8vIFNlZSBpZiBnbG9iYWxPcHRpb25zIGhvbGRzIGNoYXJ0IG9yIGV4cG9ydGluZyBzaXplXHJcbiAgY29uc3QgZ2xvYmFsT3B0aW9ucyA9IGlzQ29ycmVjdEpTT04ob3B0aW9ucy5leHBvcnQ/Lmdsb2JhbE9wdGlvbnMpO1xyXG5cclxuICAvLyBTZWN1cmUgc2NhbGUgdmFsdWVcclxuICBsZXQgc2NhbGUgPVxyXG4gICAgb3B0aW9ucy5leHBvcnQ/LnNjYWxlIHx8XHJcbiAgICBleHBvcnRpbmc/LnNjYWxlIHx8XHJcbiAgICBnbG9iYWxPcHRpb25zPy5leHBvcnRpbmc/LnNjYWxlIHx8XHJcbiAgICBvcHRpb25zLmV4cG9ydD8uZGVmYXVsdFNjYWxlIHx8XHJcbiAgICAxO1xyXG5cclxuICAvLyB0aGUgc2NhbGUgY2Fubm90IGJlIGxvd2VyIHRoYW4gMC4xIGFuZCBjYW5ub3QgYmUgaGlnaGVyIHRoYW4gNS4wXHJcbiAgc2NhbGUgPSBNYXRoLm1heCgwLjEsIE1hdGgubWluKHNjYWxlLCA1LjApKTtcclxuXHJcbiAgLy8gd2Ugd2FudCB0byByb3VuZCB0aGUgbnVtYmVycyBsaWtlIDAuMjMyMzQgLT4gMC4yM1xyXG4gIHNjYWxlID0gcm91bmROdW1iZXIoc2NhbGUsIDIpO1xyXG5cclxuICAvLyBGaW5kIGNoYXJ0IHNpemUgYW5kIHNjYWxlXHJcbiAgY29uc3Qgc2l6ZSA9IHtcclxuICAgIGhlaWdodDpcclxuICAgICAgb3B0aW9ucy5leHBvcnQ/LmhlaWdodCB8fFxyXG4gICAgICBleHBvcnRpbmc/LnNvdXJjZUhlaWdodCB8fFxyXG4gICAgICBjaGFydD8uaGVpZ2h0IHx8XHJcbiAgICAgIGdsb2JhbE9wdGlvbnM/LmV4cG9ydGluZz8uc291cmNlSGVpZ2h0IHx8XHJcbiAgICAgIGdsb2JhbE9wdGlvbnM/LmNoYXJ0Py5oZWlnaHQgfHxcclxuICAgICAgb3B0aW9ucy5leHBvcnQ/LmRlZmF1bHRIZWlnaHQgfHxcclxuICAgICAgNDAwLFxyXG4gICAgd2lkdGg6XHJcbiAgICAgIG9wdGlvbnMuZXhwb3J0Py53aWR0aCB8fFxyXG4gICAgICBleHBvcnRpbmc/LnNvdXJjZVdpZHRoIHx8XHJcbiAgICAgIGNoYXJ0Py53aWR0aCB8fFxyXG4gICAgICBnbG9iYWxPcHRpb25zPy5leHBvcnRpbmc/LnNvdXJjZVdpZHRoIHx8XHJcbiAgICAgIGdsb2JhbE9wdGlvbnM/LmNoYXJ0Py53aWR0aCB8fFxyXG4gICAgICBvcHRpb25zLmV4cG9ydD8uZGVmYXVsdFdpZHRoIHx8XHJcbiAgICAgIDYwMCxcclxuICAgIHNjYWxlXHJcbiAgfTtcclxuXHJcbiAgLy8gR2V0IHJpZCBvZiBwb3RlbnRpYWwgcHggYW5kICVcclxuICBmb3IgKGxldCBbcGFyYW0sIHZhbHVlXSBvZiBPYmplY3QuZW50cmllcyhzaXplKSkge1xyXG4gICAgc2l6ZVtwYXJhbV0gPVxyXG4gICAgICB0eXBlb2YgdmFsdWUgPT09ICdzdHJpbmcnID8gK3ZhbHVlLnJlcGxhY2UoL3B4fCUvZ2ksICcnKSA6IHZhbHVlO1xyXG4gIH1cclxuICByZXR1cm4gc2l6ZTtcclxufTtcclxuXHJcbi8qKlxyXG4gKiBGdW5jdGlvbiBmb3IgZmluYWxpemluZyBvcHRpb25zIGJlZm9yZSBleHBvcnQuXHJcbiAqXHJcbiAqIEBwYXJhbSB7T2JqZWN0fSBvcHRpb25zIC0gVGhlIG9wdGlvbnMgb2JqZWN0IGNvbnRhaW5pbmcgY29uZmlndXJhdGlvbiBmb3JcclxuICogdGhlIGV4cG9ydCBwcm9jZXNzLlxyXG4gKiBAcGFyYW0ge09iamVjdH0gY2hhcnRKc29uIC0gVGhlIEpTT04gcmVwcmVzZW50YXRpb24gb2YgdGhlIGNoYXJ0LlxyXG4gKiBAcGFyYW0ge0Z1bmN0aW9ufSBlbmRDYWxsYmFjayAtIFRoZSBjYWxsYmFjayBmdW5jdGlvbiB0byBiZSBjYWxsZWQgdXBvblxyXG4gKiBjb21wbGV0aW9uIG9yIGVycm9yLlxyXG4gKiBAcGFyYW0ge3N0cmluZ30gc3ZnIC0gVGhlIFNWRyByZXByZXNlbnRhdGlvbiBvZiB0aGUgY2hhcnQuXHJcbiAqXHJcbiAqIEByZXR1cm5zIHtQcm9taXNlPHZvaWQ+fSBBIFByb21pc2UgdGhhdCByZXNvbHZlcyBvbmNlIHRoZSBleHBvcnQgcHJvY2Vzc1xyXG4gKiBpcyBjb21wbGV0ZWQuXHJcbiAqL1xyXG5jb25zdCBkb0V4cG9ydCA9IGFzeW5jIChvcHRpb25zLCBjaGFydEpzb24sIGVuZENhbGxiYWNrLCBzdmcpID0+IHtcclxuICBsZXQgeyBleHBvcnQ6IGV4cG9ydE9wdGlvbnMsIGN1c3RvbUxvZ2ljOiBjdXN0b21Mb2dpY09wdGlvbnMgfSA9IG9wdGlvbnM7XHJcblxyXG4gIGNvbnN0IGFsbG93Q29kZUV4ZWN1dGlvblNjb3BlZCA9XHJcbiAgICB0eXBlb2YgY3VzdG9tTG9naWNPcHRpb25zLmFsbG93Q29kZUV4ZWN1dGlvbiA9PT0gJ2Jvb2xlYW4nXHJcbiAgICAgID8gY3VzdG9tTG9naWNPcHRpb25zLmFsbG93Q29kZUV4ZWN1dGlvblxyXG4gICAgICA6IGFsbG93Q29kZUV4ZWN1dGlvbjtcclxuXHJcbiAgaWYgKCFjdXN0b21Mb2dpY09wdGlvbnMpIHtcclxuICAgIGN1c3RvbUxvZ2ljT3B0aW9ucyA9IG9wdGlvbnMuY3VzdG9tTG9naWMgPSB7fTtcclxuICB9IGVsc2UgaWYgKGFsbG93Q29kZUV4ZWN1dGlvblNjb3BlZCkge1xyXG4gICAgaWYgKHR5cGVvZiBvcHRpb25zLmN1c3RvbUxvZ2ljLnJlc291cmNlcyA9PT0gJ3N0cmluZycpIHtcclxuICAgICAgLy8gUHJvY2VzcyByZXNvdXJjZXNcclxuICAgICAgb3B0aW9ucy5jdXN0b21Mb2dpYy5yZXNvdXJjZXMgPSBoYW5kbGVSZXNvdXJjZXMoXHJcbiAgICAgICAgb3B0aW9ucy5jdXN0b21Mb2dpYy5yZXNvdXJjZXMsXHJcbiAgICAgICAgdG9Cb29sZWFuKG9wdGlvbnMuY3VzdG9tTG9naWMuYWxsb3dGaWxlUmVzb3VyY2VzKVxyXG4gICAgICApO1xyXG4gICAgfSBlbHNlIGlmICghb3B0aW9ucy5jdXN0b21Mb2dpYy5yZXNvdXJjZXMpIHtcclxuICAgICAgdHJ5IHtcclxuICAgICAgICBjb25zdCByZXNvdXJjZXMgPSByZWFkRmlsZVN5bmMoJ3Jlc291cmNlcy5qc29uJywgJ3V0ZjgnKTtcclxuICAgICAgICBvcHRpb25zLmN1c3RvbUxvZ2ljLnJlc291cmNlcyA9IGhhbmRsZVJlc291cmNlcyhcclxuICAgICAgICAgIHJlc291cmNlcyxcclxuICAgICAgICAgIHRvQm9vbGVhbihvcHRpb25zLmN1c3RvbUxvZ2ljLmFsbG93RmlsZVJlc291cmNlcylcclxuICAgICAgICApO1xyXG4gICAgICB9IGNhdGNoIChlcnJvcikge1xyXG4gICAgICAgIGxvZygyLCBgW2NoYXJ0XSBVbmFibGUgdG8gbG9hZCB0aGUgZGVmYXVsdCByZXNvdXJjZXMuanNvbiBmaWxlLmApO1xyXG4gICAgICB9XHJcbiAgICB9XHJcbiAgfVxyXG5cclxuICAvLyBJZiB0aGUgYWxsb3dDb2RlRXhlY3V0aW9uIGZsYWcgaXNuJ3Qgc2V0LCB3ZSBzaG91bGQgcmVmdXNlIHRoZSB1c2FnZVxyXG4gIC8vIG9mIGNhbGxiYWNrLCByZXNvdXJjZXMsIGFuZCBjdXN0b20gY29kZS4gQWRkaXRpb25hbGx5LCB0aGUgd29ya2VyIHdpbGxcclxuICAvLyByZWZ1c2UgdG8gcnVuIGFyYml0cmFyeSBKYXZhU2NyaXB0LiBQcmlvcml0aXplZCBzaG91bGQgYmUgdGhlIHNjb3BlZFxyXG4gIC8vIG9wdGlvbiwgdGhlbiB3ZSBzaG91bGQgdGFrZSBhIGxvb2sgYXQgdGhlIG92ZXJhbGwgcG9vbCBvcHRpb24uXHJcbiAgaWYgKCFhbGxvd0NvZGVFeGVjdXRpb25TY29wZWQgJiYgY3VzdG9tTG9naWNPcHRpb25zKSB7XHJcbiAgICBpZiAoXHJcbiAgICAgIGN1c3RvbUxvZ2ljT3B0aW9ucy5jYWxsYmFjayB8fFxyXG4gICAgICBjdXN0b21Mb2dpY09wdGlvbnMucmVzb3VyY2VzIHx8XHJcbiAgICAgIGN1c3RvbUxvZ2ljT3B0aW9ucy5jdXN0b21Db2RlXHJcbiAgICApIHtcclxuICAgICAgLy8gU2VuZCBiYWNrIGEgZnJpZW5kbHkgbWVzc2FnZSBzYXlpbmcgdGhhdCB0aGUgZXhwb3J0ZXIgZG9lcyBub3Qgc3VwcG9ydFxyXG4gICAgICAvLyB0aGVzZSBzZXR0aW5ncy5cclxuICAgICAgcmV0dXJuIGVuZENhbGxiYWNrKFxyXG4gICAgICAgIG5ldyBFeHBvcnRFcnJvcihcclxuICAgICAgICAgIGBbY2hhcnRdIFRoZSAnY2FsbGJhY2snLCAncmVzb3VyY2VzJyBhbmQgJ2N1c3RvbUNvZGUnIG9wdGlvbnMgaGF2ZSBiZWVuIGRpc2FibGVkIGZvciB0aGlzIHNlcnZlci5gXHJcbiAgICAgICAgKVxyXG4gICAgICApO1xyXG4gICAgfVxyXG5cclxuICAgIC8vIFJlc2V0IGFsbCBhZGRpdGlvbmFsIGN1c3RvbSBjb2RlXHJcbiAgICBjdXN0b21Mb2dpY09wdGlvbnMuY2FsbGJhY2sgPSBmYWxzZTtcclxuICAgIGN1c3RvbUxvZ2ljT3B0aW9ucy5yZXNvdXJjZXMgPSBmYWxzZTtcclxuICAgIGN1c3RvbUxvZ2ljT3B0aW9ucy5jdXN0b21Db2RlID0gZmFsc2U7XHJcbiAgfVxyXG5cclxuICAvLyBDbGVhbiBwcm9wZXJ0aWVzIHRvIGtlZXAgaXQgbGVhbiBhbmQgbWVhblxyXG4gIGlmIChjaGFydEpzb24pIHtcclxuICAgIGNoYXJ0SnNvbi5jaGFydCA9IGNoYXJ0SnNvbi5jaGFydCB8fCB7fTtcclxuICAgIGNoYXJ0SnNvbi5leHBvcnRpbmcgPSBjaGFydEpzb24uZXhwb3J0aW5nIHx8IHt9O1xyXG4gICAgY2hhcnRKc29uLmV4cG9ydGluZy5lbmFibGVkID0gZmFsc2U7XHJcbiAgfVxyXG5cclxuICBleHBvcnRPcHRpb25zLmNvbnN0ciA9IGV4cG9ydE9wdGlvbnMuY29uc3RyIHx8ICdjaGFydCc7XHJcbiAgZXhwb3J0T3B0aW9ucy50eXBlID0gZml4VHlwZShleHBvcnRPcHRpb25zLnR5cGUsIGV4cG9ydE9wdGlvbnMub3V0ZmlsZSk7XHJcbiAgaWYgKGV4cG9ydE9wdGlvbnMudHlwZSA9PT0gJ3N2ZycpIHtcclxuICAgIGV4cG9ydE9wdGlvbnMud2lkdGggPSBmYWxzZTtcclxuICB9XHJcblxyXG4gIC8vIFByZXBhcmUgZ2xvYmFsIGFuZCB0aGVtZSBvcHRpb25zXHJcbiAgWydnbG9iYWxPcHRpb25zJywgJ3RoZW1lT3B0aW9ucyddLmZvckVhY2goKG9wdGlvbnNOYW1lKSA9PiB7XHJcbiAgICB0cnkge1xyXG4gICAgICBpZiAoZXhwb3J0T3B0aW9ucyAmJiBleHBvcnRPcHRpb25zW29wdGlvbnNOYW1lXSkge1xyXG4gICAgICAgIGlmIChcclxuICAgICAgICAgIHR5cGVvZiBleHBvcnRPcHRpb25zW29wdGlvbnNOYW1lXSA9PT0gJ3N0cmluZycgJiZcclxuICAgICAgICAgIGV4cG9ydE9wdGlvbnNbb3B0aW9uc05hbWVdLmVuZHNXaXRoKCcuanNvbicpXHJcbiAgICAgICAgKSB7XHJcbiAgICAgICAgICBleHBvcnRPcHRpb25zW29wdGlvbnNOYW1lXSA9IGlzQ29ycmVjdEpTT04oXHJcbiAgICAgICAgICAgIHJlYWRGaWxlU3luYyhleHBvcnRPcHRpb25zW29wdGlvbnNOYW1lXSwgJ3V0ZjgnKSxcclxuICAgICAgICAgICAgdHJ1ZVxyXG4gICAgICAgICAgKTtcclxuICAgICAgICB9IGVsc2Uge1xyXG4gICAgICAgICAgZXhwb3J0T3B0aW9uc1tvcHRpb25zTmFtZV0gPSBpc0NvcnJlY3RKU09OKFxyXG4gICAgICAgICAgICBleHBvcnRPcHRpb25zW29wdGlvbnNOYW1lXSxcclxuICAgICAgICAgICAgdHJ1ZVxyXG4gICAgICAgICAgKTtcclxuICAgICAgICB9XHJcbiAgICAgIH1cclxuICAgIH0gY2F0Y2ggKGVycm9yKSB7XHJcbiAgICAgIGV4cG9ydE9wdGlvbnNbb3B0aW9uc05hbWVdID0ge307XHJcbiAgICAgIGxvZ1dpdGhTdGFjaygyLCBlcnJvciwgYFtjaGFydF0gVGhlICcke29wdGlvbnNOYW1lfScgY2Fubm90IGJlIGxvYWRlZC5gKTtcclxuICAgIH1cclxuICB9KTtcclxuXHJcbiAgLy8gUHJlcGFyZSB0aGUgY3VzdG9tQ29kZVxyXG4gIGlmIChjdXN0b21Mb2dpY09wdGlvbnMuYWxsb3dDb2RlRXhlY3V0aW9uKSB7XHJcbiAgICB0cnkge1xyXG4gICAgICBjdXN0b21Mb2dpY09wdGlvbnMuY3VzdG9tQ29kZSA9IHdyYXBBcm91bmQoXHJcbiAgICAgICAgY3VzdG9tTG9naWNPcHRpb25zLmN1c3RvbUNvZGUsXHJcbiAgICAgICAgY3VzdG9tTG9naWNPcHRpb25zLmFsbG93RmlsZVJlc291cmNlc1xyXG4gICAgICApO1xyXG4gICAgfSBjYXRjaCAoZXJyb3IpIHtcclxuICAgICAgbG9nV2l0aFN0YWNrKDIsIGVycm9yLCBgW2NoYXJ0XSBUaGUgJ2N1c3RvbUNvZGUnIGNhbm5vdCBiZSBsb2FkZWQuYCk7XHJcbiAgICB9XHJcbiAgfVxyXG5cclxuICAvLyBHZXQgdGhlIGNhbGxiYWNrXHJcbiAgaWYgKFxyXG4gICAgY3VzdG9tTG9naWNPcHRpb25zICYmXHJcbiAgICBjdXN0b21Mb2dpY09wdGlvbnMuY2FsbGJhY2sgJiZcclxuICAgIGN1c3RvbUxvZ2ljT3B0aW9ucy5jYWxsYmFjaz8uaW5kZXhPZigneycpIDwgMFxyXG4gICkge1xyXG4gICAgLy8gVGhlIGFsbG93RmlsZVJlc291cmNlcyBpcyBhbHdheXMgc2V0IHRvIGZhbHNlIGZvciBIVFRQIHJlcXVlc3RzIHRvIGF2b2lkXHJcbiAgICAvLyBpbmplY3RpbmcgYXJiaXRyYXJ5IGZpbGVzIGZyb20gdGhlIGZzXHJcbiAgICBpZiAoY3VzdG9tTG9naWNPcHRpb25zLmFsbG93RmlsZVJlc291cmNlcykge1xyXG4gICAgICB0cnkge1xyXG4gICAgICAgIGN1c3RvbUxvZ2ljT3B0aW9ucy5jYWxsYmFjayA9IHJlYWRGaWxlU3luYyhcclxuICAgICAgICAgIGN1c3RvbUxvZ2ljT3B0aW9ucy5jYWxsYmFjayxcclxuICAgICAgICAgICd1dGY4J1xyXG4gICAgICAgICk7XHJcbiAgICAgIH0gY2F0Y2ggKGVycm9yKSB7XHJcbiAgICAgICAgY3VzdG9tTG9naWNPcHRpb25zLmNhbGxiYWNrID0gZmFsc2U7XHJcbiAgICAgICAgbG9nV2l0aFN0YWNrKDIsIGVycm9yLCBgW2NoYXJ0XSBUaGUgJ2NhbGxiYWNrJyBjYW5ub3QgYmUgbG9hZGVkLmApO1xyXG4gICAgICB9XHJcbiAgICB9IGVsc2Uge1xyXG4gICAgICBjdXN0b21Mb2dpY09wdGlvbnMuY2FsbGJhY2sgPSBmYWxzZTtcclxuICAgIH1cclxuICB9XHJcblxyXG4gIC8vIFNpemUgc2VhcmNoXHJcbiAgb3B0aW9ucy5leHBvcnQgPSB7XHJcbiAgICAuLi5vcHRpb25zLmV4cG9ydCxcclxuICAgIC4uLmZpbmRDaGFydFNpemUob3B0aW9ucylcclxuICB9O1xyXG5cclxuICAvLyBQb3N0IHRoZSB3b3JrIHRvIHRoZSBwb29sXHJcbiAgdHJ5IHtcclxuICAgIGNvbnN0IHJlc3VsdCA9IGF3YWl0IHBvc3RXb3JrKFxyXG4gICAgICBleHBvcnRPcHRpb25zLnN0ckluaiB8fCBjaGFydEpzb24gfHwgc3ZnLFxyXG4gICAgICBvcHRpb25zXHJcbiAgICApO1xyXG4gICAgcmV0dXJuIGVuZENhbGxiYWNrKGZhbHNlLCByZXN1bHQpO1xyXG4gIH0gY2F0Y2ggKGVycm9yKSB7XHJcbiAgICByZXR1cm4gZW5kQ2FsbGJhY2soZXJyb3IpO1xyXG4gIH1cclxufTtcclxuXHJcbi8qKlxyXG4gKiBQZXJmb3JtcyBhIGRpcmVjdCBpbmplY3Qgb2Ygb3B0aW9ucyBiZWZvcmUgZXhwb3J0LiBUaGUgZnVuY3Rpb24gYXR0ZW1wdHNcclxuICogdG8gc3RyaW5naWZ5IHRoZSBwcm92aWRlZCBvcHRpb25zIGFuZCByZW1vdmVzIHVubmVjZXNzYXJ5IGNoYXJhY3RlcnMsXHJcbiAqIGVuc3VyaW5nIGEgY2xlYW4gYW5kIGZvcm1hdHRlZCBpbnB1dC4gVGhlIHJlc3VsdGluZyBzdHJpbmcgaXMgc2F2ZWQgYXNcclxuICogYSBcInN0cmlnaHQgaW5qZWN0XCIgc3RyaW5nIGluIHRoZSBleHBvcnQgb3B0aW9ucy4gSXQgdGhlbiBpbnZva2VzIHRoZVxyXG4gKiBkb0V4cG9ydCBmdW5jdGlvbiB3aXRoIHRoZSB1cGRhdGVkIG9wdGlvbnMuXHJcbiAqXHJcbiAqIElNUE9SVEFOVDogRGFuZ2Vyb3VzIGFuZCBtdXN0IGJlIHVzZWQgZGVsaWJlcmF0ZWx5IGJ5IHNvbWVvbmUgd2hvIHNldHMgdXBcclxuICogYSBzZXJ2ZXIgKHNlZSB0aGUgIC0tYWxsb3dDb2RlRXhlY3V0aW9uIG9wdGlvbikuXHJcbiAqXHJcbiAqIEBwYXJhbSB7T2JqZWN0fSBvcHRpb25zIC0gVGhlIGV4cG9ydCBvcHRpb25zIGNvbnRhaW5pbmcgdGhlIGlucHV0XHJcbiAqIHRvIGJlIGluamVjdGVkLlxyXG4gKiBAcGFyYW0ge2Z1bmN0aW9ufSBlbmRDYWxsYmFjayAtIFRoZSBjYWxsYmFjayBmdW5jdGlvbiB0byBiZSBpbnZva2VkXHJcbiAqIGF0IHRoZSBlbmQgb2YgdGhlIHByb2Nlc3MuXHJcbiAqXHJcbiAqIEByZXR1cm5zIHtQcm9taXNlfSBBIFByb21pc2UgdGhhdCByZXNvbHZlcyB3aXRoIHRoZSByZXN1bHQgb2YgdGhlIGV4cG9ydFxyXG4gKiBvcGVyYXRpb24gb3IgcmVqZWN0cyB3aXRoIGFuIGVycm9yIGlmIGFueSBpc3N1ZXMgb2NjdXIgZHVyaW5nIHRoZSBwcm9jZXNzLlxyXG4gKi9cclxuY29uc3QgZG9TdHJhaWdodEluamVjdCA9IChvcHRpb25zLCBlbmRDYWxsYmFjaykgPT4ge1xyXG4gIHRyeSB7XHJcbiAgICBsZXQgc3RySW5qO1xyXG4gICAgbGV0IGluc3RyID0gb3B0aW9ucy5leHBvcnQuaW5zdHIgfHwgb3B0aW9ucy5leHBvcnQub3B0aW9ucztcclxuXHJcbiAgICBpZiAodHlwZW9mIGluc3RyICE9PSAnc3RyaW5nJykge1xyXG4gICAgICAvLyBUcnkgdG8gc3RyaW5naWZ5IG9wdGlvbnNcclxuICAgICAgc3RySW5qID0gaW5zdHIgPSBvcHRpb25zU3RyaW5naWZ5KFxyXG4gICAgICAgIGluc3RyLFxyXG4gICAgICAgIG9wdGlvbnMuY3VzdG9tTG9naWM/LmFsbG93Q29kZUV4ZWN1dGlvblxyXG4gICAgICApO1xyXG4gICAgfVxyXG4gICAgc3RySW5qID0gaW5zdHIucmVwbGFjZUFsbCgvXFx0fFxcbnxcXHIvZywgJycpLnRyaW0oKTtcclxuXHJcbiAgICAvLyBHZXQgcmlkIG9mIHRoZSA7XHJcbiAgICBpZiAoc3RySW5qW3N0ckluai5sZW5ndGggLSAxXSA9PT0gJzsnKSB7XHJcbiAgICAgIHN0ckluaiA9IHN0ckluai5zdWJzdHJpbmcoMCwgc3RySW5qLmxlbmd0aCAtIDEpO1xyXG4gICAgfVxyXG5cclxuICAgIC8vIFNhdmUgYXMgc3RyaWdodCBpbmplY3Qgc3RyaW5nXHJcbiAgICBvcHRpb25zLmV4cG9ydC5zdHJJbmogPSBzdHJJbmo7XHJcbiAgICByZXR1cm4gZG9FeHBvcnQob3B0aW9ucywgZmFsc2UsIGVuZENhbGxiYWNrKTtcclxuICB9IGNhdGNoIChlcnJvcikge1xyXG4gICAgcmV0dXJuIGVuZENhbGxiYWNrKFxyXG4gICAgICBuZXcgRXhwb3J0RXJyb3IoXHJcbiAgICAgICAgYFtjaGFydF0gTWFsZm9ybWVkIGlucHV0IGRldGVjdGVkIGZvciAke29wdGlvbnMuZXhwb3J0Py5yZXF1ZXN0SWQgfHwgJz8nfS4gUGxlYXNlIG1ha2Ugc3VyZSB0aGF0IHlvdXIgSlNPTi9KYXZhU2NyaXB0IG9wdGlvbnMgYXJlIHNlbnQgdXNpbmcgdGhlIFwib3B0aW9uc1wiIGF0dHJpYnV0ZSwgYW5kIHRoYXQgaWYgeW91J3JlIHVzaW5nIFNWRywgaXQgaXMgdW5lc2NhcGVkLmBcclxuICAgICAgKS5zZXRFcnJvcihlcnJvcilcclxuICAgICk7XHJcbiAgfVxyXG59O1xyXG5cclxuLyoqXHJcbiAqIEV4cG9ydHMgYSBzdHJpbmcgYmFzZWQgb24gdGhlIHByb3ZpZGVkIG9wdGlvbnMgYW5kIGludm9rZXMgYW4gZW5kIGNhbGxiYWNrLlxyXG4gKlxyXG4gKiBAcGFyYW0ge3N0cmluZ30gc3RyaW5nVG9FeHBvcnQgLSBUaGUgc3RyaW5nIGNvbnRlbnQgdG8gYmUgZXhwb3J0ZWQuXHJcbiAqIEBwYXJhbSB7T2JqZWN0fSBvcHRpb25zIC0gRXhwb3J0IG9wdGlvbnMsIGluY2x1ZGluZyBjdXN0b21Mb2dpYyB3aXRoXHJcbiAqIGFsbG93Q29kZUV4ZWN1dGlvbiBmbGFnLlxyXG4gKiBAcGFyYW0ge0Z1bmN0aW9ufSBlbmRDYWxsYmFjayAtIENhbGxiYWNrIGZ1bmN0aW9uIHRvIGJlIGludm9rZWQgYXQgdGhlIGVuZFxyXG4gKiBvZiB0aGUgZXhwb3J0IHByb2Nlc3MuXHJcbiAqXHJcbiAqIEByZXR1cm5zIHthbnl9IFJlc3VsdCBvZiB0aGUgZXhwb3J0IHByb2Nlc3Mgb3IgYW4gZXJyb3IgaWYgZW5jb3VudGVyZWQuXHJcbiAqL1xyXG5jb25zdCBleHBvcnRBc1N0cmluZyA9IChzdHJpbmdUb0V4cG9ydCwgb3B0aW9ucywgZW5kQ2FsbGJhY2spID0+IHtcclxuICBjb25zdCB7IGFsbG93Q29kZUV4ZWN1dGlvbiB9ID0gb3B0aW9ucy5jdXN0b21Mb2dpYztcclxuXHJcbiAgLy8gQ2hlY2sgaWYgaXQgaXMgU1ZHXHJcbiAgaWYgKFxyXG4gICAgc3RyaW5nVG9FeHBvcnQuaW5kZXhPZignPHN2ZycpID49IDAgfHxcclxuICAgIHN0cmluZ1RvRXhwb3J0LmluZGV4T2YoJzw/eG1sJykgPj0gMFxyXG4gICkge1xyXG4gICAgbG9nKDQsICdbY2hhcnRdIFBhcnNpbmcgaW5wdXQgYXMgU1ZHLicpO1xyXG4gICAgcmV0dXJuIGRvRXhwb3J0KG9wdGlvbnMsIGZhbHNlLCBlbmRDYWxsYmFjaywgc3RyaW5nVG9FeHBvcnQpO1xyXG4gIH1cclxuXHJcbiAgdHJ5IHtcclxuICAgIC8vIFRyeSB0byBwYXJzZSB0byBKU09OIGFuZCBjYWxsIHRoZSBkb0V4cG9ydCBmdW5jdGlvblxyXG4gICAgY29uc3QgY2hhcnRKU09OID0gSlNPTi5wYXJzZShzdHJpbmdUb0V4cG9ydC5yZXBsYWNlQWxsKC9cXHR8XFxufFxcci9nLCAnICcpKTtcclxuXHJcbiAgICAvLyBJZiBhIGNvcnJlY3QgSlNPTiwgZG8gdGhlIGV4cG9ydFxyXG4gICAgcmV0dXJuIGRvRXhwb3J0KG9wdGlvbnMsIGNoYXJ0SlNPTiwgZW5kQ2FsbGJhY2spO1xyXG4gIH0gY2F0Y2ggKGVycm9yKSB7XHJcbiAgICAvLyBOb3QgYSB2YWxpZCBKU09OXHJcbiAgICBpZiAodG9Cb29sZWFuKGFsbG93Q29kZUV4ZWN1dGlvbikpIHtcclxuICAgICAgcmV0dXJuIGRvU3RyYWlnaHRJbmplY3Qob3B0aW9ucywgZW5kQ2FsbGJhY2spO1xyXG4gICAgfSBlbHNlIHtcclxuICAgICAgLy8gRG8gbm90IGFsbG93IHN0cmFpZ2h0IGluamVjdGlvbiB3aXRob3V0IHRoZSBhbGxvd0NvZGVFeGVjdXRpb24gZmxhZ1xyXG4gICAgICByZXR1cm4gZW5kQ2FsbGJhY2soXHJcbiAgICAgICAgbmV3IEV4cG9ydEVycm9yKFxyXG4gICAgICAgICAgJ1tjaGFydF0gT25seSBKU09OIGNvbmZpZ3VyYXRpb25zIGFuZCBTVkcgYXJlIGFsbG93ZWQgZm9yIHRoaXMgc2VydmVyLiBJZiB0aGlzIGlzIHlvdXIgc2VydmVyLCBKYXZhU2NyaXB0IGN1c3RvbSBjb2RlIGNhbiBiZSBlbmFibGVkIGJ5IHN0YXJ0aW5nIHRoZSBzZXJ2ZXIgd2l0aCB0aGUgLS1hbGxvd0NvZGVFeGVjdXRpb24gZmxhZy4nXHJcbiAgICAgICAgKS5zZXRFcnJvcihlcnJvcilcclxuICAgICAgKTtcclxuICAgIH1cclxuICB9XHJcbn07XHJcblxyXG4vKipcclxuICogUmV0cmlldmVzIGFuZCByZXR1cm5zIHRoZSBjdXJyZW50IHN0YXR1cyBvZiBjb2RlIGV4ZWN1dGlvbiBwZXJtaXNzaW9uLlxyXG4gKlxyXG4gKiBAcmV0dXJucyB7YW55fSBUaGUgdmFsdWUgb2YgYWxsb3dDb2RlRXhlY3V0aW9uLlxyXG4gKi9cclxuZXhwb3J0IGNvbnN0IGdldEFsbG93Q29kZUV4ZWN1dGlvbiA9ICgpID0+IGFsbG93Q29kZUV4ZWN1dGlvbjtcclxuXHJcbi8qKlxyXG4gKiBTZXRzIHRoZSBjb2RlIGV4ZWN1dGlvbiBwZXJtaXNzaW9uIGJhc2VkIG9uIHRoZSBwcm92aWRlZCBib29sZWFuIHZhbHVlLlxyXG4gKlxyXG4gKiBAcGFyYW0ge2FueX0gdmFsdWUgLSBUaGUgdmFsdWUgdG8gYmUgY29udmVydGVkIGFuZCBhc3NpZ25lZFxyXG4gKiB0byBhbGxvd0NvZGVFeGVjdXRpb24uXHJcbiAqL1xyXG5leHBvcnQgY29uc3Qgc2V0QWxsb3dDb2RlRXhlY3V0aW9uID0gKHZhbHVlKSA9PiB7XHJcbiAgYWxsb3dDb2RlRXhlY3V0aW9uID0gdG9Cb29sZWFuKHZhbHVlKTtcclxufTtcclxuXHJcbmV4cG9ydCBkZWZhdWx0IHtcclxuICBiYXRjaEV4cG9ydCxcclxuICBzaW5nbGVFeHBvcnQsXHJcbiAgZ2V0QWxsb3dDb2RlRXhlY3V0aW9uLFxyXG4gIHNldEFsbG93Q29kZUV4ZWN1dGlvbixcclxuICBzdGFydEV4cG9ydCxcclxuICBmaW5kQ2hhcnRTaXplXHJcbn07XHJcbiIsIi8qKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqXHJcblxyXG5IaWdoY2hhcnRzIEV4cG9ydCBTZXJ2ZXJcclxuXHJcbkNvcHlyaWdodCAoYykgMjAxNi0yMDI0LCBIaWdoc29mdFxyXG5cclxuTGljZW5jZWQgdW5kZXIgdGhlIE1JVCBsaWNlbmNlLlxyXG5cclxuQWRkaXRpb25hbGx5IGEgdmFsaWQgSGlnaGNoYXJ0cyBsaWNlbnNlIGlzIHJlcXVpcmVkIGZvciB1c2UuXHJcblxyXG5TZWUgTElDRU5TRSBmaWxlIGluIHJvb3QgZm9yIGRldGFpbHMuXHJcblxyXG4qKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqL1xyXG5cclxuLyoqXHJcbiAqIEBvdmVydmlldyBVc2VkIHRvIHNhbml0aXplIHRoZSBzdHJpbmdzIGNvbWluZyBmcm9tIHRoZSBleHBvcnRpbmcgbW9kdWxlXHJcbiAqIHRvIHByZXZlbnQgWFNTIGF0dGFja3MgKHdpdGggdGhlIERPTVB1cmlmeSBsaWJyYXJ5KS5cclxuICoqL1xyXG5cclxuaW1wb3J0IHsgSlNET00gfSBmcm9tICdqc2RvbSc7XHJcbmltcG9ydCBET01QdXJpZnkgZnJvbSAnZG9tcHVyaWZ5JztcclxuXHJcbmltcG9ydCB7IGVudnMgfSBmcm9tICcuL2VudnMuanMnO1xyXG4vKipcclxuICogU2FuaXRpemVzIGEgZ2l2ZW4gSFRNTCBzdHJpbmcgYnkgcmVtb3ZpbmcgPHNjcmlwdD4gdGFncy5cclxuICogVGhpcyBmdW5jdGlvbiB1c2VzIGEgcmVndWxhciBleHByZXNzaW9uIHRvIGZpbmQgYW5kIHJlbW92ZSBhbGxcclxuICogb2NjdXJyZW5jZXMgb2YgPHNjcmlwdD4uLi48L3NjcmlwdD4gdGFncyBhbmQgYW55IGNvbnRlbnQgd2l0aGluIHRoZW0uXHJcbiAqXHJcbiAqIEBwYXJhbSB7c3RyaW5nfSBpbnB1dCBUaGUgSFRNTCBzdHJpbmcgdG8gYmUgc2FuaXRpemVkLlxyXG4gKiBAcmV0dXJucyB7c3RyaW5nfSBUaGUgc2FuaXRpemVkIEhUTUwgc3RyaW5nLlxyXG4gKi9cclxuZXhwb3J0IGZ1bmN0aW9uIHNhbml0aXplKGlucHV0KSB7XHJcbiAgY29uc3QgZm9yYmlkZGVuID0gW107XHJcblxyXG4gIGlmICghZW52cy5PVEhFUl9BTExPV19YTElOSykge1xyXG4gICAgZm9yYmlkZGVuLnB1c2goJ3hsaW5rOmhyZWYnKTtcclxuICB9XHJcblxyXG4gIGNvbnN0IHdpbmRvdyA9IG5ldyBKU0RPTSgnJykud2luZG93O1xyXG4gIGNvbnN0IHB1cmlmeSA9IERPTVB1cmlmeSh3aW5kb3cpO1xyXG4gIHJldHVybiBwdXJpZnkuc2FuaXRpemUoaW5wdXQsIHtcclxuICAgIEFERF9UQUdTOiBbJ2ZvcmVpZ25PYmplY3QnXSxcclxuICAgIEZPUkJJRF9BVFRSOiBmb3JiaWRkZW5cclxuICB9KTtcclxufVxyXG5cclxuZXhwb3J0IGRlZmF1bHQgc2FuaXRpemU7XHJcbiIsIi8qKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqXHJcblxyXG5IaWdoY2hhcnRzIEV4cG9ydCBTZXJ2ZXJcclxuXHJcbkNvcHlyaWdodCAoYykgMjAxNi0yMDI0LCBIaWdoc29mdFxyXG5cclxuTGljZW5jZWQgdW5kZXIgdGhlIE1JVCBsaWNlbmNlLlxyXG5cclxuQWRkaXRpb25hbGx5IGEgdmFsaWQgSGlnaGNoYXJ0cyBsaWNlbnNlIGlzIHJlcXVpcmVkIGZvciB1c2UuXHJcblxyXG5TZWUgTElDRU5TRSBmaWxlIGluIHJvb3QgZm9yIGRldGFpbHMuXHJcblxyXG4qKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqL1xyXG5cclxuaW1wb3J0IHsgbG9nIH0gZnJvbSAnLi9sb2dnZXIuanMnO1xyXG5cclxuLy8gQXJyYXkgdGhhdCBjb250YWlucyBpZHMgb2YgYWxsIG9uZ29pbmcgaW50ZXJ2YWxzXHJcbmNvbnN0IGludGVydmFsSWRzID0gW107XHJcblxyXG4vKipcclxuICogQWRkcyBpZCBvZiBhIHNldEludGVydmFsIHRvIHRoZSBpbnRlcnZhbElkcyBhcnJheS5cclxuICpcclxuICogQHBhcmFtIHtOb2RlSlMuVGltZW91dH0gaWQgLSBJZCBvZiBhbiBpbnRlcnZhbC5cclxuICovXHJcbmV4cG9ydCBjb25zdCBhZGRJbnRlcnZhbCA9IChpZCkgPT4ge1xyXG4gIGludGVydmFsSWRzLnB1c2goaWQpO1xyXG59O1xyXG5cclxuLyoqXHJcbiAqIENsZWFycyBhbGwgb2Ygb25nb2luZyBpbnRlcnZhbHMgYnkgaWRzIGdhdGhlcmVkIGluIHRoZSBpbnRlcnZhbElkcyBhcnJheS5cclxuICovXHJcbmV4cG9ydCBjb25zdCBjbGVhckFsbEludGVydmFscyA9ICgpID0+IHtcclxuICBsb2coNCwgYFtzZXJ2ZXJdIENsZWFyaW5nIGFsbCByZWdpc3RlcmVkIGludGVydmFscy5gKTtcclxuICBmb3IgKGNvbnN0IGlkIG9mIGludGVydmFsSWRzKSB7XHJcbiAgICBjbGVhckludGVydmFsKGlkKTtcclxuICB9XHJcbn07XHJcblxyXG5leHBvcnQgZGVmYXVsdCB7XHJcbiAgYWRkSW50ZXJ2YWwsXHJcbiAgY2xlYXJBbGxJbnRlcnZhbHNcclxufTtcclxuIiwiaW1wb3J0IHsgZW52cyB9IGZyb20gJy4uL2VudnMuanMnO1xyXG5pbXBvcnQgeyBsb2dXaXRoU3RhY2sgfSBmcm9tICcuLi9sb2dnZXIuanMnO1xyXG5cclxuLyoqXHJcbiAqIE1pZGRsZXdhcmUgZm9yIGxvZ2dpbmcgZXJyb3JzIHdpdGggc3RhY2sgdHJhY2UgYW5kIGhhbmRsaW5nIGVycm9yIHJlc3BvbnNlLlxyXG4gKlxyXG4gKiBAcGFyYW0ge0Vycm9yfSBlcnJvciAtIFRoZSBlcnJvciBvYmplY3QuXHJcbiAqIEBwYXJhbSB7RXhwcmVzcy5SZXF1ZXN0fSByZXEgLSBUaGUgRXhwcmVzcyByZXF1ZXN0IG9iamVjdC5cclxuICogQHBhcmFtIHtFeHByZXNzLlJlc3BvbnNlfSByZXMgLSBUaGUgRXhwcmVzcyByZXNwb25zZSBvYmplY3QuXHJcbiAqIEBwYXJhbSB7RnVuY3Rpb259IG5leHQgLSBUaGUgbmV4dCBtaWRkbGV3YXJlIGZ1bmN0aW9uLlxyXG4gKi9cclxuY29uc3QgbG9nRXJyb3JNaWRkbGV3YXJlID0gKGVycm9yLCByZXEsIHJlcywgbmV4dCkgPT4ge1xyXG4gIC8vIERpc3BsYXkgdGhlIGVycm9yIHdpdGggc3RhY2sgaW4gYSBjb3JyZWN0IGZvcm1hdFxyXG4gIGxvZ1dpdGhTdGFjaygxLCBlcnJvcik7XHJcblxyXG4gIC8vIERlbGV0ZSB0aGUgc3RhY2sgZm9yIHRoZSBlbnZpcm9ubWVudCBvdGhlciB0aGFuIHRoZSBkZXZlbG9wbWVudFxyXG4gIGlmIChlbnZzLk9USEVSX05PREVfRU5WICE9PSAnZGV2ZWxvcG1lbnQnKSB7XHJcbiAgICBkZWxldGUgZXJyb3Iuc3RhY2s7XHJcbiAgfVxyXG5cclxuICAvLyBDYWxsIHRoZSByZXR1cm5FcnJvck1pZGRsZXdhcmVcclxuICBuZXh0KGVycm9yKTtcclxufTtcclxuXHJcbi8qKlxyXG4gKiBNaWRkbGV3YXJlIGZvciByZXR1cm5pbmcgZXJyb3IgcmVzcG9uc2UuXHJcbiAqXHJcbiAqIEBwYXJhbSB7RXJyb3J9IGVycm9yIC0gVGhlIGVycm9yIG9iamVjdC5cclxuICogQHBhcmFtIHtFeHByZXNzLlJlcXVlc3R9IHJlcSAtIFRoZSBFeHByZXNzIHJlcXVlc3Qgb2JqZWN0LlxyXG4gKiBAcGFyYW0ge0V4cHJlc3MuUmVzcG9uc2V9IHJlcyAtIFRoZSBFeHByZXNzIHJlc3BvbnNlIG9iamVjdC5cclxuICogQHBhcmFtIHtGdW5jdGlvbn0gbmV4dCAtIFRoZSBuZXh0IG1pZGRsZXdhcmUgZnVuY3Rpb24uXHJcbiAqL1xyXG5jb25zdCByZXR1cm5FcnJvck1pZGRsZXdhcmUgPSAoZXJyb3IsIHJlcSwgcmVzLCBuZXh0KSA9PiB7XHJcbiAgLy8gR2F0aGVyIGFsbCByZXF1aWVkIGluZm9ybWF0aW9uIGZvciB0aGUgcmVzcG9uc2VcclxuICBjb25zdCB7IHN0YXR1c0NvZGU6IHN0Q29kZSwgc3RhdHVzLCBtZXNzYWdlLCBzdGFjayB9ID0gZXJyb3I7XHJcbiAgY29uc3Qgc3RhdHVzQ29kZSA9IHN0Q29kZSB8fCBzdGF0dXMgfHwgNDAwO1xyXG5cclxuICAvLyBTZXQgYW5kIHJldHVybiByZXNwb25zZVxyXG4gIHJlcy5zdGF0dXMoc3RhdHVzQ29kZSkuanNvbih7IHN0YXR1c0NvZGUsIG1lc3NhZ2UsIHN0YWNrIH0pO1xyXG59O1xyXG5cclxuZXhwb3J0IGRlZmF1bHQgKGFwcCkgPT4ge1xyXG4gIC8vIEFkZCBsb2cgZXJyb3IgbWlkZGxld2FyZVxyXG4gIGFwcC51c2UobG9nRXJyb3JNaWRkbGV3YXJlKTtcclxuXHJcbiAgLy8gQWRkIHNldCBzdGF0dXMgYW5kIHJldHVybiBlcnJvciBtaWRkbGV3YXJlXHJcbiAgYXBwLnVzZShyZXR1cm5FcnJvck1pZGRsZXdhcmUpO1xyXG59O1xyXG4iLCIvKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKlxyXG5cclxuSGlnaGNoYXJ0cyBFeHBvcnQgU2VydmVyXHJcblxyXG5Db3B5cmlnaHQgKGMpIDIwMTYtMjAyNCwgSGlnaHNvZnRcclxuXHJcbkxpY2VuY2VkIHVuZGVyIHRoZSBNSVQgbGljZW5jZS5cclxuXHJcbkFkZGl0aW9uYWxseSBhIHZhbGlkIEhpZ2hjaGFydHMgbGljZW5zZSBpcyByZXF1aXJlZCBmb3IgdXNlLlxyXG5cclxuU2VlIExJQ0VOU0UgZmlsZSBpbiByb290IGZvciBkZXRhaWxzLlxyXG5cclxuKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKi9cclxuXHJcbmltcG9ydCByYXRlTGltaXQgZnJvbSAnZXhwcmVzcy1yYXRlLWxpbWl0JztcclxuXHJcbmltcG9ydCB7IGxvZyB9IGZyb20gJy4uL2xvZ2dlci5qcyc7XHJcblxyXG4vKipcclxuICogTWlkZGxld2FyZSBmb3IgZW5hYmxpbmcgcmF0ZSBsaW1pdGluZyBvbiB0aGUgc3BlY2lmaWVkIEV4cHJlc3MgYXBwLlxyXG4gKlxyXG4gKiBAcGFyYW0ge0V4cHJlc3N9IGFwcCAtIFRoZSBFeHByZXNzIGFwcCBpbnN0YW5jZS5cclxuICogQHBhcmFtIHtPYmplY3R9IGxpbWl0Q29uZmlnIC0gQ29uZmlndXJhdGlvbiBvcHRpb25zIGZvciByYXRlIGxpbWl0aW5nLlxyXG4gKi9cclxuZXhwb3J0IGRlZmF1bHQgKGFwcCwgbGltaXRDb25maWcpID0+IHtcclxuICBjb25zdCBtc2cgPVxyXG4gICAgJ1RvbyBtYW55IHJlcXVlc3RzLCB5b3UgaGF2ZSBiZWVuIHJhdGUgbGltaXRlZC4gUGxlYXNlIHRyeSBhZ2FpbiBsYXRlci4nO1xyXG5cclxuICAvLyBPcHRpb25zIGZvciB0aGUgcmF0ZSBsaW1pdGVyXHJcbiAgY29uc3QgcmF0ZU9wdGlvbnMgPSB7XHJcbiAgICBtYXg6IGxpbWl0Q29uZmlnLm1heFJlcXVlc3RzIHx8IDMwLFxyXG4gICAgd2luZG93OiBsaW1pdENvbmZpZy53aW5kb3cgfHwgMSxcclxuICAgIGRlbGF5OiBsaW1pdENvbmZpZy5kZWxheSB8fCAwLFxyXG4gICAgdHJ1c3RQcm94eTogbGltaXRDb25maWcudHJ1c3RQcm94eSB8fCBmYWxzZSxcclxuICAgIHNraXBLZXk6IGxpbWl0Q29uZmlnLnNraXBLZXkgfHwgZmFsc2UsXHJcbiAgICBza2lwVG9rZW46IGxpbWl0Q29uZmlnLnNraXBUb2tlbiB8fCBmYWxzZVxyXG4gIH07XHJcblxyXG4gIC8vIFNldCBpZiBiZWhpbmQgYSBwcm94eVxyXG4gIGlmIChyYXRlT3B0aW9ucy50cnVzdFByb3h5KSB7XHJcbiAgICBhcHAuZW5hYmxlKCd0cnVzdCBwcm94eScpO1xyXG4gIH1cclxuXHJcbiAgLy8gQ3JlYXRlIGEgbGltaXRlclxyXG4gIGNvbnN0IGxpbWl0ZXIgPSByYXRlTGltaXQoe1xyXG4gICAgd2luZG93TXM6IHJhdGVPcHRpb25zLndpbmRvdyAqIDYwICogMTAwMCxcclxuICAgIC8vIExpbWl0IGVhY2ggSVAgdG8gMTAwIHJlcXVlc3RzIHBlciB3aW5kb3dNc1xyXG4gICAgbWF4OiByYXRlT3B0aW9ucy5tYXgsXHJcbiAgICAvLyBEaXNhYmxlIGRlbGF5aW5nLCBmdWxsIHNwZWVkIHVudGlsIHRoZSBtYXggbGltaXQgaXMgcmVhY2hlZFxyXG4gICAgZGVsYXlNczogcmF0ZU9wdGlvbnMuZGVsYXksXHJcbiAgICBoYW5kbGVyOiAocmVxdWVzdCwgcmVzcG9uc2UpID0+IHtcclxuICAgICAgcmVzcG9uc2UuZm9ybWF0KHtcclxuICAgICAgICBqc29uOiAoKSA9PiB7XHJcbiAgICAgICAgICByZXNwb25zZS5zdGF0dXMoNDI5KS5zZW5kKHsgbWVzc2FnZTogbXNnIH0pO1xyXG4gICAgICAgIH0sXHJcbiAgICAgICAgZGVmYXVsdDogKCkgPT4ge1xyXG4gICAgICAgICAgcmVzcG9uc2Uuc3RhdHVzKDQyOSkuc2VuZChtc2cpO1xyXG4gICAgICAgIH1cclxuICAgICAgfSk7XHJcbiAgICB9LFxyXG4gICAgc2tpcDogKHJlcXVlc3QpID0+IHtcclxuICAgICAgLy8gQWxsb3cgYnlwYXNzaW5nIHRoZSBsaW1pdGVyIGlmIGEgdmFsaWQga2V5L3Rva2VuIGhhcyBiZWVuIHNlbnRcclxuICAgICAgaWYgKFxyXG4gICAgICAgIHJhdGVPcHRpb25zLnNraXBLZXkgIT09IGZhbHNlICYmXHJcbiAgICAgICAgcmF0ZU9wdGlvbnMuc2tpcFRva2VuICE9PSBmYWxzZSAmJlxyXG4gICAgICAgIHJlcXVlc3QucXVlcnkua2V5ID09PSByYXRlT3B0aW9ucy5za2lwS2V5ICYmXHJcbiAgICAgICAgcmVxdWVzdC5xdWVyeS5hY2Nlc3NfdG9rZW4gPT09IHJhdGVPcHRpb25zLnNraXBUb2tlblxyXG4gICAgICApIHtcclxuICAgICAgICBsb2coNCwgJ1tyYXRlIGxpbWl0aW5nXSBTa2lwcGluZyByYXRlIGxpbWl0ZXIuJyk7XHJcbiAgICAgICAgcmV0dXJuIHRydWU7XHJcbiAgICAgIH1cclxuICAgICAgcmV0dXJuIGZhbHNlO1xyXG4gICAgfVxyXG4gIH0pO1xyXG5cclxuICAvLyBVc2UgYSBsaW1pdGVyIGFzIGEgbWlkZGxld2FyZVxyXG4gIGFwcC51c2UobGltaXRlcik7XHJcblxyXG4gIGxvZyhcclxuICAgIDMsXHJcbiAgICBgW3JhdGUgbGltaXRpbmddIEVuYWJsZWQgcmF0ZSBsaW1pdGluZyB3aXRoICR7cmF0ZU9wdGlvbnMubWF4fSByZXF1ZXN0cyBwZXIgJHtyYXRlT3B0aW9ucy53aW5kb3d9IG1pbnV0ZSBmb3IgZWFjaCBJUCwgdHJ1c3RpbmcgcHJveHk6ICR7cmF0ZU9wdGlvbnMudHJ1c3RQcm94eX0uYFxyXG4gICk7XHJcbn07XHJcbiIsImltcG9ydCBFeHBvcnRFcnJvciBmcm9tICcuL0V4cG9ydEVycm9yLmpzJztcclxuXHJcbmNsYXNzIEh0dHBFcnJvciBleHRlbmRzIEV4cG9ydEVycm9yIHtcclxuICBjb25zdHJ1Y3RvcihtZXNzYWdlLCBzdGF0dXMpIHtcclxuICAgIHN1cGVyKG1lc3NhZ2UpO1xyXG4gICAgdGhpcy5zdGF0dXMgPSB0aGlzLnN0YXR1c0NvZGUgPSBzdGF0dXM7XHJcbiAgfVxyXG5cclxuICBzZXRTdGF0dXMoc3RhdHVzKSB7XHJcbiAgICB0aGlzLnN0YXR1cyA9IHN0YXR1cztcclxuICAgIHJldHVybiB0aGlzO1xyXG4gIH1cclxufVxyXG5cclxuZXhwb3J0IGRlZmF1bHQgSHR0cEVycm9yO1xyXG4iLCIvKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKlxyXG5cclxuSGlnaGNoYXJ0cyBFeHBvcnQgU2VydmVyXHJcblxyXG5Db3B5cmlnaHQgKGMpIDIwMTYtMjAyNCwgSGlnaHNvZnRcclxuXHJcbkxpY2VuY2VkIHVuZGVyIHRoZSBNSVQgbGljZW5jZS5cclxuXHJcbkFkZGl0aW9uYWxseSBhIHZhbGlkIEhpZ2hjaGFydHMgbGljZW5zZSBpcyByZXF1aXJlZCBmb3IgdXNlLlxyXG5cclxuU2VlIExJQ0VOU0UgZmlsZSBpbiByb290IGZvciBkZXRhaWxzLlxyXG5cclxuKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKi9cclxuXHJcbmltcG9ydCB7IHVwZGF0ZVZlcnNpb24sIHZlcnNpb24gfSBmcm9tICcuLi8uLi9jYWNoZS5qcyc7XHJcbmltcG9ydCB7IGVudnMgfSBmcm9tICcuLi8uLi9lbnZzLmpzJztcclxuXHJcbmltcG9ydCBIdHRwRXJyb3IgZnJvbSAnLi4vLi4vZXJyb3JzL0h0dHBFcnJvci5qcyc7XHJcblxyXG4vKipcclxuICogQWRkcyB0aGUgUE9TVCAvY2hhbmdlX2hjX3ZlcnNpb24vOm5ld1ZlcnNpb24gcm91dGUgdGhhdCBjYW4gYmUgdXRpbGl6ZWQgdG8gbW9kaWZ5XHJcbiAqIHRoZSBIaWdoY2hhcnRzIHZlcnNpb24gb24gdGhlIHNlcnZlci5cclxuICpcclxuICogVE9ETzogQWRkIGF1dGggdG9rZW4gYW5kIGNvbm5lY3QgdG8gQVBJXHJcbiAqL1xyXG5leHBvcnQgZGVmYXVsdCAoYXBwKSA9PlxyXG4gICFhcHBcclxuICAgID8gZmFsc2VcclxuICAgIDogYXBwLnBvc3QoXHJcbiAgICAgICAgJy92ZXJzaW9uL2NoYW5nZS86bmV3VmVyc2lvbicsXHJcbiAgICAgICAgYXN5bmMgKHJlcXVlc3QsIHJlc3BvbnNlLCBuZXh0KSA9PiB7XHJcbiAgICAgICAgICB0cnkge1xyXG4gICAgICAgICAgICBjb25zdCBhZG1pblRva2VuID0gZW52cy5ISUdIQ0hBUlRTX0FETUlOX1RPS0VOO1xyXG5cclxuICAgICAgICAgICAgLy8gQ2hlY2sgdGhlIGV4aXN0ZW5jZSBvZiB0aGUgdG9rZW5cclxuICAgICAgICAgICAgaWYgKCFhZG1pblRva2VuIHx8ICFhZG1pblRva2VuLmxlbmd0aCkge1xyXG4gICAgICAgICAgICAgIHRocm93IG5ldyBIdHRwRXJyb3IoXHJcbiAgICAgICAgICAgICAgICAnVGhlIHNlcnZlciBpcyBub3QgY29uZmlndXJlZCB0byBwZXJmb3JtIHJ1bi10aW1lIHZlcnNpb24gY2hhbmdlczogSElHSENIQVJUU19BRE1JTl9UT0tFTiBpcyBub3Qgc2V0LicsXHJcbiAgICAgICAgICAgICAgICA0MDFcclxuICAgICAgICAgICAgICApO1xyXG4gICAgICAgICAgICB9XHJcblxyXG4gICAgICAgICAgICAvLyBDaGVjayBpZiB0aGUgaGMtYXV0aCBoZWFkZXIgY29udGFpbiBhIGNvcnJlY3QgdG9rZW5cclxuICAgICAgICAgICAgY29uc3QgdG9rZW4gPSByZXF1ZXN0LmdldCgnaGMtYXV0aCcpO1xyXG4gICAgICAgICAgICBpZiAoIXRva2VuIHx8IHRva2VuICE9PSBhZG1pblRva2VuKSB7XHJcbiAgICAgICAgICAgICAgdGhyb3cgbmV3IEh0dHBFcnJvcihcclxuICAgICAgICAgICAgICAgICdJbnZhbGlkIG9yIG1pc3NpbmcgdG9rZW46IFNldCB0aGUgdG9rZW4gaW4gdGhlIGhjLWF1dGggaGVhZGVyLicsXHJcbiAgICAgICAgICAgICAgICA0MDFcclxuICAgICAgICAgICAgICApO1xyXG4gICAgICAgICAgICB9XHJcblxyXG4gICAgICAgICAgICAvLyBDb21wYXJlIHZlcnNpb25zXHJcbiAgICAgICAgICAgIGNvbnN0IG5ld1ZlcnNpb24gPSByZXF1ZXN0LnBhcmFtcy5uZXdWZXJzaW9uO1xyXG5cclxuICAgICAgICAgICAgLy8gQWNjZXB0IG9ubHkgdmVyc2lvbiBzdHJpbmdzIGNvbnRhaW5pbmcgZGlnaXRzLCBsZXR0ZXJzLCBkb3RzLCBoeXBoZW5zXHJcbiAgICAgICAgICAgIGlmIChuZXdWZXJzaW9uICYmIC9eW2EtekEtWjAtOS4tXSskLy50ZXN0KG5ld1ZlcnNpb24pKSB7XHJcbiAgICAgICAgICAgICAgdHJ5IHtcclxuICAgICAgICAgICAgICAgIC8vIGVzbGludC1kaXNhYmxlLW5leHQtbGluZSBpbXBvcnQvbm8tbmFtZWQtYXMtZGVmYXVsdC1tZW1iZXJcclxuICAgICAgICAgICAgICAgIGF3YWl0IHVwZGF0ZVZlcnNpb24obmV3VmVyc2lvbik7XHJcbiAgICAgICAgICAgICAgfSBjYXRjaCAoZXJyb3IpIHtcclxuICAgICAgICAgICAgICAgIHRocm93IG5ldyBIdHRwRXJyb3IoXHJcbiAgICAgICAgICAgICAgICAgIGBWZXJzaW9uIGNoYW5nZTogJHtlcnJvci5tZXNzYWdlfWAsXHJcbiAgICAgICAgICAgICAgICAgIGVycm9yLnN0YXR1c0NvZGVcclxuICAgICAgICAgICAgICAgICkuc2V0RXJyb3IoZXJyb3IpO1xyXG4gICAgICAgICAgICAgIH1cclxuXHJcbiAgICAgICAgICAgICAgLy8gU3VjY2Vzc1xyXG4gICAgICAgICAgICAgIHJlc3BvbnNlLnN0YXR1cygyMDApLnNlbmQoe1xyXG4gICAgICAgICAgICAgICAgc3RhdHVzQ29kZTogMjAwLFxyXG4gICAgICAgICAgICAgICAgdmVyc2lvbjogdmVyc2lvbigpLFxyXG4gICAgICAgICAgICAgICAgbWVzc2FnZTogYFN1Y2Nlc3NmdWxseSB1cGRhdGVkIEhpZ2hjaGFydHMgdG8gdmVyc2lvbjogJHtuZXdWZXJzaW9ufS5gXHJcbiAgICAgICAgICAgICAgfSk7XHJcbiAgICAgICAgICAgIH0gZWxzZSB7XHJcbiAgICAgICAgICAgICAgLy8gTm8gdmVyc2lvbiBzcGVjaWZpZWRcclxuICAgICAgICAgICAgICB0aHJvdyBuZXcgSHR0cEVycm9yKCdObyBuZXcgdmVyc2lvbiBzdXBwbGllZC4nLCA0MDApO1xyXG4gICAgICAgICAgICB9XHJcbiAgICAgICAgICB9IGNhdGNoIChlcnJvcikge1xyXG4gICAgICAgICAgICBuZXh0KGVycm9yKTtcclxuICAgICAgICAgIH1cclxuICAgICAgICB9XHJcbiAgICAgICk7XHJcbiIsIi8qKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqXHJcblxyXG5IaWdoY2hhcnRzIEV4cG9ydCBTZXJ2ZXJcclxuXHJcbkNvcHlyaWdodCAoYykgMjAxNi0yMDI0LCBIaWdoc29mdFxyXG5cclxuTGljZW5jZWQgdW5kZXIgdGhlIE1JVCBsaWNlbmNlLlxyXG5cclxuQWRkaXRpb25hbGx5IGEgdmFsaWQgSGlnaGNoYXJ0cyBsaWNlbnNlIGlzIHJlcXVpcmVkIGZvciB1c2UuXHJcblxyXG5TZWUgTElDRU5TRSBmaWxlIGluIHJvb3QgZm9yIGRldGFpbHMuXHJcblxyXG4qKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqL1xyXG5cclxuaW1wb3J0IHsgdjQgYXMgdXVpZCB9IGZyb20gJ3V1aWQnO1xyXG5cclxuaW1wb3J0IHsgZ2V0QWxsb3dDb2RlRXhlY3V0aW9uLCBzdGFydEV4cG9ydCB9IGZyb20gJy4uLy4uL2NoYXJ0LmpzJztcclxuaW1wb3J0IHsgZ2V0T3B0aW9ucywgbWVyZ2VDb25maWdPcHRpb25zIH0gZnJvbSAnLi4vLi4vY29uZmlnLmpzJztcclxuaW1wb3J0IHsgbG9nIH0gZnJvbSAnLi4vLi4vbG9nZ2VyLmpzJztcclxuaW1wb3J0IHtcclxuICBmaXhUeXBlLFxyXG4gIGlzQ29ycmVjdEpTT04sXHJcbiAgaXNPYmplY3RFbXB0eSxcclxuICBpc1ByaXZhdGVSYW5nZVVybEZvdW5kLFxyXG4gIG9wdGlvbnNTdHJpbmdpZnksXHJcbiAgbWVhc3VyZVRpbWVcclxufSBmcm9tICcuLi8uLi91dGlscy5qcyc7XHJcblxyXG5pbXBvcnQgSHR0cEVycm9yIGZyb20gJy4uLy4uL2Vycm9ycy9IdHRwRXJyb3IuanMnO1xyXG5cclxuLy8gUmV2ZXJzZWQgTUlNRSB0eXBlc1xyXG5jb25zdCByZXZlcnNlZE1pbWUgPSB7XHJcbiAgcG5nOiAnaW1hZ2UvcG5nJyxcclxuICBqcGVnOiAnaW1hZ2UvanBlZycsXHJcbiAgZ2lmOiAnaW1hZ2UvZ2lmJyxcclxuICBwZGY6ICdhcHBsaWNhdGlvbi9wZGYnLFxyXG4gIHN2ZzogJ2ltYWdlL3N2Zyt4bWwnXHJcbn07XHJcblxyXG4vLyBUaGUgcmVxdWVzdHMgY291bnRlclxyXG5sZXQgcmVxdWVzdHNDb3VudGVyID0gMDtcclxuXHJcbi8vIFRoZSBhcnJheSBvZiBjYWxsYmFja3MgdG8gY2FsbCBiZWZvcmUgYSByZXF1ZXN0XHJcbmNvbnN0IGJlZm9yZVJlcXVlc3QgPSBbXTtcclxuXHJcbi8vIFRoZSBhcnJheSBvZiBjYWxsYmFja3MgdG8gY2FsbCBhZnRlciBhIHJlcXVlc3RcclxuY29uc3QgYWZ0ZXJSZXF1ZXN0ID0gW107XHJcblxyXG4vKipcclxuICogSW52b2tlcyBhbiBhcnJheSBvZiBjYWxsYmFjayBmdW5jdGlvbnMgd2l0aCBzcGVjaWZpZWQgcGFyYW1ldGVycywgYWxsb3dpbmdcclxuICogY3VzdG9taXphdGlvbiBvZiByZXF1ZXN0IGhhbmRsaW5nLlxyXG4gKlxyXG4gKiBAcGFyYW0ge0Z1bmN0aW9uW119IGNhbGxiYWNrcyAtIEFuIGFycmF5IG9mIGNhbGxiYWNrIGZ1bmN0aW9uc1xyXG4gKiB0byBiZSBleGVjdXRlZC5cclxuICogQHBhcmFtIHtFeHByZXNzLlJlcXVlc3R9IHJlcXVlc3QgLSBUaGUgRXhwcmVzcyByZXF1ZXN0IG9iamVjdC5cclxuICogQHBhcmFtIHtFeHByZXNzLlJlc3BvbnNlfSByZXNwb25zZSAtIFRoZSBFeHByZXNzIHJlc3BvbnNlIG9iamVjdC5cclxuICogQHBhcmFtIHtPYmplY3R9IGRhdGEgLSBBbiBvYmplY3QgY29udGFpbmluZyBwYXJhbWV0ZXJzIGxpa2UgaWQsIHVuaXF1ZUlkLFxyXG4gKiB0eXBlLCBhbmQgYm9keS5cclxuICpcclxuICogQHJldHVybnMge2Jvb2xlYW59IC0gUmV0dXJucyBhIGJvb2xlYW4gaW5kaWNhdGluZyB0aGUgb3ZlcmFsbCByZXN1bHRcclxuICogb2YgdGhlIGNhbGxiYWNrIGludm9jYXRpb25zLlxyXG4gKi9cclxuY29uc3QgZG9DYWxsYmFja3MgPSAoY2FsbGJhY2tzLCByZXF1ZXN0LCByZXNwb25zZSwgZGF0YSkgPT4ge1xyXG4gIGxldCByZXN1bHQgPSB0cnVlO1xyXG4gIGNvbnN0IHsgaWQsIHVuaXF1ZUlkLCB0eXBlLCBib2R5IH0gPSBkYXRhO1xyXG5cclxuICBjYWxsYmFja3Muc29tZSgoY2FsbGJhY2spID0+IHtcclxuICAgIGlmIChjYWxsYmFjaykge1xyXG4gICAgICBsZXQgY2FsbFJlc3BvbnNlID0gY2FsbGJhY2socmVxdWVzdCwgcmVzcG9uc2UsIGlkLCB1bmlxdWVJZCwgdHlwZSwgYm9keSk7XHJcblxyXG4gICAgICBpZiAoY2FsbFJlc3BvbnNlICE9PSB1bmRlZmluZWQgJiYgY2FsbFJlc3BvbnNlICE9PSB0cnVlKSB7XHJcbiAgICAgICAgcmVzdWx0ID0gY2FsbFJlc3BvbnNlO1xyXG4gICAgICB9XHJcblxyXG4gICAgICByZXR1cm4gdHJ1ZTtcclxuICAgIH1cclxuICB9KTtcclxuXHJcbiAgcmV0dXJuIHJlc3VsdDtcclxufTtcclxuXHJcbi8qKlxyXG4gKiBIYW5kbGVzIHRoZSBleHBvcnQgcmVxdWVzdHMgZnJvbSB0aGUgY2xpZW50LlxyXG4gKlxyXG4gKiBAcGFyYW0ge0V4cHJlc3MuUmVxdWVzdH0gcmVxdWVzdCAtIFRoZSBFeHByZXNzIHJlcXVlc3Qgb2JqZWN0LlxyXG4gKiBAcGFyYW0ge0V4cHJlc3MuUmVzcG9uc2V9IHJlc3BvbnNlIC0gVGhlIEV4cHJlc3MgcmVzcG9uc2Ugb2JqZWN0LlxyXG4gKiBAcGFyYW0ge0Z1bmN0aW9ufSBuZXh0IC0gVGhlIG5leHQgbWlkZGxld2FyZSBmdW5jdGlvbi5cclxuICpcclxuICogQHJldHVybnMge1Byb21pc2U8dm9pZD59IC0gQSBwcm9taXNlIHRoYXQgcmVzb2x2ZXMgb25jZSB0aGUgZXhwb3J0IHByb2Nlc3NcclxuICogaXMgY29tcGxldGUuXHJcbiAqL1xyXG5jb25zdCBleHBvcnRIYW5kbGVyID0gYXN5bmMgKHJlcXVlc3QsIHJlc3BvbnNlLCBuZXh0KSA9PiB7XHJcbiAgdHJ5IHtcclxuICAgIC8vIFN0YXJ0IGNvdW50aW5nIHRpbWVcclxuICAgIGNvbnN0IHN0b3BDb3VudGVyID0gbWVhc3VyZVRpbWUoKTtcclxuXHJcbiAgICAvLyBDcmVhdGUgYSB1bmlxdWUgSUQgZm9yIGEgcmVxdWVzdFxyXG4gICAgY29uc3QgdW5pcXVlSWQgPSB1dWlkKCkucmVwbGFjZSgvLS9nLCAnJyk7XHJcblxyXG4gICAgLy8gR2V0IHRoZSBjdXJyZW50IHNlcnZlcidzIGdlbmVyYWwgb3B0aW9uc1xyXG4gICAgY29uc3QgZGVmYXVsdE9wdGlvbnMgPSBnZXRPcHRpb25zKCk7XHJcblxyXG4gICAgY29uc3QgYm9keSA9IHJlcXVlc3QuYm9keTtcclxuICAgIGNvbnN0IGlkID0gKytyZXF1ZXN0c0NvdW50ZXI7XHJcblxyXG4gICAgbGV0IHR5cGUgPSBmaXhUeXBlKGJvZHkudHlwZSk7XHJcblxyXG4gICAgLy8gVGhyb3cgJ0JhZCBSZXF1ZXN0JyBpZiB0aGVyZSdzIG5vIGJvZHlcclxuICAgIGlmICghYm9keSB8fCBpc09iamVjdEVtcHR5KGJvZHkpKSB7XHJcbiAgICAgIHRocm93IG5ldyBIdHRwRXJyb3IoXHJcbiAgICAgICAgJ1RoZSByZXF1ZXN0IGJvZHkgaXMgcmVxdWlyZWQuIFBsZWFzZSBlbnN1cmUgdGhhdCB5b3VyIENvbnRlbnQtVHlwZSBoZWFkZXIgaXMgY29ycmVjdCAoYWNjZXB0ZWQgdHlwZXMgYXJlIGFwcGxpY2F0aW9uL2pzb24gYW5kIG11bHRpcGFydC9mb3JtLWRhdGEpLicsXHJcbiAgICAgICAgNDAwXHJcbiAgICAgICk7XHJcbiAgICB9XHJcblxyXG4gICAgLy8gQWxsIG9mIHRoZSBiZWxvdyBjYW4gYmUgdXNlZFxyXG4gICAgbGV0IGluc3RyID0gaXNDb3JyZWN0SlNPTihib2R5LmluZmlsZSB8fCBib2R5Lm9wdGlvbnMgfHwgYm9keS5kYXRhKTtcclxuXHJcbiAgICAvLyBUaHJvdyAnQmFkIFJlcXVlc3QnIGlmIHRoZXJlJ3Mgbm8gSlNPTiBvciBTVkcgdG8gZXhwb3J0XHJcbiAgICBpZiAoIWluc3RyICYmICFib2R5LnN2Zykge1xyXG4gICAgICBsb2coXHJcbiAgICAgICAgMixcclxuICAgICAgICBgVGhlIHJlcXVlc3Qgd2l0aCBJRCAke3VuaXF1ZUlkfSBmcm9tICR7XHJcbiAgICAgICAgICByZXF1ZXN0LmhlYWRlcnNbJ3gtZm9yd2FyZGVkLWZvciddIHx8IHJlcXVlc3QuY29ubmVjdGlvbi5yZW1vdGVBZGRyZXNzXHJcbiAgICAgICAgfSB3YXMgaW5jb3JyZWN0OlxyXG4gIENvbnRlbnQtVHlwZTogJHtyZXF1ZXN0LmhlYWRlcnNbJ2NvbnRlbnQtdHlwZSddfS4gXHJcbiAgQ2hhcnQgY29uc3RydWN0b3I6ICR7Ym9keS5jb25zdHJ9LlxyXG4gIERpbWVuc2lvbnM6ICR7Ym9keS53aWR0aH14JHtib2R5LmhlaWdodH0gQCAke2JvZHkuc2NhbGV9IHNjYWxlLlxyXG4gIFR5cGU6ICR7dHlwZX0uXHJcbiAgSXMgU1ZHIHNldD8gJHt0eXBlb2YgYm9keS5zdmcgIT09ICd1bmRlZmluZWQnfS5cclxuICBCNjQ/ICR7dHlwZW9mIGJvZHkuYjY0ICE9PSAndW5kZWZpbmVkJ30uXHJcbiAgTm8gZG93bmxvYWQ/ICR7dHlwZW9mIGJvZHkubm9Eb3dubG9hZCAhPT0gJ3VuZGVmaW5lZCd9LlxyXG5cclxuICBQYXlsb2FkIHJlY2VpdmVkOiAke0pTT04uc3RyaW5naWZ5KGJvZHkuaW5maWxlIHx8IGJvZHkub3B0aW9ucyB8fCBib2R5LmRhdGEgfHwgYm9keS5zdmcpfVxyXG5cclxuICBgXHJcbiAgICAgICk7XHJcblxyXG4gICAgICB0aHJvdyBuZXcgSHR0cEVycm9yKFxyXG4gICAgICAgIFwiTm8gY29ycmVjdCBjaGFydCBkYXRhIGZvdW5kLiBFbnN1cmUgdGhhdCB5b3UgYXJlIHVzaW5nIGVpdGhlciBhcHBsaWNhdGlvbi9qc29uIG9yIG11bHRpcGFydC9mb3JtLWRhdGEgaGVhZGVycy4gSWYgc2VuZGluZyBKU09OLCBtYWtlIHN1cmUgdGhlIGNoYXJ0IGRhdGEgaXMgaW4gdGhlICdpbmZpbGUnLCAnb3B0aW9ucycsIG9yICdkYXRhJyBhdHRyaWJ1dGUuIElmIHNlbmRpbmcgU1ZHLCBlbnN1cmUgaXQgaXMgaW4gdGhlICdzdmcnIGF0dHJpYnV0ZS5cIixcclxuICAgICAgICA0MDBcclxuICAgICAgKTtcclxuICAgIH1cclxuXHJcbiAgICBsZXQgY2FsbFJlc3BvbnNlID0gZmFsc2U7XHJcblxyXG4gICAgLy8gQ2FsbCB0aGUgYmVmb3JlIHJlcXVlc3QgZnVuY3Rpb25zXHJcbiAgICBjYWxsUmVzcG9uc2UgPSBkb0NhbGxiYWNrcyhiZWZvcmVSZXF1ZXN0LCByZXF1ZXN0LCByZXNwb25zZSwge1xyXG4gICAgICBpZCxcclxuICAgICAgdW5pcXVlSWQsXHJcbiAgICAgIHR5cGUsXHJcbiAgICAgIGJvZHlcclxuICAgIH0pO1xyXG5cclxuICAgIC8vIEJsb2NrIHRoZSByZXF1ZXN0IGlmIG9uZSBvZiBhIGNhbGxiYWNrcyBmYWlsZWRcclxuICAgIGlmIChjYWxsUmVzcG9uc2UgIT09IHRydWUpIHtcclxuICAgICAgcmV0dXJuIHJlc3BvbnNlLnNlbmQoY2FsbFJlc3BvbnNlKTtcclxuICAgIH1cclxuXHJcbiAgICBsZXQgY29ubmVjdGlvbkFib3J0ZWQgPSBmYWxzZTtcclxuXHJcbiAgICAvLyBJbiBjYXNlIHRoZSBjb25uZWN0aW9uIGlzIGNsb3NlZCwgZm9yY2UgdG8gYWJvcnQgZnVydGhlciBhY3Rpb25zXHJcbiAgICByZXF1ZXN0LnNvY2tldC5vbignY2xvc2UnLCAoaGFkRXJyb3JzKSA9PiB7XHJcbiAgICAgIGlmIChoYWRFcnJvcnMpIHtcclxuICAgICAgICBjb25uZWN0aW9uQWJvcnRlZCA9IHRydWU7XHJcbiAgICAgIH1cclxuICAgIH0pO1xyXG5cclxuICAgIGxvZyg0LCBgW2V4cG9ydF0gR290IGFuIGluY29taW5nIEhUVFAgcmVxdWVzdCB3aXRoIElEICR7dW5pcXVlSWR9LmApO1xyXG5cclxuICAgIGJvZHkuY29uc3RyID0gKHR5cGVvZiBib2R5LmNvbnN0ciA9PT0gJ3N0cmluZycgJiYgYm9keS5jb25zdHIpIHx8ICdjaGFydCc7XHJcblxyXG4gICAgLy8gR2F0aGVyIGFuZCBvcmdhbml6ZSBvcHRpb25zIGZyb20gdGhlIHBheWxvYWRcclxuICAgIGNvbnN0IHJlcXVlc3RPcHRpb25zID0ge1xyXG4gICAgICBleHBvcnQ6IHtcclxuICAgICAgICBpbnN0cixcclxuICAgICAgICB0eXBlLFxyXG4gICAgICAgIGNvbnN0cjogYm9keS5jb25zdHJbMF0udG9Mb3dlckNhc2UoKSArIGJvZHkuY29uc3RyLnN1YnN0cigxKSxcclxuICAgICAgICBoZWlnaHQ6IGJvZHkuaGVpZ2h0LFxyXG4gICAgICAgIHdpZHRoOiBib2R5LndpZHRoLFxyXG4gICAgICAgIHNjYWxlOiBib2R5LnNjYWxlIHx8IGRlZmF1bHRPcHRpb25zLmV4cG9ydC5zY2FsZSxcclxuICAgICAgICBnbG9iYWxPcHRpb25zOiBpc0NvcnJlY3RKU09OKGJvZHkuZ2xvYmFsT3B0aW9ucywgdHJ1ZSksXHJcbiAgICAgICAgdGhlbWVPcHRpb25zOiBpc0NvcnJlY3RKU09OKGJvZHkudGhlbWVPcHRpb25zLCB0cnVlKVxyXG4gICAgICB9LFxyXG4gICAgICBjdXN0b21Mb2dpYzoge1xyXG4gICAgICAgIGFsbG93Q29kZUV4ZWN1dGlvbjogZ2V0QWxsb3dDb2RlRXhlY3V0aW9uKCksXHJcbiAgICAgICAgYWxsb3dGaWxlUmVzb3VyY2VzOiBmYWxzZSxcclxuICAgICAgICByZXNvdXJjZXM6IGlzQ29ycmVjdEpTT04oYm9keS5yZXNvdXJjZXMsIHRydWUpLFxyXG4gICAgICAgIGNhbGxiYWNrOiBib2R5LmNhbGxiYWNrLFxyXG4gICAgICAgIGN1c3RvbUNvZGU6IGJvZHkuY3VzdG9tQ29kZVxyXG4gICAgICB9XHJcbiAgICB9O1xyXG5cclxuICAgIGlmIChpbnN0cikge1xyXG4gICAgICAvLyBTdHJpbmdpZnkgSlNPTiB3aXRoIG9wdGlvbnNcclxuICAgICAgcmVxdWVzdE9wdGlvbnMuZXhwb3J0Lmluc3RyID0gb3B0aW9uc1N0cmluZ2lmeShcclxuICAgICAgICBpbnN0cixcclxuICAgICAgICByZXF1ZXN0T3B0aW9ucy5jdXN0b21Mb2dpYy5hbGxvd0NvZGVFeGVjdXRpb25cclxuICAgICAgKTtcclxuICAgIH1cclxuXHJcbiAgICAvLyBNZXJnZSB0aGUgcmVxdWVzdCBvcHRpb25zIGludG8gZGVmYXVsdCBvbmVzXHJcbiAgICBjb25zdCBvcHRpb25zID0gbWVyZ2VDb25maWdPcHRpb25zKGRlZmF1bHRPcHRpb25zLCByZXF1ZXN0T3B0aW9ucyk7XHJcblxyXG4gICAgLy8gU2F2ZSB0aGUgSlNPTiBpZiBleGlzdHNcclxuICAgIG9wdGlvbnMuZXhwb3J0Lm9wdGlvbnMgPSBpbnN0cjtcclxuXHJcbiAgICAvLyBMYXN0bHksIGFkZCB0aGUgc2VydmVyIHNwZWNpZmljIGFyZ3VtZW50cyBpbnRvIG9wdGlvbnMgYXMgcGF5bG9hZFxyXG4gICAgb3B0aW9ucy5wYXlsb2FkID0ge1xyXG4gICAgICBzdmc6IGJvZHkuc3ZnIHx8IGZhbHNlLFxyXG4gICAgICBiNjQ6IGJvZHkuYjY0IHx8IGZhbHNlLFxyXG4gICAgICBub0Rvd25sb2FkOiBib2R5Lm5vRG93bmxvYWQgfHwgZmFsc2UsXHJcbiAgICAgIHJlcXVlc3RJZDogdW5pcXVlSWRcclxuICAgIH07XHJcblxyXG4gICAgLy8gVGVzdCB4bGluazpocmVmIGVsZW1lbnRzIGZyb20gcGF5bG9hZCdzIFNWR1xyXG4gICAgaWYgKGJvZHkuc3ZnICYmIGlzUHJpdmF0ZVJhbmdlVXJsRm91bmQob3B0aW9ucy5wYXlsb2FkLnN2ZykpIHtcclxuICAgICAgdGhyb3cgbmV3IEh0dHBFcnJvcihcclxuICAgICAgICAnU1ZHIHBvdGVudGlhbGx5IGNvbnRhaW4gYXQgbGVhc3Qgb25lIGZvcmJpZGRlbiBVUkwgaW4geGxpbms6aHJlZiBlbGVtZW50LiBQbGVhc2UgcmV2aWV3IHRoZSBTVkcgY29udGVudCBhbmQgZW5zdXJlIHRoYXQgYWxsIHJlZmVyZW5jZWQgVVJMcyBjb21wbHkgd2l0aCBzZWN1cml0eSBwb2xpY2llcy4nLFxyXG4gICAgICAgIDQwMFxyXG4gICAgICApO1xyXG4gICAgfVxyXG5cclxuICAgIC8vIFN0YXJ0IHRoZSBleHBvcnQgcHJvY2Vzc1xyXG4gICAgYXdhaXQgc3RhcnRFeHBvcnQob3B0aW9ucywgKGVycm9yLCBpbmZvKSA9PiB7XHJcbiAgICAgIC8vIFJlbW92ZSB0aGUgY2xvc2UgZXZlbnQgZnJvbSB0aGUgc29ja2V0XHJcbiAgICAgIHJlcXVlc3Quc29ja2V0LnJlbW92ZUFsbExpc3RlbmVycygnY2xvc2UnKTtcclxuXHJcbiAgICAgIC8vIEFmdGVyIHRoZSB3aG9sZSBleHBvcnRpbmcgcHJvY2Vzc1xyXG4gICAgICBpZiAoZGVmYXVsdE9wdGlvbnMuc2VydmVyLmJlbmNobWFya2luZykge1xyXG4gICAgICAgIGxvZyhcclxuICAgICAgICAgIDUsXHJcbiAgICAgICAgICBgW2JlbmNobWFya10gUmVxdWVzdCB3aXRoIElEICR7dW5pcXVlSWR9IC0gQWZ0ZXIgdGhlIHdob2xlIGV4cG9ydGluZyBwcm9jZXNzOiAke3N0b3BDb3VudGVyKCl9bXMuYFxyXG4gICAgICAgICk7XHJcbiAgICAgIH1cclxuXHJcbiAgICAgIC8vIElmIHRoZSBjb25uZWN0aW9uIHdhcyBjbG9zZWQsIGRvIG5vdGhpbmdcclxuICAgICAgaWYgKGNvbm5lY3Rpb25BYm9ydGVkKSB7XHJcbiAgICAgICAgcmV0dXJuIGxvZyhcclxuICAgICAgICAgIDMsXHJcbiAgICAgICAgICBgW2V4cG9ydF0gVGhlIGNsaWVudCBjbG9zZWQgdGhlIGNvbm5lY3Rpb24gYmVmb3JlIHRoZSBjaGFydCBmaW5pc2hlZCBwcm9jZXNzaW5nLmBcclxuICAgICAgICApO1xyXG4gICAgICB9XHJcblxyXG4gICAgICAvLyBJZiBlcnJvciwgbG9nIGl0IGFuZCBzZW5kIGl0IHRvIHRoZSBlcnJvciBtaWRkbGV3YXJlXHJcbiAgICAgIGlmIChlcnJvcikge1xyXG4gICAgICAgIHRocm93IGVycm9yO1xyXG4gICAgICB9XHJcblxyXG4gICAgICAvLyBJZiBkYXRhIGlzIG1pc3NpbmcsIGxvZyB0aGUgbWVzc2FnZSBhbmQgc2VuZCBpdCB0byB0aGUgZXJyb3IgbWlkZGxld2FyZVxyXG4gICAgICBpZiAoIWluZm8gfHwgIWluZm8ucmVzdWx0KSB7XHJcbiAgICAgICAgdGhyb3cgbmV3IEh0dHBFcnJvcihcclxuICAgICAgICAgIGBVbmV4cGVjdGVkIHJldHVybiBmcm9tIGNoYXJ0IGdlbmVyYXRpb24uIFBsZWFzZSBjaGVjayB5b3VyIHJlcXVlc3QgZGF0YS4gRm9yIHRoZSByZXF1ZXN0IHdpdGggSUQgJHt1bmlxdWVJZH0sIHRoZSByZXN1bHQgaXMgJHtpbmZvLnJlc3VsdH0uYCxcclxuICAgICAgICAgIDQwMFxyXG4gICAgICAgICk7XHJcbiAgICAgIH1cclxuXHJcbiAgICAgIC8vIEdldCB0aGUgdHlwZSBmcm9tIG9wdGlvbnNcclxuICAgICAgdHlwZSA9IGluZm8ub3B0aW9ucy5leHBvcnQudHlwZTtcclxuXHJcbiAgICAgIC8vIFRoZSBhZnRlciByZXF1ZXN0IGNhbGxiYWNrc1xyXG4gICAgICBkb0NhbGxiYWNrcyhhZnRlclJlcXVlc3QsIHJlcXVlc3QsIHJlc3BvbnNlLCB7IGlkLCBib2R5OiBpbmZvLnJlc3VsdCB9KTtcclxuXHJcbiAgICAgIGlmIChpbmZvLnJlc3VsdCkge1xyXG4gICAgICAgIC8vIElmIG9ubHkgYmFzZTY0IGlzIHJlcXVpcmVkLCByZXR1cm4gaXRcclxuICAgICAgICBpZiAoYm9keS5iNjQpIHtcclxuICAgICAgICAgIC8vIFNWRyBFeGNlcHRpb24gZm9yIHRoZSBIaWdoY2hhcnRzIDExLjMuMCB2ZXJzaW9uXHJcbiAgICAgICAgICBpZiAodHlwZSA9PT0gJ3BkZicgfHwgdHlwZSA9PSAnc3ZnJykge1xyXG4gICAgICAgICAgICByZXR1cm4gcmVzcG9uc2Uuc2VuZChcclxuICAgICAgICAgICAgICBCdWZmZXIuZnJvbShpbmZvLnJlc3VsdCwgJ3V0ZjgnKS50b1N0cmluZygnYmFzZTY0JylcclxuICAgICAgICAgICAgKTtcclxuICAgICAgICAgIH1cclxuXHJcbiAgICAgICAgICByZXR1cm4gcmVzcG9uc2Uuc2VuZChpbmZvLnJlc3VsdCk7XHJcbiAgICAgICAgfVxyXG5cclxuICAgICAgICAvLyBTZXQgY29ycmVjdCBjb250ZW50IHR5cGVcclxuICAgICAgICByZXNwb25zZS5oZWFkZXIoJ0NvbnRlbnQtVHlwZScsIHJldmVyc2VkTWltZVt0eXBlXSB8fCAnaW1hZ2UvcG5nJyk7XHJcblxyXG4gICAgICAgIC8vIERlY2lkZSB3aGV0aGVyIHRvIGRvd25sb2FkIG9yIG5vdCBjaGFydCBmaWxlXHJcbiAgICAgICAgaWYgKCFib2R5Lm5vRG93bmxvYWQpIHtcclxuICAgICAgICAgIHJlc3BvbnNlLmF0dGFjaG1lbnQoXHJcbiAgICAgICAgICAgIGAke3JlcXVlc3QucGFyYW1zLmZpbGVuYW1lIHx8IHJlcXVlc3QuYm9keS5maWxlbmFtZSB8fCAnY2hhcnQnfS4ke1xyXG4gICAgICAgICAgICAgIHR5cGUgfHwgJ3BuZydcclxuICAgICAgICAgICAgfWBcclxuICAgICAgICAgICk7XHJcbiAgICAgICAgfVxyXG5cclxuICAgICAgICAvLyBJZiBTVkcsIHJldHVybiBwbGFpbiBjb250ZW50XHJcbiAgICAgICAgcmV0dXJuIHR5cGUgPT09ICdzdmcnXHJcbiAgICAgICAgICA/IHJlc3BvbnNlLnNlbmQoaW5mby5yZXN1bHQpXHJcbiAgICAgICAgICA6IHJlc3BvbnNlLnNlbmQoQnVmZmVyLmZyb20oaW5mby5yZXN1bHQsICdiYXNlNjQnKSk7XHJcbiAgICAgIH1cclxuICAgIH0pO1xyXG4gIH0gY2F0Y2ggKGVycm9yKSB7XHJcbiAgICBuZXh0KGVycm9yKTtcclxuICB9XHJcbn07XHJcblxyXG5leHBvcnQgZGVmYXVsdCAoYXBwKSA9PiB7XHJcbiAgLyoqXHJcbiAgICogQWRkcyB0aGUgUE9TVCAvIGEgcm91dGUgZm9yIGhhbmRsaW5nIFBPU1QgcmVxdWVzdHMgYXQgdGhlIHJvb3QgZW5kcG9pbnQuXHJcbiAgICovXHJcbiAgYXBwLnBvc3QoJy8nLCBleHBvcnRIYW5kbGVyKTtcclxuXHJcbiAgLyoqXHJcbiAgICogQWRkcyB0aGUgUE9TVCAvOmZpbGVuYW1lIGEgcm91dGUgZm9yIGhhbmRsaW5nIFBPU1QgcmVxdWVzdHMgd2l0aFxyXG4gICAqIGEgc3BlY2lmaWVkIGZpbGVuYW1lIHBhcmFtZXRlci5cclxuICAgKi9cclxuICBhcHAucG9zdCgnLzpmaWxlbmFtZScsIGV4cG9ydEhhbmRsZXIpO1xyXG59O1xyXG4iLCIvKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKlxyXG5cclxuSGlnaGNoYXJ0cyBFeHBvcnQgU2VydmVyXHJcblxyXG5Db3B5cmlnaHQgKGMpIDIwMTYtMjAyNCwgSGlnaHNvZnRcclxuXHJcbkxpY2VuY2VkIHVuZGVyIHRoZSBNSVQgbGljZW5jZS5cclxuXHJcbkFkZGl0aW9uYWxseSBhIHZhbGlkIEhpZ2hjaGFydHMgbGljZW5zZSBpcyByZXF1aXJlZCBmb3IgdXNlLlxyXG5cclxuU2VlIExJQ0VOU0UgZmlsZSBpbiByb290IGZvciBkZXRhaWxzLlxyXG5cclxuKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKi9cclxuXHJcbmltcG9ydCB7IHJlYWRGaWxlU3luYyB9IGZyb20gJ2ZzJztcclxuaW1wb3J0IHsgam9pbiBhcyBwYXRoZXIgfSBmcm9tICdwYXRoJztcclxuaW1wb3J0IHsgbG9nIH0gZnJvbSAnLi4vLi4vbG9nZ2VyLmpzJztcclxuXHJcbmltcG9ydCB7IHZlcnNpb24gfSBmcm9tICcuLi8uLi9jYWNoZS5qcyc7XHJcbmltcG9ydCB7IGFkZEludGVydmFsIH0gZnJvbSAnLi4vLi4vaW50ZXJ2YWxzLmpzJztcclxuaW1wb3J0IHBvb2wgZnJvbSAnLi4vLi4vcG9vbC5qcyc7XHJcbmltcG9ydCB7IF9fZGlybmFtZSB9IGZyb20gJy4uLy4uL3V0aWxzLmpzJztcclxuXHJcbmNvbnN0IHBrZ0ZpbGUgPSBKU09OLnBhcnNlKHJlYWRGaWxlU3luYyhwYXRoZXIoX19kaXJuYW1lLCAncGFja2FnZS5qc29uJykpKTtcclxuXHJcbmNvbnN0IHNlcnZlclN0YXJ0VGltZSA9IG5ldyBEYXRlKCk7XHJcblxyXG5jb25zdCBzdWNjZXNzUmF0ZXMgPSBbXTtcclxuY29uc3QgcmVjb3JkSW50ZXJ2YWwgPSA2MCAqIDEwMDA7IC8vIHJlY29yZCBldmVyeSBtaW51dGVcclxuY29uc3Qgd2luZG93U2l6ZSA9IDMwOyAvLyAzMCBtaW51dGVzXHJcblxyXG4vKipcclxuICogQ2FsY3VsYXRlcyBtb3ZpbmcgYXZlcmFnZSBpbmRpY2F0b3IgYmFzZWQgb24gdGhlIGRhdGEgZnJvbSB0aGUgc3VjY2Vzc1JhdGVzXHJcbiAqIGFycmF5LlxyXG4gKlxyXG4gKiBAcmV0dXJucyB7bnVtYmVyfSAtIEEgbW92aW5nIGF2ZXJhZ2UgZm9yIHN1Y2Nlc3MgcmF0aW8gb2YgdGhlIHNlcnZlciBleHBvcnRzLlxyXG4gKi9cclxuZnVuY3Rpb24gY2FsY3VsYXRlTW92aW5nQXZlcmFnZSgpIHtcclxuICBjb25zdCBzdW0gPSBzdWNjZXNzUmF0ZXMucmVkdWNlKChhLCBiKSA9PiBhICsgYiwgMCk7XHJcbiAgcmV0dXJuIHN1bSAvIHN1Y2Nlc3NSYXRlcy5sZW5ndGg7XHJcbn1cclxuXHJcbi8qKlxyXG4gKiBTdGFydHMgdGhlIGludGVydmFsIHJlc3BvbnNpYmxlIGZvciBjYWxjdWxhdGluZyBjdXJyZW50IHN1Y2Nlc3MgcmF0ZSByYXRpb1xyXG4gKiBhbmQgZ2F0aGVyc1xyXG4gKlxyXG4gKiBAcmV0dXJucyB7Tm9kZUpTLlRpbWVvdXR9IGlkIC0gSWQgb2YgYW4gaW50ZXJ2YWwuXHJcbiAqL1xyXG5leHBvcnQgY29uc3Qgc3RhcnRTdWNjZXNzUmF0ZSA9ICgpID0+XHJcbiAgc2V0SW50ZXJ2YWwoKCkgPT4ge1xyXG4gICAgY29uc3Qgc3RhdHMgPSBwb29sLmdldFN0YXRzKCk7XHJcbiAgICBjb25zdCBzdWNjZXNzUmF0aW8gPVxyXG4gICAgICBzdGF0cy5leHBvcnRBdHRlbXB0cyA9PT0gMFxyXG4gICAgICAgID8gMVxyXG4gICAgICAgIDogKHN0YXRzLnBlcmZvcm1lZEV4cG9ydHMgLyBzdGF0cy5leHBvcnRBdHRlbXB0cykgKiAxMDA7XHJcblxyXG4gICAgc3VjY2Vzc1JhdGVzLnB1c2goc3VjY2Vzc1JhdGlvKTtcclxuICAgIGlmIChzdWNjZXNzUmF0ZXMubGVuZ3RoID4gd2luZG93U2l6ZSkge1xyXG4gICAgICBzdWNjZXNzUmF0ZXMuc2hpZnQoKTtcclxuICAgIH1cclxuICB9LCByZWNvcmRJbnRlcnZhbCk7XHJcblxyXG4vKipcclxuICogQWRkcyB0aGUgL2hlYWx0aCBhbmQgL3N1Y2Nlc3MtbW92aW5nLWF2ZXJhZ2Ugcm91dGVzXHJcbiAqIHdoaWNoIG91dHB1dCBiYXNpYyBzdGF0cyBmb3IgdGhlIHNlcnZlci5cclxuICovXHJcbmV4cG9ydCBkZWZhdWx0IGZ1bmN0aW9uIGFkZEhlYWx0aFJvdXRlcyhhcHApIHtcclxuICBpZiAoIWFwcCkge1xyXG4gICAgcmV0dXJuIGZhbHNlO1xyXG4gIH1cclxuXHJcbiAgLy8gU3RhcnQgcHJvY2Vzc2luZyBzdWNjZXNzIHJhdGUgcmF0aW8gaW50ZXJ2YWwgYW5kIHNhdmUgaXRzIGlkIHRvIHRoZSBhcnJheVxyXG4gIC8vIGZvciB0aGUgZ3JhY2VmdWwgY2xlYXJpbmcgb24gc2h1dGRvd24gd2l0aCBpbmplY3RlZCBhZGRJbnRlcnZhbCBmdW50aW9uXHJcbiAgYWRkSW50ZXJ2YWwoc3RhcnRTdWNjZXNzUmF0ZSgpKTtcclxuXHJcbiAgYXBwLmdldCgnL2hlYWx0aCcsIChfLCByZXMpID0+IHtcclxuICAgIGNvbnN0IHN0YXRzID0gcG9vbC5nZXRTdGF0cygpO1xyXG4gICAgY29uc3QgcGVyaW9kID0gc3VjY2Vzc1JhdGVzLmxlbmd0aDtcclxuICAgIGNvbnN0IG1vdmluZ0F2ZXJhZ2UgPSBjYWxjdWxhdGVNb3ZpbmdBdmVyYWdlKCk7XHJcblxyXG4gICAgbG9nKDQsICdbaGVhbHRoLmpzXSBHRVQgL2hlYWx0aCBbMjAwXSAtIHJldHVybmluZyBzZXJ2ZXIgaGVhbHRoLicpO1xyXG5cclxuICAgIHJlcy5zZW5kKHtcclxuICAgICAgc3RhdHVzOiAnT0snLFxyXG4gICAgICBib290VGltZTogc2VydmVyU3RhcnRUaW1lLFxyXG4gICAgICB1cHRpbWU6XHJcbiAgICAgICAgTWF0aC5mbG9vcihcclxuICAgICAgICAgIChuZXcgRGF0ZSgpLmdldFRpbWUoKSAtIHNlcnZlclN0YXJ0VGltZS5nZXRUaW1lKCkpIC8gMTAwMCAvIDYwXHJcbiAgICAgICAgKSArICcgbWludXRlcycsXHJcbiAgICAgIHZlcnNpb246IHBrZ0ZpbGUudmVyc2lvbixcclxuICAgICAgaGlnaGNoYXJ0c1ZlcnNpb246IHZlcnNpb24oKSxcclxuICAgICAgYXZlcmFnZVByb2Nlc3NpbmdUaW1lOiBzdGF0cy5zcGVudEF2ZXJhZ2UsXHJcbiAgICAgIHBlcmZvcm1lZEV4cG9ydHM6IHN0YXRzLnBlcmZvcm1lZEV4cG9ydHMsXHJcbiAgICAgIGZhaWxlZEV4cG9ydHM6IHN0YXRzLmRyb3BwZWRFeHBvcnRzLFxyXG4gICAgICBleHBvcnRBdHRlbXB0czogc3RhdHMuZXhwb3J0QXR0ZW1wdHMsXHJcbiAgICAgIHN1Y2Vzc1JhdGlvOiAoc3RhdHMucGVyZm9ybWVkRXhwb3J0cyAvIHN0YXRzLmV4cG9ydEF0dGVtcHRzKSAqIDEwMCxcclxuICAgICAgLy8gZXNsaW50LWRpc2FibGUtbmV4dC1saW5lIGltcG9ydC9uby1uYW1lZC1hcy1kZWZhdWx0LW1lbWJlclxyXG4gICAgICBwb29sOiBwb29sLmdldFBvb2xJbmZvSlNPTigpLFxyXG5cclxuICAgICAgLy8gTW92aW5nIGF2ZXJhZ2VcclxuICAgICAgcGVyaW9kLFxyXG4gICAgICBtb3ZpbmdBdmVyYWdlLFxyXG4gICAgICBtZXNzYWdlOlxyXG4gICAgICAgIGlzTmFOKG1vdmluZ0F2ZXJhZ2UpIHx8ICFzdWNjZXNzUmF0ZXMubGVuZ3RoXHJcbiAgICAgICAgICA/ICdUb28gZWFybHkgdG8gcmVwb3J0LiBObyBleHBvcnRzIG1hZGUgeWV0LiBQbGVhc2UgY2hlY2sgYmFjayBzb29uLidcclxuICAgICAgICAgIDogYExhc3QgJHtwZXJpb2R9IG1pbnV0ZXMgaGFkIGEgc3VjY2VzcyByYXRlIG9mICR7bW92aW5nQXZlcmFnZS50b0ZpeGVkKDIpfSUuYCxcclxuXHJcbiAgICAgIC8vIFNWRy9KU09OIGF0dGVtcHRzXHJcbiAgICAgIHN2Z0V4cG9ydEF0dGVtcHRzOiBzdGF0cy5leHBvcnRGcm9tU3ZnQXR0ZW1wdHMsXHJcbiAgICAgIGpzb25FeHBvcnRBdHRlbXB0czogc3RhdHMucGVyZm9ybWVkRXhwb3J0cyAtIHN0YXRzLmV4cG9ydEZyb21TdmdBdHRlbXB0c1xyXG4gICAgfSk7XHJcbiAgfSk7XHJcbn1cclxuIiwiLyoqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKipcclxuXHJcbkhpZ2hjaGFydHMgRXhwb3J0IFNlcnZlclxyXG5cclxuQ29weXJpZ2h0IChjKSAyMDE2LTIwMjQsIEhpZ2hzb2Z0XHJcblxyXG5MaWNlbmNlZCB1bmRlciB0aGUgTUlUIGxpY2VuY2UuXHJcblxyXG5BZGRpdGlvbmFsbHkgYSB2YWxpZCBIaWdoY2hhcnRzIGxpY2Vuc2UgaXMgcmVxdWlyZWQgZm9yIHVzZS5cclxuXHJcblNlZSBMSUNFTlNFIGZpbGUgaW4gcm9vdCBmb3IgZGV0YWlscy5cclxuXHJcbioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKiovXHJcblxyXG5pbXBvcnQgeyBwcm9taXNlcyBhcyBmc1Byb21pc2VzIH0gZnJvbSAnZnMnO1xyXG5pbXBvcnQgeyBwb3NpeCB9IGZyb20gJ3BhdGgnO1xyXG5cclxuaW1wb3J0IGNvcnMgZnJvbSAnY29ycyc7XHJcbmltcG9ydCBleHByZXNzIGZyb20gJ2V4cHJlc3MnO1xyXG5pbXBvcnQgaHR0cCBmcm9tICdodHRwJztcclxuaW1wb3J0IGh0dHBzIGZyb20gJ2h0dHBzJztcclxuaW1wb3J0IG11bHRlciBmcm9tICdtdWx0ZXInO1xyXG5cclxuaW1wb3J0IGVycm9ySGFuZGxlciBmcm9tICcuL2Vycm9yLmpzJztcclxuaW1wb3J0IHJhdGVMaW1pdCBmcm9tICcuL3JhdGVfbGltaXQuanMnO1xyXG5pbXBvcnQgeyBsb2csIGxvZ1dpdGhTdGFjayB9IGZyb20gJy4uL2xvZ2dlci5qcyc7XHJcbmltcG9ydCB7IF9fZGlybmFtZSB9IGZyb20gJy4uL3V0aWxzLmpzJztcclxuXHJcbmltcG9ydCB2U3dpdGNoUm91dGUgZnJvbSAnLi9yb3V0ZXMvY2hhbmdlX2hjX3ZlcnNpb24uanMnO1xyXG5pbXBvcnQgZXhwb3J0Um91dGVzIGZyb20gJy4vcm91dGVzL2V4cG9ydC5qcyc7XHJcbmltcG9ydCBoZWFsdGhSb3V0ZSBmcm9tICcuL3JvdXRlcy9oZWFsdGguanMnO1xyXG5pbXBvcnQgdWlSb3V0ZSBmcm9tICcuL3JvdXRlcy91aS5qcyc7XHJcblxyXG5pbXBvcnQgRXhwb3J0RXJyb3IgZnJvbSAnLi4vZXJyb3JzL0V4cG9ydEVycm9yLmpzJztcclxuXHJcbi8vIEFycmF5IG9mIGFuIGFjdGl2ZSBzZXJ2ZXJzXHJcbmNvbnN0IGFjdGl2ZVNlcnZlcnMgPSBuZXcgTWFwKCk7XHJcblxyXG4vLyBDcmVhdGUgZXhwcmVzcyBhcHBcclxuY29uc3QgYXBwID0gZXhwcmVzcygpO1xyXG5cclxuLy8gRGlzYWJsZSB0aGUgWC1Qb3dlcmVkLUJ5IGhlYWRlclxyXG5hcHAuZGlzYWJsZSgneC1wb3dlcmVkLWJ5Jyk7XHJcblxyXG4vLyBFbmFibGUgQ09SUyBzdXBwb3J0XHJcbmFwcC51c2UoY29ycygpKTtcclxuXHJcbi8vIEdldHRpbmcgYSBsb3Qgb2YgUmFuZ2VOb3RTYXRpc2ZpYWJsZUVycm9yIGV4Y2VwdGlvbi5cclxuLy8gRXZlbiB0aG91Z2ggdGhpcyBpcyBhIGRlcHJlY2F0ZWQgb3B0aW9ucywgbGV0J3MgdHJ5IHRvIHNldCBpdCB0byBmYWxzZS5cclxuYXBwLnVzZSgoX3JlcSwgcmVzLCBuZXh0KSA9PiB7XHJcbiAgcmVzLnNldCgnQWNjZXB0LVJhbmdlcycsICdub25lJyk7XHJcbiAgbmV4dCgpO1xyXG59KTtcclxuXHJcbi8qKlxyXG4gKiBBdHRhY2ggZXJyb3IgaGFuZGxlcnMgdG8gdGhlIHNlcnZlci5cclxuICpcclxuICogQHBhcmFtIHtodHRwLlNlcnZlcn0gc2VydmVyIC0gVGhlIEhUVFAvSFRUUFMgc2VydmVyIGluc3RhbmNlLlxyXG4gKi9cclxuY29uc3QgYXR0YWNoU2VydmVyRXJyb3JIYW5kbGVycyA9IChzZXJ2ZXIpID0+IHtcclxuICBzZXJ2ZXIub24oJ2NsaWVudEVycm9yJywgKGVycm9yLCBzb2NrZXQpID0+IHtcclxuICAgIGxvZ1dpdGhTdGFjayhcclxuICAgICAgMSxcclxuICAgICAgZXJyb3IsXHJcbiAgICAgIGBbc2VydmVyXSBDbGllbnQgZXJyb3I6ICR7ZXJyb3IubWVzc2FnZX0sIGRlc3Ryb3lpbmcgc29ja2V0LmBcclxuICAgICk7XHJcbiAgICBzb2NrZXQuZGVzdHJveSgpO1xyXG4gIH0pO1xyXG5cclxuICBzZXJ2ZXIub24oJ2Vycm9yJywgKGVycm9yKSA9PiB7XHJcbiAgICBsb2dXaXRoU3RhY2soMSwgZXJyb3IsIGBbc2VydmVyXSBTZXJ2ZXIgZXJyb3I6ICR7ZXJyb3IubWVzc2FnZX1gKTtcclxuICB9KTtcclxuXHJcbiAgc2VydmVyLm9uKCdjb25uZWN0aW9uJywgKHNvY2tldCkgPT4ge1xyXG4gICAgc29ja2V0Lm9uKCdlcnJvcicsIChlcnJvcikgPT4ge1xyXG4gICAgICBsb2dXaXRoU3RhY2soMSwgZXJyb3IsIGBbc2VydmVyXSBTb2NrZXQgZXJyb3I6ICR7ZXJyb3IubWVzc2FnZX1gKTtcclxuICAgIH0pO1xyXG4gIH0pO1xyXG59O1xyXG5cclxuLyoqXHJcbiAqIFN0YXJ0cyBhbiBIVFRQIHNlcnZlciBiYXNlZCBvbiB0aGUgcHJvdmlkZWQgY29uZmlndXJhdGlvbi4gVGhlIGBzZXJ2ZXJDb25maWdgXHJcbiAqIG9iamVjdCBjb250YWlucyBhbGwgc2VydmVyIHJlbGF0ZWQgcHJvcGVydGllcyAoc2VlIHRoZSBgc2VydmVyYCBzZWN0aW9uXHJcbiAqIGluIHRoZSBgbGliL3NjaGVtYXMvY29uZmlnLmpzYCBmaWxlIGZvciBhIHJlZmVyZW5jZSkuXHJcbiAqXHJcbiAqIEBwYXJhbSB7T2JqZWN0fSBzZXJ2ZXJDb25maWcgLSBUaGUgc2VydmVyIGNvbmZpZ3VyYXRpb24gb2JqZWN0LlxyXG4gKlxyXG4gKiBAdGhyb3dzIHtFeHBvcnRFcnJvcn0gLSBUaHJvd3MgYW4gZXJyb3IgaWYgdGhlIHNlcnZlciBjYW5ub3QgYmUgY29uZmlndXJlZFxyXG4gKiBhbmQgc3RhcnRlZC5cclxuICovXHJcbmV4cG9ydCBjb25zdCBzdGFydFNlcnZlciA9IGFzeW5jIChzZXJ2ZXJDb25maWcpID0+IHtcclxuICB0cnkge1xyXG4gICAgLy8gVE9ETzogUmVhZCBmcm9tIGNvbmZpZy9lbnZcclxuICAgIC8vIE5PVEU6XHJcbiAgICAvLyBUb28gYmlnIGxpbWl0cyBsZWFkIHRvIHRpbWVvdXRzIGluIHRoZSBleHBvcnQgcHJvY2VzcyB3aGVuIHRoZVxyXG4gICAgLy8gcmFzdGVyaXphdGlvbiB0aW1lb3V0IGlzIHNldCB0b28gbG93LlxyXG4gICAgY29uc3QgdXBsb2FkTGltaXRNaUIgPSBzZXJ2ZXJDb25maWcubWF4VXBsb2FkU2l6ZSB8fCAzO1xyXG4gICAgY29uc3QgdXBsb2FkTGltaXRCeXRlcyA9IHVwbG9hZExpbWl0TWlCICogMTAyNCAqIDEwMjQ7XHJcblxyXG4gICAgLy8gRW5hYmxlIHBhcnNpbmcgb2YgZm9ybSBkYXRhIChmaWxlcykgd2l0aCBNdWx0ZXIgcGFja2FnZVxyXG4gICAgY29uc3Qgc3RvcmFnZSA9IG11bHRlci5tZW1vcnlTdG9yYWdlKCk7XHJcbiAgICBjb25zdCB1cGxvYWQgPSBtdWx0ZXIoe1xyXG4gICAgICBzdG9yYWdlLFxyXG4gICAgICBsaW1pdHM6IHtcclxuICAgICAgICBmaWVsZFNpemU6IHVwbG9hZExpbWl0Qnl0ZXNcclxuICAgICAgfVxyXG4gICAgfSk7XHJcblxyXG4gICAgLy8gRW5hYmxlIGJvZHkgcGFyc2VyXHJcbiAgICBhcHAudXNlKGV4cHJlc3MuanNvbih7IGxpbWl0OiB1cGxvYWRMaW1pdEJ5dGVzIH0pKTtcclxuICAgIGFwcC51c2UoZXhwcmVzcy51cmxlbmNvZGVkKHsgZXh0ZW5kZWQ6IHRydWUsIGxpbWl0OiB1cGxvYWRMaW1pdEJ5dGVzIH0pKTtcclxuXHJcbiAgICAvLyBVc2Ugb25seSBub24tZmlsZSBtdWx0aXBhcnQgZm9ybSBmaWVsZHNcclxuICAgIGFwcC51c2UodXBsb2FkLm5vbmUoKSk7XHJcblxyXG4gICAgLy8gU3RvcCBpZiBub3QgZW5hYmxlZFxyXG4gICAgaWYgKCFzZXJ2ZXJDb25maWcuZW5hYmxlKSB7XHJcbiAgICAgIHJldHVybiBmYWxzZTtcclxuICAgIH1cclxuXHJcbiAgICAvLyBMaXN0ZW4gSFRUUCBzZXJ2ZXJcclxuICAgIGlmICghc2VydmVyQ29uZmlnLnNzbC5mb3JjZSkge1xyXG4gICAgICAvLyBNYWluIHNlcnZlciBpbnN0YW5jZSAoSFRUUClcclxuICAgICAgY29uc3QgaHR0cFNlcnZlciA9IGh0dHAuY3JlYXRlU2VydmVyKGFwcCk7XHJcblxyXG4gICAgICAvLyBBdHRhY2ggZXJyb3IgaGFuZGxlcnMgYW5kIGxpc3RlbiB0byB0aGUgc2VydmVyXHJcbiAgICAgIGF0dGFjaFNlcnZlckVycm9ySGFuZGxlcnMoaHR0cFNlcnZlcik7XHJcblxyXG4gICAgICAvLyBMaXN0ZW5cclxuICAgICAgaHR0cFNlcnZlci5saXN0ZW4oc2VydmVyQ29uZmlnLnBvcnQsIHNlcnZlckNvbmZpZy5ob3N0KTtcclxuXHJcbiAgICAgIC8vIFNhdmUgdGhlIHJlZmVyZW5jZSB0byBIVFRQIHNlcnZlclxyXG4gICAgICBhY3RpdmVTZXJ2ZXJzLnNldChzZXJ2ZXJDb25maWcucG9ydCwgaHR0cFNlcnZlcik7XHJcblxyXG4gICAgICBsb2coXHJcbiAgICAgICAgMyxcclxuICAgICAgICBgW3NlcnZlcl0gU3RhcnRlZCBIVFRQIHNlcnZlciBvbiAke3NlcnZlckNvbmZpZy5ob3N0fToke3NlcnZlckNvbmZpZy5wb3J0fS5gXHJcbiAgICAgICk7XHJcbiAgICB9XHJcblxyXG4gICAgLy8gTGlzdGVuIEhUVFBTIHNlcnZlclxyXG4gICAgaWYgKHNlcnZlckNvbmZpZy5zc2wuZW5hYmxlKSB7XHJcbiAgICAgIC8vIFNldCB1cCBhbiBTU0wgc2VydmVyIGFsc29cclxuICAgICAgbGV0IGtleSwgY2VydDtcclxuXHJcbiAgICAgIHRyeSB7XHJcbiAgICAgICAgLy8gR2V0IHRoZSBTU0wga2V5XHJcbiAgICAgICAga2V5ID0gYXdhaXQgZnNQcm9taXNlcy5yZWFkRmlsZShcclxuICAgICAgICAgIHBvc2l4LmpvaW4oc2VydmVyQ29uZmlnLnNzbC5jZXJ0UGF0aCwgJ3NlcnZlci5rZXknKSxcclxuICAgICAgICAgICd1dGY4J1xyXG4gICAgICAgICk7XHJcblxyXG4gICAgICAgIC8vIEdldCB0aGUgU1NMIGNlcnRpZmljYXRlXHJcbiAgICAgICAgY2VydCA9IGF3YWl0IGZzUHJvbWlzZXMucmVhZEZpbGUoXHJcbiAgICAgICAgICBwb3NpeC5qb2luKHNlcnZlckNvbmZpZy5zc2wuY2VydFBhdGgsICdzZXJ2ZXIuY3J0JyksXHJcbiAgICAgICAgICAndXRmOCdcclxuICAgICAgICApO1xyXG4gICAgICB9IGNhdGNoIChlcnJvcikge1xyXG4gICAgICAgIGxvZyhcclxuICAgICAgICAgIDIsXHJcbiAgICAgICAgICBgW3NlcnZlcl0gVW5hYmxlIHRvIGxvYWQga2V5L2NlcnRpZmljYXRlIGZyb20gdGhlICcke3NlcnZlckNvbmZpZy5zc2wuY2VydFBhdGh9JyBwYXRoLiBDb3VsZCBub3QgcnVuIHNlY3VyZWQgbGF5ZXIgc2VydmVyLmBcclxuICAgICAgICApO1xyXG4gICAgICB9XHJcblxyXG4gICAgICBpZiAoa2V5ICYmIGNlcnQpIHtcclxuICAgICAgICAvLyBNYWluIHNlcnZlciBpbnN0YW5jZSAoSFRUUFMpXHJcbiAgICAgICAgY29uc3QgaHR0cHNTZXJ2ZXIgPSBodHRwcy5jcmVhdGVTZXJ2ZXIoeyBrZXksIGNlcnQgfSwgYXBwKTtcclxuXHJcbiAgICAgICAgLy8gQXR0YWNoIGVycm9yIGhhbmRsZXJzIGFuZCBsaXN0ZW4gdG8gdGhlIHNlcnZlclxyXG4gICAgICAgIGF0dGFjaFNlcnZlckVycm9ySGFuZGxlcnMoaHR0cHNTZXJ2ZXIpO1xyXG5cclxuICAgICAgICAvLyBMaXN0ZW5cclxuICAgICAgICBodHRwc1NlcnZlci5saXN0ZW4oc2VydmVyQ29uZmlnLnNzbC5wb3J0LCBzZXJ2ZXJDb25maWcuaG9zdCk7XHJcblxyXG4gICAgICAgIC8vIFNhdmUgdGhlIHJlZmVyZW5jZSB0byBIVFRQUyBzZXJ2ZXJcclxuICAgICAgICBhY3RpdmVTZXJ2ZXJzLnNldChzZXJ2ZXJDb25maWcuc3NsLnBvcnQsIGh0dHBzU2VydmVyKTtcclxuXHJcbiAgICAgICAgbG9nKFxyXG4gICAgICAgICAgMyxcclxuICAgICAgICAgIGBbc2VydmVyXSBTdGFydGVkIEhUVFBTIHNlcnZlciBvbiAke3NlcnZlckNvbmZpZy5ob3N0fToke3NlcnZlckNvbmZpZy5zc2wucG9ydH0uYFxyXG4gICAgICAgICk7XHJcbiAgICAgIH1cclxuICAgIH1cclxuXHJcbiAgICAvLyBFbmFibGUgdGhlIHJhdGUgbGltaXRlciBpZiBjb25maWcgc2F5cyBzb1xyXG4gICAgaWYgKFxyXG4gICAgICBzZXJ2ZXJDb25maWcucmF0ZUxpbWl0aW5nICYmXHJcbiAgICAgIHNlcnZlckNvbmZpZy5yYXRlTGltaXRpbmcuZW5hYmxlICYmXHJcbiAgICAgICFbMCwgTmFOXS5pbmNsdWRlcyhzZXJ2ZXJDb25maWcucmF0ZUxpbWl0aW5nLm1heFJlcXVlc3RzKVxyXG4gICAgKSB7XHJcbiAgICAgIHJhdGVMaW1pdChhcHAsIHNlcnZlckNvbmZpZy5yYXRlTGltaXRpbmcpO1xyXG4gICAgfVxyXG5cclxuICAgIC8vIFNldCB1cCBzdGF0aWMgZm9sZGVyJ3Mgcm91dGVcclxuICAgIGFwcC51c2UoZXhwcmVzcy5zdGF0aWMocG9zaXguam9pbihfX2Rpcm5hbWUsICdwdWJsaWMnKSkpO1xyXG5cclxuICAgIC8vIFNldCB1cCByb3V0ZXNcclxuICAgIGhlYWx0aFJvdXRlKGFwcCk7XHJcbiAgICBleHBvcnRSb3V0ZXMoYXBwKTtcclxuICAgIHVpUm91dGUoYXBwKTtcclxuICAgIHZTd2l0Y2hSb3V0ZShhcHApO1xyXG5cclxuICAgIC8vIFNldCB1cCBjZW50cmFsaXplZCBlcnJvciBoYW5kbGVyXHJcbiAgICBlcnJvckhhbmRsZXIoYXBwKTtcclxuICB9IGNhdGNoIChlcnJvcikge1xyXG4gICAgdGhyb3cgbmV3IEV4cG9ydEVycm9yKFxyXG4gICAgICAnW3NlcnZlcl0gQ291bGQgbm90IGNvbmZpZ3VyZSBhbmQgc3RhcnQgdGhlIHNlcnZlci4nXHJcbiAgICApLnNldEVycm9yKGVycm9yKTtcclxuICB9XHJcbn07XHJcblxyXG4vKipcclxuICogQ2xvc2VzIGFsbCBzZXJ2ZXJzIGFzc29jaWF0ZWQgd2l0aCBFeHByZXNzIGFwcCBpbnN0YW5jZS5cclxuICovXHJcbmV4cG9ydCBjb25zdCBjbG9zZVNlcnZlcnMgPSAoKSA9PiB7XHJcbiAgbG9nKDQsIGBbc2VydmVyXSBDbG9zaW5nIGFsbCBzZXJ2ZXJzLmApO1xyXG4gIGZvciAoY29uc3QgW3BvcnQsIHNlcnZlcl0gb2YgYWN0aXZlU2VydmVycykge1xyXG4gICAgc2VydmVyLmNsb3NlKCgpID0+IHtcclxuICAgICAgYWN0aXZlU2VydmVycy5kZWxldGUocG9ydCk7XHJcbiAgICAgIGxvZyg0LCBgW3NlcnZlcl0gQ2xvc2VkIHNlcnZlciBvbiBwb3J0OiAke3BvcnR9LmApO1xyXG4gICAgfSk7XHJcbiAgfVxyXG59O1xyXG5cclxuLyoqXHJcbiAqIEdldCBhbGwgc2VydmVycyBhc3NvY2lhdGVkIHdpdGggRXhwcmVzcyBhcHAgaW5zdGFuY2UuXHJcbiAqXHJcbiAqIEByZXR1cm5zIHtBcnJheX0gLSBTZXJ2ZXJzIGFzc29jaWF0ZWQgd2l0aCBFeHByZXNzIGFwcCBpbnN0YW5jZS5cclxuICovXHJcbmV4cG9ydCBjb25zdCBnZXRTZXJ2ZXJzID0gKCkgPT4gYWN0aXZlU2VydmVycztcclxuXHJcbi8qKlxyXG4gKiBFbmFibGUgcmF0ZSBsaW1pdGluZyBmb3IgdGhlIHNlcnZlci5cclxuICpcclxuICogQHBhcmFtIHtPYmplY3R9IGxpbWl0Q29uZmlnIC0gQ29uZmlndXJhdGlvbiBvYmplY3QgZm9yIHJhdGUgbGltaXRpbmcuXHJcbiAqL1xyXG5leHBvcnQgY29uc3QgZW5hYmxlUmF0ZUxpbWl0aW5nID0gKGxpbWl0Q29uZmlnKSA9PiByYXRlTGltaXQoYXBwLCBsaW1pdENvbmZpZyk7XHJcblxyXG4vKipcclxuICogR2V0IHRoZSBFeHByZXNzIGluc3RhbmNlLlxyXG4gKlxyXG4gKiBAcmV0dXJucyB7T2JqZWN0fSAtIFRoZSBFeHByZXNzIGluc3RhbmNlLlxyXG4gKi9cclxuZXhwb3J0IGNvbnN0IGdldEV4cHJlc3MgPSAoKSA9PiBleHByZXNzO1xyXG5cclxuLyoqXHJcbiAqIEdldCB0aGUgRXhwcmVzcyBhcHAgaW5zdGFuY2UuXHJcbiAqXHJcbiAqIEByZXR1cm5zIHtPYmplY3R9IC0gVGhlIEV4cHJlc3MgYXBwIGluc3RhbmNlLlxyXG4gKi9cclxuZXhwb3J0IGNvbnN0IGdldEFwcCA9ICgpID0+IGFwcDtcclxuXHJcbi8qKlxyXG4gKiBBcHBseSBtaWRkbGV3YXJlKHMpIHRvIGEgc3BlY2lmaWMgcGF0aC5cclxuICpcclxuICogQHBhcmFtIHtzdHJpbmd9IHBhdGggLSBUaGUgcGF0aCB0byB3aGljaCB0aGUgbWlkZGxld2FyZShzKSBzaG91bGQgYmUgYXBwbGllZC5cclxuICogQHBhcmFtIHsuLi5GdW5jdGlvbn0gbWlkZGxld2FyZXMgLSBUaGUgbWlkZGxld2FyZSBmdW5jdGlvbnMgdG8gYmUgYXBwbGllZC5cclxuICovXHJcbmV4cG9ydCBjb25zdCB1c2UgPSAocGF0aCwgLi4ubWlkZGxld2FyZXMpID0+IHtcclxuICBhcHAudXNlKHBhdGgsIC4uLm1pZGRsZXdhcmVzKTtcclxufTtcclxuXHJcbi8qKlxyXG4gKiBTZXQgdXAgYSByb3V0ZSB3aXRoIEdFVCBtZXRob2QgYW5kIGFwcGx5IG1pZGRsZXdhcmUocykuXHJcbiAqXHJcbiAqIEBwYXJhbSB7c3RyaW5nfSBwYXRoIC0gVGhlIHJvdXRlIHBhdGguXHJcbiAqIEBwYXJhbSB7Li4uRnVuY3Rpb259IG1pZGRsZXdhcmVzIC0gVGhlIG1pZGRsZXdhcmUgZnVuY3Rpb25zIHRvIGJlIGFwcGxpZWQuXHJcbiAqL1xyXG5leHBvcnQgY29uc3QgZ2V0ID0gKHBhdGgsIC4uLm1pZGRsZXdhcmVzKSA9PiB7XHJcbiAgYXBwLmdldChwYXRoLCAuLi5taWRkbGV3YXJlcyk7XHJcbn07XHJcblxyXG4vKipcclxuICogU2V0IHVwIGEgcm91dGUgd2l0aCBQT1NUIG1ldGhvZCBhbmQgYXBwbHkgbWlkZGxld2FyZShzKS5cclxuICpcclxuICogQHBhcmFtIHtzdHJpbmd9IHBhdGggLSBUaGUgcm91dGUgcGF0aC5cclxuICogQHBhcmFtIHsuLi5GdW5jdGlvbn0gbWlkZGxld2FyZXMgLSBUaGUgbWlkZGxld2FyZSBmdW5jdGlvbnMgdG8gYmUgYXBwbGllZC5cclxuICovXHJcbmV4cG9ydCBjb25zdCBwb3N0ID0gKHBhdGgsIC4uLm1pZGRsZXdhcmVzKSA9PiB7XHJcbiAgYXBwLnBvc3QocGF0aCwgLi4ubWlkZGxld2FyZXMpO1xyXG59O1xyXG5cclxuZXhwb3J0IGRlZmF1bHQge1xyXG4gIHN0YXJ0U2VydmVyLFxyXG4gIGNsb3NlU2VydmVycyxcclxuICBnZXRTZXJ2ZXJzLFxyXG4gIGVuYWJsZVJhdGVMaW1pdGluZyxcclxuICBnZXRFeHByZXNzLFxyXG4gIGdldEFwcCxcclxuICB1c2UsXHJcbiAgZ2V0LFxyXG4gIHBvc3RcclxufTtcclxuIiwiLyoqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKipcclxuXHJcbkhpZ2hjaGFydHMgRXhwb3J0IFNlcnZlclxyXG5cclxuQ29weXJpZ2h0IChjKSAyMDE2LTIwMjQsIEhpZ2hzb2Z0XHJcblxyXG5MaWNlbmNlZCB1bmRlciB0aGUgTUlUIGxpY2VuY2UuXHJcblxyXG5BZGRpdGlvbmFsbHkgYSB2YWxpZCBIaWdoY2hhcnRzIGxpY2Vuc2UgaXMgcmVxdWlyZWQgZm9yIHVzZS5cclxuXHJcblNlZSBMSUNFTlNFIGZpbGUgaW4gcm9vdCBmb3IgZGV0YWlscy5cclxuXHJcbioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKiovXHJcblxyXG5pbXBvcnQgeyBqb2luIH0gZnJvbSAncGF0aCc7XHJcblxyXG5pbXBvcnQgeyBfX2Rpcm5hbWUgfSBmcm9tICcuLi8uLi91dGlscy5qcyc7XHJcblxyXG4vKipcclxuICogQWRkcyB0aGUgR0VUIC8gcm91dGUgZm9yIGEgVUkgd2hlbiBlbmFibGVkIG9uIHRoZSBleHBvcnQgc2VydmVyLlxyXG4gKi9cclxuZXhwb3J0IGRlZmF1bHQgKGFwcCkgPT5cclxuICAhYXBwXHJcbiAgICA/IGZhbHNlXHJcbiAgICA6IGFwcC5nZXQoJy8nLCAoX3JlcXVlc3QsIHJlc3BvbnNlKSA9PiB7XHJcbiAgICAgICAgcmVzcG9uc2Uuc2VuZEZpbGUoam9pbihfX2Rpcm5hbWUsICdwdWJsaWMnLCAnaW5kZXguaHRtbCcpLCB7XHJcbiAgICAgICAgICBhY2NlcHRSYW5nZXM6IGZhbHNlXHJcbiAgICAgICAgfSk7XHJcbiAgICAgIH0pO1xyXG4iLCIvKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKlxyXG5cclxuSGlnaGNoYXJ0cyBFeHBvcnQgU2VydmVyXHJcblxyXG5Db3B5cmlnaHQgKGMpIDIwMTYtMjAyNCwgSGlnaHNvZnRcclxuXHJcbkxpY2VuY2VkIHVuZGVyIHRoZSBNSVQgbGljZW5jZS5cclxuXHJcbkFkZGl0aW9uYWxseSBhIHZhbGlkIEhpZ2hjaGFydHMgbGljZW5zZSBpcyByZXF1aXJlZCBmb3IgdXNlLlxyXG5cclxuU2VlIExJQ0VOU0UgZmlsZSBpbiByb290IGZvciBkZXRhaWxzLlxyXG5cclxuKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKi9cclxuXHJcbmltcG9ydCB7IGNsZWFyQWxsSW50ZXJ2YWxzIH0gZnJvbSAnLi9pbnRlcnZhbHMuanMnO1xyXG5pbXBvcnQgeyBraWxsUG9vbCB9IGZyb20gJy4vcG9vbC5qcyc7XHJcbmltcG9ydCB7IGNsb3NlU2VydmVycyB9IGZyb20gJy4vc2VydmVyL3NlcnZlci5qcyc7XHJcblxyXG4vKipcclxuICogQ2xlYW4gdXAgZnVuY3Rpb24gdG8gdHJpZ2dlciBiZWZvcmUgZW5kaW5nIHByb2Nlc3MgZm9yIHRoZSBncmFjZWZ1bCBzaHV0ZG93bi5cclxuICpcclxuICogQHBhcmFtIHtudW1iZXJ9IGV4aXRDb2RlIC0gQW4gZXhpdCBjb2RlIGZvciB0aGUgcHJvY2Vzcy5leGl0KCkgZnVuY3Rpb24uXHJcbiAqL1xyXG5leHBvcnQgY29uc3Qgc2h1dGRvd25DbGVhblVwID0gYXN5bmMgKGV4aXRDb2RlKSA9PiB7XHJcbiAgLy8gQXdhaXQgZnJlZWluZyBhbGwgcmVzb3VyY2VzXHJcbiAgYXdhaXQgUHJvbWlzZS5hbGxTZXR0bGVkKFtcclxuICAgIC8vIENsZWFyIGFsbCBvbmdvaW5nIGludGVydmFsc1xyXG4gICAgY2xlYXJBbGxJbnRlcnZhbHMoKSxcclxuXHJcbiAgICAvLyBHZXQgYXZhaWxhYmxlIHNlcnZlciBpbnN0YW5jZXMgKEhUVFAvSFRUUFMpIGFuZCBjbG9zZSB0aGVtXHJcbiAgICBjbG9zZVNlcnZlcnMoKSxcclxuXHJcbiAgICAvLyBDbG9zZSBwb29sIGFsb25nIHdpdGggaXRzIHdvcmtlcnMgYW5kIHRoZSBicm93c2VyIGluc3RhbmNlLCBpZiBleGlzdHNcclxuICAgIGtpbGxQb29sKClcclxuICBdKTtcclxuXHJcbiAgLy8gRXhpdCBwcm9jZXNzIHdpdGggYSBjb3JyZWN0IGNvZGVcclxuICBwcm9jZXNzLmV4aXQoZXhpdENvZGUpO1xyXG59O1xyXG5cclxuZXhwb3J0IGRlZmF1bHQge1xyXG4gIHNodXRkb3duQ2xlYW5VcFxyXG59O1xyXG4iLCIvKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKlxyXG5cclxuSGlnaGNoYXJ0cyBFeHBvcnQgU2VydmVyXHJcblxyXG5Db3B5cmlnaHQgKGMpIDIwMTYtMjAyNCwgSGlnaHNvZnRcclxuXHJcbkxpY2VuY2VkIHVuZGVyIHRoZSBNSVQgbGljZW5jZS5cclxuXHJcbkFkZGl0aW9uYWxseSBhIHZhbGlkIEhpZ2hjaGFydHMgbGljZW5zZSBpcyByZXF1aXJlZCBmb3IgdXNlLlxyXG5cclxuU2VlIExJQ0VOU0UgZmlsZSBpbiByb290IGZvciBkZXRhaWxzLlxyXG5cclxuKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKi9cclxuXHJcbmltcG9ydCAnY29sb3JzJztcclxuXHJcbmltcG9ydCB7IGNoZWNrQW5kVXBkYXRlQ2FjaGUgfSBmcm9tICcuL2NhY2hlLmpzJztcclxuaW1wb3J0IHtcclxuICBiYXRjaEV4cG9ydCxcclxuICBzZXRBbGxvd0NvZGVFeGVjdXRpb24sXHJcbiAgc2luZ2xlRXhwb3J0LFxyXG4gIHN0YXJ0RXhwb3J0XHJcbn0gZnJvbSAnLi9jaGFydC5qcyc7XHJcbmltcG9ydCB7IG1hcFRvTmV3Q29uZmlnLCBtYW51YWxDb25maWcsIHNldE9wdGlvbnMgfSBmcm9tICcuL2NvbmZpZy5qcyc7XHJcbmltcG9ydCB7XHJcbiAgaW5pdExvZ2dpbmcsXHJcbiAgbG9nLFxyXG4gIGxvZ1dpdGhTdGFjayxcclxuICBzZXRMb2dMZXZlbCxcclxuICBlbmFibGVGaWxlTG9nZ2luZ1xyXG59IGZyb20gJy4vbG9nZ2VyLmpzJztcclxuaW1wb3J0IHsgaW5pdFBvb2wsIGtpbGxQb29sIH0gZnJvbSAnLi9wb29sLmpzJztcclxuaW1wb3J0IHsgc2h1dGRvd25DbGVhblVwIH0gZnJvbSAnLi9yZXNvdXJjZV9yZWxlYXNlLmpzJztcclxuaW1wb3J0IHNlcnZlciwgeyBzdGFydFNlcnZlciB9IGZyb20gJy4vc2VydmVyL3NlcnZlci5qcyc7XHJcbmltcG9ydCB7IHByaW50TG9nbywgcHJpbnRVc2FnZSB9IGZyb20gJy4vdXRpbHMuanMnO1xyXG5cclxuLyoqXHJcbiAqIEF0dGFjaGVzIGV4aXQgbGlzdGVuZXJzIHRvIHRoZSBwcm9jZXNzLCBlbnN1cmluZyBwcm9wZXIgY2xlYW51cCBvZiByZXNvdXJjZXNcclxuICogYW5kIHRlcm1pbmF0aW9uIG9uIGV4aXQgc2lnbmFscy4gSGFuZGxlcyAnZXhpdCcsICdTSUdJTlQnLCAnU0lHVEVSTScsIGFuZFxyXG4gKiAndW5jYXVnaHRFeGNlcHRpb24nIGV2ZW50cy5cclxuICovXHJcbmNvbnN0IGF0dGFjaFByb2Nlc3NFeGl0TGlzdGVuZXJzID0gKCkgPT4ge1xyXG4gIGxvZygzLCAnW3Byb2Nlc3NdIEF0dGFjaGluZyBleGl0IGxpc3RlbmVycyB0byB0aGUgcHJvY2Vzcy4nKTtcclxuXHJcbiAgLy8gSGFuZGxlciBmb3IgdGhlICdleGl0J1xyXG4gIHByb2Nlc3Mub24oJ2V4aXQnLCAoY29kZSkgPT4ge1xyXG4gICAgbG9nKDQsIGBQcm9jZXNzIGV4aXRlZCB3aXRoIGNvZGUgJHtjb2RlfS5gKTtcclxuICB9KTtcclxuXHJcbiAgLy8gSGFuZGxlciBmb3IgdGhlICdTSUdJTlQnXHJcbiAgcHJvY2Vzcy5vbignU0lHSU5UJywgYXN5bmMgKG5hbWUsIGNvZGUpID0+IHtcclxuICAgIGxvZyg0LCBgVGhlICR7bmFtZX0gZXZlbnQgd2l0aCBjb2RlOiAke2NvZGV9LmApO1xyXG4gICAgYXdhaXQgc2h1dGRvd25DbGVhblVwKDApO1xyXG4gIH0pO1xyXG5cclxuICAvLyBIYW5kbGVyIGZvciB0aGUgJ1NJR1RFUk0nXHJcbiAgcHJvY2Vzcy5vbignU0lHVEVSTScsIGFzeW5jIChuYW1lLCBjb2RlKSA9PiB7XHJcbiAgICBsb2coNCwgYFRoZSAke25hbWV9IGV2ZW50IHdpdGggY29kZTogJHtjb2RlfS5gKTtcclxuICAgIGF3YWl0IHNodXRkb3duQ2xlYW5VcCgwKTtcclxuICB9KTtcclxuXHJcbiAgLy8gSGFuZGxlciBmb3IgdGhlICdTSUdIVVAnXHJcbiAgcHJvY2Vzcy5vbignU0lHSFVQJywgYXN5bmMgKG5hbWUsIGNvZGUpID0+IHtcclxuICAgIGxvZyg0LCBgVGhlICR7bmFtZX0gZXZlbnQgd2l0aCBjb2RlOiAke2NvZGV9LmApO1xyXG4gICAgYXdhaXQgc2h1dGRvd25DbGVhblVwKDApO1xyXG4gIH0pO1xyXG5cclxuICAvLyBIYW5kbGVyIGZvciB0aGUgJ3VuY2F1Z2h0RXhjZXB0aW9uJ1xyXG4gIHByb2Nlc3Mub24oJ3VuY2F1Z2h0RXhjZXB0aW9uJywgYXN5bmMgKGVycm9yLCBuYW1lKSA9PiB7XHJcbiAgICBsb2dXaXRoU3RhY2soMSwgZXJyb3IsIGBUaGUgJHtuYW1lfSBlcnJvci5gKTtcclxuICAgIGF3YWl0IHNodXRkb3duQ2xlYW5VcCgxKTtcclxuICB9KTtcclxufTtcclxuXHJcbi8qKlxyXG4gKiBJbml0aWFsaXplcyB0aGUgZXhwb3J0IHByb2Nlc3MuIFRhc2tzIHN1Y2ggYXMgY29uZmlndXJpbmcgbG9nZ2luZywgY2hlY2tpbmdcclxuICogY2FjaGUgYW5kIHNvdXJjZXMsIGFuZCBpbml0aWFsaXppbmcgdGhlIHBvb2wgb2YgcmVzb3VyY2VzIGhhcHBlbiBkdXJpbmdcclxuICogdGhpcyBzdGFnZS4gRnVuY3Rpb24gdGhhdCBpcyByZXF1aXJlZCB0byBiZSBjYWxsZWQgYmVmb3JlIHRyeWluZyB0byBleHBvcnQgY2hhcnRzIG9yIHNldHRpbmcgYSBzZXJ2ZXIuIFRoZSBgb3B0aW9uc2AgaXMgYW4gb2JqZWN0IHRoYXQgY29udGFpbnMgYWxsIG9wdGlvbnMuXHJcbiAqXHJcbiAqIEBwYXJhbSB7T2JqZWN0fSBvcHRpb25zIC0gQWxsIGV4cG9ydCBvcHRpb25zLlxyXG4gKlxyXG4gKiBAcmV0dXJucyB7UHJvbWlzZTxPYmplY3Q+fSBQcm9taXNlIHJlc29sdmluZyB0byB0aGUgdXBkYXRlZCBleHBvcnQgb3B0aW9ucy5cclxuICovXHJcbmNvbnN0IGluaXRFeHBvcnQgPSBhc3luYyAob3B0aW9ucykgPT4ge1xyXG4gIC8vIFNldCB0aGUgYWxsb3dDb2RlRXhlY3V0aW9uIHBlciBleHBvcnQgbW9kdWxlIHNjb3BlXHJcbiAgc2V0QWxsb3dDb2RlRXhlY3V0aW9uKFxyXG4gICAgb3B0aW9ucy5jdXN0b21Mb2dpYyAmJiBvcHRpb25zLmN1c3RvbUxvZ2ljLmFsbG93Q29kZUV4ZWN1dGlvblxyXG4gICk7XHJcblxyXG4gIC8vIEluaXQgdGhlIGxvZ2dpbmdcclxuICBpbml0TG9nZ2luZyhvcHRpb25zLmxvZ2dpbmcpO1xyXG5cclxuICAvLyBBdHRhY2ggcHJvY2VzcycgZXhpdCBsaXN0ZW5lcnNcclxuICBpZiAob3B0aW9ucy5vdGhlci5saXN0ZW5Ub1Byb2Nlc3NFeGl0cykge1xyXG4gICAgYXR0YWNoUHJvY2Vzc0V4aXRMaXN0ZW5lcnMoKTtcclxuICB9XHJcblxyXG4gIC8vIENoZWNrIGlmIGNhY2hlIG5lZWRzIHRvIGJlIHVwZGF0ZWRcclxuICBhd2FpdCBjaGVja0FuZFVwZGF0ZUNhY2hlKG9wdGlvbnMpO1xyXG5cclxuICAvLyBJbml0IHRoZSBwb29sXHJcbiAgYXdhaXQgaW5pdFBvb2woe1xyXG4gICAgcG9vbDogb3B0aW9ucy5wb29sIHx8IHtcclxuICAgICAgbWluV29ya2VyczogMSxcclxuICAgICAgbWF4V29ya2VyczogMVxyXG4gICAgfSxcclxuICAgIHB1cHBldGVlckFyZ3M6IG9wdGlvbnMucHVwcGV0ZWVyLmFyZ3MgfHwgW11cclxuICB9KTtcclxuXHJcbiAgLy8gUmV0dXJuIHVwZGF0ZWQgb3B0aW9uc1xyXG4gIHJldHVybiBvcHRpb25zO1xyXG59O1xyXG5cclxuZXhwb3J0IGRlZmF1bHQge1xyXG4gIC8vIFNlcnZlclxyXG4gIHNlcnZlcixcclxuICBzdGFydFNlcnZlcixcclxuXHJcbiAgLy8gRXhwb3J0aW5nXHJcbiAgaW5pdEV4cG9ydCxcclxuICBzaW5nbGVFeHBvcnQsXHJcbiAgYmF0Y2hFeHBvcnQsXHJcbiAgc3RhcnRFeHBvcnQsXHJcblxyXG4gIC8vIFBvb2xcclxuICBpbml0UG9vbCxcclxuICBraWxsUG9vbCxcclxuXHJcbiAgLy8gT3RoZXJcclxuICBzZXRPcHRpb25zLFxyXG4gIHNodXRkb3duQ2xlYW5VcCxcclxuXHJcbiAgLy8gTG9nc1xyXG4gIGxvZyxcclxuICBsb2dXaXRoU3RhY2ssXHJcbiAgc2V0TG9nTGV2ZWwsXHJcbiAgZW5hYmxlRmlsZUxvZ2dpbmcsXHJcblxyXG4gIC8vIFV0aWxzXHJcbiAgbWFwVG9OZXdDb25maWcsXHJcbiAgbWFudWFsQ29uZmlnLFxyXG4gIHByaW50TG9nbyxcclxuICBwcmludFVzYWdlXHJcbn07XHJcbiJdLCJuYW1lcyI6WyJzY3JpcHRzTmFtZXMiLCJjb3JlIiwibW9kdWxlcyIsImluZGljYXRvcnMiLCJjdXN0b20iLCJkZWZhdWx0Q29uZmlnIiwicHVwcGV0ZWVyIiwiYXJncyIsInZhbHVlIiwidHlwZSIsImRlc2NyaXB0aW9uIiwidGVtcERpciIsImVudkxpbmsiLCJoaWdoY2hhcnRzIiwidmVyc2lvbiIsImNkblVSTCIsInVzZU5wbSIsImNvcmVTY3JpcHRzIiwibW9kdWxlU2NyaXB0cyIsImluZGljYXRvclNjcmlwdHMiLCJjdXN0b21TY3JpcHRzIiwiZm9yY2VGZXRjaCIsImNhY2hlUGF0aCIsImV4cG9ydCIsImluZmlsZSIsImluc3RyIiwib3B0aW9ucyIsIm91dGZpbGUiLCJjb25zdHIiLCJkZWZhdWx0SGVpZ2h0IiwiZGVmYXVsdFdpZHRoIiwiZGVmYXVsdFNjYWxlIiwiaGVpZ2h0Iiwid2lkdGgiLCJzY2FsZSIsImdsb2JhbE9wdGlvbnMiLCJ0aGVtZU9wdGlvbnMiLCJiYXRjaCIsInJhc3Rlcml6YXRpb25UaW1lb3V0IiwiY3VzdG9tTG9naWMiLCJhbGxvd0NvZGVFeGVjdXRpb24iLCJhbGxvd0ZpbGVSZXNvdXJjZXMiLCJjdXN0b21Db2RlIiwiY2FsbGJhY2siLCJyZXNvdXJjZXMiLCJsb2FkQ29uZmlnIiwibGVnYWN5TmFtZSIsImNyZWF0ZUNvbmZpZyIsInNlcnZlciIsIm1heFVwbG9hZFNpemUiLCJlbmFibGUiLCJjbGlOYW1lIiwiaG9zdCIsInBvcnQiLCJiZW5jaG1hcmtpbmciLCJwcm94eSIsInVzZXJuYW1lIiwicGFzc3dvcmQiLCJ0aW1lb3V0IiwicmF0ZUxpbWl0aW5nIiwibWF4UmVxdWVzdHMiLCJ3aW5kb3ciLCJkZWxheSIsInRydXN0UHJveHkiLCJza2lwS2V5Iiwic2tpcFRva2VuIiwic3NsIiwiZm9yY2UiLCJjZXJ0UGF0aCIsInBvb2wiLCJtaW5Xb3JrZXJzIiwibWF4V29ya2VycyIsIndvcmtMaW1pdCIsImFjcXVpcmVUaW1lb3V0IiwiY3JlYXRlVGltZW91dCIsImRlc3Ryb3lUaW1lb3V0IiwiaWRsZVRpbWVvdXQiLCJjcmVhdGVSZXRyeUludGVydmFsIiwicmVhcGVySW50ZXJ2YWwiLCJsb2dnaW5nIiwibGV2ZWwiLCJmaWxlIiwiZGVzdCIsInRvQ29uc29sZSIsInRvRmlsZSIsInVpIiwicm91dGUiLCJvdGhlciIsIm5vZGVFbnYiLCJsaXN0ZW5Ub1Byb2Nlc3NFeGl0cyIsIm5vTG9nbyIsImhhcmRSZXNldFBhZ2UiLCJicm93c2VyU2hlbGxNb2RlIiwiZGVidWciLCJoZWFkbGVzcyIsImRldnRvb2xzIiwibGlzdGVuVG9Db25zb2xlIiwiZHVtcGlvIiwic2xvd01vIiwiZGVidWdnaW5nUG9ydCIsInByb21wdHNDb25maWciLCJuYW1lIiwibWVzc2FnZSIsImluaXRpYWwiLCJqb2luIiwic2VwYXJhdG9yIiwiaW5zdHJ1Y3Rpb25zIiwiY2hvaWNlcyIsImhpbnQiLCJtaW4iLCJtYXgiLCJyb3VuZCIsImFic29sdXRlUHJvcHMiLCJuZXN0ZWRBcmdzIiwiY3JlYXRlTmVzdGVkQXJncyIsIm9iaiIsInByb3BDaGFpbiIsIk9iamVjdCIsImtleXMiLCJmb3JFYWNoIiwiayIsImluY2x1ZGVzIiwiZW50cnkiLCJzdWJzdHJpbmciLCJ1bmRlZmluZWQiLCJkb3RlbnYiLCJjb25maWciLCJ2IiwiZmlsdGVyQXJyYXkiLCJ6Iiwic3RyaW5nIiwidHJhbnNmb3JtIiwic3BsaXQiLCJtYXAiLCJ0cmltIiwiZmlsdGVyIiwibGVuZ3RoIiwiZW51bSIsInZhbHVlcyIsInJlZmluZSIsInRlc3QiLCJpc05hTiIsInBhcnNlRmxvYXQiLCJlbnZzIiwib2JqZWN0IiwiUFVQUEVURUVSX1RFTVBfRElSIiwiSElHSENIQVJUU19WRVJTSU9OIiwiSElHSENIQVJUU19DRE5fVVJMIiwic3RhcnRzV2l0aCIsIkhJR0hDSEFSVFNfVVNFX05QTSIsIkhJR0hDSEFSVFNfQ09SRV9TQ1JJUFRTIiwiSElHSENIQVJUU19NT0RVTEVfU0NSSVBUUyIsIkhJR0hDSEFSVFNfSU5ESUNBVE9SX1NDUklQVFMiLCJISUdIQ0hBUlRTX0ZPUkNFX0ZFVENIIiwiSElHSENIQVJUU19DQUNIRV9QQVRIIiwiSElHSENIQVJUU19BRE1JTl9UT0tFTiIsIkVYUE9SVF9UWVBFIiwiRVhQT1JUX0NPTlNUUiIsIkVYUE9SVF9ERUZBVUxUX0hFSUdIVCIsIkVYUE9SVF9ERUZBVUxUX1dJRFRIIiwiRVhQT1JUX0RFRkFVTFRfU0NBTEUiLCJFWFBPUlRfUkFTVEVSSVpBVElPTl9USU1FT1VUIiwiQ1VTVE9NX0xPR0lDX0FMTE9XX0NPREVfRVhFQ1VUSU9OIiwiQ1VTVE9NX0xPR0lDX0FMTE9XX0ZJTEVfUkVTT1VSQ0VTIiwiU0VSVkVSX0VOQUJMRSIsIlNFUlZFUl9IT1NUIiwiU0VSVkVSX1BPUlQiLCJTRVJWRVJfTUFYX1VQTE9BRF9TSVpFIiwiU0VSVkVSX0JFTkNITUFSS0lORyIsIlNFUlZFUl9QUk9YWV9IT1NUIiwiU0VSVkVSX1BST1hZX1BPUlQiLCJTRVJWRVJfUFJPWFlfVVNFUk5BTUUiLCJTRVJWRVJfUFJPWFlfUEFTU1dPUkQiLCJTRVJWRVJfUFJPWFlfVElNRU9VVCIsIlNFUlZFUl9SQVRFX0xJTUlUSU5HX0VOQUJMRSIsIlNFUlZFUl9SQVRFX0xJTUlUSU5HX01BWF9SRVFVRVNUUyIsIlNFUlZFUl9SQVRFX0xJTUlUSU5HX1dJTkRPVyIsIlNFUlZFUl9SQVRFX0xJTUlUSU5HX0RFTEFZIiwiU0VSVkVSX1JBVEVfTElNSVRJTkdfVFJVU1RfUFJPWFkiLCJTRVJWRVJfUkFURV9MSU1JVElOR19TS0lQX0tFWSIsIlNFUlZFUl9SQVRFX0xJTUlUSU5HX1NLSVBfVE9LRU4iLCJTRVJWRVJfU1NMX0VOQUJMRSIsIlNFUlZFUl9TU0xfRk9SQ0UiLCJTRVJWRVJfU1NMX1BPUlQiLCJTRVJWRVJfU1NMX0NFUlRfUEFUSCIsIlBPT0xfTUlOX1dPUktFUlMiLCJQT09MX01BWF9XT1JLRVJTIiwiUE9PTF9XT1JLX0xJTUlUIiwiUE9PTF9BQ1FVSVJFX1RJTUVPVVQiLCJQT09MX0NSRUFURV9USU1FT1VUIiwiUE9PTF9ERVNUUk9ZX1RJTUVPVVQiLCJQT09MX0lETEVfVElNRU9VVCIsIlBPT0xfQ1JFQVRFX1JFVFJZX0lOVEVSVkFMIiwiUE9PTF9SRUFQRVJfSU5URVJWQUwiLCJQT09MX0JFTkNITUFSS0lORyIsIkxPR0dJTkdfTEVWRUwiLCJMT0dHSU5HX0ZJTEUiLCJMT0dHSU5HX0RFU1QiLCJMT0dHSU5HX1RPX0NPTlNPTEUiLCJMT0dHSU5HX1RPX0ZJTEUiLCJVSV9FTkFCTEUiLCJVSV9ST1VURSIsIk9USEVSX05PREVfRU5WIiwiT1RIRVJfTElTVEVOX1RPX1BST0NFU1NfRVhJVFMiLCJPVEhFUl9OT19MT0dPIiwiT1RIRVJfSEFSRF9SRVNFVF9QQUdFIiwiT1RIRVJfQlJPV1NFUl9TSEVMTF9NT0RFIiwiT1RIRVJfQUxMT1dfWExJTksiLCJERUJVR19FTkFCTEUiLCJERUJVR19IRUFETEVTUyIsIkRFQlVHX0RFVlRPT0xTIiwiREVCVUdfTElTVEVOX1RPX0NPTlNPTEUiLCJERUJVR19EVU1QSU8iLCJERUJVR19TTE9XX01PIiwiREVCVUdfREVCVUdHSU5HX1BPUlQiLCJwYXJ0aWFsIiwicGFyc2UiLCJwcm9jZXNzIiwiZW52IiwiY29sb3JzIiwicGF0aENyZWF0ZWQiLCJsZXZlbHNEZXNjIiwidGl0bGUiLCJjb2xvciIsImxpc3RlbmVycyIsImxvZ1RvRmlsZSIsInRleHRzIiwicHJlZml4IiwiZXhpc3RzU3luYyIsIm1rZGlyU3luYyIsImFwcGVuZEZpbGUiLCJjb25jYXQiLCJlcnJvciIsImNvbnNvbGUiLCJsb2ciLCJuZXdMZXZlbCIsIkRhdGUiLCJ0b1N0cmluZyIsImZuIiwiYXBwbHkiLCJsb2dXaXRoU3RhY2siLCJjdXN0b21NZXNzYWdlIiwibWFpbk1lc3NhZ2UiLCJzdGFja01lc3NhZ2UiLCJzdGFjayIsInNsaWNlIiwic2V0TG9nTGV2ZWwiLCJlbmFibGVGaWxlTG9nZ2luZyIsImxvZ0Rlc3QiLCJsb2dGaWxlIiwiZW5kc1dpdGgiLCJfX2hpZ2hjaGFydHNEaXIiLCJkaXJuYW1lIiwiY3JlYXRlUmVxdWlyZSIsImRvY3VtZW50IiwicmVxdWlyZSIsInBhdGhUb0ZpbGVVUkwiLCJfX2ZpbGVuYW1lIiwiaHJlZiIsIl9kb2N1bWVudEN1cnJlbnRTY3JpcHQiLCJ0YWdOYW1lIiwidG9VcHBlckNhc2UiLCJzcmMiLCJVUkwiLCJiYXNlVVJJIiwicmVzb2x2ZSIsIl9fZGlybmFtZSIsImZpbGVVUkxUb1BhdGgiLCJmaXhUeXBlIiwiZm9ybWF0cyIsIm91dFR5cGUiLCJwb3AiLCJmaW5kIiwidCIsImhhbmRsZVJlc291cmNlcyIsImFsbG93ZWRQcm9wcyIsImhhbmRsZWRSZXNvdXJjZXMiLCJjb3JyZWN0UmVzb3VyY2VzIiwiaXNDb3JyZWN0SlNPTiIsInJlYWRGaWxlU3luYyIsImZpbGVzIiwicHJvcE5hbWUiLCJpdGVtIiwiZGF0YSIsInBhcnNlZERhdGEiLCJKU09OIiwic3RyaW5naWZ5IiwiZGVlcENvcHkiLCJjb3B5IiwiQXJyYXkiLCJpc0FycmF5Iiwia2V5IiwicHJvdG90eXBlIiwiaGFzT3duUHJvcGVydHkiLCJjYWxsIiwib3B0aW9uc1N0cmluZ2lmeSIsImFsbG93RnVuY3Rpb25zIiwicmVwbGFjZUFsbCIsInByaW50VXNhZ2UiLCJib2xkIiwieWVsbG93IiwiY3ljbGVDYXRlZ29yaWVzIiwib3B0aW9uIiwiZW50cmllcyIsImRlc2NOYW1lIiwiZ3JlZW4iLCJpIiwiYmx1ZSIsImNhdGVnb3J5IiwicmVkIiwidG9Cb29sZWFuIiwid3JhcEFyb3VuZCIsInJlcGxhY2UiLCJtZWFzdXJlVGltZSIsInN0YXJ0IiwiaHJ0aW1lIiwiYmlnaW50IiwiTnVtYmVyIiwiZ2VuZXJhbE9wdGlvbnMiLCJnZXRPcHRpb25zIiwibWVyZ2VDb25maWdPcHRpb25zIiwibmV3T3B0aW9ucyIsIm1lcmdlZE9wdGlvbnMiLCJ1cGRhdGVEZWZhdWx0Q29uZmlnIiwiY29uZmlnT2JqIiwiY3VzdG9tT2JqIiwiY3VzdG9tVmFsdWUiLCJpbml0T3B0aW9ucyIsIml0ZW1zIiwicmVjdXJzaXZlUHJvcHMiLCJvYmplY3RUb1VwZGF0ZSIsIm5lc3RlZE5hbWVzIiwic2hpZnQiLCJhc3NpZ24iLCJhc3luYyIsImZldGNoIiwidXJsIiwicmVxdWVzdE9wdGlvbnMiLCJQcm9taXNlIiwicmVqZWN0IiwicHJvdG9jb2wiLCJodHRwcyIsImh0dHAiLCJnZXRQcm90b2NvbCIsImdldCIsImhlYWRlcnMiLCJSZWZlcmVyIiwicmVzIiwib24iLCJjaHVuayIsInRleHQiLCJFeHBvcnRFcnJvciIsIkVycm9yIiwiY29uc3RydWN0b3IiLCJzdXBlciIsInRoaXMiLCJzZXRFcnJvciIsInN0YXR1c0NvZGUiLCJjYWNoZSIsImFjdGl2ZU1hbmlmZXN0Iiwic291cmNlcyIsImhjVmVyc2lvbiIsImV4dHJhY3RWZXJzaW9uIiwiaW5kZXhPZiIsImV4dHJhY3RNb2R1bGVOYW1lIiwic2NyaXB0UGF0aCIsImZldGNoQW5kUHJvY2Vzc1NjcmlwdCIsInNjcmlwdCIsImZldGNoZWRNb2R1bGVzIiwic2hvdWxkVGhyb3dFcnJvciIsInJlc3BvbnNlIiwicmVzb2x2ZWRTY3JpcHRQYXRoIiwic2VwIiwidXBkYXRlQ2FjaGUiLCJoaWdoY2hhcnRzT3B0aW9ucyIsInByb3h5T3B0aW9ucyIsInNvdXJjZVBhdGgiLCJwcm94eUFnZW50IiwiSHR0cHNQcm94eUFnZW50IiwiYWdlbnQiLCJhbGwiLCJjIiwibSIsImZldGNoU2NyaXB0cyIsIndyaXRlRmlsZVN5bmMiLCJjaGVja0FuZFVwZGF0ZUNhY2hlIiwibWFuaWZlc3RQYXRoIiwicmVxdWVzdFVwZGF0ZSIsIm1hbmlmZXN0IiwibW9kdWxlTWFwIiwibnVtYmVyT2ZNb2R1bGVzIiwic29tZSIsIm1vZHVsZU5hbWUiLCJuZXdNYW5pZmVzdCIsInNhdmVDb25maWdUb01hbmlmZXN0IiwiZ2V0Q2FjaGVQYXRoIiwic2V0dXBIaWdoY2hhcnRzIiwiSGlnaGNoYXJ0cyIsImFuaW1PYmplY3QiLCJkdXJhdGlvbiIsInRyaWdnZXJFeHBvcnQiLCJjaGFydE9wdGlvbnMiLCJkaXNwbGF5RXJyb3JzIiwiX2Rpc3BsYXlFcnJvcnMiLCJtZXJnZSIsInNldE9wdGlvbnMiLCJ3cmFwIiwic2V0T3B0aW9uc09iaiIsImNoYXJ0IiwiYW5pbWF0aW9uIiwic3RySW5qIiwiaXNSZW5kZXJDb21wbGV0ZSIsIkNoYXJ0IiwicHJvY2VlZCIsInVzZXJPcHRpb25zIiwiY2IiLCJleHBvcnRpbmciLCJlbmFibGVkIiwicGxvdE9wdGlvbnMiLCJzZXJpZXMiLCJsYWJlbCIsInRvb2x0aXAiLCJvbkhpZ2hjaGFydHNSZW5kZXIiLCJhZGRFdmVudCIsIlNlcmllcyIsIkZ1bmN0aW9uIiwiZmluYWxPcHRpb25zIiwiZmluYWxDYWxsYmFjayIsImRlZmF1bHRPcHRpb25zIiwicHJvcCIsInRlbXBsYXRlIiwiYnJvd3NlciIsIm5ld1BhZ2UiLCJwYWdlIiwic2V0Q2FjaGVFbmFibGVkIiwic2V0UGFnZUNvbnRlbnQiLCJpc0Nsb3NlZCIsIiRldmFsIiwiZWxlbWVudCIsImVycm9yTWVzc2FnZSIsImlubmVySFRNTCIsInNldFBhZ2VFdmVudHMiLCJjbGVhclBhZ2VSZXNvdXJjZXMiLCJpbmplY3RlZFJlc291cmNlcyIsInJlc291cmNlIiwiZGlzcG9zZSIsImV2YWx1YXRlIiwib2xkQ2hhcnRzIiwiY2hhcnRzIiwib2xkQ2hhcnQiLCJkZXN0cm95Iiwic2NyaXB0c1RvUmVtb3ZlIiwiZ2V0RWxlbWVudHNCeVRhZ05hbWUiLCJzdHlsZXNUb1JlbW92ZSIsImxpbmtzVG9SZW1vdmUiLCJyZW1vdmUiLCJzZXRDb250ZW50Iiwid2FpdFVudGlsIiwiYWRkU2NyaXB0VGFnIiwicGF0aCIsInNldEFzQ29uZmlnIiwidG90YWxTaXplIiwiQnVmZmVyIiwiYnl0ZUxlbmd0aCIsInRvRml4ZWQiLCJwdXBwZXRlZXJFeHBvcnQiLCJleHBvcnRPcHRpb25zIiwiZGVidWdnZXIiLCJpc1NWRyIsInN2Z1RlbXBsYXRlIiwiaW5qZWN0ZWRKcyIsImpzIiwicHVzaCIsImNvbnRlbnQiLCJpc0xvY2FsIiwianNSZXNvdXJjZSIsImluamVjdGVkQ3NzIiwiY3NzIiwiY3NzSW1wb3J0cyIsIm1hdGNoIiwiY3NzSW1wb3J0UGF0aCIsImNzc1Jlc291cmNlIiwiYWRkU3R5bGVUYWciLCJhZGRQYWdlUmVzb3VyY2VzIiwic2l6ZSIsInN2Z0VsZW1lbnQiLCJxdWVyeVNlbGVjdG9yIiwiY2hhcnRIZWlnaHQiLCJiYXNlVmFsIiwiY2hhcnRXaWR0aCIsImJvZHkiLCJzdHlsZSIsInpvb20iLCJtYXJnaW4iLCJ2aWV3cG9ydEhlaWdodCIsIk1hdGgiLCJhYnMiLCJjZWlsIiwidmlld3BvcnRXaWR0aCIsIngiLCJ5IiwiZ2V0Qm91bmRpbmdDbGllbnRSZWN0IiwidHJ1bmMiLCJnZXRDbGlwUmVnaW9uIiwic2V0Vmlld3BvcnQiLCJkZXZpY2VTY2FsZUZhY3RvciIsIm91dGVySFRNTCIsImNyZWF0ZVNWRyIsImVuY29kaW5nIiwiY2xpcCIsInJhY2UiLCJzY3JlZW5zaG90IiwiY2FwdHVyZUJleW9uZFZpZXdwb3J0IiwiZnVsbFBhZ2UiLCJvcHRpbWl6ZUZvclNwZWVkIiwicXVhbGl0eSIsIm9taXRCYWNrZ3JvdW5kIiwiX3Jlc29sdmUiLCJzZXRUaW1lb3V0IiwiY3JlYXRlSW1hZ2UiLCJlbXVsYXRlTWVkaWFUeXBlIiwicGRmIiwiY3JlYXRlUERGIiwic3RhdHMiLCJwZXJmb3JtZWRFeHBvcnRzIiwiZXhwb3J0QXR0ZW1wdHMiLCJleHBvcnRGcm9tU3ZnQXR0ZW1wdHMiLCJ0aW1lU3BlbnQiLCJkcm9wcGVkRXhwb3J0cyIsInNwZW50QXZlcmFnZSIsInBvb2xDb25maWciLCJmYWN0b3J5IiwiY3JlYXRlIiwiaWQiLCJ1dWlkIiwic3RhcnREYXRlIiwiZ2V0VGltZSIsIndvcmtDb3VudCIsInJhbmRvbSIsInZhbGlkYXRlIiwid29ya2VySGFuZGxlIiwiY2xvc2UiLCJpbml0UG9vbCIsInB1cHBldGVlckFyZ3MiLCJwdXBwZXRlZXJPcHRpb25zIiwiZW5hYmxlZERlYnVnIiwiZGVidWdPcHRpb25zIiwibGF1bmNoT3B0aW9ucyIsInVzZXJEYXRhRGlyIiwiaGFuZGxlU0lHSU5UIiwiaGFuZGxlU0lHVEVSTSIsImhhbmRsZVNJR0hVUCIsIndhaXRGb3JJbml0aWFsUGFnZSIsImRlZmF1bHRWaWV3cG9ydCIsIm1heFRyaWVzIiwidHJ5Q291bnQiLCJvcGVuIiwibGF1bmNoIiwiY3JlYXRlQnJvd3NlciIsInBhcnNlSW50IiwiUG9vbCIsImFjcXVpcmVUaW1lb3V0TWlsbGlzIiwiY3JlYXRlVGltZW91dE1pbGxpcyIsImRlc3Ryb3lUaW1lb3V0TWlsbGlzIiwiaWRsZVRpbWVvdXRNaWxsaXMiLCJjcmVhdGVSZXRyeUludGVydmFsTWlsbGlzIiwicmVhcEludGVydmFsTWlsbGlzIiwicHJvcGFnYXRlQ3JlYXRlRXJyb3IiLCJyIiwiaGFyZFJlc2V0IiwiZ290byIsImNsZWFyUGFnZSIsImV2ZW50SWQiLCJpbml0aWFsUmVzb3VyY2VzIiwiYWNxdWlyZSIsInByb21pc2UiLCJyZWxlYXNlIiwia2lsbFBvb2wiLCJ3b3JrZXIiLCJ1c2VkIiwiZGVzdHJveWVkIiwiY29ubmVjdGVkIiwiY2xvc2VCcm93c2VyIiwicG9zdFdvcmsiLCJnZXRQb29sSW5mbyIsImFjcXVpcmVDb3VudGVyIiwicGF5bG9hZCIsInJlcXVlc3RJZCIsIndvcmtTdGFydCIsImV4cG9ydENvdW50ZXIiLCJyZXN1bHQiLCJleHBvcnRUaW1lIiwiZ2V0UG9vbEluZm9KU09OIiwibnVtRnJlZSIsIm51bVVzZWQiLCJhdmFpbGFibGUiLCJwZW5kaW5nIiwibnVtUGVuZGluZ0FjcXVpcmVzIiwicG9vbCQxIiwic3RhcnRFeHBvcnQiLCJzZXR0aW5ncyIsImVuZENhbGxiYWNrIiwic3ZnIiwiaW5pdEV4cG9ydFNldHRpbmdzIiwiZXhwb3J0QXNTdHJpbmciLCJpbnB1dCIsImZvcmJpZGRlbiIsIkpTRE9NIiwiRE9NUHVyaWZ5Iiwic2FuaXRpemUiLCJBRERfVEFHUyIsIkZPUkJJRF9BVFRSIiwiZG9TdHJhaWdodEluamVjdCIsImRvRXhwb3J0IiwiZmluZENoYXJ0U2l6ZSIsInByZWNpc2lvbiIsIm11bHRpcGxpZXIiLCJwb3ciLCJyb3VuZE51bWJlciIsInNvdXJjZUhlaWdodCIsInNvdXJjZVdpZHRoIiwicGFyYW0iLCJjaGFydEpzb24iLCJjdXN0b21Mb2dpY09wdGlvbnMiLCJhbGxvd0NvZGVFeGVjdXRpb25TY29wZWQiLCJvcHRpb25zTmFtZSIsInN0cmluZ1RvRXhwb3J0IiwiY2hhcnRKU09OIiwiaW50ZXJ2YWxJZHMiLCJjbGVhckFsbEludGVydmFscyIsImNsZWFySW50ZXJ2YWwiLCJsb2dFcnJvck1pZGRsZXdhcmUiLCJyZXEiLCJuZXh0IiwicmV0dXJuRXJyb3JNaWRkbGV3YXJlIiwic3RDb2RlIiwic3RhdHVzIiwianNvbiIsInJhdGVMaW1pdCIsImFwcCIsImxpbWl0Q29uZmlnIiwibXNnIiwicmF0ZU9wdGlvbnMiLCJsaW1pdGVyIiwid2luZG93TXMiLCJkZWxheU1zIiwiaGFuZGxlciIsInJlcXVlc3QiLCJmb3JtYXQiLCJzZW5kIiwiZGVmYXVsdCIsInNraXAiLCJxdWVyeSIsImFjY2Vzc190b2tlbiIsInVzZSIsIkh0dHBFcnJvciIsInNldFN0YXR1cyIsInZTd2l0Y2hSb3V0ZSIsInBvc3QiLCJhZG1pblRva2VuIiwidG9rZW4iLCJuZXdWZXJzaW9uIiwicGFyYW1zIiwidXBkYXRlVmVyc2lvbiIsInJldmVyc2VkTWltZSIsInBuZyIsImpwZWciLCJnaWYiLCJyZXF1ZXN0c0NvdW50ZXIiLCJiZWZvcmVSZXF1ZXN0IiwiYWZ0ZXJSZXF1ZXN0IiwiZG9DYWxsYmFja3MiLCJjYWxsYmFja3MiLCJ1bmlxdWVJZCIsImNhbGxSZXNwb25zZSIsImV4cG9ydEhhbmRsZXIiLCJzdG9wQ291bnRlciIsImNvbm5lY3Rpb24iLCJyZW1vdGVBZGRyZXNzIiwiYjY0Iiwibm9Eb3dubG9hZCIsImNvbm5lY3Rpb25BYm9ydGVkIiwic29ja2V0IiwiaGFkRXJyb3JzIiwidG9Mb3dlckNhc2UiLCJzdWJzdHIiLCJwYXR0ZXJuIiwiaXNQcml2YXRlUmFuZ2VVcmxGb3VuZCIsImluZm8iLCJyZW1vdmVBbGxMaXN0ZW5lcnMiLCJmcm9tIiwiaGVhZGVyIiwiYXR0YWNobWVudCIsImZpbGVuYW1lIiwicGtnRmlsZSIsInBhdGhlciIsInNlcnZlclN0YXJ0VGltZSIsInN1Y2Nlc3NSYXRlcyIsImFkZEhlYWx0aFJvdXRlcyIsInNldEludGVydmFsIiwic3VjY2Vzc1JhdGlvIiwiXyIsInBlcmlvZCIsIm1vdmluZ0F2ZXJhZ2UiLCJyZWR1Y2UiLCJhIiwiYiIsImJvb3RUaW1lIiwidXB0aW1lIiwiZmxvb3IiLCJoaWdoY2hhcnRzVmVyc2lvbiIsImF2ZXJhZ2VQcm9jZXNzaW5nVGltZSIsImZhaWxlZEV4cG9ydHMiLCJzdWNlc3NSYXRpbyIsInN2Z0V4cG9ydEF0dGVtcHRzIiwianNvbkV4cG9ydEF0dGVtcHRzIiwiYWN0aXZlU2VydmVycyIsIk1hcCIsImV4cHJlc3MiLCJkaXNhYmxlIiwiY29ycyIsIl9yZXEiLCJzZXQiLCJhdHRhY2hTZXJ2ZXJFcnJvckhhbmRsZXJzIiwic3RhcnRTZXJ2ZXIiLCJzZXJ2ZXJDb25maWciLCJ1cGxvYWRMaW1pdEJ5dGVzIiwic3RvcmFnZSIsIm11bHRlciIsIm1lbW9yeVN0b3JhZ2UiLCJ1cGxvYWQiLCJsaW1pdHMiLCJmaWVsZFNpemUiLCJsaW1pdCIsInVybGVuY29kZWQiLCJleHRlbmRlZCIsIm5vbmUiLCJodHRwU2VydmVyIiwiY3JlYXRlU2VydmVyIiwibGlzdGVuIiwiY2VydCIsImZzUHJvbWlzZXMiLCJyZWFkRmlsZSIsInBvc2l4IiwiaHR0cHNTZXJ2ZXIiLCJOYU4iLCJzdGF0aWMiLCJoZWFsdGhSb3V0ZSIsImV4cG9ydFJvdXRlcyIsIl9yZXF1ZXN0Iiwic2VuZEZpbGUiLCJhY2NlcHRSYW5nZXMiLCJ1aVJvdXRlIiwiZXJyb3JIYW5kbGVyIiwiY2xvc2VTZXJ2ZXJzIiwiZGVsZXRlIiwiZ2V0U2VydmVycyIsImVuYWJsZVJhdGVMaW1pdGluZyIsImdldEV4cHJlc3MiLCJnZXRBcHAiLCJtaWRkbGV3YXJlcyIsInNodXRkb3duQ2xlYW5VcCIsImV4aXRDb2RlIiwiYWxsU2V0dGxlZCIsImV4aXQiLCJpbmRleCIsImluaXRFeHBvcnQiLCJsb2dnaW5nT3B0aW9ucyIsImluaXRMb2dnaW5nIiwiY29kZSIsInNpbmdsZUV4cG9ydCIsImJhdGNoRXhwb3J0IiwiYmF0Y2hGdW5jdGlvbnMiLCJwYWlyIiwiY29uZmlnSW5kZXgiLCJmaW5kSW5kZXgiLCJhcmciLCJmaWxlTmFtZSIsImxvYWRDb25maWdGaWxlIiwic2hvd1VzYWdlIiwicHJvcGVydGllc0NoYWluIiwiYXJndW1lbnRUeXBlIiwicGFpckFyZ3VtZW50VmFsdWUiLCJtYXBUb05ld0NvbmZpZyIsIm9sZE9wdGlvbnMiLCJtYW51YWxDb25maWciLCJjb25maWdGaWxlTmFtZSIsImNvbmZpZ0ZpbGUiLCJjaG9pY2UiLCJwcm9tcHRzIiwib25TdWJtaXQiLCJwIiwiY2F0ZWdvcmllcyIsInF1ZXN0aW9uc0NvdW50ZXIiLCJhbGxRdWVzdGlvbnMiLCJzZWN0aW9uIiwicHJvbXB0IiwiYW5zd2VyIiwibW9kdWxlIiwicHJvbWlzZXMiLCJ3cml0ZUZpbGUiLCJwcmludExvZ28iLCJwYWNrYWdlVmVyc2lvbiJdLCJtYXBwaW5ncyI6Im1lQWVPLE1BQU1BLEVBQWUsQ0FDMUJDLEtBQU0sQ0FBQyxhQUFjLGtCQUFtQixpQkFDeENDLFFBQVMsQ0FDUCxRQUNBLE1BQ0EsUUFDQSxZQUNBLHVCQUNBLGdCQUVBLGVBQ0EsUUFDQSxPQUNBLGFBQ0EsbUJBQ0EsZUFDQSxjQUNBLFVBQ0EsVUFDQSxjQUNBLFdBQ0EsVUFDQSxZQUNBLGNBQ0EsWUFDQSxzQkFDQSxTQUNBLFNBQ0EsV0FDQSxhQUNBLFlBQ0EsZUFFQSxTQUNBLGVBQ0EsWUFDQSxrQkFDQSxTQUNBLGNBQ0EsbUJBQ0EsZUFDQSxrQkFDQSxjQUNBLGVBRUEsY0FDQSxXQUNBLGVBQ0EsV0FDQSxTQUNBLE9BQ0EsV0FDQSxZQUNBLFNBQ0EscUJBQ0EsYUFDQSxXQUNBLFdBQ0EsV0FDQSxXQUNBLGVBQ0EsVUFDQSxrQkFDQSxvQkFDQSxhQUNBLFVBQ0EsY0FDQSxZQUNBLFlBRUZDLFdBQVksQ0FBQyxrQkFDYkMsT0FBUSxDQUNOLHdFQUNBLG1HQU1TQyxFQUFnQixDQUMzQkMsVUFBVyxDQUNUQyxLQUFNLENBQ0pDLE1BQU8sQ0FDTCxtQ0FDQSxrQkFDQSwwQ0FDQSwyQkFDQSxrQ0FD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 +"use strict";require("colors");var e=require("fs"),t=require("path"),r=require("https-proxy-agent"),o=require("prompts"),i=require("dotenv"),n=require("zod"),s=require("module"),a=require("url"),l=require("http"),c=require("https"),p=require("tarn"),u=require("uuid"),h=require("puppeteer"),d=require("jsdom"),g=require("dompurify"),m=require("cors"),f=require("express"),y=require("multer"),v=require("express-rate-limit"),b="undefined"!=typeof document?document.currentScript:null;const w={core:["highcharts","highcharts-more","highcharts-3d"],modules:["stock","map","gantt","exporting","parallel-coordinates","accessibility","boost-canvas","boost","data","data-tools","draggable-points","static-scale","broken-axis","heatmap","tilemap","tiledwebmap","timeline","treemap","treegraph","item-series","drilldown","histogram-bellcurve","bullet","funnel","funnel3d","geoheatmap","pyramid3d","networkgraph","pareto","pattern-fill","pictorial","price-indicator","sankey","arc-diagram","dependency-wheel","series-label","series-on-point","solid-gauge","sonification","streamgraph","sunburst","variable-pie","variwide","vector","venn","windbarb","wordcloud","xrange","no-data-to-display","drag-panes","debugger","dumbbell","lollipop","cylinder","organization","dotplot","marker-clusters","hollowcandlestick","heikinashi","flowmap","export-data","navigator","textpath"],indicators:["indicators-all"],custom:["https://cdnjs.cloudflare.com/ajax/libs/moment.js/2.30.1/moment.min.js","https://cdnjs.cloudflare.com/ajax/libs/moment-timezone/0.5.45/moment-timezone-with-data.min.js"]},E={puppeteer:{args:{value:["--allow-running-insecure-content","--ash-no-nudges","--autoplay-policy=user-gesture-required","--block-new-web-contents","--disable-accelerated-2d-canvas","--disable-background-networking","--disable-background-timer-throttling","--disable-backgrounding-occluded-windows","--disable-breakpad","--disable-checker-imaging","--disable-client-side-phishing-detection","--disable-component-extensions-with-background-pages","--disable-component-update","--disable-default-apps","--disable-dev-shm-usage","--disable-domain-reliability","--disable-extensions","--disable-features=CalculateNativeWinOcclusion,InterestFeedContentSuggestions,WebOTP","--disable-hang-monitor","--disable-ipc-flooding-protection","--disable-logging","--disable-notifications","--disable-offer-store-unmasked-wallet-cards","--disable-popup-blocking","--disable-print-preview","--disable-prompt-on-repost","--disable-renderer-backgrounding","--disable-search-engine-choice-screen","--disable-session-crashed-bubble","--disable-setuid-sandbox","--disable-site-isolation-trials","--disable-speech-api","--disable-sync","--enable-unsafe-webgpu","--hide-crash-restore-bubble","--hide-scrollbars","--metrics-recording-only","--mute-audio","--no-default-browser-check","--no-first-run","--no-pings","--pipe","--no-startup-window","--password-store=basic","--process-per-tab","--use-mock-keychain"],type:"string[]",description:"Arguments array to send to Puppeteer."},launchRetryWindow:{value:3e4,type:"number",envLink:"PUPPETEER_LAUNCH_RETRY_WINDOW",description:"How long, in milliseconds, to keep retrying a browser launch before giving up. Delays between attempts grow and carry jitter. Keep this below the time an orchestrator waits before replacing an instance that has not become healthy, so that a browser which cannot start is reported rather than silently retried."},tempDir:{value:"./tmp/",type:"string",envLink:"PUPPETEER_TEMP_DIR",description:"The directory for Puppeteer to store temporary files."}},highcharts:{version:{value:"latest",type:"string",envLink:"HIGHCHARTS_VERSION",description:"The Highcharts version to be used."},cdnURL:{value:"https://code.highcharts.com/",type:"string",envLink:"HIGHCHARTS_CDN_URL",description:"The CDN URL for Highcharts scripts to be used."},useNpm:{value:!1,type:"boolean",envLink:"HIGHCHARTS_USE_NPM",description:"Flag to use Highcharts scripts from NPM package"},coreScripts:{value:w.core,type:"string[]",envLink:"HIGHCHARTS_CORE_SCRIPTS",description:"The core Highcharts scripts to fetch."},moduleScripts:{value:w.modules,type:"string[]",envLink:"HIGHCHARTS_MODULE_SCRIPTS",description:"The modules of Highcharts to fetch."},indicatorScripts:{value:w.indicators,type:"string[]",envLink:"HIGHCHARTS_INDICATOR_SCRIPTS",description:"The indicators of Highcharts to fetch."},customScripts:{value:w.custom,type:"string[]",description:"Additional custom scripts or dependencies to fetch."},forceFetch:{value:!1,type:"boolean",envLink:"HIGHCHARTS_FORCE_FETCH",description:"The flag to determine whether to refetch all scripts after each server rerun."},cachePath:{value:".cache",type:"string",envLink:"HIGHCHARTS_CACHE_PATH",description:"The path to the cache directory. It is used to store the Highcharts scripts and custom scripts."}},export:{infile:{value:!1,type:"string",description:"The input file should include a name and a type (json or svg). It must be correctly formatted as a JSON or SVG file."},instr:{value:!1,type:"string",description:"Input, provided in the form of a stringified JSON or SVG file, will override the --infile option."},options:{value:!1,type:"string",description:"An alias for the --instr option."},outfile:{value:!1,type:"string",description:"The output filename along with a type (jpeg, png, pdf, or svg). This will ignore the --type flag."},type:{value:"png",type:"string",envLink:"EXPORT_TYPE",description:"The file export format. It can be jpeg, png, pdf, or svg."},constr:{value:"chart",type:"string",envLink:"EXPORT_CONSTR",description:"The constructor to use. Can be chart, stockChart, mapChart, or ganttChart."},defaultHeight:{value:400,type:"number",envLink:"EXPORT_DEFAULT_HEIGHT",description:"the default height of the exported chart. Used when no value is set."},defaultWidth:{value:600,type:"number",envLink:"EXPORT_DEFAULT_WIDTH",description:"The default width of the exported chart. Used when no value is set."},defaultScale:{value:1,type:"number",envLink:"EXPORT_DEFAULT_SCALE",description:"The default scale of the exported chart. Used when no value is set."},height:{value:!1,type:"number",description:"The height of the exported chart, overriding the option in the chart settings."},width:{value:!1,type:"number",description:"The width of the exported chart, overriding the option in the chart settings."},scale:{value:!1,type:"number",description:"The scale of the exported chart, overriding the option in the chart settings. Ranges between 0.1 and 5.0."},globalOptions:{value:!1,type:"string",description:"Either a stringified JSON or a filename containing options to be passed into the Highcharts.setOptions."},themeOptions:{value:!1,type:"string",description:"Either a stringified JSON or a filename containing theme options to be passed into the Highcharts.setOptions."},batch:{value:!1,type:"string",description:'Initiates a batch job with a string containing input/output pairs: "in=out;in=out;...".'},rasterizationTimeout:{value:1500,type:"number",envLink:"EXPORT_RASTERIZATION_TIMEOUT",description:"The duration in milliseconds to wait for rendering a webpage."}},customLogic:{allowCodeExecution:{value:!1,type:"boolean",envLink:"CUSTOM_LOGIC_ALLOW_CODE_EXECUTION",description:"Controls whether the execution of arbitrary code is allowed during the exporting process."},allowFileResources:{value:!1,type:"boolean",envLink:"CUSTOM_LOGIC_ALLOW_FILE_RESOURCES",description:"Controls the ability to inject resources from the filesystem. This setting has no effect when running as a server."},customCode:{value:!1,type:"string",description:"Custom code to execute before chart initialization. It can be a function, code wrapped within a function, or a filename with the .js extension."},callback:{value:!1,type:"string",description:"JavaScript code to run during construction. It can be a function or a filename with the .js extension."},resources:{value:!1,type:"string",description:"Additional resource in the form of a stringified JSON, which may contain files, js, and css sections."},loadConfig:{value:!1,type:"string",legacyName:"fromFile",description:"A file containing a pre-defined configuration to use."},createConfig:{value:!1,type:"string",description:"Enables setting options through a prompt and saving them in a provided config file."}},server:{maxUploadSize:{value:3,type:"number",envLink:"SERVER_MAX_UPLOAD_SIZE",description:"The maximum upload size, in MB, for the server."},enable:{value:!1,type:"boolean",envLink:"SERVER_ENABLE",cliName:"enableServer",description:"When set to true, the server starts on the local IP address 0.0.0.0."},host:{value:"0.0.0.0",type:"string",envLink:"SERVER_HOST",description:"The hostname of the server. Additionally, it starts a server on the provided hostname."},port:{value:7801,type:"number",envLink:"SERVER_PORT",description:"The server port when enabled."},keepAliveTimeout:{value:65e3,type:"number",envLink:"SERVER_KEEP_ALIVE_TIMEOUT",description:"How long, in milliseconds, an idle keep-alive connection is held open. Node defaults to 5 seconds, which is shorter than the idle timeout of a typical proxy or load balancer, and the mismatch causes sporadic gateway errors when the proxy sends a request into a connection this server has just closed. Keep this above the idle timeout of anything in front of the server. headersTimeout is kept 5 seconds above it automatically."},benchmarking:{value:!1,type:"boolean",envLink:"SERVER_BENCHMARKING",cliName:"serverBenchmarking",description:"Indicates whether to display the duration, in milliseconds, of specific actions that occur on the server while serving a request."},proxy:{host:{value:!1,type:"string",envLink:"SERVER_PROXY_HOST",cliName:"proxyHost",description:"The host of the proxy server to use, if it exists."},port:{value:8080,type:"number",envLink:"SERVER_PROXY_PORT",cliName:"proxyPort",description:"The port of the proxy server to use, if it exists."},username:{value:!1,type:"string",envLink:"SERVER_PROXY_USERNAME",cliName:"proxyUsername",description:"The username for the proxy server, if it exists."},password:{value:!1,type:"string",envLink:"SERVER_PROXY_PASSWORD",cliName:"proxyPassword",description:"The password for the proxy server, if it exists."},timeout:{value:5e3,type:"number",envLink:"SERVER_PROXY_TIMEOUT",cliName:"proxyTimeout",description:"The timeout for the proxy server to use, if it exists."}},rateLimiting:{enable:{value:!1,type:"boolean",envLink:"SERVER_RATE_LIMITING_ENABLE",cliName:"enableRateLimiting",description:"Enables rate limiting for the server."},maxRequests:{value:10,type:"number",envLink:"SERVER_RATE_LIMITING_MAX_REQUESTS",legacyName:"rateLimit",description:"The maximum number of requests allowed in one minute."},window:{value:1,type:"number",envLink:"SERVER_RATE_LIMITING_WINDOW",description:"The time window, in minutes, for the rate limiting."},trustProxy:{value:!1,type:"boolean",envLink:"SERVER_RATE_LIMITING_TRUST_PROXY",description:"Set this to true if the server is behind a load balancer."},skipKey:{value:!1,type:"string",envLink:"SERVER_RATE_LIMITING_SKIP_KEY",description:"Allows bypassing the rate limiter and should be provided with the skipToken argument."},skipToken:{value:!1,type:"string",envLink:"SERVER_RATE_LIMITING_SKIP_TOKEN",description:"Allows bypassing the rate limiter and should be provided with the skipKey argument."}},ssl:{enable:{value:!1,type:"boolean",envLink:"SERVER_SSL_ENABLE",cliName:"enableSsl",description:"Enables or disables the SSL protocol."},force:{value:!1,type:"boolean",envLink:"SERVER_SSL_FORCE",cliName:"sslForce",legacyName:"sslOnly",description:"When set to true, the server is forced to serve only over HTTPS."},port:{value:443,type:"number",envLink:"SERVER_SSL_PORT",cliName:"sslPort",description:"The port on which to run the SSL server."},certPath:{value:!1,type:"string",envLink:"SERVER_SSL_CERT_PATH",legacyName:"sslPath",description:"The path to the SSL certificate/key file."}}},pool:{minWorkers:{value:4,type:"number",envLink:"POOL_MIN_WORKERS",description:"The number of minimum and initial pool workers to spawn."},maxWorkers:{value:8,type:"number",envLink:"POOL_MAX_WORKERS",legacyName:"workers",description:"The number of maximum pool workers to spawn."},workLimit:{value:40,type:"number",envLink:"POOL_WORK_LIMIT",description:"The number of work pieces that can be performed before restarting the worker process."},queueLimit:{value:0,type:"number",envLink:"POOL_QUEUE_LIMIT",description:"The maximum number of exports allowed to wait for a worker. Requests arriving beyond this are refused instead of being queued. Set to 0 to derive it as four times maxWorkers. Throughput does not improve past the pool size, so a deeper queue only adds latency and memory use."},queueRejectDelay:{value:500,type:"number",envLink:"POOL_QUEUE_REJECT_DELAY",description:"The duration, in milliseconds, to wait before answering a request refused because the queue is full. This is deliberate backpressure: answering instantly lets clients that retry immediately consume the whole event loop refusing them, starving the exports already in progress. Set to 0 to answer immediately, only advisable when something upstream is limiting the request rate."},acquireTimeout:{value:5e3,type:"number",envLink:"POOL_ACQUIRE_TIMEOUT",description:"The duration, in milliseconds, to wait for acquiring a resource."},createTimeout:{value:5e3,type:"number",envLink:"POOL_CREATE_TIMEOUT",description:"The duration, in milliseconds, to wait for creating a resource."},destroyTimeout:{value:5e3,type:"number",envLink:"POOL_DESTROY_TIMEOUT",description:"The duration, in milliseconds, to wait for destroying a resource."},idleTimeout:{value:3e4,type:"number",envLink:"POOL_IDLE_TIMEOUT",description:"The duration, in milliseconds, after which an idle resource is destroyed."},createRetryInterval:{value:200,type:"number",envLink:"POOL_CREATE_RETRY_INTERVAL",description:"The duration, in milliseconds, to wait before retrying the create process in case of a failure."},reaperInterval:{value:1e3,type:"number",envLink:"POOL_REAPER_INTERVAL",description:"The duration, in milliseconds, after which the check for idle resources to destroy is triggered."},benchmarking:{value:!1,type:"boolean",envLink:"POOL_BENCHMARKING",cliName:"poolBenchmarking",description:"Indicate whether to show statistics for the pool of resources or not."}},logging:{level:{value:4,type:"number",envLink:"LOGGING_LEVEL",cliName:"logLevel",description:"The logging level to be used."},file:{value:"highcharts-export-server.log",type:"string",envLink:"LOGGING_FILE",cliName:"logFile",description:"The name of a log file. The `logToFile` and `logDest` options also need to be set to enable file logging."},dest:{value:"log/",type:"string",envLink:"LOGGING_DEST",cliName:"logDest",description:"The path to store log files. The `logToFile` option also needs to be set to enable file logging."},toConsole:{value:!0,type:"boolean",envLink:"LOGGING_TO_CONSOLE",cliName:"logToConsole",description:"Enables or disables showing logs in the console."},toFile:{value:!0,type:"boolean",envLink:"LOGGING_TO_FILE",cliName:"logToFile",description:"Enables or disables creation of the log directory and saving the log into a .log file."}},ui:{enable:{value:!1,type:"boolean",envLink:"UI_ENABLE",cliName:"enableUi",description:"Enables or disables the user interface (UI) for the export server."},route:{value:"/",type:"string",envLink:"UI_ROUTE",cliName:"uiRoute",description:"The endpoint route to which the user interface (UI) should be attached."}},other:{nodeEnv:{value:"production",type:"string",envLink:"OTHER_NODE_ENV",description:"The type of Node.js environment."},listenToProcessExits:{value:!0,type:"boolean",envLink:"OTHER_LISTEN_TO_PROCESS_EXITS",description:"Decides whether or not to attach process.exit handlers."},noLogo:{value:!1,type:"boolean",envLink:"OTHER_NO_LOGO",description:"Skip printing the logo on a startup. Will be replaced by a simple text."},hardResetPage:{value:!1,type:"boolean",envLink:"OTHER_HARD_RESET_PAGE",description:"Decides if the page content should be reset entirely."},shutdownDrainTimeout:{value:3e4,type:"number",envLink:"OTHER_SHUTDOWN_DRAIN_TIMEOUT",description:"How long, in milliseconds, to let requests already being served finish during a shutdown, before the worker pool is taken away and the process exits. Set it above the deregistration delay of anything routing traffic to this server, so that it has stopped sending before the server stops answering."},browserShellMode:{value:!0,type:"boolean",envLink:"OTHER_BROWSER_SHELL_MODE",description:"Decides if the browser runs in the shell mode."}},debug:{enable:{value:!1,type:"boolean",envLink:"DEBUG_ENABLE",cliName:"enableDebug",description:"Enables or disables debug mode for the underlying browser."},headless:{value:!0,type:"boolean",envLink:"DEBUG_HEADLESS",description:"Controls the mode in which the browser is launched when in the debug mode."},devtools:{value:!1,type:"boolean",envLink:"DEBUG_DEVTOOLS",description:"Decides whether to enable DevTools when the browser is in a headful state."},listenToConsole:{value:!1,type:"boolean",envLink:"DEBUG_LISTEN_TO_CONSOLE",description:"Decides whether to enable a listener for console messages sent from the browser."},dumpio:{value:!1,type:"boolean",envLink:"DEBUG_DUMPIO",description:"Redirects browser process stdout and stderr to process.stdout and process.stderr."},slowMo:{value:0,type:"number",envLink:"DEBUG_SLOW_MO",description:"Slows down Puppeteer operations by the specified number of milliseconds."},debuggingPort:{value:9222,type:"number",envLink:"DEBUG_DEBUGGING_PORT",description:"Specifies the debugging port."}}},T={puppeteer:[{type:"list",name:"args",message:"Puppeteer arguments",initial:E.puppeteer.args.value.join(","),separator:","}],highcharts:[{type:"text",name:"version",message:"Highcharts version",initial:E.highcharts.version.value},{type:"text",name:"cdnURL",message:"The URL of CDN",initial:E.highcharts.cdnURL.value},{type:"toggle",name:"useNpm",message:"Flag to use Highcharts scripts from NPM package",initial:E.highcharts.useNpm.value},{type:"multiselect",name:"coreScripts",message:"Available core scripts",instructions:"Space: Select specific, A: Select all, Enter: Confirm.",choices:E.highcharts.coreScripts.value},{type:"multiselect",name:"moduleScripts",message:"Available module scripts",instructions:"Space: Select specific, A: Select all, Enter: Confirm.",choices:E.highcharts.moduleScripts.value},{type:"multiselect",name:"indicatorScripts",message:"Available indicator scripts",instructions:"Space: Select specific, A: Select all, Enter: Confirm.",choices:E.highcharts.indicatorScripts.value},{type:"list",name:"customScripts",message:"Custom scripts",initial:E.highcharts.customScripts.value.join(","),separator:","},{type:"toggle",name:"forceFetch",message:"Force re-fetch the scripts",initial:E.highcharts.forceFetch.value},{type:"text",name:"cachePath",message:"The path to the cache directory",initial:E.highcharts.cachePath.value}],export:[{type:"select",name:"type",message:"The default export file type",hint:`Default: ${E.export.type.value}`,initial:0,choices:["png","jpeg","pdf","svg"]},{type:"select",name:"constr",message:"The default constructor for Highcharts",hint:`Default: ${E.export.constr.value}`,initial:0,choices:["chart","stockChart","mapChart","ganttChart"]},{type:"number",name:"defaultHeight",message:"The default fallback height of the exported chart",initial:E.export.defaultHeight.value},{type:"number",name:"defaultWidth",message:"The default fallback width of the exported chart",initial:E.export.defaultWidth.value},{type:"number",name:"defaultScale",message:"The default fallback scale of the exported chart",initial:E.export.defaultScale.value,min:.1,max:5},{type:"number",name:"rasterizationTimeout",message:"The rendering webpage timeout in milliseconds",initial:E.export.rasterizationTimeout.value}],customLogic:[{type:"toggle",name:"allowCodeExecution",message:"Enable execution of custom code",initial:E.customLogic.allowCodeExecution.value},{type:"toggle",name:"allowFileResources",message:"Enable file resources",initial:E.customLogic.allowFileResources.value}],server:[{type:"toggle",name:"enable",message:"Starts the server on 0.0.0.0",initial:E.server.enable.value},{type:"text",name:"host",message:"Server hostname",initial:E.server.host.value},{type:"number",name:"port",message:"Server port",initial:E.server.port.value},{type:"toggle",name:"benchmarking",message:"Enable server benchmarking",initial:E.server.benchmarking.value},{type:"text",name:"proxy.host",message:"The host of the proxy server to use",initial:E.server.proxy.host.value},{type:"number",name:"proxy.port",message:"The port of the proxy server to use",initial:E.server.proxy.port.value},{type:"number",name:"proxy.timeout",message:"The timeout for the proxy server to use",initial:E.server.proxy.timeout.value},{type:"toggle",name:"rateLimiting.enable",message:"Enable rate limiting",initial:E.server.rateLimiting.enable.value},{type:"number",name:"rateLimiting.maxRequests",message:"The maximum requests allowed per minute",initial:E.server.rateLimiting.maxRequests.value},{type:"number",name:"rateLimiting.window",message:"The rate-limiting time window in minutes",initial:E.server.rateLimiting.window.value},{type:"toggle",name:"rateLimiting.trustProxy",message:"Set to true if behind a load balancer",initial:E.server.rateLimiting.trustProxy.value},{type:"text",name:"rateLimiting.skipKey",message:"Allows bypassing the rate limiter when provided with the skipToken argument",initial:E.server.rateLimiting.skipKey.value},{type:"text",name:"rateLimiting.skipToken",message:"Allows bypassing the rate limiter when provided with the skipKey argument",initial:E.server.rateLimiting.skipToken.value},{type:"toggle",name:"ssl.enable",message:"Enable SSL protocol",initial:E.server.ssl.enable.value},{type:"toggle",name:"ssl.force",message:"Force serving only over HTTPS",initial:E.server.ssl.force.value},{type:"number",name:"ssl.port",message:"SSL server port",initial:E.server.ssl.port.value},{type:"text",name:"ssl.certPath",message:"The path to find the SSL certificate/key",initial:E.server.ssl.certPath.value}],pool:[{type:"number",name:"minWorkers",message:"The initial number of workers to spawn",initial:E.pool.minWorkers.value},{type:"number",name:"maxWorkers",message:"The maximum number of workers to spawn",initial:E.pool.maxWorkers.value},{type:"number",name:"workLimit",message:"The pieces of work that can be performed before restarting a Puppeteer process",initial:E.pool.workLimit.value},{type:"number",name:"queueLimit",message:"The maximum number of exports allowed to wait for a worker, or 0 to derive it from maxWorkers",initial:E.pool.queueLimit.value},{type:"number",name:"queueRejectDelay",message:"The number of milliseconds to wait before refusing a request because the queue is full",initial:E.pool.queueRejectDelay.value},{type:"number",name:"acquireTimeout",message:"The number of milliseconds to wait for acquiring a resource",initial:E.pool.acquireTimeout.value},{type:"number",name:"createTimeout",message:"The number of milliseconds to wait for creating a resource",initial:E.pool.createTimeout.value},{type:"number",name:"destroyTimeout",message:"The number of milliseconds to wait for destroying a resource",initial:E.pool.destroyTimeout.value},{type:"number",name:"idleTimeout",message:"The number of milliseconds after an idle resource is destroyed",initial:E.pool.idleTimeout.value},{type:"number",name:"createRetryInterval",message:"The retry interval in milliseconds after a create process fails",initial:E.pool.createRetryInterval.value},{type:"number",name:"reaperInterval",message:"The reaper interval in milliseconds after triggering the check for idle resources to destroy",initial:E.pool.reaperInterval.value},{type:"toggle",name:"benchmarking",message:"Enable benchmarking for a resource pool",initial:E.pool.benchmarking.value}],logging:[{type:"number",name:"level",message:"The log level (0: silent, 1: error, 2: warning, 3: notice, 4: verbose, 5: benchmark)",initial:E.logging.level.value,round:0,min:0,max:5},{type:"text",name:"file",message:"A log file name. Set with --toFile and --logDest to enable file logging",initial:E.logging.file.value},{type:"text",name:"dest",message:"The path to a log file when the file logging is enabled",initial:E.logging.dest.value},{type:"toggle",name:"toConsole",message:"Enable logging to the console",initial:E.logging.toConsole.value},{type:"toggle",name:"toFile",message:"Enables logging to a file",initial:E.logging.toFile.value}],ui:[{type:"toggle",name:"enable",message:"Enable UI for the export server",initial:E.ui.enable.value},{type:"text",name:"route",message:"A route to attach the UI",initial:E.ui.route.value}],other:[{type:"text",name:"nodeEnv",message:"The type of Node.js environment",initial:E.other.nodeEnv.value},{type:"toggle",name:"listenToProcessExits",message:"Set to false to skip attaching process.exit handlers",initial:E.other.listenToProcessExits.value},{type:"toggle",name:"noLogo",message:"Skip printing the logo on startup. Replaced by simple text",initial:E.other.noLogo.value},{type:"toggle",name:"hardResetPage",message:"Decides if the page content should be reset entirely",initial:E.other.hardResetPage.value},{type:"toggle",name:"browserShellMode",message:"Decides if the browser runs in the shell mode",initial:E.other.browserShellMode.value}],debug:[{type:"toggle",name:"enable",message:"Enables debug mode for the browser instance",initial:E.debug.enable.value},{type:"toggle",name:"headless",message:"The mode setting for the browser",initial:E.debug.headless.value},{type:"toggle",name:"devtools",message:"The DevTools for the headful browser",initial:E.debug.devtools.value},{type:"toggle",name:"listenToConsole",message:"The event listener for console messages from the browser",initial:E.debug.listenToConsole.value},{type:"toggle",name:"dumpio",message:"Redirects the browser stdout and stderr to NodeJS process",initial:E.debug.dumpio.value},{type:"number",name:"slowMo",message:"Puppeteer operations slow down in milliseconds",initial:E.debug.slowMo.value},{type:"number",name:"debuggingPort",message:"The port number for debugging",initial:E.debug.debuggingPort.value}]},S=["options","globalOptions","themeOptions","resources","payload"],R={},x=(e,t="")=>{Object.keys(e).forEach((r=>{if(!["puppeteer","highcharts"].includes(r)){const o=e[r];void 0===o.value?x(o,`${t}.${r}`):(R[o.cliName||r]=`${t}.${r}`.substring(1),void 0!==o.legacyName&&(R[o.legacyName]=`${t}.${r}`.substring(1)))}}))};x(E),i.config({quiet:!0});const _=e=>n.z.string().transform((t=>t.split(",").map((e=>e.trim())).filter((t=>e.includes(t))))).transform((e=>e.length?e:void 0)),O=()=>n.z.enum(["true","false",""]).transform((e=>""!==e?"true"===e:void 0)),L=e=>n.z.enum([...e,""]).transform((e=>""!==e?e:void 0)),k=()=>n.z.string().trim().refine((e=>!["false","undefined","null","NaN"].includes(e)||""===e),{error:e=>`The string contains forbidden values, received '${e.input}'`}).transform((e=>""!==e?e:void 0)),I=()=>n.z.string().trim().refine((e=>/^(\.\/|\.\.\/|\/|[a-zA-Z]:\\|[a-zA-Z]:\/)?((?:[\w-]+)[\\/]?)+$/.test(e)),{error:"The string is an invalid path directory string."}),C=()=>n.z.string().trim().refine((e=>""===e||!isNaN(parseFloat(e))&&parseFloat(e)>0),{error:e=>`The value must be numeric and positive, received '${e.input}'`}).transform((e=>""!==e?parseFloat(e):void 0)),N=()=>n.z.string().trim().refine((e=>""===e||!isNaN(parseFloat(e))&&parseFloat(e)>=0),{error:e=>`The value must be numeric and non-negative, received '${e.input}'`}).transform((e=>""!==e?parseFloat(e):void 0)),P=n.z.object({PUPPETEER_TEMP_DIR:I(),PUPPETEER_LAUNCH_RETRY_WINDOW:N(),HIGHCHARTS_VERSION:n.z.string().trim().refine((e=>/^(latest|\d+(\.\d+){0,2})$/.test(e)||""===e),{error:e=>`HIGHCHARTS_VERSION must be 'latest', a major version, or in the form XX.YY.ZZ, received '${e.input}'`}).transform((e=>""!==e?e:void 0)),HIGHCHARTS_CDN_URL:n.z.string().trim().refine((e=>e.startsWith("https://")||e.startsWith("http://")||""===e),{error:e=>`Invalid value for HIGHCHARTS_CDN_URL. It should start with http:// or https://, received '${e.input}'`}).transform((e=>""!==e?e:void 0)),HIGHCHARTS_USE_NPM:O(),HIGHCHARTS_CORE_SCRIPTS:_(w.core),HIGHCHARTS_MODULE_SCRIPTS:_(w.modules),HIGHCHARTS_INDICATOR_SCRIPTS:_(w.indicators),HIGHCHARTS_FORCE_FETCH:O(),HIGHCHARTS_CACHE_PATH:k(),HIGHCHARTS_ADMIN_TOKEN:k(),EXPORT_TYPE:L(["jpeg","png","pdf","svg"]),EXPORT_CONSTR:L(["chart","stockChart","mapChart","ganttChart"]),EXPORT_DEFAULT_HEIGHT:C(),EXPORT_DEFAULT_WIDTH:C(),EXPORT_DEFAULT_SCALE:C(),EXPORT_RASTERIZATION_TIMEOUT:N(),CUSTOM_LOGIC_ALLOW_CODE_EXECUTION:O(),CUSTOM_LOGIC_ALLOW_FILE_RESOURCES:O(),SERVER_ENABLE:O(),SERVER_HOST:k(),SERVER_PORT:C(),SERVER_MAX_UPLOAD_SIZE:C(),SERVER_KEEP_ALIVE_TIMEOUT:N(),SERVER_BENCHMARKING:O(),SERVER_PROXY_HOST:k(),SERVER_PROXY_PORT:C(),SERVER_PROXY_USERNAME:k(),SERVER_PROXY_PASSWORD:k(),SERVER_PROXY_TIMEOUT:N(),SERVER_RATE_LIMITING_ENABLE:O(),SERVER_RATE_LIMITING_MAX_REQUESTS:N(),SERVER_RATE_LIMITING_WINDOW:N(),SERVER_RATE_LIMITING_TRUST_PROXY:O(),SERVER_RATE_LIMITING_SKIP_KEY:k(),SERVER_RATE_LIMITING_SKIP_TOKEN:k(),SERVER_SSL_ENABLE:O(),SERVER_SSL_FORCE:O(),SERVER_SSL_PORT:C(),SERVER_SSL_CERT_PATH:k(),POOL_MIN_WORKERS:N(),POOL_MAX_WORKERS:N(),POOL_WORK_LIMIT:C(),POOL_QUEUE_LIMIT:N(),POOL_QUEUE_REJECT_DELAY:N(),POOL_ACQUIRE_TIMEOUT:N(),POOL_CREATE_TIMEOUT:N(),POOL_DESTROY_TIMEOUT:N(),POOL_IDLE_TIMEOUT:N(),POOL_CREATE_RETRY_INTERVAL:N(),POOL_REAPER_INTERVAL:N(),POOL_BENCHMARKING:O(),LOGGING_LEVEL:n.z.string().trim().refine((e=>""===e||!isNaN(parseFloat(e))&&parseFloat(e)>=0&&parseFloat(e)<=5),{error:e=>`Invalid value for LOGGING_LEVEL. We only accept values from 0 to 5 as logging levels, received '${e.input}'`}).transform((e=>""!==e?parseFloat(e):void 0)),LOGGING_FILE:k(),LOGGING_DEST:k(),LOGGING_TO_CONSOLE:O(),LOGGING_TO_FILE:O(),UI_ENABLE:O(),UI_ROUTE:k(),OTHER_SHUTDOWN_DRAIN_TIMEOUT:N(),OTHER_NODE_ENV:L(["development","production","test"]),OTHER_LISTEN_TO_PROCESS_EXITS:O(),OTHER_NO_LOGO:O(),OTHER_HARD_RESET_PAGE:O(),OTHER_BROWSER_SHELL_MODE:O(),OTHER_ALLOW_XLINK:O(),DEBUG_ENABLE:O(),DEBUG_HEADLESS:O(),DEBUG_DEVTOOLS:O(),DEBUG_LISTEN_TO_CONSOLE:O(),DEBUG_DUMPIO:O(),DEBUG_SLOW_MO:N(),DEBUG_DEBUGGING_PORT:C()}).partial().parse(process.env),A=["red","yellow","blue","gray","green"];let H={toConsole:!0,toFile:!1,pathCreated:!1,levelsDesc:[{title:"error",color:A[0]},{title:"warning",color:A[1]},{title:"notice",color:A[2]},{title:"verbose",color:A[3]},{title:"benchmark",color:A[4]}],listeners:[]};const $=(t,r)=>{H.pathCreated||(!e.existsSync(H.dest)&&e.mkdirSync(H.dest),H.pathCreated=!0),e.appendFile(`${H.dest}${H.file}`,[r].concat(t).join(" ")+"\n",(e=>{e&&(console.log(`[logger] Unable to write to log file: ${e}`),H.toFile=!1)}))},U=(...e)=>{const[t,...r]=e,{levelsDesc:o,level:i}=H;if(5!==t&&(0===t||t>i||i>o.length))return;const n=`${(new Date).toString().split("(")[0].trim()} [${o[t-1].title}] -`;H.listeners.forEach((e=>{e(n,r.join(" "))})),H.toConsole&&console.log.apply(void 0,[n.toString()[H.levelsDesc[t-1].color]].concat(r)),H.toFile&&$(r,n)},D=(e,t,r)=>{const o=r||t.message,{level:i,levelsDesc:n}=H;if(0===e||e>i||i>n.length)return;const s=`${(new Date).toString().split("(")[0].trim()} [${n[e-1].title}] -`,a=t.message!==t.stackMessage||void 0===t.stackMessage?t.stack:t.stack.split("\n").slice(1).join("\n"),l=[o,"\n",a];H.toConsole&&console.log.apply(void 0,[s.toString()[H.levelsDesc[e-1].color]].concat([o[A[e-1]],"\n",a])),H.listeners.forEach((e=>{e(s,l.join(" "))})),H.toFile&&$(l,s)},F=e=>{e>=0&&e<=H.levelsDesc.length&&(H.level=e)},j=(e,t)=>{if(H={...H,dest:e||H.dest,file:t||H.file,toFile:!0},0===H.dest.length)return U(1,"[logger] File logging initialization: no path supplied.");H.dest.endsWith("/")||(H.dest+="/")},G=t.dirname(s.createRequire("undefined"==typeof document?require("url").pathToFileURL(__filename).href:b&&"SCRIPT"===b.tagName.toUpperCase()&&b.src||new URL("index.cjs",document.baseURI).href).resolve("highcharts/package.json")),M=a.fileURLToPath(new URL("../.","undefined"==typeof document?require("url").pathToFileURL(__filename).href:b&&"SCRIPT"===b.tagName.toUpperCase()&&b.src||new URL("index.cjs",document.baseURI).href)),q=(e,t)=>{const r=["png","jpeg","pdf","svg"];if(t){const o=t.split(".").pop();"jpg"===o?e="jpeg":r.includes(o)&&e!==o&&(e=o)}return{"image/png":"png","image/jpeg":"jpeg","application/pdf":"pdf","image/svg+xml":"svg"}[e]||r.find((t=>t===e))||"png"},W=(t=!1,r)=>{const o=["js","css","files"];let i=t,n=!1;if(r&&t.endsWith(".json"))try{i=V(e.readFileSync(t,"utf8"))}catch(e){return D(2,e,"[cli] No resources found.")}else i=V(t),i&&!r&&delete i.files;for(const e in i)o.includes(e)?n||(n=!0):delete i[e];return n?(i.files&&(i.files=i.files.map((e=>e.trim())),(!i.files||i.files.length<=0)&&delete i.files),i):U(3,"[cli] No resources found.")};function V(e,t){try{const r=JSON.parse("string"!=typeof e?JSON.stringify(e):e);return"string"!=typeof r&&t?JSON.stringify(r):r}catch{return!1}}const X=e=>{if(null===e||"object"!=typeof e)return e;const t=Array.isArray(e)?[]:{};for(const r in e)Object.prototype.hasOwnProperty.call(e,r)&&(t[r]=X(e[r]));return t},B=(e,t)=>JSON.stringify(e,((e,r)=>("string"==typeof r&&((r=r.trim()).startsWith("function(")||r.startsWith("function ("))&&r.endsWith("}")&&(r=t?`EXP_FUN${(r+"").replaceAll(/\n|\t|\r/g," ")}EXP_FUN`:void 0),"function"==typeof r?`EXP_FUN${(r+"").replaceAll(/\n|\t|\r/g," ")}EXP_FUN`:r))).replaceAll(/"EXP_FUN|EXP_FUN"/g,"");function z(){console.log("\nUsage of CLI arguments:".bold,"\n------",`\nFor more detailed information, visit the readme at: ${"https://github.com/highcharts/node-export-server#readme".bold.yellow}.`);const e=t=>{for(const[r,o]of Object.entries(t))if(Object.prototype.hasOwnProperty.call(o,"value")){let e=` --${o.cliName||r} ${("<"+o.type+">").green} `;if(e.length<48)for(let t=e.length;t<48;t++)e+=".";console.log(e,o.description,`[Default: ${o.value.toString().bold}]`.blue)}else e(o)};Object.keys(E).forEach((t=>{["puppeteer","highcharts"].includes(t)||(console.log(`\n${t.toUpperCase()}`.red),e(E[t]))})),console.log("\n")}const K=e=>!["false","undefined","null","NaN","0",""].includes(e)&&!!e,J=(t,r)=>{if(t&&"string"==typeof t)return(t=t.trim()).endsWith(".js")?!!r&&J(e.readFileSync(t,"utf8")):t.startsWith("function()")||t.startsWith("function ()")||t.startsWith("()=>")||t.startsWith("() =>")?`(${t})()`:t.replace(/;$/,"")},Y=()=>{const e=process.hrtime.bigint();return()=>Number(process.hrtime.bigint()-e)/1e6};let Q={};const Z=()=>Q,ee=(e,t,r=[])=>{const o=X(e);for(const[e,n]of Object.entries(t))o[e]="object"!=typeof(i=n)||Array.isArray(i)||null===i||r.includes(e)||void 0===o[e]?void 0!==n?n:o[e]:ee(o[e],n,r);var i;return o};function te(e,t={},r=""){Object.keys(e).forEach((o=>{const i=e[o],n=t&&t[o];void 0===i.value?te(i,n,`${r}.${o}`):(void 0!==n&&(i.value=n),i.envLink in P&&void 0!==P[i.envLink]&&(i.value=P[i.envLink]))}))}function re(e){let t={};for(const[r,o]of Object.entries(e))t[r]=Object.prototype.hasOwnProperty.call(o,"value")?o.value:re(o);return t}function oe(e,t,r){for(;t.length>1;){const o=t.shift();return Object.prototype.hasOwnProperty.call(e,o)||(e[o]={}),e[o]=oe(Object.assign({},e[o]),t,r),e}return e[t[0]]=r,e}async function ie(e,t={}){return new Promise(((r,o)=>{const i=(e=>e.startsWith("https")?c:l)(e);i.get(e,Object.assign({headers:{"User-Agent":"highcharts/export",Referer:"highcharts.export"}},t||{}),(e=>{let t="";e.on("data",(e=>{t+=e})),e.on("end",(()=>{t||o("Nothing was fetched from the URL."),e.text=t,r(e)}))})).on("error",(e=>{o(e)}))}))}class ne extends Error{constructor(e){super(),this.message=e,this.stackMessage=e}setError(e){return this.error=e,e.name&&(this.name=e.name),e.statusCode&&(this.statusCode=e.statusCode),e.errorCode&&!this.errorCode&&(this.errorCode=e.errorCode),e.stack&&(this.stackMessage=e.message,this.stack=e.stack),this}setCode(e){return this.errorCode=e,this}}const se={cdnURL:"https://code.highcharts.com/",activeManifest:{},sources:"",hcVersion:""},ae=e=>e.sources.substring(0,e.sources.indexOf("*/")).replace("/*","").replace("*/","").replace(/\n/g,"").trim(),le=e=>e.replace(/\\/g,"/").split("/").pop().replace(/\.js$/i,""),ce=async(r,o,i,n=!1,s=!1)=>{let a;if(r.endsWith(".js")||(r=`${r}.js`),n)try{U(4,`[cache] Fetching script from NPM - ${t.join("node_modules","highcharts",r)}`);const o=t.resolve(G,r);if(!o.startsWith(t.resolve(G)+t.sep))throw new ne(`[cache] Invalid script path detected for '${r}'. Directory traversal attempt or bad version input.`,403);return a=e.readFileSync(o,"utf8"),i&&a&&(i[le(r)]=1),a}catch{}else if(U(4,`[cache] Fetching script from CDN - ${r}`),a=await ie(r,o),200===a.statusCode&&"string"==typeof a.text)return i&&(i[le(r)]=1),a.text;if(s)throw new ne(`[cache] Could not fetch the mandatory ${r}. The script might not exist in the requested version.`,404);return U(2,`[cache] Could not fetch the ${r}. The script might not exist in the requested version.`),""},pe=async(o,i,n)=>{try{const s={};return se.sources=await(async(e,o,i)=>{const n=e.version,s="latest"!==n&&n?`${n}/`:"",a=e.cdnURL||se.cdnURL;U(3,`[cache] Updating cache version to Highcharts: ${s||"latest"}.`);const l=e.useNpm;let c;const{host:p,port:u,username:h,password:d}=o;if(p&&u)try{c=new r.HttpsProxyAgent({host:p,port:u,...h&&d?{username:h,password:d}:{}})}catch(e){throw new ne("[cache] Could not create a Proxy Agent.").setError(e)}const g=c?{agent:c,timeout:P.SERVER_PROXY_TIMEOUT}:{};return(await Promise.all([...e.coreScripts.map((e=>ce(l&&e||`${a}${s}${e}`,g,i,l,!0))),...e.moduleScripts.map((e=>ce(l&&t.join("modules",e)||("map"===e?`${a}maps/${s}modules/${e}`:`${a}${s}modules/${e}`),g,i,l))),...e.indicatorScripts.map((e=>ce(l&&t.join("indicators",e)||`${a}stock/${s}indicators/${e}`,g,i,l))),...e.customScripts.map((e=>ce(`${e}`,g)))])).join(";\n")})(o,i,s),se.hcVersion=ae(se),e.writeFileSync(n,se.sources),s}catch(e){throw new ne("[cache] Unable to update the local Highcharts cache.").setError(e)}},ue=async r=>{const{highcharts:o,server:i}=r,n=t.join(M,o.cachePath);let s;const a=t.join(n,"manifest.json"),l=t.join(n,"sources.js");if(!e.existsSync(n)&&e.mkdirSync(n),!e.existsSync(a)||o.forceFetch)U(3,"[cache] Fetching and caching Highcharts dependencies."),s=await pe(o,i.proxy,l);else{let t=!1;const r=JSON.parse(e.readFileSync(a));if(r.modules&&Array.isArray(r.modules)){const e={};r.modules.forEach((t=>e[t]=1)),r.modules=e}const{coreScripts:n,moduleScripts:c,indicatorScripts:p}=o,u=n.length+c.length+p.length;r.version!==o.version?(U(2,"[cache] A Highcharts version mismatch in the cache, need to re-fetch."),t=!0):Object.keys(r.modules||{}).length!==u?(U(2,"[cache] The cache and the requested modules do not match, need to re-fetch."),t=!0):t=(c||[]).some((e=>{if(!r.modules[e])return U(2,`[cache] The ${e} is missing in the cache, need to re-fetch.`),!0})),t?s=await pe(o,i.proxy,l):(U(3,"[cache] Dependency cache is up to date, proceeding."),se.sources=e.readFileSync(l,"utf8"),s=r.modules,se.hcVersion=ae(se))}await(async(r,o)=>{const i={version:r.version,modules:o||{}};se.activeManifest=i,U(3,"[cache] Writing a new manifest.");try{e.writeFileSync(t.join(M,r.cachePath,"manifest.json"),JSON.stringify(i),"utf8")}catch(e){throw new ne("[cache] Error writing the cache manifest.").setError(e)}})(o,s)},he=()=>t.join(M,Z().highcharts.cachePath),de=()=>se.hcVersion;function ge(){Highcharts.animObject=function(){return{duration:0}}}async function me(e,t,r){window._displayErrors=r;const{getOptions:o,merge:i,setOptions:n,wrap:s}=Highcharts;Highcharts.setOptionsObj=i(!1,{},o());const a={animation:!1};t.export.strInj&&(a.height=e.chart.height,a.width=e.chart.width),window.isRenderComplete=!1,s(Highcharts.Chart.prototype,"init",(function(e,t,r){((t=i(t,{exporting:{enabled:!1},plotOptions:{series:{label:{enabled:!1}}},tooltip:{}})).series||[]).forEach((function(e){e.animation=!1})),window.onHighchartsRender||(window.onHighchartsRender=Highcharts.addEvent(this,"render",(()=>{window.isRenderComplete=!0}))),e.apply(this,[t,r])})),s(Highcharts.Series.prototype,"init",(function(e,t,r){e.apply(this,[t,r])}));const l=t.export.strInj?new Function(`return ${t.export.strInj}`)():e;t.customLogic.customCode&&new Function("options",t.customLogic.customCode)(l);const c=i(!1,JSON.parse(t.export.themeOptions),l,{chart:a}),p=t.customLogic.callback?new Function(`return ${t.customLogic.callback}`)():void 0,u=JSON.parse(t.export.globalOptions);u&&n(u);let h=t.export.constr||"chart";h=void 0!==Highcharts[h]?h:"chart",Highcharts[h]("container",c,p);const d=o();for(const e in d)"function"!=typeof d[e]&&delete d[e];n(Highcharts.setOptionsObj),Highcharts.setOptionsObj={}}const fe=e.readFileSync(M+"/templates/template.html","utf8");let ye,ve=0,be=[],we=!1,Ee=null;function Te(){return ve}function Se(){we||(ve++,ye=void 0,U(1,`[browser] The browser disconnected unexpectedly. Invalidating all workers and relaunching on next use (generation ${ve}).`))}function Re(e){if(!e?.pid)return!1;try{return process.kill(e.pid,0),!0}catch(e){return"EPERM"===e.code}}async function xe(e){const t=e.process();try{await e.close()}catch(e){D(2,e,"[browser] Could not cleanly close the browser.")}if(t){if(!await function(e,t){return new Promise((r=>{if(!Re(e))return r(!0);const o=()=>{clearTimeout(i),r(!0)},i=setTimeout((()=>{e.removeListener("exit",o),r(!1)}),t);e.once("exit",o)}))}(t,2e3)){U(2,"[browser] The browser process is still running after being closed, killing it.");try{t.kill("SIGKILL")}catch(e){D(2,e,"[browser] Could not kill the browser process.")}Re(t)&&U(1,`[browser] The browser process ${t.pid} has still not exited after being killed.`)}}else U(2,"[browser] No process handle for the browser, so its exit cannot be confirmed.")}async function _e(e){return void 0!==e&&(be=e),we=!1,ye?.connected?ye:(Ee||(Ee=async function(e){const{puppeteer:t,debug:r,other:o}=Z(),{enable:i,...n}=r,s={headless:!o.browserShellMode||"shell",userDataDir:t.tempDir||"./tmp/",args:e,handleSIGINT:!1,handleSIGTERM:!1,handleSIGHUP:!1,waitForInitialPage:!1,defaultViewport:null,...i&&n},a=function(){const e=parseInt(Z().puppeteer?.launchRetryWindow);return isNaN(e)||e<=0?3e4:e}(),l=Date.now()+a;let c=0,p=250;for(;;){c++;try{U(3,`[browser] Attempting to get a browser instance (attempt ${c}).`),ye=await h.launch(s);break}catch(e){const t=l-Date.now();if(t<=0)throw new ne(`[browser] Could not launch a browser within ${a}ms, after ${c} attempts.`).setError(e);D(2,e,`[browser] Failed to launch a browser instance, retrying (attempt ${c}, ${Math.ceil(t/1e3)}s of the retry window left).`),await new Promise((e=>setTimeout(e,Math.min(p+Math.round(Math.random()*p),t)))),p=Math.min(2*p,4e3)}}"shell"===s.headless&&U(3,"[browser] Launched browser in shell mode.");i&&U(3,"[browser] Launched browser in debug mode.");if(!ye)throw new ne("[browser] Cannot find a browser to open.");return ye.once("disconnected",Se),ye}(be).finally((()=>{Ee=null}))),Ee)}async function Oe(){if(ye?.connected||we||(U(3,"[browser] No browser available, attempting to relaunch it."),await _e()),!ye)return!1;let e;try{return e=await ye.newPage(),await e.setCacheEnabled(!1),await ke(e),function(e){const{debug:t}=Z();t.enable&&t.listenToConsole&&e.on("console",(e=>{console.log(`[debug] ${e.text()}`)}));e.on("pageerror",(async t=>{e.isClosed()||await e.$eval("#container",((e,t)=>{window._displayErrors&&(e.innerHTML=t)}),`

Chart input data error:

${t.toString()}`)}))}(e),e}catch(t){if(e&&!e.isClosed())try{await e.close()}catch(e){D(2,e,"[browser] Could not close a page that failed to be set up.")}throw t}}async function Le(e,t){try{for(const e of t)await e.dispose();await e.evaluate((()=>{if("undefined"!=typeof Highcharts){const e=Highcharts.charts;if(Array.isArray(e)&&e.length)for(const t of e)t&&t.destroy(),Highcharts.charts.shift()}const[...e]=document.getElementsByTagName("script"),[,...t]=document.getElementsByTagName("style"),[...r]=document.getElementsByTagName("link");for(const o of[...e,...t,...r])o.remove()}))}catch(e){D(2,e,"[browser] Could not clear page's resources.")}}async function ke(e){await e.setContent(fe,{waitUntil:"domcontentloaded"}),await e.addScriptTag({path:`${he()}/sources.js`}),await e.evaluate(ge)}const Ie=async(e,t,r,o)=>{r.export.instr=null,r.export.infile=null;const i=Buffer.byteLength(r.export?.strInj?r.export?.strInj:JSON.stringify(t),"utf-8");if(U(4,`[export] The current total size of data passed to a page is around ${(i/1048576).toFixed(2)} MB`),i>=104857600)throw new ne("[export] The data passed to a page exceeded 100MB.");return e.evaluate(me,t,r,o)};var Ce=async(r,o,i)=>{let n=[];try{U(4,"[export] Determining export path.");const s=i.export,a=s?.options?.chart?.displayErrors&&se.activeManifest.modules.debugger;let l;if(o.indexOf&&(o.indexOf("=0||o.indexOf("=0)){if(U(4,"[export] Treating as SVG."),"svg"===s.type)return o;l=!0,await r.setContent((e=>`\n\n\n \n \n Highcharts Export\n \n \n \n
\n ${e}\n
\n \n\n\n`)(o),{waitUntil:"domcontentloaded"})}else U(4,"[export] Treating as config."),s.strInj?await Ie(r,{chart:{height:s.height,width:s.width}},i,a):(o.chart.height=s.height,o.chart.width=s.width,await Ie(r,o,i,a));n=await async function(r,o){const i=[],n=o.customLogic.resources;if(n){const s=[];if(n.js&&s.push({content:n.js}),n.files)for(const t of n.files){const r=!t.startsWith("http");s.push(r?{content:e.readFileSync(t,"utf8")}:{url:t})}for(const e of s)try{i.push(await r.addScriptTag(e))}catch(e){D(2,e,"[export] The JS resource cannot be loaded.")}s.length=0;const a=[];if(n.css){let e=n.css.match(/@import\s*([^;]*);/g);if(e)for(let r of e)r&&(r=r.replace("url(","").replace("@import","").replace(/"/g,"").replace(/'/g,"").replace(/;/,"").replace(/\)/g,"").trim(),r.startsWith("http")?a.push({url:r}):o.customLogic.allowFileResources&&a.push({path:t.join(M,r)}));a.push({content:n.css.replace(/@import\s*([^;]*);/g,"")||" "});for(const e of a)try{i.push(await r.addStyleTag(e))}catch(e){D(2,e,"[export] The CSS resource cannot be loaded.")}a.length=0}}return i}(r,i);const c=l?await r.evaluate((e=>{const t=document.querySelector("#chart-container svg:first-of-type"),r=t.height.baseVal.value*e,o=t.width.baseVal.value*e;return document.body.style.zoom=e,document.body.style.margin="0px",{chartHeight:r,chartWidth:o}}),parseFloat(s.scale)):await r.evaluate((()=>{const{chartHeight:e,chartWidth:t}=window.Highcharts.charts[0];return document.body.style.zoom=1,{chartHeight:e,chartWidth:t}})),p=Math.abs(Math.ceil(c.chartHeight||s.height)),u=Math.abs(Math.ceil(c.chartWidth||s.width)),{x:h,y:d}=await(e=>e.$eval("#chart-container",(e=>{const{x:t,y:r,width:o,height:i}=e.getBoundingClientRect();return{x:t,y:r,width:o,height:Math.trunc(i>1?i:500)}})))(r);let g;if(await r.setViewport({height:p,width:u,deviceScaleFactor:l?1:parseFloat(s.scale)}),"svg"===s.type)g=await(e=>e.$eval("#container svg:first-of-type",(e=>e.outerHTML)))(r);else if(["png","jpeg"].includes(s.type))g=await(async(e,t,r,o,i)=>{let n;try{return await Promise.race([e.screenshot({type:t,encoding:r,clip:o,captureBeyondViewport:!0,fullPage:!1,optimizeForSpeed:!0,..."png"!==t?{quality:80}:{},omitBackground:"png"==t}),new Promise(((e,t)=>{n=setTimeout((()=>t(new ne("Rasterization timeout"))),i||1500)}))])}finally{clearTimeout(n)}})(r,s.type,"base64",{width:u,height:p,x:h,y:d},s.rasterizationTimeout);else{if("pdf"!==s.type)throw new ne(`[export] Unsupported output format ${s.type}.`);g=await(async(e,t,r,o,i)=>(await e.emulateMediaType("screen"),e.pdf({height:t+1,width:r,encoding:o,timeout:i||1500})))(r,p,u,"base64",s.rasterizationTimeout)}return await Le(r,n),g}catch(e){return await Le(r,n),e}};const Ne="EXPORT_INVALID_REQUEST",Pe="EXPORT_QUEUE_FULL",Ae="EXPORT_ACQUIRE_TIMEOUT",He="EXPORT_RASTERIZATION_TIMEOUT",$e="EXPORT_CLIENT_GONE",Ue="EXPORT_FAILED";let De=!1;const Fe={performedExports:0,exportAttempts:0,exportFromSvgAttempts:0,timeSpent:0,droppedExports:0,spentAverage:0,rejectedForCapacity:0,abandonedExports:0};let je={},Ge=0,Me=0;const qe=()=>Me,We=()=>Ge,Ve={create:async()=>{let e=!1;const t=u.v4(),r=(new Date).getTime();try{if(e=await Oe(),!e||e.isClosed())throw new ne("The page is invalid or closed.");Me=0,U(3,`[pool] Successfully created a worker ${t} - took ${(new Date).getTime()-r} ms.`)}catch(e){throw++Me,new ne("Error encountered when creating a new page.").setError(e)}return{id:t,page:e,generation:Te(),workCount:Math.round(Math.random()*(je.workLimit/2))}},validate:async e=>{if(!e.page||e.page?.isClosed())return!1;if(e.mustRecycle)return U(3,"[pool] Worker failed validation: its page was left unusable by a previous export."),!1;if(e.generation!==Te())return U(3,`[pool] Worker failed validation: it belongs to a browser that is gone (worker generation ${e.generation}, current ${Te()}).`),!1;if(e.cleanPromise){const t=await e.cleanPromise;if(e.cleanPromise=null,!t)return U(3,"[pool] Worker failed validation: the page could not be cleared after its previous export."),!1}return!(je.workLimit&&++e.workCount>je.workLimit)||(U(3,`[pool] Worker failed validation: exceeded work limit (limit is ${je.workLimit}).`),!1)},destroy:async e=>{U(3,`[pool] Destroying pool entry ${e.id}.`),e.page&&!e.page.isClosed()&&await e.page.close()}},Xe=async e=>{if(je=e&&e.pool?{...e.pool}:{},Ge=(e=>{const t=parseInt(e.queueLimit),r=parseInt(e.maxWorkers);return!isNaN(t)&&t>0?t:4*(isNaN(r)?8:r)})(je),await _e(e.puppeteerArgs),U(3,`[pool] Initializing pool with workers: min ${je.minWorkers}, max ${je.maxWorkers}.`),De)return U(4,"[pool] Already initialized, please kill it before creating a new one.");parseInt(je.minWorkers)>parseInt(je.maxWorkers)&&(je.minWorkers=je.maxWorkers);try{De=new p.Pool({...Ve,min:parseInt(je.minWorkers),max:parseInt(je.maxWorkers),acquireTimeoutMillis:je.acquireTimeout,createTimeoutMillis:je.createTimeout,destroyTimeoutMillis:je.destroyTimeout,idleTimeoutMillis:je.idleTimeout,createRetryIntervalMillis:je.createRetryInterval,reapIntervalMillis:je.reaperInterval,propagateCreateError:!1}),De.on("release",(e=>{e.cleanPromise=async function(e,t=!1){try{if(e&&!e.isClosed())return t?(await e.goto("about:blank",{waitUntil:"domcontentloaded"}),await ke(e)):await e.evaluate((()=>{document.body.innerHTML='
'})),!0}catch(e){D(2,e,"[browser] Could not clear the content of the page.")}return!1}(e.page,!1).catch((()=>!1)),U(4,`[pool] Releasing a worker with ID ${e.id}.`)})),De.on("destroySuccess",((e,t)=>{U(4,`[pool] Destroyed a worker with ID ${t.id}.`),t.page=null}));const e=[];for(let t=0;t{De.release(e)})),U(3,"[pool] The pool is ready"+(e.length?` with ${e.length} initial resources waiting.`:"."))}catch(e){throw new ne("[pool] Could not create the pool of workers.").setError(e)}};async function Be(){if(U(3,"[pool] Killing pool with all workers and closing browser."),De){for(const e of De.used)De.release(e.resource);De.destroyed||(await De.destroy(),U(4,"[browser] Destroyed the pool of resources."))}await async function(){we=!0,ye&&await xe(ye),ye=void 0,U(4,"[browser] Closed the browser.")}()}const ze=async(e,t)=>{let r;try{if(U(4,"[pool] Work received, starting to process."),++Fe.exportAttempts,je.benchmarking&&Ye(),!De)throw new ne("Work received, but pool has not been started.");if(De.numPendingAcquires()>=Ge)throw++Fe.rejectedForCapacity,new ne((t.payload?.requestId?`For request with ID ${t.payload?.requestId} - `:"")+`The server is at capacity: ${De.numPendingAcquires()} exports are already waiting for a worker (limit is ${Ge}). Please retry shortly.`).setCode(Pe);const o=t.payload?.abortSignal;if(o?.aborted)throw++Fe.abandonedExports,new ne((t.payload?.requestId?`For request with ID ${t.payload?.requestId} - `:"")+"The client disconnected before a worker was available.").setCode($e);const i=Y();try{U(4,"[pool] Acquiring a worker handle."),r=await De.acquire().promise,t.server.benchmarking&&U(5,t.payload?.requestId?`[benchmark] Request with ID ${t.payload?.requestId} -`:"[benchmark]",`Acquired a worker handle: ${i()}ms.`)}catch(e){throw new ne((t.payload?.requestId?`For request with ID ${t.payload?.requestId} - `:"")+`Error encountered when acquiring an available entry: ${i()}ms.`).setCode(Ae).setError(e)}if(U(4,"[pool] Acquired a worker handle."),o?.aborted)throw++Fe.abandonedExports,De.release(r),r=null,new ne((t.payload?.requestId?`For request with ID ${t.payload?.requestId} - `:"")+"The client disconnected while waiting for a worker.").setCode($e);if(!r.page)throw new ne("Resolved worker page is invalid: the pool setup is wonky.");let n=(new Date).getTime();U(4,`[pool] Starting work on pool entry with ID ${r.id}.`);const s=Y(),a=await Ce(r.page,e,t);if(a instanceof Error)throw"Rasterization timeout"===a.message&&(r.workCount=je.workLimit+1,r.mustRecycle=!0),"TimeoutError"===a.name||"Rasterization timeout"===a.message?new ne("Rasterization timeout: your chart may be too complex or large, and failed to render within the allotted time.").setCode(He).setError(a):new ne((t.payload?.requestId?`For request with ID ${t.payload?.requestId} - `:"")+`Error encountered during export: ${s()}ms.`).setCode(Ue).setError(a);t.server.benchmarking&&U(5,t.payload?.requestId?`[benchmark] Request with ID ${t.payload?.requestId} -`:"[benchmark]",`Exported a chart sucessfully: ${s()}ms.`),De.release(r);const l=(new Date).getTime()-n;return Fe.timeSpent+=l,Fe.spentAverage=Fe.timeSpent/++Fe.performedExports,U(4,`[pool] Work completed in ${l} ms.`),{result:a,options:t}}catch(e){throw e.errorCode!==$e&&++Fe.droppedExports,r&&De.release(r),new ne(`[pool] In pool.postWork: ${e.message}`).setError(e)}},Ke=()=>De,Je=()=>({min:De.min,max:De.max,all:De.numFree()+De.numUsed(),available:De.numFree(),used:De.numUsed(),pending:De.numPendingAcquires(),queueLimit:Ge});function Ye(){const{min:e,max:t,all:r,available:o,used:i,pending:n}=Je();U(5,`[pool] The minimum number of resources allowed by pool: ${e}.`),U(5,`[pool] The maximum number of resources allowed by pool: ${t}.`),U(5,`[pool] The number of all created resources: ${r}.`),U(5,`[pool] The number of available resources: ${o}.`),U(5,`[pool] The number of acquired resources: ${i}.`),U(5,`[pool] The number of resources waiting to be acquired: ${n}.`)}var Qe=Je,Ze=()=>Fe;let et;function tt(e){const t=[];return P.OTHER_ALLOW_XLINK||t.push("xlink:href"),(et||(et=g(new d.JSDOM("").window)),et).sanitize(e,{ADD_TAGS:["foreignObject"],FORBID_ATTR:t,HTML_INTEGRATION_POINTS:{foreignobject:!0}})}let rt=!1;const ot=async(t,r)=>{U(4,"[chart] Starting the exporting process.");const o=((e,t={})=>{let r={};return e.svg?(r=X(t),r.export.type=e.type||e.export.type,r.export.scale=e.scale||e.export.scale,r.export.outfile=e.outfile||e.export.outfile,r.payload={svg:e.svg}):r=ee(t,e,S),r.export.outfile=r.export?.outfile||`chart.${r.export?.type||"png"}`,r})(t,Z()),i=o.export;if(o.payload?.svg&&""!==o.payload.svg)try{U(4,"[chart] Attempting to export from a SVG input.");const e=at(tt(o.payload.svg),o,r);return++Fe.exportFromSvgAttempts,e}catch(e){return r(new ne("[chart] Error loading SVG input.").setError(e))}if(i.infile&&i.infile.length)try{return U(4,"[chart] Attempting to export from an input file."),o.export.instr=e.readFileSync(i.infile,"utf8"),at(o.export.instr.trim(),o,r)}catch(e){return r(new ne("[chart] Error loading input file.").setError(e))}if(i.instr&&""!==i.instr||i.options&&""!==i.options)try{return U(4,"[chart] Attempting to export from a raw input."),i.instr=i.instr||i.options,K(o.customLogic?.allowCodeExecution)?st(o,r):"string"==typeof i.instr?at(i.instr.trim(),o,r):nt(o,i.instr||i.options,r)}catch(e){return r(new ne("[chart] Error loading raw input.").setError(e))}return r(new ne("[chart] No valid input specified. Check if at least one of the following parameters is correctly set: 'infile', 'instr', 'options', or 'svg'."))},it=e=>{const{chart:t,exporting:r}=e.export?.options||V(e.export?.instr),o=V(e.export?.globalOptions);let i=e.export?.scale||r?.scale||o?.exporting?.scale||e.export?.defaultScale||1;i=Math.max(.1,Math.min(i,5)),i=((e,t=1)=>{const r=Math.pow(10,t||0);return Math.round(+e*r)/r})(i,2);const n={height:e.export?.height||r?.sourceHeight||t?.height||o?.exporting?.sourceHeight||o?.chart?.height||e.export?.defaultHeight||400,width:e.export?.width||r?.sourceWidth||t?.width||o?.exporting?.sourceWidth||o?.chart?.width||e.export?.defaultWidth||600,scale:i};for(let[e,t]of Object.entries(n))n[e]="string"==typeof t?+t.replace(/px|%/gi,""):t;return n},nt=async(t,r,o,i)=>{let{export:n,customLogic:s}=t;const a="boolean"==typeof s.allowCodeExecution?s.allowCodeExecution:rt;if(s){if(a)if("string"==typeof t.customLogic.resources)t.customLogic.resources=W(t.customLogic.resources,K(t.customLogic.allowFileResources));else if(!t.customLogic.resources)try{const r=e.readFileSync("resources.json","utf8");t.customLogic.resources=W(r,K(t.customLogic.allowFileResources))}catch(e){U(2,"[chart] Unable to load the default resources.json file.")}}else s=t.customLogic={};if(!a&&s){if(s.callback||s.resources||s.customCode)return o(new ne("[chart] The 'callback', 'resources' and 'customCode' options have been disabled for this server."));s.callback=!1,s.resources=!1,s.customCode=!1}if(r&&(r.chart=r.chart||{},r.exporting=r.exporting||{},r.exporting.enabled=!1),n.constr=n.constr||"chart",n.type=q(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]=V(e.readFileSync(n[t],"utf8"),!0):n[t]=V(n[t],!0))}catch(e){n[t]={},D(2,e,`[chart] The '${t}' cannot be loaded.`)}})),s.allowCodeExecution)try{s.customCode=J(s.customCode,s.allowFileResources)}catch(e){D(2,e,"[chart] The 'customCode' cannot be loaded.")}if(s&&s.callback&&s.callback?.indexOf("{")<0)if(s.allowFileResources)try{s.callback=e.readFileSync(s.callback,"utf8")}catch(e){s.callback=!1,D(2,e,"[chart] The 'callback' cannot be loaded.")}else s.callback=!1;t.export={...t.export,...it(t)};try{return o(!1,await ze(n.strInj||r||i,t))}catch(e){return o(e)}},st=(e,t)=>{try{let r,o=e.export.instr||e.export.options;return"string"!=typeof o&&(r=o=B(o,e.customLogic?.allowCodeExecution)),r=o.replaceAll(/\t|\n|\r/g,"").trim(),";"===r[r.length-1]&&(r=r.substring(0,r.length-1)),e.export.strInj=r,nt(e,!1,t)}catch(r){return t(new ne(`[chart] Malformed input detected for ${e.export?.requestId||"?"}. Please make sure that your JSON/JavaScript options are sent using the "options" attribute, and that if you're using SVG, it is unescaped.`).setError(r))}},at=(e,t,r)=>{const{allowCodeExecution:o}=t.customLogic;if(e.indexOf("=0||e.indexOf("=0)return U(4,"[chart] Parsing input as SVG."),nt(t,!1,r,e);try{const o=JSON.parse(e.replaceAll(/\t|\n|\r/g," "));return nt(t,o,r)}catch(e){return K(o)?st(t,r):r(new ne("[chart] Only JSON configurations and SVG are allowed for this server. If this is your server, JavaScript custom code can be enabled by starting the server with the --allowCodeExecution flag.").setError(e))}},lt=[],ct=(e,t,r,o)=>{D(1,e),"development"!==P.OTHER_NODE_ENV&&delete e.stack,o(e)},pt=(e,t,r,o)=>{if(r.headersSent)return r.end();const{statusCode:i,status:n,message:s,stack:a,errorCode:l}=e;let c=i||n||400;(c>=500||c<100)&&(D(1,e,`[server] An error carried the out-of-contract status ${c}, answering with 400 instead. This server must never return a 5xx.`),c=400),r.status(c).json({statusCode:c,message:s,stack:a,...l?{errorCode:l}:{}})};var ut=(e,t)=>{const r="Too many requests, you have been rate limited. Please try again later.",o={max:t.maxRequests||30,window:t.window||1,trustProxy:t.trustProxy||!1,skipKey:t.skipKey||!1,skipToken:t.skipToken||!1};o.trustProxy&&e.enable("trust proxy");const i=v({windowMs:60*o.window*1e3,limit:o.max,handler:(e,t)=>{t.format({json:()=>{t.status(429).send({message:r})},default:()=>{t.status(429).send(r)}})},skip:e=>!1!==o.skipKey&&!1!==o.skipToken&&e.query.key===o.skipKey&&e.query.access_token===o.skipToken&&(U(4,"[rate limiting] Skipping rate limiter."),!0)});e.use(i),U(3,`[rate limiting] Enabled rate limiting with ${o.max} requests per ${o.window} minute for each IP, trusting proxy: ${o.trustProxy}.`)};class ht extends ne{constructor(e,t,r=!1){super(e),this.status=this.statusCode=t,r&&(this.errorCode=r)}setStatus(e){return this.status=e,this}}var dt=e=>!!e&&e.post("/version/change/:newVersion",(async(e,t,r)=>{try{const r=P.HIGHCHARTS_ADMIN_TOKEN;if(!r||!r.length)throw new ht("The server is not configured to perform run-time version changes: HIGHCHARTS_ADMIN_TOKEN is not set.",401);const o=e.get("hc-auth");if(!o||o!==r)throw new ht("Invalid or missing token: Set the token in the hc-auth header.",401);const i=e.params.newVersion;if(!i||!/^[a-zA-Z0-9.-]+$/.test(i))throw new ht("No new version supplied.",400);try{await(async e=>{const t=Z();t?.highcharts&&(t.highcharts.version=e),await ue(t)})(i)}catch(e){throw new ht(`Version change: ${e.message}`,e.statusCode).setError(e)}t.status(200).send({statusCode:200,version:de(),message:`Successfully updated Highcharts to version: ${i}.`})}catch(e){r(e)}}));const gt={png:"image/png",jpeg:"image/jpeg",gif:"image/gif",pdf:"application/pdf",svg:"image/svg+xml"};let mt=0;const ft=[],yt=[],vt=(e,t,r,o)=>{let i=!0;const{id:n,uniqueId:s,type:a,body:l}=o;return e.some((e=>{if(e){let o=e(t,r,n,s,a,l);return void 0!==o&&!0!==o&&(i=o),!0}})),i},bt=async(e,t,r)=>{try{const r=Y(),i=u.v4().replace(/-/g,""),n=Z(),s=e.body,a=++mt;let l=q(s.type);if(!s||"object"==typeof(o=s)&&!Array.isArray(o)&&null!==o&&0===Object.keys(o).length)throw new ht("The request body is required. Please ensure that your Content-Type header is correct (accepted types are application/json and multipart/form-data).",400,Ne);let c=V(s.infile||s.options||s.data);if(!c&&!s.svg)throw U(2,`The request with ID ${i} from ${e.headers["x-forwarded-for"]||e.connection.remoteAddress} was incorrect:\n Content-Type: ${e.headers["content-type"]}. \n Chart constructor: ${s.constr}.\n Dimensions: ${s.width}x${s.height} @ ${s.scale} scale.\n Type: ${l}.\n Is SVG set? ${void 0!==s.svg}.\n B64? ${void 0!==s.b64}.\n No download? ${void 0!==s.noDownload}.\n\n Payload received: ${JSON.stringify(s.infile||s.options||s.data||s.svg)}\n\n `),new ht("No correct chart data found. Ensure that you are using either application/json or multipart/form-data headers. If sending JSON, make sure the chart data is in the 'infile', 'options', or 'data' attribute. If sending SVG, ensure it is in the 'svg' attribute.",400,Ne);let p=!1;if(p=vt(ft,e,t,{id:a,uniqueId:i,type:l,body:s}),!0!==p)return t.send(p);const h=new AbortController;t.once("close",(()=>{t.writableFinished||(h.abort(),U(4,`[export] The client closed the connection for request ${i} before it was answered.`))})),U(4,`[export] Got an incoming HTTP request with ID ${i}.`),s.constr="string"==typeof s.constr&&s.constr||"chart";const d={export:{instr:c,type:l,constr:s.constr[0].toLowerCase()+s.constr.substr(1),height:s.height,width:s.width,scale:s.scale||n.export.scale,globalOptions:V(s.globalOptions,!0),themeOptions:V(s.themeOptions,!0)},customLogic:{allowCodeExecution:rt,allowFileResources:!1,resources:V(s.resources,!0),callback:s.callback,customCode:s.customCode}};c&&(d.export.instr=B(c,d.customLogic.allowCodeExecution));const g=ee(n,d);if(g.export.options=c,g.payload={svg:s.svg||!1,b64:s.b64||!1,noDownload:s.noDownload||!1,requestId:i,abortSignal:h.signal},s.svg&&(e=>[/xlink:href="(?:http:\/\/|https:\/\/)?localhost\b/,/xlink:href="(?:http:\/\/|https:\/\/)?10\.\d{1,3}\.\d{1,3}\.\d{1,3}\b/,/xlink:href="(?:http:\/\/|https:\/\/)?127\.\d{1,3}\.\d{1,3}\.\d{1,3}\b/,/xlink:href="(?:http:\/\/|https:\/\/)?172\.(1[6-9]|2[0-9]|3[0-1])\.\d{1,3}\.\d{1,3}\b/,/xlink:href="(?:http:\/\/|https:\/\/)?192\.168\.\d{1,3}\.\d{1,3}\b/].some((t=>t.test(e))))(g.payload.svg))throw new ht("SVG potentially contain at least one forbidden URL in xlink:href element. Please review the SVG content and ensure that all referenced URLs comply with security policies.",400,Ne);await ot(g,((o,c)=>{if(h.signal.aborted)return U(4,`[export] Discarding the result for request ${i}: the client is gone.`);if(n.server.benchmarking&&U(5,`[benchmark] Request with ID ${i} - After the whole exporting process: ${r()}ms.`),o)throw o;if(!c||!c.result)throw new ht(`Unexpected return from chart generation. Please check your request data. For the request with ID ${i}, the result is ${c.result}.`,400,Ue);return l=c.options.export.type,vt(yt,e,t,{id:a,body:c.result}),c.result?s.b64?"pdf"===l||"svg"==l?t.send(Buffer.from(c.result,"utf8").toString("base64")):t.send(c.result):(t.header("Content-Type",gt[l]||"image/png"),s.noDownload||t.attachment(`${e.params.filename||e.body.filename||"chart"}.${l||"png"}`),"svg"===l?t.send(c.result):t.send(Buffer.from(c.result,"base64"))):void 0}))}catch(e){r(e)}var o};const wt=JSON.parse(e.readFileSync(t.join(M,"package.json"))),Et=new Date,Tt=[];function St(e){const t=e.exportAttempts-e.abandonedExports;return t<=0?null:e.performedExports/t*100}function Rt(e){if(!e)return!1;var t;t=setInterval((()=>{const e=St(Ze());Tt.push(null===e?1:e),Tt.length>30&&Tt.shift()}),6e4),lt.push(t),e.get("/health",((e,t)=>{const r=Ze(),o=Tt.length,i=Tt.reduce(((e,t)=>e+t),0)/Tt.length;U(4,"[health.js] GET /health [200] - returning server health."),t.send({status:"OK",bootTime:Et,uptime:Math.floor(((new Date).getTime()-Et.getTime())/1e3/60)+" minutes",version:wt.version,highchartsVersion:de(),averageProcessingTime:r.spentAverage,performedExports:r.performedExports,failedExports:r.droppedExports,abandonedExports:r.abandonedExports,rejectedForCapacity:r.rejectedForCapacity,consecutiveCreateFailures:qe(),browserConnected:!!ye?.connected,exportAttempts:r.exportAttempts,sucessRatio:St(r),pool:Qe(),period:o,movingAverage:i,message:isNaN(i)||!Tt.length?"Too early to report. No exports made yet. Please check back soon.":`Last ${o} minutes had a success rate of ${i.toFixed(2)}%.`,svgExportAttempts:r.exportFromSvgAttempts,jsonExportAttempts:r.performedExports-r.exportFromSvgAttempts})}))}const xt=new Map,_t=f();_t.disable("x-powered-by"),_t.use(m()),_t.use(((e,t,r)=>{t.set("Accept-Ranges","none"),r()}));const Ot=e=>{e.on("clientError",((e,t)=>{D(1,e,`[server] Client error: ${e.message}, destroying socket.`),t.destroy()})),e.on("error",(e=>{D(1,e,`[server] Server error: ${e.message}`)})),e.on("connection",(e=>{e.on("error",(e=>{D(1,e,`[server] Socket error: ${e.message}`)}))}))},Lt=(e,t)=>{const r=parseInt(t.keepAliveTimeout),o=isNaN(r)||r<0?65e3:r;e.keepAliveTimeout=o,e.headersTimeout=o+5e3,U(4,`[server] Set keepAliveTimeout to ${e.keepAliveTimeout}ms and headersTimeout to ${e.headersTimeout}ms.`)},kt=async r=>{try{const o=1024*(r.maxUploadSize||3)*1024,i=y.memoryStorage(),n=y({storage:i,limits:{fieldSize:o}});if(_t.use(((e,t,r)=>{if("POST"!==e.method||e.path.startsWith("/version/"))return r();const o=Ke();if(o&&o.numPendingAcquires()>=We()){++Fe.rejectedForCapacity;const e=new ht(`The server is at capacity: ${o.numPendingAcquires()} exports are already waiting for a worker (limit is ${We()}). Please retry shortly.`,400,Pe),i=(()=>{const e=parseInt(je.queueRejectDelay);return isNaN(e)||e<0?0:e})();if(!i)return r(e);const n=()=>clearTimeout(s),s=setTimeout((()=>{t.removeListener("close",n),r(e)}),i);t.once("close",n)}else r()})),_t.use(f.json({limit:o})),_t.use(f.urlencoded({extended:!0,limit:o})),_t.use(n.none()),!r.enable)return!1;if(!r.ssl.force){const e=l.createServer(_t);Ot(e),Lt(e,r),e.listen(r.port,r.host),xt.set(r.port,e),U(3,`[server] Started HTTP server on ${r.host}:${r.port}.`)}if(r.ssl.enable){let o,i;try{o=await e.promises.readFile(t.posix.join(r.ssl.certPath,"server.key"),"utf8"),i=await e.promises.readFile(t.posix.join(r.ssl.certPath,"server.crt"),"utf8")}catch(e){U(2,`[server] Unable to load key/certificate from the '${r.ssl.certPath}' path. Could not run secured layer server.`)}if(o&&i){const e=c.createServer({key:o,cert:i},_t);Ot(e),Lt(e,r),e.listen(r.ssl.port,r.host),xt.set(r.ssl.port,e),U(3,`[server] Started HTTPS server on ${r.host}:${r.ssl.port}.`)}}r.rateLimiting&&r.rateLimiting.enable&&![0,NaN].includes(r.rateLimiting.maxRequests)&&ut(_t,r.rateLimiting),_t.use(f.static(t.posix.join(M,"public"))),Rt(_t),(e=>{e.post("/",bt),e.post("/:filename",bt)})(_t),(e=>{!!e&&e.get("/",((e,r)=>{r.sendFile(t.join(M,"public","index.html"),{acceptRanges:!1})}))})(_t),dt(_t),(e=>{e.use(ct),e.use(pt)})(_t)}catch(e){throw new ne("[server] Could not configure and start the server.").setError(e)}},It=()=>(U(4,"[server] Closing all servers."),Promise.all([...xt].map((([e,t])=>new Promise((r=>{t.close((()=>{xt.delete(e),U(4,`[server] Closed server on port: ${e}.`),r()})),t.closeIdleConnections?.()}))))));var Ct={startServer:kt,closeServers:It,getServers:()=>xt,enableRateLimiting:e=>ut(_t,e),getExpress:()=>f,getApp:()=>_t,use:(e,...t)=>{_t.use(e,...t)},get:(e,...t)=>{_t.get(e,...t)},post:(e,...t)=>{_t.post(e,...t)}};const Nt=async e=>{(()=>{U(4,"[server] Clearing all registered intervals.");for(const e of lt)clearInterval(e)})();const t=(()=>{const e=parseInt(Z()?.other?.shutdownDrainTimeout);return isNaN(e)||e<0?3e4:e})();await Promise.race([It(),new Promise((e=>setTimeout(e,t)))]),await Be(),process.exit(e)};var Pt={server:Ct,startServer:kt,initExport:async e=>{var t;return t=e.customLogic&&e.customLogic.allowCodeExecution,rt=K(t),(e=>{for(const[t,r]of Object.entries(e))H[t]=r;F(e&&parseInt(e.level)),e&&e.dest&&e.toFile&&j(e.dest,e.file||"highcharts-export-server.log")})(e.logging),e.other.listenToProcessExits&&(U(3,"[process] Attaching exit listeners to the process."),process.on("exit",(e=>{U(4,`Process exited with code ${e}.`)})),process.on("SIGINT",(async(e,t)=>{U(4,`The ${e} event with code: ${t}.`),await Nt(0)})),process.on("SIGTERM",(async(e,t)=>{U(4,`The ${e} event with code: ${t}.`),await Nt(0)})),process.on("SIGHUP",(async(e,t)=>{U(4,`The ${e} event with code: ${t}.`),await Nt(0)})),process.on("uncaughtException",(async(e,t)=>{D(1,e,`The ${t} error.`),await Nt(1)}))),await ue(e),await Xe({pool:e.pool||{minWorkers:1,maxWorkers:1},puppeteerArgs:e.puppeteer.args||[]}),e},singleExport:async t=>{t.export.instr=t.export.instr||t.export.options,await ot(t,(async(t,r)=>{if(t)throw t;const{outfile:o,type:i}=r.options.export;e.writeFileSync(o||`chart.${i}`,"svg"!==i?Buffer.from(r.result,"base64"):r.result),await Be()}))},batchExport:async t=>{const r=[];for(let o of t.export.batch.split(";"))o=o.split("="),2===o.length&&r.push(ot({...t,export:{...t.export,infile:o[0],outfile:o[1]}},((t,r)=>{if(t)throw t;e.writeFileSync(r.options.export.outfile,"svg"!==r.options.export.type?Buffer.from(r.result,"base64"):r.result)})));try{await Promise.all(r),await Be()}catch(e){throw new ne("[chart] Error encountered during batch export.").setError(e)}},startExport:ot,initPool:Xe,killPool:Be,setOptions:(t,r)=>(r?.length&&(Q=function(t){const r=t.findIndex((e=>"loadConfig"===e.replace(/-/g,"")));if(r>-1&&t[r+1]){const o=t[r+1];try{if(o&&o.endsWith(".json"))return JSON.parse(e.readFileSync(o))}catch(e){D(2,e,`[config] Unable to load the configuration from the ${o} file.`)}}return{}}(r)),te(E,Q),Q=re(E),t&&(Q=ee(Q,t,S)),r?.length&&(Q=function(e,t,r){let o=!1;for(let i=0;i(s.length-1===r&&(a=e[t].type),e[t])),r),s.reduce(((e,r,l)=>(s.length-1===l&&void 0!==e[r]&&(t[++i]?"boolean"===a?e[r]=K(t[i]):"number"===a?e[r]=+t[i]:a.indexOf("]")>=0?e[r]=t[i].split(","):e[r]=t[i]:(U(2,`[config] Missing value for the '${n}' argument. Using the default value.`),o=!0)),e[r])),e)}o&&z();return e}(Q,r,E)),Q),shutdownCleanUp:Nt,log:U,logWithStack:D,setLogLevel:F,enableFileLogging:j,mapToNewConfig:e=>{const t={};for(const[r,o]of Object.entries(e)){const e=R[r]?R[r].split("."):[];e.reduce(((t,r,i)=>t[r]=e.length-1===i?o:t[r]||{}),t)}return t},manualConfig:async t=>{let r={};e.existsSync(t)&&(r=JSON.parse(e.readFileSync(t,"utf8")));const i=Object.keys(T).map((e=>({title:`${e} options`,value:e})));return o({type:"multiselect",name:"category",message:"Which category do you want to configure?",hint:"Space: Select specific, A: Select all, Enter: Confirm.",instructions:"",choices:i},{onSubmit:async(i,n)=>{let s=0,a=[];for(const e of n)T[e]=T[e].map((t=>({...t,section:e}))),a=[...a,...T[e]];return await o(a,{onSubmit:async(o,i)=>{if("moduleScripts"===o.name?(i=i.length?i.map((e=>o.choices[e])):o.choices,r[o.section][o.name]=i):r[o.section]=oe(Object.assign({},r[o.section]||{}),o.name.split("."),o.choices?o.choices[i]:i),++s===a.length){try{await e.promises.writeFile(t,JSON.stringify(r,null,2),"utf8")}catch(e){D(1,e,`[config] An error occurred while creating the ${t} file.`)}return!0}}}),!0}})},printLogo:r=>{const o=JSON.parse(e.readFileSync(t.join(M,"package.json"))).version;r?console.log(`Starting Highcharts Export Server v${o}...`):console.log(e.readFileSync(M+"/msg/startup.msg").toString().bold.yellow,`v${o}\n`.bold)},printUsage:z};module.exports=Pt; +//# sourceMappingURL=data:application/json;charset=utf-8;base64,eyJ2ZXJzaW9uIjozLCJmaWxlIjoiaW5kZXguY2pzIiwic291cmNlcyI6WyIuLi9saWIvc2NoZW1hcy9jb25maWcuanMiLCIuLi9saWIvZW52cy5qcyIsIi4uL2xpYi9sb2dnZXIuanMiLCIuLi9saWIvdXRpbHMuanMiLCIuLi9saWIvY29uZmlnLmpzIiwiLi4vbGliL2ZldGNoLmpzIiwiLi4vbGliL2Vycm9ycy9FeHBvcnRFcnJvci5qcyIsIi4uL2xpYi9jYWNoZS5qcyIsIi4uL2xpYi9oaWdoY2hhcnRzLmpzIiwiLi4vbGliL2Jyb3dzZXIuanMiLCIuLi9saWIvZXhwb3J0LmpzIiwiLi4vdGVtcGxhdGVzL3N2Z19leHBvcnQvc3ZnX2V4cG9ydC5qcyIsIi4uL2xpYi9lcnJvcnMvY29kZXMuanMiLCIuLi9saWIvcG9vbC5qcyIsIi4uL2xpYi9zYW5pdGl6ZS5qcyIsIi4uL2xpYi9jaGFydC5qcyIsIi4uL2xpYi9pbnRlcnZhbHMuanMiLCIuLi9saWIvc2VydmVyL2Vycm9yLmpzIiwiLi4vbGliL3NlcnZlci9yYXRlX2xpbWl0LmpzIiwiLi4vbGliL2Vycm9ycy9IdHRwRXJyb3IuanMiLCIuLi9saWIvc2VydmVyL3JvdXRlcy9jaGFuZ2VfaGNfdmVyc2lvbi5qcyIsIi4uL2xpYi9zZXJ2ZXIvcm91dGVzL2V4cG9ydC5qcyIsIi4uL2xpYi9zZXJ2ZXIvcm91dGVzL2hlYWx0aC5qcyIsIi4uL2xpYi9zZXJ2ZXIvc2VydmVyLmpzIiwiLi4vbGliL3NlcnZlci9yb3V0ZXMvdWkuanMiLCIuLi9saWIvcmVzb3VyY2VfcmVsZWFzZS5qcyIsIi4uL2xpYi9pbmRleC5qcyJdLCJzb3VyY2VzQ29udGVudCI6WyIvKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKlxuXG5IaWdoY2hhcnRzIEV4cG9ydCBTZXJ2ZXJcblxuQ29weXJpZ2h0IChjKSAyMDE2LTIwMjQsIEhpZ2hzb2Z0XG5cbkxpY2VuY2VkIHVuZGVyIHRoZSBNSVQgbGljZW5jZS5cblxuQWRkaXRpb25hbGx5IGEgdmFsaWQgSGlnaGNoYXJ0cyBsaWNlbnNlIGlzIHJlcXVpcmVkIGZvciB1c2UuXG5cblNlZSBMSUNFTlNFIGZpbGUgaW4gcm9vdCBmb3IgZGV0YWlscy5cblxuKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKi9cblxuLy8gUG9zc2libGUgbmFtZXMgZm9yIEhpZ2hjaGFydHMgc2NyaXB0c1xuZXhwb3J0IGNvbnN0IHNjcmlwdHNOYW1lcyA9IHtcbiAgY29yZTogWydoaWdoY2hhcnRzJywgJ2hpZ2hjaGFydHMtbW9yZScsICdoaWdoY2hhcnRzLTNkJ10sXG4gIG1vZHVsZXM6IFtcbiAgICAnc3RvY2snLFxuICAgICdtYXAnLFxuICAgICdnYW50dCcsXG4gICAgJ2V4cG9ydGluZycsXG4gICAgJ3BhcmFsbGVsLWNvb3JkaW5hdGVzJyxcbiAgICAnYWNjZXNzaWJpbGl0eScsXG4gICAgLy8gJ2Fubm90YXRpb25zLWFkdmFuY2VkJyxcbiAgICAnYm9vc3QtY2FudmFzJyxcbiAgICAnYm9vc3QnLFxuICAgICdkYXRhJyxcbiAgICAnZGF0YS10b29scycsXG4gICAgJ2RyYWdnYWJsZS1wb2ludHMnLFxuICAgICdzdGF0aWMtc2NhbGUnLFxuICAgICdicm9rZW4tYXhpcycsXG4gICAgJ2hlYXRtYXAnLFxuICAgICd0aWxlbWFwJyxcbiAgICAndGlsZWR3ZWJtYXAnLFxuICAgICd0aW1lbGluZScsXG4gICAgJ3RyZWVtYXAnLFxuICAgICd0cmVlZ3JhcGgnLFxuICAgICdpdGVtLXNlcmllcycsXG4gICAgJ2RyaWxsZG93bicsXG4gICAgJ2hpc3RvZ3JhbS1iZWxsY3VydmUnLFxuICAgICdidWxsZXQnLFxuICAgICdmdW5uZWwnLFxuICAgICdmdW5uZWwzZCcsXG4gICAgJ2dlb2hlYXRtYXAnLFxuICAgICdweXJhbWlkM2QnLFxuICAgICduZXR3b3JrZ3JhcGgnLFxuICAgIC8vICdvdmVybGFwcGluZy1kYXRhbGFiZWxzJyxcbiAgICAncGFyZXRvJyxcbiAgICAncGF0dGVybi1maWxsJyxcbiAgICAncGljdG9yaWFsJyxcbiAgICAncHJpY2UtaW5kaWNhdG9yJyxcbiAgICAnc2Fua2V5JyxcbiAgICAnYXJjLWRpYWdyYW0nLFxuICAgICdkZXBlbmRlbmN5LXdoZWVsJyxcbiAgICAnc2VyaWVzLWxhYmVsJyxcbiAgICAnc2VyaWVzLW9uLXBvaW50JyxcbiAgICAnc29saWQtZ2F1Z2UnLFxuICAgICdzb25pZmljYXRpb24nLFxuICAgIC8vICdzdG9jay10b29scycsXG4gICAgJ3N0cmVhbWdyYXBoJyxcbiAgICAnc3VuYnVyc3QnLFxuICAgICd2YXJpYWJsZS1waWUnLFxuICAgICd2YXJpd2lkZScsXG4gICAgJ3ZlY3RvcicsXG4gICAgJ3Zlbm4nLFxuICAgICd3aW5kYmFyYicsXG4gICAgJ3dvcmRjbG91ZCcsXG4gICAgJ3hyYW5nZScsXG4gICAgJ25vLWRhdGEtdG8tZGlzcGxheScsXG4gICAgJ2RyYWctcGFuZXMnLFxuICAgICdkZWJ1Z2dlcicsXG4gICAgJ2R1bWJiZWxsJyxcbiAgICAnbG9sbGlwb3AnLFxuICAgICdjeWxpbmRlcicsXG4gICAgJ29yZ2FuaXphdGlvbicsXG4gICAgJ2RvdHBsb3QnLFxuICAgICdtYXJrZXItY2x1c3RlcnMnLFxuICAgICdob2xsb3djYW5kbGVzdGljaycsXG4gICAgJ2hlaWtpbmFzaGknLFxuICAgICdmbG93bWFwJyxcbiAgICAnZXhwb3J0LWRhdGEnLFxuICAgICduYXZpZ2F0b3InLFxuICAgICd0ZXh0cGF0aCdcbiAgXSxcbiAgaW5kaWNhdG9yczogWydpbmRpY2F0b3JzLWFsbCddLFxuICBjdXN0b206IFtcbiAgICAnaHR0cHM6Ly9jZG5qcy5jbG91ZGZsYXJlLmNvbS9hamF4L2xpYnMvbW9tZW50LmpzLzIuMzAuMS9tb21lbnQubWluLmpzJyxcbiAgICAnaHR0cHM6Ly9jZG5qcy5jbG91ZGZsYXJlLmNvbS9hamF4L2xpYnMvbW9tZW50LXRpbWV6b25lLzAuNS40NS9tb21lbnQtdGltZXpvbmUtd2l0aC1kYXRhLm1pbi5qcydcbiAgXVxufTtcblxuLy8gVGhpcyBpcyB0aGUgY29uZmlndXJhdGlvbiBvYmplY3Qgd2l0aCBhbGwgb3B0aW9ucyBhbmQgdGhlaXIgZGVmYXVsdCB2YWx1ZXMsXG4vLyBhbHNvIGZyb20gdGhlIC5lbnYgZmlsZSBpZiBvbmUgZXhpc3RzXG5leHBvcnQgY29uc3QgZGVmYXVsdENvbmZpZyA9IHtcbiAgcHVwcGV0ZWVyOiB7XG4gICAgYXJnczoge1xuICAgICAgdmFsdWU6IFtcbiAgICAgICAgJy0tYWxsb3ctcnVubmluZy1pbnNlY3VyZS1jb250ZW50JyxcbiAgICAgICAgJy0tYXNoLW5vLW51ZGdlcycsXG4gICAgICAgICctLWF1dG9wbGF5LXBvbGljeT11c2VyLWdlc3R1cmUtcmVxdWlyZWQnLFxuICAgICAgICAnLS1ibG9jay1uZXctd2ViLWNvbnRlbnRzJyxcbiAgICAgICAgJy0tZGlzYWJsZS1hY2NlbGVyYXRlZC0yZC1jYW52YXMnLFxuICAgICAgICAnLS1kaXNhYmxlLWJhY2tncm91bmQtbmV0d29ya2luZycsXG4gICAgICAgICctLWRpc2FibGUtYmFja2dyb3VuZC10aW1lci10aHJvdHRsaW5nJyxcbiAgICAgICAgJy0tZGlzYWJsZS1iYWNrZ3JvdW5kaW5nLW9jY2x1ZGVkLXdpbmRvd3MnLFxuICAgICAgICAnLS1kaXNhYmxlLWJyZWFrcGFkJyxcbiAgICAgICAgJy0tZGlzYWJsZS1jaGVja2VyLWltYWdpbmcnLFxuICAgICAgICAnLS1kaXNhYmxlLWNsaWVudC1zaWRlLXBoaXNoaW5nLWRldGVjdGlvbicsXG4gICAgICAgICctLWRpc2FibGUtY29tcG9uZW50LWV4dGVuc2lvbnMtd2l0aC1iYWNrZ3JvdW5kLXBhZ2VzJyxcbiAgICAgICAgJy0tZGlzYWJsZS1jb21wb25lbnQtdXBkYXRlJyxcbiAgICAgICAgJy0tZGlzYWJsZS1kZWZhdWx0LWFwcHMnLFxuICAgICAgICAnLS1kaXNhYmxlLWRldi1zaG0tdXNhZ2UnLFxuICAgICAgICAnLS1kaXNhYmxlLWRvbWFpbi1yZWxpYWJpbGl0eScsXG4gICAgICAgICctLWRpc2FibGUtZXh0ZW5zaW9ucycsXG4gICAgICAgICctLWRpc2FibGUtZmVhdHVyZXM9Q2FsY3VsYXRlTmF0aXZlV2luT2NjbHVzaW9uLEludGVyZXN0RmVlZENvbnRlbnRTdWdnZXN0aW9ucyxXZWJPVFAnLFxuICAgICAgICAnLS1kaXNhYmxlLWhhbmctbW9uaXRvcicsXG4gICAgICAgICctLWRpc2FibGUtaXBjLWZsb29kaW5nLXByb3RlY3Rpb24nLFxuICAgICAgICAnLS1kaXNhYmxlLWxvZ2dpbmcnLFxuICAgICAgICAnLS1kaXNhYmxlLW5vdGlmaWNhdGlvbnMnLFxuICAgICAgICAnLS1kaXNhYmxlLW9mZmVyLXN0b3JlLXVubWFza2VkLXdhbGxldC1jYXJkcycsXG4gICAgICAgICctLWRpc2FibGUtcG9wdXAtYmxvY2tpbmcnLFxuICAgICAgICAnLS1kaXNhYmxlLXByaW50LXByZXZpZXcnLFxuICAgICAgICAnLS1kaXNhYmxlLXByb21wdC1vbi1yZXBvc3QnLFxuICAgICAgICAnLS1kaXNhYmxlLXJlbmRlcmVyLWJhY2tncm91bmRpbmcnLFxuICAgICAgICAnLS1kaXNhYmxlLXNlYXJjaC1lbmdpbmUtY2hvaWNlLXNjcmVlbicsXG4gICAgICAgICctLWRpc2FibGUtc2Vzc2lvbi1jcmFzaGVkLWJ1YmJsZScsXG4gICAgICAgICctLWRpc2FibGUtc2V0dWlkLXNhbmRib3gnLFxuICAgICAgICAnLS1kaXNhYmxlLXNpdGUtaXNvbGF0aW9uLXRyaWFscycsXG4gICAgICAgICctLWRpc2FibGUtc3BlZWNoLWFwaScsXG4gICAgICAgICctLWRpc2FibGUtc3luYycsXG4gICAgICAgICctLWVuYWJsZS11bnNhZmUtd2ViZ3B1JyxcbiAgICAgICAgJy0taGlkZS1jcmFzaC1yZXN0b3JlLWJ1YmJsZScsXG4gICAgICAgICctLWhpZGUtc2Nyb2xsYmFycycsXG4gICAgICAgICctLW1ldHJpY3MtcmVjb3JkaW5nLW9ubHknLFxuICAgICAgICAnLS1tdXRlLWF1ZGlvJyxcbiAgICAgICAgJy0tbm8tZGVmYXVsdC1icm93c2VyLWNoZWNrJyxcbiAgICAgICAgJy0tbm8tZmlyc3QtcnVuJyxcbiAgICAgICAgJy0tbm8tcGluZ3MnLFxuICAgICAgICAnLS1waXBlJyxcbiAgICAgICAgLy8gJy0tbm8tc2FuZGJveCcsXG4gICAgICAgICctLW5vLXN0YXJ0dXAtd2luZG93JyxcbiAgICAgICAgLy8gJy0tbm8tenlnb3RlJyxcbiAgICAgICAgJy0tcGFzc3dvcmQtc3RvcmU9YmFzaWMnLFxuICAgICAgICAnLS1wcm9jZXNzLXBlci10YWInLFxuICAgICAgICAnLS11c2UtbW9jay1rZXljaGFpbidcbiAgICAgIF0sXG4gICAgICB0eXBlOiAnc3RyaW5nW10nLFxuICAgICAgZGVzY3JpcHRpb246ICdBcmd1bWVudHMgYXJyYXkgdG8gc2VuZCB0byBQdXBwZXRlZXIuJ1xuICAgIH0sXG4gICAgbGF1bmNoUmV0cnlXaW5kb3c6IHtcbiAgICAgIHZhbHVlOiAzMDAwMCxcbiAgICAgIHR5cGU6ICdudW1iZXInLFxuICAgICAgZW52TGluazogJ1BVUFBFVEVFUl9MQVVOQ0hfUkVUUllfV0lORE9XJyxcbiAgICAgIGRlc2NyaXB0aW9uOlxuICAgICAgICAnSG93IGxvbmcsIGluIG1pbGxpc2Vjb25kcywgdG8ga2VlcCByZXRyeWluZyBhIGJyb3dzZXIgbGF1bmNoIGJlZm9yZSBnaXZpbmcgdXAuIERlbGF5cyBiZXR3ZWVuIGF0dGVtcHRzIGdyb3cgYW5kIGNhcnJ5IGppdHRlci4gS2VlcCB0aGlzIGJlbG93IHRoZSB0aW1lIGFuIG9yY2hlc3RyYXRvciB3YWl0cyBiZWZvcmUgcmVwbGFjaW5nIGFuIGluc3RhbmNlIHRoYXQgaGFzIG5vdCBiZWNvbWUgaGVhbHRoeSwgc28gdGhhdCBhIGJyb3dzZXIgd2hpY2ggY2Fubm90IHN0YXJ0IGlzIHJlcG9ydGVkIHJhdGhlciB0aGFuIHNpbGVudGx5IHJldHJpZWQuJ1xuICAgIH0sXG4gICAgdGVtcERpcjoge1xuICAgICAgdmFsdWU6ICcuL3RtcC8nLFxuICAgICAgdHlwZTogJ3N0cmluZycsXG4gICAgICBlbnZMaW5rOiAnUFVQUEVURUVSX1RFTVBfRElSJyxcbiAgICAgIGRlc2NyaXB0aW9uOiAnVGhlIGRpcmVjdG9yeSBmb3IgUHVwcGV0ZWVyIHRvIHN0b3JlIHRlbXBvcmFyeSBmaWxlcy4nXG4gICAgfVxuICB9LFxuICBoaWdoY2hhcnRzOiB7XG4gICAgdmVyc2lvbjoge1xuICAgICAgdmFsdWU6ICdsYXRlc3QnLFxuICAgICAgdHlwZTogJ3N0cmluZycsXG4gICAgICBlbnZMaW5rOiAnSElHSENIQVJUU19WRVJTSU9OJyxcbiAgICAgIGRlc2NyaXB0aW9uOiAnVGhlIEhpZ2hjaGFydHMgdmVyc2lvbiB0byBiZSB1c2VkLidcbiAgICB9LFxuICAgIGNkblVSTDoge1xuICAgICAgdmFsdWU6ICdodHRwczovL2NvZGUuaGlnaGNoYXJ0cy5jb20vJyxcbiAgICAgIHR5cGU6ICdzdHJpbmcnLFxuICAgICAgZW52TGluazogJ0hJR0hDSEFSVFNfQ0ROX1VSTCcsXG4gICAgICBkZXNjcmlwdGlvbjogJ1RoZSBDRE4gVVJMIGZvciBIaWdoY2hhcnRzIHNjcmlwdHMgdG8gYmUgdXNlZC4nXG4gICAgfSxcbiAgICB1c2VOcG06IHtcbiAgICAgIHZhbHVlOiBmYWxzZSxcbiAgICAgIHR5cGU6ICdib29sZWFuJyxcbiAgICAgIGVudkxpbms6ICdISUdIQ0hBUlRTX1VTRV9OUE0nLFxuICAgICAgZGVzY3JpcHRpb246ICdGbGFnIHRvIHVzZSBIaWdoY2hhcnRzIHNjcmlwdHMgZnJvbSBOUE0gcGFja2FnZSdcbiAgICB9LFxuICAgIGNvcmVTY3JpcHRzOiB7XG4gICAgICB2YWx1ZTogc2NyaXB0c05hbWVzLmNvcmUsXG4gICAgICB0eXBlOiAnc3RyaW5nW10nLFxuICAgICAgZW52TGluazogJ0hJR0hDSEFSVFNfQ09SRV9TQ1JJUFRTJyxcbiAgICAgIGRlc2NyaXB0aW9uOiAnVGhlIGNvcmUgSGlnaGNoYXJ0cyBzY3JpcHRzIHRvIGZldGNoLidcbiAgICB9LFxuICAgIG1vZHVsZVNjcmlwdHM6IHtcbiAgICAgIHZhbHVlOiBzY3JpcHRzTmFtZXMubW9kdWxlcyxcbiAgICAgIHR5cGU6ICdzdHJpbmdbXScsXG4gICAgICBlbnZMaW5rOiAnSElHSENIQVJUU19NT0RVTEVfU0NSSVBUUycsXG4gICAgICBkZXNjcmlwdGlvbjogJ1RoZSBtb2R1bGVzIG9mIEhpZ2hjaGFydHMgdG8gZmV0Y2guJ1xuICAgIH0sXG4gICAgaW5kaWNhdG9yU2NyaXB0czoge1xuICAgICAgdmFsdWU6IHNjcmlwdHNOYW1lcy5pbmRpY2F0b3JzLFxuICAgICAgdHlwZTogJ3N0cmluZ1tdJyxcbiAgICAgIGVudkxpbms6ICdISUdIQ0hBUlRTX0lORElDQVRPUl9TQ1JJUFRTJyxcbiAgICAgIGRlc2NyaXB0aW9uOiAnVGhlIGluZGljYXRvcnMgb2YgSGlnaGNoYXJ0cyB0byBmZXRjaC4nXG4gICAgfSxcbiAgICBjdXN0b21TY3JpcHRzOiB7XG4gICAgICB2YWx1ZTogc2NyaXB0c05hbWVzLmN1c3RvbSxcbiAgICAgIHR5cGU6ICdzdHJpbmdbXScsXG4gICAgICBkZXNjcmlwdGlvbjogJ0FkZGl0aW9uYWwgY3VzdG9tIHNjcmlwdHMgb3IgZGVwZW5kZW5jaWVzIHRvIGZldGNoLidcbiAgICB9LFxuICAgIGZvcmNlRmV0Y2g6IHtcbiAgICAgIHZhbHVlOiBmYWxzZSxcbiAgICAgIHR5cGU6ICdib29sZWFuJyxcbiAgICAgIGVudkxpbms6ICdISUdIQ0hBUlRTX0ZPUkNFX0ZFVENIJyxcbiAgICAgIGRlc2NyaXB0aW9uOlxuICAgICAgICAnVGhlIGZsYWcgdG8gZGV0ZXJtaW5lIHdoZXRoZXIgdG8gcmVmZXRjaCBhbGwgc2NyaXB0cyBhZnRlciBlYWNoIHNlcnZlciByZXJ1bi4nXG4gICAgfSxcbiAgICBjYWNoZVBhdGg6IHtcbiAgICAgIHZhbHVlOiAnLmNhY2hlJyxcbiAgICAgIHR5cGU6ICdzdHJpbmcnLFxuICAgICAgZW52TGluazogJ0hJR0hDSEFSVFNfQ0FDSEVfUEFUSCcsXG4gICAgICBkZXNjcmlwdGlvbjpcbiAgICAgICAgJ1RoZSBwYXRoIHRvIHRoZSBjYWNoZSBkaXJlY3RvcnkuIEl0IGlzIHVzZWQgdG8gc3RvcmUgdGhlIEhpZ2hjaGFydHMgc2NyaXB0cyBhbmQgY3VzdG9tIHNjcmlwdHMuJ1xuICAgIH1cbiAgfSxcbiAgZXhwb3J0OiB7XG4gICAgaW5maWxlOiB7XG4gICAgICB2YWx1ZTogZmFsc2UsXG4gICAgICB0eXBlOiAnc3RyaW5nJyxcbiAgICAgIGRlc2NyaXB0aW9uOlxuICAgICAgICAnVGhlIGlucHV0IGZpbGUgc2hvdWxkIGluY2x1ZGUgYSBuYW1lIGFuZCBhIHR5cGUgKGpzb24gb3Igc3ZnKS4gSXQgbXVzdCBiZSBjb3JyZWN0bHkgZm9ybWF0dGVkIGFzIGEgSlNPTiBvciBTVkcgZmlsZS4nXG4gICAgfSxcbiAgICBpbnN0cjoge1xuICAgICAgdmFsdWU6IGZhbHNlLFxuICAgICAgdHlwZTogJ3N0cmluZycsXG4gICAgICBkZXNjcmlwdGlvbjpcbiAgICAgICAgJ0lucHV0LCBwcm92aWRlZCBpbiB0aGUgZm9ybSBvZiBhIHN0cmluZ2lmaWVkIEpTT04gb3IgU1ZHIGZpbGUsIHdpbGwgb3ZlcnJpZGUgdGhlIC0taW5maWxlIG9wdGlvbi4nXG4gICAgfSxcbiAgICBvcHRpb25zOiB7XG4gICAgICB2YWx1ZTogZmFsc2UsXG4gICAgICB0eXBlOiAnc3RyaW5nJyxcbiAgICAgIGRlc2NyaXB0aW9uOiAnQW4gYWxpYXMgZm9yIHRoZSAtLWluc3RyIG9wdGlvbi4nXG4gICAgfSxcbiAgICBvdXRmaWxlOiB7XG4gICAgICB2YWx1ZTogZmFsc2UsXG4gICAgICB0eXBlOiAnc3RyaW5nJyxcbiAgICAgIGRlc2NyaXB0aW9uOlxuICAgICAgICAnVGhlIG91dHB1dCBmaWxlbmFtZSBhbG9uZyB3aXRoIGEgdHlwZSAoanBlZywgcG5nLCBwZGYsIG9yIHN2ZykuIFRoaXMgd2lsbCBpZ25vcmUgdGhlIC0tdHlwZSBmbGFnLidcbiAgICB9LFxuICAgIHR5cGU6IHtcbiAgICAgIHZhbHVlOiAncG5nJyxcbiAgICAgIHR5cGU6ICdzdHJpbmcnLFxuICAgICAgZW52TGluazogJ0VYUE9SVF9UWVBFJyxcbiAgICAgIGRlc2NyaXB0aW9uOiAnVGhlIGZpbGUgZXhwb3J0IGZvcm1hdC4gSXQgY2FuIGJlIGpwZWcsIHBuZywgcGRmLCBvciBzdmcuJ1xuICAgIH0sXG4gICAgY29uc3RyOiB7XG4gICAgICB2YWx1ZTogJ2NoYXJ0JyxcbiAgICAgIHR5cGU6ICdzdHJpbmcnLFxuICAgICAgZW52TGluazogJ0VYUE9SVF9DT05TVFInLFxuICAgICAgZGVzY3JpcHRpb246XG4gICAgICAgICdUaGUgY29uc3RydWN0b3IgdG8gdXNlLiBDYW4gYmUgY2hhcnQsIHN0b2NrQ2hhcnQsIG1hcENoYXJ0LCBvciBnYW50dENoYXJ0LidcbiAgICB9LFxuICAgIGRlZmF1bHRIZWlnaHQ6IHtcbiAgICAgIHZhbHVlOiA0MDAsXG4gICAgICB0eXBlOiAnbnVtYmVyJyxcbiAgICAgIGVudkxpbms6ICdFWFBPUlRfREVGQVVMVF9IRUlHSFQnLFxuICAgICAgZGVzY3JpcHRpb246XG4gICAgICAgICd0aGUgZGVmYXVsdCBoZWlnaHQgb2YgdGhlIGV4cG9ydGVkIGNoYXJ0LiBVc2VkIHdoZW4gbm8gdmFsdWUgaXMgc2V0LidcbiAgICB9LFxuICAgIGRlZmF1bHRXaWR0aDoge1xuICAgICAgdmFsdWU6IDYwMCxcbiAgICAgIHR5cGU6ICdudW1iZXInLFxuICAgICAgZW52TGluazogJ0VYUE9SVF9ERUZBVUxUX1dJRFRIJyxcbiAgICAgIGRlc2NyaXB0aW9uOlxuICAgICAgICAnVGhlIGRlZmF1bHQgd2lkdGggb2YgdGhlIGV4cG9ydGVkIGNoYXJ0LiBVc2VkIHdoZW4gbm8gdmFsdWUgaXMgc2V0LidcbiAgICB9LFxuICAgIGRlZmF1bHRTY2FsZToge1xuICAgICAgdmFsdWU6IDEsXG4gICAgICB0eXBlOiAnbnVtYmVyJyxcbiAgICAgIGVudkxpbms6ICdFWFBPUlRfREVGQVVMVF9TQ0FMRScsXG4gICAgICBkZXNjcmlwdGlvbjpcbiAgICAgICAgJ1RoZSBkZWZhdWx0IHNjYWxlIG9mIHRoZSBleHBvcnRlZCBjaGFydC4gVXNlZCB3aGVuIG5vIHZhbHVlIGlzIHNldC4nXG4gICAgfSxcbiAgICBoZWlnaHQ6IHtcbiAgICAgIHZhbHVlOiBmYWxzZSxcbiAgICAgIHR5cGU6ICdudW1iZXInLFxuICAgICAgZGVzY3JpcHRpb246XG4gICAgICAgICdUaGUgaGVpZ2h0IG9mIHRoZSBleHBvcnRlZCBjaGFydCwgb3ZlcnJpZGluZyB0aGUgb3B0aW9uIGluIHRoZSBjaGFydCBzZXR0aW5ncy4nXG4gICAgfSxcbiAgICB3aWR0aDoge1xuICAgICAgdmFsdWU6IGZhbHNlLFxuICAgICAgdHlwZTogJ251bWJlcicsXG4gICAgICBkZXNjcmlwdGlvbjpcbiAgICAgICAgJ1RoZSB3aWR0aCBvZiB0aGUgZXhwb3J0ZWQgY2hhcnQsIG92ZXJyaWRpbmcgdGhlIG9wdGlvbiBpbiB0aGUgY2hhcnQgc2V0dGluZ3MuJ1xuICAgIH0sXG4gICAgc2NhbGU6IHtcbiAgICAgIHZhbHVlOiBmYWxzZSxcbiAgICAgIHR5cGU6ICdudW1iZXInLFxuICAgICAgZGVzY3JpcHRpb246XG4gICAgICAgICdUaGUgc2NhbGUgb2YgdGhlIGV4cG9ydGVkIGNoYXJ0LCBvdmVycmlkaW5nIHRoZSBvcHRpb24gaW4gdGhlIGNoYXJ0IHNldHRpbmdzLiBSYW5nZXMgYmV0d2VlbiAwLjEgYW5kIDUuMC4nXG4gICAgfSxcbiAgICBnbG9iYWxPcHRpb25zOiB7XG4gICAgICB2YWx1ZTogZmFsc2UsXG4gICAgICB0eXBlOiAnc3RyaW5nJyxcbiAgICAgIGRlc2NyaXB0aW9uOlxuICAgICAgICAnRWl0aGVyIGEgc3RyaW5naWZpZWQgSlNPTiBvciBhIGZpbGVuYW1lIGNvbnRhaW5pbmcgb3B0aW9ucyB0byBiZSBwYXNzZWQgaW50byB0aGUgSGlnaGNoYXJ0cy5zZXRPcHRpb25zLidcbiAgICB9LFxuICAgIHRoZW1lT3B0aW9uczoge1xuICAgICAgdmFsdWU6IGZhbHNlLFxuICAgICAgdHlwZTogJ3N0cmluZycsXG4gICAgICBkZXNjcmlwdGlvbjpcbiAgICAgICAgJ0VpdGhlciBhIHN0cmluZ2lmaWVkIEpTT04gb3IgYSBmaWxlbmFtZSBjb250YWluaW5nIHRoZW1lIG9wdGlvbnMgdG8gYmUgcGFzc2VkIGludG8gdGhlIEhpZ2hjaGFydHMuc2V0T3B0aW9ucy4nXG4gICAgfSxcbiAgICBiYXRjaDoge1xuICAgICAgdmFsdWU6IGZhbHNlLFxuICAgICAgdHlwZTogJ3N0cmluZycsXG4gICAgICBkZXNjcmlwdGlvbjpcbiAgICAgICAgJ0luaXRpYXRlcyBhIGJhdGNoIGpvYiB3aXRoIGEgc3RyaW5nIGNvbnRhaW5pbmcgaW5wdXQvb3V0cHV0IHBhaXJzOiBcImluPW91dDtpbj1vdXQ7Li4uXCIuJ1xuICAgIH0sXG4gICAgcmFzdGVyaXphdGlvblRpbWVvdXQ6IHtcbiAgICAgIHZhbHVlOiAxNTAwLFxuICAgICAgdHlwZTogJ251bWJlcicsXG4gICAgICBlbnZMaW5rOiAnRVhQT1JUX1JBU1RFUklaQVRJT05fVElNRU9VVCcsXG4gICAgICBkZXNjcmlwdGlvbjpcbiAgICAgICAgJ1RoZSBkdXJhdGlvbiBpbiBtaWxsaXNlY29uZHMgdG8gd2FpdCBmb3IgcmVuZGVyaW5nIGEgd2VicGFnZS4nXG4gICAgfVxuICB9LFxuICBjdXN0b21Mb2dpYzoge1xuICAgIGFsbG93Q29kZUV4ZWN1dGlvbjoge1xuICAgICAgdmFsdWU6IGZhbHNlLFxuICAgICAgdHlwZTogJ2Jvb2xlYW4nLFxuICAgICAgZW52TGluazogJ0NVU1RPTV9MT0dJQ19BTExPV19DT0RFX0VYRUNVVElPTicsXG4gICAgICBkZXNjcmlwdGlvbjpcbiAgICAgICAgJ0NvbnRyb2xzIHdoZXRoZXIgdGhlIGV4ZWN1dGlvbiBvZiBhcmJpdHJhcnkgY29kZSBpcyBhbGxvd2VkIGR1cmluZyB0aGUgZXhwb3J0aW5nIHByb2Nlc3MuJ1xuICAgIH0sXG4gICAgYWxsb3dGaWxlUmVzb3VyY2VzOiB7XG4gICAgICB2YWx1ZTogZmFsc2UsXG4gICAgICB0eXBlOiAnYm9vbGVhbicsXG4gICAgICBlbnZMaW5rOiAnQ1VTVE9NX0xPR0lDX0FMTE9XX0ZJTEVfUkVTT1VSQ0VTJyxcbiAgICAgIGRlc2NyaXB0aW9uOlxuICAgICAgICAnQ29udHJvbHMgdGhlIGFiaWxpdHkgdG8gaW5qZWN0IHJlc291cmNlcyBmcm9tIHRoZSBmaWxlc3lzdGVtLiBUaGlzIHNldHRpbmcgaGFzIG5vIGVmZmVjdCB3aGVuIHJ1bm5pbmcgYXMgYSBzZXJ2ZXIuJ1xuICAgIH0sXG4gICAgY3VzdG9tQ29kZToge1xuICAgICAgdmFsdWU6IGZhbHNlLFxuICAgICAgdHlwZTogJ3N0cmluZycsXG4gICAgICBkZXNjcmlwdGlvbjpcbiAgICAgICAgJ0N1c3RvbSBjb2RlIHRvIGV4ZWN1dGUgYmVmb3JlIGNoYXJ0IGluaXRpYWxpemF0aW9uLiBJdCBjYW4gYmUgYSBmdW5jdGlvbiwgY29kZSB3cmFwcGVkIHdpdGhpbiBhIGZ1bmN0aW9uLCBvciBhIGZpbGVuYW1lIHdpdGggdGhlIC5qcyBleHRlbnNpb24uJ1xuICAgIH0sXG4gICAgY2FsbGJhY2s6IHtcbiAgICAgIHZhbHVlOiBmYWxzZSxcbiAgICAgIHR5cGU6ICdzdHJpbmcnLFxuICAgICAgZGVzY3JpcHRpb246XG4gICAgICAgICdKYXZhU2NyaXB0IGNvZGUgdG8gcnVuIGR1cmluZyBjb25zdHJ1Y3Rpb24uIEl0IGNhbiBiZSBhIGZ1bmN0aW9uIG9yIGEgZmlsZW5hbWUgd2l0aCB0aGUgLmpzIGV4dGVuc2lvbi4nXG4gICAgfSxcbiAgICByZXNvdXJjZXM6IHtcbiAgICAgIHZhbHVlOiBmYWxzZSxcbiAgICAgIHR5cGU6ICdzdHJpbmcnLFxuICAgICAgZGVzY3JpcHRpb246XG4gICAgICAgICdBZGRpdGlvbmFsIHJlc291cmNlIGluIHRoZSBmb3JtIG9mIGEgc3RyaW5naWZpZWQgSlNPTiwgd2hpY2ggbWF5IGNvbnRhaW4gZmlsZXMsIGpzLCBhbmQgY3NzIHNlY3Rpb25zLidcbiAgICB9LFxuICAgIGxvYWRDb25maWc6IHtcbiAgICAgIHZhbHVlOiBmYWxzZSxcbiAgICAgIHR5cGU6ICdzdHJpbmcnLFxuICAgICAgbGVnYWN5TmFtZTogJ2Zyb21GaWxlJyxcbiAgICAgIGRlc2NyaXB0aW9uOiAnQSBmaWxlIGNvbnRhaW5pbmcgYSBwcmUtZGVmaW5lZCBjb25maWd1cmF0aW9uIHRvIHVzZS4nXG4gICAgfSxcbiAgICBjcmVhdGVDb25maWc6IHtcbiAgICAgIHZhbHVlOiBmYWxzZSxcbiAgICAgIHR5cGU6ICdzdHJpbmcnLFxuICAgICAgZGVzY3JpcHRpb246XG4gICAgICAgICdFbmFibGVzIHNldHRpbmcgb3B0aW9ucyB0aHJvdWdoIGEgcHJvbXB0IGFuZCBzYXZpbmcgdGhlbSBpbiBhIHByb3ZpZGVkIGNvbmZpZyBmaWxlLidcbiAgICB9XG4gIH0sXG4gIHNlcnZlcjoge1xuICAgIG1heFVwbG9hZFNpemU6IHtcbiAgICAgIHZhbHVlOiAzLFxuICAgICAgdHlwZTogJ251bWJlcicsXG4gICAgICBlbnZMaW5rOiAnU0VSVkVSX01BWF9VUExPQURfU0laRScsXG4gICAgICBkZXNjcmlwdGlvbjogJ1RoZSBtYXhpbXVtIHVwbG9hZCBzaXplLCBpbiBNQiwgZm9yIHRoZSBzZXJ2ZXIuJ1xuICAgIH0sXG4gICAgZW5hYmxlOiB7XG4gICAgICB2YWx1ZTogZmFsc2UsXG4gICAgICB0eXBlOiAnYm9vbGVhbicsXG4gICAgICBlbnZMaW5rOiAnU0VSVkVSX0VOQUJMRScsXG4gICAgICBjbGlOYW1lOiAnZW5hYmxlU2VydmVyJyxcbiAgICAgIGRlc2NyaXB0aW9uOlxuICAgICAgICAnV2hlbiBzZXQgdG8gdHJ1ZSwgdGhlIHNlcnZlciBzdGFydHMgb24gdGhlIGxvY2FsIElQIGFkZHJlc3MgMC4wLjAuMC4nXG4gICAgfSxcbiAgICBob3N0OiB7XG4gICAgICB2YWx1ZTogJzAuMC4wLjAnLFxuICAgICAgdHlwZTogJ3N0cmluZycsXG4gICAgICBlbnZMaW5rOiAnU0VSVkVSX0hPU1QnLFxuICAgICAgZGVzY3JpcHRpb246XG4gICAgICAgICdUaGUgaG9zdG5hbWUgb2YgdGhlIHNlcnZlci4gQWRkaXRpb25hbGx5LCBpdCBzdGFydHMgYSBzZXJ2ZXIgb24gdGhlIHByb3ZpZGVkIGhvc3RuYW1lLidcbiAgICB9LFxuICAgIHBvcnQ6IHtcbiAgICAgIHZhbHVlOiA3ODAxLFxuICAgICAgdHlwZTogJ251bWJlcicsXG4gICAgICBlbnZMaW5rOiAnU0VSVkVSX1BPUlQnLFxuICAgICAgZGVzY3JpcHRpb246ICdUaGUgc2VydmVyIHBvcnQgd2hlbiBlbmFibGVkLidcbiAgICB9LFxuICAgIGtlZXBBbGl2ZVRpbWVvdXQ6IHtcbiAgICAgIHZhbHVlOiA2NTAwMCxcbiAgICAgIHR5cGU6ICdudW1iZXInLFxuICAgICAgZW52TGluazogJ1NFUlZFUl9LRUVQX0FMSVZFX1RJTUVPVVQnLFxuICAgICAgZGVzY3JpcHRpb246XG4gICAgICAgICdIb3cgbG9uZywgaW4gbWlsbGlzZWNvbmRzLCBhbiBpZGxlIGtlZXAtYWxpdmUgY29ubmVjdGlvbiBpcyBoZWxkIG9wZW4uIE5vZGUgZGVmYXVsdHMgdG8gNSBzZWNvbmRzLCB3aGljaCBpcyBzaG9ydGVyIHRoYW4gdGhlIGlkbGUgdGltZW91dCBvZiBhIHR5cGljYWwgcHJveHkgb3IgbG9hZCBiYWxhbmNlciwgYW5kIHRoZSBtaXNtYXRjaCBjYXVzZXMgc3BvcmFkaWMgZ2F0ZXdheSBlcnJvcnMgd2hlbiB0aGUgcHJveHkgc2VuZHMgYSByZXF1ZXN0IGludG8gYSBjb25uZWN0aW9uIHRoaXMgc2VydmVyIGhhcyBqdXN0IGNsb3NlZC4gS2VlcCB0aGlzIGFib3ZlIHRoZSBpZGxlIHRpbWVvdXQgb2YgYW55dGhpbmcgaW4gZnJvbnQgb2YgdGhlIHNlcnZlci4gaGVhZGVyc1RpbWVvdXQgaXMga2VwdCA1IHNlY29uZHMgYWJvdmUgaXQgYXV0b21hdGljYWxseS4nXG4gICAgfSxcbiAgICBiZW5jaG1hcmtpbmc6IHtcbiAgICAgIHZhbHVlOiBmYWxzZSxcbiAgICAgIHR5cGU6ICdib29sZWFuJyxcbiAgICAgIGVudkxpbms6ICdTRVJWRVJfQkVOQ0hNQVJLSU5HJyxcbiAgICAgIGNsaU5hbWU6ICdzZXJ2ZXJCZW5jaG1hcmtpbmcnLFxuICAgICAgZGVzY3JpcHRpb246XG4gICAgICAgICdJbmRpY2F0ZXMgd2hldGhlciB0byBkaXNwbGF5IHRoZSBkdXJhdGlvbiwgaW4gbWlsbGlzZWNvbmRzLCBvZiBzcGVjaWZpYyBhY3Rpb25zIHRoYXQgb2NjdXIgb24gdGhlIHNlcnZlciB3aGlsZSBzZXJ2aW5nIGEgcmVxdWVzdC4nXG4gICAgfSxcbiAgICBwcm94eToge1xuICAgICAgaG9zdDoge1xuICAgICAgICB2YWx1ZTogZmFsc2UsXG4gICAgICAgIHR5cGU6ICdzdHJpbmcnLFxuICAgICAgICBlbnZMaW5rOiAnU0VSVkVSX1BST1hZX0hPU1QnLFxuICAgICAgICBjbGlOYW1lOiAncHJveHlIb3N0JyxcbiAgICAgICAgZGVzY3JpcHRpb246ICdUaGUgaG9zdCBvZiB0aGUgcHJveHkgc2VydmVyIHRvIHVzZSwgaWYgaXQgZXhpc3RzLidcbiAgICAgIH0sXG4gICAgICBwb3J0OiB7XG4gICAgICAgIHZhbHVlOiA4MDgwLFxuICAgICAgICB0eXBlOiAnbnVtYmVyJyxcbiAgICAgICAgZW52TGluazogJ1NFUlZFUl9QUk9YWV9QT1JUJyxcbiAgICAgICAgY2xpTmFtZTogJ3Byb3h5UG9ydCcsXG4gICAgICAgIGRlc2NyaXB0aW9uOiAnVGhlIHBvcnQgb2YgdGhlIHByb3h5IHNlcnZlciB0byB1c2UsIGlmIGl0IGV4aXN0cy4nXG4gICAgICB9LFxuICAgICAgdXNlcm5hbWU6IHtcbiAgICAgICAgdmFsdWU6IGZhbHNlLFxuICAgICAgICB0eXBlOiAnc3RyaW5nJyxcbiAgICAgICAgZW52TGluazogJ1NFUlZFUl9QUk9YWV9VU0VSTkFNRScsXG4gICAgICAgIGNsaU5hbWU6ICdwcm94eVVzZXJuYW1lJyxcbiAgICAgICAgZGVzY3JpcHRpb246ICdUaGUgdXNlcm5hbWUgZm9yIHRoZSBwcm94eSBzZXJ2ZXIsIGlmIGl0IGV4aXN0cy4nXG4gICAgICB9LFxuICAgICAgcGFzc3dvcmQ6IHtcbiAgICAgICAgdmFsdWU6IGZhbHNlLFxuICAgICAgICB0eXBlOiAnc3RyaW5nJyxcbiAgICAgICAgZW52TGluazogJ1NFUlZFUl9QUk9YWV9QQVNTV09SRCcsXG4gICAgICAgIGNsaU5hbWU6ICdwcm94eVBhc3N3b3JkJyxcbiAgICAgICAgZGVzY3JpcHRpb246ICdUaGUgcGFzc3dvcmQgZm9yIHRoZSBwcm94eSBzZXJ2ZXIsIGlmIGl0IGV4aXN0cy4nXG4gICAgICB9LFxuICAgICAgdGltZW91dDoge1xuICAgICAgICB2YWx1ZTogNTAwMCxcbiAgICAgICAgdHlwZTogJ251bWJlcicsXG4gICAgICAgIGVudkxpbms6ICdTRVJWRVJfUFJPWFlfVElNRU9VVCcsXG4gICAgICAgIGNsaU5hbWU6ICdwcm94eVRpbWVvdXQnLFxuICAgICAgICBkZXNjcmlwdGlvbjogJ1RoZSB0aW1lb3V0IGZvciB0aGUgcHJveHkgc2VydmVyIHRvIHVzZSwgaWYgaXQgZXhpc3RzLidcbiAgICAgIH1cbiAgICB9LFxuICAgIHJhdGVMaW1pdGluZzoge1xuICAgICAgZW5hYmxlOiB7XG4gICAgICAgIHZhbHVlOiBmYWxzZSxcbiAgICAgICAgdHlwZTogJ2Jvb2xlYW4nLFxuICAgICAgICBlbnZMaW5rOiAnU0VSVkVSX1JBVEVfTElNSVRJTkdfRU5BQkxFJyxcbiAgICAgICAgY2xpTmFtZTogJ2VuYWJsZVJhdGVMaW1pdGluZycsXG4gICAgICAgIGRlc2NyaXB0aW9uOiAnRW5hYmxlcyByYXRlIGxpbWl0aW5nIGZvciB0aGUgc2VydmVyLidcbiAgICAgIH0sXG4gICAgICBtYXhSZXF1ZXN0czoge1xuICAgICAgICB2YWx1ZTogMTAsXG4gICAgICAgIHR5cGU6ICdudW1iZXInLFxuICAgICAgICBlbnZMaW5rOiAnU0VSVkVSX1JBVEVfTElNSVRJTkdfTUFYX1JFUVVFU1RTJyxcbiAgICAgICAgbGVnYWN5TmFtZTogJ3JhdGVMaW1pdCcsXG4gICAgICAgIGRlc2NyaXB0aW9uOiAnVGhlIG1heGltdW0gbnVtYmVyIG9mIHJlcXVlc3RzIGFsbG93ZWQgaW4gb25lIG1pbnV0ZS4nXG4gICAgICB9LFxuICAgICAgd2luZG93OiB7XG4gICAgICAgIHZhbHVlOiAxLFxuICAgICAgICB0eXBlOiAnbnVtYmVyJyxcbiAgICAgICAgZW52TGluazogJ1NFUlZFUl9SQVRFX0xJTUlUSU5HX1dJTkRPVycsXG4gICAgICAgIGRlc2NyaXB0aW9uOiAnVGhlIHRpbWUgd2luZG93LCBpbiBtaW51dGVzLCBmb3IgdGhlIHJhdGUgbGltaXRpbmcuJ1xuICAgICAgfSxcbiAgICAgIHRydXN0UHJveHk6IHtcbiAgICAgICAgdmFsdWU6IGZhbHNlLFxuICAgICAgICB0eXBlOiAnYm9vbGVhbicsXG4gICAgICAgIGVudkxpbms6ICdTRVJWRVJfUkFURV9MSU1JVElOR19UUlVTVF9QUk9YWScsXG4gICAgICAgIGRlc2NyaXB0aW9uOiAnU2V0IHRoaXMgdG8gdHJ1ZSBpZiB0aGUgc2VydmVyIGlzIGJlaGluZCBhIGxvYWQgYmFsYW5jZXIuJ1xuICAgICAgfSxcbiAgICAgIHNraXBLZXk6IHtcbiAgICAgICAgdmFsdWU6IGZhbHNlLFxuICAgICAgICB0eXBlOiAnc3RyaW5nJyxcbiAgICAgICAgZW52TGluazogJ1NFUlZFUl9SQVRFX0xJTUlUSU5HX1NLSVBfS0VZJyxcbiAgICAgICAgZGVzY3JpcHRpb246XG4gICAgICAgICAgJ0FsbG93cyBieXBhc3NpbmcgdGhlIHJhdGUgbGltaXRlciBhbmQgc2hvdWxkIGJlIHByb3ZpZGVkIHdpdGggdGhlIHNraXBUb2tlbiBhcmd1bWVudC4nXG4gICAgICB9LFxuICAgICAgc2tpcFRva2VuOiB7XG4gICAgICAgIHZhbHVlOiBmYWxzZSxcbiAgICAgICAgdHlwZTogJ3N0cmluZycsXG4gICAgICAgIGVudkxpbms6ICdTRVJWRVJfUkFURV9MSU1JVElOR19TS0lQX1RPS0VOJyxcbiAgICAgICAgZGVzY3JpcHRpb246XG4gICAgICAgICAgJ0FsbG93cyBieXBhc3NpbmcgdGhlIHJhdGUgbGltaXRlciBhbmQgc2hvdWxkIGJlIHByb3ZpZGVkIHdpdGggdGhlIHNraXBLZXkgYXJndW1lbnQuJ1xuICAgICAgfVxuICAgIH0sXG4gICAgc3NsOiB7XG4gICAgICBlbmFibGU6IHtcbiAgICAgICAgdmFsdWU6IGZhbHNlLFxuICAgICAgICB0eXBlOiAnYm9vbGVhbicsXG4gICAgICAgIGVudkxpbms6ICdTRVJWRVJfU1NMX0VOQUJMRScsXG4gICAgICAgIGNsaU5hbWU6ICdlbmFibGVTc2wnLFxuICAgICAgICBkZXNjcmlwdGlvbjogJ0VuYWJsZXMgb3IgZGlzYWJsZXMgdGhlIFNTTCBwcm90b2NvbC4nXG4gICAgICB9LFxuICAgICAgZm9yY2U6IHtcbiAgICAgICAgdmFsdWU6IGZhbHNlLFxuICAgICAgICB0eXBlOiAnYm9vbGVhbicsXG4gICAgICAgIGVudkxpbms6ICdTRVJWRVJfU1NMX0ZPUkNFJyxcbiAgICAgICAgY2xpTmFtZTogJ3NzbEZvcmNlJyxcbiAgICAgICAgbGVnYWN5TmFtZTogJ3NzbE9ubHknLFxuICAgICAgICBkZXNjcmlwdGlvbjpcbiAgICAgICAgICAnV2hlbiBzZXQgdG8gdHJ1ZSwgdGhlIHNlcnZlciBpcyBmb3JjZWQgdG8gc2VydmUgb25seSBvdmVyIEhUVFBTLidcbiAgICAgIH0sXG4gICAgICBwb3J0OiB7XG4gICAgICAgIHZhbHVlOiA0NDMsXG4gICAgICAgIHR5cGU6ICdudW1iZXInLFxuICAgICAgICBlbnZMaW5rOiAnU0VSVkVSX1NTTF9QT1JUJyxcbiAgICAgICAgY2xpTmFtZTogJ3NzbFBvcnQnLFxuICAgICAgICBkZXNjcmlwdGlvbjogJ1RoZSBwb3J0IG9uIHdoaWNoIHRvIHJ1biB0aGUgU1NMIHNlcnZlci4nXG4gICAgICB9LFxuICAgICAgY2VydFBhdGg6IHtcbiAgICAgICAgdmFsdWU6IGZhbHNlLFxuICAgICAgICB0eXBlOiAnc3RyaW5nJyxcbiAgICAgICAgZW52TGluazogJ1NFUlZFUl9TU0xfQ0VSVF9QQVRIJyxcbiAgICAgICAgbGVnYWN5TmFtZTogJ3NzbFBhdGgnLFxuICAgICAgICBkZXNjcmlwdGlvbjogJ1RoZSBwYXRoIHRvIHRoZSBTU0wgY2VydGlmaWNhdGUva2V5IGZpbGUuJ1xuICAgICAgfVxuICAgIH1cbiAgfSxcbiAgcG9vbDoge1xuICAgIG1pbldvcmtlcnM6IHtcbiAgICAgIHZhbHVlOiA0LFxuICAgICAgdHlwZTogJ251bWJlcicsXG4gICAgICBlbnZMaW5rOiAnUE9PTF9NSU5fV09SS0VSUycsXG4gICAgICBkZXNjcmlwdGlvbjogJ1RoZSBudW1iZXIgb2YgbWluaW11bSBhbmQgaW5pdGlhbCBwb29sIHdvcmtlcnMgdG8gc3Bhd24uJ1xuICAgIH0sXG4gICAgbWF4V29ya2Vyczoge1xuICAgICAgdmFsdWU6IDgsXG4gICAgICB0eXBlOiAnbnVtYmVyJyxcbiAgICAgIGVudkxpbms6ICdQT09MX01BWF9XT1JLRVJTJyxcbiAgICAgIGxlZ2FjeU5hbWU6ICd3b3JrZXJzJyxcbiAgICAgIGRlc2NyaXB0aW9uOiAnVGhlIG51bWJlciBvZiBtYXhpbXVtIHBvb2wgd29ya2VycyB0byBzcGF3bi4nXG4gICAgfSxcbiAgICB3b3JrTGltaXQ6IHtcbiAgICAgIHZhbHVlOiA0MCxcbiAgICAgIHR5cGU6ICdudW1iZXInLFxuICAgICAgZW52TGluazogJ1BPT0xfV09SS19MSU1JVCcsXG4gICAgICBkZXNjcmlwdGlvbjpcbiAgICAgICAgJ1RoZSBudW1iZXIgb2Ygd29yayBwaWVjZXMgdGhhdCBjYW4gYmUgcGVyZm9ybWVkIGJlZm9yZSByZXN0YXJ0aW5nIHRoZSB3b3JrZXIgcHJvY2Vzcy4nXG4gICAgfSxcbiAgICBxdWV1ZUxpbWl0OiB7XG4gICAgICB2YWx1ZTogMCxcbiAgICAgIHR5cGU6ICdudW1iZXInLFxuICAgICAgZW52TGluazogJ1BPT0xfUVVFVUVfTElNSVQnLFxuICAgICAgZGVzY3JpcHRpb246XG4gICAgICAgICdUaGUgbWF4aW11bSBudW1iZXIgb2YgZXhwb3J0cyBhbGxvd2VkIHRvIHdhaXQgZm9yIGEgd29ya2VyLiBSZXF1ZXN0cyBhcnJpdmluZyBiZXlvbmQgdGhpcyBhcmUgcmVmdXNlZCBpbnN0ZWFkIG9mIGJlaW5nIHF1ZXVlZC4gU2V0IHRvIDAgdG8gZGVyaXZlIGl0IGFzIGZvdXIgdGltZXMgbWF4V29ya2Vycy4gVGhyb3VnaHB1dCBkb2VzIG5vdCBpbXByb3ZlIHBhc3QgdGhlIHBvb2wgc2l6ZSwgc28gYSBkZWVwZXIgcXVldWUgb25seSBhZGRzIGxhdGVuY3kgYW5kIG1lbW9yeSB1c2UuJ1xuICAgIH0sXG4gICAgcXVldWVSZWplY3REZWxheToge1xuICAgICAgdmFsdWU6IDUwMCxcbiAgICAgIHR5cGU6ICdudW1iZXInLFxuICAgICAgZW52TGluazogJ1BPT0xfUVVFVUVfUkVKRUNUX0RFTEFZJyxcbiAgICAgIGRlc2NyaXB0aW9uOlxuICAgICAgICAnVGhlIGR1cmF0aW9uLCBpbiBtaWxsaXNlY29uZHMsIHRvIHdhaXQgYmVmb3JlIGFuc3dlcmluZyBhIHJlcXVlc3QgcmVmdXNlZCBiZWNhdXNlIHRoZSBxdWV1ZSBpcyBmdWxsLiBUaGlzIGlzIGRlbGliZXJhdGUgYmFja3ByZXNzdXJlOiBhbnN3ZXJpbmcgaW5zdGFudGx5IGxldHMgY2xpZW50cyB0aGF0IHJldHJ5IGltbWVkaWF0ZWx5IGNvbnN1bWUgdGhlIHdob2xlIGV2ZW50IGxvb3AgcmVmdXNpbmcgdGhlbSwgc3RhcnZpbmcgdGhlIGV4cG9ydHMgYWxyZWFkeSBpbiBwcm9ncmVzcy4gU2V0IHRvIDAgdG8gYW5zd2VyIGltbWVkaWF0ZWx5LCBvbmx5IGFkdmlzYWJsZSB3aGVuIHNvbWV0aGluZyB1cHN0cmVhbSBpcyBsaW1pdGluZyB0aGUgcmVxdWVzdCByYXRlLidcbiAgICB9LFxuICAgIGFjcXVpcmVUaW1lb3V0OiB7XG4gICAgICB2YWx1ZTogNTAwMCxcbiAgICAgIHR5cGU6ICdudW1iZXInLFxuICAgICAgZW52TGluazogJ1BPT0xfQUNRVUlSRV9USU1FT1VUJyxcbiAgICAgIGRlc2NyaXB0aW9uOlxuICAgICAgICAnVGhlIGR1cmF0aW9uLCBpbiBtaWxsaXNlY29uZHMsIHRvIHdhaXQgZm9yIGFjcXVpcmluZyBhIHJlc291cmNlLidcbiAgICB9LFxuICAgIGNyZWF0ZVRpbWVvdXQ6IHtcbiAgICAgIHZhbHVlOiA1MDAwLFxuICAgICAgdHlwZTogJ251bWJlcicsXG4gICAgICBlbnZMaW5rOiAnUE9PTF9DUkVBVEVfVElNRU9VVCcsXG4gICAgICBkZXNjcmlwdGlvbjpcbiAgICAgICAgJ1RoZSBkdXJhdGlvbiwgaW4gbWlsbGlzZWNvbmRzLCB0byB3YWl0IGZvciBjcmVhdGluZyBhIHJlc291cmNlLidcbiAgICB9LFxuICAgIGRlc3Ryb3lUaW1lb3V0OiB7XG4gICAgICB2YWx1ZTogNTAwMCxcbiAgICAgIHR5cGU6ICdudW1iZXInLFxuICAgICAgZW52TGluazogJ1BPT0xfREVTVFJPWV9USU1FT1VUJyxcbiAgICAgIGRlc2NyaXB0aW9uOlxuICAgICAgICAnVGhlIGR1cmF0aW9uLCBpbiBtaWxsaXNlY29uZHMsIHRvIHdhaXQgZm9yIGRlc3Ryb3lpbmcgYSByZXNvdXJjZS4nXG4gICAgfSxcbiAgICBpZGxlVGltZW91dDoge1xuICAgICAgdmFsdWU6IDMwMDAwLFxuICAgICAgdHlwZTogJ251bWJlcicsXG4gICAgICBlbnZMaW5rOiAnUE9PTF9JRExFX1RJTUVPVVQnLFxuICAgICAgZGVzY3JpcHRpb246XG4gICAgICAgICdUaGUgZHVyYXRpb24sIGluIG1pbGxpc2Vjb25kcywgYWZ0ZXIgd2hpY2ggYW4gaWRsZSByZXNvdXJjZSBpcyBkZXN0cm95ZWQuJ1xuICAgIH0sXG4gICAgY3JlYXRlUmV0cnlJbnRlcnZhbDoge1xuICAgICAgdmFsdWU6IDIwMCxcbiAgICAgIHR5cGU6ICdudW1iZXInLFxuICAgICAgZW52TGluazogJ1BPT0xfQ1JFQVRFX1JFVFJZX0lOVEVSVkFMJyxcbiAgICAgIGRlc2NyaXB0aW9uOlxuICAgICAgICAnVGhlIGR1cmF0aW9uLCBpbiBtaWxsaXNlY29uZHMsIHRvIHdhaXQgYmVmb3JlIHJldHJ5aW5nIHRoZSBjcmVhdGUgcHJvY2VzcyBpbiBjYXNlIG9mIGEgZmFpbHVyZS4nXG4gICAgfSxcbiAgICByZWFwZXJJbnRlcnZhbDoge1xuICAgICAgdmFsdWU6IDEwMDAsXG4gICAgICB0eXBlOiAnbnVtYmVyJyxcbiAgICAgIGVudkxpbms6ICdQT09MX1JFQVBFUl9JTlRFUlZBTCcsXG4gICAgICBkZXNjcmlwdGlvbjpcbiAgICAgICAgJ1RoZSBkdXJhdGlvbiwgaW4gbWlsbGlzZWNvbmRzLCBhZnRlciB3aGljaCB0aGUgY2hlY2sgZm9yIGlkbGUgcmVzb3VyY2VzIHRvIGRlc3Ryb3kgaXMgdHJpZ2dlcmVkLidcbiAgICB9LFxuICAgIGJlbmNobWFya2luZzoge1xuICAgICAgdmFsdWU6IGZhbHNlLFxuICAgICAgdHlwZTogJ2Jvb2xlYW4nLFxuICAgICAgZW52TGluazogJ1BPT0xfQkVOQ0hNQVJLSU5HJyxcbiAgICAgIGNsaU5hbWU6ICdwb29sQmVuY2htYXJraW5nJyxcbiAgICAgIGRlc2NyaXB0aW9uOlxuICAgICAgICAnSW5kaWNhdGUgd2hldGhlciB0byBzaG93IHN0YXRpc3RpY3MgZm9yIHRoZSBwb29sIG9mIHJlc291cmNlcyBvciBub3QuJ1xuICAgIH1cbiAgfSxcbiAgbG9nZ2luZzoge1xuICAgIGxldmVsOiB7XG4gICAgICB2YWx1ZTogNCxcbiAgICAgIHR5cGU6ICdudW1iZXInLFxuICAgICAgZW52TGluazogJ0xPR0dJTkdfTEVWRUwnLFxuICAgICAgY2xpTmFtZTogJ2xvZ0xldmVsJyxcbiAgICAgIGRlc2NyaXB0aW9uOiAnVGhlIGxvZ2dpbmcgbGV2ZWwgdG8gYmUgdXNlZC4nXG4gICAgfSxcbiAgICBmaWxlOiB7XG4gICAgICB2YWx1ZTogJ2hpZ2hjaGFydHMtZXhwb3J0LXNlcnZlci5sb2cnLFxuICAgICAgdHlwZTogJ3N0cmluZycsXG4gICAgICBlbnZMaW5rOiAnTE9HR0lOR19GSUxFJyxcbiAgICAgIGNsaU5hbWU6ICdsb2dGaWxlJyxcbiAgICAgIGRlc2NyaXB0aW9uOlxuICAgICAgICAnVGhlIG5hbWUgb2YgYSBsb2cgZmlsZS4gVGhlIGBsb2dUb0ZpbGVgIGFuZCBgbG9nRGVzdGAgb3B0aW9ucyBhbHNvIG5lZWQgdG8gYmUgc2V0IHRvIGVuYWJsZSBmaWxlIGxvZ2dpbmcuJ1xuICAgIH0sXG4gICAgZGVzdDoge1xuICAgICAgdmFsdWU6ICdsb2cvJyxcbiAgICAgIHR5cGU6ICdzdHJpbmcnLFxuICAgICAgZW52TGluazogJ0xPR0dJTkdfREVTVCcsXG4gICAgICBjbGlOYW1lOiAnbG9nRGVzdCcsXG4gICAgICBkZXNjcmlwdGlvbjpcbiAgICAgICAgJ1RoZSBwYXRoIHRvIHN0b3JlIGxvZyBmaWxlcy4gVGhlIGBsb2dUb0ZpbGVgIG9wdGlvbiBhbHNvIG5lZWRzIHRvIGJlIHNldCB0byBlbmFibGUgZmlsZSBsb2dnaW5nLidcbiAgICB9LFxuICAgIHRvQ29uc29sZToge1xuICAgICAgdmFsdWU6IHRydWUsXG4gICAgICB0eXBlOiAnYm9vbGVhbicsXG4gICAgICBlbnZMaW5rOiAnTE9HR0lOR19UT19DT05TT0xFJyxcbiAgICAgIGNsaU5hbWU6ICdsb2dUb0NvbnNvbGUnLFxuICAgICAgZGVzY3JpcHRpb246ICdFbmFibGVzIG9yIGRpc2FibGVzIHNob3dpbmcgbG9ncyBpbiB0aGUgY29uc29sZS4nXG4gICAgfSxcbiAgICB0b0ZpbGU6IHtcbiAgICAgIHZhbHVlOiB0cnVlLFxuICAgICAgdHlwZTogJ2Jvb2xlYW4nLFxuICAgICAgZW52TGluazogJ0xPR0dJTkdfVE9fRklMRScsXG4gICAgICBjbGlOYW1lOiAnbG9nVG9GaWxlJyxcbiAgICAgIGRlc2NyaXB0aW9uOlxuICAgICAgICAnRW5hYmxlcyBvciBkaXNhYmxlcyBjcmVhdGlvbiBvZiB0aGUgbG9nIGRpcmVjdG9yeSBhbmQgc2F2aW5nIHRoZSBsb2cgaW50byBhIC5sb2cgZmlsZS4nXG4gICAgfVxuICB9LFxuICB1aToge1xuICAgIGVuYWJsZToge1xuICAgICAgdmFsdWU6IGZhbHNlLFxuICAgICAgdHlwZTogJ2Jvb2xlYW4nLFxuICAgICAgZW52TGluazogJ1VJX0VOQUJMRScsXG4gICAgICBjbGlOYW1lOiAnZW5hYmxlVWknLFxuICAgICAgZGVzY3JpcHRpb246XG4gICAgICAgICdFbmFibGVzIG9yIGRpc2FibGVzIHRoZSB1c2VyIGludGVyZmFjZSAoVUkpIGZvciB0aGUgZXhwb3J0IHNlcnZlci4nXG4gICAgfSxcbiAgICByb3V0ZToge1xuICAgICAgdmFsdWU6ICcvJyxcbiAgICAgIHR5cGU6ICdzdHJpbmcnLFxuICAgICAgZW52TGluazogJ1VJX1JPVVRFJyxcbiAgICAgIGNsaU5hbWU6ICd1aVJvdXRlJyxcbiAgICAgIGRlc2NyaXB0aW9uOlxuICAgICAgICAnVGhlIGVuZHBvaW50IHJvdXRlIHRvIHdoaWNoIHRoZSB1c2VyIGludGVyZmFjZSAoVUkpIHNob3VsZCBiZSBhdHRhY2hlZC4nXG4gICAgfVxuICB9LFxuICBvdGhlcjoge1xuICAgIG5vZGVFbnY6IHtcbiAgICAgIHZhbHVlOiAncHJvZHVjdGlvbicsXG4gICAgICB0eXBlOiAnc3RyaW5nJyxcbiAgICAgIGVudkxpbms6ICdPVEhFUl9OT0RFX0VOVicsXG4gICAgICBkZXNjcmlwdGlvbjogJ1RoZSB0eXBlIG9mIE5vZGUuanMgZW52aXJvbm1lbnQuJ1xuICAgIH0sXG4gICAgbGlzdGVuVG9Qcm9jZXNzRXhpdHM6IHtcbiAgICAgIHZhbHVlOiB0cnVlLFxuICAgICAgdHlwZTogJ2Jvb2xlYW4nLFxuICAgICAgZW52TGluazogJ09USEVSX0xJU1RFTl9UT19QUk9DRVNTX0VYSVRTJyxcbiAgICAgIGRlc2NyaXB0aW9uOiAnRGVjaWRlcyB3aGV0aGVyIG9yIG5vdCB0byBhdHRhY2ggcHJvY2Vzcy5leGl0IGhhbmRsZXJzLidcbiAgICB9LFxuICAgIG5vTG9nbzoge1xuICAgICAgdmFsdWU6IGZhbHNlLFxuICAgICAgdHlwZTogJ2Jvb2xlYW4nLFxuICAgICAgZW52TGluazogJ09USEVSX05PX0xPR08nLFxuICAgICAgZGVzY3JpcHRpb246XG4gICAgICAgICdTa2lwIHByaW50aW5nIHRoZSBsb2dvIG9uIGEgc3RhcnR1cC4gV2lsbCBiZSByZXBsYWNlZCBieSBhIHNpbXBsZSB0ZXh0LidcbiAgICB9LFxuICAgIGhhcmRSZXNldFBhZ2U6IHtcbiAgICAgIHZhbHVlOiBmYWxzZSxcbiAgICAgIHR5cGU6ICdib29sZWFuJyxcbiAgICAgIGVudkxpbms6ICdPVEhFUl9IQVJEX1JFU0VUX1BBR0UnLFxuICAgICAgZGVzY3JpcHRpb246ICdEZWNpZGVzIGlmIHRoZSBwYWdlIGNvbnRlbnQgc2hvdWxkIGJlIHJlc2V0IGVudGlyZWx5LidcbiAgICB9LFxuICAgIHNodXRkb3duRHJhaW5UaW1lb3V0OiB7XG4gICAgICB2YWx1ZTogMzAwMDAsXG4gICAgICB0eXBlOiAnbnVtYmVyJyxcbiAgICAgIGVudkxpbms6ICdPVEhFUl9TSFVURE9XTl9EUkFJTl9USU1FT1VUJyxcbiAgICAgIGRlc2NyaXB0aW9uOlxuICAgICAgICAnSG93IGxvbmcsIGluIG1pbGxpc2Vjb25kcywgdG8gbGV0IHJlcXVlc3RzIGFscmVhZHkgYmVpbmcgc2VydmVkIGZpbmlzaCBkdXJpbmcgYSBzaHV0ZG93biwgYmVmb3JlIHRoZSB3b3JrZXIgcG9vbCBpcyB0YWtlbiBhd2F5IGFuZCB0aGUgcHJvY2VzcyBleGl0cy4gU2V0IGl0IGFib3ZlIHRoZSBkZXJlZ2lzdHJhdGlvbiBkZWxheSBvZiBhbnl0aGluZyByb3V0aW5nIHRyYWZmaWMgdG8gdGhpcyBzZXJ2ZXIsIHNvIHRoYXQgaXQgaGFzIHN0b3BwZWQgc2VuZGluZyBiZWZvcmUgdGhlIHNlcnZlciBzdG9wcyBhbnN3ZXJpbmcuJ1xuICAgIH0sXG4gICAgYnJvd3NlclNoZWxsTW9kZToge1xuICAgICAgdmFsdWU6IHRydWUsXG4gICAgICB0eXBlOiAnYm9vbGVhbicsXG4gICAgICBlbnZMaW5rOiAnT1RIRVJfQlJPV1NFUl9TSEVMTF9NT0RFJyxcbiAgICAgIGRlc2NyaXB0aW9uOiAnRGVjaWRlcyBpZiB0aGUgYnJvd3NlciBydW5zIGluIHRoZSBzaGVsbCBtb2RlLidcbiAgICB9XG4gIH0sXG4gIGRlYnVnOiB7XG4gICAgZW5hYmxlOiB7XG4gICAgICB2YWx1ZTogZmFsc2UsXG4gICAgICB0eXBlOiAnYm9vbGVhbicsXG4gICAgICBlbnZMaW5rOiAnREVCVUdfRU5BQkxFJyxcbiAgICAgIGNsaU5hbWU6ICdlbmFibGVEZWJ1ZycsXG4gICAgICBkZXNjcmlwdGlvbjogJ0VuYWJsZXMgb3IgZGlzYWJsZXMgZGVidWcgbW9kZSBmb3IgdGhlIHVuZGVybHlpbmcgYnJvd3Nlci4nXG4gICAgfSxcbiAgICBoZWFkbGVzczoge1xuICAgICAgdmFsdWU6IHRydWUsXG4gICAgICB0eXBlOiAnYm9vbGVhbicsXG4gICAgICBlbnZMaW5rOiAnREVCVUdfSEVBRExFU1MnLFxuICAgICAgZGVzY3JpcHRpb246XG4gICAgICAgICdDb250cm9scyB0aGUgbW9kZSBpbiB3aGljaCB0aGUgYnJvd3NlciBpcyBsYXVuY2hlZCB3aGVuIGluIHRoZSBkZWJ1ZyBtb2RlLidcbiAgICB9LFxuICAgIGRldnRvb2xzOiB7XG4gICAgICB2YWx1ZTogZmFsc2UsXG4gICAgICB0eXBlOiAnYm9vbGVhbicsXG4gICAgICBlbnZMaW5rOiAnREVCVUdfREVWVE9PTFMnLFxuICAgICAgZGVzY3JpcHRpb246XG4gICAgICAgICdEZWNpZGVzIHdoZXRoZXIgdG8gZW5hYmxlIERldlRvb2xzIHdoZW4gdGhlIGJyb3dzZXIgaXMgaW4gYSBoZWFkZnVsIHN0YXRlLidcbiAgICB9LFxuICAgIGxpc3RlblRvQ29uc29sZToge1xuICAgICAgdmFsdWU6IGZhbHNlLFxuICAgICAgdHlwZTogJ2Jvb2xlYW4nLFxuICAgICAgZW52TGluazogJ0RFQlVHX0xJU1RFTl9UT19DT05TT0xFJyxcbiAgICAgIGRlc2NyaXB0aW9uOlxuICAgICAgICAnRGVjaWRlcyB3aGV0aGVyIHRvIGVuYWJsZSBhIGxpc3RlbmVyIGZvciBjb25zb2xlIG1lc3NhZ2VzIHNlbnQgZnJvbSB0aGUgYnJvd3Nlci4nXG4gICAgfSxcbiAgICBkdW1waW86IHtcbiAgICAgIHZhbHVlOiBmYWxzZSxcbiAgICAgIHR5cGU6ICdib29sZWFuJyxcbiAgICAgIGVudkxpbms6ICdERUJVR19EVU1QSU8nLFxuICAgICAgZGVzY3JpcHRpb246XG4gICAgICAgICdSZWRpcmVjdHMgYnJvd3NlciBwcm9jZXNzIHN0ZG91dCBhbmQgc3RkZXJyIHRvIHByb2Nlc3Muc3Rkb3V0IGFuZCBwcm9jZXNzLnN0ZGVyci4nXG4gICAgfSxcbiAgICBzbG93TW86IHtcbiAgICAgIHZhbHVlOiAwLFxuICAgICAgdHlwZTogJ251bWJlcicsXG4gICAgICBlbnZMaW5rOiAnREVCVUdfU0xPV19NTycsXG4gICAgICBkZXNjcmlwdGlvbjpcbiAgICAgICAgJ1Nsb3dzIGRvd24gUHVwcGV0ZWVyIG9wZXJhdGlvbnMgYnkgdGhlIHNwZWNpZmllZCBudW1iZXIgb2YgbWlsbGlzZWNvbmRzLidcbiAgICB9LFxuICAgIGRlYnVnZ2luZ1BvcnQ6IHtcbiAgICAgIHZhbHVlOiA5MjIyLFxuICAgICAgdHlwZTogJ251bWJlcicsXG4gICAgICBlbnZMaW5rOiAnREVCVUdfREVCVUdHSU5HX1BPUlQnLFxuICAgICAgZGVzY3JpcHRpb246ICdTcGVjaWZpZXMgdGhlIGRlYnVnZ2luZyBwb3J0LidcbiAgICB9XG4gIH1cbn07XG5cbi8vIFRoZSBjb25maWcgZGVzY3JpcHRpb25zIG9iamVjdCBmb3IgdGhlIHByb21wdHMgZnVuY3Rpb25hbGl0eS4gSXQgY29udGFpbnNcbi8vIGluZm9ybWF0aW9uIGxpa2U6XG4vLyAqIFR5cGUgb2YgYSBwcm9tcHRcbi8vICogTmFtZSBvZiBhbiBvcHRpb25cbi8vICogU2hvcnQgZGVzY3JpcHRpb24gb2YgYSBjaG9zZW4gb3B0aW9uXG4vLyAqIEluaXRpYWwgdmFsdWVcbmV4cG9ydCBjb25zdCBwcm9tcHRzQ29uZmlnID0ge1xuICBwdXBwZXRlZXI6IFtcbiAgICB7XG4gICAgICB0eXBlOiAnbGlzdCcsXG4gICAgICBuYW1lOiAnYXJncycsXG4gICAgICBtZXNzYWdlOiAnUHVwcGV0ZWVyIGFyZ3VtZW50cycsXG4gICAgICBpbml0aWFsOiBkZWZhdWx0Q29uZmlnLnB1cHBldGVlci5hcmdzLnZhbHVlLmpvaW4oJywnKSxcbiAgICAgIHNlcGFyYXRvcjogJywnXG4gICAgfVxuICBdLFxuICBoaWdoY2hhcnRzOiBbXG4gICAge1xuICAgICAgdHlwZTogJ3RleHQnLFxuICAgICAgbmFtZTogJ3ZlcnNpb24nLFxuICAgICAgbWVzc2FnZTogJ0hpZ2hjaGFydHMgdmVyc2lvbicsXG4gICAgICBpbml0aWFsOiBkZWZhdWx0Q29uZmlnLmhpZ2hjaGFydHMudmVyc2lvbi52YWx1ZVxuICAgIH0sXG4gICAge1xuICAgICAgdHlwZTogJ3RleHQnLFxuICAgICAgbmFtZTogJ2NkblVSTCcsXG4gICAgICBtZXNzYWdlOiAnVGhlIFVSTCBvZiBDRE4nLFxuICAgICAgaW5pdGlhbDogZGVmYXVsdENvbmZpZy5oaWdoY2hhcnRzLmNkblVSTC52YWx1ZVxuICAgIH0sXG4gICAge1xuICAgICAgdHlwZTogJ3RvZ2dsZScsXG4gICAgICBuYW1lOiAndXNlTnBtJyxcbiAgICAgIG1lc3NhZ2U6ICdGbGFnIHRvIHVzZSBIaWdoY2hhcnRzIHNjcmlwdHMgZnJvbSBOUE0gcGFja2FnZScsXG4gICAgICBpbml0aWFsOiBkZWZhdWx0Q29uZmlnLmhpZ2hjaGFydHMudXNlTnBtLnZhbHVlXG4gICAgfSxcbiAgICB7XG4gICAgICB0eXBlOiAnbXVsdGlzZWxlY3QnLFxuICAgICAgbmFtZTogJ2NvcmVTY3JpcHRzJyxcbiAgICAgIG1lc3NhZ2U6ICdBdmFpbGFibGUgY29yZSBzY3JpcHRzJyxcbiAgICAgIGluc3RydWN0aW9uczogJ1NwYWNlOiBTZWxlY3Qgc3BlY2lmaWMsIEE6IFNlbGVjdCBhbGwsIEVudGVyOiBDb25maXJtLicsXG4gICAgICBjaG9pY2VzOiBkZWZhdWx0Q29uZmlnLmhpZ2hjaGFydHMuY29yZVNjcmlwdHMudmFsdWVcbiAgICB9LFxuICAgIHtcbiAgICAgIHR5cGU6ICdtdWx0aXNlbGVjdCcsXG4gICAgICBuYW1lOiAnbW9kdWxlU2NyaXB0cycsXG4gICAgICBtZXNzYWdlOiAnQXZhaWxhYmxlIG1vZHVsZSBzY3JpcHRzJyxcbiAgICAgIGluc3RydWN0aW9uczogJ1NwYWNlOiBTZWxlY3Qgc3BlY2lmaWMsIEE6IFNlbGVjdCBhbGwsIEVudGVyOiBDb25maXJtLicsXG4gICAgICBjaG9pY2VzOiBkZWZhdWx0Q29uZmlnLmhpZ2hjaGFydHMubW9kdWxlU2NyaXB0cy52YWx1ZVxuICAgIH0sXG4gICAge1xuICAgICAgdHlwZTogJ211bHRpc2VsZWN0JyxcbiAgICAgIG5hbWU6ICdpbmRpY2F0b3JTY3JpcHRzJyxcbiAgICAgIG1lc3NhZ2U6ICdBdmFpbGFibGUgaW5kaWNhdG9yIHNjcmlwdHMnLFxuICAgICAgaW5zdHJ1Y3Rpb25zOiAnU3BhY2U6IFNlbGVjdCBzcGVjaWZpYywgQTogU2VsZWN0IGFsbCwgRW50ZXI6IENvbmZpcm0uJyxcbiAgICAgIGNob2ljZXM6IGRlZmF1bHRDb25maWcuaGlnaGNoYXJ0cy5pbmRpY2F0b3JTY3JpcHRzLnZhbHVlXG4gICAgfSxcbiAgICB7XG4gICAgICB0eXBlOiAnbGlzdCcsXG4gICAgICBuYW1lOiAnY3VzdG9tU2NyaXB0cycsXG4gICAgICBtZXNzYWdlOiAnQ3VzdG9tIHNjcmlwdHMnLFxuICAgICAgaW5pdGlhbDogZGVmYXVsdENvbmZpZy5oaWdoY2hhcnRzLmN1c3RvbVNjcmlwdHMudmFsdWUuam9pbignLCcpLFxuICAgICAgc2VwYXJhdG9yOiAnLCdcbiAgICB9LFxuICAgIHtcbiAgICAgIHR5cGU6ICd0b2dnbGUnLFxuICAgICAgbmFtZTogJ2ZvcmNlRmV0Y2gnLFxuICAgICAgbWVzc2FnZTogJ0ZvcmNlIHJlLWZldGNoIHRoZSBzY3JpcHRzJyxcbiAgICAgIGluaXRpYWw6IGRlZmF1bHRDb25maWcuaGlnaGNoYXJ0cy5mb3JjZUZldGNoLnZhbHVlXG4gICAgfSxcbiAgICB7XG4gICAgICB0eXBlOiAndGV4dCcsXG4gICAgICBuYW1lOiAnY2FjaGVQYXRoJyxcbiAgICAgIG1lc3NhZ2U6ICdUaGUgcGF0aCB0byB0aGUgY2FjaGUgZGlyZWN0b3J5JyxcbiAgICAgIGluaXRpYWw6IGRlZmF1bHRDb25maWcuaGlnaGNoYXJ0cy5jYWNoZVBhdGgudmFsdWVcbiAgICB9XG4gIF0sXG4gIGV4cG9ydDogW1xuICAgIHtcbiAgICAgIHR5cGU6ICdzZWxlY3QnLFxuICAgICAgbmFtZTogJ3R5cGUnLFxuICAgICAgbWVzc2FnZTogJ1RoZSBkZWZhdWx0IGV4cG9ydCBmaWxlIHR5cGUnLFxuICAgICAgaGludDogYERlZmF1bHQ6ICR7ZGVmYXVsdENvbmZpZy5leHBvcnQudHlwZS52YWx1ZX1gLFxuICAgICAgaW5pdGlhbDogMCxcbiAgICAgIGNob2ljZXM6IFsncG5nJywgJ2pwZWcnLCAncGRmJywgJ3N2ZyddXG4gICAgfSxcbiAgICB7XG4gICAgICB0eXBlOiAnc2VsZWN0JyxcbiAgICAgIG5hbWU6ICdjb25zdHInLFxuICAgICAgbWVzc2FnZTogJ1RoZSBkZWZhdWx0IGNvbnN0cnVjdG9yIGZvciBIaWdoY2hhcnRzJyxcbiAgICAgIGhpbnQ6IGBEZWZhdWx0OiAke2RlZmF1bHRDb25maWcuZXhwb3J0LmNvbnN0ci52YWx1ZX1gLFxuICAgICAgaW5pdGlhbDogMCxcbiAgICAgIGNob2ljZXM6IFsnY2hhcnQnLCAnc3RvY2tDaGFydCcsICdtYXBDaGFydCcsICdnYW50dENoYXJ0J11cbiAgICB9LFxuICAgIHtcbiAgICAgIHR5cGU6ICdudW1iZXInLFxuICAgICAgbmFtZTogJ2RlZmF1bHRIZWlnaHQnLFxuICAgICAgbWVzc2FnZTogJ1RoZSBkZWZhdWx0IGZhbGxiYWNrIGhlaWdodCBvZiB0aGUgZXhwb3J0ZWQgY2hhcnQnLFxuICAgICAgaW5pdGlhbDogZGVmYXVsdENvbmZpZy5leHBvcnQuZGVmYXVsdEhlaWdodC52YWx1ZVxuICAgIH0sXG4gICAge1xuICAgICAgdHlwZTogJ251bWJlcicsXG4gICAgICBuYW1lOiAnZGVmYXVsdFdpZHRoJyxcbiAgICAgIG1lc3NhZ2U6ICdUaGUgZGVmYXVsdCBmYWxsYmFjayB3aWR0aCBvZiB0aGUgZXhwb3J0ZWQgY2hhcnQnLFxuICAgICAgaW5pdGlhbDogZGVmYXVsdENvbmZpZy5leHBvcnQuZGVmYXVsdFdpZHRoLnZhbHVlXG4gICAgfSxcbiAgICB7XG4gICAgICB0eXBlOiAnbnVtYmVyJyxcbiAgICAgIG5hbWU6ICdkZWZhdWx0U2NhbGUnLFxuICAgICAgbWVzc2FnZTogJ1RoZSBkZWZhdWx0IGZhbGxiYWNrIHNjYWxlIG9mIHRoZSBleHBvcnRlZCBjaGFydCcsXG4gICAgICBpbml0aWFsOiBkZWZhdWx0Q29uZmlnLmV4cG9ydC5kZWZhdWx0U2NhbGUudmFsdWUsXG4gICAgICBtaW46IDAuMSxcbiAgICAgIG1heDogNVxuICAgIH0sXG4gICAge1xuICAgICAgdHlwZTogJ251bWJlcicsXG4gICAgICBuYW1lOiAncmFzdGVyaXphdGlvblRpbWVvdXQnLFxuICAgICAgbWVzc2FnZTogJ1RoZSByZW5kZXJpbmcgd2VicGFnZSB0aW1lb3V0IGluIG1pbGxpc2Vjb25kcycsXG4gICAgICBpbml0aWFsOiBkZWZhdWx0Q29uZmlnLmV4cG9ydC5yYXN0ZXJpemF0aW9uVGltZW91dC52YWx1ZVxuICAgIH1cbiAgXSxcbiAgY3VzdG9tTG9naWM6IFtcbiAgICB7XG4gICAgICB0eXBlOiAndG9nZ2xlJyxcbiAgICAgIG5hbWU6ICdhbGxvd0NvZGVFeGVjdXRpb24nLFxuICAgICAgbWVzc2FnZTogJ0VuYWJsZSBleGVjdXRpb24gb2YgY3VzdG9tIGNvZGUnLFxuICAgICAgaW5pdGlhbDogZGVmYXVsdENvbmZpZy5jdXN0b21Mb2dpYy5hbGxvd0NvZGVFeGVjdXRpb24udmFsdWVcbiAgICB9LFxuICAgIHtcbiAgICAgIHR5cGU6ICd0b2dnbGUnLFxuICAgICAgbmFtZTogJ2FsbG93RmlsZVJlc291cmNlcycsXG4gICAgICBtZXNzYWdlOiAnRW5hYmxlIGZpbGUgcmVzb3VyY2VzJyxcbiAgICAgIGluaXRpYWw6IGRlZmF1bHRDb25maWcuY3VzdG9tTG9naWMuYWxsb3dGaWxlUmVzb3VyY2VzLnZhbHVlXG4gICAgfVxuICBdLFxuICBzZXJ2ZXI6IFtcbiAgICB7XG4gICAgICB0eXBlOiAndG9nZ2xlJyxcbiAgICAgIG5hbWU6ICdlbmFibGUnLFxuICAgICAgbWVzc2FnZTogJ1N0YXJ0cyB0aGUgc2VydmVyIG9uIDAuMC4wLjAnLFxuICAgICAgaW5pdGlhbDogZGVmYXVsdENvbmZpZy5zZXJ2ZXIuZW5hYmxlLnZhbHVlXG4gICAgfSxcbiAgICB7XG4gICAgICB0eXBlOiAndGV4dCcsXG4gICAgICBuYW1lOiAnaG9zdCcsXG4gICAgICBtZXNzYWdlOiAnU2VydmVyIGhvc3RuYW1lJyxcbiAgICAgIGluaXRpYWw6IGRlZmF1bHRDb25maWcuc2VydmVyLmhvc3QudmFsdWVcbiAgICB9LFxuICAgIHtcbiAgICAgIHR5cGU6ICdudW1iZXInLFxuICAgICAgbmFtZTogJ3BvcnQnLFxuICAgICAgbWVzc2FnZTogJ1NlcnZlciBwb3J0JyxcbiAgICAgIGluaXRpYWw6IGRlZmF1bHRDb25maWcuc2VydmVyLnBvcnQudmFsdWVcbiAgICB9LFxuICAgIHtcbiAgICAgIHR5cGU6ICd0b2dnbGUnLFxuICAgICAgbmFtZTogJ2JlbmNobWFya2luZycsXG4gICAgICBtZXNzYWdlOiAnRW5hYmxlIHNlcnZlciBiZW5jaG1hcmtpbmcnLFxuICAgICAgaW5pdGlhbDogZGVmYXVsdENvbmZpZy5zZXJ2ZXIuYmVuY2htYXJraW5nLnZhbHVlXG4gICAgfSxcbiAgICB7XG4gICAgICB0eXBlOiAndGV4dCcsXG4gICAgICBuYW1lOiAncHJveHkuaG9zdCcsXG4gICAgICBtZXNzYWdlOiAnVGhlIGhvc3Qgb2YgdGhlIHByb3h5IHNlcnZlciB0byB1c2UnLFxuICAgICAgaW5pdGlhbDogZGVmYXVsdENvbmZpZy5zZXJ2ZXIucHJveHkuaG9zdC52YWx1ZVxuICAgIH0sXG4gICAge1xuICAgICAgdHlwZTogJ251bWJlcicsXG4gICAgICBuYW1lOiAncHJveHkucG9ydCcsXG4gICAgICBtZXNzYWdlOiAnVGhlIHBvcnQgb2YgdGhlIHByb3h5IHNlcnZlciB0byB1c2UnLFxuICAgICAgaW5pdGlhbDogZGVmYXVsdENvbmZpZy5zZXJ2ZXIucHJveHkucG9ydC52YWx1ZVxuICAgIH0sXG4gICAge1xuICAgICAgdHlwZTogJ251bWJlcicsXG4gICAgICBuYW1lOiAncHJveHkudGltZW91dCcsXG4gICAgICBtZXNzYWdlOiAnVGhlIHRpbWVvdXQgZm9yIHRoZSBwcm94eSBzZXJ2ZXIgdG8gdXNlJyxcbiAgICAgIGluaXRpYWw6IGRlZmF1bHRDb25maWcuc2VydmVyLnByb3h5LnRpbWVvdXQudmFsdWVcbiAgICB9LFxuICAgIHtcbiAgICAgIHR5cGU6ICd0b2dnbGUnLFxuICAgICAgbmFtZTogJ3JhdGVMaW1pdGluZy5lbmFibGUnLFxuICAgICAgbWVzc2FnZTogJ0VuYWJsZSByYXRlIGxpbWl0aW5nJyxcbiAgICAgIGluaXRpYWw6IGRlZmF1bHRDb25maWcuc2VydmVyLnJhdGVMaW1pdGluZy5lbmFibGUudmFsdWVcbiAgICB9LFxuICAgIHtcbiAgICAgIHR5cGU6ICdudW1iZXInLFxuICAgICAgbmFtZTogJ3JhdGVMaW1pdGluZy5tYXhSZXF1ZXN0cycsXG4gICAgICBtZXNzYWdlOiAnVGhlIG1heGltdW0gcmVxdWVzdHMgYWxsb3dlZCBwZXIgbWludXRlJyxcbiAgICAgIGluaXRpYWw6IGRlZmF1bHRDb25maWcuc2VydmVyLnJhdGVMaW1pdGluZy5tYXhSZXF1ZXN0cy52YWx1ZVxuICAgIH0sXG4gICAge1xuICAgICAgdHlwZTogJ251bWJlcicsXG4gICAgICBuYW1lOiAncmF0ZUxpbWl0aW5nLndpbmRvdycsXG4gICAgICBtZXNzYWdlOiAnVGhlIHJhdGUtbGltaXRpbmcgdGltZSB3aW5kb3cgaW4gbWludXRlcycsXG4gICAgICBpbml0aWFsOiBkZWZhdWx0Q29uZmlnLnNlcnZlci5yYXRlTGltaXRpbmcud2luZG93LnZhbHVlXG4gICAgfSxcbiAgICB7XG4gICAgICB0eXBlOiAndG9nZ2xlJyxcbiAgICAgIG5hbWU6ICdyYXRlTGltaXRpbmcudHJ1c3RQcm94eScsXG4gICAgICBtZXNzYWdlOiAnU2V0IHRvIHRydWUgaWYgYmVoaW5kIGEgbG9hZCBiYWxhbmNlcicsXG4gICAgICBpbml0aWFsOiBkZWZhdWx0Q29uZmlnLnNlcnZlci5yYXRlTGltaXRpbmcudHJ1c3RQcm94eS52YWx1ZVxuICAgIH0sXG4gICAge1xuICAgICAgdHlwZTogJ3RleHQnLFxuICAgICAgbmFtZTogJ3JhdGVMaW1pdGluZy5za2lwS2V5JyxcbiAgICAgIG1lc3NhZ2U6XG4gICAgICAgICdBbGxvd3MgYnlwYXNzaW5nIHRoZSByYXRlIGxpbWl0ZXIgd2hlbiBwcm92aWRlZCB3aXRoIHRoZSBza2lwVG9rZW4gYXJndW1lbnQnLFxuICAgICAgaW5pdGlhbDogZGVmYXVsdENvbmZpZy5zZXJ2ZXIucmF0ZUxpbWl0aW5nLnNraXBLZXkudmFsdWVcbiAgICB9LFxuICAgIHtcbiAgICAgIHR5cGU6ICd0ZXh0JyxcbiAgICAgIG5hbWU6ICdyYXRlTGltaXRpbmcuc2tpcFRva2VuJyxcbiAgICAgIG1lc3NhZ2U6XG4gICAgICAgICdBbGxvd3MgYnlwYXNzaW5nIHRoZSByYXRlIGxpbWl0ZXIgd2hlbiBwcm92aWRlZCB3aXRoIHRoZSBza2lwS2V5IGFyZ3VtZW50JyxcbiAgICAgIGluaXRpYWw6IGRlZmF1bHRDb25maWcuc2VydmVyLnJhdGVMaW1pdGluZy5za2lwVG9rZW4udmFsdWVcbiAgICB9LFxuICAgIHtcbiAgICAgIHR5cGU6ICd0b2dnbGUnLFxuICAgICAgbmFtZTogJ3NzbC5lbmFibGUnLFxuICAgICAgbWVzc2FnZTogJ0VuYWJsZSBTU0wgcHJvdG9jb2wnLFxuICAgICAgaW5pdGlhbDogZGVmYXVsdENvbmZpZy5zZXJ2ZXIuc3NsLmVuYWJsZS52YWx1ZVxuICAgIH0sXG4gICAge1xuICAgICAgdHlwZTogJ3RvZ2dsZScsXG4gICAgICBuYW1lOiAnc3NsLmZvcmNlJyxcbiAgICAgIG1lc3NhZ2U6ICdGb3JjZSBzZXJ2aW5nIG9ubHkgb3ZlciBIVFRQUycsXG4gICAgICBpbml0aWFsOiBkZWZhdWx0Q29uZmlnLnNlcnZlci5zc2wuZm9yY2UudmFsdWVcbiAgICB9LFxuICAgIHtcbiAgICAgIHR5cGU6ICdudW1iZXInLFxuICAgICAgbmFtZTogJ3NzbC5wb3J0JyxcbiAgICAgIG1lc3NhZ2U6ICdTU0wgc2VydmVyIHBvcnQnLFxuICAgICAgaW5pdGlhbDogZGVmYXVsdENvbmZpZy5zZXJ2ZXIuc3NsLnBvcnQudmFsdWVcbiAgICB9LFxuICAgIHtcbiAgICAgIHR5cGU6ICd0ZXh0JyxcbiAgICAgIG5hbWU6ICdzc2wuY2VydFBhdGgnLFxuICAgICAgbWVzc2FnZTogJ1RoZSBwYXRoIHRvIGZpbmQgdGhlIFNTTCBjZXJ0aWZpY2F0ZS9rZXknLFxuICAgICAgaW5pdGlhbDogZGVmYXVsdENvbmZpZy5zZXJ2ZXIuc3NsLmNlcnRQYXRoLnZhbHVlXG4gICAgfVxuICBdLFxuICBwb29sOiBbXG4gICAge1xuICAgICAgdHlwZTogJ251bWJlcicsXG4gICAgICBuYW1lOiAnbWluV29ya2VycycsXG4gICAgICBtZXNzYWdlOiAnVGhlIGluaXRpYWwgbnVtYmVyIG9mIHdvcmtlcnMgdG8gc3Bhd24nLFxuICAgICAgaW5pdGlhbDogZGVmYXVsdENvbmZpZy5wb29sLm1pbldvcmtlcnMudmFsdWVcbiAgICB9LFxuICAgIHtcbiAgICAgIHR5cGU6ICdudW1iZXInLFxuICAgICAgbmFtZTogJ21heFdvcmtlcnMnLFxuICAgICAgbWVzc2FnZTogJ1RoZSBtYXhpbXVtIG51bWJlciBvZiB3b3JrZXJzIHRvIHNwYXduJyxcbiAgICAgIGluaXRpYWw6IGRlZmF1bHRDb25maWcucG9vbC5tYXhXb3JrZXJzLnZhbHVlXG4gICAgfSxcbiAgICB7XG4gICAgICB0eXBlOiAnbnVtYmVyJyxcbiAgICAgIG5hbWU6ICd3b3JrTGltaXQnLFxuICAgICAgbWVzc2FnZTpcbiAgICAgICAgJ1RoZSBwaWVjZXMgb2Ygd29yayB0aGF0IGNhbiBiZSBwZXJmb3JtZWQgYmVmb3JlIHJlc3RhcnRpbmcgYSBQdXBwZXRlZXIgcHJvY2VzcycsXG4gICAgICBpbml0aWFsOiBkZWZhdWx0Q29uZmlnLnBvb2wud29ya0xpbWl0LnZhbHVlXG4gICAgfSxcbiAgICB7XG4gICAgICB0eXBlOiAnbnVtYmVyJyxcbiAgICAgIG5hbWU6ICdxdWV1ZUxpbWl0JyxcbiAgICAgIG1lc3NhZ2U6XG4gICAgICAgICdUaGUgbWF4aW11bSBudW1iZXIgb2YgZXhwb3J0cyBhbGxvd2VkIHRvIHdhaXQgZm9yIGEgd29ya2VyLCBvciAwIHRvIGRlcml2ZSBpdCBmcm9tIG1heFdvcmtlcnMnLFxuICAgICAgaW5pdGlhbDogZGVmYXVsdENvbmZpZy5wb29sLnF1ZXVlTGltaXQudmFsdWVcbiAgICB9LFxuICAgIHtcbiAgICAgIHR5cGU6ICdudW1iZXInLFxuICAgICAgbmFtZTogJ3F1ZXVlUmVqZWN0RGVsYXknLFxuICAgICAgbWVzc2FnZTpcbiAgICAgICAgJ1RoZSBudW1iZXIgb2YgbWlsbGlzZWNvbmRzIHRvIHdhaXQgYmVmb3JlIHJlZnVzaW5nIGEgcmVxdWVzdCBiZWNhdXNlIHRoZSBxdWV1ZSBpcyBmdWxsJyxcbiAgICAgIGluaXRpYWw6IGRlZmF1bHRDb25maWcucG9vbC5xdWV1ZVJlamVjdERlbGF5LnZhbHVlXG4gICAgfSxcbiAgICB7XG4gICAgICB0eXBlOiAnbnVtYmVyJyxcbiAgICAgIG5hbWU6ICdhY3F1aXJlVGltZW91dCcsXG4gICAgICBtZXNzYWdlOiAnVGhlIG51bWJlciBvZiBtaWxsaXNlY29uZHMgdG8gd2FpdCBmb3IgYWNxdWlyaW5nIGEgcmVzb3VyY2UnLFxuICAgICAgaW5pdGlhbDogZGVmYXVsdENvbmZpZy5wb29sLmFjcXVpcmVUaW1lb3V0LnZhbHVlXG4gICAgfSxcbiAgICB7XG4gICAgICB0eXBlOiAnbnVtYmVyJyxcbiAgICAgIG5hbWU6ICdjcmVhdGVUaW1lb3V0JyxcbiAgICAgIG1lc3NhZ2U6ICdUaGUgbnVtYmVyIG9mIG1pbGxpc2Vjb25kcyB0byB3YWl0IGZvciBjcmVhdGluZyBhIHJlc291cmNlJyxcbiAgICAgIGluaXRpYWw6IGRlZmF1bHRDb25maWcucG9vbC5jcmVhdGVUaW1lb3V0LnZhbHVlXG4gICAgfSxcbiAgICB7XG4gICAgICB0eXBlOiAnbnVtYmVyJyxcbiAgICAgIG5hbWU6ICdkZXN0cm95VGltZW91dCcsXG4gICAgICBtZXNzYWdlOiAnVGhlIG51bWJlciBvZiBtaWxsaXNlY29uZHMgdG8gd2FpdCBmb3IgZGVzdHJveWluZyBhIHJlc291cmNlJyxcbiAgICAgIGluaXRpYWw6IGRlZmF1bHRDb25maWcucG9vbC5kZXN0cm95VGltZW91dC52YWx1ZVxuICAgIH0sXG4gICAge1xuICAgICAgdHlwZTogJ251bWJlcicsXG4gICAgICBuYW1lOiAnaWRsZVRpbWVvdXQnLFxuICAgICAgbWVzc2FnZTogJ1RoZSBudW1iZXIgb2YgbWlsbGlzZWNvbmRzIGFmdGVyIGFuIGlkbGUgcmVzb3VyY2UgaXMgZGVzdHJveWVkJyxcbiAgICAgIGluaXRpYWw6IGRlZmF1bHRDb25maWcucG9vbC5pZGxlVGltZW91dC52YWx1ZVxuICAgIH0sXG4gICAge1xuICAgICAgdHlwZTogJ251bWJlcicsXG4gICAgICBuYW1lOiAnY3JlYXRlUmV0cnlJbnRlcnZhbCcsXG4gICAgICBtZXNzYWdlOlxuICAgICAgICAnVGhlIHJldHJ5IGludGVydmFsIGluIG1pbGxpc2Vjb25kcyBhZnRlciBhIGNyZWF0ZSBwcm9jZXNzIGZhaWxzJyxcbiAgICAgIGluaXRpYWw6IGRlZmF1bHRDb25maWcucG9vbC5jcmVhdGVSZXRyeUludGVydmFsLnZhbHVlXG4gICAgfSxcbiAgICB7XG4gICAgICB0eXBlOiAnbnVtYmVyJyxcbiAgICAgIG5hbWU6ICdyZWFwZXJJbnRlcnZhbCcsXG4gICAgICBtZXNzYWdlOlxuICAgICAgICAnVGhlIHJlYXBlciBpbnRlcnZhbCBpbiBtaWxsaXNlY29uZHMgYWZ0ZXIgdHJpZ2dlcmluZyB0aGUgY2hlY2sgZm9yIGlkbGUgcmVzb3VyY2VzIHRvIGRlc3Ryb3knLFxuICAgICAgaW5pdGlhbDogZGVmYXVsdENvbmZpZy5wb29sLnJlYXBlckludGVydmFsLnZhbHVlXG4gICAgfSxcbiAgICB7XG4gICAgICB0eXBlOiAndG9nZ2xlJyxcbiAgICAgIG5hbWU6ICdiZW5jaG1hcmtpbmcnLFxuICAgICAgbWVzc2FnZTogJ0VuYWJsZSBiZW5jaG1hcmtpbmcgZm9yIGEgcmVzb3VyY2UgcG9vbCcsXG4gICAgICBpbml0aWFsOiBkZWZhdWx0Q29uZmlnLnBvb2wuYmVuY2htYXJraW5nLnZhbHVlXG4gICAgfVxuICBdLFxuICBsb2dnaW5nOiBbXG4gICAge1xuICAgICAgdHlwZTogJ251bWJlcicsXG4gICAgICBuYW1lOiAnbGV2ZWwnLFxuICAgICAgbWVzc2FnZTpcbiAgICAgICAgJ1RoZSBsb2cgbGV2ZWwgKDA6IHNpbGVudCwgMTogZXJyb3IsIDI6IHdhcm5pbmcsIDM6IG5vdGljZSwgNDogdmVyYm9zZSwgNTogYmVuY2htYXJrKScsXG4gICAgICBpbml0aWFsOiBkZWZhdWx0Q29uZmlnLmxvZ2dpbmcubGV2ZWwudmFsdWUsXG4gICAgICByb3VuZDogMCxcbiAgICAgIG1pbjogMCxcbiAgICAgIG1heDogNVxuICAgIH0sXG4gICAge1xuICAgICAgdHlwZTogJ3RleHQnLFxuICAgICAgbmFtZTogJ2ZpbGUnLFxuICAgICAgbWVzc2FnZTpcbiAgICAgICAgJ0EgbG9nIGZpbGUgbmFtZS4gU2V0IHdpdGggLS10b0ZpbGUgYW5kIC0tbG9nRGVzdCB0byBlbmFibGUgZmlsZSBsb2dnaW5nJyxcbiAgICAgIGluaXRpYWw6IGRlZmF1bHRDb25maWcubG9nZ2luZy5maWxlLnZhbHVlXG4gICAgfSxcbiAgICB7XG4gICAgICB0eXBlOiAndGV4dCcsXG4gICAgICBuYW1lOiAnZGVzdCcsXG4gICAgICBtZXNzYWdlOiAnVGhlIHBhdGggdG8gYSBsb2cgZmlsZSB3aGVuIHRoZSBmaWxlIGxvZ2dpbmcgaXMgZW5hYmxlZCcsXG4gICAgICBpbml0aWFsOiBkZWZhdWx0Q29uZmlnLmxvZ2dpbmcuZGVzdC52YWx1ZVxuICAgIH0sXG4gICAge1xuICAgICAgdHlwZTogJ3RvZ2dsZScsXG4gICAgICBuYW1lOiAndG9Db25zb2xlJyxcbiAgICAgIG1lc3NhZ2U6ICdFbmFibGUgbG9nZ2luZyB0byB0aGUgY29uc29sZScsXG4gICAgICBpbml0aWFsOiBkZWZhdWx0Q29uZmlnLmxvZ2dpbmcudG9Db25zb2xlLnZhbHVlXG4gICAgfSxcbiAgICB7XG4gICAgICB0eXBlOiAndG9nZ2xlJyxcbiAgICAgIG5hbWU6ICd0b0ZpbGUnLFxuICAgICAgbWVzc2FnZTogJ0VuYWJsZXMgbG9nZ2luZyB0byBhIGZpbGUnLFxuICAgICAgaW5pdGlhbDogZGVmYXVsdENvbmZpZy5sb2dnaW5nLnRvRmlsZS52YWx1ZVxuICAgIH1cbiAgXSxcbiAgdWk6IFtcbiAgICB7XG4gICAgICB0eXBlOiAndG9nZ2xlJyxcbiAgICAgIG5hbWU6ICdlbmFibGUnLFxuICAgICAgbWVzc2FnZTogJ0VuYWJsZSBVSSBmb3IgdGhlIGV4cG9ydCBzZXJ2ZXInLFxuICAgICAgaW5pdGlhbDogZGVmYXVsdENvbmZpZy51aS5lbmFibGUudmFsdWVcbiAgICB9LFxuICAgIHtcbiAgICAgIHR5cGU6ICd0ZXh0JyxcbiAgICAgIG5hbWU6ICdyb3V0ZScsXG4gICAgICBtZXNzYWdlOiAnQSByb3V0ZSB0byBhdHRhY2ggdGhlIFVJJyxcbiAgICAgIGluaXRpYWw6IGRlZmF1bHRDb25maWcudWkucm91dGUudmFsdWVcbiAgICB9XG4gIF0sXG4gIG90aGVyOiBbXG4gICAge1xuICAgICAgdHlwZTogJ3RleHQnLFxuICAgICAgbmFtZTogJ25vZGVFbnYnLFxuICAgICAgbWVzc2FnZTogJ1RoZSB0eXBlIG9mIE5vZGUuanMgZW52aXJvbm1lbnQnLFxuICAgICAgaW5pdGlhbDogZGVmYXVsdENvbmZpZy5vdGhlci5ub2RlRW52LnZhbHVlXG4gICAgfSxcbiAgICB7XG4gICAgICB0eXBlOiAndG9nZ2xlJyxcbiAgICAgIG5hbWU6ICdsaXN0ZW5Ub1Byb2Nlc3NFeGl0cycsXG4gICAgICBtZXNzYWdlOiAnU2V0IHRvIGZhbHNlIHRvIHNraXAgYXR0YWNoaW5nIHByb2Nlc3MuZXhpdCBoYW5kbGVycycsXG4gICAgICBpbml0aWFsOiBkZWZhdWx0Q29uZmlnLm90aGVyLmxpc3RlblRvUHJvY2Vzc0V4aXRzLnZhbHVlXG4gICAgfSxcbiAgICB7XG4gICAgICB0eXBlOiAndG9nZ2xlJyxcbiAgICAgIG5hbWU6ICdub0xvZ28nLFxuICAgICAgbWVzc2FnZTogJ1NraXAgcHJpbnRpbmcgdGhlIGxvZ28gb24gc3RhcnR1cC4gUmVwbGFjZWQgYnkgc2ltcGxlIHRleHQnLFxuICAgICAgaW5pdGlhbDogZGVmYXVsdENvbmZpZy5vdGhlci5ub0xvZ28udmFsdWVcbiAgICB9LFxuICAgIHtcbiAgICAgIHR5cGU6ICd0b2dnbGUnLFxuICAgICAgbmFtZTogJ2hhcmRSZXNldFBhZ2UnLFxuICAgICAgbWVzc2FnZTogJ0RlY2lkZXMgaWYgdGhlIHBhZ2UgY29udGVudCBzaG91bGQgYmUgcmVzZXQgZW50aXJlbHknLFxuICAgICAgaW5pdGlhbDogZGVmYXVsdENvbmZpZy5vdGhlci5oYXJkUmVzZXRQYWdlLnZhbHVlXG4gICAgfSxcbiAgICB7XG4gICAgICB0eXBlOiAndG9nZ2xlJyxcbiAgICAgIG5hbWU6ICdicm93c2VyU2hlbGxNb2RlJyxcbiAgICAgIG1lc3NhZ2U6ICdEZWNpZGVzIGlmIHRoZSBicm93c2VyIHJ1bnMgaW4gdGhlIHNoZWxsIG1vZGUnLFxuICAgICAgaW5pdGlhbDogZGVmYXVsdENvbmZpZy5vdGhlci5icm93c2VyU2hlbGxNb2RlLnZhbHVlXG4gICAgfVxuICBdLFxuICBkZWJ1ZzogW1xuICAgIHtcbiAgICAgIHR5cGU6ICd0b2dnbGUnLFxuICAgICAgbmFtZTogJ2VuYWJsZScsXG4gICAgICBtZXNzYWdlOiAnRW5hYmxlcyBkZWJ1ZyBtb2RlIGZvciB0aGUgYnJvd3NlciBpbnN0YW5jZScsXG4gICAgICBpbml0aWFsOiBkZWZhdWx0Q29uZmlnLmRlYnVnLmVuYWJsZS52YWx1ZVxuICAgIH0sXG4gICAge1xuICAgICAgdHlwZTogJ3RvZ2dsZScsXG4gICAgICBuYW1lOiAnaGVhZGxlc3MnLFxuICAgICAgbWVzc2FnZTogJ1RoZSBtb2RlIHNldHRpbmcgZm9yIHRoZSBicm93c2VyJyxcbiAgICAgIGluaXRpYWw6IGRlZmF1bHRDb25maWcuZGVidWcuaGVhZGxlc3MudmFsdWVcbiAgICB9LFxuICAgIHtcbiAgICAgIHR5cGU6ICd0b2dnbGUnLFxuICAgICAgbmFtZTogJ2RldnRvb2xzJyxcbiAgICAgIG1lc3NhZ2U6ICdUaGUgRGV2VG9vbHMgZm9yIHRoZSBoZWFkZnVsIGJyb3dzZXInLFxuICAgICAgaW5pdGlhbDogZGVmYXVsdENvbmZpZy5kZWJ1Zy5kZXZ0b29scy52YWx1ZVxuICAgIH0sXG4gICAge1xuICAgICAgdHlwZTogJ3RvZ2dsZScsXG4gICAgICBuYW1lOiAnbGlzdGVuVG9Db25zb2xlJyxcbiAgICAgIG1lc3NhZ2U6ICdUaGUgZXZlbnQgbGlzdGVuZXIgZm9yIGNvbnNvbGUgbWVzc2FnZXMgZnJvbSB0aGUgYnJvd3NlcicsXG4gICAgICBpbml0aWFsOiBkZWZhdWx0Q29uZmlnLmRlYnVnLmxpc3RlblRvQ29uc29sZS52YWx1ZVxuICAgIH0sXG4gICAge1xuICAgICAgdHlwZTogJ3RvZ2dsZScsXG4gICAgICBuYW1lOiAnZHVtcGlvJyxcbiAgICAgIG1lc3NhZ2U6ICdSZWRpcmVjdHMgdGhlIGJyb3dzZXIgc3Rkb3V0IGFuZCBzdGRlcnIgdG8gTm9kZUpTIHByb2Nlc3MnLFxuICAgICAgaW5pdGlhbDogZGVmYXVsdENvbmZpZy5kZWJ1Zy5kdW1waW8udmFsdWVcbiAgICB9LFxuICAgIHtcbiAgICAgIHR5cGU6ICdudW1iZXInLFxuICAgICAgbmFtZTogJ3Nsb3dNbycsXG4gICAgICBtZXNzYWdlOiAnUHVwcGV0ZWVyIG9wZXJhdGlvbnMgc2xvdyBkb3duIGluIG1pbGxpc2Vjb25kcycsXG4gICAgICBpbml0aWFsOiBkZWZhdWx0Q29uZmlnLmRlYnVnLnNsb3dNby52YWx1ZVxuICAgIH0sXG4gICAge1xuICAgICAgdHlwZTogJ251bWJlcicsXG4gICAgICBuYW1lOiAnZGVidWdnaW5nUG9ydCcsXG4gICAgICBtZXNzYWdlOiAnVGhlIHBvcnQgbnVtYmVyIGZvciBkZWJ1Z2dpbmcnLFxuICAgICAgaW5pdGlhbDogZGVmYXVsdENvbmZpZy5kZWJ1Zy5kZWJ1Z2dpbmdQb3J0LnZhbHVlXG4gICAgfVxuICBdXG59O1xuXG4vLyBBYnNvbHV0ZSBwcm9wcyB0aGF0LCBpbiBjYXNlIG9mIG1lcmdpbmcgcmVjdXJzaXZlbHksIG5lZWQgdG8gYmUgZm9yY2UgbWVyZ2VkXG5leHBvcnQgY29uc3QgYWJzb2x1dGVQcm9wcyA9IFtcbiAgJ29wdGlvbnMnLFxuICAnZ2xvYmFsT3B0aW9ucycsXG4gICd0aGVtZU9wdGlvbnMnLFxuICAncmVzb3VyY2VzJyxcbiAgJ3BheWxvYWQnXG5dO1xuXG4vLyBBcmd1bWVudCBuZXN0aW5nIGxldmVsIG9mIGFsbCBleHBvcnQgc2VydmVyIG9wdGlvbnNcbmV4cG9ydCBjb25zdCBuZXN0ZWRBcmdzID0ge307XG5cbi8qKlxuICogUmVjdXJzaXZlbHkgY3JlYXRlcyBhIGNoYWluIG9mIG5lc3RlZCBhcmd1bWVudHMgZnJvbSBhbiBvYmplY3QuXG4gKlxuICogQHBhcmFtIHtPYmplY3R9IG9iaiAtIFRoZSBvYmplY3QgY29udGFpbmluZyBuZXN0ZWQgYXJndW1lbnRzLlxuICogQHBhcmFtIHtzdHJpbmd9IHByb3BDaGFpbiAtIFRoZSBjdXJyZW50IGNoYWluIG9mIG5lc3RlZCBwcm9wZXJ0aWVzXG4gKiAodXNlZCBpbnRlcm5hbGx5IGR1cmluZyByZWN1cnNpb24pLlxuICovXG5jb25zdCBjcmVhdGVOZXN0ZWRBcmdzID0gKG9iaiwgcHJvcENoYWluID0gJycpID0+IHtcbiAgT2JqZWN0LmtleXMob2JqKS5mb3JFYWNoKChrKSA9PiB7XG4gICAgaWYgKCFbJ3B1cHBldGVlcicsICdoaWdoY2hhcnRzJ10uaW5jbHVkZXMoaykpIHtcbiAgICAgIGNvbnN0IGVudHJ5ID0gb2JqW2tdO1xuICAgICAgaWYgKHR5cGVvZiBlbnRyeS52YWx1ZSA9PT0gJ3VuZGVmaW5lZCcpIHtcbiAgICAgICAgLy8gR28gZGVlcGVyIGluIHRoZSBuZXN0ZWQgYXJndW1lbnRzXG4gICAgICAgIGNyZWF0ZU5lc3RlZEFyZ3MoZW50cnksIGAke3Byb3BDaGFpbn0uJHtrfWApO1xuICAgICAgfSBlbHNlIHtcbiAgICAgICAgLy8gQ3JlYXRlIHRoZSBjaGFpbiBvZiBuZXN0ZWQgYXJndW1lbnRzXG4gICAgICAgIG5lc3RlZEFyZ3NbZW50cnkuY2xpTmFtZSB8fCBrXSA9IGAke3Byb3BDaGFpbn0uJHtrfWAuc3Vic3RyaW5nKDEpO1xuXG4gICAgICAgIC8vIFN1cHBvcnQgZm9yIHRoZSBsZWdhY3ksIFBoYW50b21KUyBwcm9wZXJ0aWVzIG5hbWVzXG4gICAgICAgIGlmIChlbnRyeS5sZWdhY3lOYW1lICE9PSB1bmRlZmluZWQpIHtcbiAgICAgICAgICBuZXN0ZWRBcmdzW2VudHJ5LmxlZ2FjeU5hbWVdID0gYCR7cHJvcENoYWlufS4ke2t9YC5zdWJzdHJpbmcoMSk7XG4gICAgICAgIH1cbiAgICAgIH1cbiAgICB9XG4gIH0pO1xufTtcblxuY3JlYXRlTmVzdGVkQXJncyhkZWZhdWx0Q29uZmlnKTtcbiIsIi8qKlxuICogQGZpbGVvdmVydmlld1xuICogVGhpcyBmaWxlIGlzIHJlc3BvbnNpYmxlIGZvciBwYXJzaW5nIHRoZSBlbnZpcm9ubWVudCB2YXJpYWJsZXMgd2l0aCB0aGUgJ3pvZCdcbiAqIGxpYnJhcnkuIFRoZSBwYXJzZWQgZW52aXJvbm1lbnQgdmFyaWFibGVzIGFyZSB0aGVuIGV4cG9ydGVkIHRvIGJlIHVzZWRcbiAqIGluIHRoZSBhcHBsaWNhdGlvbiBhcyBcImVudnNcIi4gV2Ugc2hvdWxkIG5vdCB1c2UgcHJvY2Vzcy5lbnYgZGlyZWN0bHlcbiAqIGluIHRoZSBhcHBsaWNhdGlvbiBhcyB0aGVzZSB3b3VsZCBub3QgYmUgcGFyc2VkIHByb3Blcmx5LlxuICpcbiAqIFRoZSBlbnZpcm9ubWVudCB2YXJpYWJsZXMgYXJlIHBhcnNlZCBhbmQgdmFsaWRhdGVkIG9ubHkgb25jZSB3aGVuXG4gKiB0aGUgYXBwbGljYXRpb24gc3RhcnRzLiBXZSBzaG91bGQgd3JpdGUgYSBjdXN0b20gdmFsaWRhdG9yIG9yIGEgdHJhbnNmb3JtZXJcbiAqIGZvciBlYWNoIG9mIHRoZSBvcHRpb25zLlxuICovXG5cbmltcG9ydCBkb3RlbnYgZnJvbSAnZG90ZW52JztcbmltcG9ydCB7IHogfSBmcm9tICd6b2QnO1xuXG5pbXBvcnQgeyBzY3JpcHRzTmFtZXMgfSBmcm9tICcuL3NjaGVtYXMvY29uZmlnLmpzJztcblxuLy8gTG9hZCAuZW52IGludG8gZW52aXJvbm1lbnQgdmFyaWFibGVzLlxuLy9cbi8vIHF1aWV0OiB0cnVlIHN1cHByZXNzZXMgdGhlIHN1bW1hcnkgZG90ZW52IHByaW50cyBmcm9tIHYxNyBvbndhcmRzLiBUaGlzIHByb2Nlc3Ncbi8vIHdyaXRlcyBzdHJ1Y3R1cmVkIGxvZyBsaW5lcyBhbmQgaXRzIG91dHB1dCBpcyByZWFkIGJ5IG90aGVyIHRvb2xpbmcsIHNvIGFuXG4vLyB1bnNvbGljaXRlZCBiYW5uZXIgb24gc3Rkb3V0IGlzIG5vaXNlLlxuZG90ZW52LmNvbmZpZyh7IHF1aWV0OiB0cnVlIH0pO1xuXG4vLyBPYmplY3Qgd2l0aCBjdXN0b20gdmFsaWRhdG9ycyBhbmQgdHJhbnNmb3JtZXJzLCB0byBhdm9pZCByZXBldGl0aW9uXG4vLyBpbiB0aGUgQ29uZmlnIG9iamVjdFxuY29uc3QgdiA9IHtcbiAgLy8gU3BsaXRzIHN0cmluZyB2YWx1ZSBpbnRvIGVsZW1lbnRzIGluIGFuIGFycmF5LCB0cmltcyBldmVyeSBlbGVtZW50LCBjaGVja3NcbiAgLy8gaWYgYW4gYXJyYXkgaXMgY29ycmVjdCwgaWYgaXQgaXMgZW1wdHksIGFuZCBpZiBpdCBpcywgcmV0dXJucyB1bmRlZmluZWRcbiAgYXJyYXk6IChmaWx0ZXJBcnJheSkgPT5cbiAgICB6XG4gICAgICAuc3RyaW5nKClcbiAgICAgIC50cmFuc2Zvcm0oKHZhbHVlKSA9PlxuICAgICAgICB2YWx1ZVxuICAgICAgICAgIC5zcGxpdCgnLCcpXG4gICAgICAgICAgLm1hcCgodmFsdWUpID0+IHZhbHVlLnRyaW0oKSlcbiAgICAgICAgICAuZmlsdGVyKCh2YWx1ZSkgPT4gZmlsdGVyQXJyYXkuaW5jbHVkZXModmFsdWUpKVxuICAgICAgKVxuICAgICAgLnRyYW5zZm9ybSgodmFsdWUpID0+ICh2YWx1ZS5sZW5ndGggPyB2YWx1ZSA6IHVuZGVmaW5lZCkpLFxuXG4gIC8vIEFsbG93cyBvbmx5IHRydWUsIGZhbHNlIGFuZCBjb3JyZWN0bHkgcGFyc2UgdGhlIHZhbHVlIHRvIGJvb2xlYW5cbiAgLy8gb3Igbm8gdmFsdWUgaW4gd2hpY2ggY2FzZSB0aGUgcmV0dXJuZWQgdmFsdWUgd2lsbCBiZSB1bmRlZmluZWRcbiAgYm9vbGVhbjogKCkgPT5cbiAgICB6XG4gICAgICAuZW51bShbJ3RydWUnLCAnZmFsc2UnLCAnJ10pXG4gICAgICAudHJhbnNmb3JtKCh2YWx1ZSkgPT4gKHZhbHVlICE9PSAnJyA/IHZhbHVlID09PSAndHJ1ZScgOiB1bmRlZmluZWQpKSxcblxuICAvLyBBbGxvd3MgcGFzc2VkIHZhbHVlcyBvciBubyB2YWx1ZSBpbiB3aGljaCBjYXNlIHRoZSByZXR1cm5lZCB2YWx1ZSB3aWxsXG4gIC8vIGJlIHVuZGVmaW5lZFxuICBlbnVtOiAodmFsdWVzKSA9PlxuICAgIHpcbiAgICAgIC5lbnVtKFsuLi52YWx1ZXMsICcnXSlcbiAgICAgIC50cmFuc2Zvcm0oKHZhbHVlKSA9PiAodmFsdWUgIT09ICcnID8gdmFsdWUgOiB1bmRlZmluZWQpKSxcblxuICAvLyBUcmltcyB0aGUgc3RyaW5nIHZhbHVlIGFuZCBjaGVja3MgaWYgaXQgaXMgZW1wdHkgb3IgY29udGFpbnMgc3RyaW5naWZpZWRcbiAgLy8gdmFsdWVzIHN1Y2ggYXMgZmFsc2UsIHVuZGVmaW5lZCwgbnVsbCwgTmFOLCBpZiBpdCBkb2VzLCByZXR1cm5zIHVuZGVmaW5lZFxuICBzdHJpbmc6ICgpID0+XG4gICAgelxuICAgICAgLnN0cmluZygpXG4gICAgICAudHJpbSgpXG4gICAgICAucmVmaW5lKFxuICAgICAgICAodmFsdWUpID0+XG4gICAgICAgICAgIVsnZmFsc2UnLCAndW5kZWZpbmVkJywgJ251bGwnLCAnTmFOJ10uaW5jbHVkZXModmFsdWUpIHx8XG4gICAgICAgICAgdmFsdWUgPT09ICcnLFxuICAgICAgICB7XG4gICAgICAgICAgZXJyb3I6IChpc3N1ZSkgPT5cbiAgICAgICAgICAgIGBUaGUgc3RyaW5nIGNvbnRhaW5zIGZvcmJpZGRlbiB2YWx1ZXMsIHJlY2VpdmVkICcke2lzc3VlLmlucHV0fSdgXG4gICAgICAgIH1cbiAgICAgIClcbiAgICAgIC50cmFuc2Zvcm0oKHZhbHVlKSA9PiAodmFsdWUgIT09ICcnID8gdmFsdWUgOiB1bmRlZmluZWQpKSxcblxuICAvLyBDaGVja3MgaWYgdGhlIHN0cmluZyBpcyBhIHZhbGlkIHBhdGggZGlyZWN0b3J5IChwYXRoIGZvcm1hdClcbiAgcGF0aDogKCkgPT5cbiAgICB6XG4gICAgICAuc3RyaW5nKClcbiAgICAgIC50cmltKClcbiAgICAgIC5yZWZpbmUoXG4gICAgICAgICh2YWx1ZSkgPT4ge1xuICAgICAgICAgIC8vIFNpbXBsaWZpZWQgcmVnZXggdG8gbWF0Y2ggYm90aCBhYnNvbHV0ZSBhbmQgcmVsYXRpdmUgcGF0aHNcbiAgICAgICAgICByZXR1cm4gL14oXFwuXFwvfFxcLlxcLlxcL3xcXC98W2EtekEtWl06XFxcXHxbYS16QS1aXTpcXC8pPygoPzpbXFx3LV0rKVtcXFxcL10/KSskLy50ZXN0KFxuICAgICAgICAgICAgdmFsdWVcbiAgICAgICAgICApO1xuICAgICAgICB9LFxuICAgICAgICB7IGVycm9yOiAnVGhlIHN0cmluZyBpcyBhbiBpbnZhbGlkIHBhdGggZGlyZWN0b3J5IHN0cmluZy4nIH1cbiAgICAgICksXG5cbiAgLy8gQWxsb3dzIHBvc2l0aXZlIG51bWJlcnMgb3Igbm8gdmFsdWUgaW4gd2hpY2ggY2FzZSB0aGUgcmV0dXJuZWQgdmFsdWUgd2lsbFxuICAvLyBiZSB1bmRlZmluZWRcbiAgcG9zaXRpdmVOdW06ICgpID0+XG4gICAgelxuICAgICAgLnN0cmluZygpXG4gICAgICAudHJpbSgpXG4gICAgICAucmVmaW5lKFxuICAgICAgICAodmFsdWUpID0+XG4gICAgICAgICAgdmFsdWUgPT09ICcnIHx8ICghaXNOYU4ocGFyc2VGbG9hdCh2YWx1ZSkpICYmIHBhcnNlRmxvYXQodmFsdWUpID4gMCksXG4gICAgICAgIHtcbiAgICAgICAgICBlcnJvcjogKGlzc3VlKSA9PlxuICAgICAgICAgICAgYFRoZSB2YWx1ZSBtdXN0IGJlIG51bWVyaWMgYW5kIHBvc2l0aXZlLCByZWNlaXZlZCAnJHtpc3N1ZS5pbnB1dH0nYFxuICAgICAgICB9XG4gICAgICApXG4gICAgICAudHJhbnNmb3JtKCh2YWx1ZSkgPT4gKHZhbHVlICE9PSAnJyA/IHBhcnNlRmxvYXQodmFsdWUpIDogdW5kZWZpbmVkKSksXG5cbiAgLy8gQWxsb3dzIG5vbi1uZWdhdGl2ZSBudW1iZXJzIG9yIG5vIHZhbHVlIGluIHdoaWNoIGNhc2UgdGhlIHJldHVybmVkIHZhbHVlXG4gIC8vIHdpbGwgYmUgdW5kZWZpbmVkXG4gIG5vbk5lZ2F0aXZlTnVtOiAoKSA9PlxuICAgIHpcbiAgICAgIC5zdHJpbmcoKVxuICAgICAgLnRyaW0oKVxuICAgICAgLnJlZmluZShcbiAgICAgICAgKHZhbHVlKSA9PlxuICAgICAgICAgIHZhbHVlID09PSAnJyB8fCAoIWlzTmFOKHBhcnNlRmxvYXQodmFsdWUpKSAmJiBwYXJzZUZsb2F0KHZhbHVlKSA+PSAwKSxcbiAgICAgICAge1xuICAgICAgICAgIGVycm9yOiAoaXNzdWUpID0+XG4gICAgICAgICAgICBgVGhlIHZhbHVlIG11c3QgYmUgbnVtZXJpYyBhbmQgbm9uLW5lZ2F0aXZlLCByZWNlaXZlZCAnJHtpc3N1ZS5pbnB1dH0nYFxuICAgICAgICB9XG4gICAgICApXG4gICAgICAudHJhbnNmb3JtKCh2YWx1ZSkgPT4gKHZhbHVlICE9PSAnJyA/IHBhcnNlRmxvYXQodmFsdWUpIDogdW5kZWZpbmVkKSlcbn07XG5cbmV4cG9ydCBjb25zdCBDb25maWcgPSB6Lm9iamVjdCh7XG4gIC8vIHB1cHBldGVlclxuICBQVVBQRVRFRVJfVEVNUF9ESVI6IHYucGF0aCgpLFxuICBQVVBQRVRFRVJfTEFVTkNIX1JFVFJZX1dJTkRPVzogdi5ub25OZWdhdGl2ZU51bSgpLFxuXG4gIC8vIGhpZ2hjaGFydHNcbiAgSElHSENIQVJUU19WRVJTSU9OOiB6XG4gICAgLnN0cmluZygpXG4gICAgLnRyaW0oKVxuICAgIC5yZWZpbmUoXG4gICAgICAodmFsdWUpID0+IC9eKGxhdGVzdHxcXGQrKFxcLlxcZCspezAsMn0pJC8udGVzdCh2YWx1ZSkgfHwgdmFsdWUgPT09ICcnLFxuICAgICAge1xuICAgICAgICBlcnJvcjogKGlzc3VlKSA9PlxuICAgICAgICAgIGBISUdIQ0hBUlRTX1ZFUlNJT04gbXVzdCBiZSAnbGF0ZXN0JywgYSBtYWpvciB2ZXJzaW9uLCBvciBpbiB0aGUgZm9ybSBYWC5ZWS5aWiwgcmVjZWl2ZWQgJyR7aXNzdWUuaW5wdXR9J2BcbiAgICAgIH1cbiAgICApXG4gICAgLnRyYW5zZm9ybSgodmFsdWUpID0+ICh2YWx1ZSAhPT0gJycgPyB2YWx1ZSA6IHVuZGVmaW5lZCkpLFxuICBISUdIQ0hBUlRTX0NETl9VUkw6IHpcbiAgICAuc3RyaW5nKClcbiAgICAudHJpbSgpXG4gICAgLnJlZmluZShcbiAgICAgICh2YWx1ZSkgPT5cbiAgICAgICAgdmFsdWUuc3RhcnRzV2l0aCgnaHR0cHM6Ly8nKSB8fFxuICAgICAgICB2YWx1ZS5zdGFydHNXaXRoKCdodHRwOi8vJykgfHxcbiAgICAgICAgdmFsdWUgPT09ICcnLFxuICAgICAge1xuICAgICAgICBlcnJvcjogKGlzc3VlKSA9PlxuICAgICAgICAgIGBJbnZhbGlkIHZhbHVlIGZvciBISUdIQ0hBUlRTX0NETl9VUkwuIEl0IHNob3VsZCBzdGFydCB3aXRoIGh0dHA6Ly8gb3IgaHR0cHM6Ly8sIHJlY2VpdmVkICcke2lzc3VlLmlucHV0fSdgXG4gICAgICB9XG4gICAgKVxuICAgIC50cmFuc2Zvcm0oKHZhbHVlKSA9PiAodmFsdWUgIT09ICcnID8gdmFsdWUgOiB1bmRlZmluZWQpKSxcbiAgSElHSENIQVJUU19VU0VfTlBNOiB2LmJvb2xlYW4oKSxcbiAgSElHSENIQVJUU19DT1JFX1NDUklQVFM6IHYuYXJyYXkoc2NyaXB0c05hbWVzLmNvcmUpLFxuICBISUdIQ0hBUlRTX01PRFVMRV9TQ1JJUFRTOiB2LmFycmF5KHNjcmlwdHNOYW1lcy5tb2R1bGVzKSxcbiAgSElHSENIQVJUU19JTkRJQ0FUT1JfU0NSSVBUUzogdi5hcnJheShzY3JpcHRzTmFtZXMuaW5kaWNhdG9ycyksXG4gIEhJR0hDSEFSVFNfRk9SQ0VfRkVUQ0g6IHYuYm9vbGVhbigpLFxuICBISUdIQ0hBUlRTX0NBQ0hFX1BBVEg6IHYuc3RyaW5nKCksXG4gIEhJR0hDSEFSVFNfQURNSU5fVE9LRU46IHYuc3RyaW5nKCksXG5cbiAgLy8gZXhwb3J0XG4gIEVYUE9SVF9UWVBFOiB2LmVudW0oWydqcGVnJywgJ3BuZycsICdwZGYnLCAnc3ZnJ10pLFxuICBFWFBPUlRfQ09OU1RSOiB2LmVudW0oWydjaGFydCcsICdzdG9ja0NoYXJ0JywgJ21hcENoYXJ0JywgJ2dhbnR0Q2hhcnQnXSksXG4gIEVYUE9SVF9ERUZBVUxUX0hFSUdIVDogdi5wb3NpdGl2ZU51bSgpLFxuICBFWFBPUlRfREVGQVVMVF9XSURUSDogdi5wb3NpdGl2ZU51bSgpLFxuICBFWFBPUlRfREVGQVVMVF9TQ0FMRTogdi5wb3NpdGl2ZU51bSgpLFxuICBFWFBPUlRfUkFTVEVSSVpBVElPTl9USU1FT1VUOiB2Lm5vbk5lZ2F0aXZlTnVtKCksXG5cbiAgLy8gY3VzdG9tXG4gIENVU1RPTV9MT0dJQ19BTExPV19DT0RFX0VYRUNVVElPTjogdi5ib29sZWFuKCksXG4gIENVU1RPTV9MT0dJQ19BTExPV19GSUxFX1JFU09VUkNFUzogdi5ib29sZWFuKCksXG5cbiAgLy8gc2VydmVyXG4gIFNFUlZFUl9FTkFCTEU6IHYuYm9vbGVhbigpLFxuICBTRVJWRVJfSE9TVDogdi5zdHJpbmcoKSxcbiAgU0VSVkVSX1BPUlQ6IHYucG9zaXRpdmVOdW0oKSxcbiAgU0VSVkVSX01BWF9VUExPQURfU0laRTogdi5wb3NpdGl2ZU51bSgpLFxuICBTRVJWRVJfS0VFUF9BTElWRV9USU1FT1VUOiB2Lm5vbk5lZ2F0aXZlTnVtKCksXG4gIFNFUlZFUl9CRU5DSE1BUktJTkc6IHYuYm9vbGVhbigpLFxuXG4gIC8vIHNlcnZlciBwcm94eVxuICBTRVJWRVJfUFJPWFlfSE9TVDogdi5zdHJpbmcoKSxcbiAgU0VSVkVSX1BST1hZX1BPUlQ6IHYucG9zaXRpdmVOdW0oKSxcbiAgU0VSVkVSX1BST1hZX1VTRVJOQU1FOiB2LnN0cmluZygpLFxuICBTRVJWRVJfUFJPWFlfUEFTU1dPUkQ6IHYuc3RyaW5nKCksXG4gIFNFUlZFUl9QUk9YWV9USU1FT1VUOiB2Lm5vbk5lZ2F0aXZlTnVtKCksXG5cbiAgLy8gc2VydmVyIHJhdGUgbGltaXRpbmdcbiAgU0VSVkVSX1JBVEVfTElNSVRJTkdfRU5BQkxFOiB2LmJvb2xlYW4oKSxcbiAgU0VSVkVSX1JBVEVfTElNSVRJTkdfTUFYX1JFUVVFU1RTOiB2Lm5vbk5lZ2F0aXZlTnVtKCksXG4gIFNFUlZFUl9SQVRFX0xJTUlUSU5HX1dJTkRPVzogdi5ub25OZWdhdGl2ZU51bSgpLFxuICBTRVJWRVJfUkFURV9MSU1JVElOR19UUlVTVF9QUk9YWTogdi5ib29sZWFuKCksXG4gIFNFUlZFUl9SQVRFX0xJTUlUSU5HX1NLSVBfS0VZOiB2LnN0cmluZygpLFxuICBTRVJWRVJfUkFURV9MSU1JVElOR19TS0lQX1RPS0VOOiB2LnN0cmluZygpLFxuXG4gIC8vIHNlcnZlciBzc2xcbiAgU0VSVkVSX1NTTF9FTkFCTEU6IHYuYm9vbGVhbigpLFxuICBTRVJWRVJfU1NMX0ZPUkNFOiB2LmJvb2xlYW4oKSxcbiAgU0VSVkVSX1NTTF9QT1JUOiB2LnBvc2l0aXZlTnVtKCksXG4gIFNFUlZFUl9TU0xfQ0VSVF9QQVRIOiB2LnN0cmluZygpLFxuXG4gIC8vIHBvb2xcbiAgUE9PTF9NSU5fV09SS0VSUzogdi5ub25OZWdhdGl2ZU51bSgpLFxuICBQT09MX01BWF9XT1JLRVJTOiB2Lm5vbk5lZ2F0aXZlTnVtKCksXG4gIFBPT0xfV09SS19MSU1JVDogdi5wb3NpdGl2ZU51bSgpLFxuICBQT09MX1FVRVVFX0xJTUlUOiB2Lm5vbk5lZ2F0aXZlTnVtKCksXG4gIFBPT0xfUVVFVUVfUkVKRUNUX0RFTEFZOiB2Lm5vbk5lZ2F0aXZlTnVtKCksXG4gIFBPT0xfQUNRVUlSRV9USU1FT1VUOiB2Lm5vbk5lZ2F0aXZlTnVtKCksXG4gIFBPT0xfQ1JFQVRFX1RJTUVPVVQ6IHYubm9uTmVnYXRpdmVOdW0oKSxcbiAgUE9PTF9ERVNUUk9ZX1RJTUVPVVQ6IHYubm9uTmVnYXRpdmVOdW0oKSxcbiAgUE9PTF9JRExFX1RJTUVPVVQ6IHYubm9uTmVnYXRpdmVOdW0oKSxcbiAgUE9PTF9DUkVBVEVfUkVUUllfSU5URVJWQUw6IHYubm9uTmVnYXRpdmVOdW0oKSxcbiAgUE9PTF9SRUFQRVJfSU5URVJWQUw6IHYubm9uTmVnYXRpdmVOdW0oKSxcbiAgUE9PTF9CRU5DSE1BUktJTkc6IHYuYm9vbGVhbigpLFxuXG4gIC8vIGxvZ2dlclxuICBMT0dHSU5HX0xFVkVMOiB6XG4gICAgLnN0cmluZygpXG4gICAgLnRyaW0oKVxuICAgIC5yZWZpbmUoXG4gICAgICAodmFsdWUpID0+XG4gICAgICAgIHZhbHVlID09PSAnJyB8fFxuICAgICAgICAoIWlzTmFOKHBhcnNlRmxvYXQodmFsdWUpKSAmJlxuICAgICAgICAgIHBhcnNlRmxvYXQodmFsdWUpID49IDAgJiZcbiAgICAgICAgICBwYXJzZUZsb2F0KHZhbHVlKSA8PSA1KSxcbiAgICAgIHtcbiAgICAgICAgZXJyb3I6IChpc3N1ZSkgPT5cbiAgICAgICAgICBgSW52YWxpZCB2YWx1ZSBmb3IgTE9HR0lOR19MRVZFTC4gV2Ugb25seSBhY2NlcHQgdmFsdWVzIGZyb20gMCB0byA1IGFzIGxvZ2dpbmcgbGV2ZWxzLCByZWNlaXZlZCAnJHtpc3N1ZS5pbnB1dH0nYFxuICAgICAgfVxuICAgIClcbiAgICAudHJhbnNmb3JtKCh2YWx1ZSkgPT4gKHZhbHVlICE9PSAnJyA/IHBhcnNlRmxvYXQodmFsdWUpIDogdW5kZWZpbmVkKSksXG4gIExPR0dJTkdfRklMRTogdi5zdHJpbmcoKSxcbiAgTE9HR0lOR19ERVNUOiB2LnN0cmluZygpLFxuICBMT0dHSU5HX1RPX0NPTlNPTEU6IHYuYm9vbGVhbigpLFxuICBMT0dHSU5HX1RPX0ZJTEU6IHYuYm9vbGVhbigpLFxuXG4gIC8vIHVpXG4gIFVJX0VOQUJMRTogdi5ib29sZWFuKCksXG4gIFVJX1JPVVRFOiB2LnN0cmluZygpLFxuXG4gIC8vIG90aGVyXG4gIE9USEVSX1NIVVRET1dOX0RSQUlOX1RJTUVPVVQ6IHYubm9uTmVnYXRpdmVOdW0oKSxcbiAgT1RIRVJfTk9ERV9FTlY6IHYuZW51bShbJ2RldmVsb3BtZW50JywgJ3Byb2R1Y3Rpb24nLCAndGVzdCddKSxcbiAgT1RIRVJfTElTVEVOX1RPX1BST0NFU1NfRVhJVFM6IHYuYm9vbGVhbigpLFxuICBPVEhFUl9OT19MT0dPOiB2LmJvb2xlYW4oKSxcbiAgT1RIRVJfSEFSRF9SRVNFVF9QQUdFOiB2LmJvb2xlYW4oKSxcbiAgT1RIRVJfQlJPV1NFUl9TSEVMTF9NT0RFOiB2LmJvb2xlYW4oKSxcbiAgT1RIRVJfQUxMT1dfWExJTks6IHYuYm9vbGVhbigpLFxuXG4gIC8vIGRlYnVnZ2VyXG4gIERFQlVHX0VOQUJMRTogdi5ib29sZWFuKCksXG4gIERFQlVHX0hFQURMRVNTOiB2LmJvb2xlYW4oKSxcbiAgREVCVUdfREVWVE9PTFM6IHYuYm9vbGVhbigpLFxuICBERUJVR19MSVNURU5fVE9fQ09OU09MRTogdi5ib29sZWFuKCksXG4gIERFQlVHX0RVTVBJTzogdi5ib29sZWFuKCksXG4gIERFQlVHX1NMT1dfTU86IHYubm9uTmVnYXRpdmVOdW0oKSxcbiAgREVCVUdfREVCVUdHSU5HX1BPUlQ6IHYucG9zaXRpdmVOdW0oKVxufSk7XG5cbmV4cG9ydCBjb25zdCBlbnZzID0gQ29uZmlnLnBhcnRpYWwoKS5wYXJzZShwcm9jZXNzLmVudik7XG4iLCIvKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKlxuXG5IaWdoY2hhcnRzIEV4cG9ydCBTZXJ2ZXJcblxuQ29weXJpZ2h0IChjKSAyMDE2LTIwMjQsIEhpZ2hzb2Z0XG5cbkxpY2VuY2VkIHVuZGVyIHRoZSBNSVQgbGljZW5jZS5cblxuQWRkaXRpb25hbGx5IGEgdmFsaWQgSGlnaGNoYXJ0cyBsaWNlbnNlIGlzIHJlcXVpcmVkIGZvciB1c2UuXG5cblNlZSBMSUNFTlNFIGZpbGUgaW4gcm9vdCBmb3IgZGV0YWlscy5cblxuKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKi9cblxuaW1wb3J0IHsgYXBwZW5kRmlsZSwgZXhpc3RzU3luYywgbWtkaXJTeW5jIH0gZnJvbSAnZnMnO1xuXG4vLyBUaGUgYXZhaWxhYmxlIGNvbG9yc1xuY29uc3QgY29sb3JzID0gWydyZWQnLCAneWVsbG93JywgJ2JsdWUnLCAnZ3JheScsICdncmVlbiddO1xuXG4vLyBUaGUgZGVmYXVsdCBsb2dnaW5nIGNvbmZpZ1xubGV0IGxvZ2dpbmcgPSB7XG4gIC8vIEZsYWdzIGZvciBsb2dnaW5nIHN0YXR1c1xuICB0b0NvbnNvbGU6IHRydWUsXG4gIHRvRmlsZTogZmFsc2UsXG4gIHBhdGhDcmVhdGVkOiBmYWxzZSxcbiAgLy8gTG9nIGxldmVsc1xuICBsZXZlbHNEZXNjOiBbXG4gICAge1xuICAgICAgdGl0bGU6ICdlcnJvcicsXG4gICAgICBjb2xvcjogY29sb3JzWzBdXG4gICAgfSxcbiAgICB7XG4gICAgICB0aXRsZTogJ3dhcm5pbmcnLFxuICAgICAgY29sb3I6IGNvbG9yc1sxXVxuICAgIH0sXG4gICAge1xuICAgICAgdGl0bGU6ICdub3RpY2UnLFxuICAgICAgY29sb3I6IGNvbG9yc1syXVxuICAgIH0sXG4gICAge1xuICAgICAgdGl0bGU6ICd2ZXJib3NlJyxcbiAgICAgIGNvbG9yOiBjb2xvcnNbM11cbiAgICB9LFxuICAgIHtcbiAgICAgIHRpdGxlOiAnYmVuY2htYXJrJyxcbiAgICAgIGNvbG9yOiBjb2xvcnNbNF1cbiAgICB9XG4gIF0sXG4gIC8vIExvZyBsaXN0ZW5lcnNcbiAgbGlzdGVuZXJzOiBbXVxufTtcblxuLyoqXG4gKiBMb2dzIHRoZSBwcm92aWRlZCB0ZXh0cyB0byBhIGZpbGUsIGlmIGZpbGUgbG9nZ2luZyBpcyBlbmFibGVkLiBJdCBjcmVhdGVzXG4gKiB0aGUgbmVjZXNzYXJ5IGRpcmVjdG9yeSBzdHJ1Y3R1cmUgaWYgbm90IGFscmVhZHkgY3JlYXRlZCBhbmQgYXBwZW5kcyB0aGVcbiAqIGNvbnRlbnQsIGluY2x1ZGluZyBhbiBvcHRpb25hbCBwcmVmaXgsIHRvIHRoZSBzcGVjaWZpZWQgbG9nIGZpbGUuXG4gKlxuICogQHBhcmFtIHtzdHJpbmdbXX0gdGV4dHMgLSBBbiBhcnJheSBvZiB0ZXh0cyB0byBiZSBsb2dnZWQuXG4gKiBAcGFyYW0ge3N0cmluZ30gcHJlZml4IC0gQW4gb3B0aW9uYWwgcHJlZml4IHRvIGJlIGFkZGVkIHRvIGVhY2ggbG9nIGVudHJ5LlxuICovXG5jb25zdCBsb2dUb0ZpbGUgPSAodGV4dHMsIHByZWZpeCkgPT4ge1xuICBpZiAoIWxvZ2dpbmcucGF0aENyZWF0ZWQpIHtcbiAgICAvLyBDcmVhdGUgaWYgZG9lcyBub3QgZXhpc3RcbiAgICAhZXhpc3RzU3luYyhsb2dnaW5nLmRlc3QpICYmIG1rZGlyU3luYyhsb2dnaW5nLmRlc3QpO1xuXG4gICAgLy8gV2Ugbm93IGFzc3VtZSB0aGUgcGF0aCBpcyBhdmFpbGFibGUsIGUuZy4gaXQncyB0aGUgcmVzcG9uc2liaWxpdHlcbiAgICAvLyBvZiB0aGUgdXNlciB0byBjcmVhdGUgdGhlIHBhdGggd2l0aCB0aGUgY29ycmVjdCBhY2Nlc3MgcmlnaHRzLlxuICAgIGxvZ2dpbmcucGF0aENyZWF0ZWQgPSB0cnVlO1xuICB9XG5cbiAgLy8gQWRkIHRoZSBjb250ZW50IHRvIGEgZmlsZVxuICBhcHBlbmRGaWxlKFxuICAgIGAke2xvZ2dpbmcuZGVzdH0ke2xvZ2dpbmcuZmlsZX1gLFxuICAgIFtwcmVmaXhdLmNvbmNhdCh0ZXh0cykuam9pbignICcpICsgJ1xcbicsXG4gICAgKGVycm9yKSA9PiB7XG4gICAgICBpZiAoZXJyb3IpIHtcbiAgICAgICAgY29uc29sZS5sb2coYFtsb2dnZXJdIFVuYWJsZSB0byB3cml0ZSB0byBsb2cgZmlsZTogJHtlcnJvcn1gKTtcbiAgICAgICAgbG9nZ2luZy50b0ZpbGUgPSBmYWxzZTtcbiAgICAgIH1cbiAgICB9XG4gICk7XG59O1xuXG4vKipcbiAqIExvZ3MgYSBtZXNzYWdlLiBBY2NlcHRzIGEgdmFyaWFibGUgYW1vdW50IG9mIGFyZ3VtZW50cy4gQXJndW1lbnRzIGFmdGVyXG4gKiBgbGV2ZWxgIHdpbGwgYmUgcGFzc2VkIGRpcmVjdGx5IHRvIGNvbnNvbGUubG9nLCBhbmQvb3Igd2lsbCBiZSBqb2luZWRcbiAqIGFuZCBhcHBlbmRlZCB0byB0aGUgbG9nIGZpbGUuXG4gKlxuICogQHBhcmFtIHthbnl9IGFyZ3MgLSBBbiBhcnJheSBvZiBhcmd1bWVudHMgd2hlcmUgdGhlIGZpcnN0IGlzIHRoZSBsb2cgbGV2ZWxcbiAqIGFuZCB0aGUgcmVzdCBhcmUgc3RyaW5ncyB0byBidWlsZCBhIG1lc3NhZ2Ugd2l0aC5cbiAqL1xuZXhwb3J0IGNvbnN0IGxvZyA9ICguLi5hcmdzKSA9PiB7XG4gIGNvbnN0IFtuZXdMZXZlbCwgLi4udGV4dHNdID0gYXJncztcblxuICAvLyBDdXJyZW50IGxvZ2dpbmcgb3B0aW9uc1xuICBjb25zdCB7IGxldmVsc0Rlc2MsIGxldmVsIH0gPSBsb2dnaW5nO1xuXG4gIC8vIENoZWNrIGlmIGxvZyBsZXZlbCBpcyB3aXRoaW4gYSBjb3JyZWN0IHJhbmdlIG9yIGlzIGEgYmVuY2htYXJrIGxvZ1xuICBpZiAoXG4gICAgbmV3TGV2ZWwgIT09IDUgJiZcbiAgICAobmV3TGV2ZWwgPT09IDAgfHwgbmV3TGV2ZWwgPiBsZXZlbCB8fCBsZXZlbCA+IGxldmVsc0Rlc2MubGVuZ3RoKVxuICApIHtcbiAgICByZXR1cm47XG4gIH1cblxuICAvLyBHZXQgcmlkIG9mIHRoZSBHTVQgdGV4dCBpbmZvcm1hdGlvblxuICBjb25zdCBuZXdEYXRlID0gbmV3IERhdGUoKS50b1N0cmluZygpLnNwbGl0KCcoJylbMF0udHJpbSgpO1xuXG4gIC8vIENyZWF0ZSBhIG1lc3NhZ2UncyBwcmVmaXhcbiAgY29uc3QgcHJlZml4ID0gYCR7bmV3RGF0ZX0gWyR7bGV2ZWxzRGVzY1tuZXdMZXZlbCAtIDFdLnRpdGxlfV0gLWA7XG5cbiAgLy8gQ2FsbCBhdmFpbGFibGUgbG9nIGxpc3RlbmVyc1xuICBsb2dnaW5nLmxpc3RlbmVycy5mb3JFYWNoKChmbikgPT4ge1xuICAgIGZuKHByZWZpeCwgdGV4dHMuam9pbignICcpKTtcbiAgfSk7XG5cbiAgLy8gTG9nIHRvIGNvbnNvbGVcbiAgaWYgKGxvZ2dpbmcudG9Db25zb2xlKSB7XG4gICAgY29uc29sZS5sb2cuYXBwbHkoXG4gICAgICB1bmRlZmluZWQsXG4gICAgICBbcHJlZml4LnRvU3RyaW5nKClbbG9nZ2luZy5sZXZlbHNEZXNjW25ld0xldmVsIC0gMV0uY29sb3JdXS5jb25jYXQodGV4dHMpXG4gICAgKTtcbiAgfVxuXG4gIC8vIExvZyB0byBmaWxlXG4gIGlmIChsb2dnaW5nLnRvRmlsZSkge1xuICAgIGxvZ1RvRmlsZSh0ZXh0cywgcHJlZml4KTtcbiAgfVxufTtcblxuLyoqXG4gKiBMb2dzIGFuIGVycm9yIG1lc3NhZ2Ugd2l0aCBpdHMgc3RhY2sgdHJhY2UuIE9wdGlvbmFsbHksIGEgY3VzdG9tIG1lc3NhZ2VcbiAqIGNhbiBiZSBwcm92aWRlZC5cbiAqXG4gKiBAcGFyYW0ge251bWJlcn0gbGV2ZWwgLSBUaGUgbG9nIGxldmVsLlxuICogQHBhcmFtIHtFcnJvcn0gZXJyb3IgLSBUaGUgZXJyb3Igb2JqZWN0LlxuICogQHBhcmFtIHtzdHJpbmd9IGN1c3RvbU1lc3NhZ2UgLSBBbiBvcHRpb25hbCBjdXN0b20gbWVzc2FnZSB0byBiZSBsb2dnZWQgYWxvbmdcbiAqIHdpdGggdGhlIGVycm9yLlxuICovXG5leHBvcnQgY29uc3QgbG9nV2l0aFN0YWNrID0gKG5ld0xldmVsLCBlcnJvciwgY3VzdG9tTWVzc2FnZSkgPT4ge1xuICAvLyBHZXQgdGhlIG1haW4gbWVzc2FnZVxuICBjb25zdCBtYWluTWVzc2FnZSA9IGN1c3RvbU1lc3NhZ2UgfHwgZXJyb3IubWVzc2FnZTtcblxuICAvLyBDdXJyZW50IGxvZ2dpbmcgb3B0aW9uc1xuICBjb25zdCB7IGxldmVsLCBsZXZlbHNEZXNjIH0gPSBsb2dnaW5nO1xuXG4gIC8vIENoZWNrIGlmIGxvZyBsZXZlbCBpcyB3aXRoaW4gYSBjb3JyZWN0IHJhbmdlXG4gIGlmIChuZXdMZXZlbCA9PT0gMCB8fCBuZXdMZXZlbCA+IGxldmVsIHx8IGxldmVsID4gbGV2ZWxzRGVzYy5sZW5ndGgpIHtcbiAgICByZXR1cm47XG4gIH1cblxuICAvLyBHZXQgcmlkIG9mIHRoZSBHTVQgdGV4dCBpbmZvcm1hdGlvblxuICBjb25zdCBuZXdEYXRlID0gbmV3IERhdGUoKS50b1N0cmluZygpLnNwbGl0KCcoJylbMF0udHJpbSgpO1xuXG4gIC8vIENyZWF0ZSBhIG1lc3NhZ2UncyBwcmVmaXhcbiAgY29uc3QgcHJlZml4ID0gYCR7bmV3RGF0ZX0gWyR7bGV2ZWxzRGVzY1tuZXdMZXZlbCAtIDFdLnRpdGxlfV0gLWA7XG5cbiAgLy8gSWYgdGhlIGN1c3RvbU1lc3NhZ2UgZXhpc3RzLCB3ZSB3YW50IHRvIGRpc3BsYXkgdGhlIHdob2xlIHN0YWNrIG1lc3NhZ2VcbiAgY29uc3Qgc3RhY2tNZXNzYWdlID1cbiAgICBlcnJvci5tZXNzYWdlICE9PSBlcnJvci5zdGFja01lc3NhZ2UgfHwgZXJyb3Iuc3RhY2tNZXNzYWdlID09PSB1bmRlZmluZWRcbiAgICAgID8gZXJyb3Iuc3RhY2tcbiAgICAgIDogZXJyb3Iuc3RhY2suc3BsaXQoJ1xcbicpLnNsaWNlKDEpLmpvaW4oJ1xcbicpO1xuXG4gIC8vIENvbWJpbmUgY3VzdG9tIG1lc3NhZ2Ugb3IgZXJyb3IgbWVzc2FnZSB3aXRoIGVycm9yIHN0YWNrIG1lc3NhZ2VcbiAgY29uc3QgdGV4dHMgPSBbbWFpbk1lc3NhZ2UsICdcXG4nLCBzdGFja01lc3NhZ2VdO1xuXG4gIC8vIExvZyB0byBjb25zb2xlXG4gIGlmIChsb2dnaW5nLnRvQ29uc29sZSkge1xuICAgIGNvbnNvbGUubG9nLmFwcGx5KFxuICAgICAgdW5kZWZpbmVkLFxuICAgICAgW3ByZWZpeC50b1N0cmluZygpW2xvZ2dpbmcubGV2ZWxzRGVzY1tuZXdMZXZlbCAtIDFdLmNvbG9yXV0uY29uY2F0KFtcbiAgICAgICAgbWFpbk1lc3NhZ2VbY29sb3JzW25ld0xldmVsIC0gMV1dLFxuICAgICAgICAnXFxuJyxcbiAgICAgICAgc3RhY2tNZXNzYWdlXG4gICAgICBdKVxuICAgICk7XG4gIH1cblxuICAvLyBDYWxsIGF2YWlsYWJsZSBsb2cgbGlzdGVuZXJzXG4gIGxvZ2dpbmcubGlzdGVuZXJzLmZvckVhY2goKGZuKSA9PiB7XG4gICAgZm4ocHJlZml4LCB0ZXh0cy5qb2luKCcgJykpO1xuICB9KTtcblxuICAvLyBMb2cgdG8gZmlsZVxuICBpZiAobG9nZ2luZy50b0ZpbGUpIHtcbiAgICBsb2dUb0ZpbGUodGV4dHMsIHByZWZpeCk7XG4gIH1cbn07XG5cbi8qKlxuICogU2V0cyB0aGUgbG9nIGxldmVsIHRvIHRoZSBzcGVjaWZpZWQgdmFsdWUuIExvZyBsZXZlbHMgYXJlICgwID0gbm8gbG9nZ2luZyxcbiAqIDEgPSBlcnJvciwgMiA9IHdhcm5pbmcsIDMgPSBub3RpY2UsIDQgPSB2ZXJib3NlIG9yIDUgPSBiZW5jaG1hcmspXG4gKlxuICogQHBhcmFtIHtudW1iZXJ9IG5ld0xldmVsIC0gVGhlIG5ldyBsb2cgbGV2ZWwgdG8gYmUgc2V0LlxuICovXG5leHBvcnQgY29uc3Qgc2V0TG9nTGV2ZWwgPSAobmV3TGV2ZWwpID0+IHtcbiAgaWYgKG5ld0xldmVsID49IDAgJiYgbmV3TGV2ZWwgPD0gbG9nZ2luZy5sZXZlbHNEZXNjLmxlbmd0aCkge1xuICAgIGxvZ2dpbmcubGV2ZWwgPSBuZXdMZXZlbDtcbiAgfVxufTtcblxuLyoqXG4gKiBFbmFibGVzIGZpbGUgbG9nZ2luZyB3aXRoIHRoZSBzcGVjaWZpZWQgZGVzdGluYXRpb24gYW5kIGxvZyBmaWxlLlxuICpcbiAqIEBwYXJhbSB7c3RyaW5nfSBsb2dEZXN0IC0gVGhlIGRlc3RpbmF0aW9uIHBhdGggZm9yIGxvZyBmaWxlcy5cbiAqIEBwYXJhbSB7c3RyaW5nfSBsb2dGaWxlIC0gVGhlIGxvZyBmaWxlIG5hbWUuXG4gKi9cbmV4cG9ydCBjb25zdCBlbmFibGVGaWxlTG9nZ2luZyA9IChsb2dEZXN0LCBsb2dGaWxlKSA9PiB7XG4gIC8vIFVwZGF0ZSBsb2dnaW5nIG9wdGlvbnNcbiAgbG9nZ2luZyA9IHtcbiAgICAuLi5sb2dnaW5nLFxuICAgIGRlc3Q6IGxvZ0Rlc3QgfHwgbG9nZ2luZy5kZXN0LFxuICAgIGZpbGU6IGxvZ0ZpbGUgfHwgbG9nZ2luZy5maWxlLFxuICAgIHRvRmlsZTogdHJ1ZVxuICB9O1xuXG4gIGlmIChsb2dnaW5nLmRlc3QubGVuZ3RoID09PSAwKSB7XG4gICAgcmV0dXJuIGxvZygxLCAnW2xvZ2dlcl0gRmlsZSBsb2dnaW5nIGluaXRpYWxpemF0aW9uOiBubyBwYXRoIHN1cHBsaWVkLicpO1xuICB9XG5cbiAgaWYgKCFsb2dnaW5nLmRlc3QuZW5kc1dpdGgoJy8nKSkge1xuICAgIGxvZ2dpbmcuZGVzdCArPSAnLyc7XG4gIH1cbn07XG5cbi8qKlxuICogSW5pdGlhbGl6ZXMgbG9nZ2luZyB3aXRoIHRoZSBzcGVjaWZpZWQgbG9nZ2luZyBjb25maWd1cmF0aW9uLlxuICpcbiAqIEBwYXJhbSB7T2JqZWN0fSBsb2dnaW5nT3B0aW9ucyAtIFRoZSBsb2dnaW5nIGNvbmZpZ3VyYXRpb24gb2JqZWN0LlxuICovXG5leHBvcnQgY29uc3QgaW5pdExvZ2dpbmcgPSAobG9nZ2luZ09wdGlvbnMpID0+IHtcbiAgLy8gU2V0IGFsbCB0aGUgbG9nZ2luZyBvcHRpb25zIG9uIG91ciBsb2dnaW5nIG1vZHVsZSBvYmplY3RcbiAgZm9yIChjb25zdCBba2V5LCB2YWx1ZV0gb2YgT2JqZWN0LmVudHJpZXMobG9nZ2luZ09wdGlvbnMpKSB7XG4gICAgbG9nZ2luZ1trZXldID0gdmFsdWU7XG4gIH1cblxuICAvLyBTZXQgdGhlIGxvZyBsZXZlbFxuICBzZXRMb2dMZXZlbChsb2dnaW5nT3B0aW9ucyAmJiBwYXJzZUludChsb2dnaW5nT3B0aW9ucy5sZXZlbCkpO1xuXG4gIC8vIFNldCB0aGUgbG9nIGZpbGUgcGF0aCBhbmQgbmFtZVxuICBpZiAobG9nZ2luZ09wdGlvbnMgJiYgbG9nZ2luZ09wdGlvbnMuZGVzdCAmJiBsb2dnaW5nT3B0aW9ucy50b0ZpbGUpIHtcbiAgICBlbmFibGVGaWxlTG9nZ2luZyhcbiAgICAgIGxvZ2dpbmdPcHRpb25zLmRlc3QsXG4gICAgICBsb2dnaW5nT3B0aW9ucy5maWxlIHx8ICdoaWdoY2hhcnRzLWV4cG9ydC1zZXJ2ZXIubG9nJ1xuICAgICk7XG4gIH1cbn07XG5cbi8qKlxuICogQWRkcyBhIGxpc3RlbmVyIGZ1bmN0aW9uIHRvIHRoZSBsb2dnaW5nIHN5c3RlbS5cbiAqXG4gKiBAcGFyYW0ge2Z1bmN0aW9ufSBmbiAtIFRoZSBsaXN0ZW5lciBmdW5jdGlvbiB0byBiZSBhZGRlZC5cbiAqL1xuZXhwb3J0IGNvbnN0IGxpc3RlbiA9IChmbikgPT4ge1xuICBsb2dnaW5nLmxpc3RlbmVycy5wdXNoKGZuKTtcbn07XG5cbmV4cG9ydCBkZWZhdWx0IHtcbiAgbG9nLFxuICBsb2dXaXRoU3RhY2ssXG4gIHNldExvZ0xldmVsLFxuICBlbmFibGVGaWxlTG9nZ2luZyxcbiAgaW5pdExvZ2dpbmcsXG4gIGxpc3RlblxufTtcbiIsIi8qKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqXG5cbkhpZ2hjaGFydHMgRXhwb3J0IFNlcnZlclxuXG5Db3B5cmlnaHQgKGMpIDIwMTYtMjAyNCwgSGlnaHNvZnRcblxuTGljZW5jZWQgdW5kZXIgdGhlIE1JVCBsaWNlbmNlLlxuXG5BZGRpdGlvbmFsbHkgYSB2YWxpZCBIaWdoY2hhcnRzIGxpY2Vuc2UgaXMgcmVxdWlyZWQgZm9yIHVzZS5cblxuU2VlIExJQ0VOU0UgZmlsZSBpbiByb290IGZvciBkZXRhaWxzLlxuXG4qKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqL1xuXG5pbXBvcnQgeyByZWFkRmlsZVN5bmMgfSBmcm9tICdmcyc7XG5pbXBvcnQgeyBjcmVhdGVSZXF1aXJlIH0gZnJvbSAnbW9kdWxlJztcbmltcG9ydCB7IGRpcm5hbWUsIGpvaW4gfSBmcm9tICdwYXRoJztcbmltcG9ydCB7IGZpbGVVUkxUb1BhdGggfSBmcm9tICd1cmwnO1xuXG5pbXBvcnQgeyBkZWZhdWx0Q29uZmlnIH0gZnJvbSAnLi4vbGliL3NjaGVtYXMvY29uZmlnLmpzJztcbmltcG9ydCB7IGxvZywgbG9nV2l0aFN0YWNrIH0gZnJvbSAnLi9sb2dnZXIuanMnO1xuXG5jb25zdCBNQVhfQkFDS09GRl9BVFRFTVBUUyA9IDY7XG5cbi8vIFRoZSBoaWdoY2hhcnRzIGRlcGVuZGVuY3kgZGlyZWN0b3J5IHBhdGhcbmV4cG9ydCBjb25zdCBfX2hpZ2hjaGFydHNEaXIgPSBkaXJuYW1lKFxuICBjcmVhdGVSZXF1aXJlKGltcG9ydC5tZXRhLnVybCkucmVzb2x2ZSgnaGlnaGNoYXJ0cy9wYWNrYWdlLmpzb24nKVxuKTtcblxuZXhwb3J0IGNvbnN0IF9fZGlybmFtZSA9IGZpbGVVUkxUb1BhdGgobmV3IFVSTCgnLi4vLicsIGltcG9ydC5tZXRhLnVybCkpO1xuXG4vKipcbiAqIENsZWFycyBhbmQgc3RhbmRhcmRpemVzIHRleHQgYnkgcmVwbGFjaW5nIG11bHRpcGxlIGNvbnNlY3V0aXZlIHdoaXRlc3BhY2VcbiAqIGNoYXJhY3RlcnMgd2l0aCBhIHNpbmdsZSBzcGFjZSBhbmQgdHJpbW1pbmcgYW55IGxlYWRpbmcgb3IgdHJhaWxpbmdcbiAqIHdoaXRlc3BhY2UuXG4gKlxuICogQHBhcmFtIHtzdHJpbmd9IHRleHQgLSBUaGUgaW5wdXQgdGV4dCB0byBiZSBjbGVhcmVkLlxuICogQHBhcmFtIHtSZWdFeHB9IFtydWxlPS9cXHNcXHMrL2ddIC0gVGhlIHJlZ3VsYXIgZXhwcmVzc2lvbiBydWxlIHRvIG1hdGNoXG4gKiBtdWx0aXBsZSBjb25zZWN1dGl2ZSB3aGl0ZXNwYWNlIGNoYXJhY3RlcnMuXG4gKiBAcGFyYW0ge3N0cmluZ30gW3JlcGxhY2VyPScgJ10gLSBUaGUgc3RyaW5nIHVzZWQgdG8gcmVwbGFjZSBtdWx0aXBsZVxuICogY29uc2VjdXRpdmUgd2hpdGVzcGFjZSBjaGFyYWN0ZXJzLlxuICpcbiAqIEByZXR1cm5zIHtzdHJpbmd9IC0gVGhlIGNsZWFyZWQgYW5kIHN0YW5kYXJkaXplZCB0ZXh0LlxuICovXG5leHBvcnQgY29uc3QgY2xlYXJUZXh0ID0gKHRleHQsIHJ1bGUgPSAvXFxzXFxzKy9nLCByZXBsYWNlciA9ICcgJykgPT5cbiAgdGV4dC5yZXBsYWNlQWxsKHJ1bGUsIHJlcGxhY2VyKS50cmltKCk7XG5cbi8qKlxuICogSW1wbGVtZW50cyBhbiBleHBvbmVudGlhbCBiYWNrb2ZmIHN0cmF0ZWd5IGZvciByZXRyeWluZyBhIGZ1bmN0aW9uIHVudGlsXG4gKiBhIGNlcnRhaW4gbnVtYmVyIG9mIGF0dGVtcHRzIGFyZSByZWFjaGVkLlxuICpcbiAqIEBwYXJhbSB7RnVuY3Rpb259IGZuIC0gVGhlIGZ1bmN0aW9uIHRvIGJlIHJldHJpZWQuXG4gKiBAcGFyYW0ge251bWJlcn0gW2F0dGVtcHQ9MF0gLSBUaGUgY3VycmVudCBhdHRlbXB0IG51bWJlci5cbiAqIEBwYXJhbSB7Li4uYW55fSBhcmdzIC0gQXJndW1lbnRzIHRvIGJlIHBhc3NlZCB0byB0aGUgZnVuY3Rpb24uXG4gKlxuICogQHJldHVybnMge1Byb21pc2V9IC0gQSBwcm9taXNlIHRoYXQgcmVzb2x2ZXMgdG8gdGhlIHJlc3VsdCBvZiB0aGUgZnVuY3Rpb25cbiAqIGlmIHN1Y2Nlc3NmdWwuXG4gKlxuICogQHRocm93cyB7RXJyb3J9IC0gVGhyb3dzIGFuIGVycm9yIGlmIHRoZSBtYXhpbXVtIG51bWJlciBvZiBhdHRlbXB0c1xuICogaXMgcmVhY2hlZC5cbiAqL1xuZXhwb3J0IGNvbnN0IGV4cEJhY2tvZmYgPSBhc3luYyAoZm4sIGF0dGVtcHQgPSAwLCAuLi5hcmdzKSA9PiB7XG4gIHRyeSB7XG4gICAgLy8gVHJ5IHRvIGNhbGwgdGhlIGZ1bmN0aW9uXG4gICAgcmV0dXJuIGF3YWl0IGZuKC4uLmFyZ3MpO1xuICB9IGNhdGNoIChlcnJvcikge1xuICAgIC8vIENhbGN1bGF0ZSBkZWxheSBpbiBtc1xuICAgIGNvbnN0IGRlbGF5SW5NcyA9IDIgKiogYXR0ZW1wdCAqIDEwMDA7XG5cbiAgICAvLyBJZiB0aGUgYXR0ZW1wdCBleGNlZWRzIHRoZSBtYXhpbXVtIGF0dGVtcHRzIG9mIHJlYXBlYXQsIHRocm93IGFuIGVycm9yXG4gICAgaWYgKCsrYXR0ZW1wdCA+PSBNQVhfQkFDS09GRl9BVFRFTVBUUykge1xuICAgICAgdGhyb3cgZXJyb3I7XG4gICAgfVxuXG4gICAgLy8gV2FpdCBnaXZlbiBhbW91bnQgb2YgdGltZVxuICAgIGF3YWl0IG5ldyBQcm9taXNlKChyZXNwb25zZSkgPT4gc2V0VGltZW91dChyZXNwb25zZSwgZGVsYXlJbk1zKSk7XG4gICAgbG9nKFxuICAgICAgMyxcbiAgICAgIGBbcG9vbF0gV2FpdGVkICR7ZGVsYXlJbk1zfW1zIHVudGlsIG5leHQgY2FsbCBmb3IgdGhlIHJlc291cmNlIGlkOiAke2FyZ3NbMF19LmBcbiAgICApO1xuXG4gICAgLy8gVHJ5IGFnYWluXG4gICAgcmV0dXJuIGV4cEJhY2tvZmYoZm4sIGF0dGVtcHQsIC4uLmFyZ3MpO1xuICB9XG59O1xuXG4vKipcbiAqIEZpeGVzIHRoZSBleHBvcnQgdHlwZSBiYXNlZCBvbiBNSU1FIHR5cGVzIGFuZCBmaWxlIGV4dGVuc2lvbnMuXG4gKlxuICogQHBhcmFtIHtzdHJpbmd9IHR5cGUgLSBUaGUgb3JpZ2luYWwgZXhwb3J0IHR5cGUuXG4gKiBAcGFyYW0ge3N0cmluZ30gb3V0ZmlsZSAtIFRoZSBmaWxlIHBhdGggb3IgbmFtZS5cbiAqXG4gKiBAcmV0dXJucyB7c3RyaW5nfSAtIFRoZSBjb3JyZWN0ZWQgZXhwb3J0IHR5cGUuXG4gKi9cbmV4cG9ydCBjb25zdCBmaXhUeXBlID0gKHR5cGUsIG91dGZpbGUpID0+IHtcbiAgLy8gTUlNRSB0eXBlc1xuICBjb25zdCBtaW1lVHlwZXMgPSB7XG4gICAgJ2ltYWdlL3BuZyc6ICdwbmcnLFxuICAgICdpbWFnZS9qcGVnJzogJ2pwZWcnLFxuICAgICdhcHBsaWNhdGlvbi9wZGYnOiAncGRmJyxcbiAgICAnaW1hZ2Uvc3ZnK3htbCc6ICdzdmcnXG4gIH07XG5cbiAgLy8gRm9ybWF0c1xuICBjb25zdCBmb3JtYXRzID0gWydwbmcnLCAnanBlZycsICdwZGYnLCAnc3ZnJ107XG5cbiAgLy8gQ2hlY2sgaWYgdHlwZSBhbmQgb3V0ZmlsZSdzIGV4dGVuc2lvbnMgYXJlIHRoZSBzYW1lXG4gIGlmIChvdXRmaWxlKSB7XG4gICAgY29uc3Qgb3V0VHlwZSA9IG91dGZpbGUuc3BsaXQoJy4nKS5wb3AoKTtcblxuICAgIGlmIChvdXRUeXBlID09PSAnanBnJykge1xuICAgICAgdHlwZSA9ICdqcGVnJztcbiAgICB9IGVsc2UgaWYgKGZvcm1hdHMuaW5jbHVkZXMob3V0VHlwZSkgJiYgdHlwZSAhPT0gb3V0VHlwZSkge1xuICAgICAgdHlwZSA9IG91dFR5cGU7XG4gICAgfVxuICB9XG5cbiAgLy8gUmV0dXJuIGEgY29ycmVjdCB0eXBlXG4gIHJldHVybiBtaW1lVHlwZXNbdHlwZV0gfHwgZm9ybWF0cy5maW5kKCh0KSA9PiB0ID09PSB0eXBlKSB8fCAncG5nJztcbn07XG5cbi8qKlxuICogSGFuZGxlcyBhbmQgdmFsaWRhdGVzIHJlc291cmNlcyBmb3IgZXhwb3J0LlxuICpcbiAqIEBwYXJhbSB7T2JqZWN0fHN0cmluZ30gcmVzb3VyY2VzIC0gVGhlIHJlc291cmNlcyB0byBiZSBoYW5kbGVkLiBDYW4gYmUgZWl0aGVyXG4gKiBhIEpTT04gb2JqZWN0LCBzdHJpbmdpZmllZCBKU09OIG9yIGEgcGF0aCB0byBhIEpTT04gZmlsZS5cbiAqIEBwYXJhbSB7Ym9vbGVhbn0gYWxsb3dGaWxlUmVzb3VyY2VzIC0gV2hldGhlciB0byBhbGxvdyBsb2FkaW5nIHJlc291cmNlcyBmcm9tXG4gKiBmaWxlcy5cbiAqXG4gKiBAcmV0dXJucyB7T2JqZWN0fHVuZGVmaW5lZH0gLSBUaGUgaGFuZGxlZCByZXNvdXJjZXMgb3IgdW5kZWZpbmVkIGlmIG5vIHZhbGlkXG4gKiByZXNvdXJjZXMgYXJlIGZvdW5kLlxuICovXG5leHBvcnQgY29uc3QgaGFuZGxlUmVzb3VyY2VzID0gKHJlc291cmNlcyA9IGZhbHNlLCBhbGxvd0ZpbGVSZXNvdXJjZXMpID0+IHtcbiAgY29uc3QgYWxsb3dlZFByb3BzID0gWydqcycsICdjc3MnLCAnZmlsZXMnXTtcblxuICBsZXQgaGFuZGxlZFJlc291cmNlcyA9IHJlc291cmNlcztcbiAgbGV0IGNvcnJlY3RSZXNvdXJjZXMgPSBmYWxzZTtcblxuICAvLyBUcnkgdG8gbG9hZCByZXNvdXJjZXMgZnJvbSBhIGZpbGVcbiAgaWYgKGFsbG93RmlsZVJlc291cmNlcyAmJiByZXNvdXJjZXMuZW5kc1dpdGgoJy5qc29uJykpIHtcbiAgICB0cnkge1xuICAgICAgaGFuZGxlZFJlc291cmNlcyA9IGlzQ29ycmVjdEpTT04ocmVhZEZpbGVTeW5jKHJlc291cmNlcywgJ3V0ZjgnKSk7XG4gICAgfSBjYXRjaCAoZXJyb3IpIHtcbiAgICAgIHJldHVybiBsb2dXaXRoU3RhY2soMiwgZXJyb3IsIGBbY2xpXSBObyByZXNvdXJjZXMgZm91bmQuYCk7XG4gICAgfVxuICB9IGVsc2Uge1xuICAgIC8vIFRyeSB0byBnZXQgSlNPTlxuICAgIGhhbmRsZWRSZXNvdXJjZXMgPSBpc0NvcnJlY3RKU09OKHJlc291cmNlcyk7XG5cbiAgICAvLyBHZXQgcmlkIG9mIHRoZSBmaWxlcyBzZWN0aW9uXG4gICAgaWYgKGhhbmRsZWRSZXNvdXJjZXMgJiYgIWFsbG93RmlsZVJlc291cmNlcykge1xuICAgICAgZGVsZXRlIGhhbmRsZWRSZXNvdXJjZXMuZmlsZXM7XG4gICAgfVxuICB9XG5cbiAgLy8gRmlsdGVyIGZyb20gdW5uZWNlc3NhcnkgcHJvcGVydGllc1xuICBmb3IgKGNvbnN0IHByb3BOYW1lIGluIGhhbmRsZWRSZXNvdXJjZXMpIHtcbiAgICBpZiAoIWFsbG93ZWRQcm9wcy5pbmNsdWRlcyhwcm9wTmFtZSkpIHtcbiAgICAgIGRlbGV0ZSBoYW5kbGVkUmVzb3VyY2VzW3Byb3BOYW1lXTtcbiAgICB9IGVsc2UgaWYgKCFjb3JyZWN0UmVzb3VyY2VzKSB7XG4gICAgICBjb3JyZWN0UmVzb3VyY2VzID0gdHJ1ZTtcbiAgICB9XG4gIH1cblxuICAvLyBDaGVjayBpZiBhdCBsZWFzdCBvbmUgb2YgYWxsb3dlZCBwcm9wZXJ0aWVzIGlzIHByZXNlbnRcbiAgaWYgKCFjb3JyZWN0UmVzb3VyY2VzKSB7XG4gICAgcmV0dXJuIGxvZygzLCBgW2NsaV0gTm8gcmVzb3VyY2VzIGZvdW5kLmApO1xuICB9XG5cbiAgLy8gSGFuZGxlIGZpbGVzIHNlY3Rpb25cbiAgaWYgKGhhbmRsZWRSZXNvdXJjZXMuZmlsZXMpIHtcbiAgICBoYW5kbGVkUmVzb3VyY2VzLmZpbGVzID0gaGFuZGxlZFJlc291cmNlcy5maWxlcy5tYXAoKGl0ZW0pID0+IGl0ZW0udHJpbSgpKTtcbiAgICBpZiAoIWhhbmRsZWRSZXNvdXJjZXMuZmlsZXMgfHwgaGFuZGxlZFJlc291cmNlcy5maWxlcy5sZW5ndGggPD0gMCkge1xuICAgICAgZGVsZXRlIGhhbmRsZWRSZXNvdXJjZXMuZmlsZXM7XG4gICAgfVxuICB9XG5cbiAgLy8gUmV0dXJuIHJlc291cmNlc1xuICByZXR1cm4gaGFuZGxlZFJlc291cmNlcztcbn07XG5cbi8qKlxuICogVmFsaWRhdGVzIGFuZCBwYXJzZXMgSlNPTiBkYXRhLiBDaGVja3MgaWYgcHJvdmlkZWQgZGF0YSBpcyBvciBjYW5cbiAqIGJlIGEgY29ycmVjdCBKU09OLiBJZiBhIHByaW1pdGl2ZSBpcyBwcm92aWRlZCwgaXQgaXMgc3RyaW5naWZpZWQgYW5kIHJldHVybmVkLlxuICpcbiAqIEBwYXJhbSB7T2JqZWN0fHN0cmluZ30gZGF0YSAtIFRoZSBKU09OIGRhdGEgdG8gYmUgdmFsaWRhdGVkIGFuZCBwYXJzZWQuXG4gKiBAcGFyYW0ge2Jvb2xlYW59IHRvU3RyaW5nIC0gV2hldGhlciB0byByZXR1cm4gYSBzdHJpbmdpZmllZCByZXByZXNlbnRhdGlvblxuICogb2YgdGhlIHBhcnNlZCBKU09OLlxuICpcbiAqIEByZXR1cm5zIHtPYmplY3R8c3RyaW5nfGJvb2xlYW59IC0gVGhlIHBhcnNlZCBKU09OIG9iamVjdCwgc3RyaW5naWZpZWQgSlNPTixcbiAqIG9yIGZhbHNlIGlmIHZhbGlkYXRpb24gZmFpbHMuXG4gKi9cbmV4cG9ydCBmdW5jdGlvbiBpc0NvcnJlY3RKU09OKGRhdGEsIHRvU3RyaW5nKSB7XG4gIHRyeSB7XG4gICAgLy8gR2V0IHRoZSBzdHJpbmcgcmVwcmVzZW50YXRpb24gaWYgbm90IGFscmVhZHkgYmVmb3JlIHBhcnNpbmdcbiAgICBjb25zdCBwYXJzZWREYXRhID0gSlNPTi5wYXJzZShcbiAgICAgIHR5cGVvZiBkYXRhICE9PSAnc3RyaW5nJyA/IEpTT04uc3RyaW5naWZ5KGRhdGEpIDogZGF0YVxuICAgICk7XG5cbiAgICAvLyBSZXR1cm4gYSBzdHJpbmdpZmllZCByZXByZXNlbnRhdGlvbiBvZiBhIEpTT04gaWYgcmVxdWlyZWRcbiAgICBpZiAodHlwZW9mIHBhcnNlZERhdGEgIT09ICdzdHJpbmcnICYmIHRvU3RyaW5nKSB7XG4gICAgICByZXR1cm4gSlNPTi5zdHJpbmdpZnkocGFyc2VkRGF0YSk7XG4gICAgfVxuXG4gICAgLy8gUmV0dXJuIGEgSlNPTlxuICAgIHJldHVybiBwYXJzZWREYXRhO1xuICB9IGNhdGNoIHtcbiAgICByZXR1cm4gZmFsc2U7XG4gIH1cbn1cblxuLyoqXG4gKiBDaGVja3MgaWYgdGhlIGdpdmVuIGl0ZW0gaXMgYW4gb2JqZWN0LlxuICpcbiAqIEBwYXJhbSB7YW55fSBpdGVtIC0gVGhlIGl0ZW0gdG8gYmUgY2hlY2tlZC5cbiAqXG4gKiBAcmV0dXJucyB7Ym9vbGVhbn0gLSBUcnVlIGlmIHRoZSBpdGVtIGlzIGFuIG9iamVjdCwgZmFsc2Ugb3RoZXJ3aXNlLlxuICovXG5leHBvcnQgY29uc3QgaXNPYmplY3QgPSAoaXRlbSkgPT5cbiAgdHlwZW9mIGl0ZW0gPT09ICdvYmplY3QnICYmICFBcnJheS5pc0FycmF5KGl0ZW0pICYmIGl0ZW0gIT09IG51bGw7XG5cbi8qKlxuICogQ2hlY2tzIGlmIHRoZSBnaXZlbiBvYmplY3QgaXMgZW1wdHkuXG4gKlxuICogQHBhcmFtIHtPYmplY3R9IGl0ZW0gLSBUaGUgb2JqZWN0IHRvIGJlIGNoZWNrZWQuXG4gKlxuICogQHJldHVybnMge2Jvb2xlYW59IC0gVHJ1ZSBpZiB0aGUgb2JqZWN0IGlzIGVtcHR5LCBmYWxzZSBvdGhlcndpc2UuXG4gKi9cbmV4cG9ydCBjb25zdCBpc09iamVjdEVtcHR5ID0gKGl0ZW0pID0+XG4gIHR5cGVvZiBpdGVtID09PSAnb2JqZWN0JyAmJlxuICAhQXJyYXkuaXNBcnJheShpdGVtKSAmJlxuICBpdGVtICE9PSBudWxsICYmXG4gIE9iamVjdC5rZXlzKGl0ZW0pLmxlbmd0aCA9PT0gMDtcblxuLyoqXG4gKiBDaGVja3MgaWYgYSBwcml2YXRlIElQIHJhbmdlIFVSTCBpcyBmb3VuZCBpbiB0aGUgZ2l2ZW4gc3RyaW5nLlxuICpcbiAqIEBwYXJhbSB7c3RyaW5nfSBpdGVtIC0gVGhlIHN0cmluZyB0byBiZSBjaGVja2VkIGZvciBhIHByaXZhdGUgSVAgcmFuZ2UgVVJMLlxuICpcbiAqIEByZXR1cm5zIHtib29sZWFufSAtIFRydWUgaWYgYSBwcml2YXRlIElQIHJhbmdlIFVSTCBpcyBmb3VuZCwgZmFsc2VcbiAqIG90aGVyd2lzZS5cbiAqL1xuZXhwb3J0IGNvbnN0IGlzUHJpdmF0ZVJhbmdlVXJsRm91bmQgPSAoaXRlbSkgPT4ge1xuICBjb25zdCByZWdleFBhdHRlcm5zID0gW1xuICAgIC94bGluazpocmVmPVwiKD86aHR0cDpcXC9cXC98aHR0cHM6XFwvXFwvKT9sb2NhbGhvc3RcXGIvLFxuICAgIC94bGluazpocmVmPVwiKD86aHR0cDpcXC9cXC98aHR0cHM6XFwvXFwvKT8xMFxcLlxcZHsxLDN9XFwuXFxkezEsM31cXC5cXGR7MSwzfVxcYi8sXG4gICAgL3hsaW5rOmhyZWY9XCIoPzpodHRwOlxcL1xcL3xodHRwczpcXC9cXC8pPzEyN1xcLlxcZHsxLDN9XFwuXFxkezEsM31cXC5cXGR7MSwzfVxcYi8sXG4gICAgL3hsaW5rOmhyZWY9XCIoPzpodHRwOlxcL1xcL3xodHRwczpcXC9cXC8pPzE3MlxcLigxWzYtOV18MlswLTldfDNbMC0xXSlcXC5cXGR7MSwzfVxcLlxcZHsxLDN9XFxiLyxcbiAgICAveGxpbms6aHJlZj1cIig/Omh0dHA6XFwvXFwvfGh0dHBzOlxcL1xcLyk/MTkyXFwuMTY4XFwuXFxkezEsM31cXC5cXGR7MSwzfVxcYi9cbiAgXTtcblxuICByZXR1cm4gcmVnZXhQYXR0ZXJucy5zb21lKChwYXR0ZXJuKSA9PiBwYXR0ZXJuLnRlc3QoaXRlbSkpO1xufTtcblxuLyoqXG4gKiBDcmVhdGVzIGEgZGVlcCBjb3B5IG9mIHRoZSBnaXZlbiBvYmplY3Qgb3IgYXJyYXkuXG4gKlxuICogQHBhcmFtIHtPYmplY3R8QXJyYXl9IG9iaiAtIFRoZSBvYmplY3Qgb3IgYXJyYXkgdG8gYmUgZGVlcGx5IGNvcGllZC5cbiAqXG4gKiBAcmV0dXJucyB7T2JqZWN0fEFycmF5fSAtIFRoZSBkZWVwIGNvcHkgb2YgdGhlIHByb3ZpZGVkIG9iamVjdCBvciBhcnJheS5cbiAqL1xuZXhwb3J0IGNvbnN0IGRlZXBDb3B5ID0gKG9iaikgPT4ge1xuICBpZiAob2JqID09PSBudWxsIHx8IHR5cGVvZiBvYmogIT09ICdvYmplY3QnKSB7XG4gICAgcmV0dXJuIG9iajtcbiAgfVxuXG4gIGNvbnN0IGNvcHkgPSBBcnJheS5pc0FycmF5KG9iaikgPyBbXSA6IHt9O1xuXG4gIGZvciAoY29uc3Qga2V5IGluIG9iaikge1xuICAgIGlmIChPYmplY3QucHJvdG90eXBlLmhhc093blByb3BlcnR5LmNhbGwob2JqLCBrZXkpKSB7XG4gICAgICBjb3B5W2tleV0gPSBkZWVwQ29weShvYmpba2V5XSk7XG4gICAgfVxuICB9XG5cbiAgcmV0dXJuIGNvcHk7XG59O1xuXG4vKipcbiAqIENvbnZlcnRzIHRoZSBwcm92aWRlZCBvcHRpb25zIG9iamVjdCB0byBhIEpTT04tZm9ybWF0dGVkIHN0cmluZyB3aXRoIHRoZVxuICogb3B0aW9uIHRvIHByZXNlcnZlIGZ1bmN0aW9ucy5cbiAqXG4gKiBAcGFyYW0ge09iamVjdH0gb3B0aW9ucyAtIFRoZSBvcHRpb25zIG9iamVjdCB0byBiZSBjb252ZXJ0ZWQgdG8gYSBzdHJpbmcuXG4gKiBAcGFyYW0ge2Jvb2xlYW59IGFsbG93RnVuY3Rpb25zIC0gSWYgc2V0IHRvIHRydWUsIGZ1bmN0aW9ucyBhcmUgcHJlc2VydmVkXG4gKiBpbiB0aGUgb3V0cHV0LlxuICpcbiAqIEByZXR1cm5zIHtzdHJpbmd9IC0gVGhlIEpTT04tZm9ybWF0dGVkIHN0cmluZyByZXByZXNlbnRpbmcgdGhlIG9wdGlvbnMuXG4gKi9cbmV4cG9ydCBjb25zdCBvcHRpb25zU3RyaW5naWZ5ID0gKG9wdGlvbnMsIGFsbG93RnVuY3Rpb25zKSA9PiB7XG4gIGNvbnN0IHJlcGxhY2VyQ2FsbGJhY2sgPSAobmFtZSwgdmFsdWUpID0+IHtcbiAgICBpZiAodHlwZW9mIHZhbHVlID09PSAnc3RyaW5nJykge1xuICAgICAgdmFsdWUgPSB2YWx1ZS50cmltKCk7XG5cbiAgICAgIC8vIElmIGFsbG93RnVuY3Rpb25zIGlzIHNldCB0byB0cnVlLCBwcmVzZXJ2ZSBmdW5jdGlvbnNcbiAgICAgIGlmIChcbiAgICAgICAgKHZhbHVlLnN0YXJ0c1dpdGgoJ2Z1bmN0aW9uKCcpIHx8IHZhbHVlLnN0YXJ0c1dpdGgoJ2Z1bmN0aW9uICgnKSkgJiZcbiAgICAgICAgdmFsdWUuZW5kc1dpdGgoJ30nKVxuICAgICAgKSB7XG4gICAgICAgIHZhbHVlID0gYWxsb3dGdW5jdGlvbnNcbiAgICAgICAgICA/IGBFWFBfRlVOJHsodmFsdWUgKyAnJykucmVwbGFjZUFsbCgvXFxufFxcdHxcXHIvZywgJyAnKX1FWFBfRlVOYFxuICAgICAgICAgIDogdW5kZWZpbmVkO1xuICAgICAgfVxuICAgIH1cblxuICAgIHJldHVybiB0eXBlb2YgdmFsdWUgPT09ICdmdW5jdGlvbidcbiAgICAgID8gYEVYUF9GVU4keyh2YWx1ZSArICcnKS5yZXBsYWNlQWxsKC9cXG58XFx0fFxcci9nLCAnICcpfUVYUF9GVU5gXG4gICAgICA6IHZhbHVlO1xuICB9O1xuXG4gIC8vIFN0cmluZ2lmeSBvcHRpb25zIGFuZCBpZiByZXF1aXJlZCwgcmVwbGFjZSBzcGVjaWFsIGZ1bmN0aW9ucyBtYXJrc1xuICByZXR1cm4gSlNPTi5zdHJpbmdpZnkob3B0aW9ucywgcmVwbGFjZXJDYWxsYmFjaykucmVwbGFjZUFsbChcbiAgICAvXCJFWFBfRlVOfEVYUF9GVU5cIi9nLFxuICAgICcnXG4gICk7XG59O1xuXG4vKipcbiAqIFByaW50cyB0aGUgSGlnaGNoYXJ0cyBFeHBvcnQgU2VydmVyIGxvZ28gYW5kIHZlcnNpb24gaW5mb3JtYXRpb24uXG4gKlxuICogQHBhcmFtIHtib29sZWFufSBub0xvZ28gLSBJZiB0cnVlLCBvbmx5IHByaW50cyB2ZXJzaW9uIGluZm9ybWF0aW9uIHdpdGhvdXRcbiAqIHRoZSBsb2dvLlxuICovXG5leHBvcnQgY29uc3QgcHJpbnRMb2dvID0gKG5vTG9nbykgPT4ge1xuICAvLyBHZXQgcGFja2FnZSB2ZXJzaW9uIGVpdGhlciBmcm9tIGVudiBvciBmcm9tIHBhY2thZ2UuanNvblxuICBjb25zdCBwYWNrYWdlVmVyc2lvbiA9IEpTT04ucGFyc2UoXG4gICAgcmVhZEZpbGVTeW5jKGpvaW4oX19kaXJuYW1lLCAncGFja2FnZS5qc29uJykpXG4gICkudmVyc2lvbjtcblxuICAvLyBQcmludCB0ZXh0IG9ubHlcbiAgaWYgKG5vTG9nbykge1xuICAgIGNvbnNvbGUubG9nKGBTdGFydGluZyBIaWdoY2hhcnRzIEV4cG9ydCBTZXJ2ZXIgdiR7cGFja2FnZVZlcnNpb259Li4uYCk7XG4gICAgcmV0dXJuO1xuICB9XG5cbiAgLy8gUHJpbnQgdGhlIGxvZ29cbiAgY29uc29sZS5sb2coXG4gICAgcmVhZEZpbGVTeW5jKF9fZGlybmFtZSArICcvbXNnL3N0YXJ0dXAubXNnJykudG9TdHJpbmcoKS5ib2xkLnllbGxvdyxcbiAgICBgdiR7cGFja2FnZVZlcnNpb259XFxuYC5ib2xkXG4gICk7XG59O1xuXG4vKipcbiAqIFByaW50cyB0aGUgdXNhZ2UgaW5mb3JtYXRpb24gZm9yIENMSSBhcmd1bWVudHMuIElmIHJlcXVpcmVkLCBpdCBjYW4gbGlzdFxuICogcHJvcGVydGllcyByZWN1cnNpdmVseVxuICovXG5leHBvcnQgZnVuY3Rpb24gcHJpbnRVc2FnZSgpIHtcbiAgY29uc3QgcGFkID0gNDg7XG4gIGNvbnN0IHJlYWRtZSA9ICdodHRwczovL2dpdGh1Yi5jb20vaGlnaGNoYXJ0cy9ub2RlLWV4cG9ydC1zZXJ2ZXIjcmVhZG1lJztcblxuICAvLyBEaXNwbGF5IHJlYWRtZSBpbmZvcm1hdGlvblxuICBjb25zb2xlLmxvZyhcbiAgICAnXFxuVXNhZ2Ugb2YgQ0xJIGFyZ3VtZW50czonLmJvbGQsXG4gICAgJ1xcbi0tLS0tLScsXG4gICAgYFxcbkZvciBtb3JlIGRldGFpbGVkIGluZm9ybWF0aW9uLCB2aXNpdCB0aGUgcmVhZG1lIGF0OiAke3JlYWRtZS5ib2xkLnllbGxvd30uYFxuICApO1xuXG4gIGNvbnN0IGN5Y2xlQ2F0ZWdvcmllcyA9IChvcHRpb25zKSA9PiB7XG4gICAgZm9yIChjb25zdCBbbmFtZSwgb3B0aW9uXSBvZiBPYmplY3QuZW50cmllcyhvcHRpb25zKSkge1xuICAgICAgLy8gSWYgY2F0ZWdvcnkgaGFzIG1vcmUgbGV2ZWxzLCBnbyBmdXJ0aGVyXG4gICAgICBpZiAoIU9iamVjdC5wcm90b3R5cGUuaGFzT3duUHJvcGVydHkuY2FsbChvcHRpb24sICd2YWx1ZScpKSB7XG4gICAgICAgIGN5Y2xlQ2F0ZWdvcmllcyhvcHRpb24pO1xuICAgICAgfSBlbHNlIHtcbiAgICAgICAgbGV0IGRlc2NOYW1lID0gYCAgLS0ke29wdGlvbi5jbGlOYW1lIHx8IG5hbWV9ICR7XG4gICAgICAgICAgKCc8JyArIG9wdGlvbi50eXBlICsgJz4nKS5ncmVlblxuICAgICAgICB9IGA7XG4gICAgICAgIGlmIChkZXNjTmFtZS5sZW5ndGggPCBwYWQpIHtcbiAgICAgICAgICBmb3IgKGxldCBpID0gZGVzY05hbWUubGVuZ3RoOyBpIDwgcGFkOyBpKyspIHtcbiAgICAgICAgICAgIGRlc2NOYW1lICs9ICcuJztcbiAgICAgICAgICB9XG4gICAgICAgIH1cblxuICAgICAgICAvLyBEaXNwbGF5IGNvcnJlY3RseSBhbGlnbmVkIG1lc3NhZ2VzXG4gICAgICAgIGNvbnNvbGUubG9nKFxuICAgICAgICAgIGRlc2NOYW1lLFxuICAgICAgICAgIG9wdGlvbi5kZXNjcmlwdGlvbixcbiAgICAgICAgICBgW0RlZmF1bHQ6ICR7b3B0aW9uLnZhbHVlLnRvU3RyaW5nKCkuYm9sZH1dYC5ibHVlXG4gICAgICAgICk7XG4gICAgICB9XG4gICAgfVxuICB9O1xuXG4gIC8vIEN5Y2xlIHRocm91Z2ggb3B0aW9ucyBvZiBlYWNoIGNhdGVnb3JpZXMgYW5kIGRpc3BsYXkgdGhlIHVzYWdlIGluZm9cbiAgT2JqZWN0LmtleXMoZGVmYXVsdENvbmZpZykuZm9yRWFjaCgoY2F0ZWdvcnkpID0+IHtcbiAgICAvLyBPbmx5IHB1cHBldGVlciBhbmQgaGlnaGNoYXJ0cyBjYXRlZ29yaWVzIGNhbm5vdCBiZSBjb25maWd1cmVkIHRocm91Z2ggQ0xJXG4gICAgaWYgKCFbJ3B1cHBldGVlcicsICdoaWdoY2hhcnRzJ10uaW5jbHVkZXMoY2F0ZWdvcnkpKSB7XG4gICAgICBjb25zb2xlLmxvZyhgXFxuJHtjYXRlZ29yeS50b1VwcGVyQ2FzZSgpfWAucmVkKTtcbiAgICAgIGN5Y2xlQ2F0ZWdvcmllcyhkZWZhdWx0Q29uZmlnW2NhdGVnb3J5XSk7XG4gICAgfVxuICB9KTtcbiAgY29uc29sZS5sb2coJ1xcbicpO1xufVxuXG4vKipcbiAqIFJvdW5kcyBhIG51bWJlciB0byB0aGUgc3BlY2lmaWVkIHByZWNpc2lvbi5cbiAqXG4gKiBAcGFyYW0ge251bWJlcn0gdmFsdWUgLSBUaGUgbnVtYmVyIHRvIGJlIHJvdW5kZWQuXG4gKiBAcGFyYW0ge251bWJlcn0gcHJlY2lzaW9uIC0gVGhlIG51bWJlciBvZiBkZWNpbWFsIHBsYWNlcyB0byByb3VuZCB0by5cbiAqXG4gKiBAcmV0dXJucyB7bnVtYmVyfSAtIFRoZSByb3VuZGVkIG51bWJlci5cbiAqL1xuZXhwb3J0IGNvbnN0IHJvdW5kTnVtYmVyID0gKHZhbHVlLCBwcmVjaXNpb24gPSAxKSA9PiB7XG4gIGNvbnN0IG11bHRpcGxpZXIgPSBNYXRoLnBvdygxMCwgcHJlY2lzaW9uIHx8IDApO1xuICByZXR1cm4gTWF0aC5yb3VuZCgrdmFsdWUgKiBtdWx0aXBsaWVyKSAvIG11bHRpcGxpZXI7XG59O1xuXG4vKipcbiAqIENvbnZlcnRzIGEgdmFsdWUgdG8gYSBib29sZWFuLlxuICpcbiAqIEBwYXJhbSB7YW55fSBpdGVtIC0gVGhlIHZhbHVlIHRvIGJlIGNvbnZlcnRlZCB0byBhIGJvb2xlYW4uXG4gKlxuICogQHJldHVybnMge2Jvb2xlYW59IC0gVGhlIGJvb2xlYW4gcmVwcmVzZW50YXRpb24gb2YgdGhlIGlucHV0IHZhbHVlLlxuICovXG5leHBvcnQgY29uc3QgdG9Cb29sZWFuID0gKGl0ZW0pID0+XG4gIFsnZmFsc2UnLCAndW5kZWZpbmVkJywgJ251bGwnLCAnTmFOJywgJzAnLCAnJ10uaW5jbHVkZXMoaXRlbSlcbiAgICA/IGZhbHNlXG4gICAgOiAhIWl0ZW07XG5cbi8qKlxuICogV3JhcHMgY3VzdG9tIGNvZGUgdG8gZXhlY3V0ZSBpdCBzYWZlbHkuXG4gKlxuICogQHBhcmFtIHtzdHJpbmd9IGN1c3RvbUNvZGUgLSBUaGUgY3VzdG9tIGNvZGUgdG8gYmUgd3JhcHBlZC5cbiAqIEBwYXJhbSB7Ym9vbGVhbn0gYWxsb3dGaWxlUmVzb3VyY2VzIC0gRmxhZyB0byBhbGxvdyBsb2FkaW5nIGNvZGUgZnJvbSBhIGZpbGUuXG4gKlxuICogQHJldHVybnMge3N0cmluZ3xib29sZWFufSAtIFRoZSB3cmFwcGVkIGN1c3RvbSBjb2RlIG9yIGZhbHNlIGlmIHdyYXBwaW5nXG4gKiBmYWlscy5cbiAqL1xuZXhwb3J0IGNvbnN0IHdyYXBBcm91bmQgPSAoY3VzdG9tQ29kZSwgYWxsb3dGaWxlUmVzb3VyY2VzKSA9PiB7XG4gIGlmIChjdXN0b21Db2RlICYmIHR5cGVvZiBjdXN0b21Db2RlID09PSAnc3RyaW5nJykge1xuICAgIGN1c3RvbUNvZGUgPSBjdXN0b21Db2RlLnRyaW0oKTtcblxuICAgIGlmIChjdXN0b21Db2RlLmVuZHNXaXRoKCcuanMnKSkge1xuICAgICAgcmV0dXJuIGFsbG93RmlsZVJlc291cmNlc1xuICAgICAgICA/IHdyYXBBcm91bmQocmVhZEZpbGVTeW5jKGN1c3RvbUNvZGUsICd1dGY4JykpXG4gICAgICAgIDogZmFsc2U7XG4gICAgfSBlbHNlIGlmIChcbiAgICAgIGN1c3RvbUNvZGUuc3RhcnRzV2l0aCgnZnVuY3Rpb24oKScpIHx8XG4gICAgICBjdXN0b21Db2RlLnN0YXJ0c1dpdGgoJ2Z1bmN0aW9uICgpJykgfHxcbiAgICAgIGN1c3RvbUNvZGUuc3RhcnRzV2l0aCgnKCk9PicpIHx8XG4gICAgICBjdXN0b21Db2RlLnN0YXJ0c1dpdGgoJygpID0+JylcbiAgICApIHtcbiAgICAgIHJldHVybiBgKCR7Y3VzdG9tQ29kZX0pKClgO1xuICAgIH1cbiAgICByZXR1cm4gY3VzdG9tQ29kZS5yZXBsYWNlKC87JC8sICcnKTtcbiAgfVxufTtcblxuLyoqXG4gKiBVdGlsaXR5IHRvIG1lYXN1cmUgZWxhcHNlZCB0aW1lIHVzaW5nIHRoZSBOb2RlLmpzIHByb2Nlc3MuaHJ0aW1lKCkgbWV0aG9kLlxuICpcbiAqIEByZXR1cm5zIHtmdW5jdGlvbigpOiBudW1iZXJ9IC0gQSBmdW5jdGlvbiB0byBjYWxjdWxhdGUgdGhlIGVsYXBzZWQgdGltZVxuICogaW4gbWlsbGlzZWNvbmRzLlxuICovXG5leHBvcnQgY29uc3QgbWVhc3VyZVRpbWUgPSAoKSA9PiB7XG4gIGNvbnN0IHN0YXJ0ID0gcHJvY2Vzcy5ocnRpbWUuYmlnaW50KCk7XG4gIHJldHVybiAoKSA9PiBOdW1iZXIocHJvY2Vzcy5ocnRpbWUuYmlnaW50KCkgLSBzdGFydCkgLyAxMDAwMDAwO1xufTtcblxuZXhwb3J0IGRlZmF1bHQge1xuICBfX2hpZ2hjaGFydHNEaXIsXG4gIF9fZGlybmFtZSxcbiAgY2xlYXJUZXh0LFxuICBleHBCYWNrb2ZmLFxuICBmaXhUeXBlLFxuICBoYW5kbGVSZXNvdXJjZXMsXG4gIGlzQ29ycmVjdEpTT04sXG4gIGlzT2JqZWN0LFxuICBpc09iamVjdEVtcHR5LFxuICBpc1ByaXZhdGVSYW5nZVVybEZvdW5kLFxuICBvcHRpb25zU3RyaW5naWZ5LFxuICBwcmludExvZ28sXG4gIHByaW50VXNhZ2UsXG4gIHJvdW5kTnVtYmVyLFxuICB0b0Jvb2xlYW4sXG4gIHdyYXBBcm91bmQsXG4gIG1lYXN1cmVUaW1lXG59O1xuIiwiLyoqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKipcblxuSGlnaGNoYXJ0cyBFeHBvcnQgU2VydmVyXG5cbkNvcHlyaWdodCAoYykgMjAxNi0yMDI0LCBIaWdoc29mdFxuXG5MaWNlbmNlZCB1bmRlciB0aGUgTUlUIGxpY2VuY2UuXG5cbkFkZGl0aW9uYWxseSBhIHZhbGlkIEhpZ2hjaGFydHMgbGljZW5zZSBpcyByZXF1aXJlZCBmb3IgdXNlLlxuXG5TZWUgTElDRU5TRSBmaWxlIGluIHJvb3QgZm9yIGRldGFpbHMuXG5cbioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKiovXG5cbmltcG9ydCB7IGV4aXN0c1N5bmMsIHJlYWRGaWxlU3luYywgcHJvbWlzZXMgYXMgZnNQcm9taXNlcyB9IGZyb20gJ2ZzJztcblxuaW1wb3J0IHByb21wdHMgZnJvbSAncHJvbXB0cyc7XG5cbmltcG9ydCB7XG4gIGFic29sdXRlUHJvcHMsXG4gIGRlZmF1bHRDb25maWcsXG4gIG5lc3RlZEFyZ3MsXG4gIHByb21wdHNDb25maWdcbn0gZnJvbSAnLi9zY2hlbWFzL2NvbmZpZy5qcyc7XG5pbXBvcnQgeyBlbnZzIH0gZnJvbSAnLi9lbnZzLmpzJztcbmltcG9ydCB7IGxvZywgbG9nV2l0aFN0YWNrIH0gZnJvbSAnLi9sb2dnZXIuanMnO1xuaW1wb3J0IHsgZGVlcENvcHksIGlzT2JqZWN0LCBwcmludFVzYWdlLCB0b0Jvb2xlYW4gfSBmcm9tICcuL3V0aWxzLmpzJztcblxubGV0IGdlbmVyYWxPcHRpb25zID0ge307XG5cbi8qKlxuICogUmV0cmlldmVzIGFuZCByZXR1cm5zIHRoZSBnZW5lcmFsIG9wdGlvbnMgZm9yIHRoZSBleHBvcnQgcHJvY2Vzcy5cbiAqXG4gKiBAcmV0dXJucyB7T2JqZWN0fSBUaGUgZ2VuZXJhbCBvcHRpb25zIG9iamVjdC5cbiAqL1xuZXhwb3J0IGNvbnN0IGdldE9wdGlvbnMgPSAoKSA9PiBnZW5lcmFsT3B0aW9ucztcblxuLyoqXG4gKiBJbml0aWFsaXplcyBhbmQgc2V0cyB0aGUgZ2VuZXJhbCBvcHRpb25zIGZvciB0aGUgc2VydmVyIGluc3RhY2UsIGtlZXBpbmdcbiAqIHRoZSBwcmluY2lwbGUgb2YgdGhlIG9wdGlvbnMgbG9hZCBwcmlvcml0eS4gSXQgYWNjZXB0cyBvcHRpb25hbCB1c2VyT3B0aW9uc1xuICogYW5kIGFyZ3MgZnJvbSB0aGUgQ0xJLlxuICpcbiAqIEBwYXJhbSB7T2JqZWN0fSB1c2VyT3B0aW9ucyAtIFVzZXItcHJvdmlkZWQgb3B0aW9ucyBmb3IgY3VzdG9taXphdGlvbi5cbiAqIEBwYXJhbSB7QXJyYXl9IGFyZ3MgLSBDb21tYW5kLWxpbmUgYXJndW1lbnRzIGZvciBhZGRpdGlvbmFsIGNvbmZpZ3VyYXRpb25cbiAqIChDTEkgdXNhZ2UpLlxuICpcbiAqIEByZXR1cm5zIHtPYmplY3R9IFRoZSB1cGRhdGVkIGdlbmVyYWwgb3B0aW9ucyBvYmplY3QuXG4gKi9cbmV4cG9ydCBjb25zdCBzZXRPcHRpb25zID0gKHVzZXJPcHRpb25zLCBhcmdzKSA9PiB7XG4gIC8vIE9ubHkgZm9yIHRoZSBDTEkgdXNhZ2VcbiAgaWYgKGFyZ3M/Lmxlbmd0aCkge1xuICAgIC8vIEdldCB0aGUgYWRkaXRpb25hbCBvcHRpb25zIGZyb20gdGhlIGN1c3RvbSBKU09OIGZpbGVcbiAgICBnZW5lcmFsT3B0aW9ucyA9IGxvYWRDb25maWdGaWxlKGFyZ3MpO1xuICB9XG5cbiAgLy8gVXBkYXRlIHRoZSBkZWZhdWx0IGNvbmZpZyB3aXRoIGEgY29ycmVjdCBvcHRpb24gdmFsdWVzXG4gIHVwZGF0ZURlZmF1bHRDb25maWcoZGVmYXVsdENvbmZpZywgZ2VuZXJhbE9wdGlvbnMpO1xuXG4gIC8vIFNldCB2YWx1ZXMgZm9yIHNlcnZlcidzIG9wdGlvbnMgYW5kIHJldHVybnMgdGhlbVxuICBnZW5lcmFsT3B0aW9ucyA9IGluaXRPcHRpb25zKGRlZmF1bHRDb25maWcpO1xuXG4gIC8vIEFwcGx5IHVzZXIgb3B0aW9ucyBpZiB0aGVyZSBhcmUgYW55XG4gIGlmICh1c2VyT3B0aW9ucykge1xuICAgIC8vIE1lcmdlIHVzZXIgb3B0aW9uc1xuICAgIGdlbmVyYWxPcHRpb25zID0gbWVyZ2VDb25maWdPcHRpb25zKFxuICAgICAgZ2VuZXJhbE9wdGlvbnMsXG4gICAgICB1c2VyT3B0aW9ucyxcbiAgICAgIGFic29sdXRlUHJvcHNcbiAgICApO1xuICB9XG5cbiAgLy8gT25seSBmb3IgdGhlIENMSSB1c2FnZVxuICBpZiAoYXJncz8ubGVuZ3RoKSB7XG4gICAgLy8gUGFpciBwcm92aWRlZCBhcmd1bWVudHNcbiAgICBnZW5lcmFsT3B0aW9ucyA9IHBhaXJBcmd1bWVudFZhbHVlKGdlbmVyYWxPcHRpb25zLCBhcmdzLCBkZWZhdWx0Q29uZmlnKTtcbiAgfVxuXG4gIC8vIFJldHVybiBmaW5hbCBnZW5lcmFsIG9wdGlvbnNcbiAgcmV0dXJuIGdlbmVyYWxPcHRpb25zO1xufTtcblxuLyoqXG4gKiBBbGxvd3MgbWFudWFsIGNvbmZpZ3VyYXRpb24gYmFzZWQgb24gc3BlY2lmaWVkIHByb21wdHMgYW5kIHNhdmVzXG4gKiB0aGUgY29uZmlndXJhdGlvbiB0byBhIGZpbGUuXG4gKlxuICogQHBhcmFtIHtzdHJpbmd9IGNvbmZpZ0ZpbGVOYW1lIC0gVGhlIG5hbWUgb2YgdGhlIGNvbmZpZ3VyYXRpb24gZmlsZS5cbiAqXG4gKiBAcmV0dXJucyB7UHJvbWlzZTxib29sZWFuPn0gQSBQcm9taXNlIHRoYXQgcmVzb2x2ZXMgdG8gdHJ1ZSBvbmNlIHRoZSBtYW51YWxcbiAqIGNvbmZpZ3VyYXRpb24gaXMgY29tcGxldGVkIGFuZCBzYXZlZC5cbiAqL1xuZXhwb3J0IGNvbnN0IG1hbnVhbENvbmZpZyA9IGFzeW5jIChjb25maWdGaWxlTmFtZSkgPT4ge1xuICAvLyBQcmVwYXJlIGEgY29uZmlnIG9iamVjdFxuICBsZXQgY29uZmlnRmlsZSA9IHt9O1xuXG4gIC8vIENoZWNrIGlmIHByb3ZpZGVkIGNvbmZpZyBmaWxlIGV4aXN0c1xuICBpZiAoZXhpc3RzU3luYyhjb25maWdGaWxlTmFtZSkpIHtcbiAgICBjb25maWdGaWxlID0gSlNPTi5wYXJzZShyZWFkRmlsZVN5bmMoY29uZmlnRmlsZU5hbWUsICd1dGY4JykpO1xuICB9XG5cbiAgLy8gUXVlc3Rpb24gYWJvdXQgYSBjb25maWd1cmF0aW9uIGNhdGVnb3J5XG4gIGNvbnN0IG9uU3VibWl0ID0gYXN5bmMgKHAsIGNhdGVnb3JpZXMpID0+IHtcbiAgICBsZXQgcXVlc3Rpb25zQ291bnRlciA9IDA7XG4gICAgbGV0IGFsbFF1ZXN0aW9ucyA9IFtdO1xuXG4gICAgLy8gQ3JlYXRlIGEgY29ycmVzcG9uZGluZyBwcm9wZXJ0eSBpbiB0aGUgbWFudWFsQ29uZmlnIG9iamVjdFxuICAgIGZvciAoY29uc3Qgc2VjdGlvbiBvZiBjYXRlZ29yaWVzKSB7XG4gICAgICAvLyBNYXJrIGVhY2ggb3B0aW9uIHdpdGggYSBzZWN0aW9uXG4gICAgICBwcm9tcHRzQ29uZmlnW3NlY3Rpb25dID0gcHJvbXB0c0NvbmZpZ1tzZWN0aW9uXS5tYXAoKG9wdGlvbikgPT4gKHtcbiAgICAgICAgLi4ub3B0aW9uLFxuICAgICAgICBzZWN0aW9uXG4gICAgICB9KSk7XG5cbiAgICAgIC8vIENvbGxlY3QgdGhlIHF1ZXN0aW9uc1xuICAgICAgYWxsUXVlc3Rpb25zID0gWy4uLmFsbFF1ZXN0aW9ucywgLi4ucHJvbXB0c0NvbmZpZ1tzZWN0aW9uXV07XG4gICAgfVxuXG4gICAgYXdhaXQgcHJvbXB0cyhhbGxRdWVzdGlvbnMsIHtcbiAgICAgIG9uU3VibWl0OiBhc3luYyAocHJvbXB0LCBhbnN3ZXIpID0+IHtcbiAgICAgICAgLy8gR2V0IHRoZSBkZWZhdWx0IG1vZHVsZSBzY3JpcHRzXG4gICAgICAgIGlmIChwcm9tcHQubmFtZSA9PT0gJ21vZHVsZVNjcmlwdHMnKSB7XG4gICAgICAgICAgYW5zd2VyID0gYW5zd2VyLmxlbmd0aFxuICAgICAgICAgICAgPyBhbnN3ZXIubWFwKChtb2R1bGUpID0+IHByb21wdC5jaG9pY2VzW21vZHVsZV0pXG4gICAgICAgICAgICA6IHByb21wdC5jaG9pY2VzO1xuXG4gICAgICAgICAgY29uZmlnRmlsZVtwcm9tcHQuc2VjdGlvbl1bcHJvbXB0Lm5hbWVdID0gYW5zd2VyO1xuICAgICAgICB9IGVsc2Uge1xuICAgICAgICAgIGNvbmZpZ0ZpbGVbcHJvbXB0LnNlY3Rpb25dID0gcmVjdXJzaXZlUHJvcHMoXG4gICAgICAgICAgICBPYmplY3QuYXNzaWduKHt9LCBjb25maWdGaWxlW3Byb21wdC5zZWN0aW9uXSB8fCB7fSksXG4gICAgICAgICAgICBwcm9tcHQubmFtZS5zcGxpdCgnLicpLFxuICAgICAgICAgICAgcHJvbXB0LmNob2ljZXMgPyBwcm9tcHQuY2hvaWNlc1thbnN3ZXJdIDogYW5zd2VyXG4gICAgICAgICAgKTtcbiAgICAgICAgfVxuXG4gICAgICAgIGlmICgrK3F1ZXN0aW9uc0NvdW50ZXIgPT09IGFsbFF1ZXN0aW9ucy5sZW5ndGgpIHtcbiAgICAgICAgICB0cnkge1xuICAgICAgICAgICAgYXdhaXQgZnNQcm9taXNlcy53cml0ZUZpbGUoXG4gICAgICAgICAgICAgIGNvbmZpZ0ZpbGVOYW1lLFxuICAgICAgICAgICAgICBKU09OLnN0cmluZ2lmeShjb25maWdGaWxlLCBudWxsLCAyKSxcbiAgICAgICAgICAgICAgJ3V0ZjgnXG4gICAgICAgICAgICApO1xuICAgICAgICAgIH0gY2F0Y2ggKGVycm9yKSB7XG4gICAgICAgICAgICBsb2dXaXRoU3RhY2soXG4gICAgICAgICAgICAgIDEsXG4gICAgICAgICAgICAgIGVycm9yLFxuICAgICAgICAgICAgICBgW2NvbmZpZ10gQW4gZXJyb3Igb2NjdXJyZWQgd2hpbGUgY3JlYXRpbmcgdGhlICR7Y29uZmlnRmlsZU5hbWV9IGZpbGUuYFxuICAgICAgICAgICAgKTtcbiAgICAgICAgICB9XG4gICAgICAgICAgcmV0dXJuIHRydWU7XG4gICAgICAgIH1cbiAgICAgIH1cbiAgICB9KTtcblxuICAgIHJldHVybiB0cnVlO1xuICB9O1xuXG4gIC8vIEZpbmQgdGhlIGNhdGVnb3JpZXNcbiAgY29uc3QgY2hvaWNlcyA9IE9iamVjdC5rZXlzKHByb21wdHNDb25maWcpLm1hcCgoY2hvaWNlKSA9PiAoe1xuICAgIHRpdGxlOiBgJHtjaG9pY2V9IG9wdGlvbnNgLFxuICAgIHZhbHVlOiBjaG9pY2VcbiAgfSkpO1xuXG4gIC8vIENhdGVnb3J5IHByb21wdFxuICByZXR1cm4gcHJvbXB0cyhcbiAgICB7XG4gICAgICB0eXBlOiAnbXVsdGlzZWxlY3QnLFxuICAgICAgbmFtZTogJ2NhdGVnb3J5JyxcbiAgICAgIG1lc3NhZ2U6ICdXaGljaCBjYXRlZ29yeSBkbyB5b3Ugd2FudCB0byBjb25maWd1cmU/JyxcbiAgICAgIGhpbnQ6ICdTcGFjZTogU2VsZWN0IHNwZWNpZmljLCBBOiBTZWxlY3QgYWxsLCBFbnRlcjogQ29uZmlybS4nLFxuICAgICAgaW5zdHJ1Y3Rpb25zOiAnJyxcbiAgICAgIGNob2ljZXNcbiAgICB9LFxuICAgIHsgb25TdWJtaXQgfVxuICApO1xufTtcblxuLyoqXG4gKiBNYXBzIG9sZC1zdHJ1Y3R1cmVkIChQaGFudG9tSlMpIG9wdGlvbnMgdG8gYSBuZXcgY29uZmlndXJhdGlvbiBmb3JtYXRcbiAqIChQdXBwZXRlZXIpLlxuICpcbiAqIEBwYXJhbSB7T2JqZWN0fSBvbGRPcHRpb25zIC0gT2xkLXN0cnVjdHVyZWQgb3B0aW9ucyB0byBiZSBtYXBwZWQuXG4gKlxuICogQHJldHVybnMge09iamVjdH0gTmV3IG9wdGlvbnMgc3RydWN0dXJlZCBiYXNlZCBvbiB0aGUgZGVmaW5lZCBuZXN0ZWRBcmdzXG4gKiBtYXBwaW5nLlxuICovXG5leHBvcnQgY29uc3QgbWFwVG9OZXdDb25maWcgPSAob2xkT3B0aW9ucykgPT4ge1xuICBjb25zdCBuZXdPcHRpb25zID0ge307XG4gIC8vIEN5Y2xlIHRocm91Z2ggb2xkLXN0cnVjdHVyZWQgb3B0aW9uc1xuICBmb3IgKGNvbnN0IFtrZXksIHZhbHVlXSBvZiBPYmplY3QuZW50cmllcyhvbGRPcHRpb25zKSkge1xuICAgIGNvbnN0IHByb3BlcnRpZXNDaGFpbiA9IG5lc3RlZEFyZ3Nba2V5XSA/IG5lc3RlZEFyZ3Nba2V5XS5zcGxpdCgnLicpIDogW107XG5cbiAgICAvLyBQb3B1bGF0ZSBvYmplY3QgaW4gY29ycmVjdCBwcm9wZXJ0aWVzIGxldmVsc1xuICAgIHByb3BlcnRpZXNDaGFpbi5yZWR1Y2UoXG4gICAgICAob2JqLCBwcm9wLCBpbmRleCkgPT5cbiAgICAgICAgKG9ialtwcm9wXSA9XG4gICAgICAgICAgcHJvcGVydGllc0NoYWluLmxlbmd0aCAtIDEgPT09IGluZGV4ID8gdmFsdWUgOiBvYmpbcHJvcF0gfHwge30pLFxuICAgICAgbmV3T3B0aW9uc1xuICAgICk7XG4gIH1cbiAgcmV0dXJuIG5ld09wdGlvbnM7XG59O1xuXG4vKipcbiAqIE1lcmdlcyB0d28gc2V0cyBvZiBjb25maWd1cmF0aW9uIG9wdGlvbnMsIGNvbnNpZGVyaW5nIGFic29sdXRlIHByb3BlcnRpZXMuXG4gKlxuICogQHBhcmFtIHtPYmplY3R9IG9wdGlvbnMgLSBPcmlnaW5hbCBjb25maWd1cmF0aW9uIG9wdGlvbnMuXG4gKiBAcGFyYW0ge09iamVjdH0gbmV3T3B0aW9ucyAtIE5ldyBjb25maWd1cmF0aW9uIG9wdGlvbnMgdG8gYmUgbWVyZ2VkLlxuICogQHBhcmFtIHtBcnJheX0gYWJzb2x1dGVQcm9wcyAtIExpc3Qgb2YgcHJvcGVydGllcyB0aGF0IHNob3VsZFxuICogbm90IGJlIHJlY3Vyc2l2ZWx5IG1lcmdlZC5cbiAqXG4gKiBAcmV0dXJucyB7T2JqZWN0fSBNZXJnZWQgY29uZmlndXJhdGlvbiBvcHRpb25zLlxuICovXG5leHBvcnQgY29uc3QgbWVyZ2VDb25maWdPcHRpb25zID0gKG9wdGlvbnMsIG5ld09wdGlvbnMsIGFic29sdXRlUHJvcHMgPSBbXSkgPT4ge1xuICBjb25zdCBtZXJnZWRPcHRpb25zID0gZGVlcENvcHkob3B0aW9ucyk7XG5cbiAgZm9yIChjb25zdCBba2V5LCB2YWx1ZV0gb2YgT2JqZWN0LmVudHJpZXMobmV3T3B0aW9ucykpIHtcbiAgICBtZXJnZWRPcHRpb25zW2tleV0gPVxuICAgICAgaXNPYmplY3QodmFsdWUpICYmXG4gICAgICAhYWJzb2x1dGVQcm9wcy5pbmNsdWRlcyhrZXkpICYmXG4gICAgICBtZXJnZWRPcHRpb25zW2tleV0gIT09IHVuZGVmaW5lZFxuICAgICAgICA/IG1lcmdlQ29uZmlnT3B0aW9ucyhtZXJnZWRPcHRpb25zW2tleV0sIHZhbHVlLCBhYnNvbHV0ZVByb3BzKVxuICAgICAgICA6IHZhbHVlICE9PSB1bmRlZmluZWRcbiAgICAgICAgICA/IHZhbHVlXG4gICAgICAgICAgOiBtZXJnZWRPcHRpb25zW2tleV07XG4gIH1cblxuICByZXR1cm4gbWVyZ2VkT3B0aW9ucztcbn07XG5cbi8qKlxuICogSW5pdGlhbGl6ZXMgZXhwb3J0IHNldHRpbmdzIGJhc2VkIG9uIHByb3ZpZGVkIGV4cG9ydE9wdGlvbnNcbiAqIGFuZCBnZW5lcmFsT3B0aW9ucy5cbiAqXG4gKiBAcGFyYW0ge09iamVjdH0gZXhwb3J0T3B0aW9ucyAtIE9wdGlvbnMgc3BlY2lmaWMgdG8gdGhlIGV4cG9ydCBwcm9jZXNzLlxuICogQHBhcmFtIHtPYmplY3R9IGdlbmVyYWxPcHRpb25zIC0gR2VuZXJhbCBjb25maWd1cmF0aW9uIG9wdGlvbnMuXG4gKlxuICogQHJldHVybnMge09iamVjdH0gSW5pdGlhbGl6ZWQgZXhwb3J0IHNldHRpbmdzLlxuICovXG5leHBvcnQgY29uc3QgaW5pdEV4cG9ydFNldHRpbmdzID0gKGV4cG9ydE9wdGlvbnMsIGdlbmVyYWxPcHRpb25zID0ge30pID0+IHtcbiAgbGV0IG9wdGlvbnMgPSB7fTtcblxuICBpZiAoZXhwb3J0T3B0aW9ucy5zdmcpIHtcbiAgICBvcHRpb25zID0gZGVlcENvcHkoZ2VuZXJhbE9wdGlvbnMpO1xuICAgIG9wdGlvbnMuZXhwb3J0LnR5cGUgPSBleHBvcnRPcHRpb25zLnR5cGUgfHwgZXhwb3J0T3B0aW9ucy5leHBvcnQudHlwZTtcbiAgICBvcHRpb25zLmV4cG9ydC5zY2FsZSA9IGV4cG9ydE9wdGlvbnMuc2NhbGUgfHwgZXhwb3J0T3B0aW9ucy5leHBvcnQuc2NhbGU7XG4gICAgb3B0aW9ucy5leHBvcnQub3V0ZmlsZSA9XG4gICAgICBleHBvcnRPcHRpb25zLm91dGZpbGUgfHwgZXhwb3J0T3B0aW9ucy5leHBvcnQub3V0ZmlsZTtcbiAgICBvcHRpb25zLnBheWxvYWQgPSB7XG4gICAgICBzdmc6IGV4cG9ydE9wdGlvbnMuc3ZnXG4gICAgfTtcbiAgfSBlbHNlIHtcbiAgICBvcHRpb25zID0gbWVyZ2VDb25maWdPcHRpb25zKFxuICAgICAgZ2VuZXJhbE9wdGlvbnMsXG4gICAgICBleHBvcnRPcHRpb25zLFxuICAgICAgLy8gT21pdCBnb2luZyBkb3duIHJlY3Vyc2l2ZWx5IHdpdGggdGhlIGJlbG93c1xuICAgICAgYWJzb2x1dGVQcm9wc1xuICAgICk7XG4gIH1cblxuICBvcHRpb25zLmV4cG9ydC5vdXRmaWxlID1cbiAgICBvcHRpb25zLmV4cG9ydD8ub3V0ZmlsZSB8fCBgY2hhcnQuJHtvcHRpb25zLmV4cG9ydD8udHlwZSB8fCAncG5nJ31gO1xuICByZXR1cm4gb3B0aW9ucztcbn07XG5cbi8qKlxuICogTG9hZHMgYWRkaXRpb25hbCBjb25maWd1cmF0aW9uIGZyb20gYSBzcGVjaWZpZWQgZmlsZSB1c2luZ1xuICogdGhlIC0tbG9hZENvbmZpZyBvcHRpb24uXG4gKlxuICogQHBhcmFtIHtBcnJheX0gYXJncyAtIENvbW1hbmQtbGluZSBhcmd1bWVudHMgdG8gY2hlY2sgZm9yXG4gKiB0aGUgLS1sb2FkQ29uZmlnIG9wdGlvbi5cbiAqXG4gKiBAcmV0dXJucyB7T2JqZWN0fSBBZGRpdGlvbmFsIGNvbmZpZ3VyYXRpb24gbG9hZGVkIGZyb20gdGhlIHNwZWNpZmllZCBmaWxlLFxuICogb3IgYW4gZW1wdHkgb2JqZWN0IGlmIG5vdCBmb3VuZCBvciBpbnZhbGlkLlxuICovXG5mdW5jdGlvbiBsb2FkQ29uZmlnRmlsZShhcmdzKSB7XG4gIC8vIENoZWNrIGlmIHRoZSAtLWxvYWRDb25maWcgb3B0aW9uIHdhcyB1c2VkXG4gIGNvbnN0IGNvbmZpZ0luZGV4ID0gYXJncy5maW5kSW5kZXgoXG4gICAgKGFyZykgPT4gYXJnLnJlcGxhY2UoLy0vZywgJycpID09PSAnbG9hZENvbmZpZydcbiAgKTtcblxuICAvLyBDaGVjayBpZiB0aGUgLS1sb2FkQ29uZmlnIGhhcyBhIHZhbHVlXG4gIGlmIChjb25maWdJbmRleCA+IC0xICYmIGFyZ3NbY29uZmlnSW5kZXggKyAxXSkge1xuICAgIGNvbnN0IGZpbGVOYW1lID0gYXJnc1tjb25maWdJbmRleCArIDFdO1xuICAgIHRyeSB7XG4gICAgICAvLyBDaGVjayBpZiBhbiBhZGRpdGlvbmFsIGNvbmZpZyBmaWxlIGlzIGEgY29ycmVjdCBKU09OIGZpbGVcbiAgICAgIGlmIChmaWxlTmFtZSAmJiBmaWxlTmFtZS5lbmRzV2l0aCgnLmpzb24nKSkge1xuICAgICAgICAvLyBMb2FkIGFuIG9wdGlvbmFsIGN1c3RvbSBKU09OIGNvbmZpZyBmaWxlXG4gICAgICAgIHJldHVybiBKU09OLnBhcnNlKHJlYWRGaWxlU3luYyhmaWxlTmFtZSkpO1xuICAgICAgfVxuICAgIH0gY2F0Y2ggKGVycm9yKSB7XG4gICAgICBsb2dXaXRoU3RhY2soXG4gICAgICAgIDIsXG4gICAgICAgIGVycm9yLFxuICAgICAgICBgW2NvbmZpZ10gVW5hYmxlIHRvIGxvYWQgdGhlIGNvbmZpZ3VyYXRpb24gZnJvbSB0aGUgJHtmaWxlTmFtZX0gZmlsZS5gXG4gICAgICApO1xuICAgIH1cbiAgfVxuXG4gIC8vIE5vIGFkZGl0aW9uYWwgb3B0aW9ucyB0byByZXR1cm5cbiAgcmV0dXJuIHt9O1xufVxuXG4vKipcbiAqIFVwZGF0ZXMgdGhlIGRlZmF1bHQgY29uZmlndXJhdGlvbiBvYmplY3Qgd2l0aCB2YWx1ZXMgZnJvbSBhIGN1c3RvbSBvYmplY3RcbiAqIGFuZCBlbnZpcm9ubWVudCB2YXJpYWJsZXMuXG4gKlxuICogQHBhcmFtIHtPYmplY3R9IGNvbmZpZ09iaiAtIFRoZSBkZWZhdWx0IGNvbmZpZ3VyYXRpb24gb2JqZWN0LlxuICogQHBhcmFtIHtPYmplY3R9IGN1c3RvbU9iaiAtIEN1c3RvbSBjb25maWd1cmF0aW9uIG9iamVjdCB0byBvdmVycmlkZSBkZWZhdWx0cy5cbiAqIEBwYXJhbSB7c3RyaW5nfSBwcm9wQ2hhaW4gLSBQcm9wZXJ0eSBjaGFpbiBmb3IgdHJhY2tpbmcgbmVzdGVkIHByb3BlcnRpZXNcbiAqIGR1cmluZyByZWN1cnNpb24uXG4gKi9cbmZ1bmN0aW9uIHVwZGF0ZURlZmF1bHRDb25maWcoY29uZmlnT2JqLCBjdXN0b21PYmogPSB7fSwgcHJvcENoYWluID0gJycpIHtcbiAgT2JqZWN0LmtleXMoY29uZmlnT2JqKS5mb3JFYWNoKChrZXkpID0+IHtcbiAgICBjb25zdCBlbnRyeSA9IGNvbmZpZ09ialtrZXldO1xuICAgIGNvbnN0IGN1c3RvbVZhbHVlID0gY3VzdG9tT2JqICYmIGN1c3RvbU9ialtrZXldO1xuXG4gICAgaWYgKHR5cGVvZiBlbnRyeS52YWx1ZSA9PT0gJ3VuZGVmaW5lZCcpIHtcbiAgICAgIHVwZGF0ZURlZmF1bHRDb25maWcoZW50cnksIGN1c3RvbVZhbHVlLCBgJHtwcm9wQ2hhaW59LiR7a2V5fWApO1xuICAgIH0gZWxzZSB7XG4gICAgICAvLyBJZiBhIHZhbHVlIGZyb20gYSBjdXN0b20gSlNPTiBleGlzdHMsIGl0IHRha2UgcHJlY2VkZW5jZVxuICAgICAgaWYgKGN1c3RvbVZhbHVlICE9PSB1bmRlZmluZWQpIHtcbiAgICAgICAgZW50cnkudmFsdWUgPSBjdXN0b21WYWx1ZTtcbiAgICAgIH1cblxuICAgICAgLy8gSWYgYSB2YWx1ZSBmcm9tIGFuIGVudiB2YXJpYWJsZSBleGlzdHMsIGl0IHRha2UgcHJlY2VkZW5jZVxuICAgICAgaWYgKGVudHJ5LmVudkxpbmsgaW4gZW52cyAmJiBlbnZzW2VudHJ5LmVudkxpbmtdICE9PSB1bmRlZmluZWQpIHtcbiAgICAgICAgZW50cnkudmFsdWUgPSBlbnZzW2VudHJ5LmVudkxpbmtdO1xuICAgICAgfVxuICAgIH1cbiAgfSk7XG59XG5cbi8qKlxuICogSW5pdGlhbGl6ZXMgb3B0aW9ucyBvYmplY3QgYmFzZWQgb24gcHJvdmlkZWQgaXRlbXMsIHNldHRpbmcgdmFsdWVzIGZyb21cbiAqIG5lc3RlZCBwcm9wZXJ0aWVzIHJlY3Vyc2l2ZWx5LlxuICpcbiAqIEBwYXJhbSB7T2JqZWN0fSBpdGVtcyAtIENvbmZpZ3VyYXRpb24gaXRlbXMgdG8gYmUgdXNlZCBmb3IgaW5pdGlhbGl6aW5nXG4gKiBvcHRpb25zLlxuICpcbiAqIEByZXR1cm5zIHtPYmplY3R9IEluaXRpYWxpemVkIG9wdGlvbnMgb2JqZWN0LlxuICovXG5mdW5jdGlvbiBpbml0T3B0aW9ucyhpdGVtcykge1xuICBsZXQgb3B0aW9ucyA9IHt9O1xuICBmb3IgKGNvbnN0IFtuYW1lLCBpdGVtXSBvZiBPYmplY3QuZW50cmllcyhpdGVtcykpIHtcbiAgICBvcHRpb25zW25hbWVdID0gT2JqZWN0LnByb3RvdHlwZS5oYXNPd25Qcm9wZXJ0eS5jYWxsKGl0ZW0sICd2YWx1ZScpXG4gICAgICA/IGl0ZW0udmFsdWVcbiAgICAgIDogaW5pdE9wdGlvbnMoaXRlbSk7XG4gIH1cbiAgcmV0dXJuIG9wdGlvbnM7XG59XG5cbi8qKlxuICogUGFpcnMgYXJndW1lbnQgdmFsdWVzIHdpdGggY29ycmVzcG9uZGluZyBvcHRpb25zIGluIHRoZSBjb25maWd1cmF0aW9uLFxuICogdXBkYXRpbmcgdGhlIG9wdGlvbnMgb2JqZWN0LlxuICpcbiAqIEBwYXJhbSB7T2JqZWN0fSBvcHRpb25zIC0gQ29uZmlndXJhdGlvbiBvcHRpb25zIG9iamVjdCB0byBiZSB1cGRhdGVkLlxuICogQHBhcmFtIHtBcnJheX0gYXJncyAtIENvbW1hbmQtbGluZSBhcmd1bWVudHMgY29udGFpbmluZyB2YWx1ZXMgZm9yIHNwZWNpZmljXG4gKiBvcHRpb25zLlxuICogQHBhcmFtIHtPYmplY3R9IGRlZmF1bHRDb25maWcgLSBEZWZhdWx0IGNvbmZpZ3VyYXRpb24gb2JqZWN0IGZvciByZWZlcmVuY2UuXG4gKlxuICogQHJldHVybnMge09iamVjdH0gVXBkYXRlZCBvcHRpb25zIG9iamVjdC5cbiAqL1xuZnVuY3Rpb24gcGFpckFyZ3VtZW50VmFsdWUob3B0aW9ucywgYXJncywgZGVmYXVsdENvbmZpZykge1xuICBsZXQgc2hvd1VzYWdlID0gZmFsc2U7XG4gIGZvciAobGV0IGkgPSAwOyBpIDwgYXJncy5sZW5ndGg7IGkrKykge1xuICAgIGNvbnN0IG9wdGlvbiA9IGFyZ3NbaV0ucmVwbGFjZSgvLS9nLCAnJyk7XG5cbiAgICAvLyBGaW5kIHRoZSByaWdodCBwbGFjZSBmb3IgcHJvcGVydHkncyB2YWx1ZVxuICAgIGNvbnN0IHByb3BlcnRpZXNDaGFpbiA9IG5lc3RlZEFyZ3Nbb3B0aW9uXVxuICAgICAgPyBuZXN0ZWRBcmdzW29wdGlvbl0uc3BsaXQoJy4nKVxuICAgICAgOiBbXTtcblxuICAgIC8vIEdldCB0aGUgY29ycmVjdCB0eXBlIGZvciBDTEkgYXJncyB3aGljaCBhcmUgcGFzc2VkIGFzIHN0cmluZ3NcbiAgICBsZXQgYXJndW1lbnRUeXBlO1xuICAgIHByb3BlcnRpZXNDaGFpbi5yZWR1Y2UoKG9iaiwgcHJvcCwgaW5kZXgpID0+IHtcbiAgICAgIGlmIChwcm9wZXJ0aWVzQ2hhaW4ubGVuZ3RoIC0gMSA9PT0gaW5kZXgpIHtcbiAgICAgICAgYXJndW1lbnRUeXBlID0gb2JqW3Byb3BdLnR5cGU7XG4gICAgICB9XG4gICAgICByZXR1cm4gb2JqW3Byb3BdO1xuICAgIH0sIGRlZmF1bHRDb25maWcpO1xuXG4gICAgcHJvcGVydGllc0NoYWluLnJlZHVjZSgob2JqLCBwcm9wLCBpbmRleCkgPT4ge1xuICAgICAgaWYgKHByb3BlcnRpZXNDaGFpbi5sZW5ndGggLSAxID09PSBpbmRleCkge1xuICAgICAgICAvLyBGaW5kcyBhbiBvcHRpb24gYW5kIHNldCBhIGNvcnJlc3BvbmRpbmcgdmFsdWVcbiAgICAgICAgaWYgKHR5cGVvZiBvYmpbcHJvcF0gIT09ICd1bmRlZmluZWQnKSB7XG4gICAgICAgICAgaWYgKGFyZ3NbKytpXSkge1xuICAgICAgICAgICAgaWYgKGFyZ3VtZW50VHlwZSA9PT0gJ2Jvb2xlYW4nKSB7XG4gICAgICAgICAgICAgIG9ialtwcm9wXSA9IHRvQm9vbGVhbihhcmdzW2ldKTtcbiAgICAgICAgICAgIH0gZWxzZSBpZiAoYXJndW1lbnRUeXBlID09PSAnbnVtYmVyJykge1xuICAgICAgICAgICAgICBvYmpbcHJvcF0gPSArYXJnc1tpXTtcbiAgICAgICAgICAgIH0gZWxzZSBpZiAoYXJndW1lbnRUeXBlLmluZGV4T2YoJ10nKSA+PSAwKSB7XG4gICAgICAgICAgICAgIG9ialtwcm9wXSA9IGFyZ3NbaV0uc3BsaXQoJywnKTtcbiAgICAgICAgICAgIH0gZWxzZSB7XG4gICAgICAgICAgICAgIG9ialtwcm9wXSA9IGFyZ3NbaV07XG4gICAgICAgICAgICB9XG4gICAgICAgICAgfSBlbHNlIHtcbiAgICAgICAgICAgIGxvZyhcbiAgICAgICAgICAgICAgMixcbiAgICAgICAgICAgICAgYFtjb25maWddIE1pc3NpbmcgdmFsdWUgZm9yIHRoZSAnJHtvcHRpb259JyBhcmd1bWVudC4gVXNpbmcgdGhlIGRlZmF1bHQgdmFsdWUuYFxuICAgICAgICAgICAgKTtcbiAgICAgICAgICAgIHNob3dVc2FnZSA9IHRydWU7XG4gICAgICAgICAgfVxuICAgICAgICB9XG4gICAgICB9XG4gICAgICByZXR1cm4gb2JqW3Byb3BdO1xuICAgIH0sIG9wdGlvbnMpO1xuICB9XG5cbiAgLy8gRGlzcGxheSB0aGUgdXNhZ2UgZm9yIHRoZSByZWZlcmVuY2UgaWYgbmVlZGVkXG4gIGlmIChzaG93VXNhZ2UpIHtcbiAgICBwcmludFVzYWdlKGRlZmF1bHRDb25maWcpO1xuICB9XG5cbiAgcmV0dXJuIG9wdGlvbnM7XG59XG5cbi8qKlxuICogUmVjdXJzaXZlbHkgdXBkYXRlcyBwcm9wZXJ0aWVzIGluIGFuIG9iamVjdCBiYXNlZCBvbiBuZXN0ZWQgbmFtZXMgYW5kIGFzc2lnbnNcbiAqIHRoZSBmaW5hbCB2YWx1ZS5cbiAqXG4gKiBAcGFyYW0ge09iamVjdH0gb2JqZWN0VG9VcGRhdGUgLSBUaGUgb2JqZWN0IHRvIGJlIHVwZGF0ZWQuXG4gKiBAcGFyYW0ge0FycmF5fSBuZXN0ZWROYW1lcyAtIEFycmF5IG9mIG5lc3RlZCBwcm9wZXJ0eSBuYW1lcy5cbiAqIEBwYXJhbSB7YW55fSB2YWx1ZSAtIFRoZSBmaW5hbCB2YWx1ZSB0byBiZSBhc3NpZ25lZC5cbiAqXG4gKiBAcmV0dXJucyB7T2JqZWN0fSBVcGRhdGVkIG9iamVjdCB3aXRoIGFzc2lnbmVkIHZhbHVlcy5cbiAqL1xuZnVuY3Rpb24gcmVjdXJzaXZlUHJvcHMob2JqZWN0VG9VcGRhdGUsIG5lc3RlZE5hbWVzLCB2YWx1ZSkge1xuICB3aGlsZSAobmVzdGVkTmFtZXMubGVuZ3RoID4gMSkge1xuICAgIGNvbnN0IHByb3BOYW1lID0gbmVzdGVkTmFtZXMuc2hpZnQoKTtcblxuICAgIC8vIENyZWF0ZSBhIHByb3BlcnR5IGluIG9iamVjdCBpZiBpdCBkb2Vzbid0IGV4aXN0XG4gICAgaWYgKCFPYmplY3QucHJvdG90eXBlLmhhc093blByb3BlcnR5LmNhbGwob2JqZWN0VG9VcGRhdGUsIHByb3BOYW1lKSkge1xuICAgICAgb2JqZWN0VG9VcGRhdGVbcHJvcE5hbWVdID0ge307XG4gICAgfVxuXG4gICAgLy8gQ2FsbCBmdW5jdGlvbiBhZ2FpbiBpZiB0aGVyZSBzdGlsbCBuYW1lcyB0byBnb1xuICAgIG9iamVjdFRvVXBkYXRlW3Byb3BOYW1lXSA9IHJlY3Vyc2l2ZVByb3BzKFxuICAgICAgT2JqZWN0LmFzc2lnbih7fSwgb2JqZWN0VG9VcGRhdGVbcHJvcE5hbWVdKSxcbiAgICAgIG5lc3RlZE5hbWVzLFxuICAgICAgdmFsdWVcbiAgICApO1xuXG4gICAgcmV0dXJuIG9iamVjdFRvVXBkYXRlO1xuICB9XG5cbiAgLy8gQXNzaWduIHRoZSBmaW5hbCB2YWx1ZVxuICBvYmplY3RUb1VwZGF0ZVtuZXN0ZWROYW1lc1swXV0gPSB2YWx1ZTtcbiAgcmV0dXJuIG9iamVjdFRvVXBkYXRlO1xufVxuXG5leHBvcnQgZGVmYXVsdCB7XG4gIGdldE9wdGlvbnMsXG4gIHNldE9wdGlvbnMsXG4gIG1hbnVhbENvbmZpZyxcbiAgbWFwVG9OZXdDb25maWcsXG4gIG1lcmdlQ29uZmlnT3B0aW9ucyxcbiAgaW5pdEV4cG9ydFNldHRpbmdzXG59O1xuIiwiLyoqXG4gKiBUaGlzIG1vZHVsZSBleHBvcnRzIHR3byBmdW5jdGlvbnM6IGZldGNoIChmb3IgR0VUIHJlcXVlc3RzKSBhbmQgcG9zdCAoZm9yIFBPU1QgcmVxdWVzdHMpLlxuICovXG5cbmltcG9ydCBodHRwIGZyb20gJ2h0dHAnO1xuaW1wb3J0IGh0dHBzIGZyb20gJ2h0dHBzJztcblxuLyoqXG4gKiBSZXR1cm5zIHRoZSBIVFRQIG9yIEhUVFBTIHByb3RvY29sIG1vZHVsZSBiYXNlZCBvbiB0aGUgcHJvdmlkZWQgVVJMLlxuICpcbiAqIEBwYXJhbSB7c3RyaW5nfSB1cmwgLSBUaGUgVVJMIHRvIGRldGVybWluZSB0aGUgcHJvdG9jb2wuXG4gKlxuICogQHJldHVybnMge09iamVjdH0gVGhlIEhUVFAgb3IgSFRUUFMgcHJvdG9jb2wgbW9kdWxlIChodHRwIG9yIGh0dHBzKS5cbiAqL1xuY29uc3QgZ2V0UHJvdG9jb2wgPSAodXJsKSA9PiAodXJsLnN0YXJ0c1dpdGgoJ2h0dHBzJykgPyBodHRwcyA6IGh0dHApO1xuXG4vKipcbiAqIEZldGNoZXMgZGF0YSBmcm9tIHRoZSBzcGVjaWZpZWQgVVJMIHVzaW5nIGVpdGhlciBIVFRQIG9yIEhUVFBTIHByb3RvY29sLlxuICpcbiAqIEBwYXJhbSB7c3RyaW5nfSB1cmwgLSBUaGUgVVJMIHRvIGZldGNoIGRhdGEgZnJvbS5cbiAqIEBwYXJhbSB7T2JqZWN0fSByZXF1ZXN0T3B0aW9ucyAtIE9wdGlvbnMgZm9yIHRoZSBIVFRQIHJlcXVlc3QgKG9wdGlvbmFsKS5cbiAqXG4gKiBAcmV0dXJucyB7UHJvbWlzZTxPYmplY3Q+fSBQcm9taXNlIHJlc29sdmluZyB0byB0aGUgSFRUUCByZXNwb25zZSBvYmplY3RcbiAqIHdpdGggYWRkZWQgJ3RleHQnIHByb3BlcnR5IG9yIHJlamVjdGluZyB3aXRoIGFuIGVycm9yLlxuICovXG5hc3luYyBmdW5jdGlvbiBmZXRjaCh1cmwsIHJlcXVlc3RPcHRpb25zID0ge30pIHtcbiAgcmV0dXJuIG5ldyBQcm9taXNlKChyZXNvbHZlLCByZWplY3QpID0+IHtcbiAgICBjb25zdCBwcm90b2NvbCA9IGdldFByb3RvY29sKHVybCk7XG5cbiAgICBwcm90b2NvbFxuICAgICAgLmdldChcbiAgICAgICAgdXJsLFxuICAgICAgICBPYmplY3QuYXNzaWduKFxuICAgICAgICAgIHtcbiAgICAgICAgICAgIGhlYWRlcnM6IHtcbiAgICAgICAgICAgICAgJ1VzZXItQWdlbnQnOiAnaGlnaGNoYXJ0cy9leHBvcnQnLFxuICAgICAgICAgICAgICBSZWZlcmVyOiAnaGlnaGNoYXJ0cy5leHBvcnQnXG4gICAgICAgICAgICB9XG4gICAgICAgICAgfSxcbiAgICAgICAgICByZXF1ZXN0T3B0aW9ucyB8fCB7fVxuICAgICAgICApLFxuICAgICAgICAocmVzKSA9PiB7XG4gICAgICAgICAgbGV0IGRhdGEgPSAnJztcblxuICAgICAgICAgIC8vIEEgY2h1bmsgb2YgZGF0YSBoYXMgYmVlbiByZWNlaXZlZC5cbiAgICAgICAgICByZXMub24oJ2RhdGEnLCAoY2h1bmspID0+IHtcbiAgICAgICAgICAgIGRhdGEgKz0gY2h1bms7XG4gICAgICAgICAgfSk7XG5cbiAgICAgICAgICAvLyBUaGUgd2hvbGUgcmVzcG9uc2UgaGFzIGJlZW4gcmVjZWl2ZWQuXG4gICAgICAgICAgcmVzLm9uKCdlbmQnLCAoKSA9PiB7XG4gICAgICAgICAgICBpZiAoIWRhdGEpIHtcbiAgICAgICAgICAgICAgcmVqZWN0KCdOb3RoaW5nIHdhcyBmZXRjaGVkIGZyb20gdGhlIFVSTC4nKTtcbiAgICAgICAgICAgIH1cblxuICAgICAgICAgICAgcmVzLnRleHQgPSBkYXRhO1xuICAgICAgICAgICAgcmVzb2x2ZShyZXMpO1xuICAgICAgICAgIH0pO1xuICAgICAgICB9XG4gICAgICApXG4gICAgICAub24oJ2Vycm9yJywgKGVycm9yKSA9PiB7XG4gICAgICAgIHJlamVjdChlcnJvcik7XG4gICAgICB9KTtcbiAgfSk7XG59XG5cbi8qKlxuICogU2VuZHMgYSBQT1NUIHJlcXVlc3QgdG8gdGhlIHNwZWNpZmllZCBVUkwgd2l0aCB0aGUgcHJvdmlkZWQgSlNPTiBib2R5IHVzaW5nXG4gKiBlaXRoZXIgSFRUUCBvciBIVFRQUyBwcm90b2NvbC5cbiAqXG4gKiBAcGFyYW0ge3N0cmluZ30gdXJsIC0gVGhlIFVSTCB0byBzZW5kIHRoZSBQT1NUIHJlcXVlc3QgdG8uXG4gKiBAcGFyYW0ge09iamVjdH0gYm9keSAtIFRoZSBKU09OIGJvZHkgdG8gaW5jbHVkZSBpbiB0aGUgUE9TVCByZXF1ZXN0XG4gKiAob3B0aW9uYWwsIGRlZmF1bHQgaXMgYW4gZW1wdHkgb2JqZWN0KS5cbiAqIEBwYXJhbSB7T2JqZWN0fSByZXF1ZXN0T3B0aW9ucyAtIE9wdGlvbnMgZm9yIHRoZSBIVFRQIHJlcXVlc3QgKG9wdGlvbmFsKS5cbiAqXG4gKiBAcmV0dXJucyB7UHJvbWlzZTxPYmplY3Q+fSBQcm9taXNlIHJlc29sdmluZyB0byB0aGUgSFRUUCByZXNwb25zZSBvYmplY3Qgd2l0aFxuICogYWRkZWQgJ3RleHQnIHByb3BlcnR5IG9yIHJlamVjdGluZyB3aXRoIGFuIGVycm9yLlxuICovXG5hc3luYyBmdW5jdGlvbiBwb3N0KHVybCwgYm9keSA9IHt9LCByZXF1ZXN0T3B0aW9ucyA9IHt9KSB7XG4gIHJldHVybiBuZXcgUHJvbWlzZSgocmVzb2x2ZSwgcmVqZWN0KSA9PiB7XG4gICAgY29uc3QgcHJvdG9jb2wgPSBnZXRQcm90b2NvbCh1cmwpO1xuICAgIGNvbnN0IGRhdGEgPSBKU09OLnN0cmluZ2lmeShib2R5KTtcblxuICAgIC8vIFNldCBkZWZhdWx0IGhlYWRlcnMgYW5kIG1lcmdlIHdpdGggcmVxdWVzdE9wdGlvbnNcbiAgICBjb25zdCBvcHRpb25zID0gT2JqZWN0LmFzc2lnbihcbiAgICAgIHtcbiAgICAgICAgbWV0aG9kOiAnUE9TVCcsXG4gICAgICAgIGhlYWRlcnM6IHtcbiAgICAgICAgICAnQ29udGVudC1UeXBlJzogJ2FwcGxpY2F0aW9uL2pzb24nLFxuICAgICAgICAgICdDb250ZW50LUxlbmd0aCc6IGRhdGEubGVuZ3RoXG4gICAgICAgIH1cbiAgICAgIH0sXG4gICAgICByZXF1ZXN0T3B0aW9uc1xuICAgICk7XG5cbiAgICBjb25zdCByZXEgPSBwcm90b2NvbFxuICAgICAgLnJlcXVlc3QodXJsLCBvcHRpb25zLCAocmVzKSA9PiB7XG4gICAgICAgIGxldCByZXNwb25zZURhdGEgPSAnJztcblxuICAgICAgICAvLyBBIGNodW5rIG9mIGRhdGEgaGFzIGJlZW4gcmVjZWl2ZWQuXG4gICAgICAgIHJlcy5vbignZGF0YScsIChjaHVuaykgPT4ge1xuICAgICAgICAgIHJlc3BvbnNlRGF0YSArPSBjaHVuaztcbiAgICAgICAgfSk7XG5cbiAgICAgICAgLy8gVGhlIHdob2xlIHJlc3BvbnNlIGhhcyBiZWVuIHJlY2VpdmVkLlxuICAgICAgICByZXMub24oJ2VuZCcsICgpID0+IHtcbiAgICAgICAgICB0cnkge1xuICAgICAgICAgICAgcmVzLnRleHQgPSByZXNwb25zZURhdGE7XG4gICAgICAgICAgICByZXNvbHZlKHJlcyk7XG4gICAgICAgICAgfSBjYXRjaCAoZXJyb3IpIHtcbiAgICAgICAgICAgIHJlamVjdChlcnJvcik7XG4gICAgICAgICAgfVxuICAgICAgICB9KTtcbiAgICAgIH0pXG4gICAgICAub24oJ2Vycm9yJywgKGVycm9yKSA9PiB7XG4gICAgICAgIHJlamVjdChlcnJvcik7XG4gICAgICB9KTtcblxuICAgIC8vIFdyaXRlIHRoZSByZXF1ZXN0IGJvZHkgYW5kIGVuZCB0aGUgcmVxdWVzdC5cbiAgICByZXEud3JpdGUoZGF0YSk7XG4gICAgcmVxLmVuZCgpO1xuICB9KTtcbn1cblxuZXhwb3J0IGRlZmF1bHQgZmV0Y2g7XG5leHBvcnQgeyBmZXRjaCwgcG9zdCB9O1xuIiwiY2xhc3MgRXhwb3J0RXJyb3IgZXh0ZW5kcyBFcnJvciB7XG4gIC8vIE5PVEU6IERlbGliZXJhdGVseSB0YWtlcyBvbmx5IGEgbWVzc2FnZS4gVHdvIGV4aXN0aW5nIGNhbGwgc2l0ZXMgaW4gY2FjaGUuanNcbiAgLy8gICAgICAgcGFzcyBhIG51bWJlciBhcyBhIHNlY29uZCBhcmd1bWVudCwgaW50ZW5kaW5nIGEgc3RhdHVzIGNvZGUsIHdoaWNoXG4gIC8vICAgICAgIHRoaXMgY29uc3RydWN0b3IgaGFzIGFsd2F5cyBpZ25vcmVkLiBBY2NlcHRpbmcgYSBzZWNvbmQgcGFyYW1ldGVyIGhlcmVcbiAgLy8gICAgICAgd291bGQgc2lsZW50bHkgZ2l2ZSB0aG9zZSBhIG1lYW5pbmcuIFVzZSBzZXRDb2RlKCkgaW5zdGVhZCwgd2hpY2ggaXNcbiAgLy8gICAgICAgZXhwbGljaXQuXG4gIGNvbnN0cnVjdG9yKG1lc3NhZ2UpIHtcbiAgICBzdXBlcigpO1xuICAgIHRoaXMubWVzc2FnZSA9IG1lc3NhZ2U7XG4gICAgdGhpcy5zdGFja01lc3NhZ2UgPSBtZXNzYWdlO1xuICB9XG5cbiAgc2V0RXJyb3IoZXJyb3IpIHtcbiAgICB0aGlzLmVycm9yID0gZXJyb3I7XG4gICAgaWYgKGVycm9yLm5hbWUpIHtcbiAgICAgIHRoaXMubmFtZSA9IGVycm9yLm5hbWU7XG4gICAgfVxuICAgIGlmIChlcnJvci5zdGF0dXNDb2RlKSB7XG4gICAgICB0aGlzLnN0YXR1c0NvZGUgPSBlcnJvci5zdGF0dXNDb2RlO1xuICAgIH1cbiAgICAvLyBOT1RFOiBDYXJyeSBhIG1hY2hpbmUgcmVhZGFibGUgY29kZSB1cCBmcm9tIHRoZSB3cmFwcGVkIGVycm9yLiBFcnJvcnMgYXJlXG4gICAgLy8gICAgICAgd3JhcHBlZCBhcyB0aGV5IHRyYXZlbCB1cCB0aGUgc3RhY2ssIGFuZCB3aXRob3V0IHRoaXMgdGhlIHJlYXNvbiBhXG4gICAgLy8gICAgICAgcmVxdWVzdCBmYWlsZWQgd291bGQgYmUgbG9zdCBhdCB0aGUgZmlyc3Qgd3JhcCwgbGVhdmluZyBvbmx5IHRoZVxuICAgIC8vICAgICAgIG1lc3NhZ2UgdG8gdGVsbCBhIGNhcGFjaXR5IHByb2JsZW0gZnJvbSBhIGJhZCByZXF1ZXN0LlxuICAgIGlmIChlcnJvci5lcnJvckNvZGUgJiYgIXRoaXMuZXJyb3JDb2RlKSB7XG4gICAgICB0aGlzLmVycm9yQ29kZSA9IGVycm9yLmVycm9yQ29kZTtcbiAgICB9XG4gICAgaWYgKGVycm9yLnN0YWNrKSB7XG4gICAgICB0aGlzLnN0YWNrTWVzc2FnZSA9IGVycm9yLm1lc3NhZ2U7XG4gICAgICB0aGlzLnN0YWNrID0gZXJyb3Iuc3RhY2s7XG4gICAgfVxuICAgIHJldHVybiB0aGlzO1xuICB9XG5cbiAgc2V0Q29kZShlcnJvckNvZGUpIHtcbiAgICB0aGlzLmVycm9yQ29kZSA9IGVycm9yQ29kZTtcbiAgICByZXR1cm4gdGhpcztcbiAgfVxufVxuXG5leHBvcnQgZGVmYXVsdCBFeHBvcnRFcnJvcjtcbiIsIi8qKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqXG5cbkhpZ2hjaGFydHMgRXhwb3J0IFNlcnZlclxuXG5Db3B5cmlnaHQgKGMpIDIwMTYtMjAyNCwgSGlnaHNvZnRcblxuTGljZW5jZWQgdW5kZXIgdGhlIE1JVCBsaWNlbmNlLlxuXG5BZGRpdGlvbmFsbHkgYSB2YWxpZCBIaWdoY2hhcnRzIGxpY2Vuc2UgaXMgcmVxdWlyZWQgZm9yIHVzZS5cblxuU2VlIExJQ0VOU0UgZmlsZSBpbiByb290IGZvciBkZXRhaWxzLlxuXG4qKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqL1xuXG4vLyBUaGUgY2FjaGUgbWFuYWdlciBtYW5hZ2VzIHRoZSBIaWdoY2hhcnRzIGxpYnJhcnkgYW5kIGl0cyBkZXBlbmRlbmNpZXMuXG4vLyBUaGUgY2FjaGUgaXRzZWxmIGlzIHN0b3JlZCBpbiAuY2FjaGUsIGFuZCBpcyBjaGVja2VkIGJ5IHRoZSBjb25maWcgc3lzdGVtXG4vLyBiZWZvcmUgc3RhcnRpbmcgdGhlIHNlcnZpY2VcblxuaW1wb3J0IHsgZXhpc3RzU3luYywgbWtkaXJTeW5jLCByZWFkRmlsZVN5bmMsIHdyaXRlRmlsZVN5bmMgfSBmcm9tICdmcyc7XG5pbXBvcnQgeyBqb2luLCByZXNvbHZlLCBzZXAgfSBmcm9tICdwYXRoJztcblxuaW1wb3J0IHsgSHR0cHNQcm94eUFnZW50IH0gZnJvbSAnaHR0cHMtcHJveHktYWdlbnQnO1xuXG5pbXBvcnQgeyBnZXRPcHRpb25zIH0gZnJvbSAnLi9jb25maWcuanMnO1xuaW1wb3J0IHsgZW52cyB9IGZyb20gJy4vZW52cy5qcyc7XG5pbXBvcnQgeyBmZXRjaCB9IGZyb20gJy4vZmV0Y2guanMnO1xuaW1wb3J0IHsgbG9nIH0gZnJvbSAnLi9sb2dnZXIuanMnO1xuaW1wb3J0IHsgX19kaXJuYW1lLCBfX2hpZ2hjaGFydHNEaXIgfSBmcm9tICcuL3V0aWxzLmpzJztcblxuaW1wb3J0IEV4cG9ydEVycm9yIGZyb20gJy4vZXJyb3JzL0V4cG9ydEVycm9yLmpzJztcblxuY29uc3QgY2FjaGUgPSB7XG4gIGNkblVSTDogJ2h0dHBzOi8vY29kZS5oaWdoY2hhcnRzLmNvbS8nLFxuICBhY3RpdmVNYW5pZmVzdDoge30sXG4gIHNvdXJjZXM6ICcnLFxuICBoY1ZlcnNpb246ICcnXG59O1xuXG4vKipcbiAqIEV4dHJhY3RzIGFuZCBjYWNoZXMgdGhlIEhpZ2hjaGFydHMgdmVyc2lvbiBmcm9tIHRoZSBzb3VyY2VzIHN0cmluZy5cbiAqXG4gKiBAcmV0dXJucyB7c3RyaW5nfSBUaGUgZXh0cmFjdGVkIEhpZ2hjaGFydHMgdmVyc2lvbi5cbiAqL1xuZXhwb3J0IGNvbnN0IGV4dHJhY3RWZXJzaW9uID0gKGNhY2hlKSA9PiB7XG4gIHJldHVybiBjYWNoZS5zb3VyY2VzXG4gICAgLnN1YnN0cmluZygwLCBjYWNoZS5zb3VyY2VzLmluZGV4T2YoJyovJykpXG4gICAgLnJlcGxhY2UoJy8qJywgJycpXG4gICAgLnJlcGxhY2UoJyovJywgJycpXG4gICAgLnJlcGxhY2UoL1xcbi9nLCAnJylcbiAgICAudHJpbSgpO1xufTtcblxuLyoqXG4gKiBFeHRyYWN0cyB0aGUgSGlnaGNoYXJ0cyBtb2R1bGUgbmFtZSBiYXNlZCBvbiB0aGUgc2NyaXB0UGF0aC5cbiAqXG4gKiBAcGFyYW0ge3N0cmluZ30gc2NyaXB0UGF0aCAtIFRoZSBwYXRoIHRvIHRoZSBtb2R1bGUuXG4gKlxuICogQHJldHVybnMge3N0cmluZ30gVGhlIG5hbWUgb2YgYSBtb2R1bGUuXG4gKi9cbmV4cG9ydCBjb25zdCBleHRyYWN0TW9kdWxlTmFtZSA9IChzY3JpcHRQYXRoKSA9PiB7XG4gIC8vIE5vcm1hbGl6ZSBzbGFzaGVzLCBnZXQgcGFydCBhZnRlciB0aGUgbGFzdCAnLycgYW5kIHJlbW92ZSAuanMgZXh0ZW5zaW9uXG4gIHJldHVybiBzY3JpcHRQYXRoLnJlcGxhY2UoL1xcXFwvZywgJy8nKS5zcGxpdCgnLycpLnBvcCgpLnJlcGxhY2UoL1xcLmpzJC9pLCAnJyk7XG59O1xuXG4vKipcbiAqIFNhdmVzIHRoZSBwcm92aWRlZCBjb25maWd1cmF0aW9uIGFuZCBmZXRjaGVkIG1vZHVsZXMgdG8gdGhlIGNhY2hlIG1hbmlmZXN0XG4gKiBmaWxlLlxuICpcbiAqIEBwYXJhbSB7b2JqZWN0fSBjb25maWcgLSBIaWdoY2hhcnRzLXJlbGF0ZWQgY29uZmlndXJhdGlvbiBvYmplY3QuXG4gKiBAcGFyYW0ge29iamVjdH0gZmV0Y2hlZE1vZHVsZXMgLSBBbiBvYmplY3QgdGhhdCBjb250YWlucyBtYXBwZWQgbmFtZXMgb2ZcbiAqIGZldGNoZWQgSGlnaGNoYXJ0cyBtb2R1bGVzIHRvIHVzZS5cbiAqXG4gKiBAdGhyb3dzIHtFeHBvcnRFcnJvcn0gVGhyb3dzIGFuIEV4cG9ydEVycm9yIGlmIGFuIGVycm9yIG9jY3VycyB3aGlsZSB3cml0aW5nXG4gKiB0aGUgY2FjaGUgbWFuaWZlc3QuXG4gKi9cbmV4cG9ydCBjb25zdCBzYXZlQ29uZmlnVG9NYW5pZmVzdCA9IGFzeW5jIChjb25maWcsIGZldGNoZWRNb2R1bGVzKSA9PiB7XG4gIGNvbnN0IG5ld01hbmlmZXN0ID0ge1xuICAgIHZlcnNpb246IGNvbmZpZy52ZXJzaW9uLFxuICAgIG1vZHVsZXM6IGZldGNoZWRNb2R1bGVzIHx8IHt9XG4gIH07XG5cbiAgLy8gVXBkYXRlIGNhY2hlIG9iamVjdCB3aXRoIHRoZSBjdXJyZW50IG1vZHVsZXNcbiAgY2FjaGUuYWN0aXZlTWFuaWZlc3QgPSBuZXdNYW5pZmVzdDtcblxuICBsb2coMywgJ1tjYWNoZV0gV3JpdGluZyBhIG5ldyBtYW5pZmVzdC4nKTtcbiAgdHJ5IHtcbiAgICB3cml0ZUZpbGVTeW5jKFxuICAgICAgam9pbihfX2Rpcm5hbWUsIGNvbmZpZy5jYWNoZVBhdGgsICdtYW5pZmVzdC5qc29uJyksXG4gICAgICBKU09OLnN0cmluZ2lmeShuZXdNYW5pZmVzdCksXG4gICAgICAndXRmOCdcbiAgICApO1xuICB9IGNhdGNoIChlcnJvcikge1xuICAgIHRocm93IG5ldyBFeHBvcnRFcnJvcignW2NhY2hlXSBFcnJvciB3cml0aW5nIHRoZSBjYWNoZSBtYW5pZmVzdC4nKS5zZXRFcnJvcihcbiAgICAgIGVycm9yXG4gICAgKTtcbiAgfVxufTtcblxuLyoqXG4gKiBGZXRjaGVzIGEgc2luZ2xlIHNjcmlwdCBhbmQgdXBkYXRlcyB0aGUgZmV0Y2hlZE1vZHVsZXMgYWNjb3JkaW5nbHkuXG4gKlxuICogQHBhcmFtIHtzdHJpbmd9IHNjcmlwdCAtIEEgcGF0aCB0byBzY3JpcHQgdG8gZ2V0LlxuICogQHBhcmFtIHtPYmplY3R9IHJlcXVlc3RPcHRpb25zIC0gQWRkaXRpb25hbCBvcHRpb25zIGZvciB0aGUgcHJveHkgYWdlbnRcbiAqIHRvIHVzZSBmb3IgYSByZXF1ZXN0LlxuICogQHBhcmFtIHtPYmplY3R9IGZldGNoZWRNb2R1bGVzIC0gQW4gb2JqZWN0IHdoaWNoIHRyYWNrcyB3aGljaCBIaWdoY2hhcnRzXG4gKiBtb2R1bGVzIGhhdmUgYmVlbiBmZXRjaGVkLlxuICogQHBhcmFtIHtib29sZWFufSBbdXNlTnBtPWZhbHNlXSAtIEEgZmxhZyB0byBpbmRpY2F0ZSBpZiB0aGUgc2NyaXB0IHNob3VsZCBiZVxuICogZ2V0IGZyb20gTlBNIHBhY2thZ2Ugb3IgZmV0Y2hlZCBmcm9tIENETi4gVGhlIGRlZmF1bHQgdmFsdWUgaXMgYGZhbHNlYC5cbiAqIEBwYXJhbSB7Ym9vbGVhbn0gW3Nob3VsZFRocm93RXJyb3I9ZmFsc2VdIC0gQSBmbGFnIHRvIGluZGljYXRlIGlmIHRoZSBlcnJvclxuICogc2hvdWxkIGJlIHRocm93bi4gVGhpcyBzaG91bGQgYmUgdXNlZCBvbmx5IGZvciB0aGUgY29yZSBzY3JpcHRzLlxuICpcbiAqIEByZXR1cm5zIHtQcm9taXNlPHN0cmluZz59IEEgUHJvbWlzZSByZXNvbHZpbmcgdG8gdGhlIHRleHQgcmVwcmVzZW50YXRpb25cbiAqIG9mIHRoZSBmZXRjaGVkIHNjcmlwdC5cbiAqXG4gKiBAdGhyb3dzIHtFeHBvcnRFcnJvcn0gVGhyb3dzIGFuIEV4cG9ydEVycm9yIGlmIHRoZXJlIGlzIGEgcHJvYmxlbSB3aXRoXG4gKiBmZXRjaGluZyB0aGUgc2NyaXB0LlxuICovXG5leHBvcnQgY29uc3QgZmV0Y2hBbmRQcm9jZXNzU2NyaXB0ID0gYXN5bmMgKFxuICBzY3JpcHQsXG4gIHJlcXVlc3RPcHRpb25zLFxuICBmZXRjaGVkTW9kdWxlcyxcbiAgdXNlTnBtID0gZmFsc2UsXG4gIHNob3VsZFRocm93RXJyb3IgPSBmYWxzZVxuKSA9PiB7XG4gIGxldCByZXNwb25zZTtcblxuICAvLyBBZGQgdGhlIG1pc3NpbmcgLmpzIHRvIHRoZSBzdHJpbmdzXG4gIGlmICghc2NyaXB0LmVuZHNXaXRoKCcuanMnKSkge1xuICAgIHNjcmlwdCA9IGAke3NjcmlwdH0uanNgO1xuICB9XG5cbiAgLy8gV2hldGhlciB0byB1c2UgTlBNIHBhY2thZ2Ugc2NyaXB0cyBvciBmZXRjaCBpdCBmcm9tIENETlxuICBpZiAodXNlTnBtKSB7XG4gICAgdHJ5IHtcbiAgICAgIC8vIExvZyBmZXRjaGVkIHNjcmlwdFxuICAgICAgbG9nKFxuICAgICAgICA0LFxuICAgICAgICBgW2NhY2hlXSBGZXRjaGluZyBzY3JpcHQgZnJvbSBOUE0gLSAke2pvaW4oJ25vZGVfbW9kdWxlcycsICdoaWdoY2hhcnRzJywgc2NyaXB0KX1gXG4gICAgICApO1xuXG4gICAgICAvLyBTYW5pdGl6ZSBhbmQgdmFsaWRhdGUgcGF0aFxuICAgICAgY29uc3QgcmVzb2x2ZWRTY3JpcHRQYXRoID0gcmVzb2x2ZShfX2hpZ2hjaGFydHNEaXIsIHNjcmlwdCk7XG4gICAgICBpZiAoIXJlc29sdmVkU2NyaXB0UGF0aC5zdGFydHNXaXRoKHJlc29sdmUoX19oaWdoY2hhcnRzRGlyKSArIHNlcCkpIHtcbiAgICAgICAgdGhyb3cgbmV3IEV4cG9ydEVycm9yKFxuICAgICAgICAgIGBbY2FjaGVdIEludmFsaWQgc2NyaXB0IHBhdGggZGV0ZWN0ZWQgZm9yICcke3NjcmlwdH0nLiBEaXJlY3RvcnkgdHJhdmVyc2FsIGF0dGVtcHQgb3IgYmFkIHZlcnNpb24gaW5wdXQuYCxcbiAgICAgICAgICA0MDNcbiAgICAgICAgKTtcbiAgICAgIH1cblxuICAgICAgLy8gRmV0Y2ggdGhlIHNjcmlwdCBmcm9tIE5QTVxuICAgICAgcmVzcG9uc2UgPSByZWFkRmlsZVN5bmMocmVzb2x2ZWRTY3JpcHRQYXRoLCAndXRmOCcpO1xuXG4gICAgICAvLyBJZiBPSywgcmV0dXJuIGl0cyB0ZXh0IHJlcHJlc2VudGF0aW9uXG4gICAgICBpZiAoZmV0Y2hlZE1vZHVsZXMgJiYgcmVzcG9uc2UpIHtcbiAgICAgICAgZmV0Y2hlZE1vZHVsZXNbZXh0cmFjdE1vZHVsZU5hbWUoc2NyaXB0KV0gPSAxO1xuICAgICAgfVxuICAgICAgcmV0dXJuIHJlc3BvbnNlO1xuICAgIH0gY2F0Y2gge1xuICAgICAgLy8gUHJvY2VlZFxuICAgIH1cbiAgfSBlbHNlIHtcbiAgICAvLyBMb2cgZmV0Y2hlZCBzY3JpcHRcbiAgICBsb2coNCwgYFtjYWNoZV0gRmV0Y2hpbmcgc2NyaXB0IGZyb20gQ0ROIC0gJHtzY3JpcHR9YCk7XG5cbiAgICAvLyBGZXRjaCB0aGUgc2NyaXB0IGZyb20gQ0ROXG4gICAgcmVzcG9uc2UgPSBhd2FpdCBmZXRjaChzY3JpcHQsIHJlcXVlc3RPcHRpb25zKTtcblxuICAgIC8vIElmIE9LLCByZXR1cm4gaXRzIHRleHQgcmVwcmVzZW50YXRpb25cbiAgICBpZiAocmVzcG9uc2Uuc3RhdHVzQ29kZSA9PT0gMjAwICYmIHR5cGVvZiByZXNwb25zZS50ZXh0ID09ICdzdHJpbmcnKSB7XG4gICAgICBpZiAoZmV0Y2hlZE1vZHVsZXMpIHtcbiAgICAgICAgZmV0Y2hlZE1vZHVsZXNbZXh0cmFjdE1vZHVsZU5hbWUoc2NyaXB0KV0gPSAxO1xuICAgICAgfVxuICAgICAgcmV0dXJuIHJlc3BvbnNlLnRleHQ7XG4gICAgfVxuICB9XG5cbiAgLy8gQmFzZWQgb24gdGhlIGBzaG91bGRUaHJvd0Vycm9yYCBmbGFnLCBkZWNpZGUgaG93IHRvIHNlcnZlIGVycm9yIG1lc3NhZ2VcbiAgaWYgKHNob3VsZFRocm93RXJyb3IpIHtcbiAgICB0aHJvdyBuZXcgRXhwb3J0RXJyb3IoXG4gICAgICBgW2NhY2hlXSBDb3VsZCBub3QgZmV0Y2ggdGhlIG1hbmRhdG9yeSAke3NjcmlwdH0uIFRoZSBzY3JpcHQgbWlnaHQgbm90IGV4aXN0IGluIHRoZSByZXF1ZXN0ZWQgdmVyc2lvbi5gLFxuICAgICAgNDA0XG4gICAgKTtcbiAgfSBlbHNlIHtcbiAgICBsb2coXG4gICAgICAyLFxuICAgICAgYFtjYWNoZV0gQ291bGQgbm90IGZldGNoIHRoZSAke3NjcmlwdH0uIFRoZSBzY3JpcHQgbWlnaHQgbm90IGV4aXN0IGluIHRoZSByZXF1ZXN0ZWQgdmVyc2lvbi5gXG4gICAgKTtcbiAgfVxuXG4gIHJldHVybiAnJztcbn07XG5cbi8qKlxuICogRmV0Y2hlcyBIaWdoY2hhcnRzIHNjcmlwdHMgYW5kIGN1c3RvbVNjcmlwdHMgZnJvbSB0aGUgZ2l2ZW4gQ0ROcy5cbiAqXG4gKiBAcGFyYW0ge09iamVjdH0gaGlnaGNoYXJ0c09wdGlvbnMgLSBPYmplY3QgY29udGFpbmluZyBhbGwgaGlnaGNoYXJ0cyBvcHRpb25zLlxuICogQHBhcmFtIHtvYmplY3R9IHByb3h5T3B0aW9ucyAtIE9wdGlvbnMgZm9yIHRoZSBwcm94eSBhZ2VudCB0byB1c2UgZm9yXG4gKiBhIHJlcXVlc3QuXG4gKiBAcGFyYW0ge29iamVjdH0gZmV0Y2hlZE1vZHVsZXMgLSBBbiBvYmplY3Qgd2hpY2ggdHJhY2tzIHdoaWNoIEhpZ2hjaGFydHNcbiAqIG1vZHVsZXMgaGF2ZSBiZWVuIGZldGNoZWQuXG4gKlxuICogQHJldHVybnMge1Byb21pc2U8c3RyaW5nPn0gVGhlIGZldGNoZWQgc2NyaXB0cyBjb250ZW50IGpvaW5lZC5cbiAqL1xuZXhwb3J0IGNvbnN0IGZldGNoU2NyaXB0cyA9IGFzeW5jIChcbiAgaGlnaGNoYXJ0c09wdGlvbnMsXG4gIHByb3h5T3B0aW9ucyxcbiAgZmV0Y2hlZE1vZHVsZXNcbikgPT4ge1xuICBjb25zdCB2ZXJzaW9uID0gaGlnaGNoYXJ0c09wdGlvbnMudmVyc2lvbjtcbiAgY29uc3QgaGNWZXJzaW9uID0gdmVyc2lvbiA9PT0gJ2xhdGVzdCcgfHwgIXZlcnNpb24gPyAnJyA6IGAke3ZlcnNpb259L2A7XG4gIGNvbnN0IGNkblVSTCA9IGhpZ2hjaGFydHNPcHRpb25zLmNkblVSTCB8fCBjYWNoZS5jZG5VUkw7XG5cbiAgbG9nKFxuICAgIDMsXG4gICAgYFtjYWNoZV0gVXBkYXRpbmcgY2FjaGUgdmVyc2lvbiB0byBIaWdoY2hhcnRzOiAke2hjVmVyc2lvbiB8fCAnbGF0ZXN0J30uYFxuICApO1xuXG4gIC8vIFdoZXRoZXIgdG8gdXNlIE5QTSBvciBDRE5cbiAgY29uc3QgdXNlTnBtID0gaGlnaGNoYXJ0c09wdGlvbnMudXNlTnBtO1xuXG4gIC8vIENvbmZpZ3VyZSBwcm94eSBpZiBleGlzdHNcbiAgbGV0IHByb3h5QWdlbnQ7XG4gIGNvbnN0IHsgaG9zdCwgcG9ydCwgdXNlcm5hbWUsIHBhc3N3b3JkIH0gPSBwcm94eU9wdGlvbnM7XG5cbiAgLy8gVHJ5IHRvIGNyZWF0ZSBhIFByb3h5IEFnZW50XG4gIGlmIChob3N0ICYmIHBvcnQpIHtcbiAgICB0cnkge1xuICAgICAgcHJveHlBZ2VudCA9IG5ldyBIdHRwc1Byb3h5QWdlbnQoe1xuICAgICAgICBob3N0LFxuICAgICAgICBwb3J0LFxuICAgICAgICAuLi4odXNlcm5hbWUgJiYgcGFzc3dvcmQgPyB7IHVzZXJuYW1lLCBwYXNzd29yZCB9IDoge30pXG4gICAgICB9KTtcbiAgICB9IGNhdGNoIChlcnJvcikge1xuICAgICAgdGhyb3cgbmV3IEV4cG9ydEVycm9yKCdbY2FjaGVdIENvdWxkIG5vdCBjcmVhdGUgYSBQcm94eSBBZ2VudC4nKS5zZXRFcnJvcihcbiAgICAgICAgZXJyb3JcbiAgICAgICk7XG4gICAgfVxuICB9XG5cbiAgLy8gSWYgZXhpc3RzLCBhZGQgcHJveHkgYWdlbnQgdG8gcmVxdWVzdCBvcHRpb25zXG4gIGNvbnN0IHJlcXVlc3RPcHRpb25zID0gcHJveHlBZ2VudFxuICAgID8ge1xuICAgICAgICBhZ2VudDogcHJveHlBZ2VudCxcbiAgICAgICAgdGltZW91dDogZW52cy5TRVJWRVJfUFJPWFlfVElNRU9VVFxuICAgICAgfVxuICAgIDoge307XG5cbiAgY29uc3QgZmV0Y2hlZFNjcmlwdHMgPSBhd2FpdCBQcm9taXNlLmFsbChbXG4gICAgLi4uaGlnaGNoYXJ0c09wdGlvbnMuY29yZVNjcmlwdHMubWFwKChjKSA9PlxuICAgICAgZmV0Y2hBbmRQcm9jZXNzU2NyaXB0KFxuICAgICAgICAodXNlTnBtICYmIGMpIHx8IGAke2NkblVSTH0ke2hjVmVyc2lvbn0ke2N9YCxcbiAgICAgICAgcmVxdWVzdE9wdGlvbnMsXG4gICAgICAgIGZldGNoZWRNb2R1bGVzLFxuICAgICAgICB1c2VOcG0sXG4gICAgICAgIHRydWVcbiAgICAgIClcbiAgICApLFxuICAgIC4uLmhpZ2hjaGFydHNPcHRpb25zLm1vZHVsZVNjcmlwdHMubWFwKChtKSA9PlxuICAgICAgZmV0Y2hBbmRQcm9jZXNzU2NyaXB0KFxuICAgICAgICAodXNlTnBtICYmIGpvaW4oJ21vZHVsZXMnLCBtKSkgfHxcbiAgICAgICAgICAobSA9PT0gJ21hcCdcbiAgICAgICAgICAgID8gYCR7Y2RuVVJMfW1hcHMvJHtoY1ZlcnNpb259bW9kdWxlcy8ke219YFxuICAgICAgICAgICAgOiBgJHtjZG5VUkx9JHtoY1ZlcnNpb259bW9kdWxlcy8ke219YCksXG4gICAgICAgIHJlcXVlc3RPcHRpb25zLFxuICAgICAgICBmZXRjaGVkTW9kdWxlcyxcbiAgICAgICAgdXNlTnBtXG4gICAgICApXG4gICAgKSxcbiAgICAuLi5oaWdoY2hhcnRzT3B0aW9ucy5pbmRpY2F0b3JTY3JpcHRzLm1hcCgoaSkgPT5cbiAgICAgIGZldGNoQW5kUHJvY2Vzc1NjcmlwdChcbiAgICAgICAgKHVzZU5wbSAmJiBqb2luKCdpbmRpY2F0b3JzJywgaSkpIHx8XG4gICAgICAgICAgYCR7Y2RuVVJMfXN0b2NrLyR7aGNWZXJzaW9ufWluZGljYXRvcnMvJHtpfWAsXG4gICAgICAgIHJlcXVlc3RPcHRpb25zLFxuICAgICAgICBmZXRjaGVkTW9kdWxlcyxcbiAgICAgICAgdXNlTnBtXG4gICAgICApXG4gICAgKSxcbiAgICAuLi5oaWdoY2hhcnRzT3B0aW9ucy5jdXN0b21TY3JpcHRzLm1hcCgoYykgPT5cbiAgICAgIGZldGNoQW5kUHJvY2Vzc1NjcmlwdChgJHtjfWAsIHJlcXVlc3RPcHRpb25zKVxuICAgIClcbiAgXSk7XG5cbiAgcmV0dXJuIGZldGNoZWRTY3JpcHRzLmpvaW4oJztcXG4nKTtcbn07XG5cbi8qKlxuICogVXBkYXRlcyB0aGUgbG9jYWwgY2FjaGUgd2l0aCBIaWdoY2hhcnRzIHNjcmlwdHMgYW5kIHRoZWlyIHZlcnNpb25zLlxuICpcbiAqIEBwYXJhbSB7T2JqZWN0fSBoaWdoY2hhcnRzT3B0aW9ucyAtIE9iamVjdCBjb250YWluaW5nIGFsbCBvcHRpb25zIGZyb21cbiAqIHRoZSBoaWdoY2hhcnRzIHNlY3Rpb24uXG4gKiBAcGFyYW0ge3N0cmluZ30gc291cmNlUGF0aCAtIFRoZSBwYXRoIHRvIHRoZSBzb3VyY2UgZmlsZSBpbiB0aGUgY2FjaGUuXG4gKlxuICogQHJldHVybnMge1Byb21pc2U8b2JqZWN0Pn0gQSBQcm9taXNlIHJlc29sdmluZyB0byBhbiBvYmplY3QgcmVwcmVzZW50aW5nXG4gKiB0aGUgZmV0Y2hlZCBtb2R1bGVzLlxuICpcbiAqIEB0aHJvd3Mge0V4cG9ydEVycm9yfSBUaHJvd3MgYW4gRXhwb3J0RXJyb3IgaWYgdGhlcmUgaXMgYW4gaXNzdWUgdXBkYXRpbmdcbiAqIHRoZSBsb2NhbCBIaWdoY2hhcnRzIGNhY2hlLlxuICovXG5leHBvcnQgY29uc3QgdXBkYXRlQ2FjaGUgPSBhc3luYyAoXG4gIGhpZ2hjaGFydHNPcHRpb25zLFxuICBwcm94eU9wdGlvbnMsXG4gIHNvdXJjZVBhdGhcbikgPT4ge1xuICB0cnkge1xuICAgIGNvbnN0IGZldGNoZWRNb2R1bGVzID0ge307XG5cbiAgICAvLyBHZXQgc291cmNlc1xuICAgIGNhY2hlLnNvdXJjZXMgPSBhd2FpdCBmZXRjaFNjcmlwdHMoXG4gICAgICBoaWdoY2hhcnRzT3B0aW9ucyxcbiAgICAgIHByb3h5T3B0aW9ucyxcbiAgICAgIGZldGNoZWRNb2R1bGVzXG4gICAgKTtcblxuICAgIC8vIEdldCBzb3VyY2VzIHZlcnNpb25cbiAgICBjYWNoZS5oY1ZlcnNpb24gPSBleHRyYWN0VmVyc2lvbihjYWNoZSk7XG5cbiAgICAvLyBTYXZlIHRoZSBmZXRjaGVkIG1vZHVsZXMgaW50byBjYWNoZXMnIHNvdXJjZSBKU09OXG4gICAgd3JpdGVGaWxlU3luYyhzb3VyY2VQYXRoLCBjYWNoZS5zb3VyY2VzKTtcblxuICAgIHJldHVybiBmZXRjaGVkTW9kdWxlcztcbiAgfSBjYXRjaCAoZXJyb3IpIHtcbiAgICB0aHJvdyBuZXcgRXhwb3J0RXJyb3IoXG4gICAgICAnW2NhY2hlXSBVbmFibGUgdG8gdXBkYXRlIHRoZSBsb2NhbCBIaWdoY2hhcnRzIGNhY2hlLidcbiAgICApLnNldEVycm9yKGVycm9yKTtcbiAgfVxufTtcblxuLyoqXG4gKiBVcGRhdGVzIHRoZSBIaWdoY2hhcnRzIHZlcnNpb24gaW4gdGhlIGFwcGxpZWQgY29uZmlndXJhdGlvbiBhbmQgY2hlY2tzXG4gKiB0aGUgY2FjaGUgZm9yIHRoZSBuZXcgdmVyc2lvbi5cbiAqXG4gKiBAcGFyYW0ge3N0cmluZ30gbmV3VmVyc2lvbiAtIFRoZSBuZXcgSGlnaGNoYXJ0cyB2ZXJzaW9uIHRvIGJlIGFwcGxpZWQuXG4gKlxuICogQHJldHVybnMge1Byb21pc2U8KG9iamVjdHxib29sZWFuKT59IEEgUHJvbWlzZSByZXNvbHZpbmcgdG8gdGhlIHVwZGF0ZWRcbiAqIGNvbmZpZ3VyYXRpb24gd2l0aCB0aGUgbmV3IHZlcnNpb24sIG9yIGZhbHNlIGlmIG5vIGFwcGxpZWQgY29uZmlndXJhdGlvblxuICogZXhpc3RzLlxuICovXG5leHBvcnQgY29uc3QgdXBkYXRlVmVyc2lvbiA9IGFzeW5jIChuZXdWZXJzaW9uKSA9PiB7XG4gIGNvbnN0IG9wdGlvbnMgPSBnZXRPcHRpb25zKCk7XG4gIGlmIChvcHRpb25zPy5oaWdoY2hhcnRzKSB7XG4gICAgb3B0aW9ucy5oaWdoY2hhcnRzLnZlcnNpb24gPSBuZXdWZXJzaW9uO1xuICB9XG4gIGF3YWl0IGNoZWNrQW5kVXBkYXRlQ2FjaGUob3B0aW9ucyk7XG59O1xuXG4vKipcbiAqIENoZWNrcyB0aGUgY2FjaGUgZm9yIEhpZ2hjaGFydHMgZGVwZW5kZW5jaWVzLCB1cGRhdGVzIHRoZSBjYWNoZSBpZiBuZWVkZWQsXG4gKiBhbmQgbG9hZHMgdGhlIHNvdXJjZXMuXG4gKlxuICogQHBhcmFtIHtPYmplY3R9IG9wdGlvbnMgLSBPYmplY3QgY29udGFpbmluZyBhbGwgb3B0aW9ucy5cbiAqXG4gKiBAcmV0dXJucyB7UHJvbWlzZTx2b2lkPn0gQSBQcm9taXNlIHRoYXQgcmVzb2x2ZXMgb25jZSB0aGUgY2FjaGUgaXMgY2hlY2tlZFxuICogYW5kIHVwZGF0ZWQuXG4gKlxuICogQHRocm93cyB7RXhwb3J0RXJyb3J9IFRocm93cyBhbiBFeHBvcnRFcnJvciBpZiB0aGVyZSBpcyBhbiBpc3N1ZSB1cGRhdGluZ1xuICogb3IgcmVhZGluZyB0aGUgY2FjaGUuXG4gKi9cbmV4cG9ydCBjb25zdCBjaGVja0FuZFVwZGF0ZUNhY2hlID0gYXN5bmMgKG9wdGlvbnMpID0+IHtcbiAgY29uc3QgeyBoaWdoY2hhcnRzLCBzZXJ2ZXIgfSA9IG9wdGlvbnM7XG4gIGNvbnN0IGNhY2hlUGF0aCA9IGpvaW4oX19kaXJuYW1lLCBoaWdoY2hhcnRzLmNhY2hlUGF0aCk7XG5cbiAgbGV0IGZldGNoZWRNb2R1bGVzO1xuICAvLyBQcmVwYXJlIHBhdGhzIHRvIG1hbmlmZXN0IGFuZCBzb3VyY2VzIGZyb20gdGhlIC5jYWNoZSBmb2xkZXJcbiAgY29uc3QgbWFuaWZlc3RQYXRoID0gam9pbihjYWNoZVBhdGgsICdtYW5pZmVzdC5qc29uJyk7XG4gIGNvbnN0IHNvdXJjZVBhdGggPSBqb2luKGNhY2hlUGF0aCwgJ3NvdXJjZXMuanMnKTtcblxuICAvLyBDcmVhdGUgdGhlIGNhY2hlIGRlc3RpbmF0aW9uIGlmIGl0IGRvZXNuJ3QgZXhpc3QgYWxyZWFkeVxuICAhZXhpc3RzU3luYyhjYWNoZVBhdGgpICYmIG1rZGlyU3luYyhjYWNoZVBhdGgpO1xuXG4gIC8vIEZldGNoIGFsbCB0aGUgc2NyaXB0cyBlaXRoZXIgaWYgbWFuaWZlc3QuanNvbiBkb2VzIG5vdCBleGlzdFxuICAvLyBvciBpZiB0aGUgZm9yY2VGZXRjaCBvcHRpb24gaXMgZW5hYmxlZFxuICBpZiAoIWV4aXN0c1N5bmMobWFuaWZlc3RQYXRoKSB8fCBoaWdoY2hhcnRzLmZvcmNlRmV0Y2gpIHtcbiAgICBsb2coMywgJ1tjYWNoZV0gRmV0Y2hpbmcgYW5kIGNhY2hpbmcgSGlnaGNoYXJ0cyBkZXBlbmRlbmNpZXMuJyk7XG4gICAgZmV0Y2hlZE1vZHVsZXMgPSBhd2FpdCB1cGRhdGVDYWNoZShoaWdoY2hhcnRzLCBzZXJ2ZXIucHJveHksIHNvdXJjZVBhdGgpO1xuICB9IGVsc2Uge1xuICAgIGxldCByZXF1ZXN0VXBkYXRlID0gZmFsc2U7XG5cbiAgICAvLyBSZWFkIHRoZSBtYW5pZmVzdCBKU09OXG4gICAgY29uc3QgbWFuaWZlc3QgPSBKU09OLnBhcnNlKHJlYWRGaWxlU3luYyhtYW5pZmVzdFBhdGgpKTtcblxuICAgIC8vIENoZWNrIGlmIHRoZSBtb2R1bGVzIGlzIGFuIGFycmF5LCBpZiBzbywgd2UgcmV3cml0ZSBpdCB0byBhIG1hcCB0byBtYWtlXG4gICAgLy8gaXQgZWFzaWVyIHRvIHJlc29sdmUgbW9kdWxlcy5cbiAgICBpZiAobWFuaWZlc3QubW9kdWxlcyAmJiBBcnJheS5pc0FycmF5KG1hbmlmZXN0Lm1vZHVsZXMpKSB7XG4gICAgICBjb25zdCBtb2R1bGVNYXAgPSB7fTtcbiAgICAgIG1hbmlmZXN0Lm1vZHVsZXMuZm9yRWFjaCgobSkgPT4gKG1vZHVsZU1hcFttXSA9IDEpKTtcbiAgICAgIG1hbmlmZXN0Lm1vZHVsZXMgPSBtb2R1bGVNYXA7XG4gICAgfVxuXG4gICAgY29uc3QgeyBjb3JlU2NyaXB0cywgbW9kdWxlU2NyaXB0cywgaW5kaWNhdG9yU2NyaXB0cyB9ID0gaGlnaGNoYXJ0cztcbiAgICBjb25zdCBudW1iZXJPZk1vZHVsZXMgPVxuICAgICAgY29yZVNjcmlwdHMubGVuZ3RoICsgbW9kdWxlU2NyaXB0cy5sZW5ndGggKyBpbmRpY2F0b3JTY3JpcHRzLmxlbmd0aDtcblxuICAgIC8vIENvbXBhcmUgdGhlIGxvYWRlZCBoaWdoY2hhcnRzIGNvbmZpZyB3aXRoIHRoZSBjb250ZW50cyBpbiBjYWNoZS5cbiAgICAvLyBJZiB0aGVyZSBhcmUgY2hhbmdlcywgZmV0Y2ggcmVxdWVzdGVkIG1vZHVsZXMgYW5kIHByb2R1Y3RzLFxuICAgIC8vIGFuZCBiYWtlIHRoZW0gaW50byBhIGdpYW50IGJsb2IuIFNhdmUgdGhlIGJsb2IuXG4gICAgaWYgKG1hbmlmZXN0LnZlcnNpb24gIT09IGhpZ2hjaGFydHMudmVyc2lvbikge1xuICAgICAgbG9nKFxuICAgICAgICAyLFxuICAgICAgICAnW2NhY2hlXSBBIEhpZ2hjaGFydHMgdmVyc2lvbiBtaXNtYXRjaCBpbiB0aGUgY2FjaGUsIG5lZWQgdG8gcmUtZmV0Y2guJ1xuICAgICAgKTtcbiAgICAgIHJlcXVlc3RVcGRhdGUgPSB0cnVlO1xuICAgIH0gZWxzZSBpZiAoT2JqZWN0LmtleXMobWFuaWZlc3QubW9kdWxlcyB8fCB7fSkubGVuZ3RoICE9PSBudW1iZXJPZk1vZHVsZXMpIHtcbiAgICAgIGxvZyhcbiAgICAgICAgMixcbiAgICAgICAgJ1tjYWNoZV0gVGhlIGNhY2hlIGFuZCB0aGUgcmVxdWVzdGVkIG1vZHVsZXMgZG8gbm90IG1hdGNoLCBuZWVkIHRvIHJlLWZldGNoLidcbiAgICAgICk7XG4gICAgICByZXF1ZXN0VXBkYXRlID0gdHJ1ZTtcbiAgICB9IGVsc2Uge1xuICAgICAgLy8gQ2hlY2sgZWFjaCBtb2R1bGUsIGlmIGFueXRoaW5nIGlzIG1pc3NpbmcgcmVmZXRjaCBldmVyeXRoaW5nXG4gICAgICByZXF1ZXN0VXBkYXRlID0gKG1vZHVsZVNjcmlwdHMgfHwgW10pLnNvbWUoKG1vZHVsZU5hbWUpID0+IHtcbiAgICAgICAgaWYgKCFtYW5pZmVzdC5tb2R1bGVzW21vZHVsZU5hbWVdKSB7XG4gICAgICAgICAgbG9nKFxuICAgICAgICAgICAgMixcbiAgICAgICAgICAgIGBbY2FjaGVdIFRoZSAke21vZHVsZU5hbWV9IGlzIG1pc3NpbmcgaW4gdGhlIGNhY2hlLCBuZWVkIHRvIHJlLWZldGNoLmBcbiAgICAgICAgICApO1xuICAgICAgICAgIHJldHVybiB0cnVlO1xuICAgICAgICB9XG4gICAgICB9KTtcbiAgICB9XG5cbiAgICBpZiAocmVxdWVzdFVwZGF0ZSkge1xuICAgICAgZmV0Y2hlZE1vZHVsZXMgPSBhd2FpdCB1cGRhdGVDYWNoZShoaWdoY2hhcnRzLCBzZXJ2ZXIucHJveHksIHNvdXJjZVBhdGgpO1xuICAgIH0gZWxzZSB7XG4gICAgICBsb2coMywgJ1tjYWNoZV0gRGVwZW5kZW5jeSBjYWNoZSBpcyB1cCB0byBkYXRlLCBwcm9jZWVkaW5nLicpO1xuXG4gICAgICAvLyBMb2FkIHRoZSBzb3VyY2VzXG4gICAgICBjYWNoZS5zb3VyY2VzID0gcmVhZEZpbGVTeW5jKHNvdXJjZVBhdGgsICd1dGY4Jyk7XG5cbiAgICAgIC8vIEdldCBjdXJyZW50IG1vZHVsZXMgbWFwXG4gICAgICBmZXRjaGVkTW9kdWxlcyA9IG1hbmlmZXN0Lm1vZHVsZXM7XG5cbiAgICAgIGNhY2hlLmhjVmVyc2lvbiA9IGV4dHJhY3RWZXJzaW9uKGNhY2hlKTtcbiAgICB9XG4gIH1cblxuICAvLyBGaW5hbGx5LCBzYXZlIHRoZSBuZXcgbWFuaWZlc3QsIHdoaWNoIGlzIGJhc2ljYWxseSBvdXIgY3VycmVudCBjb25maWdcbiAgLy8gaW4gYSBzbGlnaHRseSBkaWZmZXJlbnQgZm9ybWF0XG4gIGF3YWl0IHNhdmVDb25maWdUb01hbmlmZXN0KGhpZ2hjaGFydHMsIGZldGNoZWRNb2R1bGVzKTtcbn07XG5cbmV4cG9ydCBjb25zdCBnZXRDYWNoZVBhdGggPSAoKSA9PlxuICBqb2luKF9fZGlybmFtZSwgZ2V0T3B0aW9ucygpLmhpZ2hjaGFydHMuY2FjaGVQYXRoKTtcblxuZXhwb3J0IGNvbnN0IGdldENhY2hlID0gKCkgPT4gY2FjaGU7XG5cbmV4cG9ydCBjb25zdCBoaWdoY2hhcnRzID0gKCkgPT4gY2FjaGUuc291cmNlcztcblxuZXhwb3J0IGNvbnN0IHZlcnNpb24gPSAoKSA9PiBjYWNoZS5oY1ZlcnNpb247XG5cbmV4cG9ydCBkZWZhdWx0IHtcbiAgY2hlY2tBbmRVcGRhdGVDYWNoZSxcbiAgZ2V0Q2FjaGVQYXRoLFxuICB1cGRhdGVWZXJzaW9uLFxuICBnZXRDYWNoZSxcbiAgaGlnaGNoYXJ0cyxcbiAgdmVyc2lvblxufTtcbiIsIi8qKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqXG5cbkhpZ2hjaGFydHMgRXhwb3J0IFNlcnZlclxuXG5Db3B5cmlnaHQgKGMpIDIwMTYtMjAyNCwgSGlnaHNvZnRcblxuTGljZW5jZWQgdW5kZXIgdGhlIE1JVCBsaWNlbmNlLlxuXG5BZGRpdGlvbmFsbHkgYSB2YWxpZCBIaWdoY2hhcnRzIGxpY2Vuc2UgaXMgcmVxdWlyZWQgZm9yIHVzZS5cblxuU2VlIExJQ0VOU0UgZmlsZSBpbiByb290IGZvciBkZXRhaWxzLlxuXG4qKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqL1xuXG4vKiBlc2xpbnQtZGlzYWJsZSBuby11bmRlZiAqL1xuXG4vKipcbiAqIFNldHRpbmcgdGhlIGFuaW1PYmplY3QuIENhbGxlZCB3aGVuIGluaXRpbmcgdGhlIHBhZ2UuXG4gKi9cbmV4cG9ydCBmdW5jdGlvbiBzZXR1cEhpZ2hjaGFydHMoKSB7XG4gIEhpZ2hjaGFydHMuYW5pbU9iamVjdCA9IGZ1bmN0aW9uICgpIHtcbiAgICByZXR1cm4geyBkdXJhdGlvbjogMCB9O1xuICB9O1xufVxuXG4vKipcbiAqIENyZWF0ZXMgdGhlIGFjdHVhbCBjaGFydC5cbiAqXG4gKiBAcGFyYW0ge29iamVjdH0gY2hhcnRPcHRpb25zIC0gVGhlIG9wdGlvbnMgZm9yIHRoZSBIaWdoY2hhcnRzIGNoYXJ0LlxuICogQHBhcmFtIHtvYmplY3R9IG9wdGlvbnMgLSBUaGUgZXhwb3J0IG9wdGlvbnMuXG4gKiBAcGFyYW0ge2Jvb2xlYW59IGRpc3BsYXlFcnJvcnMgLSBBIGZsYWcgaW5kaWNhdGluZyB3aGV0aGVyIHRvIGRpc3BsYXkgZXJyb3JzLlxuICovXG5leHBvcnQgYXN5bmMgZnVuY3Rpb24gdHJpZ2dlckV4cG9ydChjaGFydE9wdGlvbnMsIG9wdGlvbnMsIGRpc3BsYXlFcnJvcnMpIHtcbiAgLy8gRGlzcGxheSBlcnJvcnMgZmxhZyB0YWtlbiBmcm9tIGNoYXJ0IG9wdGlvbnMgbmFkIGRlYnVnZ2VyIG1vZHVsZVxuICB3aW5kb3cuX2Rpc3BsYXlFcnJvcnMgPSBkaXNwbGF5RXJyb3JzO1xuXG4gIC8vIEdldCByZXF1aXJlZCBmdW5jdGlvbnNcbiAgY29uc3QgeyBnZXRPcHRpb25zLCBtZXJnZSwgc2V0T3B0aW9ucywgd3JhcCB9ID0gSGlnaGNoYXJ0cztcblxuICAvLyBDcmVhdGUgYSBzZXBhcmF0ZSBvYmplY3QgZm9yIGEgcG90ZW50aWFsIHNldE9wdGlvbnMgdXNhZ2VzIGluIG9yZGVyIHRvXG4gIC8vIHByZXZlbnQgZnJvbSBwb2xsdXRpbmcgb3RoZXIgZXhwb3J0cyB0aGF0IGNhbiBoYXBwZW4gb24gdGhlIHNhbWUgcGFnZVxuICBIaWdoY2hhcnRzLnNldE9wdGlvbnNPYmogPSBtZXJnZShmYWxzZSwge30sIGdldE9wdGlvbnMoKSk7XG5cbiAgLy8gQnkgZGVmYXVsdCBhbmltYXRpb24gaXMgZGlzYWJsZWRcbiAgY29uc3QgY2hhcnQgPSB7XG4gICAgYW5pbWF0aW9uOiBmYWxzZVxuICB9O1xuXG4gIC8vIFdoZW4gc3RyYWlnaHQgaW5qZWN0LCB0aGUgc2l6ZSBpcyBzZXQgdGhyb3VnaCBDU1Mgb25seVxuICBpZiAob3B0aW9ucy5leHBvcnQuc3RySW5qKSB7XG4gICAgY2hhcnQuaGVpZ2h0ID0gY2hhcnRPcHRpb25zLmNoYXJ0LmhlaWdodDtcbiAgICBjaGFydC53aWR0aCA9IGNoYXJ0T3B0aW9ucy5jaGFydC53aWR0aDtcbiAgfVxuXG4gIC8vIE5PVEU6IElzIHRoaXMgdXNlZCBmb3IgYW55dGhpbmcgdXNlZnVsP1xuICB3aW5kb3cuaXNSZW5kZXJDb21wbGV0ZSA9IGZhbHNlO1xuICB3cmFwKEhpZ2hjaGFydHMuQ2hhcnQucHJvdG90eXBlLCAnaW5pdCcsIGZ1bmN0aW9uIChwcm9jZWVkLCB1c2VyT3B0aW9ucywgY2IpIHtcbiAgICAvLyBPdmVycmlkZSB1c2VyT3B0aW9ucyB3aXRoIGltYWdlIGZyaWVuZGx5IG9wdGlvbnNcbiAgICB1c2VyT3B0aW9ucyA9IG1lcmdlKHVzZXJPcHRpb25zLCB7XG4gICAgICBleHBvcnRpbmc6IHtcbiAgICAgICAgZW5hYmxlZDogZmFsc2VcbiAgICAgIH0sXG4gICAgICBwbG90T3B0aW9uczoge1xuICAgICAgICBzZXJpZXM6IHtcbiAgICAgICAgICBsYWJlbDoge1xuICAgICAgICAgICAgZW5hYmxlZDogZmFsc2VcbiAgICAgICAgICB9XG4gICAgICAgIH1cbiAgICAgIH0sXG4gICAgICAvKiBFeHBlY3RzIHRvb2x0aXAgaW4gdXNlck9wdGlvbnMgd2hlbiBmb3JFeHBvcnQgaXMgdHJ1ZS5cbiAgICAgICAgaHR0cHM6Ly9naXRodWIuY29tL2hpZ2hjaGFydHMvaGlnaGNoYXJ0cy9ibG9iLzNhZDQzMGEzNTNiODA1NmI5ZTc2NGFhNGU1Y2Q2ODI4YWE0NzlkYjIvanMvcGFydHMvQ2hhcnQuanMjTDI0MVxuICAgICAgICAqL1xuICAgICAgdG9vbHRpcDoge31cbiAgICB9KTtcblxuICAgICh1c2VyT3B0aW9ucy5zZXJpZXMgfHwgW10pLmZvckVhY2goZnVuY3Rpb24gKHNlcmllcykge1xuICAgICAgc2VyaWVzLmFuaW1hdGlvbiA9IGZhbHNlO1xuICAgIH0pO1xuXG4gICAgLy8gQWRkIGZsYWcgdG8ga25vdyBpZiBjaGFydCByZW5kZXIgaGFzIGJlZW4gY2FsbGVkLlxuICAgIGlmICghd2luZG93Lm9uSGlnaGNoYXJ0c1JlbmRlcikge1xuICAgICAgd2luZG93Lm9uSGlnaGNoYXJ0c1JlbmRlciA9IEhpZ2hjaGFydHMuYWRkRXZlbnQodGhpcywgJ3JlbmRlcicsICgpID0+IHtcbiAgICAgICAgd2luZG93LmlzUmVuZGVyQ29tcGxldGUgPSB0cnVlO1xuICAgICAgfSk7XG4gICAgfVxuXG4gICAgcHJvY2VlZC5hcHBseSh0aGlzLCBbdXNlck9wdGlvbnMsIGNiXSk7XG4gIH0pO1xuXG4gIHdyYXAoSGlnaGNoYXJ0cy5TZXJpZXMucHJvdG90eXBlLCAnaW5pdCcsIGZ1bmN0aW9uIChwcm9jZWVkLCBjaGFydCwgb3B0aW9ucykge1xuICAgIHByb2NlZWQuYXBwbHkodGhpcywgW2NoYXJ0LCBvcHRpb25zXSk7XG4gIH0pO1xuXG4gIC8vIEdldCB0aGUgdXNlciBvcHRpb25zXG4gIGNvbnN0IHVzZXJPcHRpb25zID0gb3B0aW9ucy5leHBvcnQuc3RySW5qXG4gICAgPyBuZXcgRnVuY3Rpb24oYHJldHVybiAke29wdGlvbnMuZXhwb3J0LnN0ckluan1gKSgpXG4gICAgOiBjaGFydE9wdGlvbnM7XG5cbiAgLy8gVHJpZ2dlciBjdXN0b20gY29kZVxuICBpZiAob3B0aW9ucy5jdXN0b21Mb2dpYy5jdXN0b21Db2RlKSB7XG4gICAgbmV3IEZ1bmN0aW9uKCdvcHRpb25zJywgb3B0aW9ucy5jdXN0b21Mb2dpYy5jdXN0b21Db2RlKSh1c2VyT3B0aW9ucyk7XG4gIH1cblxuICAvLyBNZXJnZSB0aGUgZ2xvYmFsT3B0aW9ucywgdGhlbWVPcHRpb25zLCBvcHRpb25zIGZyb20gdGhlIHdyYXBwZWRcbiAgLy8gc2V0T3B0aW9ucyBmdW5jdGlvbiBhbmQgdXNlciBvcHRpb25zIHRvIGNyZWF0ZSB0aGUgZmluYWwgb3B0aW9ucyBvYmplY3RcbiAgY29uc3QgZmluYWxPcHRpb25zID0gbWVyZ2UoXG4gICAgZmFsc2UsXG4gICAgSlNPTi5wYXJzZShvcHRpb25zLmV4cG9ydC50aGVtZU9wdGlvbnMpLFxuICAgIHVzZXJPcHRpb25zLFxuICAgIC8vIFBsYWNlZCBpdCBoZXJlIGluc3RlYWQgaW4gdGhlIGluaXQgYmVjYXVzZSBvZiB0aGUgc2l6ZSBpc3N1ZXNcbiAgICB7IGNoYXJ0IH1cbiAgKTtcblxuICBjb25zdCBmaW5hbENhbGxiYWNrID0gb3B0aW9ucy5jdXN0b21Mb2dpYy5jYWxsYmFja1xuICAgID8gbmV3IEZ1bmN0aW9uKGByZXR1cm4gJHtvcHRpb25zLmN1c3RvbUxvZ2ljLmNhbGxiYWNrfWApKClcbiAgICA6IHVuZGVmaW5lZDtcblxuICAvLyBTZXQgdGhlIGdsb2JhbCBvcHRpb25zIGlmIGV4aXN0XG4gIGNvbnN0IGdsb2JhbE9wdGlvbnMgPSBKU09OLnBhcnNlKG9wdGlvbnMuZXhwb3J0Lmdsb2JhbE9wdGlvbnMpO1xuICBpZiAoZ2xvYmFsT3B0aW9ucykge1xuICAgIHNldE9wdGlvbnMoZ2xvYmFsT3B0aW9ucyk7XG4gIH1cblxuICBsZXQgY29uc3RyID0gb3B0aW9ucy5leHBvcnQuY29uc3RyIHx8ICdjaGFydCc7XG4gIGNvbnN0ciA9IHR5cGVvZiBIaWdoY2hhcnRzW2NvbnN0cl0gIT09ICd1bmRlZmluZWQnID8gY29uc3RyIDogJ2NoYXJ0JztcblxuICBIaWdoY2hhcnRzW2NvbnN0cl0oJ2NvbnRhaW5lcicsIGZpbmFsT3B0aW9ucywgZmluYWxDYWxsYmFjayk7XG5cbiAgLy8gR2V0IHRoZSBjdXJyZW50IGdsb2JhbCBvcHRpb25zXG4gIGNvbnN0IGRlZmF1bHRPcHRpb25zID0gZ2V0T3B0aW9ucygpO1xuXG4gIC8vIENsZWFyIGl0IGp1c3QgaW4gY2FzZSAoZS5nLiB0aGUgc2V0T3B0aW9ucyB3YXMgdXNlZCBpbiB0aGUgY3VzdG9tQ29kZSlcbiAgZm9yIChjb25zdCBwcm9wIGluIGRlZmF1bHRPcHRpb25zKSB7XG4gICAgaWYgKHR5cGVvZiBkZWZhdWx0T3B0aW9uc1twcm9wXSAhPT0gJ2Z1bmN0aW9uJykge1xuICAgICAgZGVsZXRlIGRlZmF1bHRPcHRpb25zW3Byb3BdO1xuICAgIH1cbiAgfVxuXG4gIC8vIFNldCB0aGUgZGVmYXVsdCBvcHRpb25zIGJhY2tcbiAgc2V0T3B0aW9ucyhIaWdoY2hhcnRzLnNldE9wdGlvbnNPYmopO1xuXG4gIC8vIEVtcHR5IHRoZSBjdXN0b20gZ2xvYmFsIG9wdGlvbnMgb2JqZWN0XG4gIEhpZ2hjaGFydHMuc2V0T3B0aW9uc09iaiA9IHt9O1xufVxuIiwiLyoqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKipcblxuSGlnaGNoYXJ0cyBFeHBvcnQgU2VydmVyXG5cbkNvcHlyaWdodCAoYykgMjAxNi0yMDI0LCBIaWdoc29mdFxuXG5MaWNlbmNlZCB1bmRlciB0aGUgTUlUIGxpY2VuY2UuXG5cbkFkZGl0aW9uYWxseSBhIHZhbGlkIEhpZ2hjaGFydHMgbGljZW5zZSBpcyByZXF1aXJlZCBmb3IgdXNlLlxuXG5TZWUgTElDRU5TRSBmaWxlIGluIHJvb3QgZm9yIGRldGFpbHMuXG5cbioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKiovXG5cbmltcG9ydCB7IHJlYWRGaWxlU3luYyB9IGZyb20gJ2ZzJztcbmltcG9ydCBwYXRoIGZyb20gJ3BhdGgnO1xuXG5pbXBvcnQgcHVwcGV0ZWVyIGZyb20gJ3B1cHBldGVlcic7XG5cbmltcG9ydCB7IGdldENhY2hlUGF0aCB9IGZyb20gJy4vY2FjaGUuanMnO1xuaW1wb3J0IHsgZ2V0T3B0aW9ucyB9IGZyb20gJy4vY29uZmlnLmpzJztcbmltcG9ydCB7IHNldHVwSGlnaGNoYXJ0cyB9IGZyb20gJy4vaGlnaGNoYXJ0cy5qcyc7XG5pbXBvcnQgeyBsb2csIGxvZ1dpdGhTdGFjayB9IGZyb20gJy4vbG9nZ2VyLmpzJztcbmltcG9ydCB7IF9fZGlybmFtZSB9IGZyb20gJy4vdXRpbHMuanMnO1xuXG5pbXBvcnQgRXhwb3J0RXJyb3IgZnJvbSAnLi9lcnJvcnMvRXhwb3J0RXJyb3IuanMnO1xuXG4vLyBHZXQgdGhlIHRlbXBsYXRlIGZvciB0aGUgcGFnZVxuY29uc3QgdGVtcGxhdGUgPSByZWFkRmlsZVN5bmMoX19kaXJuYW1lICsgJy90ZW1wbGF0ZXMvdGVtcGxhdGUuaHRtbCcsICd1dGY4Jyk7XG5cbmxldCBicm93c2VyO1xuXG4vLyBJbmNyZW1lbnRlZCBldmVyeSB0aW1lIHRoZSBicm93c2VyIGlzIGxvc3QuIFBvb2wgd29ya2VycyBhcmUgc3RhbXBlZCB3aXRoIHRoZVxuLy8gdmFsdWUgY3VycmVudCB3aGVuIHRoZXkgd2VyZSBjcmVhdGVkLCB3aGljaCBsZXRzIHRoZSBwb29sIHRlbGwgdGhhdCBhIHdvcmtlclxuLy8gYmVsb25ncyB0byBhIGJyb3dzZXIgdGhhdCBubyBsb25nZXIgZXhpc3RzLiBUaGlzIGlzIG5lY2Vzc2FyeSBiZWNhdXNlIGEgcGFnZVxuLy8gYmVsb25naW5nIHRvIGEgZGVhZCBicm93c2VyIHN0aWxsIHJlcG9ydHMgaXNDbG9zZWQoKSA9PT0gZmFsc2UsIHNvIHRoZSBwYWdlXG4vLyBpdHNlbGYgY2Fubm90IGJlIGFza2VkIHdoZXRoZXIgaXQgaXMgdXNhYmxlLlxubGV0IGJyb3dzZXJHZW5lcmF0aW9uID0gMDtcblxuLy8gVGhlIGFyZ3VtZW50cyB0aGUgYnJvd3NlciB3YXMgbGFzdCBsYXVuY2hlZCB3aXRoLCBrZXB0IHNvIHRoYXQgaXQgY2FuIGJlXG4vLyByZWxhdW5jaGVkIG9uIHRoZSBzYW1lIHRlcm1zIGFmdGVyIGFuIHVuZXhwZWN0ZWQgZGlzY29ubmVjdC5cbmxldCBsYXN0UHVwcGV0ZWVyQXJncyA9IFtdO1xuXG4vLyBTZXQgd2hpbGUgdGhlIGJyb3dzZXIgaXMgYmVpbmcgZGVsaWJlcmF0ZWx5IGNsb3NlZCwgc28gdGhhdCB0aGUgcmVzdWx0aW5nXG4vLyBkaXNjb25uZWN0IGlzIG5vdCBtaXN0YWtlbiBmb3IgYSBjcmFzaC5cbmxldCBjbG9zaW5nT25QdXJwb3NlID0gZmFsc2U7XG5cbi8vIFNoYXJlZCBwcm9taXNlIGZvciBhbiBpbi1mbGlnaHQgbGF1bmNoLCBzbyB0aGF0IGNvbmN1cnJlbnQgY2FsbGVycyAtIHRoZSBwb29sXG4vLyBjcmVhdGluZyBzZXZlcmFsIHdvcmtlcnMgYXQgb25jZSwgdHlwaWNhbGx5IC0gdHJpZ2dlciBhIHNpbmdsZSBsYXVuY2ggcmF0aGVyXG4vLyB0aGFuIG9uZSBlYWNoLlxubGV0IGxhdW5jaFByb21pc2UgPSBudWxsO1xuXG4vKipcbiAqIFJldHVybnMgaG93IGxvbmcgdG8ga2VlcCByZXRyeWluZyBhIGJyb3dzZXIgbGF1bmNoIGJlZm9yZSBnaXZpbmcgdXAuXG4gKlxuICogQHJldHVybnMge251bWJlcn0gVGhlIHJldHJ5IHdpbmRvdyBpbiBtaWxsaXNlY29uZHMuXG4gKi9cbmZ1bmN0aW9uIGdldExhdW5jaFJldHJ5V2luZG93KCkge1xuICBjb25zdCBjb25maWd1cmVkID0gcGFyc2VJbnQoZ2V0T3B0aW9ucygpLnB1cHBldGVlcj8ubGF1bmNoUmV0cnlXaW5kb3cpO1xuICByZXR1cm4gaXNOYU4oY29uZmlndXJlZCkgfHwgY29uZmlndXJlZCA8PSAwID8gMzAwMDAgOiBjb25maWd1cmVkO1xufVxuXG4vKipcbiAqIFJldHJpZXZlcyB0aGUgZXhpc3RpbmcgUHVwcGV0ZWVyIGJyb3dzZXIgaW5zdGFuY2UuXG4gKlxuICogQHJldHVybnMge1Byb21pc2U8b2JqZWN0Pn0gQSBQcm9taXNlIHJlc29sdmluZyB0byB0aGUgUHVwcGV0ZWVyIGJyb3dzZXJcbiAqIGluc3RhbmNlLlxuICpcbiAqIEB0aHJvd3Mge0V4cG9ydEVycm9yfSBUaHJvd3MgYW4gRXhwb3J0RXJyb3IgaWYgbm8gdmFsaWQgYnJvd3NlciBoYXMgYmVlblxuICogY3JlYXRlZC5cbiAqL1xuZXhwb3J0IGZ1bmN0aW9uIGdldCgpIHtcbiAgaWYgKCFicm93c2VyKSB7XG4gICAgdGhyb3cgbmV3IEV4cG9ydEVycm9yKCdbYnJvd3Nlcl0gTm8gdmFsaWQgYnJvd3NlciBoYXMgYmVlbiBjcmVhdGVkLicpO1xuICB9XG4gIHJldHVybiBicm93c2VyO1xufVxuXG4vKipcbiAqIFJldHVybnMgdGhlIGN1cnJlbnQgYnJvd3NlciBnZW5lcmF0aW9uLiBQb29sIHdvcmtlcnMgYXJlIHN0YW1wZWQgd2l0aCB0aGlzXG4gKiB2YWx1ZSBvbiBjcmVhdGlvbiBhbmQgY29tcGFyZWQgYWdhaW5zdCBpdCBvbiB2YWxpZGF0aW9uLCBzbyB0aGF0IHdvcmtlcnNcbiAqIGhvbGRpbmcgYSBwYWdlIGZyb20gYSBwcmV2aW91cywgbm93IGRlYWQsIGJyb3dzZXIgY2FuIGJlIGlkZW50aWZpZWQgYW5kXG4gKiByZXBsYWNlZC5cbiAqXG4gKiBAcmV0dXJucyB7bnVtYmVyfSBUaGUgY3VycmVudCBicm93c2VyIGdlbmVyYXRpb24uXG4gKi9cbmV4cG9ydCBmdW5jdGlvbiBnZXRHZW5lcmF0aW9uKCkge1xuICByZXR1cm4gYnJvd3NlckdlbmVyYXRpb247XG59XG5cbi8qKlxuICogUmVwb3J0cyB3aGV0aGVyIGEgdXNhYmxlIGJyb3dzZXIgaXMgY3VycmVudGx5IGNvbm5lY3RlZC5cbiAqXG4gKiBAcmV0dXJucyB7Ym9vbGVhbn0gVHJ1ZSB3aGVuIGEgYnJvd3NlciBleGlzdHMgYW5kIGlzIGNvbm5lY3RlZC5cbiAqL1xuZXhwb3J0IGZ1bmN0aW9uIGlzQ29ubmVjdGVkKCkge1xuICByZXR1cm4gISFicm93c2VyPy5jb25uZWN0ZWQ7XG59XG5cbi8qKlxuICogSGFuZGxlcyB0aGUgYnJvd3NlciBkaXNjb25uZWN0aW5nLiBQdXBwZXRlZXIgZW1pdHMgdGhpcyB3aGVuIHRoZSBicm93c2VyXG4gKiBwcm9jZXNzIGdvZXMgYXdheSBmb3IgYW55IHJlYXNvbiwgaW5jbHVkaW5nIGJlaW5nIGtpbGxlZCBieSBhbiBvdXQgb2YgbWVtb3J5XG4gKiByZWFwZXIsIHdoaWNoIGlzIHRoZSBjYXNlIHRoaXMgZXhpc3RzIGZvci5cbiAqXG4gKiBUaGUgYnJvd3NlciByZWZlcmVuY2UgaXMgY2xlYXJlZCBzbyB0aGF0IHRoZSBndWFyZCBpbiBjcmVhdGUoKSB3aWxsIGFjdHVhbGx5XG4gKiByZWxhdW5jaCBpdCwgYW5kIHRoZSBnZW5lcmF0aW9uIGlzIGFkdmFuY2VkIHNvIHRoYXQgZXZlcnkgcG9vbCB3b3JrZXIgaG9sZGluZ1xuICogYSBwYWdlIGZyb20gdGhlIGRlYWQgYnJvd3NlciBmYWlscyB2YWxpZGF0aW9uIGFuZCBpcyByZXBsYWNlZC5cbiAqL1xuZnVuY3Rpb24gaGFuZGxlRGlzY29ubmVjdCgpIHtcbiAgaWYgKGNsb3NpbmdPblB1cnBvc2UpIHtcbiAgICByZXR1cm47XG4gIH1cblxuICBicm93c2VyR2VuZXJhdGlvbisrO1xuICBicm93c2VyID0gdW5kZWZpbmVkO1xuXG4gIGxvZyhcbiAgICAxLFxuICAgIGBbYnJvd3Nlcl0gVGhlIGJyb3dzZXIgZGlzY29ubmVjdGVkIHVuZXhwZWN0ZWRseS4gSW52YWxpZGF0aW5nIGFsbCB3b3JrZXJzIGFuZCByZWxhdW5jaGluZyBvbiBuZXh0IHVzZSAoZ2VuZXJhdGlvbiAke2Jyb3dzZXJHZW5lcmF0aW9ufSkuYFxuICApO1xufVxuXG4vKipcbiAqIFJlcG9ydHMgd2hldGhlciBhIHByb2Nlc3MgaXMgc3RpbGwgcnVubmluZywgYnkgYXNraW5nIHRoZSBvcGVyYXRpbmcgc3lzdGVtXG4gKiByYXRoZXIgdGhhbiB0cnVzdGluZyB0aGUgY2hpbGQgcHJvY2VzcyBvYmplY3QuXG4gKlxuICogTk9URTogZXhpdENvZGUgYW5kIHNpZ25hbENvZGUgYXJlIG5vdCBkZXBlbmRhYmxlIGhlcmUuIEJvdGggYXJlIG51bGwgZm9yIGFcbiAqICAgICAgIHJ1bm5pbmcgcHJvY2VzcywgYnV0IGVpdGhlciBjYW4gYmUgdW5kZWZpbmVkIGRlcGVuZGluZyBvbiBob3cgdGhlXG4gKiAgICAgICBwcm9jZXNzIG9iamVjdCB3YXMgcHJvZHVjZWQsIGFuZCBgdW5kZWZpbmVkICE9PSBudWxsYCByZWFkcyBhcyBleGl0ZWQgLVxuICogICAgICAgd2hpY2ggc2lsZW50bHkgc2tpcHMgYm90aCB0aGUgd2FpdCBhbmQgdGhlIGtpbGwgYmVsb3csIGxlYXZpbmcgdGhlXG4gKiAgICAgICBwcm9jZXNzIGFsaXZlLiBTaWduYWwgMCBwZXJmb3JtcyB0aGUgcGVybWlzc2lvbiBhbmQgZXhpc3RlbmNlIGNoZWNrc1xuICogICAgICAgd2l0aG91dCBkZWxpdmVyaW5nIGFueXRoaW5nLlxuICpcbiAqIEBwYXJhbSB7T2JqZWN0fSBwcm9jIC0gVGhlIGNoaWxkIHByb2Nlc3MgdG8gY2hlY2suXG4gKlxuICogQHJldHVybnMge2Jvb2xlYW59IFRydWUgd2hpbGUgdGhlIHByb2Nlc3Mgc3RpbGwgZXhpc3RzLlxuICovXG5mdW5jdGlvbiBpc0FsaXZlKHByb2MpIHtcbiAgaWYgKCFwcm9jPy5waWQpIHtcbiAgICByZXR1cm4gZmFsc2U7XG4gIH1cblxuICB0cnkge1xuICAgIHByb2Nlc3Mua2lsbChwcm9jLnBpZCwgMCk7XG4gICAgcmV0dXJuIHRydWU7XG4gIH0gY2F0Y2ggKGVycm9yKSB7XG4gICAgLy8gRVNSQ0ggbWVhbnMgbm8gc3VjaCBwcm9jZXNzOyBFUEVSTSBtZWFucyBpdCBleGlzdHMgYnV0IGlzIG5vdCBvdXJzXG4gICAgcmV0dXJuIGVycm9yLmNvZGUgPT09ICdFUEVSTSc7XG4gIH1cbn1cblxuLyoqXG4gKiBXYWl0cyBmb3IgYSBjaGlsZCBwcm9jZXNzIHRvIGV4aXQsIHVwIHRvIGEgbGltaXQuXG4gKlxuICogQHBhcmFtIHtPYmplY3R9IHByb2MgLSBUaGUgY2hpbGQgcHJvY2VzcyB0byB3YWl0IGZvci5cbiAqIEBwYXJhbSB7bnVtYmVyfSB0aW1lb3V0IC0gSG93IGxvbmcgdG8gd2FpdCwgaW4gbWlsbGlzZWNvbmRzLlxuICpcbiAqIEByZXR1cm5zIHtQcm9taXNlPGJvb2xlYW4+fSBUcnVlIGlmIHRoZSBwcm9jZXNzIGV4aXRlZCB3aXRoaW4gdGhlIGxpbWl0LlxuICovXG5mdW5jdGlvbiB3YWl0Rm9yRXhpdChwcm9jLCB0aW1lb3V0KSB7XG4gIHJldHVybiBuZXcgUHJvbWlzZSgocmVzb2x2ZSkgPT4ge1xuICAgIGlmICghaXNBbGl2ZShwcm9jKSkge1xuICAgICAgcmV0dXJuIHJlc29sdmUodHJ1ZSk7XG4gICAgfVxuXG4gICAgY29uc3Qgb25FeGl0ID0gKCkgPT4ge1xuICAgICAgY2xlYXJUaW1lb3V0KHRpbWVyKTtcbiAgICAgIHJlc29sdmUodHJ1ZSk7XG4gICAgfTtcblxuICAgIGNvbnN0IHRpbWVyID0gc2V0VGltZW91dCgoKSA9PiB7XG4gICAgICBwcm9jLnJlbW92ZUxpc3RlbmVyKCdleGl0Jywgb25FeGl0KTtcbiAgICAgIHJlc29sdmUoZmFsc2UpO1xuICAgIH0sIHRpbWVvdXQpO1xuXG4gICAgcHJvYy5vbmNlKCdleGl0Jywgb25FeGl0KTtcbiAgfSk7XG59XG5cbi8qKlxuICogQ2xvc2VzIGEgYnJvd3NlciBhbmQgbWFrZXMgY2VydGFpbiBpdHMgcHJvY2VzcyBoYXMgYWN0dWFsbHkgZ29uZS5cbiAqXG4gKiBAcGFyYW0ge09iamVjdH0gaW5zdGFuY2UgLSBUaGUgUHVwcGV0ZWVyIGJyb3dzZXIgaW5zdGFuY2UgdG8gZW5kLlxuICpcbiAqIEByZXR1cm5zIHtQcm9taXNlPHZvaWQ+fSBSZXNvbHZlcyBvbmNlIHRoZSBwcm9jZXNzIGhhcyBleGl0ZWQsIG9yIG9uY2UgZ2l2aW5nXG4gKiB1cCB3YWl0aW5nIGZvciBpdC5cbiAqL1xuYXN5bmMgZnVuY3Rpb24gdGVybWluYXRlKGluc3RhbmNlKSB7XG4gIC8vIFRha2UgdGhlIHByb2Nlc3MgcmVmZXJlbmNlIGJlZm9yZSBjbG9zaW5nLCBhcyBpdCBpcyBub3QgcmVhY2hhYmxlIGFmdGVyd2FyZHNcbiAgY29uc3QgcHJvYyA9IGluc3RhbmNlLnByb2Nlc3MoKTtcblxuICB0cnkge1xuICAgIGF3YWl0IGluc3RhbmNlLmNsb3NlKCk7XG4gIH0gY2F0Y2ggKGVycm9yKSB7XG4gICAgbG9nV2l0aFN0YWNrKDIsIGVycm9yLCAnW2Jyb3dzZXJdIENvdWxkIG5vdCBjbGVhbmx5IGNsb3NlIHRoZSBicm93c2VyLicpO1xuICB9XG5cbiAgaWYgKCFwcm9jKSB7XG4gICAgbG9nKFxuICAgICAgMixcbiAgICAgICdbYnJvd3Nlcl0gTm8gcHJvY2VzcyBoYW5kbGUgZm9yIHRoZSBicm93c2VyLCBzbyBpdHMgZXhpdCBjYW5ub3QgYmUgY29uZmlybWVkLidcbiAgICApO1xuICAgIHJldHVybjtcbiAgfVxuXG4gIC8vIE5PVEU6IGNsb3NlKCkgcmVzb2x2aW5nIGRvZXMgbm90IG1lYW4gdGhlIHByb2Nlc3MgaGFzIGdvbmUuIEl0IGNhbiByZXNvbHZlXG4gIC8vICAgICAgIHdoZW4gdGhlIGNvbm5lY3Rpb24gZHJvcHMsIGFuZCB0aGUgbGF1bmNoIG9wdGlvbnMgZGVsaWJlcmF0ZWx5IGRpc2FibGVcbiAgLy8gICAgICAgUHVwcGV0ZWVyJ3Mgc2lnbmFsIGhhbmRsaW5nLCBzbyBub3RoaW5nIHdpbGwgY2xlYW4gdXAgb24gb3VyIGJlaGFsZi5cbiAgLy9cbiAgLy8gICAgICAgQSBzdXJ2aXZpbmcgcHJvY2VzcyBrZWVwcyBDaHJvbWUncyBsb2NrIG9uIHRoZSB1c2VyIGRhdGEgZGlyZWN0b3J5LFxuICAvLyAgICAgICB3aGljaCBpcyBzaGFyZWQgYnkgZXZlcnkgbGF1bmNoIGhlcmUsIHNvIGxlYXZpbmcgb25lIGJlaGluZCBzdG9wcyBhbnlcbiAgLy8gICAgICAgbGF0ZXIgYnJvd3NlciBmcm9tIHN0YXJ0aW5nIGF0IGFsbCAtIGFuZCBvbiBzaHV0ZG93biBpdCBsZWFrcyBhIGJyb3dzZXJcbiAgLy8gICAgICAgcHJvY2VzcyBvdXRyaWdodC5cbiAgaWYgKGF3YWl0IHdhaXRGb3JFeGl0KHByb2MsIDIwMDApKSB7XG4gICAgcmV0dXJuO1xuICB9XG5cbiAgbG9nKFxuICAgIDIsXG4gICAgJ1ticm93c2VyXSBUaGUgYnJvd3NlciBwcm9jZXNzIGlzIHN0aWxsIHJ1bm5pbmcgYWZ0ZXIgYmVpbmcgY2xvc2VkLCBraWxsaW5nIGl0LidcbiAgKTtcblxuICB0cnkge1xuICAgIHByb2Mua2lsbCgnU0lHS0lMTCcpO1xuICB9IGNhdGNoIChlcnJvcikge1xuICAgIGxvZ1dpdGhTdGFjaygyLCBlcnJvciwgJ1ticm93c2VyXSBDb3VsZCBub3Qga2lsbCB0aGUgYnJvd3NlciBwcm9jZXNzLicpO1xuICB9XG5cbiAgaWYgKGlzQWxpdmUocHJvYykpIHtcbiAgICBsb2coXG4gICAgICAxLFxuICAgICAgYFticm93c2VyXSBUaGUgYnJvd3NlciBwcm9jZXNzICR7cHJvYy5waWR9IGhhcyBzdGlsbCBub3QgZXhpdGVkIGFmdGVyIGJlaW5nIGtpbGxlZC5gXG4gICAgKTtcbiAgfVxufVxuXG4vKipcbiAqIENyZWF0ZXMgYSBQdXBwZXRlZXIgYnJvd3NlciBpbnN0YW5jZSB3aXRoIHRoZSBzcGVjaWZpZWQgYXJndW1lbnRzLlxuICpcbiAqIEBwYXJhbSB7QXJyYXl9IHB1cHBldGVlckFyZ3MgLSBBZGRpdGlvbmFsIGFyZ3VtZW50cyBmb3IgUHVwcGV0ZWVyIGxhdW5jaC5cbiAqXG4gKiBAcmV0dXJucyB7UHJvbWlzZTxvYmplY3Q+fSBBIFByb21pc2UgcmVzb2x2aW5nIHRvIHRoZSBQdXBwZXRlZXIgYnJvd3NlclxuICogaW5zdGFuY2UuXG4gKlxuICogQHRocm93cyB7RXhwb3J0RXJyb3J9IFRocm93cyBhbiBFeHBvcnRFcnJvciBpZiBtYXggcmV0cmllcyB0byBvcGVuIGEgYnJvd3NlclxuICogaW5zdGFuY2UgYXJlIHJlYWNoZWQsIG9yIGlmIG5vIGJyb3dzZXIgaW5zdGFuY2UgaXMgZm91bmQgYWZ0ZXIgcmV0cmllcy5cbiAqL1xuZXhwb3J0IGFzeW5jIGZ1bmN0aW9uIGNyZWF0ZShwdXBwZXRlZXJBcmdzKSB7XG4gIC8vIFJlbWVtYmVyIHRoZSBhcmd1bWVudHMgc28gdGhhdCBhbiB1bmV4cGVjdGVkIGRpc2Nvbm5lY3QgY2FuIHJlbGF1bmNoIHRoZVxuICAvLyBicm93c2VyIG9uIHRoZSBzYW1lIHRlcm1zLCBhbmQgY2xlYXIgYW55IGRlbGliZXJhdGUtY2xvc2Ugc3RhdGUgbGVmdCBmcm9tIGFcbiAgLy8gcHJldmlvdXMgY3ljbGVcbiAgaWYgKHB1cHBldGVlckFyZ3MgIT09IHVuZGVmaW5lZCkge1xuICAgIGxhc3RQdXBwZXRlZXJBcmdzID0gcHVwcGV0ZWVyQXJncztcbiAgfVxuICBjbG9zaW5nT25QdXJwb3NlID0gZmFsc2U7XG5cbiAgaWYgKGJyb3dzZXI/LmNvbm5lY3RlZCkge1xuICAgIHJldHVybiBicm93c2VyO1xuICB9XG5cbiAgLy8gTk9URTogQ29uY3VycmVudCBjYWxsZXJzIG11c3Qgc2hhcmUgYSBzaW5nbGUgbGF1bmNoLiBUaGUgcG9vbCBjcmVhdGVzXG4gIC8vICAgICAgIHNldmVyYWwgd29ya2VycyBhdCBvbmNlLCBhbmQgYWZ0ZXIgYSBkaXNjb25uZWN0IGVhY2ggb2YgdGhlbSBmaW5kc1xuICAvLyAgICAgICBubyBicm93c2VyIGF0IHRoZSBzYW1lIG1vbWVudCAtIHdpdGhvdXQgdGhpcyB0aGV5IHdvdWxkIGxhdW5jaCBhXG4gIC8vICAgICAgIGJyb3dzZXIgZWFjaC5cbiAgaWYgKCFsYXVuY2hQcm9taXNlKSB7XG4gICAgbGF1bmNoUHJvbWlzZSA9IGxhdW5jaEJyb3dzZXIobGFzdFB1cHBldGVlckFyZ3MpLmZpbmFsbHkoKCkgPT4ge1xuICAgICAgbGF1bmNoUHJvbWlzZSA9IG51bGw7XG4gICAgfSk7XG4gIH1cblxuICByZXR1cm4gbGF1bmNoUHJvbWlzZTtcbn1cblxuLyoqXG4gKiBMYXVuY2hlcyBhIFB1cHBldGVlciBicm93c2VyIGluc3RhbmNlLCByZXRyeWluZyBvbiBmYWlsdXJlLlxuICpcbiAqIEBwYXJhbSB7QXJyYXl9IHB1cHBldGVlckFyZ3MgLSBBZGRpdGlvbmFsIGFyZ3VtZW50cyBmb3IgUHVwcGV0ZWVyIGxhdW5jaC5cbiAqXG4gKiBAcmV0dXJucyB7UHJvbWlzZTxvYmplY3Q+fSBBIFByb21pc2UgcmVzb2x2aW5nIHRvIHRoZSBQdXBwZXRlZXIgYnJvd3NlclxuICogaW5zdGFuY2UuXG4gKlxuICogQHRocm93cyB7RXhwb3J0RXJyb3J9IFRocm93cyBhbiBFeHBvcnRFcnJvciBpZiBtYXggcmV0cmllcyB0byBvcGVuIGEgYnJvd3NlclxuICogaW5zdGFuY2UgYXJlIHJlYWNoZWQsIG9yIGlmIG5vIGJyb3dzZXIgaW5zdGFuY2UgaXMgZm91bmQgYWZ0ZXIgcmV0cmllcy5cbiAqL1xuYXN5bmMgZnVuY3Rpb24gbGF1bmNoQnJvd3NlcihwdXBwZXRlZXJBcmdzKSB7XG4gIC8vIEdldCBkZWJ1ZyBhbmQgb3RoZXIgb3B0aW9uc1xuICBjb25zdCB7IHB1cHBldGVlcjogcHVwcGV0ZWVyT3B0aW9ucywgZGVidWcsIG90aGVyIH0gPSBnZXRPcHRpb25zKCk7XG5cbiAgLy8gR2V0IHRoZSBkZWJ1ZyBvcHRpb25zXG4gIGNvbnN0IHsgZW5hYmxlOiBlbmFibGVkRGVidWcsIC4uLmRlYnVnT3B0aW9ucyB9ID0gZGVidWc7XG5cbiAgY29uc3QgbGF1bmNoT3B0aW9ucyA9IHtcbiAgICBoZWFkbGVzczogb3RoZXIuYnJvd3NlclNoZWxsTW9kZSA/ICdzaGVsbCcgOiB0cnVlLFxuICAgIHVzZXJEYXRhRGlyOiBwdXBwZXRlZXJPcHRpb25zLnRlbXBEaXIgfHwgJy4vdG1wLycsXG4gICAgYXJnczogcHVwcGV0ZWVyQXJncyxcbiAgICBoYW5kbGVTSUdJTlQ6IGZhbHNlLFxuICAgIGhhbmRsZVNJR1RFUk06IGZhbHNlLFxuICAgIGhhbmRsZVNJR0hVUDogZmFsc2UsXG4gICAgd2FpdEZvckluaXRpYWxQYWdlOiBmYWxzZSxcbiAgICBkZWZhdWx0Vmlld3BvcnQ6IG51bGwsXG4gICAgLi4uKGVuYWJsZWREZWJ1ZyAmJiBkZWJ1Z09wdGlvbnMpXG4gIH07XG5cbiAgLy8gTk9URTogUmV0cnkgd2l0aGluIGEgdGltZSBidWRnZXQgcmF0aGVyIHRoYW4gZm9yIGEgZml4ZWQgbnVtYmVyIG9mIGF0dGVtcHRzLlxuICAvLyAgICAgICBUaGUgcHJldmlvdXMgMjUgYXR0ZW1wdHMgZm91ciBzZWNvbmRzIGFwYXJ0IG1lYW50IGEgYnJvd3NlciB0aGF0IGNvdWxkXG4gIC8vICAgICAgIG5ldmVyIHN0YXJ0IHRvb2sgMTAwIHNlY29uZHMgdG8gc2F5IHNvLCB3aGljaCBpcyBsb25nZXIgdGhhbiBhblxuICAvLyAgICAgICBvcmNoZXN0cmF0b3Igd2lsbCB1c3VhbGx5IHdhaXQgYmVmb3JlIGRlY2lkaW5nIHRoZSBpbnN0YW5jZSBpc1xuICAvLyAgICAgICB1bmhlYWx0aHkgLSBzbyBpdCB3b3VsZCBiZSByZXBsYWNlZCB3aGlsZSBzdGlsbCByZXBvcnRpbmcgdGhhdCBpdCB3YXNcbiAgLy8gICAgICAgc3RhcnRpbmcsIGFuZCB0aGUgcmVhbCByZWFzb24gbmV2ZXIgc3VyZmFjZWQuXG4gIC8vXG4gIC8vICAgICAgIFRoZSBkZWxheSBncm93cyBhbmQgY2FycmllcyBqaXR0ZXIsIHNvIHRoYXQgYSBmbGVldCByZXN0YXJ0aW5nIHRvZ2V0aGVyXG4gIC8vICAgICAgIGRvZXMgbm90IHJldHJ5IGluIGxvY2tzdGVwIGFnYWluc3Qgd2hhdGV2ZXIgdGhleSBhcmUgYWxsIGNvbnRlbmRpbmcgZm9yLlxuICBjb25zdCByZXRyeVdpbmRvdyA9IGdldExhdW5jaFJldHJ5V2luZG93KCk7XG4gIGNvbnN0IGRlYWRsaW5lID0gRGF0ZS5ub3coKSArIHJldHJ5V2luZG93O1xuXG4gIGxldCBhdHRlbXB0ID0gMDtcbiAgbGV0IGRlbGF5ID0gMjUwO1xuXG4gIGZvciAoOzspIHtcbiAgICBhdHRlbXB0Kys7XG5cbiAgICB0cnkge1xuICAgICAgbG9nKFxuICAgICAgICAzLFxuICAgICAgICBgW2Jyb3dzZXJdIEF0dGVtcHRpbmcgdG8gZ2V0IGEgYnJvd3NlciBpbnN0YW5jZSAoYXR0ZW1wdCAke2F0dGVtcHR9KS5gXG4gICAgICApO1xuICAgICAgYnJvd3NlciA9IGF3YWl0IHB1cHBldGVlci5sYXVuY2gobGF1bmNoT3B0aW9ucyk7XG4gICAgICBicmVhaztcbiAgICB9IGNhdGNoIChlcnJvcikge1xuICAgICAgY29uc3QgcmVtYWluaW5nID0gZGVhZGxpbmUgLSBEYXRlLm5vdygpO1xuXG4gICAgICBpZiAocmVtYWluaW5nIDw9IDApIHtcbiAgICAgICAgdGhyb3cgbmV3IEV4cG9ydEVycm9yKFxuICAgICAgICAgIGBbYnJvd3Nlcl0gQ291bGQgbm90IGxhdW5jaCBhIGJyb3dzZXIgd2l0aGluICR7cmV0cnlXaW5kb3d9bXMsIGFmdGVyICR7YXR0ZW1wdH0gYXR0ZW1wdHMuYFxuICAgICAgICApLnNldEVycm9yKGVycm9yKTtcbiAgICAgIH1cblxuICAgICAgLy8gTm90IGEgZnVsbCBlcnJvciB5ZXQsIGFzIFB1cHBldGVlciBzb21ldGltZXMgbmVlZHMgYSBtb21lbnQgdG8gc2V0dGxlXG4gICAgICBsb2dXaXRoU3RhY2soXG4gICAgICAgIDIsXG4gICAgICAgIGVycm9yLFxuICAgICAgICBgW2Jyb3dzZXJdIEZhaWxlZCB0byBsYXVuY2ggYSBicm93c2VyIGluc3RhbmNlLCByZXRyeWluZyAoYXR0ZW1wdCAke2F0dGVtcHR9LCAke01hdGguY2VpbChyZW1haW5pbmcgLyAxMDAwKX1zIG9mIHRoZSByZXRyeSB3aW5kb3cgbGVmdCkuYFxuICAgICAgKTtcblxuICAgICAgYXdhaXQgbmV3IFByb21pc2UoKHJlc29sdmUpID0+XG4gICAgICAgIHNldFRpbWVvdXQoXG4gICAgICAgICAgcmVzb2x2ZSxcbiAgICAgICAgICBNYXRoLm1pbihkZWxheSArIE1hdGgucm91bmQoTWF0aC5yYW5kb20oKSAqIGRlbGF5KSwgcmVtYWluaW5nKVxuICAgICAgICApXG4gICAgICApO1xuXG4gICAgICBkZWxheSA9IE1hdGgubWluKGRlbGF5ICogMiwgNDAwMCk7XG4gICAgfVxuICB9XG5cbiAgLy8gU2hlbGwgbW9kZSBpbmZvcm1cbiAgaWYgKGxhdW5jaE9wdGlvbnMuaGVhZGxlc3MgPT09ICdzaGVsbCcpIHtcbiAgICBsb2coMywgYFticm93c2VyXSBMYXVuY2hlZCBicm93c2VyIGluIHNoZWxsIG1vZGUuYCk7XG4gIH1cblxuICAvLyBEZWJ1ZyBtb2RlIGluZm9ybVxuICBpZiAoZW5hYmxlZERlYnVnKSB7XG4gICAgbG9nKDMsIGBbYnJvd3Nlcl0gTGF1bmNoZWQgYnJvd3NlciBpbiBkZWJ1ZyBtb2RlLmApO1xuICB9XG5cbiAgaWYgKCFicm93c2VyKSB7XG4gICAgdGhyb3cgbmV3IEV4cG9ydEVycm9yKCdbYnJvd3Nlcl0gQ2Fubm90IGZpbmQgYSBicm93c2VyIHRvIG9wZW4uJyk7XG4gIH1cblxuICAvLyBOb3RpY2UgdGhlIGJyb3dzZXIgZ29pbmcgYXdheSwgc28gdGhhdCB0aGUgcG9vbCdzIHdvcmtlcnMgY2FuIGJlXG4gIC8vIGludmFsaWRhdGVkIGFuZCB0aGUgYnJvd3NlciByZWxhdW5jaGVkLCByYXRoZXIgdGhhbiB0aGUgcG9vbCBoYW5kaW5nIG91dFxuICAvLyBwYWdlcyBiZWxvbmdpbmcgdG8gYSBwcm9jZXNzIHRoYXQgbm8gbG9uZ2VyIGV4aXN0c1xuICBicm93c2VyLm9uY2UoJ2Rpc2Nvbm5lY3RlZCcsIGhhbmRsZURpc2Nvbm5lY3QpO1xuXG4gIHJldHVybiBicm93c2VyO1xufVxuXG4vKipcbiAqIENsb3NlcyB0aGUgUHVwcGV0ZWVyIGJyb3dzZXIgaW5zdGFuY2UgaWYgaXQgaXMgY29ubmVjdGVkLlxuICpcbiAqIEByZXR1cm5zIHtQcm9taXNlPGJvb2xlYW4+fSBBIFByb21pc2UgcmVzb2x2aW5nIHRvIHRydWUgYWZ0ZXIgdGhlIGJyb3dzZXJcbiAqIGlzIGNsb3NlZC5cbiAqL1xuZXhwb3J0IGFzeW5jIGZ1bmN0aW9uIGNsb3NlKCkge1xuICAvLyBNYXJrIHRoaXMgYXMgaW50ZW50aW9uYWwgc28gdGhlIHJlc3VsdGluZyBkaXNjb25uZWN0IGlzIG5vdCB0cmVhdGVkIGFzIGFcbiAgLy8gY3Jhc2ggYW5kIGRvZXMgbm90IHRyaWdnZXIgYSByZWxhdW5jaFxuICBjbG9zaW5nT25QdXJwb3NlID0gdHJ1ZTtcblxuICAvLyBDbG9zZSB0aGUgYnJvd3NlciBhbmQgbWFrZSBzdXJlIGl0cyBwcm9jZXNzIGhhcyBnb25lLCBzbyB0aGF0IHNodXRkb3duIGRvZXNcbiAgLy8gbm90IGxlYXZlIG9uZSBiZWhpbmQgaG9sZGluZyB0aGUgdXNlciBkYXRhIGRpcmVjdG9yeVxuICBpZiAoYnJvd3Nlcikge1xuICAgIGF3YWl0IHRlcm1pbmF0ZShicm93c2VyKTtcbiAgfVxuXG4gIGJyb3dzZXIgPSB1bmRlZmluZWQ7XG5cbiAgbG9nKDQsICdbYnJvd3Nlcl0gQ2xvc2VkIHRoZSBicm93c2VyLicpO1xufVxuXG4vKipcbiAqIENyZWF0ZXMgYSBuZXcgUHVwcGV0ZWVyIFBhZ2Ugd2l0aGluIGFuIGV4aXN0aW5nIGJyb3dzZXIgaW5zdGFuY2UuXG4gKlxuICogSWYgdGhlIGJyb3dzZXIgaW5zdGFuY2UgaXMgbm90IGF2YWlsYWJsZSwgcmV0dXJucyBmYWxzZS5cbiAqXG4gKiBUaGUgZnVuY3Rpb24gY3JlYXRlcyBhIG5ldyBwYWdlLCBkaXNhYmxlcyBjYWNoaW5nLCBzZXRzIGNvbnRlbnQgdXNpbmdcbiAqIHNldFBhZ2VDb250ZW50KCksIGFuZCByZXR1cm5zIHRoZSBjcmVhdGVkIFB1cHBldGVlciBQYWdlLlxuICpcbiAqIElmIHRoZSBicm93c2VyIGlzIG5vdCBjdXJyZW50bHkgYXZhaWxhYmxlIGl0IGlzIHJlbGF1bmNoZWQgZmlyc3QsIHNvIHRoYXQgdGhlXG4gKiBwb29sIGNhbiByZWNvdmVyIGJ5IGl0c2VsZiBhZnRlciB0aGUgYnJvd3NlciBwcm9jZXNzIGhhcyBnb25lIGF3YXkuXG4gKlxuICogQHJldHVybnMgeyhib29sZWFufG9iamVjdCl9IFJldHVybnMgZmFsc2UgaWYgdGhlIGJyb3dzZXIgaW5zdGFuY2UgaXMgbm90XG4gKiBhdmFpbGFibGUsIG9yIGEgUHVwcGV0ZWVyIFBhZ2Ugb2JqZWN0IHJlcHJlc2VudGluZyB0aGUgbmV3bHkgY3JlYXRlZCBwYWdlLlxuICovXG5leHBvcnQgYXN5bmMgZnVuY3Rpb24gbmV3UGFnZSgpIHtcbiAgLy8gVGhlIGJyb3dzZXIgbWF5IGhhdmUgZ29uZSBhd2F5IHNpbmNlIHRoZSBsYXN0IHBhZ2Ugd2FzIG1hZGUuIEJyaW5nIGl0IGJhY2tcbiAgLy8gcmF0aGVyIHRoYW4gZmFpbGluZyBldmVyeSBleHBvcnQgZnJvbSBoZXJlIG9uIC0gY3JlYXRlKCkgaXMgZ3VhcmRlZCwgc29cbiAgLy8gY29uY3VycmVudCBjYWxsZXJzIHNoYXJlIHRoZSBvbmUgcmVsYXVuY2guXG4gIGlmICghYnJvd3Nlcj8uY29ubmVjdGVkICYmICFjbG9zaW5nT25QdXJwb3NlKSB7XG4gICAgbG9nKDMsICdbYnJvd3Nlcl0gTm8gYnJvd3NlciBhdmFpbGFibGUsIGF0dGVtcHRpbmcgdG8gcmVsYXVuY2ggaXQuJyk7XG4gICAgYXdhaXQgY3JlYXRlKCk7XG4gIH1cblxuICBpZiAoIWJyb3dzZXIpIHtcbiAgICByZXR1cm4gZmFsc2U7XG4gIH1cblxuICBsZXQgcGFnZTtcblxuICB0cnkge1xuICAgIC8vIENyZWF0ZSBhIHBhZ2VcbiAgICBwYWdlID0gYXdhaXQgYnJvd3Nlci5uZXdQYWdlKCk7XG5cbiAgICAvLyBEaXNhYmxlIGNhY2hlXG4gICAgYXdhaXQgcGFnZS5zZXRDYWNoZUVuYWJsZWQoZmFsc2UpO1xuXG4gICAgLy8gU2V0IHRoZSBjb250ZW50XG4gICAgYXdhaXQgc2V0UGFnZUNvbnRlbnQocGFnZSk7XG5cbiAgICAvLyBTZXQgcGFnZSBldmVudHNcbiAgICBzZXRQYWdlRXZlbnRzKHBhZ2UpO1xuXG4gICAgcmV0dXJuIHBhZ2U7XG4gIH0gY2F0Y2ggKGVycm9yKSB7XG4gICAgLy8gTk9URTogV2l0aG91dCB0aGlzLCBhIHBhZ2UgY3JlYXRlZCBhYm92ZSBidXQgZmFpbGluZyBhbnkgb2YgdGhlXG4gICAgLy8gICAgICAgc3Vic2VxdWVudCBzdGVwcyBpcyBsZWZ0IG9wZW4gZm9yZXZlciAtIHRoZSBjYWxsZXIgb25seSByZWNlaXZlc1xuICAgIC8vICAgICAgIHRoZSBlcnJvciBhbmQgbmV2ZXIgaGFkIGEgcmVmZXJlbmNlIHRvIGNsb3NlIGl0LiBzZXRQYWdlQ29udGVudCBpc1xuICAgIC8vICAgICAgIHRoZSBsaWtlbHkgdGhyb3dlciwgYXMgaXQgaW5qZWN0cyB0aGUgZW50aXJlIEhpZ2hjaGFydHMgYnVuZGxlIGFuZFxuICAgIC8vICAgICAgIHNvIGlzIHNlbnNpdGl2ZSB0byBhIENQVSBzdGFydmVkIGluc3RhbmNlLiBUaGF0IG1hdHRlcnMgYmVjYXVzZSBvblxuICAgIC8vICAgICAgIGEgc3VzdGFpbmVkIGNyZWF0ZSBmYWlsdXJlIHRhcm4gcmV0cmllcyBldmVyeVxuICAgIC8vICAgICAgIGNyZWF0ZVJldHJ5SW50ZXJ2YWwgKDIwMG1zIGJ5IGRlZmF1bHQpLCB3aGljaCB3b3VsZCBsZWFrIGFub3RoZXJcbiAgICAvLyAgICAgICBicm93c2VyIHRhYiBvbiBldmVyeSBhdHRlbXB0LlxuICAgIGlmIChwYWdlICYmICFwYWdlLmlzQ2xvc2VkKCkpIHtcbiAgICAgIHRyeSB7XG4gICAgICAgIGF3YWl0IHBhZ2UuY2xvc2UoKTtcbiAgICAgIH0gY2F0Y2ggKGNsb3NlRXJyb3IpIHtcbiAgICAgICAgbG9nV2l0aFN0YWNrKFxuICAgICAgICAgIDIsXG4gICAgICAgICAgY2xvc2VFcnJvcixcbiAgICAgICAgICAnW2Jyb3dzZXJdIENvdWxkIG5vdCBjbG9zZSBhIHBhZ2UgdGhhdCBmYWlsZWQgdG8gYmUgc2V0IHVwLidcbiAgICAgICAgKTtcbiAgICAgIH1cbiAgICB9XG5cbiAgICB0aHJvdyBlcnJvcjtcbiAgfVxufVxuXG4vKipcbiAqIENsZWFycyB0aGUgY29udGVudCBvZiBhIFB1cHBldGVlciBQYWdlIGJhc2VkIG9uIHRoZSBzcGVjaWZpZWQgbW9kZS5cbiAqXG4gKiBAcGFyYW0ge09iamVjdH0gcGFnZSAtIFRoZSBQdXBwZXRlZXIgUGFnZSBvYmplY3QgdG8gYmUgY2xlYXJlZC5cbiAqIEBwYXJhbSB7Ym9vbGVhbn0gaGFyZFJlc2V0IC0gQSBmbGFnIGluZGljYXRpbmcgdGhlIHR5cGUgb2YgY2xlYXJpbmdcbiAqIHRvIGJlIHBlcmZvcm1lZC4gSWYgdHJ1ZSwgbmF2aWdhdGVzIHRvICdhYm91dDpibGFuaycgYW5kIHJlc2V0cyBjb250ZW50XG4gKiBhbmQgc2NyaXB0cy4gSWYgZmFsc2UsIGNsZWFycyB0aGUgYm9keSBjb250ZW50IGJ5IHNldHRpbmcgYSBwcmVkZWZpbmVkIEhUTUxcbiAqIHN0cnVjdHVyZS5cbiAqXG4gKiBAdGhyb3dzIHtFcnJvcn0gTG9ncyB0aHJvd24gZXJyb3IgaWYgY2xlYXJpbmcgdGhlIHBhZ2UgY29udGVudCBmYWlscy5cbiAqL1xuZXhwb3J0IGFzeW5jIGZ1bmN0aW9uIGNsZWFyUGFnZShwYWdlLCBoYXJkUmVzZXQgPSBmYWxzZSkge1xuICB0cnkge1xuICAgIGlmIChwYWdlICYmICFwYWdlLmlzQ2xvc2VkKCkpIHtcbiAgICAgIGlmIChoYXJkUmVzZXQpIHtcbiAgICAgICAgLy8gTmF2aWdhdGUgdG8gYWJvdXQ6YmxhbmtcbiAgICAgICAgYXdhaXQgcGFnZS5nb3RvKCdhYm91dDpibGFuaycsIHsgd2FpdFVudGlsOiAnZG9tY29udGVudGxvYWRlZCcgfSk7XG5cbiAgICAgICAgLy8gU2V0IHRoZSBjb250ZW50IGFuZCBhbmQgc2NyaXB0cyBhZ2FpblxuICAgICAgICBhd2FpdCBzZXRQYWdlQ29udGVudChwYWdlKTtcbiAgICAgIH0gZWxzZSB7XG4gICAgICAgIC8vIENsZWFyIGJvZHkgY29udGVudFxuICAgICAgICBhd2FpdCBwYWdlLmV2YWx1YXRlKCgpID0+IHtcbiAgICAgICAgICBkb2N1bWVudC5ib2R5LmlubmVySFRNTCA9XG4gICAgICAgICAgICAnPGRpdiBpZD1cImNoYXJ0LWNvbnRhaW5lclwiPjxkaXYgaWQ9XCJjb250YWluZXJcIj48L2Rpdj48L2Rpdj4nO1xuICAgICAgICB9KTtcbiAgICAgIH1cbiAgICAgIHJldHVybiB0cnVlO1xuICAgIH1cbiAgfSBjYXRjaCAoZXJyb3IpIHtcbiAgICBsb2dXaXRoU3RhY2soXG4gICAgICAyLFxuICAgICAgZXJyb3IsXG4gICAgICAnW2Jyb3dzZXJdIENvdWxkIG5vdCBjbGVhciB0aGUgY29udGVudCBvZiB0aGUgcGFnZS4nXG4gICAgKTtcbiAgfVxuXG4gIHJldHVybiBmYWxzZTtcbn1cblxuLyoqXG4gKiBBZGRzIGN1c3RvbSBKUyBhbmQgQ1NTIHJlc291cmNlcyB0byBhIFB1cHBldGVlciBQYWdlIGJhc2VkIG9uIHRoZSBzcGVjaWZpZWRcbiAqIG9wdGlvbnMuXG4gKlxuICogQHBhcmFtIHtPYmplY3R9IHBhZ2UgLSBUaGUgUHVwcGV0ZWVyIFBhZ2Ugb2JqZWN0IHRvIHdoaWNoIHJlc291cmNlcyB3aWxsIGJlXG4gKiBhZGRlZC5cbiAqIEBwYXJhbSB7T2JqZWN0fSBvcHRpb25zIC0gQWxsIG9wdGlvbnMgYW5kIGNvbmZpZ3VyYXRpb24uXG4gKlxuICogQHJldHVybnMge1Byb21pc2U8QXJyYXk8T2JqZWN0Pj59IC0gUHJvbWlzZSByZXNvbHZpbmcgdG8gYW4gYXJyYXkgb2YgaW5qZWN0ZWRcbiAqIHJlc291cmNlcy5cbiAqL1xuZXhwb3J0IGFzeW5jIGZ1bmN0aW9uIGFkZFBhZ2VSZXNvdXJjZXMocGFnZSwgb3B0aW9ucykge1xuICAvLyBJbmplY3RlZCByZXNvdXJjZXMgYXJyYXlcbiAgY29uc3QgaW5qZWN0ZWRSZXNvdXJjZXMgPSBbXTtcblxuICAvLyBVc2UgcmVzb3VyY2VzXG4gIGNvbnN0IHJlc291cmNlcyA9IG9wdGlvbnMuY3VzdG9tTG9naWMucmVzb3VyY2VzO1xuICBpZiAocmVzb3VyY2VzKSB7XG4gICAgY29uc3QgaW5qZWN0ZWRKcyA9IFtdO1xuXG4gICAgLy8gTG9hZCBjdXN0b20gSlMgY29kZVxuICAgIGlmIChyZXNvdXJjZXMuanMpIHtcbiAgICAgIGluamVjdGVkSnMucHVzaCh7XG4gICAgICAgIGNvbnRlbnQ6IHJlc291cmNlcy5qc1xuICAgICAgfSk7XG4gICAgfVxuXG4gICAgLy8gTG9hZCBzY3JpcHRzIGZyb20gYWxsIGN1c3RvbSBmaWxlc1xuICAgIGlmIChyZXNvdXJjZXMuZmlsZXMpIHtcbiAgICAgIGZvciAoY29uc3QgZmlsZSBvZiByZXNvdXJjZXMuZmlsZXMpIHtcbiAgICAgICAgY29uc3QgaXNMb2NhbCA9ICFmaWxlLnN0YXJ0c1dpdGgoJ2h0dHAnKSA/IHRydWUgOiBmYWxzZTtcblxuICAgICAgICAvLyBBZGQgZWFjaCBjdXN0b20gc2NyaXB0IGZyb20gcmVzb3VyY2VzJyBmaWxlc1xuICAgICAgICBpbmplY3RlZEpzLnB1c2goXG4gICAgICAgICAgaXNMb2NhbFxuICAgICAgICAgICAgPyB7XG4gICAgICAgICAgICAgICAgY29udGVudDogcmVhZEZpbGVTeW5jKGZpbGUsICd1dGY4JylcbiAgICAgICAgICAgICAgfVxuICAgICAgICAgICAgOiB7XG4gICAgICAgICAgICAgICAgdXJsOiBmaWxlXG4gICAgICAgICAgICAgIH1cbiAgICAgICAgKTtcbiAgICAgIH1cbiAgICB9XG5cbiAgICBmb3IgKGNvbnN0IGpzUmVzb3VyY2Ugb2YgaW5qZWN0ZWRKcykge1xuICAgICAgdHJ5IHtcbiAgICAgICAgaW5qZWN0ZWRSZXNvdXJjZXMucHVzaChhd2FpdCBwYWdlLmFkZFNjcmlwdFRhZyhqc1Jlc291cmNlKSk7XG4gICAgICB9IGNhdGNoIChlcnJvcikge1xuICAgICAgICBsb2dXaXRoU3RhY2soMiwgZXJyb3IsIGBbZXhwb3J0XSBUaGUgSlMgcmVzb3VyY2UgY2Fubm90IGJlIGxvYWRlZC5gKTtcbiAgICAgIH1cbiAgICB9XG4gICAgaW5qZWN0ZWRKcy5sZW5ndGggPSAwO1xuXG4gICAgLy8gTG9hZCBDU1NcbiAgICBjb25zdCBpbmplY3RlZENzcyA9IFtdO1xuICAgIGlmIChyZXNvdXJjZXMuY3NzKSB7XG4gICAgICBsZXQgY3NzSW1wb3J0cyA9IHJlc291cmNlcy5jc3MubWF0Y2goL0BpbXBvcnRcXHMqKFteO10qKTsvZyk7XG4gICAgICBpZiAoY3NzSW1wb3J0cykge1xuICAgICAgICAvLyBIYW5kbGUgY3NzIHNlY3Rpb25cbiAgICAgICAgZm9yIChsZXQgY3NzSW1wb3J0UGF0aCBvZiBjc3NJbXBvcnRzKSB7XG4gICAgICAgICAgaWYgKGNzc0ltcG9ydFBhdGgpIHtcbiAgICAgICAgICAgIGNzc0ltcG9ydFBhdGggPSBjc3NJbXBvcnRQYXRoXG4gICAgICAgICAgICAgIC5yZXBsYWNlKCd1cmwoJywgJycpXG4gICAgICAgICAgICAgIC5yZXBsYWNlKCdAaW1wb3J0JywgJycpXG4gICAgICAgICAgICAgIC5yZXBsYWNlKC9cIi9nLCAnJylcbiAgICAgICAgICAgICAgLnJlcGxhY2UoLycvZywgJycpXG4gICAgICAgICAgICAgIC5yZXBsYWNlKC87LywgJycpXG4gICAgICAgICAgICAgIC5yZXBsYWNlKC9cXCkvZywgJycpXG4gICAgICAgICAgICAgIC50cmltKCk7XG5cbiAgICAgICAgICAgIC8vIEFkZCBlYWNoIGN1c3RvbSBjc3MgZnJvbSByZXNvdXJjZXNcbiAgICAgICAgICAgIGlmIChjc3NJbXBvcnRQYXRoLnN0YXJ0c1dpdGgoJ2h0dHAnKSkge1xuICAgICAgICAgICAgICBpbmplY3RlZENzcy5wdXNoKHtcbiAgICAgICAgICAgICAgICB1cmw6IGNzc0ltcG9ydFBhdGhcbiAgICAgICAgICAgICAgfSk7XG4gICAgICAgICAgICB9IGVsc2UgaWYgKG9wdGlvbnMuY3VzdG9tTG9naWMuYWxsb3dGaWxlUmVzb3VyY2VzKSB7XG4gICAgICAgICAgICAgIGluamVjdGVkQ3NzLnB1c2goe1xuICAgICAgICAgICAgICAgIHBhdGg6IHBhdGguam9pbihfX2Rpcm5hbWUsIGNzc0ltcG9ydFBhdGgpXG4gICAgICAgICAgICAgIH0pO1xuICAgICAgICAgICAgfVxuICAgICAgICAgIH1cbiAgICAgICAgfVxuICAgICAgfVxuXG4gICAgICAvLyBUaGUgcmVzdCBvZiB0aGUgQ1NTIHNlY3Rpb24gd2lsbCBiZSBjb250ZW50IGJ5IG5vd1xuICAgICAgaW5qZWN0ZWRDc3MucHVzaCh7XG4gICAgICAgIGNvbnRlbnQ6IHJlc291cmNlcy5jc3MucmVwbGFjZSgvQGltcG9ydFxccyooW147XSopOy9nLCAnJykgfHwgJyAnXG4gICAgICB9KTtcblxuICAgICAgZm9yIChjb25zdCBjc3NSZXNvdXJjZSBvZiBpbmplY3RlZENzcykge1xuICAgICAgICB0cnkge1xuICAgICAgICAgIGluamVjdGVkUmVzb3VyY2VzLnB1c2goYXdhaXQgcGFnZS5hZGRTdHlsZVRhZyhjc3NSZXNvdXJjZSkpO1xuICAgICAgICB9IGNhdGNoIChlcnJvcikge1xuICAgICAgICAgIGxvZ1dpdGhTdGFjaygyLCBlcnJvciwgYFtleHBvcnRdIFRoZSBDU1MgcmVzb3VyY2UgY2Fubm90IGJlIGxvYWRlZC5gKTtcbiAgICAgICAgfVxuICAgICAgfVxuICAgICAgaW5qZWN0ZWRDc3MubGVuZ3RoID0gMDtcbiAgICB9XG4gIH1cbiAgcmV0dXJuIGluamVjdGVkUmVzb3VyY2VzO1xufVxuXG4vKipcbiAqIENsZWFycyBvdXQgYWxsIHN0YXRlIHNldCBvbiB0aGUgcGFnZSB3aXRoIGFkZFNjcmlwdFRhZy9hZGRTdHlsZVRhZy4gUmVtb3Zlc1xuICogaW5qZWN0ZWQgcmVzb3VyY2VzIGFuZCByZXNldHMgQ1NTIGFuZCBzY3JpcHQgdGFncyBvbiB0aGUgcGFnZS4gQWRkaXRpb25hbGx5LFxuICogaXQgZGVzdHJveXMgcHJldmlvdXNseSBleGlzdGluZyBjaGFydHMuXG4gKlxuICogQHBhcmFtIHtPYmplY3R9IHBhZ2UgLSBUaGUgUHVwcGV0ZWVyIFBhZ2Ugb2JqZWN0IGZyb20gd2hpY2ggcmVzb3VyY2VzIHdpbGxcbiAqIGJlIGNsZWFyZWQuXG4gKiBAcGFyYW0ge0FycmF5PE9iamVjdD59IGluamVjdGVkUmVzb3VyY2VzIC0gQXJyYXkgb2YgaW5qZWN0ZWQgcmVzb3VyY2VzXG4gKiB0byBiZSBjbGVhcmVkLlxuICovXG5leHBvcnQgYXN5bmMgZnVuY3Rpb24gY2xlYXJQYWdlUmVzb3VyY2VzKHBhZ2UsIGluamVjdGVkUmVzb3VyY2VzKSB7XG4gIHRyeSB7XG4gICAgZm9yIChjb25zdCByZXNvdXJjZSBvZiBpbmplY3RlZFJlc291cmNlcykge1xuICAgICAgYXdhaXQgcmVzb3VyY2UuZGlzcG9zZSgpO1xuICAgIH1cblxuICAgIC8vIERlc3Ryb3kgb2xkIGNoYXJ0cyBhZnRlciBleHBvcnQgaXMgZG9uZSBhbmQgcmVzZXQgYWxsIENTUyBhbmQgc2NyaXB0IHRhZ3NcbiAgICBhd2FpdCBwYWdlLmV2YWx1YXRlKCgpID0+IHtcbiAgICAgIC8vIFdlIGFyZSBub3QgZ3VhcmFudGVlZCB0aGF0IEhpZ2hjaGFydHMgaXMgbG9hZGVkLCBlLGcsIHdoZW4gZG9pbmcgU1ZHXG4gICAgICAvLyBleHBvcnRzXG4gICAgICBpZiAodHlwZW9mIEhpZ2hjaGFydHMgIT09ICd1bmRlZmluZWQnKSB7XG4gICAgICAgIC8vIGVzbGludC1kaXNhYmxlLW5leHQtbGluZSBuby11bmRlZlxuICAgICAgICBjb25zdCBvbGRDaGFydHMgPSBIaWdoY2hhcnRzLmNoYXJ0cztcblxuICAgICAgICAvLyBDaGVjayBpbiBhbnkgYWxyZWFkeSBleGlzdGluZyBjaGFydHNcbiAgICAgICAgaWYgKEFycmF5LmlzQXJyYXkob2xkQ2hhcnRzKSAmJiBvbGRDaGFydHMubGVuZ3RoKSB7XG4gICAgICAgICAgLy8gRGVzdHJveSBvbGQgY2hhcnRzXG4gICAgICAgICAgZm9yIChjb25zdCBvbGRDaGFydCBvZiBvbGRDaGFydHMpIHtcbiAgICAgICAgICAgIG9sZENoYXJ0ICYmIG9sZENoYXJ0LmRlc3Ryb3koKTtcbiAgICAgICAgICAgIC8vIGVzbGludC1kaXNhYmxlLW5leHQtbGluZSBuby11bmRlZlxuICAgICAgICAgICAgSGlnaGNoYXJ0cy5jaGFydHMuc2hpZnQoKTtcbiAgICAgICAgICB9XG4gICAgICAgIH1cbiAgICAgIH1cblxuICAgICAgY29uc3QgWy4uLnNjcmlwdHNUb1JlbW92ZV0gPSBkb2N1bWVudC5nZXRFbGVtZW50c0J5VGFnTmFtZSgnc2NyaXB0Jyk7XG4gICAgICBjb25zdCBbLCAuLi5zdHlsZXNUb1JlbW92ZV0gPSBkb2N1bWVudC5nZXRFbGVtZW50c0J5VGFnTmFtZSgnc3R5bGUnKTtcbiAgICAgIGNvbnN0IFsuLi5saW5rc1RvUmVtb3ZlXSA9IGRvY3VtZW50LmdldEVsZW1lbnRzQnlUYWdOYW1lKCdsaW5rJyk7XG5cbiAgICAgIC8vIFJlbW92ZSB0YWdzXG4gICAgICBmb3IgKGNvbnN0IGVsZW1lbnQgb2YgW1xuICAgICAgICAuLi5zY3JpcHRzVG9SZW1vdmUsXG4gICAgICAgIC4uLnN0eWxlc1RvUmVtb3ZlLFxuICAgICAgICAuLi5saW5rc1RvUmVtb3ZlXG4gICAgICBdKSB7XG4gICAgICAgIGVsZW1lbnQucmVtb3ZlKCk7XG4gICAgICB9XG4gICAgfSk7XG4gIH0gY2F0Y2ggKGVycm9yKSB7XG4gICAgbG9nV2l0aFN0YWNrKDIsIGVycm9yLCBgW2Jyb3dzZXJdIENvdWxkIG5vdCBjbGVhciBwYWdlJ3MgcmVzb3VyY2VzLmApO1xuICB9XG59XG5cbi8qKlxuICogU2V0cyB0aGUgY29udGVudCBmb3IgYSBQdXBwZXRlZXIgUGFnZSB1c2luZyBhIHByZWRlZmluZWQgdGVtcGxhdGVcbiAqIGFuZCBhZGRpdGlvbmFsIHNjcmlwdHMuIEFsc28sIHNldHMgdGhlIHBhZ2VlcnJvciBpbiBvcmRlciB0byBjYXRjaFxuICogYW5kIGRpc3BsYXkgZXJyb3JzIGZyb20gdGhlIHdpbmRvdyBjb250ZXh0LlxuICpcbiAqIEBwYXJhbSB7T2JqZWN0fSBwYWdlIC0gVGhlIFB1cHBldGVlciBQYWdlIG9iamVjdCBmb3Igd2hpY2ggdGhlIGNvbnRlbnRcbiAqIGlzIGJlaW5nIHNldC5cbiAqL1xuYXN5bmMgZnVuY3Rpb24gc2V0UGFnZUNvbnRlbnQocGFnZSkge1xuICBhd2FpdCBwYWdlLnNldENvbnRlbnQodGVtcGxhdGUsIHsgd2FpdFVudGlsOiAnZG9tY29udGVudGxvYWRlZCcgfSk7XG5cbiAgLy8gQWRkIGFsbCByZWdpc3RlcmVkIEhpZ2NoYXJ0cyBzY3JpcHRzLCBxdWl0ZSBkZW1hbmRpbmdcbiAgYXdhaXQgcGFnZS5hZGRTY3JpcHRUYWcoeyBwYXRoOiBgJHtnZXRDYWNoZVBhdGgoKX0vc291cmNlcy5qc2AgfSk7XG5cbiAgLy8gU2V0IHRoZSBpbml0aWFsIGFuaW1PYmplY3RcbiAgYXdhaXQgcGFnZS5ldmFsdWF0ZShzZXR1cEhpZ2hjaGFydHMpO1xufVxuXG4vKipcbiAqIFNldCBldmVudHMgZm9yIGEgUHVwcGV0ZWVyIFBhZ2UuXG4gKlxuICogQHBhcmFtIHtPYmplY3R9IHBhZ2UgLSBUaGUgUHVwcGV0ZWVyIFBhZ2Ugb2JqZWN0IHRvIHNldCBldmVudHMgdG8uXG4gKi9cbmZ1bmN0aW9uIHNldFBhZ2VFdmVudHMocGFnZSkge1xuICAvLyBHZXQgZGVidWcgb3B0aW9uc1xuICBjb25zdCB7IGRlYnVnIH0gPSBnZXRPcHRpb25zKCk7XG5cbiAgLy8gU2V0IHRoZSBjb25zb2xlIGxpc3RlbmVyLCBpZiBuZWVkZWRcbiAgaWYgKGRlYnVnLmVuYWJsZSAmJiBkZWJ1Zy5saXN0ZW5Ub0NvbnNvbGUpIHtcbiAgICBwYWdlLm9uKCdjb25zb2xlJywgKG1lc3NhZ2UpID0+IHtcbiAgICAgIGNvbnNvbGUubG9nKGBbZGVidWddICR7bWVzc2FnZS50ZXh0KCl9YCk7XG4gICAgfSk7XG4gIH1cblxuICAvLyBTZXQgdGhlIHBhZ2VlcnJvciBsaXN0ZW5lclxuICBwYWdlLm9uKCdwYWdlZXJyb3InLCBhc3luYyAoZXJyb3IpID0+IHtcbiAgICAvLyBJdCB3b3VsZCBzZWVtIGxpa2UgdGhpcyBtYXkgZmlyZSBhdCB0aGUgc2FtZSB0aW1lIG9yIHNob3J0bHkgYmVmb3JlXG4gICAgLy8gYSBwYWdlIGlzIGNsb3NlZC5cbiAgICBpZiAocGFnZS5pc0Nsb3NlZCgpKSB7XG4gICAgICByZXR1cm47XG4gICAgfVxuXG4gICAgLy8gVE9ETzogQ29uc2lkZXIgYWRkaW5nIGEgc3dpdGNoIGhlcmUgdGhhdCB0dXJucyBvbiBsb2coMCkgbG9nZ2luZ1xuICAgIC8vIG9uIHBhZ2UgZXJyb3JzLlxuICAgIGF3YWl0IHBhZ2UuJGV2YWwoXG4gICAgICAnI2NvbnRhaW5lcicsXG4gICAgICAoZWxlbWVudCwgZXJyb3JNZXNzYWdlKSA9PiB7XG4gICAgICAgIGlmICh3aW5kb3cuX2Rpc3BsYXlFcnJvcnMpIHtcbiAgICAgICAgICBlbGVtZW50LmlubmVySFRNTCA9IGVycm9yTWVzc2FnZTtcbiAgICAgICAgfVxuICAgICAgfSxcbiAgICAgIGA8aDE+Q2hhcnQgaW5wdXQgZGF0YSBlcnJvcjogPC9oMT4ke2Vycm9yLnRvU3RyaW5nKCl9YFxuICAgICk7XG4gIH0pO1xufVxuXG5leHBvcnQgZGVmYXVsdCB7XG4gIGdldCxcbiAgZ2V0R2VuZXJhdGlvbixcbiAgaXNDb25uZWN0ZWQsXG4gIGNyZWF0ZSxcbiAgY2xvc2UsXG4gIG5ld1BhZ2UsXG4gIGNsZWFyUGFnZSxcbiAgYWRkUGFnZVJlc291cmNlcyxcbiAgY2xlYXJQYWdlUmVzb3VyY2VzXG59O1xuIiwiLyoqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKipcblxuSGlnaGNoYXJ0cyBFeHBvcnQgU2VydmVyXG5cbkNvcHlyaWdodCAoYykgMjAxNi0yMDI0LCBIaWdoc29mdFxuXG5MaWNlbmNlZCB1bmRlciB0aGUgTUlUIGxpY2VuY2UuXG5cbkFkZGl0aW9uYWxseSBhIHZhbGlkIEhpZ2hjaGFydHMgbGljZW5zZSBpcyByZXF1aXJlZCBmb3IgdXNlLlxuXG5TZWUgTElDRU5TRSBmaWxlIGluIHJvb3QgZm9yIGRldGFpbHMuXG5cbioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKiovXG5cbmltcG9ydCB7IGFkZFBhZ2VSZXNvdXJjZXMsIGNsZWFyUGFnZVJlc291cmNlcyB9IGZyb20gJy4vYnJvd3Nlci5qcyc7XG5pbXBvcnQgeyBnZXRDYWNoZSB9IGZyb20gJy4vY2FjaGUuanMnO1xuaW1wb3J0IHsgdHJpZ2dlckV4cG9ydCB9IGZyb20gJy4vaGlnaGNoYXJ0cy5qcyc7XG5pbXBvcnQgeyBsb2cgfSBmcm9tICcuL2xvZ2dlci5qcyc7XG5cbmltcG9ydCBzdmdUZW1wbGF0ZSBmcm9tICcuLy4uL3RlbXBsYXRlcy9zdmdfZXhwb3J0L3N2Z19leHBvcnQuanMnO1xuXG5pbXBvcnQgRXhwb3J0RXJyb3IgZnJvbSAnLi9lcnJvcnMvRXhwb3J0RXJyb3IuanMnO1xuXG4vKipcbiAqIFJldHJpZXZlcyB0aGUgY2xpcHBpbmcgcmVnaW9uIGNvb3JkaW5hdGVzIG9mIHRoZSBzcGVjaWZpZWQgcGFnZSBlbGVtZW50IHdpdGhcbiAqIHRoZSBpZCAnY2hhcnQtY29udGFpbmVyJy5cbiAqXG4gKiBAcGFyYW0ge09iamVjdH0gcGFnZSAtIFB1cHBldGVlciBwYWdlIG9iamVjdC5cbiAqXG4gKiBAcmV0dXJucyB7UHJvbWlzZTxPYmplY3Q+fSBQcm9taXNlIHJlc29sdmluZyB0byBhbiBvYmplY3QgY29udGFpbmluZ1xuICogeCwgeSwgd2lkdGgsIGFuZCBoZWlnaHQgcHJvcGVydGllcy5cbiAqL1xuY29uc3QgZ2V0Q2xpcFJlZ2lvbiA9IChwYWdlKSA9PlxuICBwYWdlLiRldmFsKCcjY2hhcnQtY29udGFpbmVyJywgKGVsZW1lbnQpID0+IHtcbiAgICBjb25zdCB7IHgsIHksIHdpZHRoLCBoZWlnaHQgfSA9IGVsZW1lbnQuZ2V0Qm91bmRpbmdDbGllbnRSZWN0KCk7XG4gICAgcmV0dXJuIHtcbiAgICAgIHgsXG4gICAgICB5LFxuICAgICAgd2lkdGgsXG4gICAgICBoZWlnaHQ6IE1hdGgudHJ1bmMoaGVpZ2h0ID4gMSA/IGhlaWdodCA6IDUwMClcbiAgICB9O1xuICB9KTtcblxuLyoqXG4gKiBDcmVhdGVzIGFuIGltYWdlIHVzaW5nIFB1cHBldGVlcidzIHBhZ2Ugc2NyZWVuc2hvdCBmdW5jdGlvbmFsaXR5IHdpdGhcbiAqIHNwZWNpZmllZCBvcHRpb25zLlxuICpcbiAqIEBwYXJhbSB7T2JqZWN0fSBwYWdlIC0gUHVwcGV0ZWVyIHBhZ2Ugb2JqZWN0LlxuICogQHBhcmFtIHtzdHJpbmd9IHR5cGUgLSBJbWFnZSB0eXBlLlxuICogQHBhcmFtIHtzdHJpbmd9IGVuY29kaW5nIC0gSW1hZ2UgZW5jb2RpbmcuXG4gKiBAcGFyYW0ge09iamVjdH0gY2xpcCAtIENsaXBwaW5nIHJlZ2lvbiBjb29yZGluYXRlcy5cbiAqIEBwYXJhbSB7bnVtYmVyfSByYXN0ZXJpemF0aW9uVGltZW91dCAtIFRpbWVvdXQgZm9yIHJhc3Rlcml6YXRpb25cbiAqIGluIG1pbGxpc2Vjb25kcy5cbiAqXG4gKiBAcmV0dXJucyB7UHJvbWlzZTxCdWZmZXI+fSBQcm9taXNlIHJlc29sdmluZyB0byB0aGUgaW1hZ2UgYnVmZmVyIG9yIHJlamVjdGluZ1xuICogd2l0aCBhbiBFeHBvcnRFcnJvciBmb3IgdGltZW91dC5cbiAqL1xuY29uc3QgY3JlYXRlSW1hZ2UgPSBhc3luYyAoXG4gIHBhZ2UsXG4gIHR5cGUsXG4gIGVuY29kaW5nLFxuICBjbGlwLFxuICByYXN0ZXJpemF0aW9uVGltZW91dFxuKSA9PiB7XG4gIGxldCB0aW1lcjtcblxuICB0cnkge1xuICAgIHJldHVybiBhd2FpdCBQcm9taXNlLnJhY2UoW1xuICAgICAgcGFnZS5zY3JlZW5zaG90KHtcbiAgICAgICAgdHlwZSxcbiAgICAgICAgZW5jb2RpbmcsXG4gICAgICAgIGNsaXAsXG4gICAgICAgIGNhcHR1cmVCZXlvbmRWaWV3cG9ydDogdHJ1ZSxcbiAgICAgICAgZnVsbFBhZ2U6IGZhbHNlLFxuICAgICAgICBvcHRpbWl6ZUZvclNwZWVkOiB0cnVlLFxuICAgICAgICAuLi4odHlwZSAhPT0gJ3BuZycgPyB7IHF1YWxpdHk6IDgwIH0gOiB7fSksXG5cbiAgICAgICAgLy8gIzQ0NywgIzQ2MyAtIGFsd2F5cyByZW5kZXIgb24gYSB0cmFuc3BhcmVudCBwYWdlIGlmIHRoZSBleHBlY3RlZCB0eXBlXG4gICAgICAgIC8vIGZvcm1hdCBpcyBQTkdcbiAgICAgICAgb21pdEJhY2tncm91bmQ6IHR5cGUgPT0gJ3BuZydcbiAgICAgIH0pLFxuICAgICAgbmV3IFByb21pc2UoKF9yZXNvbHZlLCByZWplY3QpID0+IHtcbiAgICAgICAgdGltZXIgPSBzZXRUaW1lb3V0KFxuICAgICAgICAgICgpID0+IHJlamVjdChuZXcgRXhwb3J0RXJyb3IoJ1Jhc3Rlcml6YXRpb24gdGltZW91dCcpKSxcbiAgICAgICAgICByYXN0ZXJpemF0aW9uVGltZW91dCB8fCAxNTAwXG4gICAgICAgICk7XG4gICAgICB9KVxuICAgIF0pO1xuICB9IGZpbmFsbHkge1xuICAgIC8vIFRoZSB0aW1lciBvdXRsaXZlcyB0aGUgcmFjZSBvdGhlcndpc2UsIGtlZXBpbmcgaXRzZWxmIGFuZCBldmVyeXRoaW5nIGl0c1xuICAgIC8vIGNhbGxiYWNrIGNsb3NlcyBvdmVyIGFsaXZlIGZvciB0aGUgcmVzdCBvZiB0aGUgdGltZW91dCBvbiBldmVyeSBzdWNjZXNzZnVsXG4gICAgLy8gZXhwb3J0XG4gICAgY2xlYXJUaW1lb3V0KHRpbWVyKTtcbiAgfVxufTtcblxuLyoqXG4gKiBDcmVhdGVzIGEgUERGIHVzaW5nIFB1cHBldGVlcidzIHBhZ2UgcGRmIGZ1bmN0aW9uYWxpdHkgd2l0aCBzcGVjaWZpZWRcbiAqIG9wdGlvbnMuXG4gKlxuICogQHBhcmFtIHtPYmplY3R9IHBhZ2UgLSBQdXBwZXRlZXIgcGFnZSBvYmplY3QuXG4gKiBAcGFyYW0ge251bWJlcn0gaGVpZ2h0IC0gUERGIGhlaWdodC5cbiAqIEBwYXJhbSB7bnVtYmVyfSB3aWR0aCAtIFBERiB3aWR0aC5cbiAqIEBwYXJhbSB7c3RyaW5nfSBlbmNvZGluZyAtIFBERiBlbmNvZGluZy5cbiAqXG4gKiBAcmV0dXJucyB7UHJvbWlzZTxCdWZmZXI+fSBQcm9taXNlIHJlc29sdmluZyB0byB0aGUgUERGIGJ1ZmZlci5cbiAqL1xuY29uc3QgY3JlYXRlUERGID0gYXN5bmMgKFxuICBwYWdlLFxuICBoZWlnaHQsXG4gIHdpZHRoLFxuICBlbmNvZGluZyxcbiAgcmFzdGVyaXphdGlvblRpbWVvdXRcbikgPT4ge1xuICBhd2FpdCBwYWdlLmVtdWxhdGVNZWRpYVR5cGUoJ3NjcmVlbicpO1xuXG4gIHJldHVybiBwYWdlLnBkZih7XG4gICAgLy8gVGhpcyB3aWxsIHJlbW92ZSBhbiBleHRyYSBlbXB0eSBwYWdlIGluIFBERiBleHBvcnRzXG4gICAgaGVpZ2h0OiBoZWlnaHQgKyAxLFxuICAgIHdpZHRoLFxuICAgIGVuY29kaW5nLFxuICAgIHRpbWVvdXQ6IHJhc3Rlcml6YXRpb25UaW1lb3V0IHx8IDE1MDBcbiAgfSk7XG59O1xuXG4vKipcbiAqIENyZWF0ZXMgYW4gU1ZHIHN0cmluZyBieSBldmFsdWF0aW5nIHRoZSBvdXRlckhUTUwgb2YgdGhlIGZpcnN0ICdzdmcnIGVsZW1lbnRcbiAqIGluc2lkZSBhbiBlbGVtZW50IHdpdGggdGhlIGlkICdjb250YWluZXInLlxuICpcbiAqIEBwYXJhbSB7T2JqZWN0fSBwYWdlIC0gUHVwcGV0ZWVyIHBhZ2Ugb2JqZWN0LlxuICpcbiAqIEByZXR1cm5zIHtQcm9taXNlPHN0cmluZz59IFByb21pc2UgcmVzb2x2aW5nIHRvIHRoZSBTVkcgc3RyaW5nLlxuICovXG5jb25zdCBjcmVhdGVTVkcgPSAocGFnZSkgPT5cbiAgcGFnZS4kZXZhbCgnI2NvbnRhaW5lciBzdmc6Zmlyc3Qtb2YtdHlwZScsIChlbGVtZW50KSA9PiBlbGVtZW50Lm91dGVySFRNTCk7XG5cbi8qKlxuICogU2V0cyB0aGUgc3BlY2lmaWVkIGNoYXJ0IGFuZCBvcHRpb25zIGFzIGNvbmZpZ3VyYXRpb24gaW50byB0aGUgdHJpZ2dlckV4cG9ydFxuICogZnVuY3Rpb24gd2l0aGluIHRoZSB3aW5kb3cgY29udGV4dCB1c2luZyBwYWdlLmV2YWx1YXRlLlxuICpcbiAqIEBwYXJhbSB7T2JqZWN0fSBwYWdlIC0gUHVwcGV0ZWVyIHBhZ2Ugb2JqZWN0LlxuICogQHBhcmFtIHthbnl9IGNoYXJ0IC0gVGhlIGNoYXJ0IG9iamVjdCB0byBiZSBjb25maWd1cmVkLlxuICogQHBhcmFtIHtPYmplY3R9IG9wdGlvbnMgLSBDb25maWd1cmF0aW9uIG9wdGlvbnMgZm9yIHRoZSBjaGFydC5cbiAqXG4gKiBAcmV0dXJucyB7UHJvbWlzZTx2b2lkPn0gUHJvbWlzZSByZXNvbHZpbmcgYWZ0ZXIgdGhlIGNvbmZpZ3VyYXRpb24gaXMgc2V0LlxuICovXG5jb25zdCBzZXRBc0NvbmZpZyA9IGFzeW5jIChwYWdlLCBjaGFydCwgb3B0aW9ucywgZGlzcGxheUVycm9ycykgPT4ge1xuICAvLyBHZXQgcmlkIG9mIHRoZSByZWR1bmFudCBzdHJpbmcgZGF0YVxuICBvcHRpb25zLmV4cG9ydC5pbnN0ciA9IG51bGw7XG4gIG9wdGlvbnMuZXhwb3J0LmluZmlsZSA9IG51bGw7XG5cbiAgLy8gR2V0IHRoZSBzaXplIG9mIHRoZSBleHBvcnQgaW5wdXRcbiAgY29uc3QgdG90YWxTaXplID0gQnVmZmVyLmJ5dGVMZW5ndGgoXG4gICAgb3B0aW9ucy5leHBvcnQ/LnN0ckluaiA/IG9wdGlvbnMuZXhwb3J0Py5zdHJJbmogOiBKU09OLnN0cmluZ2lmeShjaGFydCksXG4gICAgJ3V0Zi04J1xuICApO1xuXG4gIC8vIExvZyB0aGUgc2l6ZSBpbiBNQlxuICBsb2coXG4gICAgNCxcbiAgICBgW2V4cG9ydF0gVGhlIGN1cnJlbnQgdG90YWwgc2l6ZSBvZiBkYXRhIHBhc3NlZCB0byBhIHBhZ2UgaXMgYXJvdW5kICR7KFxuICAgICAgdG90YWxTaXplIC9cbiAgICAgICgxMDI0ICogMTAyNClcbiAgICApLnRvRml4ZWQoMil9IE1CYFxuICApO1xuXG4gIC8vIENoZWNrIHRoZSBzaXplIG9mIGRhdGEgcGFzc2VkIHRvIHRoZSBwYWdlXG4gIGlmICh0b3RhbFNpemUgPj0gMTAwICogMTAyNCAqIDEwMjQpIHtcbiAgICB0aHJvdyBuZXcgRXhwb3J0RXJyb3IoYFtleHBvcnRdIFRoZSBkYXRhIHBhc3NlZCB0byBhIHBhZ2UgZXhjZWVkZWQgMTAwTUIuYCk7XG4gIH1cblxuICAvLyBUcmlnZ2VyIHRoZSBIaWdoY2hhcnRzIGNoYXJ0IGNyZWF0aW9uXG4gIHJldHVybiBwYWdlLmV2YWx1YXRlKHRyaWdnZXJFeHBvcnQsIGNoYXJ0LCBvcHRpb25zLCBkaXNwbGF5RXJyb3JzKTtcbn07XG5cbi8qKlxuICogRXhwb3J0cyB0byBhIGNoYXJ0IGZyb20gYSBwYWdlIHVzaW5nIFB1cHBldGVlci5cbiAqXG4gKiBAcGFyYW0ge09iamVjdH0gcGFnZSAtIFB1cHBldGVlciBwYWdlIG9iamVjdC5cbiAqIEBwYXJhbSB7YW55fSBjaGFydCAtIFRoZSBjaGFydCBvYmplY3Qgb3IgU1ZHIGNvbmZpZ3VyYXRpb24gdG8gYmUgZXhwb3J0ZWQuXG4gKiBAcGFyYW0ge09iamVjdH0gb3B0aW9ucyAtIEV4cG9ydCBvcHRpb25zIGFuZCBjb25maWd1cmF0aW9uLlxuICpcbiAqIEByZXR1cm5zIHtQcm9taXNlPHN0cmluZyB8IEJ1ZmZlciB8IEV4cG9ydEVycm9yPn0gUHJvbWlzZSByZXNvbHZpbmcgdG9cbiAqIHRoZSBleHBvcnRlZCBkYXRhIG9yIHJlamVjdGluZyB3aXRoIGFuIEV4cG9ydEVycm9yLlxuICovXG5leHBvcnQgZGVmYXVsdCBhc3luYyAocGFnZSwgY2hhcnQsIG9wdGlvbnMpID0+IHtcbiAgLy8gSW5qZWN0ZWQgcmVzb3VyY2VzIGFycmF5IChhZGRpdGlvbmFsIEpTIGFuZCBDU1MpXG4gIGxldCBpbmplY3RlZFJlc291cmNlcyA9IFtdO1xuXG4gIHRyeSB7XG4gICAgbG9nKDQsICdbZXhwb3J0XSBEZXRlcm1pbmluZyBleHBvcnQgcGF0aC4nKTtcblxuICAgIGNvbnN0IGV4cG9ydE9wdGlvbnMgPSBvcHRpb25zLmV4cG9ydDtcblxuICAgIC8vIERlY2lkZSB3aGV0aGVyIGRpc3BsYXkgZXJyb3Igb3IgZGViYnVnZXIgd3JhcHBlciBhcm91bmQgaXRcbiAgICBjb25zdCBkaXNwbGF5RXJyb3JzID1cbiAgICAgIGV4cG9ydE9wdGlvbnM/Lm9wdGlvbnM/LmNoYXJ0Py5kaXNwbGF5RXJyb3JzICYmXG4gICAgICBnZXRDYWNoZSgpLmFjdGl2ZU1hbmlmZXN0Lm1vZHVsZXMuZGVidWdnZXI7XG5cbiAgICBsZXQgaXNTVkc7XG4gICAgaWYgKFxuICAgICAgY2hhcnQuaW5kZXhPZiAmJlxuICAgICAgKGNoYXJ0LmluZGV4T2YoJzxzdmcnKSA+PSAwIHx8IGNoYXJ0LmluZGV4T2YoJzw/eG1sJykgPj0gMClcbiAgICApIHtcbiAgICAgIC8vIFNWRyBpbnB1dCBoYW5kbGluZ1xuICAgICAgbG9nKDQsICdbZXhwb3J0XSBUcmVhdGluZyBhcyBTVkcuJyk7XG5cbiAgICAgIC8vIElmIGlucHV0IGlzIGFsc28gU1ZHLCBqdXN0IHJldHVybiBpdFxuICAgICAgaWYgKGV4cG9ydE9wdGlvbnMudHlwZSA9PT0gJ3N2ZycpIHtcbiAgICAgICAgcmV0dXJuIGNoYXJ0O1xuICAgICAgfVxuXG4gICAgICBpc1NWRyA9IHRydWU7XG4gICAgICBhd2FpdCBwYWdlLnNldENvbnRlbnQoc3ZnVGVtcGxhdGUoY2hhcnQpLCB7XG4gICAgICAgIHdhaXRVbnRpbDogJ2RvbWNvbnRlbnRsb2FkZWQnXG4gICAgICB9KTtcbiAgICB9IGVsc2Uge1xuICAgICAgLy8gSlNPTiBjb25maWcgaGFuZGxpbmdcbiAgICAgIGxvZyg0LCAnW2V4cG9ydF0gVHJlYXRpbmcgYXMgY29uZmlnLicpO1xuXG4gICAgICAvLyBOZWVkIHRvIHBlcmZvcm0gc3RyYWlnaHQgaW5qZWN0XG4gICAgICBpZiAoZXhwb3J0T3B0aW9ucy5zdHJJbmopIHtcbiAgICAgICAgLy8gSW5qZWN0aW9uIGJhc2VkIGNvbmZpZ3VyYXRpb24gZXhwb3J0XG4gICAgICAgIGF3YWl0IHNldEFzQ29uZmlnKFxuICAgICAgICAgIHBhZ2UsXG4gICAgICAgICAge1xuICAgICAgICAgICAgY2hhcnQ6IHtcbiAgICAgICAgICAgICAgaGVpZ2h0OiBleHBvcnRPcHRpb25zLmhlaWdodCxcbiAgICAgICAgICAgICAgd2lkdGg6IGV4cG9ydE9wdGlvbnMud2lkdGhcbiAgICAgICAgICAgIH1cbiAgICAgICAgICB9LFxuICAgICAgICAgIG9wdGlvbnMsXG4gICAgICAgICAgZGlzcGxheUVycm9yc1xuICAgICAgICApO1xuICAgICAgfSBlbHNlIHtcbiAgICAgICAgLy8gQmFzaWMgY29uZmlndXJhdGlvbiBleHBvcnRcbiAgICAgICAgY2hhcnQuY2hhcnQuaGVpZ2h0ID0gZXhwb3J0T3B0aW9ucy5oZWlnaHQ7XG4gICAgICAgIGNoYXJ0LmNoYXJ0LndpZHRoID0gZXhwb3J0T3B0aW9ucy53aWR0aDtcblxuICAgICAgICBhd2FpdCBzZXRBc0NvbmZpZyhwYWdlLCBjaGFydCwgb3B0aW9ucywgZGlzcGxheUVycm9ycyk7XG4gICAgICB9XG4gICAgfVxuXG4gICAgLy8gS2VlcHMgdHJhY2sgb2YgYWxsIHJlc291cmNlcyBhZGRlZCBvbiB0aGUgcGFnZSB3aXRoIGFkZFhYWFRhZy4gZXRjXG4gICAgLy8gSXQncyBWSVRBTCB0aGF0IGFsbCBhZGRlZCByZXNvdXJjZXMgZW5kcyB1cCBoZXJlIHNvIHdlIGNhbiBjbGVhciB0aGluZ3NcbiAgICAvLyBvdXQgd2hlbiBkb2luZyBhIG5ldyBleHBvcnQgaW4gdGhlIHNhbWUgcGFnZSFcbiAgICBpbmplY3RlZFJlc291cmNlcyA9IGF3YWl0IGFkZFBhZ2VSZXNvdXJjZXMocGFnZSwgb3B0aW9ucyk7XG5cbiAgICAvLyBHZXQgdGhlIHJlYWwgY2hhcnQgc2l6ZSBhbmQgc2V0IHRoZSB6b29tIGFjY29yZGluZ2x5XG4gICAgY29uc3Qgc2l6ZSA9IGlzU1ZHXG4gICAgICA/IGF3YWl0IHBhZ2UuZXZhbHVhdGUoKHNjYWxlKSA9PiB7XG4gICAgICAgICAgY29uc3Qgc3ZnRWxlbWVudCA9IGRvY3VtZW50LnF1ZXJ5U2VsZWN0b3IoXG4gICAgICAgICAgICAnI2NoYXJ0LWNvbnRhaW5lciBzdmc6Zmlyc3Qtb2YtdHlwZSdcbiAgICAgICAgICApO1xuXG4gICAgICAgICAgLy8gR2V0IHRoZSB2YWx1ZXMgY29ycmVjdGx5IHNjYWxlZFxuICAgICAgICAgIGNvbnN0IGNoYXJ0SGVpZ2h0ID0gc3ZnRWxlbWVudC5oZWlnaHQuYmFzZVZhbC52YWx1ZSAqIHNjYWxlO1xuICAgICAgICAgIGNvbnN0IGNoYXJ0V2lkdGggPSBzdmdFbGVtZW50LndpZHRoLmJhc2VWYWwudmFsdWUgKiBzY2FsZTtcblxuICAgICAgICAgIC8vIEluIGNhc2Ugb2YgU1ZHIHRoZSB6b29tIG11c3QgYmUgc2V0IGRpcmVjdGx5IGZvciBib2R5XG4gICAgICAgICAgLy8gU2V0IHRoZSB6b29tIGFzIHNjYWxlXG4gICAgICAgICAgZG9jdW1lbnQuYm9keS5zdHlsZS56b29tID0gc2NhbGU7XG5cbiAgICAgICAgICAvLyBTZXQgdGhlIG1hcmdpbiB0byAwcHhcbiAgICAgICAgICBkb2N1bWVudC5ib2R5LnN0eWxlLm1hcmdpbiA9ICcwcHgnO1xuXG4gICAgICAgICAgcmV0dXJuIHtcbiAgICAgICAgICAgIGNoYXJ0SGVpZ2h0LFxuICAgICAgICAgICAgY2hhcnRXaWR0aFxuICAgICAgICAgIH07XG4gICAgICAgIH0sIHBhcnNlRmxvYXQoZXhwb3J0T3B0aW9ucy5zY2FsZSkpXG4gICAgICA6IGF3YWl0IHBhZ2UuZXZhbHVhdGUoKCkgPT4ge1xuICAgICAgICAgIGNvbnN0IHsgY2hhcnRIZWlnaHQsIGNoYXJ0V2lkdGggfSA9IHdpbmRvdy5IaWdoY2hhcnRzLmNoYXJ0c1swXTtcblxuICAgICAgICAgIC8vIE5vIG5lZWQgZm9yIHN1Y2ggc2NhbGUgbWFuaXB1bGF0aW9uIGluIGNhc2Ugb2Ygb3RoZXIgdHlwZXMgb2YgZXhwb3J0c1xuICAgICAgICAgIC8vIFJlc2V0IHRoZSB6b29tIGZvciBvdGhlciBleHBvcnRzIHRoYW4gdG8gU1ZHc1xuICAgICAgICAgIGRvY3VtZW50LmJvZHkuc3R5bGUuem9vbSA9IDE7XG5cbiAgICAgICAgICByZXR1cm4ge1xuICAgICAgICAgICAgY2hhcnRIZWlnaHQsXG4gICAgICAgICAgICBjaGFydFdpZHRoXG4gICAgICAgICAgfTtcbiAgICAgICAgfSk7XG5cbiAgICAvLyBTZXQgZmluYWwgaGVpZ2h0IGFuZCB3aWR0aCBmb3Igdmlld3BvcnRcbiAgICBjb25zdCB2aWV3cG9ydEhlaWdodCA9IE1hdGguYWJzKFxuICAgICAgTWF0aC5jZWlsKHNpemUuY2hhcnRIZWlnaHQgfHwgZXhwb3J0T3B0aW9ucy5oZWlnaHQpXG4gICAgKTtcbiAgICBjb25zdCB2aWV3cG9ydFdpZHRoID0gTWF0aC5hYnMoXG4gICAgICBNYXRoLmNlaWwoc2l6ZS5jaGFydFdpZHRoIHx8IGV4cG9ydE9wdGlvbnMud2lkdGgpXG4gICAgKTtcblxuICAgIC8vIEdldCB0aGUgY2xpcCByZWdpb24gZm9yIHRoZSBwYWdlXG4gICAgY29uc3QgeyB4LCB5IH0gPSBhd2FpdCBnZXRDbGlwUmVnaW9uKHBhZ2UpO1xuXG4gICAgLy8gU2V0IHRoZSBmaW5hbCB2aWV3cG9ydCBub3cgdGhhdCB3ZSBoYXZlIHRoZSByZWFsIGhlaWdodFxuICAgIGF3YWl0IHBhZ2Uuc2V0Vmlld3BvcnQoe1xuICAgICAgaGVpZ2h0OiB2aWV3cG9ydEhlaWdodCxcbiAgICAgIHdpZHRoOiB2aWV3cG9ydFdpZHRoLFxuICAgICAgZGV2aWNlU2NhbGVGYWN0b3I6IGlzU1ZHID8gMSA6IHBhcnNlRmxvYXQoZXhwb3J0T3B0aW9ucy5zY2FsZSlcbiAgICB9KTtcblxuICAgIGxldCBkYXRhO1xuICAgIC8vIFJhc3Rlcml6YXRpb24gcHJvY2Vzc1xuICAgIGlmIChleHBvcnRPcHRpb25zLnR5cGUgPT09ICdzdmcnKSB7XG4gICAgICAvLyBTVkdcbiAgICAgIGRhdGEgPSBhd2FpdCBjcmVhdGVTVkcocGFnZSk7XG4gICAgfSBlbHNlIGlmIChbJ3BuZycsICdqcGVnJ10uaW5jbHVkZXMoZXhwb3J0T3B0aW9ucy50eXBlKSkge1xuICAgICAgLy8gUE5HIG9yIEpQRUdcbiAgICAgIGRhdGEgPSBhd2FpdCBjcmVhdGVJbWFnZShcbiAgICAgICAgcGFnZSxcbiAgICAgICAgZXhwb3J0T3B0aW9ucy50eXBlLFxuICAgICAgICAnYmFzZTY0JyxcbiAgICAgICAge1xuICAgICAgICAgIHdpZHRoOiB2aWV3cG9ydFdpZHRoLFxuICAgICAgICAgIGhlaWdodDogdmlld3BvcnRIZWlnaHQsXG4gICAgICAgICAgeCxcbiAgICAgICAgICB5XG4gICAgICAgIH0sXG4gICAgICAgIGV4cG9ydE9wdGlvbnMucmFzdGVyaXphdGlvblRpbWVvdXRcbiAgICAgICk7XG4gICAgfSBlbHNlIGlmIChleHBvcnRPcHRpb25zLnR5cGUgPT09ICdwZGYnKSB7XG4gICAgICAvLyBQREZcbiAgICAgIGRhdGEgPSBhd2FpdCBjcmVhdGVQREYoXG4gICAgICAgIHBhZ2UsXG4gICAgICAgIHZpZXdwb3J0SGVpZ2h0LFxuICAgICAgICB2aWV3cG9ydFdpZHRoLFxuICAgICAgICAnYmFzZTY0JyxcbiAgICAgICAgZXhwb3J0T3B0aW9ucy5yYXN0ZXJpemF0aW9uVGltZW91dFxuICAgICAgKTtcbiAgICB9IGVsc2Uge1xuICAgICAgdGhyb3cgbmV3IEV4cG9ydEVycm9yKFxuICAgICAgICBgW2V4cG9ydF0gVW5zdXBwb3J0ZWQgb3V0cHV0IGZvcm1hdCAke2V4cG9ydE9wdGlvbnMudHlwZX0uYFxuICAgICAgKTtcbiAgICB9XG5cbiAgICAvLyBDbGVhciBwcmV2aW91c2x5IGluamVjdGVkIEpTIGFuZCBDU1MgcmVzb3VyY2VzXG4gICAgYXdhaXQgY2xlYXJQYWdlUmVzb3VyY2VzKHBhZ2UsIGluamVjdGVkUmVzb3VyY2VzKTtcbiAgICByZXR1cm4gZGF0YTtcbiAgfSBjYXRjaCAoZXJyb3IpIHtcbiAgICBhd2FpdCBjbGVhclBhZ2VSZXNvdXJjZXMocGFnZSwgaW5qZWN0ZWRSZXNvdXJjZXMpO1xuICAgIHJldHVybiBlcnJvcjtcbiAgfVxufTtcbiIsIi8qKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqXG5cbkhpZ2hjaGFydHMgRXhwb3J0IFNlcnZlclxuXG5Db3B5cmlnaHQgKGMpIDIwMTYtMjAyNCwgSGlnaHNvZnRcblxuTGljZW5jZWQgdW5kZXIgdGhlIE1JVCBsaWNlbmNlLlxuXG5BZGRpdGlvbmFsbHkgYSB2YWxpZCBIaWdoY2hhcnRzIGxpY2Vuc2UgaXMgcmVxdWlyZWQgZm9yIHVzZS5cblxuU2VlIExJQ0VOU0UgZmlsZSBpbiByb290IGZvciBkZXRhaWxzLlxuXG4qKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqL1xuXG5pbXBvcnQgY3NzVGVtcGxhdGUgZnJvbSAnLi9jc3MuanMnO1xuXG5leHBvcnQgZGVmYXVsdCAoY2hhcnQpID0+IGBcbjwhRE9DVFlQRSBodG1sPlxuPGh0bWwgbGFuZz0nZW4tVVMnPlxuICA8aGVhZD5cbiAgICA8bWV0YSBodHRwLWVxdWl2PVwiQ29udGVudC1UeXBlXCIgY29udGVudD1cInRleHQvaHRtbDsgY2hhcnNldD11dGYtOFwiPlxuICAgIDx0aXRsZT5IaWdoY2hhcnRzIEV4cG9ydDwvdGl0bGU+XG4gIDwvaGVhZD5cbiAgPHN0eWxlPlxuICAgICR7Y3NzVGVtcGxhdGUoKX1cbiAgPC9zdHlsZT5cbiAgPGJvZHk+XG4gICAgPGRpdiBpZD1cImNoYXJ0LWNvbnRhaW5lclwiPlxuICAgICAgJHtjaGFydH1cbiAgICA8L2Rpdj5cbiAgPC9ib2R5PlxuPC9odG1sPlxuXG5gO1xuIiwiLyoqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKipcblxuSGlnaGNoYXJ0cyBFeHBvcnQgU2VydmVyXG5cbkNvcHlyaWdodCAoYykgMjAxNi0yMDI0LCBIaWdoc29mdFxuXG5MaWNlbmNlZCB1bmRlciB0aGUgTUlUIGxpY2VuY2UuXG5cbkFkZGl0aW9uYWxseSBhIHZhbGlkIEhpZ2hjaGFydHMgbGljZW5zZSBpcyByZXF1aXJlZCBmb3IgdXNlLlxuXG5TZWUgTElDRU5TRSBmaWxlIGluIHJvb3QgZm9yIGRldGFpbHMuXG5cbioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKiovXG5cbi8qKlxuICogTWFjaGluZSByZWFkYWJsZSBlcnJvciBjb2RlcywgcmV0dXJuZWQgYXMgdGhlIGBlcnJvckNvZGVgIHByb3BlcnR5IG9mIGFuXG4gKiBlcnJvciByZXNwb25zZS5cbiAqXG4gKiBUaGVzZSBleGlzdCBiZWNhdXNlIHRoZSBIVFRQIHN0YXR1cyBjb2RlIGNhbm5vdCBjYXJyeSB0aGUgZGlzdGluY3Rpb24gdGhhdFxuICogbWF0dGVycyBtb3N0IGluIHByb2R1Y3Rpb246IGEgcmVxdWVzdCByZWZ1c2VkIGJlY2F1c2UgdGhlIHNlcnZlciB3YXMgYnVzeSBhbmRcbiAqIGEgcmVxdWVzdCByZWZ1c2VkIGJlY2F1c2UgaXQgd2FzIG1hbGZvcm1lZCBhcmUgYm90aCByZXBvcnRlZCBhcyA0MDAuIFdpdGhvdXQgYVxuICogY29kZSwgYSBkYXNoYm9hcmQgY2Fubm90IHNlcGFyYXRlIGEgY2FwYWNpdHkgcHJvYmxlbSBmcm9tIGNhbGxlcnMgc2VuZGluZyBiYWRcbiAqIGRhdGEsIGFuZCBhIGNsaWVudCBjYW5ub3QgdGVsbCB3aGV0aGVyIHJldHJ5aW5nIGlzIHdvcnRod2hpbGUuXG4gKlxuICogVHJlYXQgdGhlc2UgYXMgYSBzdGFibGUgY29udHJhY3QgLSBjYWxsZXJzIG1heSBicmFuY2ggb24gdGhlbS5cbiAqL1xuZXhwb3J0IGNvbnN0IGVycm9yQ29kZXMgPSB7XG4gIC8vIFRoZSByZXF1ZXN0IGl0c2VsZiB3YXMgbm90IHVzYWJsZTogbWlzc2luZyBib2R5LCBubyBjaGFydCBkYXRhLCBvciBjb250ZW50XG4gIC8vIHRoYXQgaXMgbm90IGFsbG93ZWQuIFJldHJ5aW5nIHdpdGhvdXQgY2hhbmdpbmcgdGhlIHJlcXVlc3Qgd2lsbCBub3QgaGVscC5cbiAgSU5WQUxJRF9SRVFVRVNUOiAnRVhQT1JUX0lOVkFMSURfUkVRVUVTVCcsXG5cbiAgLy8gVGhlIHNlcnZlciB3YXMgYWxyZWFkeSBob2xkaW5nIGFzIG1hbnkgcXVldWVkIGV4cG9ydHMgYXMgaXQgaXMgd2lsbGluZyB0byxcbiAgLy8gYW5kIHJlZnVzZWQgdGhpcyBvbmUgd2l0aG91dCBzdGFydGluZyB3b3JrIG9uIGl0LiBSZXRyeWluZyBsYXRlciwgaWRlYWxseVxuICAvLyB3aXRoIGJhY2tvZmYsIGlzIGFwcHJvcHJpYXRlLlxuICBRVUVVRV9GVUxMOiAnRVhQT1JUX1FVRVVFX0ZVTEwnLFxuXG4gIC8vIE5vIHdvcmtlciBiZWNhbWUgYXZhaWxhYmxlIHdpdGhpbiB0aGUgYWNxdWlyZSB0aW1lb3V0LiBTYW1lIG1lYW5pbmcgZm9yIGFcbiAgLy8gY2FsbGVyIGFzIFFVRVVFX0ZVTEwsIGJ1dCByZWFjaGVkIGJ5IHdhaXRpbmcgcmF0aGVyIHRoYW4gYnkgYmVpbmcgcmVmdXNlZFxuICAvLyB1cCBmcm9udC5cbiAgQUNRVUlSRV9USU1FT1VUOiAnRVhQT1JUX0FDUVVJUkVfVElNRU9VVCcsXG5cbiAgLy8gVGhlIGNoYXJ0IHdhcyB0b28gbGFyZ2Ugb3IgY29tcGxleCB0byByZW5kZXIgd2l0aGluIHRoZSBhbGxvdHRlZCB0aW1lLlxuICBSQVNURVJJWkFUSU9OX1RJTUVPVVQ6ICdFWFBPUlRfUkFTVEVSSVpBVElPTl9USU1FT1VUJyxcblxuICAvLyBUaGUgY2xpZW50IGRpc2Nvbm5lY3RlZCBiZWZvcmUgdGhlIGV4cG9ydCBjb3VsZCBiZSBzZXJ2ZWQsIHNvIHRoZSB3b3JrIHdhc1xuICAvLyBkaXNjYXJkZWQuIE5ldmVyIHJlYWNoZXMgYSBjYWxsZXIgYnkgZGVmaW5pdGlvbiAtIGl0IGV4aXN0cyBzbyB0aGF0XG4gIC8vIGFiYW5kb25lZCB3b3JrIGlzIGRpc3Rpbmd1aXNoYWJsZSBpbiBsb2dzIGFuZCBjb3VudGVycyBmcm9tIHdvcmsgdGhhdFxuICAvLyBnZW51aW5lbHkgZmFpbGVkLlxuICBDTElFTlRfR09ORTogJ0VYUE9SVF9DTElFTlRfR09ORScsXG5cbiAgLy8gVGhlIGV4cG9ydCBmYWlsZWQgZm9yIGEgcmVhc29uIHRoYXQgaXMgbm90IG9uZSBvZiB0aGUgYWJvdmUuXG4gIEVYUE9SVF9GQUlMRUQ6ICdFWFBPUlRfRkFJTEVEJ1xufTtcblxuZXhwb3J0IGRlZmF1bHQgZXJyb3JDb2RlcztcbiIsIi8qKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqXG5cbkhpZ2hjaGFydHMgRXhwb3J0IFNlcnZlclxuXG5Db3B5cmlnaHQgKGMpIDIwMTYtMjAyNCwgSGlnaHNvZnRcblxuTGljZW5jZWQgdW5kZXIgdGhlIE1JVCBsaWNlbmNlLlxuXG5BZGRpdGlvbmFsbHkgYSB2YWxpZCBIaWdoY2hhcnRzIGxpY2Vuc2UgaXMgcmVxdWlyZWQgZm9yIHVzZS5cblxuU2VlIExJQ0VOU0UgZmlsZSBpbiByb290IGZvciBkZXRhaWxzLlxuXG4qKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqL1xuXG5pbXBvcnQgeyBQb29sIH0gZnJvbSAndGFybic7XG5pbXBvcnQgeyB2NCBhcyB1dWlkIH0gZnJvbSAndXVpZCc7XG5cbmltcG9ydCB7XG4gIGNyZWF0ZSBhcyBjcmVhdGVCcm93c2VyLFxuICBjbG9zZSBhcyBjbG9zZUJyb3dzZXIsXG4gIG5ld1BhZ2UsXG4gIGNsZWFyUGFnZSxcbiAgZ2V0R2VuZXJhdGlvbiBhcyBnZXRCcm93c2VyR2VuZXJhdGlvblxufSBmcm9tICcuL2Jyb3dzZXIuanMnO1xuaW1wb3J0IHB1cHBldGVlckV4cG9ydCBmcm9tICcuL2V4cG9ydC5qcyc7XG5pbXBvcnQgeyBsb2csIGxvZ1dpdGhTdGFjayB9IGZyb20gJy4vbG9nZ2VyLmpzJztcbmltcG9ydCB7IG1lYXN1cmVUaW1lIH0gZnJvbSAnLi91dGlscy5qcyc7XG5cbmltcG9ydCBFeHBvcnRFcnJvciBmcm9tICcuL2Vycm9ycy9FeHBvcnRFcnJvci5qcyc7XG5pbXBvcnQgeyBlcnJvckNvZGVzIH0gZnJvbSAnLi9lcnJvcnMvY29kZXMuanMnO1xuXG4vLyBUaGUgcG9vbCBpbnN0YW5jZVxubGV0IHBvb2wgPSBmYWxzZTtcblxuLy8gUG9vbCBzdGF0aXN0aWNzXG5leHBvcnQgY29uc3Qgc3RhdHMgPSB7XG4gIHBlcmZvcm1lZEV4cG9ydHM6IDAsXG4gIGV4cG9ydEF0dGVtcHRzOiAwLFxuICBleHBvcnRGcm9tU3ZnQXR0ZW1wdHM6IDAsXG4gIHRpbWVTcGVudDogMCxcbiAgZHJvcHBlZEV4cG9ydHM6IDAsXG4gIHNwZW50QXZlcmFnZTogMCxcbiAgcmVqZWN0ZWRGb3JDYXBhY2l0eTogMCxcbiAgYWJhbmRvbmVkRXhwb3J0czogMFxufTtcblxubGV0IHBvb2xDb25maWcgPSB7fTtcblxuLy8gVGhlIHJlc29sdmVkIG1heGltdW0gbnVtYmVyIG9mIGV4cG9ydHMgYWxsb3dlZCB0byB3YWl0IGZvciBhIHdvcmtlclxubGV0IHF1ZXVlTGltaXQgPSAwO1xuXG4vLyBXb3JrZXIgY3JlYXRpb25zIHRoYXQgaGF2ZSBmYWlsZWQgaW4gYSByb3csIHJlc2V0IGJ5IGFueSBzdWNjZXNzLiBVc2VkIHRvIHN0b3Bcbi8vIHRoZSBwb29sIHJldHJ5aW5nIGFnYWluc3QgYSBicm93c2VyIHRoYXQgaXMgbm90IGdvaW5nIHRvIHN0YXJ0IHdvcmtpbmcgYWdhaW4uXG5sZXQgY29uc2VjdXRpdmVDcmVhdGVGYWlsdXJlcyA9IDA7XG5cbi8qKlxuICogUmV0dXJucyB0aGUgbnVtYmVyIG9mIHdvcmtlciBjcmVhdGlvbnMgdGhhdCBoYXZlIGZhaWxlZCBpbiBhIHJvdy5cbiAqXG4gKiBAcmV0dXJucyB7bnVtYmVyfSBUaGUgY3VycmVudCBjb25zZWN1dGl2ZSBmYWlsdXJlIGNvdW50LlxuICovXG5leHBvcnQgY29uc3QgZ2V0Q29uc2VjdXRpdmVDcmVhdGVGYWlsdXJlcyA9ICgpID0+IGNvbnNlY3V0aXZlQ3JlYXRlRmFpbHVyZXM7XG5cbi8qKlxuICogUmVzb2x2ZXMgdGhlIHF1ZXVlIGxpbWl0IGZyb20gdGhlIHBvb2wgY29uZmlndXJhdGlvbi5cbiAqXG4gKiBBIGxpbWl0IG9mIDAgbWVhbnMgZGVyaXZlIGl0IGZyb20gdGhlIHBvb2wgc2l6ZS4gRm91ciB0aW1lcyBtYXhXb3JrZXJzIGtlZXBzXG4gKiB0aGUgd29yc3QgY2FzZSB3YWl0IGF0IHJvdWdobHkgZm91ciBleHBvcnRzJyB3b3J0aCBvZiB0aW1lLCB3aGljaCBpcyBhXG4gKiBtZWFuaW5nZnVsIGJvdW5kLCB3aGlsZSBsZWF2aW5nIGVub3VnaCBzbGFjayB0byBhYnNvcmIgbm9ybWFsIGJ1cnN0cy5cbiAqXG4gKiBAcGFyYW0ge09iamVjdH0gY29uZmlnIC0gVGhlIHBvb2wgc2VjdGlvbiBvZiB0aGUgY29uZmlndXJhdGlvbi5cbiAqXG4gKiBAcmV0dXJucyB7bnVtYmVyfSBUaGUgcmVzb2x2ZWQgcXVldWUgbGltaXQuXG4gKi9cbmNvbnN0IHJlc29sdmVRdWV1ZUxpbWl0ID0gKGNvbmZpZykgPT4ge1xuICBjb25zdCBjb25maWd1cmVkID0gcGFyc2VJbnQoY29uZmlnLnF1ZXVlTGltaXQpO1xuICBjb25zdCBtYXhXb3JrZXJzID0gcGFyc2VJbnQoY29uZmlnLm1heFdvcmtlcnMpO1xuXG4gIGlmICghaXNOYU4oY29uZmlndXJlZCkgJiYgY29uZmlndXJlZCA+IDApIHtcbiAgICByZXR1cm4gY29uZmlndXJlZDtcbiAgfVxuXG4gIHJldHVybiAoaXNOYU4obWF4V29ya2VycykgPyA4IDogbWF4V29ya2VycykgKiA0O1xufTtcblxuLyoqXG4gKiBSZXR1cm5zIHRoZSBudW1iZXIgb2YgZXhwb3J0cyBjdXJyZW50bHkgYWxsb3dlZCB0byB3YWl0IGZvciBhIHdvcmtlci5cbiAqXG4gKiBAcmV0dXJucyB7bnVtYmVyfSBUaGUgYWN0aXZlIHF1ZXVlIGxpbWl0LlxuICovXG5leHBvcnQgY29uc3QgZ2V0UXVldWVMaW1pdCA9ICgpID0+IHF1ZXVlTGltaXQ7XG5cbi8qKlxuICogUmV0dXJucyBob3cgbG9uZyB0byB3YWl0IGJlZm9yZSBhbnN3ZXJpbmcgYSByZXF1ZXN0IHJlZnVzZWQgYmVjYXVzZSB0aGUgcXVldWVcbiAqIHdhcyBmdWxsLlxuICpcbiAqIEByZXR1cm5zIHtudW1iZXJ9IFRoZSBkZWxheSBpbiBtaWxsaXNlY29uZHMuXG4gKi9cbmV4cG9ydCBjb25zdCBnZXRRdWV1ZVJlamVjdERlbGF5ID0gKCkgPT4ge1xuICBjb25zdCBkZWxheSA9IHBhcnNlSW50KHBvb2xDb25maWcucXVldWVSZWplY3REZWxheSk7XG4gIHJldHVybiBpc05hTihkZWxheSkgfHwgZGVsYXkgPCAwID8gMCA6IGRlbGF5O1xufTtcblxuY29uc3QgZmFjdG9yeSA9IHtcbiAgLyoqXG4gICAqIENyZWF0ZXMgYSBuZXcgd29ya2VyIHBhZ2UgZm9yIHRoZSBleHBvcnQgcG9vbC5cbiAgICpcbiAgICogQHJldHVybnMge09iamVjdH0gLSBBbiBvYmplY3QgY29udGFpbmluZyB0aGUgd29ya2VyIElELCBhIHJlZmVyZW5jZSB0byB0aGVcbiAgICogYnJvd3NlciBwYWdlLCBhbmQgaW5pdGlhbCB3b3JrIGNvdW50LlxuICAgKlxuICAgKiBAdGhyb3dzIHtFeHBvcnRFcnJvcn0gLSBJZiB0aGVyZSdzIGFuIGVycm9yIGR1cmluZyB0aGUgY3JlYXRpb24gb2YgdGhlIG5ld1xuICAgKiBwYWdlLlxuICAgKi9cbiAgY3JlYXRlOiBhc3luYyAoKSA9PiB7XG4gICAgbGV0IHBhZ2UgPSBmYWxzZTtcblxuICAgIGNvbnN0IGlkID0gdXVpZCgpO1xuICAgIGNvbnN0IHN0YXJ0RGF0ZSA9IG5ldyBEYXRlKCkuZ2V0VGltZSgpO1xuXG4gICAgdHJ5IHtcbiAgICAgIHBhZ2UgPSBhd2FpdCBuZXdQYWdlKCk7XG5cbiAgICAgIGlmICghcGFnZSB8fCBwYWdlLmlzQ2xvc2VkKCkpIHtcbiAgICAgICAgdGhyb3cgbmV3IEV4cG9ydEVycm9yKCdUaGUgcGFnZSBpcyBpbnZhbGlkIG9yIGNsb3NlZC4nKTtcbiAgICAgIH1cblxuICAgICAgY29uc2VjdXRpdmVDcmVhdGVGYWlsdXJlcyA9IDA7XG5cbiAgICAgIGxvZyhcbiAgICAgICAgMyxcbiAgICAgICAgYFtwb29sXSBTdWNjZXNzZnVsbHkgY3JlYXRlZCBhIHdvcmtlciAke2lkfSAtIHRvb2sgJHtcbiAgICAgICAgICBuZXcgRGF0ZSgpLmdldFRpbWUoKSAtIHN0YXJ0RGF0ZVxuICAgICAgICB9IG1zLmBcbiAgICAgICk7XG4gICAgfSBjYXRjaCAoZXJyb3IpIHtcbiAgICAgICsrY29uc2VjdXRpdmVDcmVhdGVGYWlsdXJlcztcblxuICAgICAgdGhyb3cgbmV3IEV4cG9ydEVycm9yKFxuICAgICAgICAnRXJyb3IgZW5jb3VudGVyZWQgd2hlbiBjcmVhdGluZyBhIG5ldyBwYWdlLidcbiAgICAgICkuc2V0RXJyb3IoZXJyb3IpO1xuICAgIH1cblxuICAgIHJldHVybiB7XG4gICAgICBpZCxcbiAgICAgIHBhZ2UsXG4gICAgICAvLyBXaGljaCBicm93c2VyIHRoaXMgcGFnZSBiZWxvbmdzIHRvLCBzbyB0aGF0IGl0IGNhbiBiZSByZWNvZ25pc2VkIGFzXG4gICAgICAvLyBzdGFsZSBpZiB0aGF0IGJyb3dzZXIgZ29lcyBhd2F5IC0gc2VlIGZhY3RvcnkudmFsaWRhdGVcbiAgICAgIGdlbmVyYXRpb246IGdldEJyb3dzZXJHZW5lcmF0aW9uKCksXG4gICAgICAvLyBUcnkgdG8gZGlzdHJpYnV0ZSB0aGUgaW5pdGlhbCB3b3JrIGNvdW50XG4gICAgICB3b3JrQ291bnQ6IE1hdGgucm91bmQoTWF0aC5yYW5kb20oKSAqIChwb29sQ29uZmlnLndvcmtMaW1pdCAvIDIpKVxuICAgIH07XG4gIH0sXG5cbiAgLyoqXG4gICAqIFZhbGlkYXRlcyBhIHdvcmtlciBwYWdlIGluIHRoZSBleHBvcnQgcG9vbCwgY2hlY2tpbmcgaWYgaXQgaGFzIGV4Y2VlZGVkXG4gICAqIHRoZSB3b3JrIGxpbWl0LlxuICAgKlxuICAgKiBAcGFyYW0ge09iamVjdH0gd29ya2VySGFuZGxlIC0gVGhlIGhhbmRsZSB0byB0aGUgd29ya2VyLCBjb250YWluaW5nIHRoZVxuICAgKiB3b3JrZXIncyBJRCwgYSByZWZlcmVuY2UgdG8gdGhlIGJyb3dzZXIgcGFnZSwgYW5kIHdvcmsgY291bnQuXG4gICAqXG4gICAqIEByZXR1cm5zIHtib29sZWFufSAtIFJldHVybnMgdHJ1ZSBpZiB0aGUgd29ya2VyIGlzIHZhbGlkIGFuZCB3aXRoaW5cbiAgICogdGhlIHdvcmsgbGltaXQ7IG90aGVyd2lzZSwgcmV0dXJucyBmYWxzZS5cbiAgICovXG4gIHZhbGlkYXRlOiBhc3luYyAod29ya2VySGFuZGxlKSA9PiB7XG4gICAgLy8gTk9URTogSW4gY2VydGFpbiBjYXNlcyBhY3F1aXJpbmcgdGhyb3dzIGEgVGFyZ2V0Q2xvc2VFcnJvciwgd2hpY2ggbWF5XG4gICAgLy8gICAgICAgYmUgY2F1c2VkIGJ5IHR3byB0aGluZ3M6XG4gICAgLy8gICAgICAgICAtIFRoZSBwYWdlIGlzIGNsb3NlZCBhbmQgYXR0ZW1wdGVkIHRvIGJlIHJldXNlZC5cbiAgICAvLyAgICAgICAgIC0gTG9zdCBjb250YWN0IHdpdGggdGhlIGJyb3dzZXJcbiAgICAvLyAgICAgICBXaGF0IHdlJ3JlIHNlZWluZyBpbiBsb2dzIGlzIHRoYXQgc3VjY2Vzc2l2ZSBleHBvcnRzIHR5cGljYWxseVxuICAgIC8vICAgICAgIHN1Y2NlZWRzLCBhbmQgdGhlIHNlcnZlciByZWNvdmVycywgaW5kaWNhdGluZyB0aGF0IGl0J3MgbGlrZWx5XG4gICAgLy8gICAgICAgdGhlIGZpcnN0IGNhc2UuIFRoaXMgaXMgYW4gYXR0ZW1wdCBhdCBhbGxpZXZhdGluZyB0aGUgaXNzdWUgYnlcbiAgICAvLyAgICAgICBzaW1wbHkgbm90IHZhbGlkYXRpbmcgdGhlIHdvcmtlciBpZiB0aGUgcGFnZSBpcyBudWxsIG9yIGNsb3NlZC5cbiAgICAvL1xuICAgIC8vICAgICAgIFRoZSBhY3R1YWwgcmVzdWx0IGZyb20gd2hlbiB0aGlzIGhhcHBlbmVkLCB3YXMgdGhhdCBhIHdvcmtlciB3b3VsZFxuICAgIC8vICAgICAgIGJlIGNvbXBsZXRlbHkgbG9ja2VkLCBzdG9wcGluZyBpdCBmcm9tIGJlaW5nIGFjcXVpcmVkIHVudGlsXG4gICAgLy8gICAgICAgaXRzIHdvcmsgY291bnQgcmVhY2hlZCB0aGUgbGltaXQuXG4gICAgaWYgKCF3b3JrZXJIYW5kbGUucGFnZSB8fCB3b3JrZXJIYW5kbGUucGFnZT8uaXNDbG9zZWQoKSkge1xuICAgICAgcmV0dXJuIGZhbHNlO1xuICAgIH1cblxuICAgIC8vIFNldCB3aGVuIGFuIGV4cG9ydCBsZWZ0IHRoZSBwYWdlIGluIGEgc3RhdGUgaXQgY2Fubm90IGJlIHJldXNlZCBmcm9tIC0gc2VlXG4gICAgLy8gdGhlIHJhc3Rlcml6YXRpb24gdGltZW91dCBoYW5kbGluZyBpbiBwb3N0V29ya1xuICAgIGlmICh3b3JrZXJIYW5kbGUubXVzdFJlY3ljbGUpIHtcbiAgICAgIGxvZyhcbiAgICAgICAgMyxcbiAgICAgICAgYFtwb29sXSBXb3JrZXIgZmFpbGVkIHZhbGlkYXRpb246IGl0cyBwYWdlIHdhcyBsZWZ0IHVudXNhYmxlIGJ5IGEgcHJldmlvdXMgZXhwb3J0LmBcbiAgICAgICk7XG4gICAgICByZXR1cm4gZmFsc2U7XG4gICAgfVxuXG4gICAgLy8gTk9URTogQSBwYWdlIHdob3NlIGJyb3dzZXIgaGFzIGdvbmUgYXdheSBzdGlsbCByZXBvcnRzXG4gICAgLy8gICAgICAgaXNDbG9zZWQoKSA9PT0gZmFsc2UsIHNvIHRoZSBjaGVjayBhYm92ZSBjYW5ub3QgZGV0ZWN0IGl0IGFuZCB0aGVcbiAgICAvLyAgICAgICBwb29sIHdvdWxkIGtlZXAgaGFuZGluZyBvdXQgcGFnZXMgYmVsb25naW5nIHRvIGEgZGVhZCBwcm9jZXNzLFxuICAgIC8vICAgICAgIGZhaWxpbmcgZXZlcnkgZXhwb3J0IHdoaWxlIGNvbnRpbnVpbmcgdG8gcmVwb3J0IGhlYWx0aHkgd29ya2Vycy5cbiAgICAvL1xuICAgIC8vICAgICAgIFRoZSBnZW5lcmF0aW9uIGlzIHRoZSByZWxpYWJsZSBzaWduYWw6IGl0IGFkdmFuY2VzIHdoZW5ldmVyIHRoZVxuICAgIC8vICAgICAgIGJyb3dzZXIgaXMgbG9zdCwgc28gYW55IHdvcmtlciBjcmVhdGVkIGFnYWluc3QgYW4gZWFybGllciBvbmUgaXNcbiAgICAvLyAgICAgICBzdGFsZS4gUmV0dXJuaW5nIGZhbHNlIGhlcmUgbWFrZXMgdGFybiBkZXN0cm95IHRoZSB3b3JrZXIgYW5kXG4gICAgLy8gICAgICAgY3JlYXRlIGEgcmVwbGFjZW1lbnQsIHdoaWNoIHJlbGF1bmNoZXMgdGhlIGJyb3dzZXIgdmlhIG5ld1BhZ2UuXG4gICAgaWYgKHdvcmtlckhhbmRsZS5nZW5lcmF0aW9uICE9PSBnZXRCcm93c2VyR2VuZXJhdGlvbigpKSB7XG4gICAgICBsb2coXG4gICAgICAgIDMsXG4gICAgICAgIGBbcG9vbF0gV29ya2VyIGZhaWxlZCB2YWxpZGF0aW9uOiBpdCBiZWxvbmdzIHRvIGEgYnJvd3NlciB0aGF0IGlzIGdvbmUgKHdvcmtlciBnZW5lcmF0aW9uICR7d29ya2VySGFuZGxlLmdlbmVyYXRpb259LCBjdXJyZW50ICR7Z2V0QnJvd3NlckdlbmVyYXRpb24oKX0pLmBcbiAgICAgICk7XG4gICAgICByZXR1cm4gZmFsc2U7XG4gICAgfVxuXG4gICAgLy8gTk9URTogV2FpdCBmb3IgdGhlIHBhZ2UgY2xlYXJpbmcgc3RhcnRlZCB3aGVuIHRoaXMgd29ya2VyIHdhcyByZWxlYXNlZFxuICAgIC8vICAgICAgIHRvIGNvbXBsZXRlIGJlZm9yZSBoYW5kaW5nIGl0IG91dCBhZ2Fpbi5cbiAgICAvL1xuICAgIC8vICAgICAgIFRhcm4ncyByZWxlYXNlKCkgaXMgc3luY2hyb25vdXM6IGl0IGludm9rZXMgdGhlICdyZWxlYXNlJyBldmVudFxuICAgIC8vICAgICAgIGhhbmRsZXJzIGFuZCB0aGVuIHJldHVybnMgdGhlIHJlc291cmNlIHRvIHRoZSBmcmVlIGxpc3Qgd2l0aGluIHRoZVxuICAgIC8vICAgICAgIHNhbWUgdGljaywgd2l0aG91dCBhd2FpdGluZyBhbnl0aGluZy4gQ2xlYXJpbmcgdGhlIHBhZ2UgaW4gdGhhdFxuICAgIC8vICAgICAgIGhhbmRsZXIgdGhlcmVmb3JlIG92ZXJsYXBzIHRoZSBuZXh0IGV4cG9ydCB3aGVuZXZlciBhbiBhY3F1aXJlIGlzXG4gICAgLy8gICAgICAgYWxyZWFkeSB3YWl0aW5nLCB3aGljaCBpcyBleGFjdGx5IHRoZSBjYXNlIHVuZGVyIHNhdHVyYXRpb24uXG4gICAgLy9cbiAgICAvLyAgICAgICBJbiBwcmFjdGljZSB0aGUgdmlzaWJsZSBkYW1hZ2UgaXMgY3VycmVudGx5IGxpbWl0ZWQsIGJlY2F1c2VcbiAgICAvLyAgICAgICBleHBvcnQuanMgYWxyZWFkeSBjYWxscyBjbGVhclBhZ2VSZXNvdXJjZXMgYXQgdGhlIGVuZCBvZiBldmVyeVxuICAgIC8vICAgICAgIGV4cG9ydCwgd2hpY2ggZGVzdHJveXMgdGhlIG9sZCBjaGFydHMgYW5kIHJlbW92ZXMgdGhlIGluamVjdGVkXG4gICAgLy8gICAgICAgdGFncy4gVGhlIGlubmVySFRNTCByZXNldCBkb25lIGhlcmUgaXMgYSBzZWNvbmQgcGFzcy4gQnV0IHRoYXRcbiAgICAvLyAgICAgICBtYWtlcyBjb3JyZWN0bmVzcyBkZXBlbmQgb24gdGhlIG9yZGVyaW5nIG9mIHR3byBpbmRlcGVuZGVudCBjbGVhbnVwXG4gICAgLy8gICAgICAgcGF0aHMsIGFuZCBhbiBpbm5lckhUTUwgcmVzZXQgbGFuZGluZyBwYXJ0IHdheSB0aHJvdWdoIHRoZSBuZXh0XG4gICAgLy8gICAgICAgcmVuZGVyIHdvdWxkIHdpcGUgaXRzIGNvbnRhaW5lci5cbiAgICAvL1xuICAgIC8vICAgICAgIFRhcm4gZG9lcyBhd2FpdCB2YWxpZGF0ZSwgc28gdGhpcyBpcyB0aGUgcG9pbnQgd2hlcmUgdGhlIGNsZWFyaW5nXG4gICAgLy8gICAgICAgaXMgZ3VhcmFudGVlZCB0byBoYXZlIGZpbmlzaGVkLiBEb2luZyBpdCBoZXJlIHJhdGhlciB0aGFuIGluIHRoZVxuICAgIC8vICAgICAgIGhhbmRsZXIga2VlcHMgdGhlIHdvcmsgb3ZlcmxhcHBlZCB3aXRoIHRoZSB3b3JrZXIncyBpZGxlIHRpbWUsIGFuZFxuICAgIC8vICAgICAgIGxldHMgYSBwYWdlIHRoYXQgY291bGQgbm90IGJlIGNsZWFyZWQgcmVjeWNsZSB0aGUgd29ya2VyIGluc3RlYWQgb2ZcbiAgICAvLyAgICAgICBiZWluZyBleHBvcnRlZCBvbnRvLlxuICAgIGlmICh3b3JrZXJIYW5kbGUuY2xlYW5Qcm9taXNlKSB7XG4gICAgICBjb25zdCBjbGVhcmVkID0gYXdhaXQgd29ya2VySGFuZGxlLmNsZWFuUHJvbWlzZTtcbiAgICAgIHdvcmtlckhhbmRsZS5jbGVhblByb21pc2UgPSBudWxsO1xuXG4gICAgICBpZiAoIWNsZWFyZWQpIHtcbiAgICAgICAgbG9nKFxuICAgICAgICAgIDMsXG4gICAgICAgICAgYFtwb29sXSBXb3JrZXIgZmFpbGVkIHZhbGlkYXRpb246IHRoZSBwYWdlIGNvdWxkIG5vdCBiZSBjbGVhcmVkIGFmdGVyIGl0cyBwcmV2aW91cyBleHBvcnQuYFxuICAgICAgICApO1xuICAgICAgICByZXR1cm4gZmFsc2U7XG4gICAgICB9XG4gICAgfVxuXG4gICAgaWYgKFxuICAgICAgcG9vbENvbmZpZy53b3JrTGltaXQgJiZcbiAgICAgICsrd29ya2VySGFuZGxlLndvcmtDb3VudCA+IHBvb2xDb25maWcud29ya0xpbWl0XG4gICAgKSB7XG4gICAgICBsb2coXG4gICAgICAgIDMsXG4gICAgICAgIGBbcG9vbF0gV29ya2VyIGZhaWxlZCB2YWxpZGF0aW9uOiBleGNlZWRlZCB3b3JrIGxpbWl0IChsaW1pdCBpcyAke3Bvb2xDb25maWcud29ya0xpbWl0fSkuYFxuICAgICAgKTtcbiAgICAgIHJldHVybiBmYWxzZTtcbiAgICB9XG4gICAgcmV0dXJuIHRydWU7XG4gIH0sXG5cbiAgLyoqXG4gICAqIERlc3Ryb3lzIGEgd29ya2VyIGVudHJ5IGluIHRoZSBleHBvcnQgcG9vbCwgY2xvc2luZyBpdHMgYXNzb2NpYXRlZCBwYWdlLlxuICAgKlxuICAgKiBAcGFyYW0ge09iamVjdH0gd29ya2VySGFuZGxlIC0gVGhlIGhhbmRsZSB0byB0aGUgd29ya2VyLCBjb250YWluaW5nXG4gICAqIHRoZSB3b3JrZXIncyBJRCBhbmQgYSByZWZlcmVuY2UgdG8gdGhlIGJyb3dzZXIgcGFnZS5cbiAgICovXG4gIGRlc3Ryb3k6IGFzeW5jICh3b3JrZXJIYW5kbGUpID0+IHtcbiAgICBsb2coMywgYFtwb29sXSBEZXN0cm95aW5nIHBvb2wgZW50cnkgJHt3b3JrZXJIYW5kbGUuaWR9LmApO1xuXG4gICAgaWYgKHdvcmtlckhhbmRsZS5wYWdlICYmICF3b3JrZXJIYW5kbGUucGFnZS5pc0Nsb3NlZCgpKSB7XG4gICAgICBhd2FpdCB3b3JrZXJIYW5kbGUucGFnZS5jbG9zZSgpO1xuICAgIH1cbiAgfVxuXG4gIC8vIGxvZzogKG1lc3NhZ2UsIGxldmVsKSA9PiBsb2coMSwgJ1t0YXJuXSAnICsgIG1lc3NhZ2UpXG59O1xuXG4vKipcbiAqIEluaXRpYWxpemVzIHRoZSBleHBvcnQgcG9vbCB3aXRoIHRoZSBwcm92aWRlZCBjb25maWd1cmF0aW9uLCBjcmVhdGluZ1xuICogYSBicm93c2VyIGluc3RhbmNlIGFuZCBzZXR0aW5nIHVwIHdvcmtlciByZXNvdXJjZXMuXG4gKlxuICogQHBhcmFtIHtPYmplY3R9IGNvbmZpZyAtIENvbmZpZ3VyYXRpb24gb3B0aW9ucyBmb3IgdGhlIGV4cG9ydCBwb29sIGFsb25nXG4gKiB3aXRoIGN1c3RvbSBwdXBwZXRlZXIgYXJndW1lbnRzIGZvciB0aGUgcHVwcGV0ZWVyLmxhdW5jaCBmdW5jdGlvbi5cbiAqL1xuZXhwb3J0IGNvbnN0IGluaXRQb29sID0gYXN5bmMgKGNvbmZpZykgPT4ge1xuICAvLyBGb3IgdGhlIG1vZHVsZSBzY29wZSB1c2FnZVxuICBwb29sQ29uZmlnID0gY29uZmlnICYmIGNvbmZpZy5wb29sID8geyAuLi5jb25maWcucG9vbCB9IDoge307XG5cbiAgLy8gV29yayBvdXQgaG93IGRlZXAgdGhlIHF1ZXVlIG9mIHdhaXRpbmcgZXhwb3J0cyBpcyBhbGxvd2VkIHRvIGdldFxuICBxdWV1ZUxpbWl0ID0gcmVzb2x2ZVF1ZXVlTGltaXQocG9vbENvbmZpZyk7XG5cbiAgLy8gQ3JlYXRlIGEgYnJvd3NlciBpbnN0YW5jZSB3aXRoIHRoZSBwdXBwZXRlZXIgYXJndW1lbnRzXG4gIGF3YWl0IGNyZWF0ZUJyb3dzZXIoY29uZmlnLnB1cHBldGVlckFyZ3MpO1xuXG4gIGxvZyhcbiAgICAzLFxuICAgIGBbcG9vbF0gSW5pdGlhbGl6aW5nIHBvb2wgd2l0aCB3b3JrZXJzOiBtaW4gJHtwb29sQ29uZmlnLm1pbldvcmtlcnN9LCBtYXggJHtwb29sQ29uZmlnLm1heFdvcmtlcnN9LmBcbiAgKTtcblxuICBpZiAocG9vbCkge1xuICAgIHJldHVybiBsb2coXG4gICAgICA0LFxuICAgICAgJ1twb29sXSBBbHJlYWR5IGluaXRpYWxpemVkLCBwbGVhc2Uga2lsbCBpdCBiZWZvcmUgY3JlYXRpbmcgYSBuZXcgb25lLidcbiAgICApO1xuICB9XG5cbiAgaWYgKHBhcnNlSW50KHBvb2xDb25maWcubWluV29ya2VycykgPiBwYXJzZUludChwb29sQ29uZmlnLm1heFdvcmtlcnMpKSB7XG4gICAgcG9vbENvbmZpZy5taW5Xb3JrZXJzID0gcG9vbENvbmZpZy5tYXhXb3JrZXJzO1xuICB9XG5cbiAgdHJ5IHtcbiAgICAvLyBDcmVhdGUgYSBwb29sIGFsb25nIHdpdGggYSBtaW5pbWFsIG51bWJlciBvZiByZXNvdXJjZXNcbiAgICBwb29sID0gbmV3IFBvb2woe1xuICAgICAgLy8gR2V0IHRoZSBjcmVhdGUvdmFsaWRhdGUvZGVzdHJveS9sb2cgZnVuY3Rpb25zXG4gICAgICAuLi5mYWN0b3J5LFxuICAgICAgbWluOiBwYXJzZUludChwb29sQ29uZmlnLm1pbldvcmtlcnMpLFxuICAgICAgbWF4OiBwYXJzZUludChwb29sQ29uZmlnLm1heFdvcmtlcnMpLFxuICAgICAgYWNxdWlyZVRpbWVvdXRNaWxsaXM6IHBvb2xDb25maWcuYWNxdWlyZVRpbWVvdXQsXG4gICAgICBjcmVhdGVUaW1lb3V0TWlsbGlzOiBwb29sQ29uZmlnLmNyZWF0ZVRpbWVvdXQsXG4gICAgICBkZXN0cm95VGltZW91dE1pbGxpczogcG9vbENvbmZpZy5kZXN0cm95VGltZW91dCxcbiAgICAgIGlkbGVUaW1lb3V0TWlsbGlzOiBwb29sQ29uZmlnLmlkbGVUaW1lb3V0LFxuICAgICAgY3JlYXRlUmV0cnlJbnRlcnZhbE1pbGxpczogcG9vbENvbmZpZy5jcmVhdGVSZXRyeUludGVydmFsLFxuICAgICAgcmVhcEludGVydmFsTWlsbGlzOiBwb29sQ29uZmlnLnJlYXBlckludGVydmFsLFxuICAgICAgcHJvcGFnYXRlQ3JlYXRlRXJyb3I6IGZhbHNlXG4gICAgfSk7XG5cbiAgICAvLyBTZXQgZXZlbnRzXG4gICAgcG9vbC5vbigncmVsZWFzZScsIChyZXNvdXJjZSkgPT4ge1xuICAgICAgLy8gU3RhcnQgY2xlYXJpbmcgdGhlIHBhZ2UsIGJ1dCBkZWxpYmVyYXRlbHkgZG8gbm90IGF3YWl0IGl0IGhlcmUgLSBzZWVcbiAgICAgIC8vIHRoZSBub3RlIGluIGZhY3RvcnkudmFsaWRhdGUsIHdoaWNoIGlzIHdoZXJlIHRoZSByZXN1bHQgaXMgYXdhaXRlZFxuICAgICAgLy8gYmVmb3JlIHRoZSB3b3JrZXIgY2FuIGJlIGhhbmRlZCBvdXQgYWdhaW4uIFRoZSBjYXRjaCBpcyBiZWx0IGFuZCBicmFjZXM6XG4gICAgICAvLyBjbGVhclBhZ2UgcmVzb2x2ZXMgZmFsc2UgcmF0aGVyIHRoYW4gcmVqZWN0aW5nLCBhbmQgbm90aGluZyBtdXN0IGJlXG4gICAgICAvLyBhYmxlIHRvIHR1cm4gdGhpcyBpbnRvIGFuIHVuaGFuZGxlZCByZWplY3Rpb24uXG4gICAgICByZXNvdXJjZS5jbGVhblByb21pc2UgPSBjbGVhclBhZ2UocmVzb3VyY2UucGFnZSwgZmFsc2UpLmNhdGNoKFxuICAgICAgICAoKSA9PiBmYWxzZVxuICAgICAgKTtcblxuICAgICAgbG9nKDQsIGBbcG9vbF0gUmVsZWFzaW5nIGEgd29ya2VyIHdpdGggSUQgJHtyZXNvdXJjZS5pZH0uYCk7XG4gICAgfSk7XG5cbiAgICBwb29sLm9uKCdkZXN0cm95U3VjY2VzcycsIChldmVudElkLCByZXNvdXJjZSkgPT4ge1xuICAgICAgbG9nKDQsIGBbcG9vbF0gRGVzdHJveWVkIGEgd29ya2VyIHdpdGggSUQgJHtyZXNvdXJjZS5pZH0uYCk7XG4gICAgICByZXNvdXJjZS5wYWdlID0gbnVsbDtcbiAgICB9KTtcblxuICAgIGNvbnN0IGluaXRpYWxSZXNvdXJjZXMgPSBbXTtcbiAgICAvLyBDcmVhdGUgYW4gaW5pdGlhbCBudW1iZXIgb2YgcmVzb3VyY2VzXG4gICAgZm9yIChsZXQgaSA9IDA7IGkgPCBwb29sQ29uZmlnLm1pbldvcmtlcnM7IGkrKykge1xuICAgICAgdHJ5IHtcbiAgICAgICAgY29uc3QgcmVzb3VyY2UgPSBhd2FpdCBwb29sLmFjcXVpcmUoKS5wcm9taXNlO1xuICAgICAgICBpbml0aWFsUmVzb3VyY2VzLnB1c2gocmVzb3VyY2UpO1xuICAgICAgfSBjYXRjaCAoZXJyb3IpIHtcbiAgICAgICAgbG9nV2l0aFN0YWNrKDIsIGVycm9yLCAnW3Bvb2xdIENvdWxkIG5vdCBjcmVhdGUgYW4gaW5pdGlhbCByZXNvdXJjZS4nKTtcbiAgICAgIH1cbiAgICB9XG5cbiAgICAvLyBSZWxlYXNlIHRoZSBpbml0aWFsIG51bWJlciBvZiByZXNvdXJjZXMgYmFjayB0byB0aGUgcG9vbFxuICAgIGluaXRpYWxSZXNvdXJjZXMuZm9yRWFjaCgocmVzb3VyY2UpID0+IHtcbiAgICAgIHBvb2wucmVsZWFzZShyZXNvdXJjZSk7XG4gICAgfSk7XG5cbiAgICBsb2coXG4gICAgICAzLFxuICAgICAgYFtwb29sXSBUaGUgcG9vbCBpcyByZWFkeSR7aW5pdGlhbFJlc291cmNlcy5sZW5ndGggPyBgIHdpdGggJHtpbml0aWFsUmVzb3VyY2VzLmxlbmd0aH0gaW5pdGlhbCByZXNvdXJjZXMgd2FpdGluZy5gIDogJy4nfWBcbiAgICApO1xuICB9IGNhdGNoIChlcnJvcikge1xuICAgIHRocm93IG5ldyBFeHBvcnRFcnJvcihcbiAgICAgICdbcG9vbF0gQ291bGQgbm90IGNyZWF0ZSB0aGUgcG9vbCBvZiB3b3JrZXJzLidcbiAgICApLnNldEVycm9yKGVycm9yKTtcbiAgfVxufTtcblxuLyoqXG4gKiBLaWxscyBhbGwgd29ya2VycyBpbiB0aGUgcG9vbCwgZGVzdHJveXMgdGhlIHBvb2wsIGFuZCBjbG9zZXMgdGhlIGJyb3dzZXJcbiAqIGluc3RhbmNlLlxuICpcbiAqIEByZXR1cm5zIHtQcm9taXNlPHZvaWQ+fSBBIHByb21pc2UgdGhhdCByZXNvbHZlcyBhZnRlciB0aGUgd29ya2VycyBhcmVcbiAqIGtpbGxlZCwgdGhlIHBvb2wgaXMgZGVzdHJveWVkLCBhbmQgdGhlIGJyb3dzZXIgaXMgY2xvc2VkLlxuICovXG5leHBvcnQgYXN5bmMgZnVuY3Rpb24ga2lsbFBvb2woKSB7XG4gIGxvZygzLCAnW3Bvb2xdIEtpbGxpbmcgcG9vbCB3aXRoIGFsbCB3b3JrZXJzIGFuZCBjbG9zaW5nIGJyb3dzZXIuJyk7XG5cbiAgLy8gSWYgc3RpbGwgYWxpdmUsIGRlc3Ryb3kgdGhlIHBvb2wgb2YgcGFnZXMgYmVmb3JlIGNsb3NpbmcgYSBicm93c2VyXG4gIGlmIChwb29sKSB7XG4gICAgLy8gRnJlZSB1cCBub3QgcmVsZWFzZWQgd29ya2Vyc1xuICAgIGZvciAoY29uc3Qgd29ya2VyIG9mIHBvb2wudXNlZCkge1xuICAgICAgcG9vbC5yZWxlYXNlKHdvcmtlci5yZXNvdXJjZSk7XG4gICAgfVxuXG4gICAgLy8gRGVzdHJveSB0aGUgcG9vbCBpZiBpdCBpcyBzdGlsbCBhdmFpbGFibGVcbiAgICBpZiAoIXBvb2wuZGVzdHJveWVkKSB7XG4gICAgICBhd2FpdCBwb29sLmRlc3Ryb3koKTtcbiAgICAgIGxvZyg0LCAnW2Jyb3dzZXJdIERlc3Ryb3llZCB0aGUgcG9vbCBvZiByZXNvdXJjZXMuJyk7XG4gICAgfVxuICB9XG5cbiAgLy8gQ2xvc2UgdGhlIGJyb3dzZXIgaW5zdGFuY2VcbiAgYXdhaXQgY2xvc2VCcm93c2VyKCk7XG59XG5cbi8qKlxuICogUHJvY2Vzc2VzIHRoZSBleHBvcnQgd29yayB1c2luZyBhIHdvcmtlciBmcm9tIHRoZSBwb29sLiBBY3F1aXJlcyBhIHdvcmtlclxuICogaGFuZGxlIGZyb20gdGhlIHBvb2wsIHBlcmZvcm1zIHRoZSBleHBvcnQgdXNpbmcgcHVwcGV0ZWVyLCBhbmQgcmVsZWFzZXNcbiAqIHRoZSB3b3JrZXIgaGFuZGxlIGJhY2sgdG8gdGhlIHBvb2wuXG4gKlxuICogQHBhcmFtIHtzdHJpbmd9IGNoYXJ0IC0gVGhlIGNoYXJ0IGRhdGEgb3IgY29uZmlndXJhdGlvbiB0byBiZSBleHBvcnRlZC5cbiAqIEBwYXJhbSB7T2JqZWN0fSBvcHRpb25zIC0gRXhwb3J0IG9wdGlvbnMgYW5kIGNvbmZpZ3VyYXRpb24uXG4gKlxuICogQHJldHVybnMge1Byb21pc2U8T2JqZWN0Pn0gQSBwcm9taXNlIHRoYXQgcmVzb2x2ZXMgd2l0aCB0aGUgZXhwb3J0IHJlc3VsdGFuZFxuICogb3B0aW9ucy5cbiAqXG4gKiBAdGhyb3dzIHtFeHBvcnRFcnJvcn0gSWYgYW4gZXJyb3Igb2NjdXJzIGR1cmluZyB0aGUgZXhwb3J0IHByb2Nlc3MuXG4gKi9cbmV4cG9ydCBjb25zdCBwb3N0V29yayA9IGFzeW5jIChjaGFydCwgb3B0aW9ucykgPT4ge1xuICBsZXQgd29ya2VySGFuZGxlO1xuXG4gIHRyeSB7XG4gICAgbG9nKDQsICdbcG9vbF0gV29yayByZWNlaXZlZCwgc3RhcnRpbmcgdG8gcHJvY2Vzcy4nKTtcblxuICAgICsrc3RhdHMuZXhwb3J0QXR0ZW1wdHM7XG4gICAgaWYgKHBvb2xDb25maWcuYmVuY2htYXJraW5nKSB7XG4gICAgICBnZXRQb29sSW5mbygpO1xuICAgIH1cblxuICAgIGlmICghcG9vbCkge1xuICAgICAgdGhyb3cgbmV3IEV4cG9ydEVycm9yKCdXb3JrIHJlY2VpdmVkLCBidXQgcG9vbCBoYXMgbm90IGJlZW4gc3RhcnRlZC4nKTtcbiAgICB9XG5cbiAgICAvLyBOT1RFOiBSZWZ1c2UgdGhlIHdvcmsgcmF0aGVyIHRoYW4gcXVldWUgaXQgd2hlbiB0aGUgcXVldWUgaXMgYWxyZWFkeSBhdFxuICAgIC8vICAgICAgIGl0cyBsaW1pdC5cbiAgICAvL1xuICAgIC8vICAgICAgIFRocm91Z2hwdXQgZG9lcyBub3QgaW1wcm92ZSBwYXN0IHRoZSBwb29sIHNpemUgLSBtZWFzdXJlZCwgaXQgcGVha3NcbiAgICAvLyAgICAgICBhdCByb3VnaGx5IHRoZSBudW1iZXIgb2Ygd29ya2VycyBhbmQgdGhlbiBmYWxscyAtIHNvIGFuIHVuYm91bmRlZFxuICAgIC8vICAgICAgIHF1ZXVlIGNhbm5vdCBidXkgY2FwYWNpdHkuIFdoYXQgaXQgZG9lcyBidXkgaXMgbGF0ZW5jeSBhbmQgbWVtb3J5OlxuICAgIC8vICAgICAgIGV2ZXJ5IHdhaXRpbmcgcmVxdWVzdCBob2xkcyBpdHMgcGFyc2VkIGJvZHkgdW50aWwgaXQgaXMgc2VydmVkIG9yXG4gICAgLy8gICAgICAgdGltZXMgb3V0LCBhbmQgYSBzYXR1cmF0ZWQgc2VydmVyIHdhcyBvYnNlcnZlZCBhY2NlcHRpbmcgbW9yZSB0aGFuXG4gICAgLy8gICAgICAgdHdlbnR5IHRpbWVzIHRoZSB3b3JrIGl0IGNvdWxkIGNvbXBsZXRlLCBxdWV1ZWluZyB0aG91c2FuZHMgb2ZcbiAgICAvLyAgICAgICByZXF1ZXN0cyBhbmQgdGhlbiBmYWlsaW5nIG1vc3Qgb2YgdGhlbSBvbiB0aW1lb3V0LlxuICAgIC8vXG4gICAgLy8gICAgICAgRmFpbGluZyBoZXJlIGlzIGNoZWFwIGFuZCBpbW1lZGlhdGUsIGFuZCBjYXJyaWVzIGEgZGlzdGluY3QgZXJyb3JcbiAgICAvLyAgICAgICBjb2RlIHNvIHRoYXQgYSBjYWxsZXIgYW5kIGEgZGFzaGJvYXJkIGNhbiB0ZWxsIHRoaXMgYXBhcnQgZnJvbSBhXG4gICAgLy8gICAgICAgbWFsZm9ybWVkIHJlcXVlc3QuXG4gICAgaWYgKHBvb2wubnVtUGVuZGluZ0FjcXVpcmVzKCkgPj0gcXVldWVMaW1pdCkge1xuICAgICAgKytzdGF0cy5yZWplY3RlZEZvckNhcGFjaXR5O1xuXG4gICAgICB0aHJvdyBuZXcgRXhwb3J0RXJyb3IoXG4gICAgICAgIChvcHRpb25zLnBheWxvYWQ/LnJlcXVlc3RJZFxuICAgICAgICAgID8gYEZvciByZXF1ZXN0IHdpdGggSUQgJHtvcHRpb25zLnBheWxvYWQ/LnJlcXVlc3RJZH0gLSBgXG4gICAgICAgICAgOiAnJykgK1xuICAgICAgICAgIGBUaGUgc2VydmVyIGlzIGF0IGNhcGFjaXR5OiAke3Bvb2wubnVtUGVuZGluZ0FjcXVpcmVzKCl9IGV4cG9ydHMgYXJlIGFscmVhZHkgd2FpdGluZyBmb3IgYSB3b3JrZXIgKGxpbWl0IGlzICR7cXVldWVMaW1pdH0pLiBQbGVhc2UgcmV0cnkgc2hvcnRseS5gXG4gICAgICApLnNldENvZGUoZXJyb3JDb2Rlcy5RVUVVRV9GVUxMKTtcbiAgICB9XG5cbiAgICAvLyBOT1RFOiBEcm9wIHdvcmsgd2hvc2UgY2xpZW50IGhhcyBhbHJlYWR5IGdvbmUsIGJlZm9yZSBpdCB0YWtlcyBhIHF1ZXVlXG4gICAgLy8gICAgICAgc2xvdCBvciBhIHdvcmtlci5cbiAgICAvL1xuICAgIC8vICAgICAgIE1lYXN1cmVkIG9uIHRoZSB1bmJvdW5kZWQgcXVldWU6IDYwIGNsaWVudHMgdGhhdCBlYWNoIGdhdmUgdXAgYWZ0ZXJcbiAgICAvLyAgICAgICAxNTBtcyBsZWZ0IDEzNjcgZXhwb3J0cyBzdGlsbCBxdWV1ZWQgYSBzZWNvbmQgYWZ0ZXIgdGhlIGxhc3QgY2xpZW50XG4gICAgLy8gICAgICAgaGFkIGRpc2Nvbm5lY3RlZCwgYW5kIHRoZSBwb29sIHNwZW50IGEgZnVydGhlciBmaXZlIHNlY29uZHNcbiAgICAvLyAgICAgICByZW5kZXJpbmcgY2hhcnRzIG5vYm9keSB3b3VsZCByZWNlaXZlLiBVbmRlciBhIHJldHJ5IHN0b3JtIHRoYXRcbiAgICAvLyAgICAgICBjb21wb3VuZHMgLSBldmVyeSByZXRyeSBhZGRzIHdvcmsgd2hpbGUgdGhlIGFiYW5kb25lZCBvcmlnaW5hbCBpc1xuICAgIC8vICAgICAgIHN0aWxsIGJlaW5nIHJlbmRlcmVkIC0gc28gY2FwYWNpdHkgZmFsbHMgd2l0aCBlYWNoIHJvdW5kLlxuICAgIGNvbnN0IGFib3J0U2lnbmFsID0gb3B0aW9ucy5wYXlsb2FkPy5hYm9ydFNpZ25hbDtcblxuICAgIGlmIChhYm9ydFNpZ25hbD8uYWJvcnRlZCkge1xuICAgICAgKytzdGF0cy5hYmFuZG9uZWRFeHBvcnRzO1xuXG4gICAgICB0aHJvdyBuZXcgRXhwb3J0RXJyb3IoXG4gICAgICAgIChvcHRpb25zLnBheWxvYWQ/LnJlcXVlc3RJZFxuICAgICAgICAgID8gYEZvciByZXF1ZXN0IHdpdGggSUQgJHtvcHRpb25zLnBheWxvYWQ/LnJlcXVlc3RJZH0gLSBgXG4gICAgICAgICAgOiAnJykgKyAnVGhlIGNsaWVudCBkaXNjb25uZWN0ZWQgYmVmb3JlIGEgd29ya2VyIHdhcyBhdmFpbGFibGUuJ1xuICAgICAgKS5zZXRDb2RlKGVycm9yQ29kZXMuQ0xJRU5UX0dPTkUpO1xuICAgIH1cblxuICAgIC8vIEFjcXVpcmUgdGhlIHdvcmtlciBhbG9uZyB3aXRoIHRoZSBpZCBvZiByZXNvdXJjZSBhbmQgd29yayBjb3VudFxuICAgIGNvbnN0IGFjcXVpcmVDb3VudGVyID0gbWVhc3VyZVRpbWUoKTtcbiAgICB0cnkge1xuICAgICAgbG9nKDQsICdbcG9vbF0gQWNxdWlyaW5nIGEgd29ya2VyIGhhbmRsZS4nKTtcbiAgICAgIHdvcmtlckhhbmRsZSA9IGF3YWl0IHBvb2wuYWNxdWlyZSgpLnByb21pc2U7XG5cbiAgICAgIC8vIENoZWNrIHRoZSBwYWdlIGFjcXVpcmUgdGltZVxuICAgICAgaWYgKG9wdGlvbnMuc2VydmVyLmJlbmNobWFya2luZykge1xuICAgICAgICBsb2coXG4gICAgICAgICAgNSxcbiAgICAgICAgICBvcHRpb25zLnBheWxvYWQ/LnJlcXVlc3RJZFxuICAgICAgICAgICAgPyBgW2JlbmNobWFya10gUmVxdWVzdCB3aXRoIElEICR7b3B0aW9ucy5wYXlsb2FkPy5yZXF1ZXN0SWR9IC1gXG4gICAgICAgICAgICA6ICdbYmVuY2htYXJrXScsXG4gICAgICAgICAgYEFjcXVpcmVkIGEgd29ya2VyIGhhbmRsZTogJHthY3F1aXJlQ291bnRlcigpfW1zLmBcbiAgICAgICAgKTtcbiAgICAgIH1cbiAgICB9IGNhdGNoIChlcnJvcikge1xuICAgICAgdGhyb3cgbmV3IEV4cG9ydEVycm9yKFxuICAgICAgICAob3B0aW9ucy5wYXlsb2FkPy5yZXF1ZXN0SWRcbiAgICAgICAgICA/IGBGb3IgcmVxdWVzdCB3aXRoIElEICR7b3B0aW9ucy5wYXlsb2FkPy5yZXF1ZXN0SWR9IC0gYFxuICAgICAgICAgIDogJycpICtcbiAgICAgICAgICBgRXJyb3IgZW5jb3VudGVyZWQgd2hlbiBhY3F1aXJpbmcgYW4gYXZhaWxhYmxlIGVudHJ5OiAke2FjcXVpcmVDb3VudGVyKCl9bXMuYFxuICAgICAgKVxuICAgICAgICAuc2V0Q29kZShlcnJvckNvZGVzLkFDUVVJUkVfVElNRU9VVClcbiAgICAgICAgLnNldEVycm9yKGVycm9yKTtcbiAgICB9XG4gICAgbG9nKDQsICdbcG9vbF0gQWNxdWlyZWQgYSB3b3JrZXIgaGFuZGxlLicpO1xuXG4gICAgLy8gVGhlIGNsaWVudCBtYXkgaGF2ZSBnb25lIHdoaWxlIHRoaXMgcmVxdWVzdCB3YXMgcXVldWVkLiBIYW5kIHRoZSB3b3JrZXJcbiAgICAvLyBzdHJhaWdodCBiYWNrIHJhdGhlciB0aGFuIHNwZW5kaW5nIGl0IG9uIGEgcmVzdWx0IG5vYm9keSB3aWxsIHJlYWQuXG4gICAgaWYgKGFib3J0U2lnbmFsPy5hYm9ydGVkKSB7XG4gICAgICArK3N0YXRzLmFiYW5kb25lZEV4cG9ydHM7XG4gICAgICBwb29sLnJlbGVhc2Uod29ya2VySGFuZGxlKTtcbiAgICAgIHdvcmtlckhhbmRsZSA9IG51bGw7XG5cbiAgICAgIHRocm93IG5ldyBFeHBvcnRFcnJvcihcbiAgICAgICAgKG9wdGlvbnMucGF5bG9hZD8ucmVxdWVzdElkXG4gICAgICAgICAgPyBgRm9yIHJlcXVlc3Qgd2l0aCBJRCAke29wdGlvbnMucGF5bG9hZD8ucmVxdWVzdElkfSAtIGBcbiAgICAgICAgICA6ICcnKSArICdUaGUgY2xpZW50IGRpc2Nvbm5lY3RlZCB3aGlsZSB3YWl0aW5nIGZvciBhIHdvcmtlci4nXG4gICAgICApLnNldENvZGUoZXJyb3JDb2Rlcy5DTElFTlRfR09ORSk7XG4gICAgfVxuXG4gICAgaWYgKCF3b3JrZXJIYW5kbGUucGFnZSkge1xuICAgICAgdGhyb3cgbmV3IEV4cG9ydEVycm9yKFxuICAgICAgICAnUmVzb2x2ZWQgd29ya2VyIHBhZ2UgaXMgaW52YWxpZDogdGhlIHBvb2wgc2V0dXAgaXMgd29ua3kuJ1xuICAgICAgKTtcbiAgICB9XG5cbiAgICAvLyBTYXZlIHRoZSBzdGFydCB0aW1lXG4gICAgbGV0IHdvcmtTdGFydCA9IG5ldyBEYXRlKCkuZ2V0VGltZSgpO1xuXG4gICAgbG9nKDQsIGBbcG9vbF0gU3RhcnRpbmcgd29yayBvbiBwb29sIGVudHJ5IHdpdGggSUQgJHt3b3JrZXJIYW5kbGUuaWR9LmApO1xuXG4gICAgLy8gUGVyZm9ybSBhbiBleHBvcnQgb24gYSBwdXBwZXRlZXIgbGV2ZWxcbiAgICBjb25zdCBleHBvcnRDb3VudGVyID0gbWVhc3VyZVRpbWUoKTtcbiAgICBjb25zdCByZXN1bHQgPSBhd2FpdCBwdXBwZXRlZXJFeHBvcnQod29ya2VySGFuZGxlLnBhZ2UsIGNoYXJ0LCBvcHRpb25zKTtcblxuICAgIC8vIENoZWNrIGlmIGl0J3MgYW4gZXJyb3JcbiAgICBpZiAocmVzdWx0IGluc3RhbmNlb2YgRXJyb3IpIHtcbiAgICAgIC8vIE5PVEU6IElmIHRoZXJlJ3MgYSByYXN0ZXJpemF0aW9uIHRpbWVvdXQsIHdlIHdhbnQgbmVlZCB0byBmbHVzaCB0aGUgcGFnZS5cbiAgICAgIC8vICAgICAgIFRoaXMgaXMgYmVjYXVzZSB0aGUgcGFnZSBtYXkgYmUgaW4gYSBzdGF0ZSB3aGVyZSBpdCdzIHdhaXRpbmcgZm9yXG4gICAgICAvLyAgICAgICB0aGUgc2NyZWVuc2hvdCB0byBmaW5pc2ggZXZlbiB0aG91Z2ggdGhlIHRpbWVvdXQgaGFzIG9jY3VyZWQuXG4gICAgICAvLyAgICAgICBXaGljaCBvZiBjb3Vyc2UgY2F1c2VzIGEgbG90IG9mIGlzc3VlcyB3aXRoIHRoZSBldmVudCBzeXN0ZW0sXG4gICAgICAvLyAgICAgICBhbmQgcGFnZSBjb25zaXN0ZW5jeS5cbiAgICAgIC8vXG4gICAgICAvLyBOT1RFOiBPbmx5IHBhZ2Uuc2NyZWVuc2hvdCB3aWxsIHRocm93IHRoaXMsIHRpbWVvdXRzIGZvciBQREYncyBhcmVcbiAgICAgIC8vICAgICAgIGhhbmRsZWQgYnkgdGhlIHBhZ2UucGRmIGZ1bmN0aW9uIGl0c2VsZi5cbiAgICAgIC8vXG4gICAgICAvLyAgICAgICAuLi55ZXMsIHRoaXMgaXMgdWdseS5cbiAgICAgIGlmIChyZXN1bHQubWVzc2FnZSA9PT0gJ1Jhc3Rlcml6YXRpb24gdGltZW91dCcpIHtcbiAgICAgICAgLy8gTk9URTogVGhpcyB3b3JrZXIgbXVzdCBub3Qgc2VydmUgYW5vdGhlciBleHBvcnQuIEl0cyBwYWdlIG1heSBzdGlsbCBiZVxuICAgICAgICAvLyAgICAgICB3YWl0aW5nIG9uIHRoZSBzY3JlZW5zaG90IHRoYXQgdGltZWQgb3V0LCBzbyBpdHMgc3RhdGUgaXMgdW5rbm93bi5cbiAgICAgICAgLy9cbiAgICAgICAgLy8gICAgICAgTWFya2luZyBpdCByYXRoZXIgdGhhbiBkaXNjYXJkaW5nIHRoZSBwYWdlIHJlZmVyZW5jZSBpcyB0aGUgcG9pbnRcbiAgICAgICAgLy8gICAgICAgaGVyZS4gU2V0dGluZyBwYWdlIHRvIG51bGwgdXNlZCB0byBiZSBob3cgdGhpcyB3YXMgZG9uZSwgYnV0XG4gICAgICAgIC8vICAgICAgIGZhY3RvcnkuZGVzdHJveSBvbmx5IGNsb3NlcyBhIHBhZ2UgaXQgY2FuIHN0aWxsIHNlZSwgc28gbnVsbGluZ1xuICAgICAgICAvLyAgICAgICB0aGUgcmVmZXJlbmNlIG1lYW50IHRoZSBwYWdlIHdhcyBuZXZlciBjbG9zZWQgLSBhbmQgd2l0aCBhIHByb2Nlc3NcbiAgICAgICAgLy8gICAgICAgcGVyIHRhYiwgZXZlcnkgdGltZW91dCBsZWFrZWQgYSByZW5kZXJlciBwcm9jZXNzLiBVbmRlciBsb2FkIHRoYXRcbiAgICAgICAgLy8gICAgICAgY29tcG91bmRzOiB0aGUgbGVha2VkIHByb2Nlc3NlcyB0YWtlIENQVSBmcm9tIHRoZSBleHBvcnRzIHN0aWxsXG4gICAgICAgIC8vICAgICAgIHJ1bm5pbmcsIHdoaWNoIG1ha2VzIG1vcmUgb2YgdGhlbSB0aW1lIG91dC5cbiAgICAgICAgd29ya2VySGFuZGxlLndvcmtDb3VudCA9IHBvb2xDb25maWcud29ya0xpbWl0ICsgMTtcbiAgICAgICAgd29ya2VySGFuZGxlLm11c3RSZWN5Y2xlID0gdHJ1ZTtcbiAgICAgIH1cblxuICAgICAgaWYgKFxuICAgICAgICByZXN1bHQubmFtZSA9PT0gJ1RpbWVvdXRFcnJvcicgfHxcbiAgICAgICAgcmVzdWx0Lm1lc3NhZ2UgPT09ICdSYXN0ZXJpemF0aW9uIHRpbWVvdXQnXG4gICAgICApIHtcbiAgICAgICAgdGhyb3cgbmV3IEV4cG9ydEVycm9yKFxuICAgICAgICAgICdSYXN0ZXJpemF0aW9uIHRpbWVvdXQ6IHlvdXIgY2hhcnQgbWF5IGJlIHRvbyBjb21wbGV4IG9yIGxhcmdlLCBhbmQgZmFpbGVkIHRvIHJlbmRlciB3aXRoaW4gdGhlIGFsbG90dGVkIHRpbWUuJ1xuICAgICAgICApXG4gICAgICAgICAgLnNldENvZGUoZXJyb3JDb2Rlcy5SQVNURVJJWkFUSU9OX1RJTUVPVVQpXG4gICAgICAgICAgLnNldEVycm9yKHJlc3VsdCk7XG4gICAgICB9IGVsc2Uge1xuICAgICAgICB0aHJvdyBuZXcgRXhwb3J0RXJyb3IoXG4gICAgICAgICAgKG9wdGlvbnMucGF5bG9hZD8ucmVxdWVzdElkXG4gICAgICAgICAgICA/IGBGb3IgcmVxdWVzdCB3aXRoIElEICR7b3B0aW9ucy5wYXlsb2FkPy5yZXF1ZXN0SWR9IC0gYFxuICAgICAgICAgICAgOiAnJykgKyBgRXJyb3IgZW5jb3VudGVyZWQgZHVyaW5nIGV4cG9ydDogJHtleHBvcnRDb3VudGVyKCl9bXMuYFxuICAgICAgICApXG4gICAgICAgICAgLnNldENvZGUoZXJyb3JDb2Rlcy5FWFBPUlRfRkFJTEVEKVxuICAgICAgICAgIC5zZXRFcnJvcihyZXN1bHQpO1xuICAgICAgfVxuICAgIH1cblxuICAgIC8vIENoZWNrIHRoZSBQdXBwZXRlZXIgZXhwb3J0IHRpbWVcbiAgICBpZiAob3B0aW9ucy5zZXJ2ZXIuYmVuY2htYXJraW5nKSB7XG4gICAgICBsb2coXG4gICAgICAgIDUsXG4gICAgICAgIG9wdGlvbnMucGF5bG9hZD8ucmVxdWVzdElkXG4gICAgICAgICAgPyBgW2JlbmNobWFya10gUmVxdWVzdCB3aXRoIElEICR7b3B0aW9ucy5wYXlsb2FkPy5yZXF1ZXN0SWR9IC1gXG4gICAgICAgICAgOiAnW2JlbmNobWFya10nLFxuICAgICAgICBgRXhwb3J0ZWQgYSBjaGFydCBzdWNlc3NmdWxseTogJHtleHBvcnRDb3VudGVyKCl9bXMuYFxuICAgICAgKTtcbiAgICB9XG5cbiAgICAvLyBSZWxlYXNlIHRoZSByZXNvdXJjZSBiYWNrIHRvIHRoZSBwb29sXG4gICAgcG9vbC5yZWxlYXNlKHdvcmtlckhhbmRsZSk7XG5cbiAgICAvLyBVc2VkIGZvciBzdGF0aXN0aWNzIGluIGF2ZXJhZ2VUaW1lIGFuZCBwcm9jZXNzZWRXb3JrQ291bnQsIHdoaWNoXG4gICAgLy8gaW4gdHVybiBpcyB1c2VkIGJ5IHRoZSAvaGVhbHRoIHJvdXRlLlxuICAgIGNvbnN0IHdvcmtFbmQgPSBuZXcgRGF0ZSgpLmdldFRpbWUoKTtcbiAgICBjb25zdCBleHBvcnRUaW1lID0gd29ya0VuZCAtIHdvcmtTdGFydDtcbiAgICBzdGF0cy50aW1lU3BlbnQgKz0gZXhwb3J0VGltZTtcbiAgICBzdGF0cy5zcGVudEF2ZXJhZ2UgPSBzdGF0cy50aW1lU3BlbnQgLyArK3N0YXRzLnBlcmZvcm1lZEV4cG9ydHM7XG5cbiAgICBsb2coNCwgYFtwb29sXSBXb3JrIGNvbXBsZXRlZCBpbiAke2V4cG9ydFRpbWV9IG1zLmApO1xuXG4gICAgLy8gT3RoZXJ3aXNlIHJldHVybiB0aGUgcmVzdWx0XG4gICAgcmV0dXJuIHtcbiAgICAgIHJlc3VsdCxcbiAgICAgIG9wdGlvbnNcbiAgICB9O1xuICB9IGNhdGNoIChlcnJvcikge1xuICAgIC8vIE5PVEU6IFdvcmsgZGlzY2FyZGVkIGJlY2F1c2UgaXRzIGNsaWVudCBsZWZ0IGlzIG5vdCBhIGZhaWx1cmUgb2YgdGhlXG4gICAgLy8gICAgICAgc2VydmVyLCBzbyBpdCBpcyBjb3VudGVkIHNlcGFyYXRlbHkgcmF0aGVyIHRoYW4gaW5mbGF0aW5nXG4gICAgLy8gICAgICAgZHJvcHBlZEV4cG9ydHMgYW5kIGRlcHJlc3NpbmcgdGhlIHN1Y2Nlc3MgcmF0aW8gdGhhdCAvaGVhbHRoXG4gICAgLy8gICAgICAgcmVwb3J0cy4gSXQgaGFzIGl0cyBvd24gY291bnRlciwgaW5jcmVtZW50ZWQgd2hlcmUgaXQgaXMgZGV0ZWN0ZWQuXG4gICAgaWYgKGVycm9yLmVycm9yQ29kZSAhPT0gZXJyb3JDb2Rlcy5DTElFTlRfR09ORSkge1xuICAgICAgKytzdGF0cy5kcm9wcGVkRXhwb3J0cztcbiAgICB9XG5cbiAgICBpZiAod29ya2VySGFuZGxlKSB7XG4gICAgICBwb29sLnJlbGVhc2Uod29ya2VySGFuZGxlKTtcbiAgICB9XG5cbiAgICB0aHJvdyBuZXcgRXhwb3J0RXJyb3IoYFtwb29sXSBJbiBwb29sLnBvc3RXb3JrOiAke2Vycm9yLm1lc3NhZ2V9YCkuc2V0RXJyb3IoXG4gICAgICBlcnJvclxuICAgICk7XG4gIH1cbn07XG5cbi8qKlxuICogUmV0cmlldmVzIHRoZSBjdXJyZW50IHBvb2wgaW5zdGFuY2UuXG4gKlxuICogQHJldHVybnMge09iamVjdHxudWxsfSBUaGUgY3VycmVudCBwb29sIGluc3RhbmNlIGlmIGluaXRpYWxpemVkLCBvciBudWxsXG4gKiBpZiB0aGUgcG9vbCBoYXMgbm90IGJlZW4gY3JlYXRlZC5cbiAqL1xuZXhwb3J0IGNvbnN0IGdldFBvb2wgPSAoKSA9PiBwb29sO1xuXG4vKipcbiAqIFJldHJpZXZlcyBwb29sIGluZm9ybWF0aW9uIGluIEpTT04gZm9ybWF0LCBpbmNsdWRpbmcgbWluaW11bSBhbmQgbWF4aW11bVxuICogd29ya2VycywgYXZhaWxhYmxlIHdvcmtlcnMsIHdvcmtlcnMgaW4gdXNlLCBhbmQgcGVuZGluZyBhY3F1aXJlIHJlcXVlc3RzLlxuICpcbiAqIEByZXR1cm5zIHtPYmplY3R9IFBvb2wgaW5mb3JtYXRpb24gaW4gSlNPTiBmb3JtYXQuXG4gKi9cbmV4cG9ydCBjb25zdCBnZXRQb29sSW5mb0pTT04gPSAoKSA9PiAoe1xuICBtaW46IHBvb2wubWluLFxuICBtYXg6IHBvb2wubWF4LFxuICBhbGw6IHBvb2wubnVtRnJlZSgpICsgcG9vbC5udW1Vc2VkKCksXG4gIGF2YWlsYWJsZTogcG9vbC5udW1GcmVlKCksXG4gIHVzZWQ6IHBvb2wubnVtVXNlZCgpLFxuICBwZW5kaW5nOiBwb29sLm51bVBlbmRpbmdBY3F1aXJlcygpLFxuICBxdWV1ZUxpbWl0XG59KTtcblxuLyoqXG4gKiBMb2dzIGluZm9ybWF0aW9uIGFib3V0IHRoZSBjdXJyZW50IHN0YXRlIG9mIHRoZSBwb29sLCBpbmNsdWRpbmcgdGhlIG1pbmltdW1cbiAqIGFuZCBtYXhpbXVtIHdvcmtlcnMsIGF2YWlsYWJsZSB3b3JrZXJzLCB3b3JrZXJzIGluIHVzZSwgYW5kIHBlbmRpbmcgYWNxdWlyZVxuICogcmVxdWVzdHMuXG4gKi9cbmV4cG9ydCBmdW5jdGlvbiBnZXRQb29sSW5mbygpIHtcbiAgY29uc3QgeyBtaW4sIG1heCwgYWxsLCBhdmFpbGFibGUsIHVzZWQsIHBlbmRpbmcgfSA9IGdldFBvb2xJbmZvSlNPTigpO1xuXG4gIGxvZyg1LCBgW3Bvb2xdIFRoZSBtaW5pbXVtIG51bWJlciBvZiByZXNvdXJjZXMgYWxsb3dlZCBieSBwb29sOiAke21pbn0uYCk7XG4gIGxvZyg1LCBgW3Bvb2xdIFRoZSBtYXhpbXVtIG51bWJlciBvZiByZXNvdXJjZXMgYWxsb3dlZCBieSBwb29sOiAke21heH0uYCk7XG4gIGxvZyg1LCBgW3Bvb2xdIFRoZSBudW1iZXIgb2YgYWxsIGNyZWF0ZWQgcmVzb3VyY2VzOiAke2FsbH0uYCk7XG4gIGxvZyg1LCBgW3Bvb2xdIFRoZSBudW1iZXIgb2YgYXZhaWxhYmxlIHJlc291cmNlczogJHthdmFpbGFibGV9LmApO1xuICBsb2coNSwgYFtwb29sXSBUaGUgbnVtYmVyIG9mIGFjcXVpcmVkIHJlc291cmNlczogJHt1c2VkfS5gKTtcbiAgbG9nKDUsIGBbcG9vbF0gVGhlIG51bWJlciBvZiByZXNvdXJjZXMgd2FpdGluZyB0byBiZSBhY3F1aXJlZDogJHtwZW5kaW5nfS5gKTtcbn1cblxuZXhwb3J0IGRlZmF1bHQge1xuICBpbml0UG9vbCxcbiAga2lsbFBvb2wsXG4gIHBvc3RXb3JrLFxuICBnZXRQb29sLFxuICBnZXRQb29sSW5mbyxcbiAgZ2V0UG9vbEluZm9KU09OLFxuICBnZXRRdWV1ZUxpbWl0LFxuICBnZXRDb25zZWN1dGl2ZUNyZWF0ZUZhaWx1cmVzLFxuICBnZXRTdGF0czogKCkgPT4gc3RhdHNcbn07XG4iLCIvKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKlxuXG5IaWdoY2hhcnRzIEV4cG9ydCBTZXJ2ZXJcblxuQ29weXJpZ2h0IChjKSAyMDE2LTIwMjQsIEhpZ2hzb2Z0XG5cbkxpY2VuY2VkIHVuZGVyIHRoZSBNSVQgbGljZW5jZS5cblxuQWRkaXRpb25hbGx5IGEgdmFsaWQgSGlnaGNoYXJ0cyBsaWNlbnNlIGlzIHJlcXVpcmVkIGZvciB1c2UuXG5cblNlZSBMSUNFTlNFIGZpbGUgaW4gcm9vdCBmb3IgZGV0YWlscy5cblxuKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKi9cblxuLyoqXG4gKiBAb3ZlcnZpZXcgVXNlZCB0byBzYW5pdGl6ZSB0aGUgc3RyaW5ncyBjb21pbmcgZnJvbSB0aGUgZXhwb3J0aW5nIG1vZHVsZVxuICogdG8gcHJldmVudCBYU1MgYXR0YWNrcyAod2l0aCB0aGUgRE9NUHVyaWZ5IGxpYnJhcnkpLlxuICoqL1xuXG5pbXBvcnQgeyBKU0RPTSB9IGZyb20gJ2pzZG9tJztcbmltcG9ydCBET01QdXJpZnkgZnJvbSAnZG9tcHVyaWZ5JztcblxuaW1wb3J0IHsgZW52cyB9IGZyb20gJy4vZW52cy5qcyc7XG5cbi8vIFRoZSBwdXJpZmllciwgYnVpbHQgb24gZmlyc3QgdXNlIGFuZCB0aGVuIHJldXNlZC5cbi8vXG4vLyBOT1RFOiBCdWlsZGluZyBhIERPTSBhbmQgYSBwdXJpZmllciBwZXIgY2FsbCBpcyBieSBmYXIgdGhlIG1vc3QgZXhwZW5zaXZlIHBhcnRcbi8vICAgICAgIG9mIHNhbml0aXppbmcsIGFuZCB0aGlzIHJ1bnMgb24gZXZlcnkgU1ZHIGV4cG9ydC4gSXQgaXMgYWxzbyBzeW5jaHJvbm91cyxcbi8vICAgICAgIHNvIHRoZSBjb3N0IGlzIHBhaWQgb24gdGhlIGV2ZW50IGxvb3AgYW5kIGRlbGF5cyBldmVyeSBvdGhlciByZXF1ZXN0IGluXG4vLyAgICAgICBmbGlnaHQsIG5vdCBqdXN0IHRoaXMgb25lLlxuLy9cbi8vICAgICAgIE9ubHkgdGhlIGluc3RhbmNlIGlzIHNoYXJlZC4gVGhlIG9wdGlvbnMgc3RheSBwZXIgY2FsbCwgc2luY2UgRk9SQklEX0FUVFJcbi8vICAgICAgIGRlcGVuZHMgb24gY29uZmlndXJhdGlvbiB0aGF0IGNhbiBiZSByZWFkIGF0IGFueSB0aW1lLCBhbmQgRE9NUHVyaWZ5XG4vLyAgICAgICBhcHBsaWVzIHRoZSBvcHRpb25zIGl0IGlzIGdpdmVuIG9uIGVhY2ggY2FsbC5cbmxldCBwdXJpZmllcjtcblxuLyoqXG4gKiBSZXR1cm5zIHRoZSBzaGFyZWQgcHVyaWZpZXIsIGJ1aWxkaW5nIGl0IGlmIHRoaXMgaXMgdGhlIGZpcnN0IGNhbGwuXG4gKlxuICogQHJldHVybnMge09iamVjdH0gVGhlIERPTVB1cmlmeSBpbnN0YW5jZS5cbiAqL1xuZnVuY3Rpb24gZ2V0UHVyaWZpZXIoKSB7XG4gIGlmICghcHVyaWZpZXIpIHtcbiAgICBwdXJpZmllciA9IERPTVB1cmlmeShuZXcgSlNET00oJycpLndpbmRvdyk7XG4gIH1cblxuICByZXR1cm4gcHVyaWZpZXI7XG59XG5cbi8qKlxuICogU2FuaXRpemVzIGEgZ2l2ZW4gSFRNTCBzdHJpbmcgYnkgcmVtb3ZpbmcgPHNjcmlwdD4gdGFncy5cbiAqIFRoaXMgZnVuY3Rpb24gdXNlcyBhIHJlZ3VsYXIgZXhwcmVzc2lvbiB0byBmaW5kIGFuZCByZW1vdmUgYWxsXG4gKiBvY2N1cnJlbmNlcyBvZiA8c2NyaXB0Pi4uLjwvc2NyaXB0PiB0YWdzIGFuZCBhbnkgY29udGVudCB3aXRoaW4gdGhlbS5cbiAqXG4gKiBAcGFyYW0ge3N0cmluZ30gaW5wdXQgVGhlIEhUTUwgc3RyaW5nIHRvIGJlIHNhbml0aXplZC5cbiAqIEByZXR1cm5zIHtzdHJpbmd9IFRoZSBzYW5pdGl6ZWQgSFRNTCBzdHJpbmcuXG4gKi9cbmV4cG9ydCBmdW5jdGlvbiBzYW5pdGl6ZShpbnB1dCkge1xuICBjb25zdCBmb3JiaWRkZW4gPSBbXTtcblxuICBpZiAoIWVudnMuT1RIRVJfQUxMT1dfWExJTkspIHtcbiAgICBmb3JiaWRkZW4ucHVzaCgneGxpbms6aHJlZicpO1xuICB9XG5cbiAgcmV0dXJuIGdldFB1cmlmaWVyKCkuc2FuaXRpemUoaW5wdXQsIHtcbiAgICBBRERfVEFHUzogWydmb3JlaWduT2JqZWN0J10sXG4gICAgRk9SQklEX0FUVFI6IGZvcmJpZGRlbixcbiAgICBIVE1MX0lOVEVHUkFUSU9OX1BPSU5UUzogeyBmb3JlaWdub2JqZWN0OiB0cnVlIH1cbiAgfSk7XG59XG5cbmV4cG9ydCBkZWZhdWx0IHNhbml0aXplO1xuIiwiLyoqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKipcblxuSGlnaGNoYXJ0cyBFeHBvcnQgU2VydmVyXG5cbkNvcHlyaWdodCAoYykgMjAxNi0yMDI0LCBIaWdoc29mdFxuXG5MaWNlbmNlZCB1bmRlciB0aGUgTUlUIGxpY2VuY2UuXG5cbkFkZGl0aW9uYWxseSBhIHZhbGlkIEhpZ2hjaGFydHMgbGljZW5zZSBpcyByZXF1aXJlZCBmb3IgdXNlLlxuXG5TZWUgTElDRU5TRSBmaWxlIGluIHJvb3QgZm9yIGRldGFpbHMuXG5cbioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKiovXG5cbmltcG9ydCB7IHJlYWRGaWxlU3luYywgd3JpdGVGaWxlU3luYyB9IGZyb20gJ2ZzJztcblxuaW1wb3J0IHsgZ2V0T3B0aW9ucywgaW5pdEV4cG9ydFNldHRpbmdzIH0gZnJvbSAnLi9jb25maWcuanMnO1xuaW1wb3J0IHsgbG9nLCBsb2dXaXRoU3RhY2sgfSBmcm9tICcuL2xvZ2dlci5qcyc7XG5pbXBvcnQgeyBraWxsUG9vbCwgcG9zdFdvcmssIHN0YXRzIH0gZnJvbSAnLi9wb29sLmpzJztcbmltcG9ydCB7XG4gIGZpeFR5cGUsXG4gIGhhbmRsZVJlc291cmNlcyxcbiAgaXNDb3JyZWN0SlNPTixcbiAgb3B0aW9uc1N0cmluZ2lmeSxcbiAgcm91bmROdW1iZXIsXG4gIHRvQm9vbGVhbixcbiAgd3JhcEFyb3VuZFxufSBmcm9tICcuL3V0aWxzLmpzJztcbmltcG9ydCB7IHNhbml0aXplIH0gZnJvbSAnLi9zYW5pdGl6ZS5qcyc7XG5pbXBvcnQgRXhwb3J0RXJyb3IgZnJvbSAnLi9lcnJvcnMvRXhwb3J0RXJyb3IuanMnO1xuXG5sZXQgYWxsb3dDb2RlRXhlY3V0aW9uID0gZmFsc2U7XG5cbi8qKlxuICogU3RhcnRzIGFuIGV4cG9ydCBwcm9jZXNzLiBUaGUgYHNldHRpbmdzYCBjb250YWlucyBmaW5hbCBvcHRpb25zIGdhdGhlcmVkXG4gKiBmcm9tIGFsbCBwb3NzaWJsZSBzb3VyY2VzIChjb25maWcsIGVudiwgY2xpLCBqc29uKS4gVGhlIGBlbmRDYWxsYmFja2AgaXNcbiAqIGNhbGxlZCB3aGVuIHRoZSBleHBvcnQgaXMgY29tcGxldGVkLCB3aXRoIGFuIGVycm9yIG9iamVjdCBhcyB0aGUgZmlyc3RcbiAqIGFyZ3VtZW50IGFuZCB0aGUgc2Vjb25kIGNvbnRhaW5pbmcgdGhlIGJhc2U2NCByZXNwcmVzZW50YXRpb24gb2YgYSBjaGFydC5cbiAqXG4gKiBAcGFyYW0ge09iamVjdH0gc2V0dGluZ3MgLSBUaGUgc2V0dGluZ3Mgb2JqZWN0IGNvbnRhaW5pbmcgZXhwb3J0XG4gKiBjb25maWd1cmF0aW9uLlxuICogQHBhcmFtIHtmdW5jdGlvbn0gZW5kQ2FsbGJhY2sgLSBUaGUgY2FsbGJhY2sgZnVuY3Rpb24gdG8gYmUgaW52b2tlZCB1cG9uXG4gKiBmaW5hbGl6aW5nIHdvcmsgb3IgdXBvbiBlcnJvciBvY2N1cmFuY2Ugb2YgdGhlIGV4cG9ydGluZyBwcm9jZXNzLlxuICpcbiAqIEByZXR1cm5zIHt2b2lkfSBUaGlzIGZ1bmN0aW9uIGRvZXMgbm90IHJldHVybiBhIHZhbHVlIGRpcmVjdGx5OyBpbnN0ZWFkLFxuICogaXQgY29tbXVuaWNhdGVzIHJlc3VsdHMgdmlhIHRoZSBlbmRDYWxsYmFjay5cbiAqL1xuZXhwb3J0IGNvbnN0IHN0YXJ0RXhwb3J0ID0gYXN5bmMgKHNldHRpbmdzLCBlbmRDYWxsYmFjaykgPT4ge1xuICAvLyBTdGFydGluZyBleHBvcnRpbmcgcHJvY2VzcyBtZXNzYWdlXG4gIGxvZyg0LCAnW2NoYXJ0XSBTdGFydGluZyB0aGUgZXhwb3J0aW5nIHByb2Nlc3MuJyk7XG5cbiAgLy8gSW5pdGlhbGl6ZSBvcHRpb25zXG4gIGNvbnN0IG9wdGlvbnMgPSBpbml0RXhwb3J0U2V0dGluZ3Moc2V0dGluZ3MsIGdldE9wdGlvbnMoKSk7XG5cbiAgLy8gR2V0IHRoZSBleHBvcnQgb3B0aW9uc1xuICBjb25zdCBleHBvcnRPcHRpb25zID0gb3B0aW9ucy5leHBvcnQ7XG5cbiAgLy8gSWYgU1ZHIGlzIGFuIGlucHV0IChhcmd1bWVudCBjYW4gYmUgc2VudCBvbmx5IGJ5IHRoZSByZXF1ZXN0KVxuICBpZiAob3B0aW9ucy5wYXlsb2FkPy5zdmcgJiYgb3B0aW9ucy5wYXlsb2FkLnN2ZyAhPT0gJycpIHtcbiAgICB0cnkge1xuICAgICAgbG9nKDQsICdbY2hhcnRdIEF0dGVtcHRpbmcgdG8gZXhwb3J0IGZyb20gYSBTVkcgaW5wdXQuJyk7XG5cbiAgICAgIGNvbnN0IHJlc3VsdCA9IGV4cG9ydEFzU3RyaW5nKFxuICAgICAgICBzYW5pdGl6ZShvcHRpb25zLnBheWxvYWQuc3ZnKSwgLy8gIzIwOVxuICAgICAgICBvcHRpb25zLFxuICAgICAgICBlbmRDYWxsYmFja1xuICAgICAgKTtcblxuICAgICAgKytzdGF0cy5leHBvcnRGcm9tU3ZnQXR0ZW1wdHM7XG4gICAgICByZXR1cm4gcmVzdWx0O1xuICAgIH0gY2F0Y2ggKGVycm9yKSB7XG4gICAgICByZXR1cm4gZW5kQ2FsbGJhY2soXG4gICAgICAgIG5ldyBFeHBvcnRFcnJvcignW2NoYXJ0XSBFcnJvciBsb2FkaW5nIFNWRyBpbnB1dC4nKS5zZXRFcnJvcihlcnJvcilcbiAgICAgICk7XG4gICAgfVxuICB9XG5cbiAgLy8gRXhwb3J0IHVzaW5nIG9wdGlvbnMgZnJvbSB0aGUgZmlsZVxuICBpZiAoZXhwb3J0T3B0aW9ucy5pbmZpbGUgJiYgZXhwb3J0T3B0aW9ucy5pbmZpbGUubGVuZ3RoKSB7XG4gICAgLy8gVHJ5IHRvIHJlYWQgdGhlIGZpbGUgdG8gZ2V0IHRoZSBzdHJpbmcgcmVwcmVzZW50YXRpb25cbiAgICB0cnkge1xuICAgICAgbG9nKDQsICdbY2hhcnRdIEF0dGVtcHRpbmcgdG8gZXhwb3J0IGZyb20gYW4gaW5wdXQgZmlsZS4nKTtcbiAgICAgIG9wdGlvbnMuZXhwb3J0Lmluc3RyID0gcmVhZEZpbGVTeW5jKGV4cG9ydE9wdGlvbnMuaW5maWxlLCAndXRmOCcpO1xuICAgICAgcmV0dXJuIGV4cG9ydEFzU3RyaW5nKG9wdGlvbnMuZXhwb3J0Lmluc3RyLnRyaW0oKSwgb3B0aW9ucywgZW5kQ2FsbGJhY2spO1xuICAgIH0gY2F0Y2ggKGVycm9yKSB7XG4gICAgICByZXR1cm4gZW5kQ2FsbGJhY2soXG4gICAgICAgIG5ldyBFeHBvcnRFcnJvcignW2NoYXJ0XSBFcnJvciBsb2FkaW5nIGlucHV0IGZpbGUuJykuc2V0RXJyb3IoZXJyb3IpXG4gICAgICApO1xuICAgIH1cbiAgfVxuXG4gIC8vIEV4cG9ydCB3aXRoIG9wdGlvbnMgZnJvbSB0aGUgcmF3IHJlcHJlc2VudGF0aW9uXG4gIGlmIChcbiAgICAoZXhwb3J0T3B0aW9ucy5pbnN0ciAmJiBleHBvcnRPcHRpb25zLmluc3RyICE9PSAnJykgfHxcbiAgICAoZXhwb3J0T3B0aW9ucy5vcHRpb25zICYmIGV4cG9ydE9wdGlvbnMub3B0aW9ucyAhPT0gJycpXG4gICkge1xuICAgIHRyeSB7XG4gICAgICBsb2coNCwgJ1tjaGFydF0gQXR0ZW1wdGluZyB0byBleHBvcnQgZnJvbSBhIHJhdyBpbnB1dC4nKTtcblxuICAgICAgLy8gVXNlIHdoaWNoZXZlciBvbmUgaXMgYXZhaWxhYmxlXG4gICAgICBleHBvcnRPcHRpb25zLmluc3RyID0gZXhwb3J0T3B0aW9ucy5pbnN0ciB8fCBleHBvcnRPcHRpb25zLm9wdGlvbnM7XG5cbiAgICAgIC8vIFBlcmZvcm0gYSBkaXJlY3QgaW5qZWN0IHdoZW4gZm9yY2VkXG4gICAgICBpZiAodG9Cb29sZWFuKG9wdGlvbnMuY3VzdG9tTG9naWM/LmFsbG93Q29kZUV4ZWN1dGlvbikpIHtcbiAgICAgICAgcmV0dXJuIGRvU3RyYWlnaHRJbmplY3Qob3B0aW9ucywgZW5kQ2FsbGJhY2spO1xuICAgICAgfVxuXG4gICAgICAvLyBFaXRoZXIgdHJ5IHRvIHBhcnNlIHRvIEpTT04gZmlyc3Qgb3IgZG8gdGhlIGRpcmVjdCBleHBvcnRcbiAgICAgIHJldHVybiB0eXBlb2YgZXhwb3J0T3B0aW9ucy5pbnN0ciA9PT0gJ3N0cmluZydcbiAgICAgICAgPyBleHBvcnRBc1N0cmluZyhleHBvcnRPcHRpb25zLmluc3RyLnRyaW0oKSwgb3B0aW9ucywgZW5kQ2FsbGJhY2spXG4gICAgICAgIDogZG9FeHBvcnQoXG4gICAgICAgICAgICBvcHRpb25zLFxuICAgICAgICAgICAgZXhwb3J0T3B0aW9ucy5pbnN0ciB8fCBleHBvcnRPcHRpb25zLm9wdGlvbnMsXG4gICAgICAgICAgICBlbmRDYWxsYmFja1xuICAgICAgICAgICk7XG4gICAgfSBjYXRjaCAoZXJyb3IpIHtcbiAgICAgIHJldHVybiBlbmRDYWxsYmFjayhcbiAgICAgICAgbmV3IEV4cG9ydEVycm9yKCdbY2hhcnRdIEVycm9yIGxvYWRpbmcgcmF3IGlucHV0LicpLnNldEVycm9yKGVycm9yKVxuICAgICAgKTtcbiAgICB9XG4gIH1cblxuICAvLyBObyBpbnB1dCBzcGVjaWZpZWQsIHBhc3MgYW4gZXJyb3IgbWVzc2FnZSB0byB0aGUgY2FsbGJhY2tcbiAgcmV0dXJuIGVuZENhbGxiYWNrKFxuICAgIG5ldyBFeHBvcnRFcnJvcihcbiAgICAgIGBbY2hhcnRdIE5vIHZhbGlkIGlucHV0IHNwZWNpZmllZC4gQ2hlY2sgaWYgYXQgbGVhc3Qgb25lIG9mIHRoZSBmb2xsb3dpbmcgcGFyYW1ldGVycyBpcyBjb3JyZWN0bHkgc2V0OiAnaW5maWxlJywgJ2luc3RyJywgJ29wdGlvbnMnLCBvciAnc3ZnJy5gXG4gICAgKVxuICApO1xufTtcblxuLyoqXG4gKiBTdGFydHMgYSBiYXRjaCBleHBvcnQgcHJvY2VzcyBmb3IgbXVsdGlwbGUgY2hhcnRzIGJhc2VkIG9uIHRoZSBpbmZvcm1hdGlvblxuICogaW4gdGhlIGJhdGNoIG9wdGlvbi4gVGhlIGJhdGNoIGlzIGEgc3RyaW5nIGluIHRoZSBmb2xsb3dpbmcgZm9ybWF0OlxuICogXCJpbmZpbGUxLmpzb249b3V0ZmlsZTEucG5nO2luZmlsZTIuanNvbj1vdXRmaWxlMi5wbmc7Li4uXCJcbiAqXG4gKiBAcGFyYW0ge09iamVjdH0gb3B0aW9ucyAtIFRoZSBvcHRpb25zIG9iamVjdCBjb250YWluaW5nIGNvbmZpZ3VyYXRpb24gZm9yXG4gKiBhIGJhdGNoIGV4cG9ydC5cbiAqXG4gKiBAcmV0dXJucyB7UHJvbWlzZTx2b2lkPn0gQSBQcm9taXNlIHRoYXQgcmVzb2x2ZXMgb25jZSB0aGUgYmF0Y2ggZXhwb3J0XG4gKiBwcm9jZXNzIGlzIGNvbXBsZXRlZC5cbiAqXG4gKiBAdGhyb3dzIHtFeHBvcnRFcnJvcn0gVGhyb3dzIGFuIEV4cG9ydEVycm9yIGlmIGFuIGVycm9yIG9jY3VycyBkdXJpbmdcbiAqIGFueSBvZiB0aGUgYmF0Y2ggZXhwb3J0IHByb2Nlc3MuXG4gKi9cbmV4cG9ydCBjb25zdCBiYXRjaEV4cG9ydCA9IGFzeW5jIChvcHRpb25zKSA9PiB7XG4gIGNvbnN0IGJhdGNoRnVuY3Rpb25zID0gW107XG5cbiAgLy8gU3BsaXQgYW5kIHBhaXIgdGhlIC0tYmF0Y2ggYXJndW1lbnRzXG4gIGZvciAobGV0IHBhaXIgb2Ygb3B0aW9ucy5leHBvcnQuYmF0Y2guc3BsaXQoJzsnKSkge1xuICAgIHBhaXIgPSBwYWlyLnNwbGl0KCc9Jyk7XG4gICAgaWYgKHBhaXIubGVuZ3RoID09PSAyKSB7XG4gICAgICBiYXRjaEZ1bmN0aW9ucy5wdXNoKFxuICAgICAgICBzdGFydEV4cG9ydChcbiAgICAgICAgICB7XG4gICAgICAgICAgICAuLi5vcHRpb25zLFxuICAgICAgICAgICAgZXhwb3J0OiB7XG4gICAgICAgICAgICAgIC4uLm9wdGlvbnMuZXhwb3J0LFxuICAgICAgICAgICAgICBpbmZpbGU6IHBhaXJbMF0sXG4gICAgICAgICAgICAgIG91dGZpbGU6IHBhaXJbMV1cbiAgICAgICAgICAgIH1cbiAgICAgICAgICB9LFxuICAgICAgICAgIChlcnJvciwgaW5mbykgPT4ge1xuICAgICAgICAgICAgLy8gVGhyb3cgYW4gZXJyb3JcbiAgICAgICAgICAgIGlmIChlcnJvcikge1xuICAgICAgICAgICAgICB0aHJvdyBlcnJvcjtcbiAgICAgICAgICAgIH1cblxuICAgICAgICAgICAgLy8gU2F2ZSB0aGUgYmFzZTY0IGZyb20gYSBidWZmZXIgdG8gYSBjb3JyZWN0IGltYWdlIGZpbGVcbiAgICAgICAgICAgIHdyaXRlRmlsZVN5bmMoXG4gICAgICAgICAgICAgIGluZm8ub3B0aW9ucy5leHBvcnQub3V0ZmlsZSxcbiAgICAgICAgICAgICAgaW5mby5vcHRpb25zLmV4cG9ydC50eXBlICE9PSAnc3ZnJ1xuICAgICAgICAgICAgICAgID8gQnVmZmVyLmZyb20oaW5mby5yZXN1bHQsICdiYXNlNjQnKVxuICAgICAgICAgICAgICAgIDogaW5mby5yZXN1bHRcbiAgICAgICAgICAgICk7XG4gICAgICAgICAgfVxuICAgICAgICApXG4gICAgICApO1xuICAgIH1cbiAgfVxuXG4gIHRyeSB7XG4gICAgLy8gQXdhaXQgYWxsIGV4cG9ydHMgYXJlIGRvbmVcbiAgICBhd2FpdCBQcm9taXNlLmFsbChiYXRjaEZ1bmN0aW9ucyk7XG5cbiAgICAvLyBLaWxsIHBvb2wgYW5kIGNsb3NlIGJyb3dzZXIgYWZ0ZXIgZmluaXNoaW5nIGJhdGNoIGV4cG9ydFxuICAgIGF3YWl0IGtpbGxQb29sKCk7XG4gIH0gY2F0Y2ggKGVycm9yKSB7XG4gICAgdGhyb3cgbmV3IEV4cG9ydEVycm9yKFxuICAgICAgJ1tjaGFydF0gRXJyb3IgZW5jb3VudGVyZWQgZHVyaW5nIGJhdGNoIGV4cG9ydC4nXG4gICAgKS5zZXRFcnJvcihlcnJvcik7XG4gIH1cbn07XG5cbi8qKlxuICogU3RhcnRzIGEgc2luZ2xlIGV4cG9ydCBwcm9jZXNzIGJhc2VkIG9uIHRoZSBzcGVjaWZpZWQgb3B0aW9ucy5cbiAqXG4gKiBAcGFyYW0ge09iamVjdH0gb3B0aW9ucyAtIFRoZSBvcHRpb25zIG9iamVjdCBjb250YWluaW5nIGNvbmZpZ3VyYXRpb24gZm9yXG4gKiBhIHNpbmdsZSBleHBvcnQuXG4gKlxuICogQHJldHVybnMge1Byb21pc2U8dm9pZD59IEEgUHJvbWlzZSB0aGF0IHJlc29sdmVzIG9uY2UgdGhlIHNpbmdsZSBleHBvcnRcbiAqIHByb2Nlc3MgaXMgY29tcGxldGVkLlxuICpcbiAqIEB0aHJvd3Mge0V4cG9ydEVycm9yfSBUaHJvd3MgYW4gRXhwb3J0RXJyb3IgaWYgYW4gZXJyb3Igb2NjdXJzIGR1cmluZ1xuICogdGhlIHNpbmdsZSBleHBvcnQgcHJvY2Vzcy5cbiAqL1xuZXhwb3J0IGNvbnN0IHNpbmdsZUV4cG9ydCA9IGFzeW5jIChvcHRpb25zKSA9PiB7XG4gIC8vIFVzZSBpbnN0ciBvciBpdHMgYWxpYXMsIG9wdGlvbnNcbiAgb3B0aW9ucy5leHBvcnQuaW5zdHIgPSBvcHRpb25zLmV4cG9ydC5pbnN0ciB8fCBvcHRpb25zLmV4cG9ydC5vcHRpb25zO1xuXG4gIC8vIFBlcmZvcm0gYW4gZXhwb3J0XG4gIGF3YWl0IHN0YXJ0RXhwb3J0KG9wdGlvbnMsIGFzeW5jIChlcnJvciwgaW5mbykgPT4ge1xuICAgIC8vIEV4aXQgcHJvY2VzcyB3aGVuIGVycm9yXG4gICAgaWYgKGVycm9yKSB7XG4gICAgICB0aHJvdyBlcnJvcjtcbiAgICB9XG5cbiAgICBjb25zdCB7IG91dGZpbGUsIHR5cGUgfSA9IGluZm8ub3B0aW9ucy5leHBvcnQ7XG5cbiAgICAvLyBTYXZlIHRoZSBiYXNlNjQgZnJvbSBhIGJ1ZmZlciB0byBhIGNvcnJlY3QgaW1hZ2UgZmlsZVxuICAgIHdyaXRlRmlsZVN5bmMoXG4gICAgICBvdXRmaWxlIHx8IGBjaGFydC4ke3R5cGV9YCxcbiAgICAgIHR5cGUgIT09ICdzdmcnID8gQnVmZmVyLmZyb20oaW5mby5yZXN1bHQsICdiYXNlNjQnKSA6IGluZm8ucmVzdWx0XG4gICAgKTtcblxuICAgIC8vIEtpbGwgcG9vbCBhbmQgY2xvc2UgYnJvd3NlciBhZnRlciBmaW5pc2hpbmcgc2luZ2xlIGV4cG9ydFxuICAgIGF3YWl0IGtpbGxQb29sKCk7XG4gIH0pO1xufTtcblxuLyoqXG4gKiBEZXRlcm1pbmVzIHRoZSBzaXplIGFuZCBzY2FsZSBmb3IgY2hhcnQgZXhwb3J0IGJhc2VkIG9uIHRoZSBwcm92aWRlZCBvcHRpb25zLlxuICpcbiAqIEBwYXJhbSB7T2JqZWN0fSBvcHRpb25zIC0gVGhlIG9wdGlvbnMgb2JqZWN0IGNvbnRhaW5pbmcgY29uZmlndXJhdGlvbiBmb3JcbiAqIGNoYXJ0IGV4cG9ydC5cbiAqXG4gKiBAcmV0dXJucyB7T2JqZWN0fSBBbiBvYmplY3QgY29udGFpbmluZyB0aGUgY2FsY3VsYXRlZCBoZWlnaHQsIHdpZHRoLFxuICogYW5kIHNjYWxlIGZvciB0aGUgY2hhcnQgZXhwb3J0LlxuICovXG5leHBvcnQgY29uc3QgZmluZENoYXJ0U2l6ZSA9IChvcHRpb25zKSA9PiB7XG4gIGNvbnN0IHsgY2hhcnQsIGV4cG9ydGluZyB9ID1cbiAgICBvcHRpb25zLmV4cG9ydD8ub3B0aW9ucyB8fCBpc0NvcnJlY3RKU09OKG9wdGlvbnMuZXhwb3J0Py5pbnN0cik7XG5cbiAgLy8gU2VlIGlmIGdsb2JhbE9wdGlvbnMgaG9sZHMgY2hhcnQgb3IgZXhwb3J0aW5nIHNpemVcbiAgY29uc3QgZ2xvYmFsT3B0aW9ucyA9IGlzQ29ycmVjdEpTT04ob3B0aW9ucy5leHBvcnQ/Lmdsb2JhbE9wdGlvbnMpO1xuXG4gIC8vIFNlY3VyZSBzY2FsZSB2YWx1ZVxuICBsZXQgc2NhbGUgPVxuICAgIG9wdGlvbnMuZXhwb3J0Py5zY2FsZSB8fFxuICAgIGV4cG9ydGluZz8uc2NhbGUgfHxcbiAgICBnbG9iYWxPcHRpb25zPy5leHBvcnRpbmc/LnNjYWxlIHx8XG4gICAgb3B0aW9ucy5leHBvcnQ/LmRlZmF1bHRTY2FsZSB8fFxuICAgIDE7XG5cbiAgLy8gdGhlIHNjYWxlIGNhbm5vdCBiZSBsb3dlciB0aGFuIDAuMSBhbmQgY2Fubm90IGJlIGhpZ2hlciB0aGFuIDUuMFxuICBzY2FsZSA9IE1hdGgubWF4KDAuMSwgTWF0aC5taW4oc2NhbGUsIDUuMCkpO1xuXG4gIC8vIHdlIHdhbnQgdG8gcm91bmQgdGhlIG51bWJlcnMgbGlrZSAwLjIzMjM0IC0+IDAuMjNcbiAgc2NhbGUgPSByb3VuZE51bWJlcihzY2FsZSwgMik7XG5cbiAgLy8gRmluZCBjaGFydCBzaXplIGFuZCBzY2FsZVxuICBjb25zdCBzaXplID0ge1xuICAgIGhlaWdodDpcbiAgICAgIG9wdGlvbnMuZXhwb3J0Py5oZWlnaHQgfHxcbiAgICAgIGV4cG9ydGluZz8uc291cmNlSGVpZ2h0IHx8XG4gICAgICBjaGFydD8uaGVpZ2h0IHx8XG4gICAgICBnbG9iYWxPcHRpb25zPy5leHBvcnRpbmc/LnNvdXJjZUhlaWdodCB8fFxuICAgICAgZ2xvYmFsT3B0aW9ucz8uY2hhcnQ/LmhlaWdodCB8fFxuICAgICAgb3B0aW9ucy5leHBvcnQ/LmRlZmF1bHRIZWlnaHQgfHxcbiAgICAgIDQwMCxcbiAgICB3aWR0aDpcbiAgICAgIG9wdGlvbnMuZXhwb3J0Py53aWR0aCB8fFxuICAgICAgZXhwb3J0aW5nPy5zb3VyY2VXaWR0aCB8fFxuICAgICAgY2hhcnQ/LndpZHRoIHx8XG4gICAgICBnbG9iYWxPcHRpb25zPy5leHBvcnRpbmc/LnNvdXJjZVdpZHRoIHx8XG4gICAgICBnbG9iYWxPcHRpb25zPy5jaGFydD8ud2lkdGggfHxcbiAgICAgIG9wdGlvbnMuZXhwb3J0Py5kZWZhdWx0V2lkdGggfHxcbiAgICAgIDYwMCxcbiAgICBzY2FsZVxuICB9O1xuXG4gIC8vIEdldCByaWQgb2YgcG90ZW50aWFsIHB4IGFuZCAlXG4gIGZvciAobGV0IFtwYXJhbSwgdmFsdWVdIG9mIE9iamVjdC5lbnRyaWVzKHNpemUpKSB7XG4gICAgc2l6ZVtwYXJhbV0gPVxuICAgICAgdHlwZW9mIHZhbHVlID09PSAnc3RyaW5nJyA/ICt2YWx1ZS5yZXBsYWNlKC9weHwlL2dpLCAnJykgOiB2YWx1ZTtcbiAgfVxuICByZXR1cm4gc2l6ZTtcbn07XG5cbi8qKlxuICogRnVuY3Rpb24gZm9yIGZpbmFsaXppbmcgb3B0aW9ucyBiZWZvcmUgZXhwb3J0LlxuICpcbiAqIEBwYXJhbSB7T2JqZWN0fSBvcHRpb25zIC0gVGhlIG9wdGlvbnMgb2JqZWN0IGNvbnRhaW5pbmcgY29uZmlndXJhdGlvbiBmb3JcbiAqIHRoZSBleHBvcnQgcHJvY2Vzcy5cbiAqIEBwYXJhbSB7T2JqZWN0fSBjaGFydEpzb24gLSBUaGUgSlNPTiByZXByZXNlbnRhdGlvbiBvZiB0aGUgY2hhcnQuXG4gKiBAcGFyYW0ge0Z1bmN0aW9ufSBlbmRDYWxsYmFjayAtIFRoZSBjYWxsYmFjayBmdW5jdGlvbiB0byBiZSBjYWxsZWQgdXBvblxuICogY29tcGxldGlvbiBvciBlcnJvci5cbiAqIEBwYXJhbSB7c3RyaW5nfSBzdmcgLSBUaGUgU1ZHIHJlcHJlc2VudGF0aW9uIG9mIHRoZSBjaGFydC5cbiAqXG4gKiBAcmV0dXJucyB7UHJvbWlzZTx2b2lkPn0gQSBQcm9taXNlIHRoYXQgcmVzb2x2ZXMgb25jZSB0aGUgZXhwb3J0IHByb2Nlc3NcbiAqIGlzIGNvbXBsZXRlZC5cbiAqL1xuY29uc3QgZG9FeHBvcnQgPSBhc3luYyAob3B0aW9ucywgY2hhcnRKc29uLCBlbmRDYWxsYmFjaywgc3ZnKSA9PiB7XG4gIGxldCB7IGV4cG9ydDogZXhwb3J0T3B0aW9ucywgY3VzdG9tTG9naWM6IGN1c3RvbUxvZ2ljT3B0aW9ucyB9ID0gb3B0aW9ucztcblxuICBjb25zdCBhbGxvd0NvZGVFeGVjdXRpb25TY29wZWQgPVxuICAgIHR5cGVvZiBjdXN0b21Mb2dpY09wdGlvbnMuYWxsb3dDb2RlRXhlY3V0aW9uID09PSAnYm9vbGVhbidcbiAgICAgID8gY3VzdG9tTG9naWNPcHRpb25zLmFsbG93Q29kZUV4ZWN1dGlvblxuICAgICAgOiBhbGxvd0NvZGVFeGVjdXRpb247XG5cbiAgaWYgKCFjdXN0b21Mb2dpY09wdGlvbnMpIHtcbiAgICBjdXN0b21Mb2dpY09wdGlvbnMgPSBvcHRpb25zLmN1c3RvbUxvZ2ljID0ge307XG4gIH0gZWxzZSBpZiAoYWxsb3dDb2RlRXhlY3V0aW9uU2NvcGVkKSB7XG4gICAgaWYgKHR5cGVvZiBvcHRpb25zLmN1c3RvbUxvZ2ljLnJlc291cmNlcyA9PT0gJ3N0cmluZycpIHtcbiAgICAgIC8vIFByb2Nlc3MgcmVzb3VyY2VzXG4gICAgICBvcHRpb25zLmN1c3RvbUxvZ2ljLnJlc291cmNlcyA9IGhhbmRsZVJlc291cmNlcyhcbiAgICAgICAgb3B0aW9ucy5jdXN0b21Mb2dpYy5yZXNvdXJjZXMsXG4gICAgICAgIHRvQm9vbGVhbihvcHRpb25zLmN1c3RvbUxvZ2ljLmFsbG93RmlsZVJlc291cmNlcylcbiAgICAgICk7XG4gICAgfSBlbHNlIGlmICghb3B0aW9ucy5jdXN0b21Mb2dpYy5yZXNvdXJjZXMpIHtcbiAgICAgIHRyeSB7XG4gICAgICAgIGNvbnN0IHJlc291cmNlcyA9IHJlYWRGaWxlU3luYygncmVzb3VyY2VzLmpzb24nLCAndXRmOCcpO1xuICAgICAgICBvcHRpb25zLmN1c3RvbUxvZ2ljLnJlc291cmNlcyA9IGhhbmRsZVJlc291cmNlcyhcbiAgICAgICAgICByZXNvdXJjZXMsXG4gICAgICAgICAgdG9Cb29sZWFuKG9wdGlvbnMuY3VzdG9tTG9naWMuYWxsb3dGaWxlUmVzb3VyY2VzKVxuICAgICAgICApO1xuICAgICAgfSBjYXRjaCAoZXJyb3IpIHtcbiAgICAgICAgbG9nKDIsIGBbY2hhcnRdIFVuYWJsZSB0byBsb2FkIHRoZSBkZWZhdWx0IHJlc291cmNlcy5qc29uIGZpbGUuYCk7XG4gICAgICB9XG4gICAgfVxuICB9XG5cbiAgLy8gSWYgdGhlIGFsbG93Q29kZUV4ZWN1dGlvbiBmbGFnIGlzbid0IHNldCwgd2Ugc2hvdWxkIHJlZnVzZSB0aGUgdXNhZ2VcbiAgLy8gb2YgY2FsbGJhY2ssIHJlc291cmNlcywgYW5kIGN1c3RvbSBjb2RlLiBBZGRpdGlvbmFsbHksIHRoZSB3b3JrZXIgd2lsbFxuICAvLyByZWZ1c2UgdG8gcnVuIGFyYml0cmFyeSBKYXZhU2NyaXB0LiBQcmlvcml0aXplZCBzaG91bGQgYmUgdGhlIHNjb3BlZFxuICAvLyBvcHRpb24sIHRoZW4gd2Ugc2hvdWxkIHRha2UgYSBsb29rIGF0IHRoZSBvdmVyYWxsIHBvb2wgb3B0aW9uLlxuICBpZiAoIWFsbG93Q29kZUV4ZWN1dGlvblNjb3BlZCAmJiBjdXN0b21Mb2dpY09wdGlvbnMpIHtcbiAgICBpZiAoXG4gICAgICBjdXN0b21Mb2dpY09wdGlvbnMuY2FsbGJhY2sgfHxcbiAgICAgIGN1c3RvbUxvZ2ljT3B0aW9ucy5yZXNvdXJjZXMgfHxcbiAgICAgIGN1c3RvbUxvZ2ljT3B0aW9ucy5jdXN0b21Db2RlXG4gICAgKSB7XG4gICAgICAvLyBTZW5kIGJhY2sgYSBmcmllbmRseSBtZXNzYWdlIHNheWluZyB0aGF0IHRoZSBleHBvcnRlciBkb2VzIG5vdCBzdXBwb3J0XG4gICAgICAvLyB0aGVzZSBzZXR0aW5ncy5cbiAgICAgIHJldHVybiBlbmRDYWxsYmFjayhcbiAgICAgICAgbmV3IEV4cG9ydEVycm9yKFxuICAgICAgICAgIGBbY2hhcnRdIFRoZSAnY2FsbGJhY2snLCAncmVzb3VyY2VzJyBhbmQgJ2N1c3RvbUNvZGUnIG9wdGlvbnMgaGF2ZSBiZWVuIGRpc2FibGVkIGZvciB0aGlzIHNlcnZlci5gXG4gICAgICAgIClcbiAgICAgICk7XG4gICAgfVxuXG4gICAgLy8gUmVzZXQgYWxsIGFkZGl0aW9uYWwgY3VzdG9tIGNvZGVcbiAgICBjdXN0b21Mb2dpY09wdGlvbnMuY2FsbGJhY2sgPSBmYWxzZTtcbiAgICBjdXN0b21Mb2dpY09wdGlvbnMucmVzb3VyY2VzID0gZmFsc2U7XG4gICAgY3VzdG9tTG9naWNPcHRpb25zLmN1c3RvbUNvZGUgPSBmYWxzZTtcbiAgfVxuXG4gIC8vIENsZWFuIHByb3BlcnRpZXMgdG8ga2VlcCBpdCBsZWFuIGFuZCBtZWFuXG4gIGlmIChjaGFydEpzb24pIHtcbiAgICBjaGFydEpzb24uY2hhcnQgPSBjaGFydEpzb24uY2hhcnQgfHwge307XG4gICAgY2hhcnRKc29uLmV4cG9ydGluZyA9IGNoYXJ0SnNvbi5leHBvcnRpbmcgfHwge307XG4gICAgY2hhcnRKc29uLmV4cG9ydGluZy5lbmFibGVkID0gZmFsc2U7XG4gIH1cblxuICBleHBvcnRPcHRpb25zLmNvbnN0ciA9IGV4cG9ydE9wdGlvbnMuY29uc3RyIHx8ICdjaGFydCc7XG4gIGV4cG9ydE9wdGlvbnMudHlwZSA9IGZpeFR5cGUoZXhwb3J0T3B0aW9ucy50eXBlLCBleHBvcnRPcHRpb25zLm91dGZpbGUpO1xuICBpZiAoZXhwb3J0T3B0aW9ucy50eXBlID09PSAnc3ZnJykge1xuICAgIGV4cG9ydE9wdGlvbnMud2lkdGggPSBmYWxzZTtcbiAgfVxuXG4gIC8vIFByZXBhcmUgZ2xvYmFsIGFuZCB0aGVtZSBvcHRpb25zXG4gIFsnZ2xvYmFsT3B0aW9ucycsICd0aGVtZU9wdGlvbnMnXS5mb3JFYWNoKChvcHRpb25zTmFtZSkgPT4ge1xuICAgIHRyeSB7XG4gICAgICBpZiAoZXhwb3J0T3B0aW9ucyAmJiBleHBvcnRPcHRpb25zW29wdGlvbnNOYW1lXSkge1xuICAgICAgICBpZiAoXG4gICAgICAgICAgdHlwZW9mIGV4cG9ydE9wdGlvbnNbb3B0aW9uc05hbWVdID09PSAnc3RyaW5nJyAmJlxuICAgICAgICAgIGV4cG9ydE9wdGlvbnNbb3B0aW9uc05hbWVdLmVuZHNXaXRoKCcuanNvbicpXG4gICAgICAgICkge1xuICAgICAgICAgIGV4cG9ydE9wdGlvbnNbb3B0aW9uc05hbWVdID0gaXNDb3JyZWN0SlNPTihcbiAgICAgICAgICAgIHJlYWRGaWxlU3luYyhleHBvcnRPcHRpb25zW29wdGlvbnNOYW1lXSwgJ3V0ZjgnKSxcbiAgICAgICAgICAgIHRydWVcbiAgICAgICAgICApO1xuICAgICAgICB9IGVsc2Uge1xuICAgICAgICAgIGV4cG9ydE9wdGlvbnNbb3B0aW9uc05hbWVdID0gaXNDb3JyZWN0SlNPTihcbiAgICAgICAgICAgIGV4cG9ydE9wdGlvbnNbb3B0aW9uc05hbWVdLFxuICAgICAgICAgICAgdHJ1ZVxuICAgICAgICAgICk7XG4gICAgICAgIH1cbiAgICAgIH1cbiAgICB9IGNhdGNoIChlcnJvcikge1xuICAgICAgZXhwb3J0T3B0aW9uc1tvcHRpb25zTmFtZV0gPSB7fTtcbiAgICAgIGxvZ1dpdGhTdGFjaygyLCBlcnJvciwgYFtjaGFydF0gVGhlICcke29wdGlvbnNOYW1lfScgY2Fubm90IGJlIGxvYWRlZC5gKTtcbiAgICB9XG4gIH0pO1xuXG4gIC8vIFByZXBhcmUgdGhlIGN1c3RvbUNvZGVcbiAgaWYgKGN1c3RvbUxvZ2ljT3B0aW9ucy5hbGxvd0NvZGVFeGVjdXRpb24pIHtcbiAgICB0cnkge1xuICAgICAgY3VzdG9tTG9naWNPcHRpb25zLmN1c3RvbUNvZGUgPSB3cmFwQXJvdW5kKFxuICAgICAgICBjdXN0b21Mb2dpY09wdGlvbnMuY3VzdG9tQ29kZSxcbiAgICAgICAgY3VzdG9tTG9naWNPcHRpb25zLmFsbG93RmlsZVJlc291cmNlc1xuICAgICAgKTtcbiAgICB9IGNhdGNoIChlcnJvcikge1xuICAgICAgbG9nV2l0aFN0YWNrKDIsIGVycm9yLCBgW2NoYXJ0XSBUaGUgJ2N1c3RvbUNvZGUnIGNhbm5vdCBiZSBsb2FkZWQuYCk7XG4gICAgfVxuICB9XG5cbiAgLy8gR2V0IHRoZSBjYWxsYmFja1xuICBpZiAoXG4gICAgY3VzdG9tTG9naWNPcHRpb25zICYmXG4gICAgY3VzdG9tTG9naWNPcHRpb25zLmNhbGxiYWNrICYmXG4gICAgY3VzdG9tTG9naWNPcHRpb25zLmNhbGxiYWNrPy5pbmRleE9mKCd7JykgPCAwXG4gICkge1xuICAgIC8vIFRoZSBhbGxvd0ZpbGVSZXNvdXJjZXMgaXMgYWx3YXlzIHNldCB0byBmYWxzZSBmb3IgSFRUUCByZXF1ZXN0cyB0byBhdm9pZFxuICAgIC8vIGluamVjdGluZyBhcmJpdHJhcnkgZmlsZXMgZnJvbSB0aGUgZnNcbiAgICBpZiAoY3VzdG9tTG9naWNPcHRpb25zLmFsbG93RmlsZVJlc291cmNlcykge1xuICAgICAgdHJ5IHtcbiAgICAgICAgY3VzdG9tTG9naWNPcHRpb25zLmNhbGxiYWNrID0gcmVhZEZpbGVTeW5jKFxuICAgICAgICAgIGN1c3RvbUxvZ2ljT3B0aW9ucy5jYWxsYmFjayxcbiAgICAgICAgICAndXRmOCdcbiAgICAgICAgKTtcbiAgICAgIH0gY2F0Y2ggKGVycm9yKSB7XG4gICAgICAgIGN1c3RvbUxvZ2ljT3B0aW9ucy5jYWxsYmFjayA9IGZhbHNlO1xuICAgICAgICBsb2dXaXRoU3RhY2soMiwgZXJyb3IsIGBbY2hhcnRdIFRoZSAnY2FsbGJhY2snIGNhbm5vdCBiZSBsb2FkZWQuYCk7XG4gICAgICB9XG4gICAgfSBlbHNlIHtcbiAgICAgIGN1c3RvbUxvZ2ljT3B0aW9ucy5jYWxsYmFjayA9IGZhbHNlO1xuICAgIH1cbiAgfVxuXG4gIC8vIFNpemUgc2VhcmNoXG4gIG9wdGlvbnMuZXhwb3J0ID0ge1xuICAgIC4uLm9wdGlvbnMuZXhwb3J0LFxuICAgIC4uLmZpbmRDaGFydFNpemUob3B0aW9ucylcbiAgfTtcblxuICAvLyBQb3N0IHRoZSB3b3JrIHRvIHRoZSBwb29sXG4gIHRyeSB7XG4gICAgY29uc3QgcmVzdWx0ID0gYXdhaXQgcG9zdFdvcmsoXG4gICAgICBleHBvcnRPcHRpb25zLnN0ckluaiB8fCBjaGFydEpzb24gfHwgc3ZnLFxuICAgICAgb3B0aW9uc1xuICAgICk7XG4gICAgcmV0dXJuIGVuZENhbGxiYWNrKGZhbHNlLCByZXN1bHQpO1xuICB9IGNhdGNoIChlcnJvcikge1xuICAgIHJldHVybiBlbmRDYWxsYmFjayhlcnJvcik7XG4gIH1cbn07XG5cbi8qKlxuICogUGVyZm9ybXMgYSBkaXJlY3QgaW5qZWN0IG9mIG9wdGlvbnMgYmVmb3JlIGV4cG9ydC4gVGhlIGZ1bmN0aW9uIGF0dGVtcHRzXG4gKiB0byBzdHJpbmdpZnkgdGhlIHByb3ZpZGVkIG9wdGlvbnMgYW5kIHJlbW92ZXMgdW5uZWNlc3NhcnkgY2hhcmFjdGVycyxcbiAqIGVuc3VyaW5nIGEgY2xlYW4gYW5kIGZvcm1hdHRlZCBpbnB1dC4gVGhlIHJlc3VsdGluZyBzdHJpbmcgaXMgc2F2ZWQgYXNcbiAqIGEgXCJzdHJpZ2h0IGluamVjdFwiIHN0cmluZyBpbiB0aGUgZXhwb3J0IG9wdGlvbnMuIEl0IHRoZW4gaW52b2tlcyB0aGVcbiAqIGRvRXhwb3J0IGZ1bmN0aW9uIHdpdGggdGhlIHVwZGF0ZWQgb3B0aW9ucy5cbiAqXG4gKiBJTVBPUlRBTlQ6IERhbmdlcm91cyBhbmQgbXVzdCBiZSB1c2VkIGRlbGliZXJhdGVseSBieSBzb21lb25lIHdobyBzZXRzIHVwXG4gKiBhIHNlcnZlciAoc2VlIHRoZSAgLS1hbGxvd0NvZGVFeGVjdXRpb24gb3B0aW9uKS5cbiAqXG4gKiBAcGFyYW0ge09iamVjdH0gb3B0aW9ucyAtIFRoZSBleHBvcnQgb3B0aW9ucyBjb250YWluaW5nIHRoZSBpbnB1dFxuICogdG8gYmUgaW5qZWN0ZWQuXG4gKiBAcGFyYW0ge2Z1bmN0aW9ufSBlbmRDYWxsYmFjayAtIFRoZSBjYWxsYmFjayBmdW5jdGlvbiB0byBiZSBpbnZva2VkXG4gKiBhdCB0aGUgZW5kIG9mIHRoZSBwcm9jZXNzLlxuICpcbiAqIEByZXR1cm5zIHtQcm9taXNlfSBBIFByb21pc2UgdGhhdCByZXNvbHZlcyB3aXRoIHRoZSByZXN1bHQgb2YgdGhlIGV4cG9ydFxuICogb3BlcmF0aW9uIG9yIHJlamVjdHMgd2l0aCBhbiBlcnJvciBpZiBhbnkgaXNzdWVzIG9jY3VyIGR1cmluZyB0aGUgcHJvY2Vzcy5cbiAqL1xuY29uc3QgZG9TdHJhaWdodEluamVjdCA9IChvcHRpb25zLCBlbmRDYWxsYmFjaykgPT4ge1xuICB0cnkge1xuICAgIGxldCBzdHJJbmo7XG4gICAgbGV0IGluc3RyID0gb3B0aW9ucy5leHBvcnQuaW5zdHIgfHwgb3B0aW9ucy5leHBvcnQub3B0aW9ucztcblxuICAgIGlmICh0eXBlb2YgaW5zdHIgIT09ICdzdHJpbmcnKSB7XG4gICAgICAvLyBUcnkgdG8gc3RyaW5naWZ5IG9wdGlvbnNcbiAgICAgIHN0ckluaiA9IGluc3RyID0gb3B0aW9uc1N0cmluZ2lmeShcbiAgICAgICAgaW5zdHIsXG4gICAgICAgIG9wdGlvbnMuY3VzdG9tTG9naWM/LmFsbG93Q29kZUV4ZWN1dGlvblxuICAgICAgKTtcbiAgICB9XG4gICAgc3RySW5qID0gaW5zdHIucmVwbGFjZUFsbCgvXFx0fFxcbnxcXHIvZywgJycpLnRyaW0oKTtcblxuICAgIC8vIEdldCByaWQgb2YgdGhlIDtcbiAgICBpZiAoc3RySW5qW3N0ckluai5sZW5ndGggLSAxXSA9PT0gJzsnKSB7XG4gICAgICBzdHJJbmogPSBzdHJJbmouc3Vic3RyaW5nKDAsIHN0ckluai5sZW5ndGggLSAxKTtcbiAgICB9XG5cbiAgICAvLyBTYXZlIGFzIHN0cmlnaHQgaW5qZWN0IHN0cmluZ1xuICAgIG9wdGlvbnMuZXhwb3J0LnN0ckluaiA9IHN0ckluajtcbiAgICByZXR1cm4gZG9FeHBvcnQob3B0aW9ucywgZmFsc2UsIGVuZENhbGxiYWNrKTtcbiAgfSBjYXRjaCAoZXJyb3IpIHtcbiAgICByZXR1cm4gZW5kQ2FsbGJhY2soXG4gICAgICBuZXcgRXhwb3J0RXJyb3IoXG4gICAgICAgIGBbY2hhcnRdIE1hbGZvcm1lZCBpbnB1dCBkZXRlY3RlZCBmb3IgJHtvcHRpb25zLmV4cG9ydD8ucmVxdWVzdElkIHx8ICc/J30uIFBsZWFzZSBtYWtlIHN1cmUgdGhhdCB5b3VyIEpTT04vSmF2YVNjcmlwdCBvcHRpb25zIGFyZSBzZW50IHVzaW5nIHRoZSBcIm9wdGlvbnNcIiBhdHRyaWJ1dGUsIGFuZCB0aGF0IGlmIHlvdSdyZSB1c2luZyBTVkcsIGl0IGlzIHVuZXNjYXBlZC5gXG4gICAgICApLnNldEVycm9yKGVycm9yKVxuICAgICk7XG4gIH1cbn07XG5cbi8qKlxuICogRXhwb3J0cyBhIHN0cmluZyBiYXNlZCBvbiB0aGUgcHJvdmlkZWQgb3B0aW9ucyBhbmQgaW52b2tlcyBhbiBlbmQgY2FsbGJhY2suXG4gKlxuICogQHBhcmFtIHtzdHJpbmd9IHN0cmluZ1RvRXhwb3J0IC0gVGhlIHN0cmluZyBjb250ZW50IHRvIGJlIGV4cG9ydGVkLlxuICogQHBhcmFtIHtPYmplY3R9IG9wdGlvbnMgLSBFeHBvcnQgb3B0aW9ucywgaW5jbHVkaW5nIGN1c3RvbUxvZ2ljIHdpdGhcbiAqIGFsbG93Q29kZUV4ZWN1dGlvbiBmbGFnLlxuICogQHBhcmFtIHtGdW5jdGlvbn0gZW5kQ2FsbGJhY2sgLSBDYWxsYmFjayBmdW5jdGlvbiB0byBiZSBpbnZva2VkIGF0IHRoZSBlbmRcbiAqIG9mIHRoZSBleHBvcnQgcHJvY2Vzcy5cbiAqXG4gKiBAcmV0dXJucyB7YW55fSBSZXN1bHQgb2YgdGhlIGV4cG9ydCBwcm9jZXNzIG9yIGFuIGVycm9yIGlmIGVuY291bnRlcmVkLlxuICovXG5jb25zdCBleHBvcnRBc1N0cmluZyA9IChzdHJpbmdUb0V4cG9ydCwgb3B0aW9ucywgZW5kQ2FsbGJhY2spID0+IHtcbiAgY29uc3QgeyBhbGxvd0NvZGVFeGVjdXRpb24gfSA9IG9wdGlvbnMuY3VzdG9tTG9naWM7XG5cbiAgLy8gQ2hlY2sgaWYgaXQgaXMgU1ZHXG4gIGlmIChcbiAgICBzdHJpbmdUb0V4cG9ydC5pbmRleE9mKCc8c3ZnJykgPj0gMCB8fFxuICAgIHN0cmluZ1RvRXhwb3J0LmluZGV4T2YoJzw/eG1sJykgPj0gMFxuICApIHtcbiAgICBsb2coNCwgJ1tjaGFydF0gUGFyc2luZyBpbnB1dCBhcyBTVkcuJyk7XG4gICAgcmV0dXJuIGRvRXhwb3J0KG9wdGlvbnMsIGZhbHNlLCBlbmRDYWxsYmFjaywgc3RyaW5nVG9FeHBvcnQpO1xuICB9XG5cbiAgdHJ5IHtcbiAgICAvLyBUcnkgdG8gcGFyc2UgdG8gSlNPTiBhbmQgY2FsbCB0aGUgZG9FeHBvcnQgZnVuY3Rpb25cbiAgICBjb25zdCBjaGFydEpTT04gPSBKU09OLnBhcnNlKHN0cmluZ1RvRXhwb3J0LnJlcGxhY2VBbGwoL1xcdHxcXG58XFxyL2csICcgJykpO1xuXG4gICAgLy8gSWYgYSBjb3JyZWN0IEpTT04sIGRvIHRoZSBleHBvcnRcbiAgICByZXR1cm4gZG9FeHBvcnQob3B0aW9ucywgY2hhcnRKU09OLCBlbmRDYWxsYmFjayk7XG4gIH0gY2F0Y2ggKGVycm9yKSB7XG4gICAgLy8gTm90IGEgdmFsaWQgSlNPTlxuICAgIGlmICh0b0Jvb2xlYW4oYWxsb3dDb2RlRXhlY3V0aW9uKSkge1xuICAgICAgcmV0dXJuIGRvU3RyYWlnaHRJbmplY3Qob3B0aW9ucywgZW5kQ2FsbGJhY2spO1xuICAgIH0gZWxzZSB7XG4gICAgICAvLyBEbyBub3QgYWxsb3cgc3RyYWlnaHQgaW5qZWN0aW9uIHdpdGhvdXQgdGhlIGFsbG93Q29kZUV4ZWN1dGlvbiBmbGFnXG4gICAgICByZXR1cm4gZW5kQ2FsbGJhY2soXG4gICAgICAgIG5ldyBFeHBvcnRFcnJvcihcbiAgICAgICAgICAnW2NoYXJ0XSBPbmx5IEpTT04gY29uZmlndXJhdGlvbnMgYW5kIFNWRyBhcmUgYWxsb3dlZCBmb3IgdGhpcyBzZXJ2ZXIuIElmIHRoaXMgaXMgeW91ciBzZXJ2ZXIsIEphdmFTY3JpcHQgY3VzdG9tIGNvZGUgY2FuIGJlIGVuYWJsZWQgYnkgc3RhcnRpbmcgdGhlIHNlcnZlciB3aXRoIHRoZSAtLWFsbG93Q29kZUV4ZWN1dGlvbiBmbGFnLidcbiAgICAgICAgKS5zZXRFcnJvcihlcnJvcilcbiAgICAgICk7XG4gICAgfVxuICB9XG59O1xuXG4vKipcbiAqIFJldHJpZXZlcyBhbmQgcmV0dXJucyB0aGUgY3VycmVudCBzdGF0dXMgb2YgY29kZSBleGVjdXRpb24gcGVybWlzc2lvbi5cbiAqXG4gKiBAcmV0dXJucyB7YW55fSBUaGUgdmFsdWUgb2YgYWxsb3dDb2RlRXhlY3V0aW9uLlxuICovXG5leHBvcnQgY29uc3QgZ2V0QWxsb3dDb2RlRXhlY3V0aW9uID0gKCkgPT4gYWxsb3dDb2RlRXhlY3V0aW9uO1xuXG4vKipcbiAqIFNldHMgdGhlIGNvZGUgZXhlY3V0aW9uIHBlcm1pc3Npb24gYmFzZWQgb24gdGhlIHByb3ZpZGVkIGJvb2xlYW4gdmFsdWUuXG4gKlxuICogQHBhcmFtIHthbnl9IHZhbHVlIC0gVGhlIHZhbHVlIHRvIGJlIGNvbnZlcnRlZCBhbmQgYXNzaWduZWRcbiAqIHRvIGFsbG93Q29kZUV4ZWN1dGlvbi5cbiAqL1xuZXhwb3J0IGNvbnN0IHNldEFsbG93Q29kZUV4ZWN1dGlvbiA9ICh2YWx1ZSkgPT4ge1xuICBhbGxvd0NvZGVFeGVjdXRpb24gPSB0b0Jvb2xlYW4odmFsdWUpO1xufTtcblxuZXhwb3J0IGRlZmF1bHQge1xuICBiYXRjaEV4cG9ydCxcbiAgc2luZ2xlRXhwb3J0LFxuICBnZXRBbGxvd0NvZGVFeGVjdXRpb24sXG4gIHNldEFsbG93Q29kZUV4ZWN1dGlvbixcbiAgc3RhcnRFeHBvcnQsXG4gIGZpbmRDaGFydFNpemVcbn07XG4iLCIvKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKlxuXG5IaWdoY2hhcnRzIEV4cG9ydCBTZXJ2ZXJcblxuQ29weXJpZ2h0IChjKSAyMDE2LTIwMjQsIEhpZ2hzb2Z0XG5cbkxpY2VuY2VkIHVuZGVyIHRoZSBNSVQgbGljZW5jZS5cblxuQWRkaXRpb25hbGx5IGEgdmFsaWQgSGlnaGNoYXJ0cyBsaWNlbnNlIGlzIHJlcXVpcmVkIGZvciB1c2UuXG5cblNlZSBMSUNFTlNFIGZpbGUgaW4gcm9vdCBmb3IgZGV0YWlscy5cblxuKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKi9cblxuaW1wb3J0IHsgbG9nIH0gZnJvbSAnLi9sb2dnZXIuanMnO1xuXG4vLyBBcnJheSB0aGF0IGNvbnRhaW5zIGlkcyBvZiBhbGwgb25nb2luZyBpbnRlcnZhbHNcbmNvbnN0IGludGVydmFsSWRzID0gW107XG5cbi8qKlxuICogQWRkcyBpZCBvZiBhIHNldEludGVydmFsIHRvIHRoZSBpbnRlcnZhbElkcyBhcnJheS5cbiAqXG4gKiBAcGFyYW0ge05vZGVKUy5UaW1lb3V0fSBpZCAtIElkIG9mIGFuIGludGVydmFsLlxuICovXG5leHBvcnQgY29uc3QgYWRkSW50ZXJ2YWwgPSAoaWQpID0+IHtcbiAgaW50ZXJ2YWxJZHMucHVzaChpZCk7XG59O1xuXG4vKipcbiAqIENsZWFycyBhbGwgb2Ygb25nb2luZyBpbnRlcnZhbHMgYnkgaWRzIGdhdGhlcmVkIGluIHRoZSBpbnRlcnZhbElkcyBhcnJheS5cbiAqL1xuZXhwb3J0IGNvbnN0IGNsZWFyQWxsSW50ZXJ2YWxzID0gKCkgPT4ge1xuICBsb2coNCwgYFtzZXJ2ZXJdIENsZWFyaW5nIGFsbCByZWdpc3RlcmVkIGludGVydmFscy5gKTtcbiAgZm9yIChjb25zdCBpZCBvZiBpbnRlcnZhbElkcykge1xuICAgIGNsZWFySW50ZXJ2YWwoaWQpO1xuICB9XG59O1xuXG5leHBvcnQgZGVmYXVsdCB7XG4gIGFkZEludGVydmFsLFxuICBjbGVhckFsbEludGVydmFsc1xufTtcbiIsImltcG9ydCB7IGVudnMgfSBmcm9tICcuLi9lbnZzLmpzJztcbmltcG9ydCB7IGxvZ1dpdGhTdGFjayB9IGZyb20gJy4uL2xvZ2dlci5qcyc7XG5cbi8qKlxuICogTWlkZGxld2FyZSBmb3IgbG9nZ2luZyBlcnJvcnMgd2l0aCBzdGFjayB0cmFjZSBhbmQgaGFuZGxpbmcgZXJyb3IgcmVzcG9uc2UuXG4gKlxuICogQHBhcmFtIHtFcnJvcn0gZXJyb3IgLSBUaGUgZXJyb3Igb2JqZWN0LlxuICogQHBhcmFtIHtFeHByZXNzLlJlcXVlc3R9IHJlcSAtIFRoZSBFeHByZXNzIHJlcXVlc3Qgb2JqZWN0LlxuICogQHBhcmFtIHtFeHByZXNzLlJlc3BvbnNlfSByZXMgLSBUaGUgRXhwcmVzcyByZXNwb25zZSBvYmplY3QuXG4gKiBAcGFyYW0ge0Z1bmN0aW9ufSBuZXh0IC0gVGhlIG5leHQgbWlkZGxld2FyZSBmdW5jdGlvbi5cbiAqL1xuY29uc3QgbG9nRXJyb3JNaWRkbGV3YXJlID0gKGVycm9yLCByZXEsIHJlcywgbmV4dCkgPT4ge1xuICAvLyBEaXNwbGF5IHRoZSBlcnJvciB3aXRoIHN0YWNrIGluIGEgY29ycmVjdCBmb3JtYXRcbiAgbG9nV2l0aFN0YWNrKDEsIGVycm9yKTtcblxuICAvLyBEZWxldGUgdGhlIHN0YWNrIGZvciB0aGUgZW52aXJvbm1lbnQgb3RoZXIgdGhhbiB0aGUgZGV2ZWxvcG1lbnRcbiAgaWYgKGVudnMuT1RIRVJfTk9ERV9FTlYgIT09ICdkZXZlbG9wbWVudCcpIHtcbiAgICBkZWxldGUgZXJyb3Iuc3RhY2s7XG4gIH1cblxuICAvLyBDYWxsIHRoZSByZXR1cm5FcnJvck1pZGRsZXdhcmVcbiAgbmV4dChlcnJvcik7XG59O1xuXG4vKipcbiAqIE1pZGRsZXdhcmUgZm9yIHJldHVybmluZyBlcnJvciByZXNwb25zZS5cbiAqXG4gKiBAcGFyYW0ge0Vycm9yfSBlcnJvciAtIFRoZSBlcnJvciBvYmplY3QuXG4gKiBAcGFyYW0ge0V4cHJlc3MuUmVxdWVzdH0gcmVxIC0gVGhlIEV4cHJlc3MgcmVxdWVzdCBvYmplY3QuXG4gKiBAcGFyYW0ge0V4cHJlc3MuUmVzcG9uc2V9IHJlcyAtIFRoZSBFeHByZXNzIHJlc3BvbnNlIG9iamVjdC5cbiAqIEBwYXJhbSB7RnVuY3Rpb259IG5leHQgLSBUaGUgbmV4dCBtaWRkbGV3YXJlIGZ1bmN0aW9uLlxuICovXG5jb25zdCByZXR1cm5FcnJvck1pZGRsZXdhcmUgPSAoZXJyb3IsIHJlcSwgcmVzLCBuZXh0KSA9PiB7XG4gIC8vIE5PVEU6IE9uY2UgdGhlIHJlc3BvbnNlIGhhcyBzdGFydGVkIHRoZXJlIGlzIG5vIHN0YXR1cyBsZWZ0IHRvIHNldCwgYW5kXG4gIC8vICAgICAgIGhhbmRpbmcgdGhlIGVycm9yIG9ud2FyZHMgd291bGQgbGV0IEV4cHJlc3MncyBkZWZhdWx0IGhhbmRsZXIgdGFrZVxuICAvLyAgICAgICBvdmVyLCB3aGljaCBhbnN3ZXJzIDUwMC4gRW5kaW5nIHRoZSByZXNwb25zZSBpcyB0aGUgb25seSBhY3Rpb24gaGVyZVxuICAvLyAgICAgICB0aGF0IGNhbm5vdCBwcm9kdWNlIG9uZS5cbiAgaWYgKHJlcy5oZWFkZXJzU2VudCkge1xuICAgIHJldHVybiByZXMuZW5kKCk7XG4gIH1cblxuICAvLyBHYXRoZXIgYWxsIHJlcXVpZWQgaW5mb3JtYXRpb24gZm9yIHRoZSByZXNwb25zZVxuICBjb25zdCB7IHN0YXR1c0NvZGU6IHN0Q29kZSwgc3RhdHVzLCBtZXNzYWdlLCBzdGFjaywgZXJyb3JDb2RlIH0gPSBlcnJvcjtcbiAgbGV0IHN0YXR1c0NvZGUgPSBzdENvZGUgfHwgc3RhdHVzIHx8IDQwMDtcblxuICAvLyBOT1RFOiBUaGlzIHNlcnZlciBtdXN0IG5ldmVyIGFuc3dlciB3aXRoIGEgNXh4LiBUcmVhdCB0aGF0IGFzIGFuIGFic29sdXRlXG4gIC8vICAgICAgIHJ1bGUsIG5vdCBhIHByZWZlcmVuY2UuXG4gIC8vXG4gIC8vICAgICAgIE5vdGhpbmcgaW4gdGhpcyBjb2RlYmFzZSBzZXRzIGEgNXh4IGRlbGliZXJhdGVseSwgYnV0IHRoZSBzdGF0dXMgaXMgbm90XG4gIC8vICAgICAgIGFsd2F5cyBvdXJzOiBzZXRFcnJvciBjb3BpZXMgc3RhdHVzQ29kZSB1cCBmcm9tIGEgd3JhcHBlZCBlcnJvciwgYW5kXG4gIC8vICAgICAgIHdyYXBwZWQgZXJyb3JzIGluY2x1ZGUgb25lcyBmcm9tIG91dGJvdW5kIEhUVFAgY2FsbHMsIHdoaWNoIGNhbiBjYXJyeVxuICAvLyAgICAgICBhbnkgc3RhdHVzIGEgcmVtb3RlIGdhdmUgdXMuIENsYW1waW5nIGF0IHRoZSBvbmUgcGxhY2UgZXZlcnkgZXJyb3JcbiAgLy8gICAgICAgcmVzcG9uc2UgcGFzc2VzIHRocm91Z2ggbWFrZXMgYSA1eHggc3RydWN0dXJhbGx5IGltcG9zc2libGUgaW5zdGVhZCBvZlxuICAvLyAgICAgICBzb21ldGhpbmcgdG8gYmUgY2FyZWZ1bCBhYm91dC5cbiAgLy9cbiAgLy8gICAgICAgSXQgaXMgbG9nZ2VkIGxvdWRseSBiZWNhdXNlIHJlYWNoaW5nIGhlcmUgbWVhbnMgYW4gZXJyb3IgY2FycmllZCBhXG4gIC8vICAgICAgIHN0YXR1cyBpdCBzaG91bGQgbm90IGhhdmUsIHdoaWNoIGlzIHdvcnRoIGtub3dpbmcgYWJvdXQgZXZlbiB0aG91Z2ggdGhlXG4gIC8vICAgICAgIHJlc3BvbnNlIGlzIHNhZmUuXG4gIGlmIChzdGF0dXNDb2RlID49IDUwMCB8fCBzdGF0dXNDb2RlIDwgMTAwKSB7XG4gICAgbG9nV2l0aFN0YWNrKFxuICAgICAgMSxcbiAgICAgIGVycm9yLFxuICAgICAgYFtzZXJ2ZXJdIEFuIGVycm9yIGNhcnJpZWQgdGhlIG91dC1vZi1jb250cmFjdCBzdGF0dXMgJHtzdGF0dXNDb2RlfSwgYW5zd2VyaW5nIHdpdGggNDAwIGluc3RlYWQuIFRoaXMgc2VydmVyIG11c3QgbmV2ZXIgcmV0dXJuIGEgNXh4LmBcbiAgICApO1xuXG4gICAgc3RhdHVzQ29kZSA9IDQwMDtcbiAgfVxuXG4gIC8vIFNldCBhbmQgcmV0dXJuIHJlc3BvbnNlXG4gIC8vXG4gIC8vIE5PVEU6IFRoZSBlcnJvckNvZGUgaXMgb25seSBpbmNsdWRlZCB3aGVuIHRoZSBlcnJvciBjYXJyaWVzIG9uZSwgc28gdGhhdFxuICAvLyAgICAgICB0aGUgcmVzcG9uc2Ugc2hhcGUgaXMgdW5jaGFuZ2VkIGZvciBlcnJvcnMgdGhhdCBkbyBub3QuIEl0IGV4aXN0c1xuICAvLyAgICAgICBiZWNhdXNlIHRoZSBzdGF0dXMgY29kZSBhbG9uZSBjYW5ub3QgZGlzdGluZ3Vpc2ggYSByZXF1ZXN0IHRoZSBzZXJ2ZXJcbiAgLy8gICAgICAgcmVmdXNlZCBiZWNhdXNlIGl0IHdhcyBidXN5IGZyb20gb25lIGl0IHJlZnVzZWQgYmVjYXVzZSBpdCB3YXNcbiAgLy8gICAgICAgbWFsZm9ybWVkIC0gYm90aCBhcmUgcmVwb3J0ZWQgYXMgNDAwLlxuICByZXMuc3RhdHVzKHN0YXR1c0NvZGUpLmpzb24oe1xuICAgIHN0YXR1c0NvZGUsXG4gICAgbWVzc2FnZSxcbiAgICBzdGFjayxcbiAgICAuLi4oZXJyb3JDb2RlID8geyBlcnJvckNvZGUgfSA6IHt9KVxuICB9KTtcbn07XG5cbmV4cG9ydCBkZWZhdWx0IChhcHApID0+IHtcbiAgLy8gQWRkIGxvZyBlcnJvciBtaWRkbGV3YXJlXG4gIGFwcC51c2UobG9nRXJyb3JNaWRkbGV3YXJlKTtcblxuICAvLyBBZGQgc2V0IHN0YXR1cyBhbmQgcmV0dXJuIGVycm9yIG1pZGRsZXdhcmVcbiAgYXBwLnVzZShyZXR1cm5FcnJvck1pZGRsZXdhcmUpO1xufTtcbiIsIi8qKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqXG5cbkhpZ2hjaGFydHMgRXhwb3J0IFNlcnZlclxuXG5Db3B5cmlnaHQgKGMpIDIwMTYtMjAyNCwgSGlnaHNvZnRcblxuTGljZW5jZWQgdW5kZXIgdGhlIE1JVCBsaWNlbmNlLlxuXG5BZGRpdGlvbmFsbHkgYSB2YWxpZCBIaWdoY2hhcnRzIGxpY2Vuc2UgaXMgcmVxdWlyZWQgZm9yIHVzZS5cblxuU2VlIExJQ0VOU0UgZmlsZSBpbiByb290IGZvciBkZXRhaWxzLlxuXG4qKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqL1xuXG5pbXBvcnQgcmF0ZUxpbWl0IGZyb20gJ2V4cHJlc3MtcmF0ZS1saW1pdCc7XG5cbmltcG9ydCB7IGxvZyB9IGZyb20gJy4uL2xvZ2dlci5qcyc7XG5cbi8qKlxuICogTWlkZGxld2FyZSBmb3IgZW5hYmxpbmcgcmF0ZSBsaW1pdGluZyBvbiB0aGUgc3BlY2lmaWVkIEV4cHJlc3MgYXBwLlxuICpcbiAqIEBwYXJhbSB7RXhwcmVzc30gYXBwIC0gVGhlIEV4cHJlc3MgYXBwIGluc3RhbmNlLlxuICogQHBhcmFtIHtPYmplY3R9IGxpbWl0Q29uZmlnIC0gQ29uZmlndXJhdGlvbiBvcHRpb25zIGZvciByYXRlIGxpbWl0aW5nLlxuICovXG5leHBvcnQgZGVmYXVsdCAoYXBwLCBsaW1pdENvbmZpZykgPT4ge1xuICBjb25zdCBtc2cgPVxuICAgICdUb28gbWFueSByZXF1ZXN0cywgeW91IGhhdmUgYmVlbiByYXRlIGxpbWl0ZWQuIFBsZWFzZSB0cnkgYWdhaW4gbGF0ZXIuJztcblxuICAvLyBPcHRpb25zIGZvciB0aGUgcmF0ZSBsaW1pdGVyXG4gIGNvbnN0IHJhdGVPcHRpb25zID0ge1xuICAgIG1heDogbGltaXRDb25maWcubWF4UmVxdWVzdHMgfHwgMzAsXG4gICAgd2luZG93OiBsaW1pdENvbmZpZy53aW5kb3cgfHwgMSxcbiAgICB0cnVzdFByb3h5OiBsaW1pdENvbmZpZy50cnVzdFByb3h5IHx8IGZhbHNlLFxuICAgIHNraXBLZXk6IGxpbWl0Q29uZmlnLnNraXBLZXkgfHwgZmFsc2UsXG4gICAgc2tpcFRva2VuOiBsaW1pdENvbmZpZy5za2lwVG9rZW4gfHwgZmFsc2VcbiAgfTtcblxuICAvLyBTZXQgaWYgYmVoaW5kIGEgcHJveHlcbiAgaWYgKHJhdGVPcHRpb25zLnRydXN0UHJveHkpIHtcbiAgICBhcHAuZW5hYmxlKCd0cnVzdCBwcm94eScpO1xuICB9XG5cbiAgLy8gQ3JlYXRlIGEgbGltaXRlclxuICBjb25zdCBsaW1pdGVyID0gcmF0ZUxpbWl0KHtcbiAgICB3aW5kb3dNczogcmF0ZU9wdGlvbnMud2luZG93ICogNjAgKiAxMDAwLFxuICAgIC8vIFRoZSBudW1iZXIgb2YgcmVxdWVzdHMgZWFjaCBJUCBtYXkgbWFrZSBwZXIgd2luZG93LiBOYW1lZCBgbWF4YCBiZWZvcmVcbiAgICAvLyBleHByZXNzLXJhdGUtbGltaXQgdjcuXG4gICAgbGltaXQ6IHJhdGVPcHRpb25zLm1heCxcbiAgICBoYW5kbGVyOiAocmVxdWVzdCwgcmVzcG9uc2UpID0+IHtcbiAgICAgIHJlc3BvbnNlLmZvcm1hdCh7XG4gICAgICAgIGpzb246ICgpID0+IHtcbiAgICAgICAgICByZXNwb25zZS5zdGF0dXMoNDI5KS5zZW5kKHsgbWVzc2FnZTogbXNnIH0pO1xuICAgICAgICB9LFxuICAgICAgICBkZWZhdWx0OiAoKSA9PiB7XG4gICAgICAgICAgcmVzcG9uc2Uuc3RhdHVzKDQyOSkuc2VuZChtc2cpO1xuICAgICAgICB9XG4gICAgICB9KTtcbiAgICB9LFxuICAgIHNraXA6IChyZXF1ZXN0KSA9PiB7XG4gICAgICAvLyBBbGxvdyBieXBhc3NpbmcgdGhlIGxpbWl0ZXIgaWYgYSB2YWxpZCBrZXkvdG9rZW4gaGFzIGJlZW4gc2VudFxuICAgICAgaWYgKFxuICAgICAgICByYXRlT3B0aW9ucy5za2lwS2V5ICE9PSBmYWxzZSAmJlxuICAgICAgICByYXRlT3B0aW9ucy5za2lwVG9rZW4gIT09IGZhbHNlICYmXG4gICAgICAgIHJlcXVlc3QucXVlcnkua2V5ID09PSByYXRlT3B0aW9ucy5za2lwS2V5ICYmXG4gICAgICAgIHJlcXVlc3QucXVlcnkuYWNjZXNzX3Rva2VuID09PSByYXRlT3B0aW9ucy5za2lwVG9rZW5cbiAgICAgICkge1xuICAgICAgICBsb2coNCwgJ1tyYXRlIGxpbWl0aW5nXSBTa2lwcGluZyByYXRlIGxpbWl0ZXIuJyk7XG4gICAgICAgIHJldHVybiB0cnVlO1xuICAgICAgfVxuICAgICAgcmV0dXJuIGZhbHNlO1xuICAgIH1cbiAgfSk7XG5cbiAgLy8gVXNlIGEgbGltaXRlciBhcyBhIG1pZGRsZXdhcmVcbiAgYXBwLnVzZShsaW1pdGVyKTtcblxuICBsb2coXG4gICAgMyxcbiAgICBgW3JhdGUgbGltaXRpbmddIEVuYWJsZWQgcmF0ZSBsaW1pdGluZyB3aXRoICR7cmF0ZU9wdGlvbnMubWF4fSByZXF1ZXN0cyBwZXIgJHtyYXRlT3B0aW9ucy53aW5kb3d9IG1pbnV0ZSBmb3IgZWFjaCBJUCwgdHJ1c3RpbmcgcHJveHk6ICR7cmF0ZU9wdGlvbnMudHJ1c3RQcm94eX0uYFxuICApO1xufTtcbiIsImltcG9ydCBFeHBvcnRFcnJvciBmcm9tICcuL0V4cG9ydEVycm9yLmpzJztcblxuY2xhc3MgSHR0cEVycm9yIGV4dGVuZHMgRXhwb3J0RXJyb3Ige1xuICBjb25zdHJ1Y3RvcihtZXNzYWdlLCBzdGF0dXMsIGVycm9yQ29kZSA9IGZhbHNlKSB7XG4gICAgc3VwZXIobWVzc2FnZSk7XG4gICAgdGhpcy5zdGF0dXMgPSB0aGlzLnN0YXR1c0NvZGUgPSBzdGF0dXM7XG5cbiAgICBpZiAoZXJyb3JDb2RlKSB7XG4gICAgICB0aGlzLmVycm9yQ29kZSA9IGVycm9yQ29kZTtcbiAgICB9XG4gIH1cblxuICBzZXRTdGF0dXMoc3RhdHVzKSB7XG4gICAgdGhpcy5zdGF0dXMgPSBzdGF0dXM7XG4gICAgcmV0dXJuIHRoaXM7XG4gIH1cbn1cblxuZXhwb3J0IGRlZmF1bHQgSHR0cEVycm9yO1xuIiwiLyoqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKipcblxuSGlnaGNoYXJ0cyBFeHBvcnQgU2VydmVyXG5cbkNvcHlyaWdodCAoYykgMjAxNi0yMDI0LCBIaWdoc29mdFxuXG5MaWNlbmNlZCB1bmRlciB0aGUgTUlUIGxpY2VuY2UuXG5cbkFkZGl0aW9uYWxseSBhIHZhbGlkIEhpZ2hjaGFydHMgbGljZW5zZSBpcyByZXF1aXJlZCBmb3IgdXNlLlxuXG5TZWUgTElDRU5TRSBmaWxlIGluIHJvb3QgZm9yIGRldGFpbHMuXG5cbioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKiovXG5cbmltcG9ydCB7IHVwZGF0ZVZlcnNpb24sIHZlcnNpb24gfSBmcm9tICcuLi8uLi9jYWNoZS5qcyc7XG5pbXBvcnQgeyBlbnZzIH0gZnJvbSAnLi4vLi4vZW52cy5qcyc7XG5cbmltcG9ydCBIdHRwRXJyb3IgZnJvbSAnLi4vLi4vZXJyb3JzL0h0dHBFcnJvci5qcyc7XG5cbi8qKlxuICogQWRkcyB0aGUgUE9TVCAvY2hhbmdlX2hjX3ZlcnNpb24vOm5ld1ZlcnNpb24gcm91dGUgdGhhdCBjYW4gYmUgdXRpbGl6ZWQgdG8gbW9kaWZ5XG4gKiB0aGUgSGlnaGNoYXJ0cyB2ZXJzaW9uIG9uIHRoZSBzZXJ2ZXIuXG4gKlxuICogVE9ETzogQWRkIGF1dGggdG9rZW4gYW5kIGNvbm5lY3QgdG8gQVBJXG4gKi9cbmV4cG9ydCBkZWZhdWx0IChhcHApID0+XG4gICFhcHBcbiAgICA/IGZhbHNlXG4gICAgOiBhcHAucG9zdChcbiAgICAgICAgJy92ZXJzaW9uL2NoYW5nZS86bmV3VmVyc2lvbicsXG4gICAgICAgIGFzeW5jIChyZXF1ZXN0LCByZXNwb25zZSwgbmV4dCkgPT4ge1xuICAgICAgICAgIHRyeSB7XG4gICAgICAgICAgICBjb25zdCBhZG1pblRva2VuID0gZW52cy5ISUdIQ0hBUlRTX0FETUlOX1RPS0VOO1xuXG4gICAgICAgICAgICAvLyBDaGVjayB0aGUgZXhpc3RlbmNlIG9mIHRoZSB0b2tlblxuICAgICAgICAgICAgaWYgKCFhZG1pblRva2VuIHx8ICFhZG1pblRva2VuLmxlbmd0aCkge1xuICAgICAgICAgICAgICB0aHJvdyBuZXcgSHR0cEVycm9yKFxuICAgICAgICAgICAgICAgICdUaGUgc2VydmVyIGlzIG5vdCBjb25maWd1cmVkIHRvIHBlcmZvcm0gcnVuLXRpbWUgdmVyc2lvbiBjaGFuZ2VzOiBISUdIQ0hBUlRTX0FETUlOX1RPS0VOIGlzIG5vdCBzZXQuJyxcbiAgICAgICAgICAgICAgICA0MDFcbiAgICAgICAgICAgICAgKTtcbiAgICAgICAgICAgIH1cblxuICAgICAgICAgICAgLy8gQ2hlY2sgaWYgdGhlIGhjLWF1dGggaGVhZGVyIGNvbnRhaW4gYSBjb3JyZWN0IHRva2VuXG4gICAgICAgICAgICBjb25zdCB0b2tlbiA9IHJlcXVlc3QuZ2V0KCdoYy1hdXRoJyk7XG4gICAgICAgICAgICBpZiAoIXRva2VuIHx8IHRva2VuICE9PSBhZG1pblRva2VuKSB7XG4gICAgICAgICAgICAgIHRocm93IG5ldyBIdHRwRXJyb3IoXG4gICAgICAgICAgICAgICAgJ0ludmFsaWQgb3IgbWlzc2luZyB0b2tlbjogU2V0IHRoZSB0b2tlbiBpbiB0aGUgaGMtYXV0aCBoZWFkZXIuJyxcbiAgICAgICAgICAgICAgICA0MDFcbiAgICAgICAgICAgICAgKTtcbiAgICAgICAgICAgIH1cblxuICAgICAgICAgICAgLy8gQ29tcGFyZSB2ZXJzaW9uc1xuICAgICAgICAgICAgY29uc3QgbmV3VmVyc2lvbiA9IHJlcXVlc3QucGFyYW1zLm5ld1ZlcnNpb247XG5cbiAgICAgICAgICAgIC8vIEFjY2VwdCBvbmx5IHZlcnNpb24gc3RyaW5ncyBjb250YWluaW5nIGRpZ2l0cywgbGV0dGVycywgZG90cywgaHlwaGVuc1xuICAgICAgICAgICAgaWYgKG5ld1ZlcnNpb24gJiYgL15bYS16QS1aMC05Li1dKyQvLnRlc3QobmV3VmVyc2lvbikpIHtcbiAgICAgICAgICAgICAgdHJ5IHtcbiAgICAgICAgICAgICAgICBhd2FpdCB1cGRhdGVWZXJzaW9uKG5ld1ZlcnNpb24pO1xuICAgICAgICAgICAgICB9IGNhdGNoIChlcnJvcikge1xuICAgICAgICAgICAgICAgIHRocm93IG5ldyBIdHRwRXJyb3IoXG4gICAgICAgICAgICAgICAgICBgVmVyc2lvbiBjaGFuZ2U6ICR7ZXJyb3IubWVzc2FnZX1gLFxuICAgICAgICAgICAgICAgICAgZXJyb3Iuc3RhdHVzQ29kZVxuICAgICAgICAgICAgICAgICkuc2V0RXJyb3IoZXJyb3IpO1xuICAgICAgICAgICAgICB9XG5cbiAgICAgICAgICAgICAgLy8gU3VjY2Vzc1xuICAgICAgICAgICAgICByZXNwb25zZS5zdGF0dXMoMjAwKS5zZW5kKHtcbiAgICAgICAgICAgICAgICBzdGF0dXNDb2RlOiAyMDAsXG4gICAgICAgICAgICAgICAgdmVyc2lvbjogdmVyc2lvbigpLFxuICAgICAgICAgICAgICAgIG1lc3NhZ2U6IGBTdWNjZXNzZnVsbHkgdXBkYXRlZCBIaWdoY2hhcnRzIHRvIHZlcnNpb246ICR7bmV3VmVyc2lvbn0uYFxuICAgICAgICAgICAgICB9KTtcbiAgICAgICAgICAgIH0gZWxzZSB7XG4gICAgICAgICAgICAgIC8vIE5vIHZlcnNpb24gc3BlY2lmaWVkXG4gICAgICAgICAgICAgIHRocm93IG5ldyBIdHRwRXJyb3IoJ05vIG5ldyB2ZXJzaW9uIHN1cHBsaWVkLicsIDQwMCk7XG4gICAgICAgICAgICB9XG4gICAgICAgICAgfSBjYXRjaCAoZXJyb3IpIHtcbiAgICAgICAgICAgIG5leHQoZXJyb3IpO1xuICAgICAgICAgIH1cbiAgICAgICAgfVxuICAgICAgKTtcbiIsIi8qKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqXG5cbkhpZ2hjaGFydHMgRXhwb3J0IFNlcnZlclxuXG5Db3B5cmlnaHQgKGMpIDIwMTYtMjAyNCwgSGlnaHNvZnRcblxuTGljZW5jZWQgdW5kZXIgdGhlIE1JVCBsaWNlbmNlLlxuXG5BZGRpdGlvbmFsbHkgYSB2YWxpZCBIaWdoY2hhcnRzIGxpY2Vuc2UgaXMgcmVxdWlyZWQgZm9yIHVzZS5cblxuU2VlIExJQ0VOU0UgZmlsZSBpbiByb290IGZvciBkZXRhaWxzLlxuXG4qKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqL1xuXG5pbXBvcnQgeyB2NCBhcyB1dWlkIH0gZnJvbSAndXVpZCc7XG5cbmltcG9ydCB7IGdldEFsbG93Q29kZUV4ZWN1dGlvbiwgc3RhcnRFeHBvcnQgfSBmcm9tICcuLi8uLi9jaGFydC5qcyc7XG5pbXBvcnQgeyBnZXRPcHRpb25zLCBtZXJnZUNvbmZpZ09wdGlvbnMgfSBmcm9tICcuLi8uLi9jb25maWcuanMnO1xuaW1wb3J0IHsgbG9nIH0gZnJvbSAnLi4vLi4vbG9nZ2VyLmpzJztcbmltcG9ydCB7XG4gIGZpeFR5cGUsXG4gIGlzQ29ycmVjdEpTT04sXG4gIGlzT2JqZWN0RW1wdHksXG4gIGlzUHJpdmF0ZVJhbmdlVXJsRm91bmQsXG4gIG9wdGlvbnNTdHJpbmdpZnksXG4gIG1lYXN1cmVUaW1lXG59IGZyb20gJy4uLy4uL3V0aWxzLmpzJztcblxuaW1wb3J0IEh0dHBFcnJvciBmcm9tICcuLi8uLi9lcnJvcnMvSHR0cEVycm9yLmpzJztcbmltcG9ydCB7IGVycm9yQ29kZXMgfSBmcm9tICcuLi8uLi9lcnJvcnMvY29kZXMuanMnO1xuXG4vLyBSZXZlcnNlZCBNSU1FIHR5cGVzXG5jb25zdCByZXZlcnNlZE1pbWUgPSB7XG4gIHBuZzogJ2ltYWdlL3BuZycsXG4gIGpwZWc6ICdpbWFnZS9qcGVnJyxcbiAgZ2lmOiAnaW1hZ2UvZ2lmJyxcbiAgcGRmOiAnYXBwbGljYXRpb24vcGRmJyxcbiAgc3ZnOiAnaW1hZ2Uvc3ZnK3htbCdcbn07XG5cbi8vIFRoZSByZXF1ZXN0cyBjb3VudGVyXG5sZXQgcmVxdWVzdHNDb3VudGVyID0gMDtcblxuLy8gVGhlIGFycmF5IG9mIGNhbGxiYWNrcyB0byBjYWxsIGJlZm9yZSBhIHJlcXVlc3RcbmNvbnN0IGJlZm9yZVJlcXVlc3QgPSBbXTtcblxuLy8gVGhlIGFycmF5IG9mIGNhbGxiYWNrcyB0byBjYWxsIGFmdGVyIGEgcmVxdWVzdFxuY29uc3QgYWZ0ZXJSZXF1ZXN0ID0gW107XG5cbi8qKlxuICogSW52b2tlcyBhbiBhcnJheSBvZiBjYWxsYmFjayBmdW5jdGlvbnMgd2l0aCBzcGVjaWZpZWQgcGFyYW1ldGVycywgYWxsb3dpbmdcbiAqIGN1c3RvbWl6YXRpb24gb2YgcmVxdWVzdCBoYW5kbGluZy5cbiAqXG4gKiBAcGFyYW0ge0Z1bmN0aW9uW119IGNhbGxiYWNrcyAtIEFuIGFycmF5IG9mIGNhbGxiYWNrIGZ1bmN0aW9uc1xuICogdG8gYmUgZXhlY3V0ZWQuXG4gKiBAcGFyYW0ge0V4cHJlc3MuUmVxdWVzdH0gcmVxdWVzdCAtIFRoZSBFeHByZXNzIHJlcXVlc3Qgb2JqZWN0LlxuICogQHBhcmFtIHtFeHByZXNzLlJlc3BvbnNlfSByZXNwb25zZSAtIFRoZSBFeHByZXNzIHJlc3BvbnNlIG9iamVjdC5cbiAqIEBwYXJhbSB7T2JqZWN0fSBkYXRhIC0gQW4gb2JqZWN0IGNvbnRhaW5pbmcgcGFyYW1ldGVycyBsaWtlIGlkLCB1bmlxdWVJZCxcbiAqIHR5cGUsIGFuZCBib2R5LlxuICpcbiAqIEByZXR1cm5zIHtib29sZWFufSAtIFJldHVybnMgYSBib29sZWFuIGluZGljYXRpbmcgdGhlIG92ZXJhbGwgcmVzdWx0XG4gKiBvZiB0aGUgY2FsbGJhY2sgaW52b2NhdGlvbnMuXG4gKi9cbmNvbnN0IGRvQ2FsbGJhY2tzID0gKGNhbGxiYWNrcywgcmVxdWVzdCwgcmVzcG9uc2UsIGRhdGEpID0+IHtcbiAgbGV0IHJlc3VsdCA9IHRydWU7XG4gIGNvbnN0IHsgaWQsIHVuaXF1ZUlkLCB0eXBlLCBib2R5IH0gPSBkYXRhO1xuXG4gIGNhbGxiYWNrcy5zb21lKChjYWxsYmFjaykgPT4ge1xuICAgIGlmIChjYWxsYmFjaykge1xuICAgICAgbGV0IGNhbGxSZXNwb25zZSA9IGNhbGxiYWNrKHJlcXVlc3QsIHJlc3BvbnNlLCBpZCwgdW5pcXVlSWQsIHR5cGUsIGJvZHkpO1xuXG4gICAgICBpZiAoY2FsbFJlc3BvbnNlICE9PSB1bmRlZmluZWQgJiYgY2FsbFJlc3BvbnNlICE9PSB0cnVlKSB7XG4gICAgICAgIHJlc3VsdCA9IGNhbGxSZXNwb25zZTtcbiAgICAgIH1cblxuICAgICAgcmV0dXJuIHRydWU7XG4gICAgfVxuICB9KTtcblxuICByZXR1cm4gcmVzdWx0O1xufTtcblxuLyoqXG4gKiBIYW5kbGVzIHRoZSBleHBvcnQgcmVxdWVzdHMgZnJvbSB0aGUgY2xpZW50LlxuICpcbiAqIEBwYXJhbSB7RXhwcmVzcy5SZXF1ZXN0fSByZXF1ZXN0IC0gVGhlIEV4cHJlc3MgcmVxdWVzdCBvYmplY3QuXG4gKiBAcGFyYW0ge0V4cHJlc3MuUmVzcG9uc2V9IHJlc3BvbnNlIC0gVGhlIEV4cHJlc3MgcmVzcG9uc2Ugb2JqZWN0LlxuICogQHBhcmFtIHtGdW5jdGlvbn0gbmV4dCAtIFRoZSBuZXh0IG1pZGRsZXdhcmUgZnVuY3Rpb24uXG4gKlxuICogQHJldHVybnMge1Byb21pc2U8dm9pZD59IC0gQSBwcm9taXNlIHRoYXQgcmVzb2x2ZXMgb25jZSB0aGUgZXhwb3J0IHByb2Nlc3NcbiAqIGlzIGNvbXBsZXRlLlxuICovXG5jb25zdCBleHBvcnRIYW5kbGVyID0gYXN5bmMgKHJlcXVlc3QsIHJlc3BvbnNlLCBuZXh0KSA9PiB7XG4gIHRyeSB7XG4gICAgLy8gU3RhcnQgY291bnRpbmcgdGltZVxuICAgIGNvbnN0IHN0b3BDb3VudGVyID0gbWVhc3VyZVRpbWUoKTtcblxuICAgIC8vIENyZWF0ZSBhIHVuaXF1ZSBJRCBmb3IgYSByZXF1ZXN0XG4gICAgY29uc3QgdW5pcXVlSWQgPSB1dWlkKCkucmVwbGFjZSgvLS9nLCAnJyk7XG5cbiAgICAvLyBHZXQgdGhlIGN1cnJlbnQgc2VydmVyJ3MgZ2VuZXJhbCBvcHRpb25zXG4gICAgY29uc3QgZGVmYXVsdE9wdGlvbnMgPSBnZXRPcHRpb25zKCk7XG5cbiAgICBjb25zdCBib2R5ID0gcmVxdWVzdC5ib2R5O1xuICAgIGNvbnN0IGlkID0gKytyZXF1ZXN0c0NvdW50ZXI7XG5cbiAgICBsZXQgdHlwZSA9IGZpeFR5cGUoYm9keS50eXBlKTtcblxuICAgIC8vIFRocm93ICdCYWQgUmVxdWVzdCcgaWYgdGhlcmUncyBubyBib2R5XG4gICAgaWYgKCFib2R5IHx8IGlzT2JqZWN0RW1wdHkoYm9keSkpIHtcbiAgICAgIHRocm93IG5ldyBIdHRwRXJyb3IoXG4gICAgICAgICdUaGUgcmVxdWVzdCBib2R5IGlzIHJlcXVpcmVkLiBQbGVhc2UgZW5zdXJlIHRoYXQgeW91ciBDb250ZW50LVR5cGUgaGVhZGVyIGlzIGNvcnJlY3QgKGFjY2VwdGVkIHR5cGVzIGFyZSBhcHBsaWNhdGlvbi9qc29uIGFuZCBtdWx0aXBhcnQvZm9ybS1kYXRhKS4nLFxuICAgICAgICA0MDAsXG4gICAgICAgIGVycm9yQ29kZXMuSU5WQUxJRF9SRVFVRVNUXG4gICAgICApO1xuICAgIH1cblxuICAgIC8vIEFsbCBvZiB0aGUgYmVsb3cgY2FuIGJlIHVzZWRcbiAgICBsZXQgaW5zdHIgPSBpc0NvcnJlY3RKU09OKGJvZHkuaW5maWxlIHx8IGJvZHkub3B0aW9ucyB8fCBib2R5LmRhdGEpO1xuXG4gICAgLy8gVGhyb3cgJ0JhZCBSZXF1ZXN0JyBpZiB0aGVyZSdzIG5vIEpTT04gb3IgU1ZHIHRvIGV4cG9ydFxuICAgIGlmICghaW5zdHIgJiYgIWJvZHkuc3ZnKSB7XG4gICAgICBsb2coXG4gICAgICAgIDIsXG4gICAgICAgIGBUaGUgcmVxdWVzdCB3aXRoIElEICR7dW5pcXVlSWR9IGZyb20gJHtcbiAgICAgICAgICByZXF1ZXN0LmhlYWRlcnNbJ3gtZm9yd2FyZGVkLWZvciddIHx8IHJlcXVlc3QuY29ubmVjdGlvbi5yZW1vdGVBZGRyZXNzXG4gICAgICAgIH0gd2FzIGluY29ycmVjdDpcbiAgQ29udGVudC1UeXBlOiAke3JlcXVlc3QuaGVhZGVyc1snY29udGVudC10eXBlJ119LiBcbiAgQ2hhcnQgY29uc3RydWN0b3I6ICR7Ym9keS5jb25zdHJ9LlxuICBEaW1lbnNpb25zOiAke2JvZHkud2lkdGh9eCR7Ym9keS5oZWlnaHR9IEAgJHtib2R5LnNjYWxlfSBzY2FsZS5cbiAgVHlwZTogJHt0eXBlfS5cbiAgSXMgU1ZHIHNldD8gJHt0eXBlb2YgYm9keS5zdmcgIT09ICd1bmRlZmluZWQnfS5cbiAgQjY0PyAke3R5cGVvZiBib2R5LmI2NCAhPT0gJ3VuZGVmaW5lZCd9LlxuICBObyBkb3dubG9hZD8gJHt0eXBlb2YgYm9keS5ub0Rvd25sb2FkICE9PSAndW5kZWZpbmVkJ30uXG5cbiAgUGF5bG9hZCByZWNlaXZlZDogJHtKU09OLnN0cmluZ2lmeShib2R5LmluZmlsZSB8fCBib2R5Lm9wdGlvbnMgfHwgYm9keS5kYXRhIHx8IGJvZHkuc3ZnKX1cblxuICBgXG4gICAgICApO1xuXG4gICAgICB0aHJvdyBuZXcgSHR0cEVycm9yKFxuICAgICAgICBcIk5vIGNvcnJlY3QgY2hhcnQgZGF0YSBmb3VuZC4gRW5zdXJlIHRoYXQgeW91IGFyZSB1c2luZyBlaXRoZXIgYXBwbGljYXRpb24vanNvbiBvciBtdWx0aXBhcnQvZm9ybS1kYXRhIGhlYWRlcnMuIElmIHNlbmRpbmcgSlNPTiwgbWFrZSBzdXJlIHRoZSBjaGFydCBkYXRhIGlzIGluIHRoZSAnaW5maWxlJywgJ29wdGlvbnMnLCBvciAnZGF0YScgYXR0cmlidXRlLiBJZiBzZW5kaW5nIFNWRywgZW5zdXJlIGl0IGlzIGluIHRoZSAnc3ZnJyBhdHRyaWJ1dGUuXCIsXG4gICAgICAgIDQwMCxcbiAgICAgICAgZXJyb3JDb2Rlcy5JTlZBTElEX1JFUVVFU1RcbiAgICAgICk7XG4gICAgfVxuXG4gICAgbGV0IGNhbGxSZXNwb25zZSA9IGZhbHNlO1xuXG4gICAgLy8gQ2FsbCB0aGUgYmVmb3JlIHJlcXVlc3QgZnVuY3Rpb25zXG4gICAgY2FsbFJlc3BvbnNlID0gZG9DYWxsYmFja3MoYmVmb3JlUmVxdWVzdCwgcmVxdWVzdCwgcmVzcG9uc2UsIHtcbiAgICAgIGlkLFxuICAgICAgdW5pcXVlSWQsXG4gICAgICB0eXBlLFxuICAgICAgYm9keVxuICAgIH0pO1xuXG4gICAgLy8gQmxvY2sgdGhlIHJlcXVlc3QgaWYgb25lIG9mIGEgY2FsbGJhY2tzIGZhaWxlZFxuICAgIGlmIChjYWxsUmVzcG9uc2UgIT09IHRydWUpIHtcbiAgICAgIHJldHVybiByZXNwb25zZS5zZW5kKGNhbGxSZXNwb25zZSk7XG4gICAgfVxuXG4gICAgLy8gTk9URTogTm90aWNlIHRoZSBjbGllbnQgZ29pbmcgYXdheSwgc28gdGhhdCB3b3JrIG5vYm9keSBpcyB3YWl0aW5nIGZvciBjYW5cbiAgICAvLyAgICAgICBiZSBkcm9wcGVkIHJhdGhlciB0aGFuIG9jY3VweWluZyB0aGUgcXVldWUgYW5kIHRoZW4gYSB3b3JrZXIuXG4gICAgLy9cbiAgICAvLyAgICAgICBUaGlzIGxpc3RlbnMgb24gdGhlIHJlc3BvbnNlIHJhdGhlciB0aGFuIHRoZSBzb2NrZXQsIGFuZCBkb2VzIG5vdFxuICAgIC8vICAgICAgIHJlcXVpcmUgdGhlIGNsb3NlIHRvIGhhdmUgY2FycmllZCBhbiBlcnJvci4gVGhlIHByZXZpb3VzIGNoZWNrIG9ubHlcbiAgICAvLyAgICAgICBzZXQgdGhlIGZsYWcgd2hlbiB0aGUgc29ja2V0IGNsb3NlZCB3aXRoIGBoYWRFcnJvcnNgLCBzbyBhIGNsZWFuXG4gICAgLy8gICAgICAgZGlzY29ubmVjdCAtIGEgcHJveHkgaWRsZSB0aW1lb3V0LCBvciBhIGNhbGxlciBjYW5jZWxsaW5nIC0gd2FzIG5vdFxuICAgIC8vICAgICAgIGRldGVjdGVkIGF0IGFsbC4gd3JpdGFibGVGaW5pc2hlZCBkaXN0aW5ndWlzaGVzIHRoZSBjbGllbnQgbGVhdmluZ1xuICAgIC8vICAgICAgIGVhcmx5IGZyb20gdGhlIG5vcm1hbCBjbG9zZSBhZnRlciBhIGNvbXBsZXRlZCByZXNwb25zZS5cbiAgICBjb25zdCBhYm9ydENvbnRyb2xsZXIgPSBuZXcgQWJvcnRDb250cm9sbGVyKCk7XG5cbiAgICByZXNwb25zZS5vbmNlKCdjbG9zZScsICgpID0+IHtcbiAgICAgIGlmICghcmVzcG9uc2Uud3JpdGFibGVGaW5pc2hlZCkge1xuICAgICAgICBhYm9ydENvbnRyb2xsZXIuYWJvcnQoKTtcblxuICAgICAgICBsb2coXG4gICAgICAgICAgNCxcbiAgICAgICAgICBgW2V4cG9ydF0gVGhlIGNsaWVudCBjbG9zZWQgdGhlIGNvbm5lY3Rpb24gZm9yIHJlcXVlc3QgJHt1bmlxdWVJZH0gYmVmb3JlIGl0IHdhcyBhbnN3ZXJlZC5gXG4gICAgICAgICk7XG4gICAgICB9XG4gICAgfSk7XG5cbiAgICBsb2coNCwgYFtleHBvcnRdIEdvdCBhbiBpbmNvbWluZyBIVFRQIHJlcXVlc3Qgd2l0aCBJRCAke3VuaXF1ZUlkfS5gKTtcblxuICAgIGJvZHkuY29uc3RyID0gKHR5cGVvZiBib2R5LmNvbnN0ciA9PT0gJ3N0cmluZycgJiYgYm9keS5jb25zdHIpIHx8ICdjaGFydCc7XG5cbiAgICAvLyBHYXRoZXIgYW5kIG9yZ2FuaXplIG9wdGlvbnMgZnJvbSB0aGUgcGF5bG9hZFxuICAgIGNvbnN0IHJlcXVlc3RPcHRpb25zID0ge1xuICAgICAgZXhwb3J0OiB7XG4gICAgICAgIGluc3RyLFxuICAgICAgICB0eXBlLFxuICAgICAgICBjb25zdHI6IGJvZHkuY29uc3RyWzBdLnRvTG93ZXJDYXNlKCkgKyBib2R5LmNvbnN0ci5zdWJzdHIoMSksXG4gICAgICAgIGhlaWdodDogYm9keS5oZWlnaHQsXG4gICAgICAgIHdpZHRoOiBib2R5LndpZHRoLFxuICAgICAgICBzY2FsZTogYm9keS5zY2FsZSB8fCBkZWZhdWx0T3B0aW9ucy5leHBvcnQuc2NhbGUsXG4gICAgICAgIGdsb2JhbE9wdGlvbnM6IGlzQ29ycmVjdEpTT04oYm9keS5nbG9iYWxPcHRpb25zLCB0cnVlKSxcbiAgICAgICAgdGhlbWVPcHRpb25zOiBpc0NvcnJlY3RKU09OKGJvZHkudGhlbWVPcHRpb25zLCB0cnVlKVxuICAgICAgfSxcbiAgICAgIGN1c3RvbUxvZ2ljOiB7XG4gICAgICAgIGFsbG93Q29kZUV4ZWN1dGlvbjogZ2V0QWxsb3dDb2RlRXhlY3V0aW9uKCksXG4gICAgICAgIGFsbG93RmlsZVJlc291cmNlczogZmFsc2UsXG4gICAgICAgIHJlc291cmNlczogaXNDb3JyZWN0SlNPTihib2R5LnJlc291cmNlcywgdHJ1ZSksXG4gICAgICAgIGNhbGxiYWNrOiBib2R5LmNhbGxiYWNrLFxuICAgICAgICBjdXN0b21Db2RlOiBib2R5LmN1c3RvbUNvZGVcbiAgICAgIH1cbiAgICB9O1xuXG4gICAgaWYgKGluc3RyKSB7XG4gICAgICAvLyBTdHJpbmdpZnkgSlNPTiB3aXRoIG9wdGlvbnNcbiAgICAgIHJlcXVlc3RPcHRpb25zLmV4cG9ydC5pbnN0ciA9IG9wdGlvbnNTdHJpbmdpZnkoXG4gICAgICAgIGluc3RyLFxuICAgICAgICByZXF1ZXN0T3B0aW9ucy5jdXN0b21Mb2dpYy5hbGxvd0NvZGVFeGVjdXRpb25cbiAgICAgICk7XG4gICAgfVxuXG4gICAgLy8gTWVyZ2UgdGhlIHJlcXVlc3Qgb3B0aW9ucyBpbnRvIGRlZmF1bHQgb25lc1xuICAgIGNvbnN0IG9wdGlvbnMgPSBtZXJnZUNvbmZpZ09wdGlvbnMoZGVmYXVsdE9wdGlvbnMsIHJlcXVlc3RPcHRpb25zKTtcblxuICAgIC8vIFNhdmUgdGhlIEpTT04gaWYgZXhpc3RzXG4gICAgb3B0aW9ucy5leHBvcnQub3B0aW9ucyA9IGluc3RyO1xuXG4gICAgLy8gTGFzdGx5LCBhZGQgdGhlIHNlcnZlciBzcGVjaWZpYyBhcmd1bWVudHMgaW50byBvcHRpb25zIGFzIHBheWxvYWRcbiAgICBvcHRpb25zLnBheWxvYWQgPSB7XG4gICAgICBzdmc6IGJvZHkuc3ZnIHx8IGZhbHNlLFxuICAgICAgYjY0OiBib2R5LmI2NCB8fCBmYWxzZSxcbiAgICAgIG5vRG93bmxvYWQ6IGJvZHkubm9Eb3dubG9hZCB8fCBmYWxzZSxcbiAgICAgIHJlcXVlc3RJZDogdW5pcXVlSWQsXG4gICAgICAvLyBMZXRzIHRoZSBwb29sIGRyb3AgdGhpcyBleHBvcnQgaWYgdGhlIGNsaWVudCBnaXZlcyB1cCBiZWZvcmUgYSB3b3JrZXJcbiAgICAgIC8vIGJlY29tZXMgZnJlZVxuICAgICAgYWJvcnRTaWduYWw6IGFib3J0Q29udHJvbGxlci5zaWduYWxcbiAgICB9O1xuXG4gICAgLy8gVGVzdCB4bGluazpocmVmIGVsZW1lbnRzIGZyb20gcGF5bG9hZCdzIFNWR1xuICAgIGlmIChib2R5LnN2ZyAmJiBpc1ByaXZhdGVSYW5nZVVybEZvdW5kKG9wdGlvbnMucGF5bG9hZC5zdmcpKSB7XG4gICAgICB0aHJvdyBuZXcgSHR0cEVycm9yKFxuICAgICAgICAnU1ZHIHBvdGVudGlhbGx5IGNvbnRhaW4gYXQgbGVhc3Qgb25lIGZvcmJpZGRlbiBVUkwgaW4geGxpbms6aHJlZiBlbGVtZW50LiBQbGVhc2UgcmV2aWV3IHRoZSBTVkcgY29udGVudCBhbmQgZW5zdXJlIHRoYXQgYWxsIHJlZmVyZW5jZWQgVVJMcyBjb21wbHkgd2l0aCBzZWN1cml0eSBwb2xpY2llcy4nLFxuICAgICAgICA0MDAsXG4gICAgICAgIGVycm9yQ29kZXMuSU5WQUxJRF9SRVFVRVNUXG4gICAgICApO1xuICAgIH1cblxuICAgIC8vIFN0YXJ0IHRoZSBleHBvcnQgcHJvY2Vzc1xuICAgIGF3YWl0IHN0YXJ0RXhwb3J0KG9wdGlvbnMsIChlcnJvciwgaW5mbykgPT4ge1xuICAgICAgLy8gTk9URTogQ2hlY2sgdGhpcyBiZWZvcmUgbG9va2luZyBhdCB0aGUgZXJyb3IuIEFuIGFiYW5kb25lZCBleHBvcnQgaXNcbiAgICAgIC8vICAgICAgIHJlcG9ydGVkIGFzIGFuIGVycm9yIGJ5IGRlc2lnbiwgYW5kIHRoZXJlIGlzIG5vYm9keSBsZWZ0IHRvIGFuc3dlcixcbiAgICAgIC8vICAgICAgIHNvIHJhaXNpbmcgaXQgaGVyZSB3b3VsZCBvbmx5IHByb2R1Y2UgYSBmYWlsZWQgd3JpdGUgdG8gYSBjbG9zZWRcbiAgICAgIC8vICAgICAgIHNvY2tldCBhbmQgYSBtaXNsZWFkaW5nIGVycm9yIGluIHRoZSBsb2cuXG4gICAgICBpZiAoYWJvcnRDb250cm9sbGVyLnNpZ25hbC5hYm9ydGVkKSB7XG4gICAgICAgIHJldHVybiBsb2coXG4gICAgICAgICAgNCxcbiAgICAgICAgICBgW2V4cG9ydF0gRGlzY2FyZGluZyB0aGUgcmVzdWx0IGZvciByZXF1ZXN0ICR7dW5pcXVlSWR9OiB0aGUgY2xpZW50IGlzIGdvbmUuYFxuICAgICAgICApO1xuICAgICAgfVxuXG4gICAgICAvLyBBZnRlciB0aGUgd2hvbGUgZXhwb3J0aW5nIHByb2Nlc3NcbiAgICAgIGlmIChkZWZhdWx0T3B0aW9ucy5zZXJ2ZXIuYmVuY2htYXJraW5nKSB7XG4gICAgICAgIGxvZyhcbiAgICAgICAgICA1LFxuICAgICAgICAgIGBbYmVuY2htYXJrXSBSZXF1ZXN0IHdpdGggSUQgJHt1bmlxdWVJZH0gLSBBZnRlciB0aGUgd2hvbGUgZXhwb3J0aW5nIHByb2Nlc3M6ICR7c3RvcENvdW50ZXIoKX1tcy5gXG4gICAgICAgICk7XG4gICAgICB9XG5cbiAgICAgIC8vIElmIGVycm9yLCBsb2cgaXQgYW5kIHNlbmQgaXQgdG8gdGhlIGVycm9yIG1pZGRsZXdhcmVcbiAgICAgIGlmIChlcnJvcikge1xuICAgICAgICB0aHJvdyBlcnJvcjtcbiAgICAgIH1cblxuICAgICAgLy8gSWYgZGF0YSBpcyBtaXNzaW5nLCBsb2cgdGhlIG1lc3NhZ2UgYW5kIHNlbmQgaXQgdG8gdGhlIGVycm9yIG1pZGRsZXdhcmVcbiAgICAgIGlmICghaW5mbyB8fCAhaW5mby5yZXN1bHQpIHtcbiAgICAgICAgdGhyb3cgbmV3IEh0dHBFcnJvcihcbiAgICAgICAgICBgVW5leHBlY3RlZCByZXR1cm4gZnJvbSBjaGFydCBnZW5lcmF0aW9uLiBQbGVhc2UgY2hlY2sgeW91ciByZXF1ZXN0IGRhdGEuIEZvciB0aGUgcmVxdWVzdCB3aXRoIElEICR7dW5pcXVlSWR9LCB0aGUgcmVzdWx0IGlzICR7aW5mby5yZXN1bHR9LmAsXG4gICAgICAgICAgNDAwLFxuICAgICAgICAgIGVycm9yQ29kZXMuRVhQT1JUX0ZBSUxFRFxuICAgICAgICApO1xuICAgICAgfVxuXG4gICAgICAvLyBHZXQgdGhlIHR5cGUgZnJvbSBvcHRpb25zXG4gICAgICB0eXBlID0gaW5mby5vcHRpb25zLmV4cG9ydC50eXBlO1xuXG4gICAgICAvLyBUaGUgYWZ0ZXIgcmVxdWVzdCBjYWxsYmFja3NcbiAgICAgIGRvQ2FsbGJhY2tzKGFmdGVyUmVxdWVzdCwgcmVxdWVzdCwgcmVzcG9uc2UsIHsgaWQsIGJvZHk6IGluZm8ucmVzdWx0IH0pO1xuXG4gICAgICBpZiAoaW5mby5yZXN1bHQpIHtcbiAgICAgICAgLy8gSWYgb25seSBiYXNlNjQgaXMgcmVxdWlyZWQsIHJldHVybiBpdFxuICAgICAgICBpZiAoYm9keS5iNjQpIHtcbiAgICAgICAgICAvLyBTVkcgRXhjZXB0aW9uIGZvciB0aGUgSGlnaGNoYXJ0cyAxMS4zLjAgdmVyc2lvblxuICAgICAgICAgIGlmICh0eXBlID09PSAncGRmJyB8fCB0eXBlID09ICdzdmcnKSB7XG4gICAgICAgICAgICByZXR1cm4gcmVzcG9uc2Uuc2VuZChcbiAgICAgICAgICAgICAgQnVmZmVyLmZyb20oaW5mby5yZXN1bHQsICd1dGY4JykudG9TdHJpbmcoJ2Jhc2U2NCcpXG4gICAgICAgICAgICApO1xuICAgICAgICAgIH1cblxuICAgICAgICAgIHJldHVybiByZXNwb25zZS5zZW5kKGluZm8ucmVzdWx0KTtcbiAgICAgICAgfVxuXG4gICAgICAgIC8vIFNldCBjb3JyZWN0IGNvbnRlbnQgdHlwZVxuICAgICAgICByZXNwb25zZS5oZWFkZXIoJ0NvbnRlbnQtVHlwZScsIHJldmVyc2VkTWltZVt0eXBlXSB8fCAnaW1hZ2UvcG5nJyk7XG5cbiAgICAgICAgLy8gRGVjaWRlIHdoZXRoZXIgdG8gZG93bmxvYWQgb3Igbm90IGNoYXJ0IGZpbGVcbiAgICAgICAgaWYgKCFib2R5Lm5vRG93bmxvYWQpIHtcbiAgICAgICAgICByZXNwb25zZS5hdHRhY2htZW50KFxuICAgICAgICAgICAgYCR7cmVxdWVzdC5wYXJhbXMuZmlsZW5hbWUgfHwgcmVxdWVzdC5ib2R5LmZpbGVuYW1lIHx8ICdjaGFydCd9LiR7XG4gICAgICAgICAgICAgIHR5cGUgfHwgJ3BuZydcbiAgICAgICAgICAgIH1gXG4gICAgICAgICAgKTtcbiAgICAgICAgfVxuXG4gICAgICAgIC8vIElmIFNWRywgcmV0dXJuIHBsYWluIGNvbnRlbnRcbiAgICAgICAgcmV0dXJuIHR5cGUgPT09ICdzdmcnXG4gICAgICAgICAgPyByZXNwb25zZS5zZW5kKGluZm8ucmVzdWx0KVxuICAgICAgICAgIDogcmVzcG9uc2Uuc2VuZChCdWZmZXIuZnJvbShpbmZvLnJlc3VsdCwgJ2Jhc2U2NCcpKTtcbiAgICAgIH1cbiAgICB9KTtcbiAgfSBjYXRjaCAoZXJyb3IpIHtcbiAgICBuZXh0KGVycm9yKTtcbiAgfVxufTtcblxuZXhwb3J0IGRlZmF1bHQgKGFwcCkgPT4ge1xuICAvKipcbiAgICogQWRkcyB0aGUgUE9TVCAvIGEgcm91dGUgZm9yIGhhbmRsaW5nIFBPU1QgcmVxdWVzdHMgYXQgdGhlIHJvb3QgZW5kcG9pbnQuXG4gICAqL1xuICBhcHAucG9zdCgnLycsIGV4cG9ydEhhbmRsZXIpO1xuXG4gIC8qKlxuICAgKiBBZGRzIHRoZSBQT1NUIC86ZmlsZW5hbWUgYSByb3V0ZSBmb3IgaGFuZGxpbmcgUE9TVCByZXF1ZXN0cyB3aXRoXG4gICAqIGEgc3BlY2lmaWVkIGZpbGVuYW1lIHBhcmFtZXRlci5cbiAgICovXG4gIGFwcC5wb3N0KCcvOmZpbGVuYW1lJywgZXhwb3J0SGFuZGxlcik7XG59O1xuIiwiLyoqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKipcblxuSGlnaGNoYXJ0cyBFeHBvcnQgU2VydmVyXG5cbkNvcHlyaWdodCAoYykgMjAxNi0yMDI0LCBIaWdoc29mdFxuXG5MaWNlbmNlZCB1bmRlciB0aGUgTUlUIGxpY2VuY2UuXG5cbkFkZGl0aW9uYWxseSBhIHZhbGlkIEhpZ2hjaGFydHMgbGljZW5zZSBpcyByZXF1aXJlZCBmb3IgdXNlLlxuXG5TZWUgTElDRU5TRSBmaWxlIGluIHJvb3QgZm9yIGRldGFpbHMuXG5cbioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKiovXG5cbmltcG9ydCB7IHJlYWRGaWxlU3luYyB9IGZyb20gJ2ZzJztcbmltcG9ydCB7IGpvaW4gYXMgcGF0aGVyIH0gZnJvbSAncGF0aCc7XG5pbXBvcnQgeyBsb2cgfSBmcm9tICcuLi8uLi9sb2dnZXIuanMnO1xuXG5pbXBvcnQgeyB2ZXJzaW9uIH0gZnJvbSAnLi4vLi4vY2FjaGUuanMnO1xuaW1wb3J0IHsgYWRkSW50ZXJ2YWwgfSBmcm9tICcuLi8uLi9pbnRlcnZhbHMuanMnO1xuaW1wb3J0IHBvb2wsIHsgZ2V0Q29uc2VjdXRpdmVDcmVhdGVGYWlsdXJlcyB9IGZyb20gJy4uLy4uL3Bvb2wuanMnO1xuaW1wb3J0IHsgaXNDb25uZWN0ZWQgYXMgYnJvd3NlcklzQ29ubmVjdGVkIH0gZnJvbSAnLi4vLi4vYnJvd3Nlci5qcyc7XG5pbXBvcnQgeyBfX2Rpcm5hbWUgfSBmcm9tICcuLi8uLi91dGlscy5qcyc7XG5cbmNvbnN0IHBrZ0ZpbGUgPSBKU09OLnBhcnNlKHJlYWRGaWxlU3luYyhwYXRoZXIoX19kaXJuYW1lLCAncGFja2FnZS5qc29uJykpKTtcblxuY29uc3Qgc2VydmVyU3RhcnRUaW1lID0gbmV3IERhdGUoKTtcblxuY29uc3Qgc3VjY2Vzc1JhdGVzID0gW107XG5jb25zdCByZWNvcmRJbnRlcnZhbCA9IDYwICogMTAwMDsgLy8gcmVjb3JkIGV2ZXJ5IG1pbnV0ZVxuY29uc3Qgd2luZG93U2l6ZSA9IDMwOyAvLyAzMCBtaW51dGVzXG5cbi8qKlxuICogQ2FsY3VsYXRlcyBtb3ZpbmcgYXZlcmFnZSBpbmRpY2F0b3IgYmFzZWQgb24gdGhlIGRhdGEgZnJvbSB0aGUgc3VjY2Vzc1JhdGVzXG4gKiBhcnJheS5cbiAqXG4gKiBAcmV0dXJucyB7bnVtYmVyfSAtIEEgbW92aW5nIGF2ZXJhZ2UgZm9yIHN1Y2Nlc3MgcmF0aW8gb2YgdGhlIHNlcnZlciBleHBvcnRzLlxuICovXG5mdW5jdGlvbiBjYWxjdWxhdGVNb3ZpbmdBdmVyYWdlKCkge1xuICBjb25zdCBzdW0gPSBzdWNjZXNzUmF0ZXMucmVkdWNlKChhLCBiKSA9PiBhICsgYiwgMCk7XG4gIHJldHVybiBzdW0gLyBzdWNjZXNzUmF0ZXMubGVuZ3RoO1xufVxuXG4vKipcbiAqIFN0YXJ0cyB0aGUgaW50ZXJ2YWwgcmVzcG9uc2libGUgZm9yIGNhbGN1bGF0aW5nIGN1cnJlbnQgc3VjY2VzcyByYXRlIHJhdGlvXG4gKiBhbmQgZ2F0aGVyc1xuICpcbiAqIEByZXR1cm5zIHtOb2RlSlMuVGltZW91dH0gaWQgLSBJZCBvZiBhbiBpbnRlcnZhbC5cbiAqL1xuZXhwb3J0IGNvbnN0IHN0YXJ0U3VjY2Vzc1JhdGUgPSAoKSA9PlxuICBzZXRJbnRlcnZhbCgoKSA9PiB7XG4gICAgY29uc3Qgc3VjY2Vzc1JhdGlvID0gY2FsY3VsYXRlU3VjY2Vzc1JhdGlvKHBvb2wuZ2V0U3RhdHMoKSk7XG5cbiAgICBzdWNjZXNzUmF0ZXMucHVzaChzdWNjZXNzUmF0aW8gPT09IG51bGwgPyAxIDogc3VjY2Vzc1JhdGlvKTtcbiAgICBpZiAoc3VjY2Vzc1JhdGVzLmxlbmd0aCA+IHdpbmRvd1NpemUpIHtcbiAgICAgIHN1Y2Nlc3NSYXRlcy5zaGlmdCgpO1xuICAgIH1cbiAgfSwgcmVjb3JkSW50ZXJ2YWwpO1xuXG4vKipcbiAqIENhbGN1bGF0ZXMgdGhlIHJhdGlvIG9mIGV4cG9ydHMgdGhhdCBzdWNjZWVkZWQsIGFzIGEgcGVyY2VudGFnZS5cbiAqXG4gKiBFeHBvcnRzIGFiYW5kb25lZCBieSB0aGVpciBjbGllbnQgYXJlIGV4Y2x1ZGVkIGZyb20gdGhlIGRlbm9taW5hdG9yLiBUaGV5IGFyZVxuICogbm90IGZhaWx1cmVzIG9mIHRoZSBzZXJ2ZXIgLSB0aGUgY2FsbGVyIHdlbnQgYXdheSAtIGFuZCBjb3VudGluZyB0aGVtIHdvdWxkXG4gKiByZXBvcnQgYSBoZWFsdGh5IHNlcnZlciBhcyBmYWlsaW5nIHdoZW5ldmVyIGNhbGxlcnMgdGltZSBvdXQgb3IgY2FuY2VsLCB3aGljaFxuICogaXMgZXhhY3RseSB3aGVuIHRoaXMgZmlndXJlIGlzIG1vc3QgbGlrZWx5IHRvIGJlIGxvb2tlZCBhdC5cbiAqXG4gKiBAcGFyYW0ge09iamVjdH0gc3RhdHMgLSBUaGUgcG9vbCBzdGF0aXN0aWNzLlxuICpcbiAqIEByZXR1cm5zIHtudW1iZXJ8bnVsbH0gVGhlIHN1Y2Nlc3MgcmF0aW8gYXMgYSBwZXJjZW50YWdlLCBvciBudWxsIHdoZW4gbm9cbiAqIGV4cG9ydCBoYXMgYmVlbiBhdHRlbXB0ZWQgeWV0LlxuICovXG5mdW5jdGlvbiBjYWxjdWxhdGVTdWNjZXNzUmF0aW8oc3RhdHMpIHtcbiAgY29uc3QgYXR0ZW1wdHMgPSBzdGF0cy5leHBvcnRBdHRlbXB0cyAtIHN0YXRzLmFiYW5kb25lZEV4cG9ydHM7XG5cbiAgaWYgKGF0dGVtcHRzIDw9IDApIHtcbiAgICByZXR1cm4gbnVsbDtcbiAgfVxuXG4gIHJldHVybiAoc3RhdHMucGVyZm9ybWVkRXhwb3J0cyAvIGF0dGVtcHRzKSAqIDEwMDtcbn1cblxuLyoqXG4gKiBBZGRzIHRoZSAvaGVhbHRoIGFuZCAvc3VjY2Vzcy1tb3ZpbmctYXZlcmFnZSByb3V0ZXNcbiAqIHdoaWNoIG91dHB1dCBiYXNpYyBzdGF0cyBmb3IgdGhlIHNlcnZlci5cbiAqL1xuZXhwb3J0IGRlZmF1bHQgZnVuY3Rpb24gYWRkSGVhbHRoUm91dGVzKGFwcCkge1xuICBpZiAoIWFwcCkge1xuICAgIHJldHVybiBmYWxzZTtcbiAgfVxuXG4gIC8vIFN0YXJ0IHByb2Nlc3Npbmcgc3VjY2VzcyByYXRlIHJhdGlvIGludGVydmFsIGFuZCBzYXZlIGl0cyBpZCB0byB0aGUgYXJyYXlcbiAgLy8gZm9yIHRoZSBncmFjZWZ1bCBjbGVhcmluZyBvbiBzaHV0ZG93biB3aXRoIGluamVjdGVkIGFkZEludGVydmFsIGZ1bnRpb25cbiAgYWRkSW50ZXJ2YWwoc3RhcnRTdWNjZXNzUmF0ZSgpKTtcblxuICBhcHAuZ2V0KCcvaGVhbHRoJywgKF8sIHJlcykgPT4ge1xuICAgIGNvbnN0IHN0YXRzID0gcG9vbC5nZXRTdGF0cygpO1xuICAgIGNvbnN0IHBlcmlvZCA9IHN1Y2Nlc3NSYXRlcy5sZW5ndGg7XG4gICAgY29uc3QgbW92aW5nQXZlcmFnZSA9IGNhbGN1bGF0ZU1vdmluZ0F2ZXJhZ2UoKTtcblxuICAgIGxvZyg0LCAnW2hlYWx0aC5qc10gR0VUIC9oZWFsdGggWzIwMF0gLSByZXR1cm5pbmcgc2VydmVyIGhlYWx0aC4nKTtcblxuICAgIHJlcy5zZW5kKHtcbiAgICAgIHN0YXR1czogJ09LJyxcbiAgICAgIGJvb3RUaW1lOiBzZXJ2ZXJTdGFydFRpbWUsXG4gICAgICB1cHRpbWU6XG4gICAgICAgIE1hdGguZmxvb3IoXG4gICAgICAgICAgKG5ldyBEYXRlKCkuZ2V0VGltZSgpIC0gc2VydmVyU3RhcnRUaW1lLmdldFRpbWUoKSkgLyAxMDAwIC8gNjBcbiAgICAgICAgKSArICcgbWludXRlcycsXG4gICAgICB2ZXJzaW9uOiBwa2dGaWxlLnZlcnNpb24sXG4gICAgICBoaWdoY2hhcnRzVmVyc2lvbjogdmVyc2lvbigpLFxuICAgICAgYXZlcmFnZVByb2Nlc3NpbmdUaW1lOiBzdGF0cy5zcGVudEF2ZXJhZ2UsXG4gICAgICBwZXJmb3JtZWRFeHBvcnRzOiBzdGF0cy5wZXJmb3JtZWRFeHBvcnRzLFxuICAgICAgZmFpbGVkRXhwb3J0czogc3RhdHMuZHJvcHBlZEV4cG9ydHMsXG4gICAgICBhYmFuZG9uZWRFeHBvcnRzOiBzdGF0cy5hYmFuZG9uZWRFeHBvcnRzLFxuICAgICAgcmVqZWN0ZWRGb3JDYXBhY2l0eTogc3RhdHMucmVqZWN0ZWRGb3JDYXBhY2l0eSxcbiAgICAgIGNvbnNlY3V0aXZlQ3JlYXRlRmFpbHVyZXM6IGdldENvbnNlY3V0aXZlQ3JlYXRlRmFpbHVyZXMoKSxcbiAgICAgIGJyb3dzZXJDb25uZWN0ZWQ6IGJyb3dzZXJJc0Nvbm5lY3RlZCgpLFxuICAgICAgZXhwb3J0QXR0ZW1wdHM6IHN0YXRzLmV4cG9ydEF0dGVtcHRzLFxuICAgICAgc3VjZXNzUmF0aW86IGNhbGN1bGF0ZVN1Y2Nlc3NSYXRpbyhzdGF0cyksXG4gICAgICAvLyBlc2xpbnQtZGlzYWJsZS1uZXh0LWxpbmUgaW1wb3J0L25vLW5hbWVkLWFzLWRlZmF1bHQtbWVtYmVyXG4gICAgICBwb29sOiBwb29sLmdldFBvb2xJbmZvSlNPTigpLFxuXG4gICAgICAvLyBNb3ZpbmcgYXZlcmFnZVxuICAgICAgcGVyaW9kLFxuICAgICAgbW92aW5nQXZlcmFnZSxcbiAgICAgIG1lc3NhZ2U6XG4gICAgICAgIGlzTmFOKG1vdmluZ0F2ZXJhZ2UpIHx8ICFzdWNjZXNzUmF0ZXMubGVuZ3RoXG4gICAgICAgICAgPyAnVG9vIGVhcmx5IHRvIHJlcG9ydC4gTm8gZXhwb3J0cyBtYWRlIHlldC4gUGxlYXNlIGNoZWNrIGJhY2sgc29vbi4nXG4gICAgICAgICAgOiBgTGFzdCAke3BlcmlvZH0gbWludXRlcyBoYWQgYSBzdWNjZXNzIHJhdGUgb2YgJHttb3ZpbmdBdmVyYWdlLnRvRml4ZWQoMil9JS5gLFxuXG4gICAgICAvLyBTVkcvSlNPTiBhdHRlbXB0c1xuICAgICAgc3ZnRXhwb3J0QXR0ZW1wdHM6IHN0YXRzLmV4cG9ydEZyb21TdmdBdHRlbXB0cyxcbiAgICAgIGpzb25FeHBvcnRBdHRlbXB0czogc3RhdHMucGVyZm9ybWVkRXhwb3J0cyAtIHN0YXRzLmV4cG9ydEZyb21TdmdBdHRlbXB0c1xuICAgIH0pO1xuICB9KTtcbn1cbiIsIi8qKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqXG5cbkhpZ2hjaGFydHMgRXhwb3J0IFNlcnZlclxuXG5Db3B5cmlnaHQgKGMpIDIwMTYtMjAyNCwgSGlnaHNvZnRcblxuTGljZW5jZWQgdW5kZXIgdGhlIE1JVCBsaWNlbmNlLlxuXG5BZGRpdGlvbmFsbHkgYSB2YWxpZCBIaWdoY2hhcnRzIGxpY2Vuc2UgaXMgcmVxdWlyZWQgZm9yIHVzZS5cblxuU2VlIExJQ0VOU0UgZmlsZSBpbiByb290IGZvciBkZXRhaWxzLlxuXG4qKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqL1xuXG5pbXBvcnQgeyBwcm9taXNlcyBhcyBmc1Byb21pc2VzIH0gZnJvbSAnZnMnO1xuaW1wb3J0IHsgcG9zaXggfSBmcm9tICdwYXRoJztcblxuaW1wb3J0IGNvcnMgZnJvbSAnY29ycyc7XG5pbXBvcnQgZXhwcmVzcyBmcm9tICdleHByZXNzJztcbmltcG9ydCBodHRwIGZyb20gJ2h0dHAnO1xuaW1wb3J0IGh0dHBzIGZyb20gJ2h0dHBzJztcbmltcG9ydCBtdWx0ZXIgZnJvbSAnbXVsdGVyJztcblxuaW1wb3J0IGVycm9ySGFuZGxlciBmcm9tICcuL2Vycm9yLmpzJztcbmltcG9ydCByYXRlTGltaXQgZnJvbSAnLi9yYXRlX2xpbWl0LmpzJztcbmltcG9ydCB7IGxvZywgbG9nV2l0aFN0YWNrIH0gZnJvbSAnLi4vbG9nZ2VyLmpzJztcbmltcG9ydCB7XG4gIGdldFBvb2wsXG4gIGdldFF1ZXVlTGltaXQsXG4gIGdldFF1ZXVlUmVqZWN0RGVsYXksXG4gIHN0YXRzIGFzIHBvb2xTdGF0c1xufSBmcm9tICcuLi9wb29sLmpzJztcbmltcG9ydCB7IF9fZGlybmFtZSB9IGZyb20gJy4uL3V0aWxzLmpzJztcblxuaW1wb3J0IHsgZXJyb3JDb2RlcyB9IGZyb20gJy4uL2Vycm9ycy9jb2Rlcy5qcyc7XG5pbXBvcnQgSHR0cEVycm9yIGZyb20gJy4uL2Vycm9ycy9IdHRwRXJyb3IuanMnO1xuXG5pbXBvcnQgdlN3aXRjaFJvdXRlIGZyb20gJy4vcm91dGVzL2NoYW5nZV9oY192ZXJzaW9uLmpzJztcbmltcG9ydCBleHBvcnRSb3V0ZXMgZnJvbSAnLi9yb3V0ZXMvZXhwb3J0LmpzJztcbmltcG9ydCBoZWFsdGhSb3V0ZSBmcm9tICcuL3JvdXRlcy9oZWFsdGguanMnO1xuaW1wb3J0IHVpUm91dGUgZnJvbSAnLi9yb3V0ZXMvdWkuanMnO1xuXG5pbXBvcnQgRXhwb3J0RXJyb3IgZnJvbSAnLi4vZXJyb3JzL0V4cG9ydEVycm9yLmpzJztcblxuLy8gQXJyYXkgb2YgYW4gYWN0aXZlIHNlcnZlcnNcbmNvbnN0IGFjdGl2ZVNlcnZlcnMgPSBuZXcgTWFwKCk7XG5cbi8vIENyZWF0ZSBleHByZXNzIGFwcFxuY29uc3QgYXBwID0gZXhwcmVzcygpO1xuXG4vLyBEaXNhYmxlIHRoZSBYLVBvd2VyZWQtQnkgaGVhZGVyXG5hcHAuZGlzYWJsZSgneC1wb3dlcmVkLWJ5Jyk7XG5cbi8vIEVuYWJsZSBDT1JTIHN1cHBvcnRcbmFwcC51c2UoY29ycygpKTtcblxuLy8gR2V0dGluZyBhIGxvdCBvZiBSYW5nZU5vdFNhdGlzZmlhYmxlRXJyb3IgZXhjZXB0aW9uLlxuLy8gRXZlbiB0aG91Z2ggdGhpcyBpcyBhIGRlcHJlY2F0ZWQgb3B0aW9ucywgbGV0J3MgdHJ5IHRvIHNldCBpdCB0byBmYWxzZS5cbmFwcC51c2UoKF9yZXEsIHJlcywgbmV4dCkgPT4ge1xuICByZXMuc2V0KCdBY2NlcHQtUmFuZ2VzJywgJ25vbmUnKTtcbiAgbmV4dCgpO1xufSk7XG5cbi8qKlxuICogQXR0YWNoIGVycm9yIGhhbmRsZXJzIHRvIHRoZSBzZXJ2ZXIuXG4gKlxuICogQHBhcmFtIHtodHRwLlNlcnZlcn0gc2VydmVyIC0gVGhlIEhUVFAvSFRUUFMgc2VydmVyIGluc3RhbmNlLlxuICovXG5jb25zdCBhdHRhY2hTZXJ2ZXJFcnJvckhhbmRsZXJzID0gKHNlcnZlcikgPT4ge1xuICBzZXJ2ZXIub24oJ2NsaWVudEVycm9yJywgKGVycm9yLCBzb2NrZXQpID0+IHtcbiAgICBsb2dXaXRoU3RhY2soXG4gICAgICAxLFxuICAgICAgZXJyb3IsXG4gICAgICBgW3NlcnZlcl0gQ2xpZW50IGVycm9yOiAke2Vycm9yLm1lc3NhZ2V9LCBkZXN0cm95aW5nIHNvY2tldC5gXG4gICAgKTtcbiAgICBzb2NrZXQuZGVzdHJveSgpO1xuICB9KTtcblxuICBzZXJ2ZXIub24oJ2Vycm9yJywgKGVycm9yKSA9PiB7XG4gICAgbG9nV2l0aFN0YWNrKDEsIGVycm9yLCBgW3NlcnZlcl0gU2VydmVyIGVycm9yOiAke2Vycm9yLm1lc3NhZ2V9YCk7XG4gIH0pO1xuXG4gIHNlcnZlci5vbignY29ubmVjdGlvbicsIChzb2NrZXQpID0+IHtcbiAgICBzb2NrZXQub24oJ2Vycm9yJywgKGVycm9yKSA9PiB7XG4gICAgICBsb2dXaXRoU3RhY2soMSwgZXJyb3IsIGBbc2VydmVyXSBTb2NrZXQgZXJyb3I6ICR7ZXJyb3IubWVzc2FnZX1gKTtcbiAgICB9KTtcbiAgfSk7XG59O1xuXG4vKipcbiAqIEFwcGxpZXMgdGhlIGNvbm5lY3Rpb24gdGltZW91dHMgdG8gYSBzZXJ2ZXIuXG4gKlxuICogTk9URTogTm9kZSdzIGRlZmF1bHQga2VlcEFsaXZlVGltZW91dCBpcyA1IHNlY29uZHMsIHdoaWNoIGlzIHNob3J0ZXIgdGhhbiB0aGVcbiAqICAgICAgIGlkbGUgdGltZW91dCBvZiBhIHR5cGljYWwgcHJveHkgb3IgbG9hZCBiYWxhbmNlciBzaXR0aW5nIGluIGZyb250IG9mIHRoaXNcbiAqICAgICAgIHNlcnZlciAtIGNvbW1vbmx5IDYwLiBXaGVuIHRoZSBzaG9ydGVyIHNpZGUgY2xvc2VzIGFuIGlkbGUgY29ubmVjdGlvbiB0aGVcbiAqICAgICAgIG90aGVyIHNpZGUgZG9lcyBub3Qga25vdywgc28gaXQgY2FuIHNlbmQgYSByZXF1ZXN0IGludG8gYSBjb25uZWN0aW9uIHRoYXRcbiAqICAgICAgIGlzIGFscmVhZHkgZ29pbmcgYXdheSwgYW5kIHRoZSBjYWxsZXIgc2VlcyBhIGdhdGV3YXkgZXJyb3IgdGhhdCBoYXNcbiAqICAgICAgIG5vdGhpbmcgdG8gZG8gd2l0aCB0aGUgcmVxdWVzdC4gS2VlcGluZyBvdXJzIGxvbmdlciB0aGFuIHRoZWlycyBsZWF2ZXMgdGhlXG4gKiAgICAgICBjbG9zaW5nIHRvIHRoZW0uXG4gKlxuICogICAgICAgaGVhZGVyc1RpbWVvdXQgaXMga2VwdCBhYm92ZSBrZWVwQWxpdmVUaW1lb3V0IGRlbGliZXJhdGVseTogaWYgaXQgd2VyZVxuICogICAgICAgc2hvcnRlciBpdCB3b3VsZCBmaXJlIHdoaWxlIGEga2VwdC1hbGl2ZSBjb25uZWN0aW9uIHdhcyBzdGlsbCBsZWdpdGltYXRlbHlcbiAqICAgICAgIGlkbGUgYmV0d2VlbiByZXF1ZXN0cy5cbiAqXG4gKiBAcGFyYW0ge2h0dHAuU2VydmVyfSBzZXJ2ZXIgLSBUaGUgSFRUUC9IVFRQUyBzZXJ2ZXIgaW5zdGFuY2UuXG4gKiBAcGFyYW0ge09iamVjdH0gc2VydmVyQ29uZmlnIC0gVGhlIHNlcnZlciBjb25maWd1cmF0aW9uIG9iamVjdC5cbiAqL1xuY29uc3QgYXBwbHlTZXJ2ZXJUaW1lb3V0cyA9IChzZXJ2ZXIsIHNlcnZlckNvbmZpZykgPT4ge1xuICBjb25zdCBrZWVwQWxpdmUgPSBwYXJzZUludChzZXJ2ZXJDb25maWcua2VlcEFsaXZlVGltZW91dCk7XG4gIGNvbnN0IGtlZXBBbGl2ZVRpbWVvdXQgPVxuICAgIGlzTmFOKGtlZXBBbGl2ZSkgfHwga2VlcEFsaXZlIDwgMCA/IDY1MDAwIDoga2VlcEFsaXZlO1xuXG4gIHNlcnZlci5rZWVwQWxpdmVUaW1lb3V0ID0ga2VlcEFsaXZlVGltZW91dDtcbiAgc2VydmVyLmhlYWRlcnNUaW1lb3V0ID0ga2VlcEFsaXZlVGltZW91dCArIDUwMDA7XG5cbiAgbG9nKFxuICAgIDQsXG4gICAgYFtzZXJ2ZXJdIFNldCBrZWVwQWxpdmVUaW1lb3V0IHRvICR7c2VydmVyLmtlZXBBbGl2ZVRpbWVvdXR9bXMgYW5kIGhlYWRlcnNUaW1lb3V0IHRvICR7c2VydmVyLmhlYWRlcnNUaW1lb3V0fW1zLmBcbiAgKTtcbn07XG5cbi8qKlxuICogU3RhcnRzIGFuIEhUVFAgc2VydmVyIGJhc2VkIG9uIHRoZSBwcm92aWRlZCBjb25maWd1cmF0aW9uLiBUaGUgYHNlcnZlckNvbmZpZ2BcbiAqIG9iamVjdCBjb250YWlucyBhbGwgc2VydmVyIHJlbGF0ZWQgcHJvcGVydGllcyAoc2VlIHRoZSBgc2VydmVyYCBzZWN0aW9uXG4gKiBpbiB0aGUgYGxpYi9zY2hlbWFzL2NvbmZpZy5qc2AgZmlsZSBmb3IgYSByZWZlcmVuY2UpLlxuICpcbiAqIEBwYXJhbSB7T2JqZWN0fSBzZXJ2ZXJDb25maWcgLSBUaGUgc2VydmVyIGNvbmZpZ3VyYXRpb24gb2JqZWN0LlxuICpcbiAqIEB0aHJvd3Mge0V4cG9ydEVycm9yfSAtIFRocm93cyBhbiBlcnJvciBpZiB0aGUgc2VydmVyIGNhbm5vdCBiZSBjb25maWd1cmVkXG4gKiBhbmQgc3RhcnRlZC5cbiAqL1xuZXhwb3J0IGNvbnN0IHN0YXJ0U2VydmVyID0gYXN5bmMgKHNlcnZlckNvbmZpZykgPT4ge1xuICB0cnkge1xuICAgIC8vIFRPRE86IFJlYWQgZnJvbSBjb25maWcvZW52XG4gICAgLy8gTk9URTpcbiAgICAvLyBUb28gYmlnIGxpbWl0cyBsZWFkIHRvIHRpbWVvdXRzIGluIHRoZSBleHBvcnQgcHJvY2VzcyB3aGVuIHRoZVxuICAgIC8vIHJhc3Rlcml6YXRpb24gdGltZW91dCBpcyBzZXQgdG9vIGxvdy5cbiAgICBjb25zdCB1cGxvYWRMaW1pdE1pQiA9IHNlcnZlckNvbmZpZy5tYXhVcGxvYWRTaXplIHx8IDM7XG4gICAgY29uc3QgdXBsb2FkTGltaXRCeXRlcyA9IHVwbG9hZExpbWl0TWlCICogMTAyNCAqIDEwMjQ7XG5cbiAgICAvLyBFbmFibGUgcGFyc2luZyBvZiBmb3JtIGRhdGEgKGZpbGVzKSB3aXRoIE11bHRlciBwYWNrYWdlXG4gICAgY29uc3Qgc3RvcmFnZSA9IG11bHRlci5tZW1vcnlTdG9yYWdlKCk7XG4gICAgY29uc3QgdXBsb2FkID0gbXVsdGVyKHtcbiAgICAgIHN0b3JhZ2UsXG4gICAgICBsaW1pdHM6IHtcbiAgICAgICAgZmllbGRTaXplOiB1cGxvYWRMaW1pdEJ5dGVzXG4gICAgICB9XG4gICAgfSk7XG5cbiAgICAvLyBOT1RFOiBSZWZ1c2Ugd29yayBiZWZvcmUgdGhlIGJvZHkgaXMgcGFyc2VkIHdoZW4gdGhlIHF1ZXVlIGlzIGFscmVhZHlcbiAgICAvLyAgICAgICBmdWxsLiBDaGVja2luZyBvbmx5IGluc2lkZSB0aGUgcG9vbCB3b3VsZCBtZWFuIGEgcmVxdWVzdCB0aGF0IGlzXG4gICAgLy8gICAgICAgZ29pbmcgdG8gYmUgcmVmdXNlZCBhbnl3YXkgaGFzIGl0cyBib2R5IC0gdXAgdG8gbWF4VXBsb2FkU2l6ZSwgM01pQlxuICAgIC8vICAgICAgIGJ5IGRlZmF1bHQgLSByZWFkIGFuZCBoZWxkIGluIG1lbW9yeSBmaXJzdC4gV2l0aCBhIGRlZXAgcXVldWUgdGhhdFxuICAgIC8vICAgICAgIGlzIGV4YWN0bHkgdGhlIG1lbW9yeSBwcmVzc3VyZSBhIHNhdHVyYXRlZCBzZXJ2ZXIgY2Fubm90IGFmZm9yZCxcbiAgICAvLyAgICAgICBhbmQgaXQgaXMgd2hhdCBldmVudHVhbGx5IGdldHMgdGhlIGJyb3dzZXIga2lsbGVkLlxuICAgIGFwcC51c2UoKHJlcXVlc3QsIHJlc3BvbnNlLCBuZXh0KSA9PiB7XG4gICAgICAvLyBPbmx5IGV4cG9ydCByZXF1ZXN0cyBhcmUgd29ydGggZ2F0aW5nOyB0aGUgYWRtaW4gdmVyc2lvbiByb3V0ZSBpcyBub3RcbiAgICAgIC8vIHBvb2wgd29yayBhbmQgbXVzdCBzdGF5IHJlYWNoYWJsZSB3aGVuIHRoZSBzZXJ2ZXIgaXMgYnVzeVxuICAgICAgaWYgKHJlcXVlc3QubWV0aG9kICE9PSAnUE9TVCcgfHwgcmVxdWVzdC5wYXRoLnN0YXJ0c1dpdGgoJy92ZXJzaW9uLycpKSB7XG4gICAgICAgIHJldHVybiBuZXh0KCk7XG4gICAgICB9XG5cbiAgICAgIGNvbnN0IHBvb2wgPSBnZXRQb29sKCk7XG5cbiAgICAgIGlmIChwb29sICYmIHBvb2wubnVtUGVuZGluZ0FjcXVpcmVzKCkgPj0gZ2V0UXVldWVMaW1pdCgpKSB7XG4gICAgICAgICsrcG9vbFN0YXRzLnJlamVjdGVkRm9yQ2FwYWNpdHk7XG5cbiAgICAgICAgY29uc3QgZXJyb3IgPSBuZXcgSHR0cEVycm9yKFxuICAgICAgICAgIGBUaGUgc2VydmVyIGlzIGF0IGNhcGFjaXR5OiAke3Bvb2wubnVtUGVuZGluZ0FjcXVpcmVzKCl9IGV4cG9ydHMgYXJlIGFscmVhZHkgd2FpdGluZyBmb3IgYSB3b3JrZXIgKGxpbWl0IGlzICR7Z2V0UXVldWVMaW1pdCgpfSkuIFBsZWFzZSByZXRyeSBzaG9ydGx5LmAsXG4gICAgICAgICAgNDAwLFxuICAgICAgICAgIGVycm9yQ29kZXMuUVVFVUVfRlVMTFxuICAgICAgICApO1xuXG4gICAgICAgIC8vIE5PVEU6IERvIG5vdCBhbnN3ZXIgaW1tZWRpYXRlbHkgYnkgZGVmYXVsdC4gTWVhc3VyZWQ6IHdpdGggYW4gaW5zdGFudFxuICAgICAgICAvLyAgICAgICByZWZ1c2FsLCBjbGllbnRzIHRoYXQgcmV0cnkgYXMgc29vbiBhcyB0aGV5IGFyZSByZWZ1c2VkIGRyaXZlXG4gICAgICAgIC8vICAgICAgIHRoZSByZXF1ZXN0IHJhdGUgdXAgYnkgb3JkZXJzIG9mIG1hZ25pdHVkZSAtIDE1MCBjb25jdXJyZW50XG4gICAgICAgIC8vICAgICAgIGNsaWVudHMgcmVhY2hlZCAxMTAwMCByZXF1ZXN0cyBwZXIgc2Vjb25kIC0gYW5kIHRoZSBzZXJ2ZXIgdGhlblxuICAgICAgICAvLyAgICAgICBzcGVuZHMgaXRzIGVudGlyZSBldmVudCBsb29wIHJlZnVzaW5nIHRoZW0sIHN0YXJ2aW5nIHRoZSBleHBvcnRzXG4gICAgICAgIC8vICAgICAgIGFscmVhZHkgaW4gcHJvZ3Jlc3MuIEdvb2RwdXQgZmVsbCBmcm9tIH4xOSBleHBvcnRzIHBlciBzZWNvbmQgdG9cbiAgICAgICAgLy8gICAgICAgdW5kZXIgMS5cbiAgICAgICAgLy9cbiAgICAgICAgLy8gICAgICAgVGhlIDUgc2Vjb25kIGFjcXVpcmUgdGltZW91dCB1c2VkIHRvIHByb3ZpZGUgdGhpcyBiYWNrcHJlc3N1cmVcbiAgICAgICAgLy8gICAgICAgYWNjaWRlbnRhbGx5LCBieSBtYWtpbmcgZXZlcnkgY2xpZW50IHdhaXQgYmVmb3JlIGl0IGNvdWxkIHJldHJ5LlxuICAgICAgICAvLyAgICAgICBCb3VuZGluZyB0aGUgcXVldWUgcmVtb3ZlcyB0aGF0LCBzbyB0aGUgZGVsYXkgcHV0cyBpdCBiYWNrXG4gICAgICAgIC8vICAgICAgIGRlbGliZXJhdGVseSwgYW5kIGZhciBtb3JlIGNoZWFwbHk6IG5vIGJvZHkgaGFzIGJlZW4gcGFyc2VkIGFuZFxuICAgICAgICAvLyAgICAgICBubyB3b3JrZXIgaXMgaGVsZCwgb25seSBhIHNvY2tldCBhbmQgYSB0aW1lci5cbiAgICAgICAgY29uc3QgZGVsYXkgPSBnZXRRdWV1ZVJlamVjdERlbGF5KCk7XG5cbiAgICAgICAgaWYgKCFkZWxheSkge1xuICAgICAgICAgIHJldHVybiBuZXh0KGVycm9yKTtcbiAgICAgICAgfVxuXG4gICAgICAgIGNvbnN0IG9uQ2xvc2UgPSAoKSA9PiBjbGVhclRpbWVvdXQodGltZXIpO1xuXG4gICAgICAgIGNvbnN0IHRpbWVyID0gc2V0VGltZW91dCgoKSA9PiB7XG4gICAgICAgICAgcmVzcG9uc2UucmVtb3ZlTGlzdGVuZXIoJ2Nsb3NlJywgb25DbG9zZSk7XG4gICAgICAgICAgbmV4dChlcnJvcik7XG4gICAgICAgIH0sIGRlbGF5KTtcblxuICAgICAgICAvLyBEbyBub3Qga2VlcCBhIHRpbWVyIGFsaXZlIGZvciBhIGNsaWVudCB0aGF0IGhhcyBhbHJlYWR5IGdvbmVcbiAgICAgICAgcmVzcG9uc2Uub25jZSgnY2xvc2UnLCBvbkNsb3NlKTtcblxuICAgICAgICByZXR1cm47XG4gICAgICB9XG5cbiAgICAgIG5leHQoKTtcbiAgICB9KTtcblxuICAgIC8vIEVuYWJsZSBib2R5IHBhcnNlclxuICAgIGFwcC51c2UoZXhwcmVzcy5qc29uKHsgbGltaXQ6IHVwbG9hZExpbWl0Qnl0ZXMgfSkpO1xuICAgIGFwcC51c2UoZXhwcmVzcy51cmxlbmNvZGVkKHsgZXh0ZW5kZWQ6IHRydWUsIGxpbWl0OiB1cGxvYWRMaW1pdEJ5dGVzIH0pKTtcblxuICAgIC8vIFVzZSBvbmx5IG5vbi1maWxlIG11bHRpcGFydCBmb3JtIGZpZWxkc1xuICAgIGFwcC51c2UodXBsb2FkLm5vbmUoKSk7XG5cbiAgICAvLyBTdG9wIGlmIG5vdCBlbmFibGVkXG4gICAgaWYgKCFzZXJ2ZXJDb25maWcuZW5hYmxlKSB7XG4gICAgICByZXR1cm4gZmFsc2U7XG4gICAgfVxuXG4gICAgLy8gTGlzdGVuIEhUVFAgc2VydmVyXG4gICAgaWYgKCFzZXJ2ZXJDb25maWcuc3NsLmZvcmNlKSB7XG4gICAgICAvLyBNYWluIHNlcnZlciBpbnN0YW5jZSAoSFRUUClcbiAgICAgIGNvbnN0IGh0dHBTZXJ2ZXIgPSBodHRwLmNyZWF0ZVNlcnZlcihhcHApO1xuXG4gICAgICAvLyBBdHRhY2ggZXJyb3IgaGFuZGxlcnMgYW5kIGxpc3RlbiB0byB0aGUgc2VydmVyXG4gICAgICBhdHRhY2hTZXJ2ZXJFcnJvckhhbmRsZXJzKGh0dHBTZXJ2ZXIpO1xuICAgICAgYXBwbHlTZXJ2ZXJUaW1lb3V0cyhodHRwU2VydmVyLCBzZXJ2ZXJDb25maWcpO1xuXG4gICAgICAvLyBMaXN0ZW5cbiAgICAgIGh0dHBTZXJ2ZXIubGlzdGVuKHNlcnZlckNvbmZpZy5wb3J0LCBzZXJ2ZXJDb25maWcuaG9zdCk7XG5cbiAgICAgIC8vIFNhdmUgdGhlIHJlZmVyZW5jZSB0byBIVFRQIHNlcnZlclxuICAgICAgYWN0aXZlU2VydmVycy5zZXQoc2VydmVyQ29uZmlnLnBvcnQsIGh0dHBTZXJ2ZXIpO1xuXG4gICAgICBsb2coXG4gICAgICAgIDMsXG4gICAgICAgIGBbc2VydmVyXSBTdGFydGVkIEhUVFAgc2VydmVyIG9uICR7c2VydmVyQ29uZmlnLmhvc3R9OiR7c2VydmVyQ29uZmlnLnBvcnR9LmBcbiAgICAgICk7XG4gICAgfVxuXG4gICAgLy8gTGlzdGVuIEhUVFBTIHNlcnZlclxuICAgIGlmIChzZXJ2ZXJDb25maWcuc3NsLmVuYWJsZSkge1xuICAgICAgLy8gU2V0IHVwIGFuIFNTTCBzZXJ2ZXIgYWxzb1xuICAgICAgbGV0IGtleSwgY2VydDtcblxuICAgICAgdHJ5IHtcbiAgICAgICAgLy8gR2V0IHRoZSBTU0wga2V5XG4gICAgICAgIGtleSA9IGF3YWl0IGZzUHJvbWlzZXMucmVhZEZpbGUoXG4gICAgICAgICAgcG9zaXguam9pbihzZXJ2ZXJDb25maWcuc3NsLmNlcnRQYXRoLCAnc2VydmVyLmtleScpLFxuICAgICAgICAgICd1dGY4J1xuICAgICAgICApO1xuXG4gICAgICAgIC8vIEdldCB0aGUgU1NMIGNlcnRpZmljYXRlXG4gICAgICAgIGNlcnQgPSBhd2FpdCBmc1Byb21pc2VzLnJlYWRGaWxlKFxuICAgICAgICAgIHBvc2l4LmpvaW4oc2VydmVyQ29uZmlnLnNzbC5jZXJ0UGF0aCwgJ3NlcnZlci5jcnQnKSxcbiAgICAgICAgICAndXRmOCdcbiAgICAgICAgKTtcbiAgICAgIH0gY2F0Y2ggKGVycm9yKSB7XG4gICAgICAgIGxvZyhcbiAgICAgICAgICAyLFxuICAgICAgICAgIGBbc2VydmVyXSBVbmFibGUgdG8gbG9hZCBrZXkvY2VydGlmaWNhdGUgZnJvbSB0aGUgJyR7c2VydmVyQ29uZmlnLnNzbC5jZXJ0UGF0aH0nIHBhdGguIENvdWxkIG5vdCBydW4gc2VjdXJlZCBsYXllciBzZXJ2ZXIuYFxuICAgICAgICApO1xuICAgICAgfVxuXG4gICAgICBpZiAoa2V5ICYmIGNlcnQpIHtcbiAgICAgICAgLy8gTWFpbiBzZXJ2ZXIgaW5zdGFuY2UgKEhUVFBTKVxuICAgICAgICBjb25zdCBodHRwc1NlcnZlciA9IGh0dHBzLmNyZWF0ZVNlcnZlcih7IGtleSwgY2VydCB9LCBhcHApO1xuXG4gICAgICAgIC8vIEF0dGFjaCBlcnJvciBoYW5kbGVycyBhbmQgbGlzdGVuIHRvIHRoZSBzZXJ2ZXJcbiAgICAgICAgYXR0YWNoU2VydmVyRXJyb3JIYW5kbGVycyhodHRwc1NlcnZlcik7XG4gICAgICAgIGFwcGx5U2VydmVyVGltZW91dHMoaHR0cHNTZXJ2ZXIsIHNlcnZlckNvbmZpZyk7XG5cbiAgICAgICAgLy8gTGlzdGVuXG4gICAgICAgIGh0dHBzU2VydmVyLmxpc3RlbihzZXJ2ZXJDb25maWcuc3NsLnBvcnQsIHNlcnZlckNvbmZpZy5ob3N0KTtcblxuICAgICAgICAvLyBTYXZlIHRoZSByZWZlcmVuY2UgdG8gSFRUUFMgc2VydmVyXG4gICAgICAgIGFjdGl2ZVNlcnZlcnMuc2V0KHNlcnZlckNvbmZpZy5zc2wucG9ydCwgaHR0cHNTZXJ2ZXIpO1xuXG4gICAgICAgIGxvZyhcbiAgICAgICAgICAzLFxuICAgICAgICAgIGBbc2VydmVyXSBTdGFydGVkIEhUVFBTIHNlcnZlciBvbiAke3NlcnZlckNvbmZpZy5ob3N0fToke3NlcnZlckNvbmZpZy5zc2wucG9ydH0uYFxuICAgICAgICApO1xuICAgICAgfVxuICAgIH1cblxuICAgIC8vIEVuYWJsZSB0aGUgcmF0ZSBsaW1pdGVyIGlmIGNvbmZpZyBzYXlzIHNvXG4gICAgaWYgKFxuICAgICAgc2VydmVyQ29uZmlnLnJhdGVMaW1pdGluZyAmJlxuICAgICAgc2VydmVyQ29uZmlnLnJhdGVMaW1pdGluZy5lbmFibGUgJiZcbiAgICAgICFbMCwgTmFOXS5pbmNsdWRlcyhzZXJ2ZXJDb25maWcucmF0ZUxpbWl0aW5nLm1heFJlcXVlc3RzKVxuICAgICkge1xuICAgICAgcmF0ZUxpbWl0KGFwcCwgc2VydmVyQ29uZmlnLnJhdGVMaW1pdGluZyk7XG4gICAgfVxuXG4gICAgLy8gU2V0IHVwIHN0YXRpYyBmb2xkZXIncyByb3V0ZVxuICAgIGFwcC51c2UoZXhwcmVzcy5zdGF0aWMocG9zaXguam9pbihfX2Rpcm5hbWUsICdwdWJsaWMnKSkpO1xuXG4gICAgLy8gU2V0IHVwIHJvdXRlc1xuICAgIGhlYWx0aFJvdXRlKGFwcCk7XG4gICAgZXhwb3J0Um91dGVzKGFwcCk7XG4gICAgdWlSb3V0ZShhcHApO1xuICAgIHZTd2l0Y2hSb3V0ZShhcHApO1xuXG4gICAgLy8gU2V0IHVwIGNlbnRyYWxpemVkIGVycm9yIGhhbmRsZXJcbiAgICBlcnJvckhhbmRsZXIoYXBwKTtcbiAgfSBjYXRjaCAoZXJyb3IpIHtcbiAgICB0aHJvdyBuZXcgRXhwb3J0RXJyb3IoXG4gICAgICAnW3NlcnZlcl0gQ291bGQgbm90IGNvbmZpZ3VyZSBhbmQgc3RhcnQgdGhlIHNlcnZlci4nXG4gICAgKS5zZXRFcnJvcihlcnJvcik7XG4gIH1cbn07XG5cbi8qKlxuICogQ2xvc2VzIGFsbCBzZXJ2ZXJzIGFzc29jaWF0ZWQgd2l0aCBFeHByZXNzIGFwcCBpbnN0YW5jZSwgcmVzb2x2aW5nIG9uY2UgdGhlXG4gKiByZXF1ZXN0cyBhbHJlYWR5IGluIGZsaWdodCBoYXZlIGJlZW4gc2VydmVkLlxuICpcbiAqIEByZXR1cm5zIHtQcm9taXNlPHZvaWQ+fSBSZXNvbHZlcyB3aGVuIGV2ZXJ5IHNlcnZlciBoYXMgY2xvc2VkLlxuICovXG5leHBvcnQgY29uc3QgY2xvc2VTZXJ2ZXJzID0gKCkgPT4ge1xuICBsb2coNCwgYFtzZXJ2ZXJdIENsb3NpbmcgYWxsIHNlcnZlcnMuYCk7XG5cbiAgcmV0dXJuIFByb21pc2UuYWxsKFxuICAgIFsuLi5hY3RpdmVTZXJ2ZXJzXS5tYXAoXG4gICAgICAoW3BvcnQsIHNlcnZlcl0pID0+XG4gICAgICAgIG5ldyBQcm9taXNlKChyZXNvbHZlKSA9PiB7XG4gICAgICAgICAgLy8gY2xvc2UoKSBzdG9wcyBuZXcgY29ubmVjdGlvbnMgYmVpbmcgYWNjZXB0ZWQgYW5kIGNhbGxzIGJhY2sgb25jZSB0aGVcbiAgICAgICAgICAvLyBvbmVzIGluIGZsaWdodCBoYXZlIGZpbmlzaGVkLCB3aGljaCBpcyB3aGF0IG1ha2VzIGEgZ3JhY2VmdWwgc2h1dGRvd25cbiAgICAgICAgICAvLyBwb3NzaWJsZSAtIHRoZSBwcmV2aW91cyB2ZXJzaW9uIGRpc2NhcmRlZCB0aGF0IGNhbGxiYWNrIGVudGlyZWx5XG4gICAgICAgICAgc2VydmVyLmNsb3NlKCgpID0+IHtcbiAgICAgICAgICAgIGFjdGl2ZVNlcnZlcnMuZGVsZXRlKHBvcnQpO1xuICAgICAgICAgICAgbG9nKDQsIGBbc2VydmVyXSBDbG9zZWQgc2VydmVyIG9uIHBvcnQ6ICR7cG9ydH0uYCk7XG4gICAgICAgICAgICByZXNvbHZlKCk7XG4gICAgICAgICAgfSk7XG5cbiAgICAgICAgICAvLyBOT1RFOiBXaXRob3V0IHRoaXMsIGNsb3NlKCkgYWxzbyB3YWl0cyBmb3IgY29ubmVjdGlvbnMgdGhhdCBhcmUgbWVyZWx5XG4gICAgICAgICAgLy8gICAgICAgaWRsZSwgd2hpY2ggbm93IG1lYW5zIHVwIHRvIGtlZXBBbGl2ZVRpbWVvdXQgLSA2NSBzZWNvbmRzLiBJZGxlXG4gICAgICAgICAgLy8gICAgICAgY29ubmVjdGlvbnMgaGF2ZSBubyB3b3JrIHdvcnRoIHdhaXRpbmcgZm9yLCBzbyBlbmQgdGhlbSBhbmQgbGV0XG4gICAgICAgICAgLy8gICAgICAgY2xvc2UoKSB3YWl0IG9ubHkgb24gcmVxdWVzdHMgYWN0dWFsbHkgYmVpbmcgc2VydmVkLlxuICAgICAgICAgIHNlcnZlci5jbG9zZUlkbGVDb25uZWN0aW9ucz8uKCk7XG4gICAgICAgIH0pXG4gICAgKVxuICApO1xufTtcblxuLyoqXG4gKiBHZXQgYWxsIHNlcnZlcnMgYXNzb2NpYXRlZCB3aXRoIEV4cHJlc3MgYXBwIGluc3RhbmNlLlxuICpcbiAqIEByZXR1cm5zIHtBcnJheX0gLSBTZXJ2ZXJzIGFzc29jaWF0ZWQgd2l0aCBFeHByZXNzIGFwcCBpbnN0YW5jZS5cbiAqL1xuZXhwb3J0IGNvbnN0IGdldFNlcnZlcnMgPSAoKSA9PiBhY3RpdmVTZXJ2ZXJzO1xuXG4vKipcbiAqIEVuYWJsZSByYXRlIGxpbWl0aW5nIGZvciB0aGUgc2VydmVyLlxuICpcbiAqIEBwYXJhbSB7T2JqZWN0fSBsaW1pdENvbmZpZyAtIENvbmZpZ3VyYXRpb24gb2JqZWN0IGZvciByYXRlIGxpbWl0aW5nLlxuICovXG5leHBvcnQgY29uc3QgZW5hYmxlUmF0ZUxpbWl0aW5nID0gKGxpbWl0Q29uZmlnKSA9PiByYXRlTGltaXQoYXBwLCBsaW1pdENvbmZpZyk7XG5cbi8qKlxuICogR2V0IHRoZSBFeHByZXNzIGluc3RhbmNlLlxuICpcbiAqIEByZXR1cm5zIHtPYmplY3R9IC0gVGhlIEV4cHJlc3MgaW5zdGFuY2UuXG4gKi9cbmV4cG9ydCBjb25zdCBnZXRFeHByZXNzID0gKCkgPT4gZXhwcmVzcztcblxuLyoqXG4gKiBHZXQgdGhlIEV4cHJlc3MgYXBwIGluc3RhbmNlLlxuICpcbiAqIEByZXR1cm5zIHtPYmplY3R9IC0gVGhlIEV4cHJlc3MgYXBwIGluc3RhbmNlLlxuICovXG5leHBvcnQgY29uc3QgZ2V0QXBwID0gKCkgPT4gYXBwO1xuXG4vKipcbiAqIEFwcGx5IG1pZGRsZXdhcmUocykgdG8gYSBzcGVjaWZpYyBwYXRoLlxuICpcbiAqIEBwYXJhbSB7c3RyaW5nfSBwYXRoIC0gVGhlIHBhdGggdG8gd2hpY2ggdGhlIG1pZGRsZXdhcmUocykgc2hvdWxkIGJlIGFwcGxpZWQuXG4gKiBAcGFyYW0gey4uLkZ1bmN0aW9ufSBtaWRkbGV3YXJlcyAtIFRoZSBtaWRkbGV3YXJlIGZ1bmN0aW9ucyB0byBiZSBhcHBsaWVkLlxuICovXG5leHBvcnQgY29uc3QgdXNlID0gKHBhdGgsIC4uLm1pZGRsZXdhcmVzKSA9PiB7XG4gIGFwcC51c2UocGF0aCwgLi4ubWlkZGxld2FyZXMpO1xufTtcblxuLyoqXG4gKiBTZXQgdXAgYSByb3V0ZSB3aXRoIEdFVCBtZXRob2QgYW5kIGFwcGx5IG1pZGRsZXdhcmUocykuXG4gKlxuICogQHBhcmFtIHtzdHJpbmd9IHBhdGggLSBUaGUgcm91dGUgcGF0aC5cbiAqIEBwYXJhbSB7Li4uRnVuY3Rpb259IG1pZGRsZXdhcmVzIC0gVGhlIG1pZGRsZXdhcmUgZnVuY3Rpb25zIHRvIGJlIGFwcGxpZWQuXG4gKi9cbmV4cG9ydCBjb25zdCBnZXQgPSAocGF0aCwgLi4ubWlkZGxld2FyZXMpID0+IHtcbiAgYXBwLmdldChwYXRoLCAuLi5taWRkbGV3YXJlcyk7XG59O1xuXG4vKipcbiAqIFNldCB1cCBhIHJvdXRlIHdpdGggUE9TVCBtZXRob2QgYW5kIGFwcGx5IG1pZGRsZXdhcmUocykuXG4gKlxuICogQHBhcmFtIHtzdHJpbmd9IHBhdGggLSBUaGUgcm91dGUgcGF0aC5cbiAqIEBwYXJhbSB7Li4uRnVuY3Rpb259IG1pZGRsZXdhcmVzIC0gVGhlIG1pZGRsZXdhcmUgZnVuY3Rpb25zIHRvIGJlIGFwcGxpZWQuXG4gKi9cbmV4cG9ydCBjb25zdCBwb3N0ID0gKHBhdGgsIC4uLm1pZGRsZXdhcmVzKSA9PiB7XG4gIGFwcC5wb3N0KHBhdGgsIC4uLm1pZGRsZXdhcmVzKTtcbn07XG5cbmV4cG9ydCBkZWZhdWx0IHtcbiAgc3RhcnRTZXJ2ZXIsXG4gIGNsb3NlU2VydmVycyxcbiAgZ2V0U2VydmVycyxcbiAgZW5hYmxlUmF0ZUxpbWl0aW5nLFxuICBnZXRFeHByZXNzLFxuICBnZXRBcHAsXG4gIHVzZSxcbiAgZ2V0LFxuICBwb3N0XG59O1xuIiwiLyoqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKipcblxuSGlnaGNoYXJ0cyBFeHBvcnQgU2VydmVyXG5cbkNvcHlyaWdodCAoYykgMjAxNi0yMDI0LCBIaWdoc29mdFxuXG5MaWNlbmNlZCB1bmRlciB0aGUgTUlUIGxpY2VuY2UuXG5cbkFkZGl0aW9uYWxseSBhIHZhbGlkIEhpZ2hjaGFydHMgbGljZW5zZSBpcyByZXF1aXJlZCBmb3IgdXNlLlxuXG5TZWUgTElDRU5TRSBmaWxlIGluIHJvb3QgZm9yIGRldGFpbHMuXG5cbioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKiovXG5cbmltcG9ydCB7IGpvaW4gfSBmcm9tICdwYXRoJztcblxuaW1wb3J0IHsgX19kaXJuYW1lIH0gZnJvbSAnLi4vLi4vdXRpbHMuanMnO1xuXG4vKipcbiAqIEFkZHMgdGhlIEdFVCAvIHJvdXRlIGZvciBhIFVJIHdoZW4gZW5hYmxlZCBvbiB0aGUgZXhwb3J0IHNlcnZlci5cbiAqL1xuZXhwb3J0IGRlZmF1bHQgKGFwcCkgPT5cbiAgIWFwcFxuICAgID8gZmFsc2VcbiAgICA6IGFwcC5nZXQoJy8nLCAoX3JlcXVlc3QsIHJlc3BvbnNlKSA9PiB7XG4gICAgICAgIHJlc3BvbnNlLnNlbmRGaWxlKGpvaW4oX19kaXJuYW1lLCAncHVibGljJywgJ2luZGV4Lmh0bWwnKSwge1xuICAgICAgICAgIGFjY2VwdFJhbmdlczogZmFsc2VcbiAgICAgICAgfSk7XG4gICAgICB9KTtcbiIsIi8qKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqXG5cbkhpZ2hjaGFydHMgRXhwb3J0IFNlcnZlclxuXG5Db3B5cmlnaHQgKGMpIDIwMTYtMjAyNCwgSGlnaHNvZnRcblxuTGljZW5jZWQgdW5kZXIgdGhlIE1JVCBsaWNlbmNlLlxuXG5BZGRpdGlvbmFsbHkgYSB2YWxpZCBIaWdoY2hhcnRzIGxpY2Vuc2UgaXMgcmVxdWlyZWQgZm9yIHVzZS5cblxuU2VlIExJQ0VOU0UgZmlsZSBpbiByb290IGZvciBkZXRhaWxzLlxuXG4qKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqL1xuXG5pbXBvcnQgeyBnZXRPcHRpb25zIH0gZnJvbSAnLi9jb25maWcuanMnO1xuaW1wb3J0IHsgY2xlYXJBbGxJbnRlcnZhbHMgfSBmcm9tICcuL2ludGVydmFscy5qcyc7XG5pbXBvcnQgeyBraWxsUG9vbCB9IGZyb20gJy4vcG9vbC5qcyc7XG5pbXBvcnQgeyBjbG9zZVNlcnZlcnMgfSBmcm9tICcuL3NlcnZlci9zZXJ2ZXIuanMnO1xuXG4vKipcbiAqIFJldHVybnMgaG93IGxvbmcgdG8gbGV0IGluLWZsaWdodCByZXF1ZXN0cyBmaW5pc2ggZHVyaW5nIGEgc2h1dGRvd24uXG4gKlxuICogQHJldHVybnMge251bWJlcn0gVGhlIGRyYWluIHRpbWVvdXQgaW4gbWlsbGlzZWNvbmRzLlxuICovXG5jb25zdCBnZXREcmFpblRpbWVvdXQgPSAoKSA9PiB7XG4gIGNvbnN0IGNvbmZpZ3VyZWQgPSBwYXJzZUludChnZXRPcHRpb25zKCk/Lm90aGVyPy5zaHV0ZG93bkRyYWluVGltZW91dCk7XG4gIHJldHVybiBpc05hTihjb25maWd1cmVkKSB8fCBjb25maWd1cmVkIDwgMCA/IDMwMDAwIDogY29uZmlndXJlZDtcbn07XG5cbi8qKlxuICogQ2xlYW4gdXAgZnVuY3Rpb24gdG8gdHJpZ2dlciBiZWZvcmUgZW5kaW5nIHByb2Nlc3MgZm9yIHRoZSBncmFjZWZ1bCBzaHV0ZG93bi5cbiAqXG4gKiBAcGFyYW0ge251bWJlcn0gZXhpdENvZGUgLSBBbiBleGl0IGNvZGUgZm9yIHRoZSBwcm9jZXNzLmV4aXQoKSBmdW5jdGlvbi5cbiAqL1xuZXhwb3J0IGNvbnN0IHNodXRkb3duQ2xlYW5VcCA9IGFzeW5jIChleGl0Q29kZSkgPT4ge1xuICAvLyBTdG9wIHRoZSBiYWNrZ3JvdW5kIGludGVydmFsczsgbm90aGluZyBkZXBlbmRzIG9uIHRoZWlyIHRpbWluZyBmcm9tIGhlcmVcbiAgY2xlYXJBbGxJbnRlcnZhbHMoKTtcblxuICAvLyBOT1RFOiBUaGVzZSBoYXZlIHRvIGhhcHBlbiBpbiBvcmRlciwgbm90IHRvZ2V0aGVyLiBQcmV2aW91c2x5IGFsbCB0aHJlZSB3ZXJlXG4gIC8vICAgICAgIHN0YXJ0ZWQgYXQgb25jZSBhbmQgY2xvc2VTZXJ2ZXJzKCkgd2FzIG5vdCBldmVuIGF3YWl0YWJsZSAtIGl0IGlzIG5vdFxuICAvLyAgICAgICBhc3luYyBhbmQgcmV0dXJuZWQgdW5kZWZpbmVkLCBzbyBQcm9taXNlLmFsbFNldHRsZWQgdHJlYXRlZCBpdCBhc1xuICAvLyAgICAgICBhbHJlYWR5IGRvbmUuIFRoZSBwcm9jZXNzIHRoZXJlZm9yZSBleGl0ZWQgYXMgc29vbiBhcyB0aGUgcG9vbCBoYWQgYmVlblxuICAvLyAgICAgICBkZXN0cm95ZWQsIGN1dHRpbmcgb2ZmIGV2ZXJ5IGV4cG9ydCBzdGlsbCBiZWluZyBzZXJ2ZWQuIFRoYXQgaGFwcGVucyBvblxuICAvLyAgICAgICBlYWNoIHJlc3RhcnQsIGRlcGxveSBhbmQgc2NhbGUtaW4sIHNvIHRoZSBkcm9wcGVkIHJlcXVlc3RzIHdlcmUgbm90IHJhcmUuXG4gIC8vXG4gIC8vICAgICAgIENsb3NpbmcgdGhlIHNlcnZlcnMgZmlyc3Qgc3RvcHMgbmV3IHdvcmsgYXJyaXZpbmcgYW5kIHJlc29sdmVzIG9uY2UgdGhlXG4gIC8vICAgICAgIHJlcXVlc3RzIGluIGZsaWdodCBoYXZlIGJlZW4gYW5zd2VyZWQsIHdoaWNoIGlzIHRoZSBkcmFpbi4gSXQgaXMgcmFjZWRcbiAgLy8gICAgICAgYWdhaW5zdCBhIHRpbWVvdXQgc28gdGhhdCBvbmUgcmVxdWVzdCB3aGljaCBuZXZlciBjb21wbGV0ZXMgY2Fubm90IGhvbGRcbiAgLy8gICAgICAgdGhlIHNodXRkb3duIG9wZW4gaW5kZWZpbml0ZWx5LlxuICBjb25zdCBkcmFpblRpbWVvdXQgPSBnZXREcmFpblRpbWVvdXQoKTtcblxuICBhd2FpdCBQcm9taXNlLnJhY2UoW1xuICAgIGNsb3NlU2VydmVycygpLFxuICAgIG5ldyBQcm9taXNlKChyZXNvbHZlKSA9PiBzZXRUaW1lb3V0KHJlc29sdmUsIGRyYWluVGltZW91dCkpXG4gIF0pO1xuXG4gIC8vIE9ubHkgb25jZSBub3RoaW5nIGlzIGJlaW5nIHNlcnZlZCBpcyBpdCBzYWZlIHRvIHRha2UgdGhlIHdvcmtlcnMgYXdheVxuICBhd2FpdCBraWxsUG9vbCgpO1xuXG4gIC8vIEV4aXQgcHJvY2VzcyB3aXRoIGEgY29ycmVjdCBjb2RlXG4gIHByb2Nlc3MuZXhpdChleGl0Q29kZSk7XG59O1xuXG5leHBvcnQgZGVmYXVsdCB7XG4gIHNodXRkb3duQ2xlYW5VcFxufTtcbiIsIi8qKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqXG5cbkhpZ2hjaGFydHMgRXhwb3J0IFNlcnZlclxuXG5Db3B5cmlnaHQgKGMpIDIwMTYtMjAyNCwgSGlnaHNvZnRcblxuTGljZW5jZWQgdW5kZXIgdGhlIE1JVCBsaWNlbmNlLlxuXG5BZGRpdGlvbmFsbHkgYSB2YWxpZCBIaWdoY2hhcnRzIGxpY2Vuc2UgaXMgcmVxdWlyZWQgZm9yIHVzZS5cblxuU2VlIExJQ0VOU0UgZmlsZSBpbiByb290IGZvciBkZXRhaWxzLlxuXG4qKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqL1xuXG5pbXBvcnQgJ2NvbG9ycyc7XG5cbmltcG9ydCB7IGNoZWNrQW5kVXBkYXRlQ2FjaGUgfSBmcm9tICcuL2NhY2hlLmpzJztcbmltcG9ydCB7XG4gIGJhdGNoRXhwb3J0LFxuICBzZXRBbGxvd0NvZGVFeGVjdXRpb24sXG4gIHNpbmdsZUV4cG9ydCxcbiAgc3RhcnRFeHBvcnRcbn0gZnJvbSAnLi9jaGFydC5qcyc7XG5pbXBvcnQgeyBtYXBUb05ld0NvbmZpZywgbWFudWFsQ29uZmlnLCBzZXRPcHRpb25zIH0gZnJvbSAnLi9jb25maWcuanMnO1xuaW1wb3J0IHtcbiAgaW5pdExvZ2dpbmcsXG4gIGxvZyxcbiAgbG9nV2l0aFN0YWNrLFxuICBzZXRMb2dMZXZlbCxcbiAgZW5hYmxlRmlsZUxvZ2dpbmdcbn0gZnJvbSAnLi9sb2dnZXIuanMnO1xuaW1wb3J0IHsgaW5pdFBvb2wsIGtpbGxQb29sIH0gZnJvbSAnLi9wb29sLmpzJztcbmltcG9ydCB7IHNodXRkb3duQ2xlYW5VcCB9IGZyb20gJy4vcmVzb3VyY2VfcmVsZWFzZS5qcyc7XG5pbXBvcnQgc2VydmVyLCB7IHN0YXJ0U2VydmVyIH0gZnJvbSAnLi9zZXJ2ZXIvc2VydmVyLmpzJztcbmltcG9ydCB7IHByaW50TG9nbywgcHJpbnRVc2FnZSB9IGZyb20gJy4vdXRpbHMuanMnO1xuXG4vKipcbiAqIEF0dGFjaGVzIGV4aXQgbGlzdGVuZXJzIHRvIHRoZSBwcm9jZXNzLCBlbnN1cmluZyBwcm9wZXIgY2xlYW51cCBvZiByZXNvdXJjZXNcbiAqIGFuZCB0ZXJtaW5hdGlvbiBvbiBleGl0IHNpZ25hbHMuIEhhbmRsZXMgJ2V4aXQnLCAnU0lHSU5UJywgJ1NJR1RFUk0nLCBhbmRcbiAqICd1bmNhdWdodEV4Y2VwdGlvbicgZXZlbnRzLlxuICovXG5jb25zdCBhdHRhY2hQcm9jZXNzRXhpdExpc3RlbmVycyA9ICgpID0+IHtcbiAgbG9nKDMsICdbcHJvY2Vzc10gQXR0YWNoaW5nIGV4aXQgbGlzdGVuZXJzIHRvIHRoZSBwcm9jZXNzLicpO1xuXG4gIC8vIEhhbmRsZXIgZm9yIHRoZSAnZXhpdCdcbiAgcHJvY2Vzcy5vbignZXhpdCcsIChjb2RlKSA9PiB7XG4gICAgbG9nKDQsIGBQcm9jZXNzIGV4aXRlZCB3aXRoIGNvZGUgJHtjb2RlfS5gKTtcbiAgfSk7XG5cbiAgLy8gSGFuZGxlciBmb3IgdGhlICdTSUdJTlQnXG4gIHByb2Nlc3Mub24oJ1NJR0lOVCcsIGFzeW5jIChuYW1lLCBjb2RlKSA9PiB7XG4gICAgbG9nKDQsIGBUaGUgJHtuYW1lfSBldmVudCB3aXRoIGNvZGU6ICR7Y29kZX0uYCk7XG4gICAgYXdhaXQgc2h1dGRvd25DbGVhblVwKDApO1xuICB9KTtcblxuICAvLyBIYW5kbGVyIGZvciB0aGUgJ1NJR1RFUk0nXG4gIHByb2Nlc3Mub24oJ1NJR1RFUk0nLCBhc3luYyAobmFtZSwgY29kZSkgPT4ge1xuICAgIGxvZyg0LCBgVGhlICR7bmFtZX0gZXZlbnQgd2l0aCBjb2RlOiAke2NvZGV9LmApO1xuICAgIGF3YWl0IHNodXRkb3duQ2xlYW5VcCgwKTtcbiAgfSk7XG5cbiAgLy8gSGFuZGxlciBmb3IgdGhlICdTSUdIVVAnXG4gIHByb2Nlc3Mub24oJ1NJR0hVUCcsIGFzeW5jIChuYW1lLCBjb2RlKSA9PiB7XG4gICAgbG9nKDQsIGBUaGUgJHtuYW1lfSBldmVudCB3aXRoIGNvZGU6ICR7Y29kZX0uYCk7XG4gICAgYXdhaXQgc2h1dGRvd25DbGVhblVwKDApO1xuICB9KTtcblxuICAvLyBIYW5kbGVyIGZvciB0aGUgJ3VuY2F1Z2h0RXhjZXB0aW9uJ1xuICBwcm9jZXNzLm9uKCd1bmNhdWdodEV4Y2VwdGlvbicsIGFzeW5jIChlcnJvciwgbmFtZSkgPT4ge1xuICAgIGxvZ1dpdGhTdGFjaygxLCBlcnJvciwgYFRoZSAke25hbWV9IGVycm9yLmApO1xuICAgIGF3YWl0IHNodXRkb3duQ2xlYW5VcCgxKTtcbiAgfSk7XG59O1xuXG4vKipcbiAqIEluaXRpYWxpemVzIHRoZSBleHBvcnQgcHJvY2Vzcy4gVGFza3Mgc3VjaCBhcyBjb25maWd1cmluZyBsb2dnaW5nLCBjaGVja2luZ1xuICogY2FjaGUgYW5kIHNvdXJjZXMsIGFuZCBpbml0aWFsaXppbmcgdGhlIHBvb2wgb2YgcmVzb3VyY2VzIGhhcHBlbiBkdXJpbmdcbiAqIHRoaXMgc3RhZ2UuIEZ1bmN0aW9uIHRoYXQgaXMgcmVxdWlyZWQgdG8gYmUgY2FsbGVkIGJlZm9yZSB0cnlpbmcgdG8gZXhwb3J0IGNoYXJ0cyBvciBzZXR0aW5nIGEgc2VydmVyLiBUaGUgYG9wdGlvbnNgIGlzIGFuIG9iamVjdCB0aGF0IGNvbnRhaW5zIGFsbCBvcHRpb25zLlxuICpcbiAqIEBwYXJhbSB7T2JqZWN0fSBvcHRpb25zIC0gQWxsIGV4cG9ydCBvcHRpb25zLlxuICpcbiAqIEByZXR1cm5zIHtQcm9taXNlPE9iamVjdD59IFByb21pc2UgcmVzb2x2aW5nIHRvIHRoZSB1cGRhdGVkIGV4cG9ydCBvcHRpb25zLlxuICovXG5jb25zdCBpbml0RXhwb3J0ID0gYXN5bmMgKG9wdGlvbnMpID0+IHtcbiAgLy8gU2V0IHRoZSBhbGxvd0NvZGVFeGVjdXRpb24gcGVyIGV4cG9ydCBtb2R1bGUgc2NvcGVcbiAgc2V0QWxsb3dDb2RlRXhlY3V0aW9uKFxuICAgIG9wdGlvbnMuY3VzdG9tTG9naWMgJiYgb3B0aW9ucy5jdXN0b21Mb2dpYy5hbGxvd0NvZGVFeGVjdXRpb25cbiAgKTtcblxuICAvLyBJbml0IHRoZSBsb2dnaW5nXG4gIGluaXRMb2dnaW5nKG9wdGlvbnMubG9nZ2luZyk7XG5cbiAgLy8gQXR0YWNoIHByb2Nlc3MnIGV4aXQgbGlzdGVuZXJzXG4gIGlmIChvcHRpb25zLm90aGVyLmxpc3RlblRvUHJvY2Vzc0V4aXRzKSB7XG4gICAgYXR0YWNoUHJvY2Vzc0V4aXRMaXN0ZW5lcnMoKTtcbiAgfVxuXG4gIC8vIENoZWNrIGlmIGNhY2hlIG5lZWRzIHRvIGJlIHVwZGF0ZWRcbiAgYXdhaXQgY2hlY2tBbmRVcGRhdGVDYWNoZShvcHRpb25zKTtcblxuICAvLyBJbml0IHRoZSBwb29sXG4gIGF3YWl0IGluaXRQb29sKHtcbiAgICBwb29sOiBvcHRpb25zLnBvb2wgfHwge1xuICAgICAgbWluV29ya2VyczogMSxcbiAgICAgIG1heFdvcmtlcnM6IDFcbiAgICB9LFxuICAgIHB1cHBldGVlckFyZ3M6IG9wdGlvbnMucHVwcGV0ZWVyLmFyZ3MgfHwgW11cbiAgfSk7XG5cbiAgLy8gUmV0dXJuIHVwZGF0ZWQgb3B0aW9uc1xuICByZXR1cm4gb3B0aW9ucztcbn07XG5cbmV4cG9ydCBkZWZhdWx0IHtcbiAgLy8gU2VydmVyXG4gIHNlcnZlcixcbiAgc3RhcnRTZXJ2ZXIsXG5cbiAgLy8gRXhwb3J0aW5nXG4gIGluaXRFeHBvcnQsXG4gIHNpbmdsZUV4cG9ydCxcbiAgYmF0Y2hFeHBvcnQsXG4gIHN0YXJ0RXhwb3J0LFxuXG4gIC8vIFBvb2xcbiAgaW5pdFBvb2wsXG4gIGtpbGxQb29sLFxuXG4gIC8vIE90aGVyXG4gIHNldE9wdGlvbnMsXG4gIHNodXRkb3duQ2xlYW5VcCxcblxuICAvLyBMb2dzXG4gIGxvZyxcbiAgbG9nV2l0aFN0YWNrLFxuICBzZXRMb2dMZXZlbCxcbiAgZW5hYmxlRmlsZUxvZ2dpbmcsXG5cbiAgLy8gVXRpbHNcbiAgbWFwVG9OZXdDb25maWcsXG4gIG1hbnVhbENvbmZpZyxcbiAgcHJpbnRMb2dvLFxuICBwcmludFVzYWdlXG59O1xuIl0sIm5hbWVzIjpbInNjcmlwdHNOYW1lcyIsImNvcmUiLCJtb2R1bGVzIiwiaW5kaWNhdG9ycyIsImN1c3RvbSIsImRlZmF1bHRDb25maWciLCJwdXBwZXRlZXIiLCJhcmdzIiwidmFsdWUiLCJ0eXBlIiwiZGVzY3JpcHRpb24iLCJsYXVuY2hSZXRyeVdpbmRvdyIsImVudkxpbmsiLCJ0ZW1wRGlyIiwiaGlnaGNoYXJ0cyIsInZlcnNpb24iLCJjZG5VUkwiLCJ1c2VOcG0iLCJjb3JlU2NyaXB0cyIsIm1vZHVsZVNjcmlwdHMiLCJpbmRpY2F0b3JTY3JpcHRzIiwiY3VzdG9tU2NyaXB0cyIsImZvcmNlRmV0Y2giLCJjYWNoZVBhdGgiLCJleHBvcnQiLCJpbmZpbGUiLCJpbnN0ciIsIm9wdGlvbnMiLCJvdXRmaWxlIiwiY29uc3RyIiwiZGVmYXVsdEhlaWdodCIsImRlZmF1bHRXaWR0aCIsImRlZmF1bHRTY2FsZSIsImhlaWdodCIsIndpZHRoIiwic2NhbGUiLCJnbG9iYWxPcHRpb25zIiwidGhlbWVPcHRpb25zIiwiYmF0Y2giLCJyYXN0ZXJpemF0aW9uVGltZW91dCIsImN1c3RvbUxvZ2ljIiwiYWxsb3dDb2RlRXhlY3V0aW9uIiwiYWxsb3dGaWxlUmVzb3VyY2VzIiwiY3VzdG9tQ29kZSIsImNhbGxiYWNrIiwicmVzb3VyY2VzIiwibG9hZENvbmZpZyIsImxlZ2FjeU5hbWUiLCJjcmVhdGVDb25maWciLCJzZXJ2ZXIiLCJtYXhVcGxvYWRTaXplIiwiZW5hYmxlIiwiY2xpTmFtZSIsImhvc3QiLCJwb3J0Iiwia2VlcEFsaXZlVGltZW91dCIsImJlbmNobWFya2luZyIsInByb3h5IiwidXNlcm5hbWUiLCJwYXNzd29yZCIsInRpbWVvdXQiLCJyYXRlTGltaXRpbmciLCJtYXhSZXF1ZXN0cyIsIndpbmRvdyIsInRydXN0UHJveHkiLCJza2lwS2V5Iiwic2tpcFRva2VuIiwic3NsIiwiZm9yY2UiLCJjZXJ0UGF0aCIsInBvb2wiLCJtaW5Xb3JrZXJzIiwibWF4V29ya2VycyIsIndvcmtMaW1pdCIsInF1ZXVlTGltaXQiLCJxdWV1ZVJlamVjdERlbGF5IiwiYWNxdWlyZVRpbWVvdXQiLCJjcmVhdGVUaW1lb3V0IiwiZGVzdHJveVRpbWVvdXQiLCJpZGxlVGltZW91dCIsImNyZWF0ZVJldHJ5SW50ZXJ2YWwiLCJyZWFwZXJJbnRlcnZhbCIsImxvZ2dpbmciLCJsZXZlbCIsImZpbGUiLCJkZXN0IiwidG9Db25zb2xlIiwidG9GaWxlIiwidWkiLCJyb3V0ZSIsIm90aGVyIiwibm9kZUVudiIsImxpc3RlblRvUHJvY2Vzc0V4aXRzIiwibm9Mb2dvIiwiaGFyZFJlc2V0UGFnZSIsInNodXRkb3duRHJhaW5UaW1lb3V0IiwiYnJvd3NlclNoZWxsTW9kZSIsImRlYnVnIiwiaGVhZGxlc3MiLCJkZXZ0b29scyIsImxpc3RlblRvQ29uc29sZSIsImR1bXBpbyIsInNsb3dNbyIsImRlYnVnZ2luZ1BvcnQiLCJwcm9tcHRzQ29uZmlnIiwibmFtZSIsIm1lc3NhZ2UiLCJpbml0aWFsIiwiam9pbiIsInNlcGFyYXRvciIsImluc3RydWN0aW9ucyIsImNob2ljZXMiLCJoaW50IiwibWluIiwibWF4Iiwicm91bmQiLCJhYnNvbHV0ZVByb3BzIiwibmVzdGVkQXJncyIsImNyZWF0ZU5lc3RlZEFyZ3MiLCJvYmoiLCJwcm9wQ2hhaW4iLCJPYmplY3QiLCJrZXlzIiwiZm9yRWFjaCIsImsiLCJpbmNsdWRlcyIsImVudHJ5Iiwic3Vic3RyaW5nIiwidW5kZWZpbmVkIiwiZG90ZW52IiwiY29uZmlnIiwicXVpZXQiLCJ2IiwiZmlsdGVyQXJyYXkiLCJ6Iiwic3RyaW5nIiwidHJhbnNmb3JtIiwic3BsaXQiLCJtYXAiLCJ0cmltIiwiZmlsdGVyIiwibGVuZ3RoIiwiZW51bSIsInZhbHVlcyIsInJlZmluZSIsImVycm9yIiwiaXNzdWUiLCJpbnB1dCIsInRlc3QiLCJpc05hTiIsInBhcnNlRmxvYXQiLCJlbnZzIiwib2JqZWN0IiwiUFVQUEVURUVSX1RFTVBfRElSIiwiUFVQUEVURUVSX0xBVU5DSF9SRVRSWV9XSU5ET1ciLCJISUdIQ0hBUlRTX1ZFUlNJT04iLCJISUdIQ0hBUlRTX0NETl9VUkwiLCJzdGFydHNXaXRoIiwiSElHSENIQVJUU19VU0VfTlBNIiwiSElHSENIQVJUU19DT1JFX1NDUklQVFMiLCJISUdIQ0hBUlRTX01PRFVMRV9TQ1JJUFRTIiwiSElHSENIQVJUU19JTkRJQ0FUT1JfU0NSSVBUUyIsIkhJR0hDSEFSVFNfRk9SQ0VfRkVUQ0giLCJISUdIQ0hBUlRTX0NBQ0hFX1BBVEgiLCJISUdIQ0hBUlRTX0FETUlOX1RPS0VOIiwiRVhQT1JUX1RZUEUiLCJFWFBPUlRfQ09OU1RSIiwiRVhQT1JUX0RFRkFVTFRfSEVJR0hUIiwiRVhQT1JUX0RFRkFVTFRfV0lEVEgiLCJFWFBPUlRfREVGQVVMVF9TQ0FMRSIsIkVYUE9SVF9SQVNURVJJWkFUSU9OX1RJTUVPVVQiLCJDVVNUT01fTE9HSUNfQUxMT1dfQ09ERV9FWEVDVVRJT04iLCJDVVNUT01fTE9HSUNfQUxMT1dfRklMRV9SRVNPVVJDRVMiLCJTRVJWRVJfRU5BQkxFIiwiU0VSVkVSX0hPU1QiLCJTRVJWRVJfUE9SVCIsIlNFUlZFUl9NQVhfVVBMT0FEX1NJWkUiLCJTRVJWRVJfS0VFUF9BTElWRV9USU1FT1VUIiwiU0VSVkVSX0JFTkNITUFSS0lORyIsIlNFUlZFUl9QUk9YWV9IT1NUIiwiU0VSVkVSX1BST1hZX1BPUlQiLCJTRVJWRVJfUFJPWFlfVVNFUk5BTUUiLCJTRVJWRVJfUFJPWFlfUEFTU1dPUkQiLCJTRVJWRVJfUFJPWFlfVElNRU9VVCIsIlNFUlZFUl9SQVRFX0xJTUlUSU5HX0VOQUJMRSIsIlNFUlZFUl9SQVRFX0xJTUlUSU5HX01BWF9SRVFVRVNUUyIsIlNFUlZFUl9SQVRFX0xJTUlUSU5HX1dJTkRPVyIsIlNFUlZFUl9SQVRFX0xJTUlUSU5HX1RSVVNUX1BST1hZIiwiU0VSVkVSX1JBVEVfTElNSVRJTkdfU0tJUF9LRVkiLCJTRVJWRVJfUkFURV9MSU1JVElOR19TS0lQX1RPS0VOIiwiU0VSVkVSX1NTTF9FTkFCTEUiLCJTRVJWRVJfU1NMX0ZPUkNFIiwiU0VSVkVSX1NTTF9QT1JUIiwiU0VSVkVSX1NTTF9DRVJUX1BBVEgiLCJQT09MX01JTl9XT1JLRVJTIiwiUE9PTF9NQVhfV09SS0VSUyIsIlBPT0xfV09SS19MSU1JVCIsIlBPT0xfUVVFVUVfTElNSVQiLCJQT09MX1FVRVVFX1JFSkVDVF9ERUxBWSIsIlBPT0xfQUNRVUlSRV9USU1FT1VUIiwiUE9PTF9DUkVBVEVfVElNRU9VVCIsIlBPT0xfREVTVFJPWV9USU1FT1VUIiwiUE9PTF9JRExFX1RJTUVPVVQiLCJQT09MX0NSRUFURV9SRVRSWV9JTlRFUlZBTCIsIlBPT0xfUkVBUEVSX0lOVEVSVkFMIiwiUE9PTF9CRU5DSE1BUktJTkciLCJMT0dHSU5HX0xFVkVMIiwiTE9HR0lOR19GSUxFIiwiTE9HR0lOR19ERVNUIiwiTE9HR0lOR19UT19DT05TT0xFIiwiTE9HR0lOR19UT19GSUxFIiwiVUlfRU5BQkxFIiwiVUlfUk9VVEUiLCJPVEhFUl9TSFVURE9XTl9EUkFJTl9USU1FT1VUIiwiT1RIRVJfTk9ERV9FTlYiLCJPVEhFUl9MSVNURU5fVE9fUFJPQ0VTU19FWElUUyIsIk9USEVSX05PX0xPR08iLCJPVEhFUl9IQVJEX1JFU0VUX1BBR0UiLCJPVEhFUl9CUk9XU0VSX1NIRUxMX01PREUiLCJPVEhFUl9BTExPV19YTElOSyIsIkRFQlVHX0VOQUJMRSIsIkRFQlVHX0hFQURMRVNTIiwiREVCVUdfREVWVE9PTFMiLCJERUJVR19MSVNURU5fVE9fQ09OU09MRSIsIkRFQlVHX0RVTVBJTyIsIkRFQlVHX1NMT1dfTU8iLCJERUJVR19ERUJVR0dJTkdfUE9SVCIsInBhcnRpYWwiLCJwYXJzZSIsInByb2Nlc3MiLCJlbnYiLCJjb2xvcnMiLCJwYXRoQ3JlYXRlZCIsImxldmVsc0Rlc2MiLCJ0aXRsZSIsImNvbG9yIiwibGlzdGVuZXJzIiwibG9nVG9GaWxlIiwidGV4dHMiLCJwcmVmaXgiLCJleGlzdHNTeW5jIiwibWtkaXJTeW5jIiwiYXBwZW5kRmlsZSIsImNvbmNhdCIsImNvbnNvbGUiLCJsb2ciLCJuZXdMZXZlbCIsIkRhdGUiLCJ0b1N0cmluZyIsImZuIiwiYXBwbHkiLCJsb2dXaXRoU3RhY2siLCJjdXN0b21NZXNzYWdlIiwibWFpbk1lc3NhZ2UiLCJzdGFja01lc3NhZ2UiLCJzdGFjayIsInNsaWNlIiwic2V0TG9nTGV2ZWwiLCJlbmFibGVGaWxlTG9nZ2luZyIsImxvZ0Rlc3QiLCJsb2dGaWxlIiwiZW5kc1dpdGgiLCJfX2hpZ2hjaGFydHNEaXIiLCJkaXJuYW1lIiwiY3JlYXRlUmVxdWlyZSIsImRvY3VtZW50IiwicmVxdWlyZSIsInBhdGhUb0ZpbGVVUkwiLCJfX2ZpbGVuYW1lIiwiaHJlZiIsIl9kb2N1bWVudEN1cnJlbnRTY3JpcHQiLCJ0YWdOYW1lIiwidG9VcHBlckNhc2UiLCJzcmMiLCJVUkwiLCJiYXNlVVJJIiwicmVzb2x2ZSIsIl9fZGlybmFtZSIsImZpbGVVUkxUb1BhdGgiLCJmaXhUeXBlIiwiZm9ybWF0cyIsIm91dFR5cGUiLCJwb3AiLCJmaW5kIiwidCIsImhhbmRsZVJlc291cmNlcyIsImFsbG93ZWRQcm9wcyIsImhhbmRsZWRSZXNvdXJjZXMiLCJjb3JyZWN0UmVzb3VyY2VzIiwiaXNDb3JyZWN0SlNPTiIsInJlYWRGaWxlU3luYyIsImZpbGVzIiwicHJvcE5hbWUiLCJpdGVtIiwiZGF0YSIsInBhcnNlZERhdGEiLCJKU09OIiwic3RyaW5naWZ5IiwiZGVlcENvcHkiLCJjb3B5IiwiQXJyYXkiLCJpc0FycmF5Iiwia2V5IiwicHJvdG90eXBlIiwiaGFzT3duUHJvcGVydHkiLCJjYWxsIiwib3B0aW9uc1N0cmluZ2lmeSIsImFsbG93RnVuY3Rpb25zIiwicmVwbGFjZUFsbCIsInByaW50VXNhZ2UiLCJib2xkIiwieWVsbG93IiwiY3ljbGVDYXRlZ29yaWVzIiwib3B0aW9uIiwiZW50cmllcyIsImRlc2NOYW1lIiwiZ3JlZW4iLCJpIiwiYmx1ZSIsImNhdGVnb3J5IiwicmVkIiwidG9Cb29sZWFuIiwid3JhcEFyb3VuZCIsInJlcGxhY2UiLCJtZWFzdXJlVGltZSIsInN0YXJ0IiwiaHJ0aW1lIiwiYmlnaW50IiwiTnVtYmVyIiwiZ2VuZXJhbE9wdGlvbnMiLCJnZXRPcHRpb25zIiwibWVyZ2VDb25maWdPcHRpb25zIiwibmV3T3B0aW9ucyIsIm1lcmdlZE9wdGlvbnMiLCJ1cGRhdGVEZWZhdWx0Q29uZmlnIiwiY29uZmlnT2JqIiwiY3VzdG9tT2JqIiwiY3VzdG9tVmFsdWUiLCJpbml0T3B0aW9ucyIsIml0ZW1zIiwicmVjdXJzaXZlUHJvcHMiLCJvYmplY3RUb1VwZGF0ZSIsIm5lc3RlZE5hbWVzIiwic2hpZnQiLCJhc3NpZ24iLCJhc3luYyIsImZldGNoIiwidXJsIiwicmVxdWVzdE9wdGlvbnMiLCJQcm9taXNlIiwicmVqZWN0IiwicHJvdG9jb2wiLCJodHRwcyIsImh0dHAiLCJnZXRQcm90b2NvbCIsImdldCIsImhlYWRlcnMiLCJSZWZlcmVyIiwicmVzIiwib24iLCJjaHVuayIsInRleHQiLCJFeHBvcnRFcnJvciIsIkVycm9yIiwiY29uc3RydWN0b3IiLCJzdXBlciIsInRoaXMiLCJzZXRFcnJvciIsInN0YXR1c0NvZGUiLCJlcnJvckNvZGUiLCJzZXRDb2RlIiwiY2FjaGUiLCJhY3RpdmVNYW5pZmVzdCIsInNvdXJjZXMiLCJoY1ZlcnNpb24iLCJleHRyYWN0VmVyc2lvbiIsImluZGV4T2YiLCJleHRyYWN0TW9kdWxlTmFtZSIsInNjcmlwdFBhdGgiLCJmZXRjaEFuZFByb2Nlc3NTY3JpcHQiLCJzY3JpcHQiLCJmZXRjaGVkTW9kdWxlcyIsInNob3VsZFRocm93RXJyb3IiLCJyZXNwb25zZSIsInJlc29sdmVkU2NyaXB0UGF0aCIsInNlcCIsInVwZGF0ZUNhY2hlIiwiaGlnaGNoYXJ0c09wdGlvbnMiLCJwcm94eU9wdGlvbnMiLCJzb3VyY2VQYXRoIiwicHJveHlBZ2VudCIsIkh0dHBzUHJveHlBZ2VudCIsImFnZW50IiwiYWxsIiwiYyIsIm0iLCJmZXRjaFNjcmlwdHMiLCJ3cml0ZUZpbGVTeW5jIiwiY2hlY2tBbmRVcGRhdGVDYWNoZSIsIm1hbmlmZXN0UGF0aCIsInJlcXVlc3RVcGRhdGUiLCJtYW5pZmVzdCIsIm1vZHVsZU1hcCIsIm51bWJlck9mTW9kdWxlcyIsInNvbWUiLCJtb2R1bGVOYW1lIiwibmV3TWFuaWZlc3QiLCJzYXZlQ29uZmlnVG9NYW5pZmVzdCIsImdldENhY2hlUGF0aCIsInNldHVwSGlnaGNoYXJ0cyIsIkhpZ2hjaGFydHMiLCJhbmltT2JqZWN0IiwiZHVyYXRpb24iLCJ0cmlnZ2VyRXhwb3J0IiwiY2hhcnRPcHRpb25zIiwiZGlzcGxheUVycm9ycyIsIl9kaXNwbGF5RXJyb3JzIiwibWVyZ2UiLCJzZXRPcHRpb25zIiwid3JhcCIsInNldE9wdGlvbnNPYmoiLCJjaGFydCIsImFuaW1hdGlvbiIsInN0ckluaiIsImlzUmVuZGVyQ29tcGxldGUiLCJDaGFydCIsInByb2NlZWQiLCJ1c2VyT3B0aW9ucyIsImNiIiwiZXhwb3J0aW5nIiwiZW5hYmxlZCIsInBsb3RPcHRpb25zIiwic2VyaWVzIiwibGFiZWwiLCJ0b29sdGlwIiwib25IaWdoY2hhcnRzUmVuZGVyIiwiYWRkRXZlbnQiLCJTZXJpZXMiLCJGdW5jdGlvbiIsImZpbmFsT3B0aW9ucyIsImZpbmFsQ2FsbGJhY2siLCJkZWZhdWx0T3B0aW9ucyIsInByb3AiLCJ0ZW1wbGF0ZSIsImJyb3dzZXIiLCJicm93c2VyR2VuZXJhdGlvbiIsImxhc3RQdXBwZXRlZXJBcmdzIiwiY2xvc2luZ09uUHVycG9zZSIsImxhdW5jaFByb21pc2UiLCJnZXRHZW5lcmF0aW9uIiwiaGFuZGxlRGlzY29ubmVjdCIsImlzQWxpdmUiLCJwcm9jIiwicGlkIiwia2lsbCIsImNvZGUiLCJ0ZXJtaW5hdGUiLCJpbnN0YW5jZSIsImNsb3NlIiwib25FeGl0IiwiY2xlYXJUaW1lb3V0IiwidGltZXIiLCJzZXRUaW1lb3V0IiwicmVtb3ZlTGlzdGVuZXIiLCJvbmNlIiwid2FpdEZvckV4aXQiLCJjcmVhdGUiLCJwdXBwZXRlZXJBcmdzIiwiY29ubmVjdGVkIiwicHVwcGV0ZWVyT3B0aW9ucyIsImVuYWJsZWREZWJ1ZyIsImRlYnVnT3B0aW9ucyIsImxhdW5jaE9wdGlvbnMiLCJ1c2VyRGF0YURpciIsImhhbmRsZVNJR0lOVCIsImhhbmRsZVNJR1RFUk0iLCJoYW5kbGVTSUdIVVAiLCJ3YWl0Rm9ySW5pdGlhbFBhZ2UiLCJkZWZhdWx0Vmlld3BvcnQiLCJyZXRyeVdpbmRvdyIsImNvbmZpZ3VyZWQiLCJwYXJzZUludCIsImdldExhdW5jaFJldHJ5V2luZG93IiwiZGVhZGxpbmUiLCJub3ciLCJhdHRlbXB0IiwiZGVsYXkiLCJsYXVuY2giLCJyZW1haW5pbmciLCJNYXRoIiwiY2VpbCIsInJhbmRvbSIsImxhdW5jaEJyb3dzZXIiLCJmaW5hbGx5IiwibmV3UGFnZSIsInBhZ2UiLCJzZXRDYWNoZUVuYWJsZWQiLCJzZXRQYWdlQ29udGVudCIsImlzQ2xvc2VkIiwiJGV2YWwiLCJlbGVtZW50IiwiZXJyb3JNZXNzYWdlIiwiaW5uZXJIVE1MIiwic2V0UGFnZUV2ZW50cyIsImNsb3NlRXJyb3IiLCJjbGVhclBhZ2VSZXNvdXJjZXMiLCJpbmplY3RlZFJlc291cmNlcyIsInJlc291cmNlIiwiZGlzcG9zZSIsImV2YWx1YXRlIiwib2xkQ2hhcnRzIiwiY2hhcnRzIiwib2xkQ2hhcnQiLCJkZXN0cm95Iiwic2NyaXB0c1RvUmVtb3ZlIiwiZ2V0RWxlbWVudHNCeVRhZ05hbWUiLCJzdHlsZXNUb1JlbW92ZSIsImxpbmtzVG9SZW1vdmUiLCJyZW1vdmUiLCJzZXRDb250ZW50Iiwid2FpdFVudGlsIiwiYWRkU2NyaXB0VGFnIiwicGF0aCIsInNldEFzQ29uZmlnIiwidG90YWxTaXplIiwiQnVmZmVyIiwiYnl0ZUxlbmd0aCIsInRvRml4ZWQiLCJwdXBwZXRlZXJFeHBvcnQiLCJleHBvcnRPcHRpb25zIiwiZGVidWdnZXIiLCJpc1NWRyIsInN2Z1RlbXBsYXRlIiwiaW5qZWN0ZWRKcyIsImpzIiwicHVzaCIsImNvbnRlbnQiLCJpc0xvY2FsIiwianNSZXNvdXJjZSIsImluamVjdGVkQ3NzIiwiY3NzIiwiY3NzSW1wb3J0cyIsIm1hdGNoIiwiY3NzSW1wb3J0UGF0aCIsImNzc1Jlc291cmNlIiwiYWRkU3R5bGVUYWciLCJhZGRQYWdlUmVzb3VyY2VzIiwic2l6ZSIsInN2Z0VsZW1lbnQiLCJxdWVyeVNlbGVjdG9yIiwiY2hhcnRIZWlnaHQiLCJiYXNlVmFsIiwiY2hhcnRXaWR0aCIsImJvZHkiLCJzdHlsZSIsInpvb20iLCJtYXJnaW4iLCJ2aWV3cG9ydEhlaWdodCIsImFicyIsInZpZXdwb3J0V2lkdGgiLCJ4IiwieSIsImdldEJvdW5kaW5nQ2xpZW50UmVjdCIsInRydW5jIiwiZ2V0Q2xpcFJlZ2lvbiIsInNldFZpZXdwb3J0IiwiZGV2aWNlU2NhbGVGYWN0b3IiLCJvdXRlckhUTUwiLCJjcmVhdGVTVkciLCJlbmNvZGluZyIsImNsaXAiLCJyYWNlIiwic2NyZWVuc2hvdCIsImNhcHR1cmVCZXlvbmRWaWV3cG9ydCIsImZ1bGxQYWdlIiwib3B0aW1pemVGb3JTcGVlZCIsInF1YWxpdHkiLCJvbWl0QmFja2dyb3VuZCIsIl9yZXNvbHZlIiwiY3JlYXRlSW1hZ2UiLCJlbXVsYXRlTWVkaWFUeXBlIiwicGRmIiwiY3JlYXRlUERGIiwiZXJyb3JDb2RlcyIsInN0YXRzIiwicGVyZm9ybWVkRXhwb3J0cyIsImV4cG9ydEF0dGVtcHRzIiwiZXhwb3J0RnJvbVN2Z0F0dGVtcHRzIiwidGltZVNwZW50IiwiZHJvcHBlZEV4cG9ydHMiLCJzcGVudEF2ZXJhZ2UiLCJyZWplY3RlZEZvckNhcGFjaXR5IiwiYWJhbmRvbmVkRXhwb3J0cyIsInBvb2xDb25maWciLCJjb25zZWN1dGl2ZUNyZWF0ZUZhaWx1cmVzIiwiZ2V0Q29uc2VjdXRpdmVDcmVhdGVGYWlsdXJlcyIsImdldFF1ZXVlTGltaXQiLCJmYWN0b3J5IiwiaWQiLCJ1dWlkIiwic3RhcnREYXRlIiwiZ2V0VGltZSIsImdlbmVyYXRpb24iLCJnZXRCcm93c2VyR2VuZXJhdGlvbiIsIndvcmtDb3VudCIsInZhbGlkYXRlIiwid29ya2VySGFuZGxlIiwibXVzdFJlY3ljbGUiLCJjbGVhblByb21pc2UiLCJjbGVhcmVkIiwiaW5pdFBvb2wiLCJyZXNvbHZlUXVldWVMaW1pdCIsImNyZWF0ZUJyb3dzZXIiLCJQb29sIiwiYWNxdWlyZVRpbWVvdXRNaWxsaXMiLCJjcmVhdGVUaW1lb3V0TWlsbGlzIiwiZGVzdHJveVRpbWVvdXRNaWxsaXMiLCJpZGxlVGltZW91dE1pbGxpcyIsImNyZWF0ZVJldHJ5SW50ZXJ2YWxNaWxsaXMiLCJyZWFwSW50ZXJ2YWxNaWxsaXMiLCJwcm9wYWdhdGVDcmVhdGVFcnJvciIsImhhcmRSZXNldCIsImdvdG8iLCJjbGVhclBhZ2UiLCJjYXRjaCIsImV2ZW50SWQiLCJpbml0aWFsUmVzb3VyY2VzIiwiYWNxdWlyZSIsInByb21pc2UiLCJyZWxlYXNlIiwia2lsbFBvb2wiLCJ3b3JrZXIiLCJ1c2VkIiwiZGVzdHJveWVkIiwiY2xvc2VCcm93c2VyIiwicG9zdFdvcmsiLCJnZXRQb29sSW5mbyIsIm51bVBlbmRpbmdBY3F1aXJlcyIsInBheWxvYWQiLCJyZXF1ZXN0SWQiLCJhYm9ydFNpZ25hbCIsImFib3J0ZWQiLCJhY3F1aXJlQ291bnRlciIsIndvcmtTdGFydCIsImV4cG9ydENvdW50ZXIiLCJyZXN1bHQiLCJleHBvcnRUaW1lIiwiZ2V0UG9vbCIsImdldFBvb2xJbmZvSlNPTiIsIm51bUZyZWUiLCJudW1Vc2VkIiwiYXZhaWxhYmxlIiwicGVuZGluZyIsInB1cmlmaWVyIiwic2FuaXRpemUiLCJmb3JiaWRkZW4iLCJET01QdXJpZnkiLCJKU0RPTSIsIkFERF9UQUdTIiwiRk9SQklEX0FUVFIiLCJIVE1MX0lOVEVHUkFUSU9OX1BPSU5UUyIsImZvcmVpZ25vYmplY3QiLCJzdGFydEV4cG9ydCIsInNldHRpbmdzIiwiZW5kQ2FsbGJhY2siLCJzdmciLCJpbml0RXhwb3J0U2V0dGluZ3MiLCJleHBvcnRBc1N0cmluZyIsImRvU3RyYWlnaHRJbmplY3QiLCJkb0V4cG9ydCIsImZpbmRDaGFydFNpemUiLCJwcmVjaXNpb24iLCJtdWx0aXBsaWVyIiwicG93Iiwicm91bmROdW1iZXIiLCJzb3VyY2VIZWlnaHQiLCJzb3VyY2VXaWR0aCIsInBhcmFtIiwiY2hhcnRKc29uIiwiY3VzdG9tTG9naWNPcHRpb25zIiwiYWxsb3dDb2RlRXhlY3V0aW9uU2NvcGVkIiwib3B0aW9uc05hbWUiLCJzdHJpbmdUb0V4cG9ydCIsImNoYXJ0SlNPTiIsImludGVydmFsSWRzIiwibG9nRXJyb3JNaWRkbGV3YXJlIiwicmVxIiwibmV4dCIsInJldHVybkVycm9yTWlkZGxld2FyZSIsImhlYWRlcnNTZW50IiwiZW5kIiwic3RDb2RlIiwic3RhdHVzIiwianNvbiIsInJhdGVMaW1pdCIsImFwcCIsImxpbWl0Q29uZmlnIiwibXNnIiwicmF0ZU9wdGlvbnMiLCJsaW1pdGVyIiwid2luZG93TXMiLCJsaW1pdCIsImhhbmRsZXIiLCJyZXF1ZXN0IiwiZm9ybWF0Iiwic2VuZCIsImRlZmF1bHQiLCJza2lwIiwicXVlcnkiLCJhY2Nlc3NfdG9rZW4iLCJ1c2UiLCJIdHRwRXJyb3IiLCJzZXRTdGF0dXMiLCJ2U3dpdGNoUm91dGUiLCJwb3N0IiwiYWRtaW5Ub2tlbiIsInRva2VuIiwibmV3VmVyc2lvbiIsInBhcmFtcyIsInVwZGF0ZVZlcnNpb24iLCJyZXZlcnNlZE1pbWUiLCJwbmciLCJqcGVnIiwiZ2lmIiwicmVxdWVzdHNDb3VudGVyIiwiYmVmb3JlUmVxdWVzdCIsImFmdGVyUmVxdWVzdCIsImRvQ2FsbGJhY2tzIiwiY2FsbGJhY2tzIiwidW5pcXVlSWQiLCJjYWxsUmVzcG9uc2UiLCJleHBvcnRIYW5kbGVyIiwic3RvcENvdW50ZXIiLCJjb25uZWN0aW9uIiwicmVtb3RlQWRkcmVzcyIsImI2NCIsIm5vRG93bmxvYWQiLCJhYm9ydENvbnRyb2xsZXIiLCJBYm9ydENvbnRyb2xsZXIiLCJ3cml0YWJsZUZpbmlzaGVkIiwiYWJvcnQiLCJ0b0xvd2VyQ2FzZSIsInN1YnN0ciIsInNpZ25hbCIsInBhdHRlcm4iLCJpc1ByaXZhdGVSYW5nZVVybEZvdW5kIiwiaW5mbyIsImZyb20iLCJoZWFkZXIiLCJhdHRhY2htZW50IiwiZmlsZW5hbWUiLCJwa2dGaWxlIiwicGF0aGVyIiwic2VydmVyU3RhcnRUaW1lIiwic3VjY2Vzc1JhdGVzIiwiY2FsY3VsYXRlU3VjY2Vzc1JhdGlvIiwiYXR0ZW1wdHMiLCJhZGRIZWFsdGhSb3V0ZXMiLCJzZXRJbnRlcnZhbCIsInN1Y2Nlc3NSYXRpbyIsIl8iLCJwZXJpb2QiLCJtb3ZpbmdBdmVyYWdlIiwicmVkdWNlIiwiYSIsImIiLCJib290VGltZSIsInVwdGltZSIsImZsb29yIiwiaGlnaGNoYXJ0c1ZlcnNpb24iLCJhdmVyYWdlUHJvY2Vzc2luZ1RpbWUiLCJmYWlsZWRFeHBvcnRzIiwiYnJvd3NlckNvbm5lY3RlZCIsInN1Y2Vzc1JhdGlvIiwic3ZnRXhwb3J0QXR0ZW1wdHMiLCJqc29uRXhwb3J0QXR0ZW1wdHMiLCJhY3RpdmVTZXJ2ZXJzIiwiTWFwIiwiZXhwcmVzcyIsImRpc2FibGUiLCJjb3JzIiwiX3JlcSIsInNldCIsImF0dGFjaFNlcnZlckVycm9ySGFuZGxlcnMiLCJzb2NrZXQiLCJhcHBseVNlcnZlclRpbWVvdXRzIiwic2VydmVyQ29uZmlnIiwia2VlcEFsaXZlIiwiaGVhZGVyc1RpbWVvdXQiLCJzdGFydFNlcnZlciIsInVwbG9hZExpbWl0Qnl0ZXMiLCJzdG9yYWdlIiwibXVsdGVyIiwibWVtb3J5U3RvcmFnZSIsInVwbG9hZCIsImxpbWl0cyIsImZpZWxkU2l6ZSIsIm1ldGhvZCIsInBvb2xTdGF0cyIsImdldFF1ZXVlUmVqZWN0RGVsYXkiLCJvbkNsb3NlIiwidXJsZW5jb2RlZCIsImV4dGVuZGVkIiwibm9uZSIsImh0dHBTZXJ2ZXIiLCJjcmVhdGVTZXJ2ZXIiLCJsaXN0ZW4iLCJjZXJ0IiwiZnNQcm9taXNlcyIsInJlYWRGaWxlIiwicG9zaXgiLCJodHRwc1NlcnZlciIsIk5hTiIsInN0YXRpYyIsImhlYWx0aFJvdXRlIiwiZXhwb3J0Um91dGVzIiwiX3JlcXVlc3QiLCJzZW5kRmlsZSIsImFjY2VwdFJhbmdlcyIsInVpUm91dGUiLCJlcnJvckhhbmRsZXIiLCJjbG9zZVNlcnZlcnMiLCJkZWxldGUiLCJjbG9zZUlkbGVDb25uZWN0aW9ucyIsImdldFNlcnZlcnMiLCJlbmFibGVSYXRlTGltaXRpbmciLCJnZXRFeHByZXNzIiwiZ2V0QXBwIiwibWlkZGxld2FyZXMiLCJzaHV0ZG93bkNsZWFuVXAiLCJleGl0Q29kZSIsImNsZWFySW50ZXJ2YWwiLCJjbGVhckFsbEludGVydmFscyIsImRyYWluVGltZW91dCIsImdldERyYWluVGltZW91dCIsImV4aXQiLCJpbmRleCIsImluaXRFeHBvcnQiLCJsb2dnaW5nT3B0aW9ucyIsImluaXRMb2dnaW5nIiwic2luZ2xlRXhwb3J0IiwiYmF0Y2hFeHBvcnQiLCJiYXRjaEZ1bmN0aW9ucyIsInBhaXIiLCJjb25maWdJbmRleCIsImZpbmRJbmRleCIsImFyZyIsImZpbGVOYW1lIiwibG9hZENvbmZpZ0ZpbGUiLCJzaG93VXNhZ2UiLCJwcm9wZXJ0aWVzQ2hhaW4iLCJhcmd1bWVudFR5cGUiLCJwYWlyQXJndW1lbnRWYWx1ZSIsIm1hcFRvTmV3Q29uZmlnIiwib2xkT3B0aW9ucyIsIm1hbnVhbENvbmZpZyIsImNvbmZpZ0ZpbGVOYW1lIiwiY29uZmlnRmlsZSIsImNob2ljZSIsInByb21wdHMiLCJvblN1Ym1pdCIsInAiLCJjYXRlZ29yaWVzIiwicXVlc3Rpb25zQ291bnRlciIsImFsbFF1ZXN0aW9ucyIsInNlY3Rpb24iLCJwcm9tcHQiLCJhbnN3ZXIiLCJtb2R1bGUiLCJ3cml0ZUZpbGUiLCJwcmludExvZ28iLCJwYWNrYWdlVmVyc2lvbiJdLCJtYXBwaW5ncyI6Im1lQWVPLE1BQU1BLEVBQWUsQ0FDMUJDLEtBQU0sQ0FBQyxhQUFjLGtCQUFtQixpQkFDeENDLFFB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 diff --git a/dist/index.esm.js b/dist/index.esm.js index 3769f7ea..31395c94 100644 --- a/dist/index.esm.js +++ b/dist/index.esm.js @@ -1,2 +1,2 @@ -import"colors";import{existsSync as e,mkdirSync as t,appendFile as r,readFileSync as o,promises as i,writeFileSync as s}from"fs";import n,{dirname as a,join as l,resolve as c,sep as p,posix as h}from"path";import{HttpsProxyAgent as u}from"https-proxy-agent";import d from"prompts";import g from"dotenv";import{z as m}from"zod";import{createRequire as f}from"module";import{fileURLToPath as v}from"url";import y from"http";import b from"https";import{Pool as w}from"tarn";import{v4 as E}from"uuid";import T from"puppeteer";import{JSDOM as S}from"jsdom";import R from"dompurify";import x from"cors";import _ from"express";import O from"multer";import L from"express-rate-limit";const k={core:["highcharts","highcharts-more","highcharts-3d"],modules:["stock","map","gantt","exporting","parallel-coordinates","accessibility","boost-canvas","boost","data","data-tools","draggable-points","static-scale","broken-axis","heatmap","tilemap","tiledwebmap","timeline","treemap","treegraph","item-series","drilldown","histogram-bellcurve","bullet","funnel","funnel3d","geoheatmap","pyramid3d","networkgraph","pareto","pattern-fill","pictorial","price-indicator","sankey","arc-diagram","dependency-wheel","series-label","series-on-point","solid-gauge","sonification","streamgraph","sunburst","variable-pie","variwide","vector","venn","windbarb","wordcloud","xrange","no-data-to-display","drag-panes","debugger","dumbbell","lollipop","cylinder","organization","dotplot","marker-clusters","hollowcandlestick","heikinashi","flowmap","export-data","navigator","textpath"],indicators:["indicators-all"],custom:["https://cdnjs.cloudflare.com/ajax/libs/moment.js/2.30.1/moment.min.js","https://cdnjs.cloudflare.com/ajax/libs/moment-timezone/0.5.45/moment-timezone-with-data.min.js"]},I={puppeteer:{args:{value:["--allow-running-insecure-content","--ash-no-nudges","--autoplay-policy=user-gesture-required","--block-new-web-contents","--disable-accelerated-2d-canvas","--disable-background-networking","--disable-background-timer-throttling","--disable-backgrounding-occluded-windows","--disable-breakpad","--disable-checker-imaging","--disable-client-side-phishing-detection","--disable-component-extensions-with-background-pages","--disable-component-update","--disable-default-apps","--disable-dev-shm-usage","--disable-domain-reliability","--disable-extensions","--disable-features=CalculateNativeWinOcclusion,InterestFeedContentSuggestions,WebOTP","--disable-hang-monitor","--disable-ipc-flooding-protection","--disable-logging","--disable-notifications","--disable-offer-store-unmasked-wallet-cards","--disable-popup-blocking","--disable-print-preview","--disable-prompt-on-repost","--disable-renderer-backgrounding","--disable-search-engine-choice-screen","--disable-session-crashed-bubble","--disable-setuid-sandbox","--disable-site-isolation-trials","--disable-speech-api","--disable-sync","--enable-unsafe-webgpu","--hide-crash-restore-bubble","--hide-scrollbars","--metrics-recording-only","--mute-audio","--no-default-browser-check","--no-first-run","--no-pings","--pipe","--no-startup-window","--password-store=basic","--process-per-tab","--use-mock-keychain"],type:"string[]",description:"Arguments array to send to Puppeteer."},tempDir:{value:"./tmp/",type:"string",envLink:"PUPPETEER_TEMP_DIR",description:"The directory for Puppeteer to store temporary files."}},highcharts:{version:{value:"latest",type:"string",envLink:"HIGHCHARTS_VERSION",description:"The Highcharts version to be used."},cdnURL:{value:"https://code.highcharts.com/",type:"string",envLink:"HIGHCHARTS_CDN_URL",description:"The CDN URL for Highcharts scripts to be used."},useNpm:{value:!1,type:"boolean",envLink:"HIGHCHARTS_USE_NPM",description:"Flag to use Highcharts scripts from NPM package"},coreScripts:{value:k.core,type:"string[]",envLink:"HIGHCHARTS_CORE_SCRIPTS",description:"The core Highcharts scripts to fetch."},moduleScripts:{value:k.modules,type:"string[]",envLink:"HIGHCHARTS_MODULE_SCRIPTS",description:"The modules of Highcharts to fetch."},indicatorScripts:{value:k.indicators,type:"string[]",envLink:"HIGHCHARTS_INDICATOR_SCRIPTS",description:"The indicators of Highcharts to fetch."},customScripts:{value:k.custom,type:"string[]",description:"Additional custom scripts or dependencies to fetch."},forceFetch:{value:!1,type:"boolean",envLink:"HIGHCHARTS_FORCE_FETCH",description:"The flag to determine whether to refetch all scripts after each server rerun."},cachePath:{value:".cache",type:"string",envLink:"HIGHCHARTS_CACHE_PATH",description:"The path to the cache directory. It is used to store the Highcharts scripts and custom scripts."}},export:{infile:{value:!1,type:"string",description:"The input file should include a name and a type (json or svg). It must be correctly formatted as a JSON or SVG file."},instr:{value:!1,type:"string",description:"Input, provided in the form of a stringified JSON or SVG file, will override the --infile option."},options:{value:!1,type:"string",description:"An alias for the --instr option."},outfile:{value:!1,type:"string",description:"The output filename along with a type (jpeg, png, pdf, or svg). This will ignore the --type flag."},type:{value:"png",type:"string",envLink:"EXPORT_TYPE",description:"The file export format. It can be jpeg, png, pdf, or svg."},constr:{value:"chart",type:"string",envLink:"EXPORT_CONSTR",description:"The constructor to use. Can be chart, stockChart, mapChart, or ganttChart."},defaultHeight:{value:400,type:"number",envLink:"EXPORT_DEFAULT_HEIGHT",description:"the default height of the exported chart. Used when no value is set."},defaultWidth:{value:600,type:"number",envLink:"EXPORT_DEFAULT_WIDTH",description:"The default width of the exported chart. Used when no value is set."},defaultScale:{value:1,type:"number",envLink:"EXPORT_DEFAULT_SCALE",description:"The default scale of the exported chart. Used when no value is set."},height:{value:!1,type:"number",description:"The height of the exported chart, overriding the option in the chart settings."},width:{value:!1,type:"number",description:"The width of the exported chart, overriding the option in the chart settings."},scale:{value:!1,type:"number",description:"The scale of the exported chart, overriding the option in the chart settings. Ranges between 0.1 and 5.0."},globalOptions:{value:!1,type:"string",description:"Either a stringified JSON or a filename containing options to be passed into the Highcharts.setOptions."},themeOptions:{value:!1,type:"string",description:"Either a stringified JSON or a filename containing theme options to be passed into the Highcharts.setOptions."},batch:{value:!1,type:"string",description:'Initiates a batch job with a string containing input/output pairs: "in=out;in=out;...".'},rasterizationTimeout:{value:1500,type:"number",envLink:"EXPORT_RASTERIZATION_TIMEOUT",description:"The duration in milliseconds to wait for rendering a webpage."}},customLogic:{allowCodeExecution:{value:!1,type:"boolean",envLink:"CUSTOM_LOGIC_ALLOW_CODE_EXECUTION",description:"Controls whether the execution of arbitrary code is allowed during the exporting process."},allowFileResources:{value:!1,type:"boolean",envLink:"CUSTOM_LOGIC_ALLOW_FILE_RESOURCES",description:"Controls the ability to inject resources from the filesystem. This setting has no effect when running as a server."},customCode:{value:!1,type:"string",description:"Custom code to execute before chart initialization. It can be a function, code wrapped within a function, or a filename with the .js extension."},callback:{value:!1,type:"string",description:"JavaScript code to run during construction. It can be a function or a filename with the .js extension."},resources:{value:!1,type:"string",description:"Additional resource in the form of a stringified JSON, which may contain files, js, and css sections."},loadConfig:{value:!1,type:"string",legacyName:"fromFile",description:"A file containing a pre-defined configuration to use."},createConfig:{value:!1,type:"string",description:"Enables setting options through a prompt and saving them in a provided config file."}},server:{maxUploadSize:{value:3,type:"number",envLink:"SERVER_MAX_UPLOAD_SIZE",description:"The maximum upload size, in MB, for the server."},enable:{value:!1,type:"boolean",envLink:"SERVER_ENABLE",cliName:"enableServer",description:"When set to true, the server starts on the local IP address 0.0.0.0."},host:{value:"0.0.0.0",type:"string",envLink:"SERVER_HOST",description:"The hostname of the server. Additionally, it starts a server on the provided hostname."},port:{value:7801,type:"number",envLink:"SERVER_PORT",description:"The server port when enabled."},benchmarking:{value:!1,type:"boolean",envLink:"SERVER_BENCHMARKING",cliName:"serverBenchmarking",description:"Indicates whether to display the duration, in milliseconds, of specific actions that occur on the server while serving a request."},proxy:{host:{value:!1,type:"string",envLink:"SERVER_PROXY_HOST",cliName:"proxyHost",description:"The host of the proxy server to use, if it exists."},port:{value:8080,type:"number",envLink:"SERVER_PROXY_PORT",cliName:"proxyPort",description:"The port of the proxy server to use, if it exists."},username:{value:!1,type:"string",envLink:"SERVER_PROXY_USERNAME",cliName:"proxyUsername",description:"The username for the proxy server, if it exists."},password:{value:!1,type:"string",envLink:"SERVER_PROXY_PASSWORD",cliName:"proxyPassword",description:"The password for the proxy server, if it exists."},timeout:{value:5e3,type:"number",envLink:"SERVER_PROXY_TIMEOUT",cliName:"proxyTimeout",description:"The timeout for the proxy server to use, if it exists."}},rateLimiting:{enable:{value:!1,type:"boolean",envLink:"SERVER_RATE_LIMITING_ENABLE",cliName:"enableRateLimiting",description:"Enables rate limiting for the server."},maxRequests:{value:10,type:"number",envLink:"SERVER_RATE_LIMITING_MAX_REQUESTS",legacyName:"rateLimit",description:"The maximum number of requests allowed in one minute."},window:{value:1,type:"number",envLink:"SERVER_RATE_LIMITING_WINDOW",description:"The time window, in minutes, for the rate limiting."},delay:{value:0,type:"number",envLink:"SERVER_RATE_LIMITING_DELAY",description:"The delay duration for each successive request before reaching the maximum limit."},trustProxy:{value:!1,type:"boolean",envLink:"SERVER_RATE_LIMITING_TRUST_PROXY",description:"Set this to true if the server is behind a load balancer."},skipKey:{value:!1,type:"string",envLink:"SERVER_RATE_LIMITING_SKIP_KEY",description:"Allows bypassing the rate limiter and should be provided with the skipToken argument."},skipToken:{value:!1,type:"string",envLink:"SERVER_RATE_LIMITING_SKIP_TOKEN",description:"Allows bypassing the rate limiter and should be provided with the skipKey argument."}},ssl:{enable:{value:!1,type:"boolean",envLink:"SERVER_SSL_ENABLE",cliName:"enableSsl",description:"Enables or disables the SSL protocol."},force:{value:!1,type:"boolean",envLink:"SERVER_SSL_FORCE",cliName:"sslForce",legacyName:"sslOnly",description:"When set to true, the server is forced to serve only over HTTPS."},port:{value:443,type:"number",envLink:"SERVER_SSL_PORT",cliName:"sslPort",description:"The port on which to run the SSL server."},certPath:{value:!1,type:"string",envLink:"SERVER_SSL_CERT_PATH",legacyName:"sslPath",description:"The path to the SSL certificate/key file."}}},pool:{minWorkers:{value:4,type:"number",envLink:"POOL_MIN_WORKERS",description:"The number of minimum and initial pool workers to spawn."},maxWorkers:{value:8,type:"number",envLink:"POOL_MAX_WORKERS",legacyName:"workers",description:"The number of maximum pool workers to spawn."},workLimit:{value:40,type:"number",envLink:"POOL_WORK_LIMIT",description:"The number of work pieces that can be performed before restarting the worker process."},acquireTimeout:{value:5e3,type:"number",envLink:"POOL_ACQUIRE_TIMEOUT",description:"The duration, in milliseconds, to wait for acquiring a resource."},createTimeout:{value:5e3,type:"number",envLink:"POOL_CREATE_TIMEOUT",description:"The duration, in milliseconds, to wait for creating a resource."},destroyTimeout:{value:5e3,type:"number",envLink:"POOL_DESTROY_TIMEOUT",description:"The duration, in milliseconds, to wait for destroying a resource."},idleTimeout:{value:3e4,type:"number",envLink:"POOL_IDLE_TIMEOUT",description:"The duration, in milliseconds, after which an idle resource is destroyed."},createRetryInterval:{value:200,type:"number",envLink:"POOL_CREATE_RETRY_INTERVAL",description:"The duration, in milliseconds, to wait before retrying the create process in case of a failure."},reaperInterval:{value:1e3,type:"number",envLink:"POOL_REAPER_INTERVAL",description:"The duration, in milliseconds, after which the check for idle resources to destroy is triggered."},benchmarking:{value:!1,type:"boolean",envLink:"POOL_BENCHMARKING",cliName:"poolBenchmarking",description:"Indicate whether to show statistics for the pool of resources or not."}},logging:{level:{value:4,type:"number",envLink:"LOGGING_LEVEL",cliName:"logLevel",description:"The logging level to be used."},file:{value:"highcharts-export-server.log",type:"string",envLink:"LOGGING_FILE",cliName:"logFile",description:"The name of a log file. The `logToFile` and `logDest` options also need to be set to enable file logging."},dest:{value:"log/",type:"string",envLink:"LOGGING_DEST",cliName:"logDest",description:"The path to store log files. The `logToFile` option also needs to be set to enable file logging."},toConsole:{value:!0,type:"boolean",envLink:"LOGGING_TO_CONSOLE",cliName:"logToConsole",description:"Enables or disables showing logs in the console."},toFile:{value:!0,type:"boolean",envLink:"LOGGING_TO_FILE",cliName:"logToFile",description:"Enables or disables creation of the log directory and saving the log into a .log file."}},ui:{enable:{value:!1,type:"boolean",envLink:"UI_ENABLE",cliName:"enableUi",description:"Enables or disables the user interface (UI) for the export server."},route:{value:"/",type:"string",envLink:"UI_ROUTE",cliName:"uiRoute",description:"The endpoint route to which the user interface (UI) should be attached."}},other:{nodeEnv:{value:"production",type:"string",envLink:"OTHER_NODE_ENV",description:"The type of Node.js environment."},listenToProcessExits:{value:!0,type:"boolean",envLink:"OTHER_LISTEN_TO_PROCESS_EXITS",description:"Decides whether or not to attach process.exit handlers."},noLogo:{value:!1,type:"boolean",envLink:"OTHER_NO_LOGO",description:"Skip printing the logo on a startup. Will be replaced by a simple text."},hardResetPage:{value:!1,type:"boolean",envLink:"OTHER_HARD_RESET_PAGE",description:"Decides if the page content should be reset entirely."},browserShellMode:{value:!0,type:"boolean",envLink:"OTHER_BROWSER_SHELL_MODE",description:"Decides if the browser runs in the shell mode."}},debug:{enable:{value:!1,type:"boolean",envLink:"DEBUG_ENABLE",cliName:"enableDebug",description:"Enables or disables debug mode for the underlying browser."},headless:{value:!0,type:"boolean",envLink:"DEBUG_HEADLESS",description:"Controls the mode in which the browser is launched when in the debug mode."},devtools:{value:!1,type:"boolean",envLink:"DEBUG_DEVTOOLS",description:"Decides whether to enable DevTools when the browser is in a headful state."},listenToConsole:{value:!1,type:"boolean",envLink:"DEBUG_LISTEN_TO_CONSOLE",description:"Decides whether to enable a listener for console messages sent from the browser."},dumpio:{value:!1,type:"boolean",envLink:"DEBUG_DUMPIO",description:"Redirects browser process stdout and stderr to process.stdout and process.stderr."},slowMo:{value:0,type:"number",envLink:"DEBUG_SLOW_MO",description:"Slows down Puppeteer operations by the specified number of milliseconds."},debuggingPort:{value:9222,type:"number",envLink:"DEBUG_DEBUGGING_PORT",description:"Specifies the debugging port."}}},C={puppeteer:[{type:"list",name:"args",message:"Puppeteer arguments",initial:I.puppeteer.args.value.join(","),separator:","}],highcharts:[{type:"text",name:"version",message:"Highcharts version",initial:I.highcharts.version.value},{type:"text",name:"cdnURL",message:"The URL of CDN",initial:I.highcharts.cdnURL.value},{type:"toggle",name:"useNpm",message:"Flag to use Highcharts scripts from NPM package",initial:I.highcharts.useNpm.value},{type:"multiselect",name:"coreScripts",message:"Available core scripts",instructions:"Space: Select specific, A: Select all, Enter: Confirm.",choices:I.highcharts.coreScripts.value},{type:"multiselect",name:"moduleScripts",message:"Available module scripts",instructions:"Space: Select specific, A: Select all, Enter: Confirm.",choices:I.highcharts.moduleScripts.value},{type:"multiselect",name:"indicatorScripts",message:"Available indicator scripts",instructions:"Space: Select specific, A: Select all, Enter: Confirm.",choices:I.highcharts.indicatorScripts.value},{type:"list",name:"customScripts",message:"Custom scripts",initial:I.highcharts.customScripts.value.join(","),separator:","},{type:"toggle",name:"forceFetch",message:"Force re-fetch the scripts",initial:I.highcharts.forceFetch.value},{type:"text",name:"cachePath",message:"The path to the cache directory",initial:I.highcharts.cachePath.value}],export:[{type:"select",name:"type",message:"The default export file type",hint:`Default: ${I.export.type.value}`,initial:0,choices:["png","jpeg","pdf","svg"]},{type:"select",name:"constr",message:"The default constructor for Highcharts",hint:`Default: ${I.export.constr.value}`,initial:0,choices:["chart","stockChart","mapChart","ganttChart"]},{type:"number",name:"defaultHeight",message:"The default fallback height of the exported chart",initial:I.export.defaultHeight.value},{type:"number",name:"defaultWidth",message:"The default fallback width of the exported chart",initial:I.export.defaultWidth.value},{type:"number",name:"defaultScale",message:"The default fallback scale of the exported chart",initial:I.export.defaultScale.value,min:.1,max:5},{type:"number",name:"rasterizationTimeout",message:"The rendering webpage timeout in milliseconds",initial:I.export.rasterizationTimeout.value}],customLogic:[{type:"toggle",name:"allowCodeExecution",message:"Enable execution of custom code",initial:I.customLogic.allowCodeExecution.value},{type:"toggle",name:"allowFileResources",message:"Enable file resources",initial:I.customLogic.allowFileResources.value}],server:[{type:"toggle",name:"enable",message:"Starts the server on 0.0.0.0",initial:I.server.enable.value},{type:"text",name:"host",message:"Server hostname",initial:I.server.host.value},{type:"number",name:"port",message:"Server port",initial:I.server.port.value},{type:"toggle",name:"benchmarking",message:"Enable server benchmarking",initial:I.server.benchmarking.value},{type:"text",name:"proxy.host",message:"The host of the proxy server to use",initial:I.server.proxy.host.value},{type:"number",name:"proxy.port",message:"The port of the proxy server to use",initial:I.server.proxy.port.value},{type:"number",name:"proxy.timeout",message:"The timeout for the proxy server to use",initial:I.server.proxy.timeout.value},{type:"toggle",name:"rateLimiting.enable",message:"Enable rate limiting",initial:I.server.rateLimiting.enable.value},{type:"number",name:"rateLimiting.maxRequests",message:"The maximum requests allowed per minute",initial:I.server.rateLimiting.maxRequests.value},{type:"number",name:"rateLimiting.window",message:"The rate-limiting time window in minutes",initial:I.server.rateLimiting.window.value},{type:"number",name:"rateLimiting.delay",message:"The delay for each successive request before reaching the maximum",initial:I.server.rateLimiting.delay.value},{type:"toggle",name:"rateLimiting.trustProxy",message:"Set to true if behind a load balancer",initial:I.server.rateLimiting.trustProxy.value},{type:"text",name:"rateLimiting.skipKey",message:"Allows bypassing the rate limiter when provided with the skipToken argument",initial:I.server.rateLimiting.skipKey.value},{type:"text",name:"rateLimiting.skipToken",message:"Allows bypassing the rate limiter when provided with the skipKey argument",initial:I.server.rateLimiting.skipToken.value},{type:"toggle",name:"ssl.enable",message:"Enable SSL protocol",initial:I.server.ssl.enable.value},{type:"toggle",name:"ssl.force",message:"Force serving only over HTTPS",initial:I.server.ssl.force.value},{type:"number",name:"ssl.port",message:"SSL server port",initial:I.server.ssl.port.value},{type:"text",name:"ssl.certPath",message:"The path to find the SSL certificate/key",initial:I.server.ssl.certPath.value}],pool:[{type:"number",name:"minWorkers",message:"The initial number of workers to spawn",initial:I.pool.minWorkers.value},{type:"number",name:"maxWorkers",message:"The maximum number of workers to spawn",initial:I.pool.maxWorkers.value},{type:"number",name:"workLimit",message:"The pieces of work that can be performed before restarting a Puppeteer process",initial:I.pool.workLimit.value},{type:"number",name:"acquireTimeout",message:"The number of milliseconds to wait for acquiring a resource",initial:I.pool.acquireTimeout.value},{type:"number",name:"createTimeout",message:"The number of milliseconds to wait for creating a resource",initial:I.pool.createTimeout.value},{type:"number",name:"destroyTimeout",message:"The number of milliseconds to wait for destroying a resource",initial:I.pool.destroyTimeout.value},{type:"number",name:"idleTimeout",message:"The number of milliseconds after an idle resource is destroyed",initial:I.pool.idleTimeout.value},{type:"number",name:"createRetryInterval",message:"The retry interval in milliseconds after a create process fails",initial:I.pool.createRetryInterval.value},{type:"number",name:"reaperInterval",message:"The reaper interval in milliseconds after triggering the check for idle resources to destroy",initial:I.pool.reaperInterval.value},{type:"toggle",name:"benchmarking",message:"Enable benchmarking for a resource pool",initial:I.pool.benchmarking.value}],logging:[{type:"number",name:"level",message:"The log level (0: silent, 1: error, 2: warning, 3: notice, 4: verbose, 5: benchmark)",initial:I.logging.level.value,round:0,min:0,max:5},{type:"text",name:"file",message:"A log file name. Set with --toFile and --logDest to enable file logging",initial:I.logging.file.value},{type:"text",name:"dest",message:"The path to a log file when the file logging is enabled",initial:I.logging.dest.value},{type:"toggle",name:"toConsole",message:"Enable logging to the console",initial:I.logging.toConsole.value},{type:"toggle",name:"toFile",message:"Enables logging to a file",initial:I.logging.toFile.value}],ui:[{type:"toggle",name:"enable",message:"Enable UI for the export server",initial:I.ui.enable.value},{type:"text",name:"route",message:"A route to attach the UI",initial:I.ui.route.value}],other:[{type:"text",name:"nodeEnv",message:"The type of Node.js environment",initial:I.other.nodeEnv.value},{type:"toggle",name:"listenToProcessExits",message:"Set to false to skip attaching process.exit handlers",initial:I.other.listenToProcessExits.value},{type:"toggle",name:"noLogo",message:"Skip printing the logo on startup. Replaced by simple text",initial:I.other.noLogo.value},{type:"toggle",name:"hardResetPage",message:"Decides if the page content should be reset entirely",initial:I.other.hardResetPage.value},{type:"toggle",name:"browserShellMode",message:"Decides if the browser runs in the shell mode",initial:I.other.browserShellMode.value}],debug:[{type:"toggle",name:"enable",message:"Enables debug mode for the browser instance",initial:I.debug.enable.value},{type:"toggle",name:"headless",message:"The mode setting for the browser",initial:I.debug.headless.value},{type:"toggle",name:"devtools",message:"The DevTools for the headful browser",initial:I.debug.devtools.value},{type:"toggle",name:"listenToConsole",message:"The event listener for console messages from the browser",initial:I.debug.listenToConsole.value},{type:"toggle",name:"dumpio",message:"Redirects the browser stdout and stderr to NodeJS process",initial:I.debug.dumpio.value},{type:"number",name:"slowMo",message:"Puppeteer operations slow down in milliseconds",initial:I.debug.slowMo.value},{type:"number",name:"debuggingPort",message:"The port number for debugging",initial:I.debug.debuggingPort.value}]},N=["options","globalOptions","themeOptions","resources","payload"],A={},P=(e,t="")=>{Object.keys(e).forEach((r=>{if(!["puppeteer","highcharts"].includes(r)){const o=e[r];void 0===o.value?P(o,`${t}.${r}`):(A[o.cliName||r]=`${t}.${r}`.substring(1),void 0!==o.legacyName&&(A[o.legacyName]=`${t}.${r}`.substring(1)))}}))};P(I),g.config();const H=e=>m.string().transform((t=>t.split(",").map((e=>e.trim())).filter((t=>e.includes(t))))).transform((e=>e.length?e:void 0)),$=()=>m.enum(["true","false",""]).transform((e=>""!==e?"true"===e:void 0)),D=e=>m.enum([...e,""]).transform((e=>""!==e?e:void 0)),G=()=>m.string().trim().refine((e=>!["false","undefined","null","NaN"].includes(e)||""===e),(e=>({message:`The string contains forbidden values, received '${e}'`}))).transform((e=>""!==e?e:void 0)),U=()=>m.string().trim().refine((e=>/^(\.\/|\.\.\/|\/|[a-zA-Z]:\\|[a-zA-Z]:\/)?((?:[\w-]+)[\\/]?)+$/.test(e)),{},{message:"The string is an invalid path directory string."}),M=()=>m.string().trim().refine((e=>""===e||!isNaN(parseFloat(e))&&parseFloat(e)>0),(e=>({message:`The value must be numeric and positive, received '${e}'`}))).transform((e=>""!==e?parseFloat(e):void 0)),j=()=>m.string().trim().refine((e=>""===e||!isNaN(parseFloat(e))&&parseFloat(e)>=0),(e=>({message:`The value must be numeric and non-negative, received '${e}'`}))).transform((e=>""!==e?parseFloat(e):void 0)),F=m.object({PUPPETEER_TEMP_DIR:U(),HIGHCHARTS_VERSION:m.string().trim().refine((e=>/^(latest|\d+(\.\d+){0,2})$/.test(e)||""===e),(e=>({message:`HIGHCHARTS_VERSION must be 'latest', a major version, or in the form XX.YY.ZZ, received '${e}'`}))).transform((e=>""!==e?e:void 0)),HIGHCHARTS_CDN_URL:m.string().trim().refine((e=>e.startsWith("https://")||e.startsWith("http://")||""===e),(e=>({message:`Invalid value for HIGHCHARTS_CDN_URL. It should start with http:// or https://, received '${e}'`}))).transform((e=>""!==e?e:void 0)),HIGHCHARTS_USE_NPM:$(),HIGHCHARTS_CORE_SCRIPTS:H(k.core),HIGHCHARTS_MODULE_SCRIPTS:H(k.modules),HIGHCHARTS_INDICATOR_SCRIPTS:H(k.indicators),HIGHCHARTS_FORCE_FETCH:$(),HIGHCHARTS_CACHE_PATH:G(),HIGHCHARTS_ADMIN_TOKEN:G(),EXPORT_TYPE:D(["jpeg","png","pdf","svg"]),EXPORT_CONSTR:D(["chart","stockChart","mapChart","ganttChart"]),EXPORT_DEFAULT_HEIGHT:M(),EXPORT_DEFAULT_WIDTH:M(),EXPORT_DEFAULT_SCALE:M(),EXPORT_RASTERIZATION_TIMEOUT:j(),CUSTOM_LOGIC_ALLOW_CODE_EXECUTION:$(),CUSTOM_LOGIC_ALLOW_FILE_RESOURCES:$(),SERVER_ENABLE:$(),SERVER_HOST:G(),SERVER_PORT:M(),SERVER_MAX_UPLOAD_SIZE:M(),SERVER_BENCHMARKING:$(),SERVER_PROXY_HOST:G(),SERVER_PROXY_PORT:M(),SERVER_PROXY_USERNAME:G(),SERVER_PROXY_PASSWORD:G(),SERVER_PROXY_TIMEOUT:j(),SERVER_RATE_LIMITING_ENABLE:$(),SERVER_RATE_LIMITING_MAX_REQUESTS:j(),SERVER_RATE_LIMITING_WINDOW:j(),SERVER_RATE_LIMITING_DELAY:j(),SERVER_RATE_LIMITING_TRUST_PROXY:$(),SERVER_RATE_LIMITING_SKIP_KEY:G(),SERVER_RATE_LIMITING_SKIP_TOKEN:G(),SERVER_SSL_ENABLE:$(),SERVER_SSL_FORCE:$(),SERVER_SSL_PORT:M(),SERVER_SSL_CERT_PATH:G(),POOL_MIN_WORKERS:j(),POOL_MAX_WORKERS:j(),POOL_WORK_LIMIT:M(),POOL_ACQUIRE_TIMEOUT:j(),POOL_CREATE_TIMEOUT:j(),POOL_DESTROY_TIMEOUT:j(),POOL_IDLE_TIMEOUT:j(),POOL_CREATE_RETRY_INTERVAL:j(),POOL_REAPER_INTERVAL:j(),POOL_BENCHMARKING:$(),LOGGING_LEVEL:m.string().trim().refine((e=>""===e||!isNaN(parseFloat(e))&&parseFloat(e)>=0&&parseFloat(e)<=5),(e=>({message:`Invalid value for LOGGING_LEVEL. We only accept values from 0 to 5 as logging levels, received '${e}'`}))).transform((e=>""!==e?parseFloat(e):void 0)),LOGGING_FILE:G(),LOGGING_DEST:G(),LOGGING_TO_CONSOLE:$(),LOGGING_TO_FILE:$(),UI_ENABLE:$(),UI_ROUTE:G(),OTHER_NODE_ENV:D(["development","production","test"]),OTHER_LISTEN_TO_PROCESS_EXITS:$(),OTHER_NO_LOGO:$(),OTHER_HARD_RESET_PAGE:$(),OTHER_BROWSER_SHELL_MODE:$(),OTHER_ALLOW_XLINK:$(),DEBUG_ENABLE:$(),DEBUG_HEADLESS:$(),DEBUG_DEVTOOLS:$(),DEBUG_LISTEN_TO_CONSOLE:$(),DEBUG_DUMPIO:$(),DEBUG_SLOW_MO:j(),DEBUG_DEBUGGING_PORT:M()}).partial().parse(process.env),W=["red","yellow","blue","gray","green"];let V={toConsole:!0,toFile:!1,pathCreated:!1,levelsDesc:[{title:"error",color:W[0]},{title:"warning",color:W[1]},{title:"notice",color:W[2]},{title:"verbose",color:W[3]},{title:"benchmark",color:W[4]}],listeners:[]};const q=(o,i)=>{V.pathCreated||(!e(V.dest)&&t(V.dest),V.pathCreated=!0),r(`${V.dest}${V.file}`,[i].concat(o).join(" ")+"\n",(e=>{e&&(console.log(`[logger] Unable to write to log file: ${e}`),V.toFile=!1)}))},B=(...e)=>{const[t,...r]=e,{levelsDesc:o,level:i}=V;if(5!==t&&(0===t||t>i||i>o.length))return;const s=`${(new Date).toString().split("(")[0].trim()} [${o[t-1].title}] -`;V.listeners.forEach((e=>{e(s,r.join(" "))})),V.toConsole&&console.log.apply(void 0,[s.toString()[V.levelsDesc[t-1].color]].concat(r)),V.toFile&&q(r,s)},X=(e,t,r)=>{const o=r||t.message,{level:i,levelsDesc:s}=V;if(0===e||e>i||i>s.length)return;const n=`${(new Date).toString().split("(")[0].trim()} [${s[e-1].title}] -`,a=t.message!==t.stackMessage||void 0===t.stackMessage?t.stack:t.stack.split("\n").slice(1).join("\n"),l=[o,"\n",a];V.toConsole&&console.log.apply(void 0,[n.toString()[V.levelsDesc[e-1].color]].concat([o[W[e-1]],"\n",a])),V.listeners.forEach((e=>{e(n,l.join(" "))})),V.toFile&&q(l,n)},K=e=>{e>=0&&e<=V.levelsDesc.length&&(V.level=e)},z=(e,t)=>{if(V={...V,dest:e||V.dest,file:t||V.file,toFile:!0},0===V.dest.length)return B(1,"[logger] File logging initialization: no path supplied.");V.dest.endsWith("/")||(V.dest+="/")},J=a(f(import.meta.url).resolve("highcharts/package.json")),Y=v(new URL("../.",import.meta.url)),Z=(e,t)=>{const r=["png","jpeg","pdf","svg"];if(t){const o=t.split(".").pop();"jpg"===o?e="jpeg":r.includes(o)&&e!==o&&(e=o)}return{"image/png":"png","image/jpeg":"jpeg","application/pdf":"pdf","image/svg+xml":"svg"}[e]||r.find((t=>t===e))||"png"},Q=(e=!1,t)=>{const r=["js","css","files"];let i=e,s=!1;if(t&&e.endsWith(".json"))try{i=ee(o(e,"utf8"))}catch(e){return X(2,e,"[cli] No resources found.")}else i=ee(e),i&&!t&&delete i.files;for(const e in i)r.includes(e)?s||(s=!0):delete i[e];return s?(i.files&&(i.files=i.files.map((e=>e.trim())),(!i.files||i.files.length<=0)&&delete i.files),i):B(3,"[cli] No resources found.")};function ee(e,t){try{const r=JSON.parse("string"!=typeof e?JSON.stringify(e):e);return"string"!=typeof r&&t?JSON.stringify(r):r}catch{return!1}}const te=e=>{if(null===e||"object"!=typeof e)return e;const t=Array.isArray(e)?[]:{};for(const r in e)Object.prototype.hasOwnProperty.call(e,r)&&(t[r]=te(e[r]));return t},re=(e,t)=>JSON.stringify(e,((e,r)=>("string"==typeof r&&((r=r.trim()).startsWith("function(")||r.startsWith("function ("))&&r.endsWith("}")&&(r=t?`EXP_FUN${(r+"").replaceAll(/\n|\t|\r/g," ")}EXP_FUN`:void 0),"function"==typeof r?`EXP_FUN${(r+"").replaceAll(/\n|\t|\r/g," ")}EXP_FUN`:r))).replaceAll(/"EXP_FUN|EXP_FUN"/g,"");function oe(){console.log("\nUsage of CLI arguments:".bold,"\n------",`\nFor more detailed information, visit the readme at: ${"https://github.com/highcharts/node-export-server#readme".bold.yellow}.`);const e=t=>{for(const[r,o]of Object.entries(t))if(Object.prototype.hasOwnProperty.call(o,"value")){let e=` --${o.cliName||r} ${("<"+o.type+">").green} `;if(e.length<48)for(let t=e.length;t<48;t++)e+=".";console.log(e,o.description,`[Default: ${o.value.toString().bold}]`.blue)}else e(o)};Object.keys(I).forEach((t=>{["puppeteer","highcharts"].includes(t)||(console.log(`\n${t.toUpperCase()}`.red),e(I[t]))})),console.log("\n")}const ie=e=>!["false","undefined","null","NaN","0",""].includes(e)&&!!e,se=(e,t)=>{if(e&&"string"==typeof e)return(e=e.trim()).endsWith(".js")?!!t&&se(o(e,"utf8")):e.startsWith("function()")||e.startsWith("function ()")||e.startsWith("()=>")||e.startsWith("() =>")?`(${e})()`:e.replace(/;$/,"")},ne=()=>{const e=process.hrtime.bigint();return()=>Number(process.hrtime.bigint()-e)/1e6};let ae={};const le=()=>ae,ce=(e,t,r=[])=>{const o=te(e);for(const[e,s]of Object.entries(t))o[e]="object"!=typeof(i=s)||Array.isArray(i)||null===i||r.includes(e)||void 0===o[e]?void 0!==s?s:o[e]:ce(o[e],s,r);var i;return o};function pe(e,t={},r=""){Object.keys(e).forEach((o=>{const i=e[o],s=t&&t[o];void 0===i.value?pe(i,s,`${r}.${o}`):(void 0!==s&&(i.value=s),i.envLink in F&&void 0!==F[i.envLink]&&(i.value=F[i.envLink]))}))}function he(e){let t={};for(const[r,o]of Object.entries(e))t[r]=Object.prototype.hasOwnProperty.call(o,"value")?o.value:he(o);return t}function ue(e,t,r){for(;t.length>1;){const o=t.shift();return Object.prototype.hasOwnProperty.call(e,o)||(e[o]={}),e[o]=ue(Object.assign({},e[o]),t,r),e}return e[t[0]]=r,e}async function de(e,t={}){return new Promise(((r,o)=>{const i=(e=>e.startsWith("https")?b:y)(e);i.get(e,Object.assign({headers:{"User-Agent":"highcharts/export",Referer:"highcharts.export"}},t||{}),(e=>{let t="";e.on("data",(e=>{t+=e})),e.on("end",(()=>{t||o("Nothing was fetched from the URL."),e.text=t,r(e)}))})).on("error",(e=>{o(e)}))}))}class ge extends Error{constructor(e){super(),this.message=e,this.stackMessage=e}setError(e){return this.error=e,e.name&&(this.name=e.name),e.statusCode&&(this.statusCode=e.statusCode),e.stack&&(this.stackMessage=e.message,this.stack=e.stack),this}}const me={cdnURL:"https://code.highcharts.com/",activeManifest:{},sources:"",hcVersion:""},fe=e=>e.sources.substring(0,e.sources.indexOf("*/")).replace("/*","").replace("*/","").replace(/\n/g,"").trim(),ve=e=>e.replace(/\\/g,"/").split("/").pop().replace(/\.js$/i,""),ye=async(e,t,r,i=!1,s=!1)=>{let n;if(e.endsWith(".js")||(e=`${e}.js`),i)try{B(4,`[cache] Fetching script from NPM - ${l("node_modules","highcharts",e)}`);const t=c(J,e);if(!t.startsWith(c(J)+p))throw new ge(`[cache] Invalid script path detected for '${e}'. Directory traversal attempt or bad version input.`,403);return n=o(t,"utf8"),r&&n&&(r[ve(e)]=1),n}catch{}else if(B(4,`[cache] Fetching script from CDN - ${e}`),n=await de(e,t),200===n.statusCode&&"string"==typeof n.text)return r&&(r[ve(e)]=1),n.text;if(s)throw new ge(`[cache] Could not fetch the mandatory ${e}. The script might not exist in the requested version.`,404);return B(2,`[cache] Could not fetch the ${e}. The script might not exist in the requested version.`),""},be=async(e,t,r)=>{try{const o={};return me.sources=await(async(e,t,r)=>{const o=e.version,i="latest"!==o&&o?`${o}/`:"",s=e.cdnURL||me.cdnURL;B(3,`[cache] Updating cache version to Highcharts: ${i||"latest"}.`);const n=e.useNpm;let a;const{host:c,port:p,username:h,password:d}=t;if(c&&p)try{a=new u({host:c,port:p,...h&&d?{username:h,password:d}:{}})}catch(e){throw new ge("[cache] Could not create a Proxy Agent.").setError(e)}const g=a?{agent:a,timeout:F.SERVER_PROXY_TIMEOUT}:{};return(await Promise.all([...e.coreScripts.map((e=>ye(n&&e||`${s}${i}${e}`,g,r,n,!0))),...e.moduleScripts.map((e=>ye(n&&l("modules",e)||("map"===e?`${s}maps/${i}modules/${e}`:`${s}${i}modules/${e}`),g,r,n))),...e.indicatorScripts.map((e=>ye(n&&l("indicators",e)||`${s}stock/${i}indicators/${e}`,g,r,n))),...e.customScripts.map((e=>ye(`${e}`,g)))])).join(";\n")})(e,t,o),me.hcVersion=fe(me),s(r,me.sources),o}catch(e){throw new ge("[cache] Unable to update the local Highcharts cache.").setError(e)}},we=async r=>{const{highcharts:i,server:n}=r,a=l(Y,i.cachePath);let c;const p=l(a,"manifest.json"),h=l(a,"sources.js");if(!e(a)&&t(a),!e(p)||i.forceFetch)B(3,"[cache] Fetching and caching Highcharts dependencies."),c=await be(i,n.proxy,h);else{let e=!1;const t=JSON.parse(o(p));if(t.modules&&Array.isArray(t.modules)){const e={};t.modules.forEach((t=>e[t]=1)),t.modules=e}const{coreScripts:r,moduleScripts:s,indicatorScripts:a}=i,l=r.length+s.length+a.length;t.version!==i.version?(B(2,"[cache] A Highcharts version mismatch in the cache, need to re-fetch."),e=!0):Object.keys(t.modules||{}).length!==l?(B(2,"[cache] The cache and the requested modules do not match, need to re-fetch."),e=!0):e=(s||[]).some((e=>{if(!t.modules[e])return B(2,`[cache] The ${e} is missing in the cache, need to re-fetch.`),!0})),e?c=await be(i,n.proxy,h):(B(3,"[cache] Dependency cache is up to date, proceeding."),me.sources=o(h,"utf8"),c=t.modules,me.hcVersion=fe(me))}await(async(e,t)=>{const r={version:e.version,modules:t||{}};me.activeManifest=r,B(3,"[cache] Writing a new manifest.");try{s(l(Y,e.cachePath,"manifest.json"),JSON.stringify(r),"utf8")}catch(e){throw new ge("[cache] Error writing the cache manifest.").setError(e)}})(i,c)},Ee=()=>l(Y,le().highcharts.cachePath),Te=()=>me.hcVersion;function Se(){Highcharts.animObject=function(){return{duration:0}}}async function Re(e,t,r){window._displayErrors=r;const{getOptions:o,merge:i,setOptions:s,wrap:n}=Highcharts;Highcharts.setOptionsObj=i(!1,{},o());const a={animation:!1};t.export.strInj&&(a.height=e.chart.height,a.width=e.chart.width),window.isRenderComplete=!1,n(Highcharts.Chart.prototype,"init",(function(e,t,r){((t=i(t,{exporting:{enabled:!1},plotOptions:{series:{label:{enabled:!1}}},tooltip:{}})).series||[]).forEach((function(e){e.animation=!1})),window.onHighchartsRender||(window.onHighchartsRender=Highcharts.addEvent(this,"render",(()=>{window.isRenderComplete=!0}))),e.apply(this,[t,r])})),n(Highcharts.Series.prototype,"init",(function(e,t,r){e.apply(this,[t,r])}));const l=t.export.strInj?new Function(`return ${t.export.strInj}`)():e;t.customLogic.customCode&&new Function("options",t.customLogic.customCode)(l);const c=i(!1,JSON.parse(t.export.themeOptions),l,{chart:a}),p=t.customLogic.callback?new Function(`return ${t.customLogic.callback}`)():void 0,h=JSON.parse(t.export.globalOptions);h&&s(h);let u=t.export.constr||"chart";u=void 0!==Highcharts[u]?u:"chart",Highcharts[u]("container",c,p);const d=o();for(const e in d)"function"!=typeof d[e]&&delete d[e];s(Highcharts.setOptionsObj),Highcharts.setOptionsObj={}}const xe=o(Y+"/templates/template.html","utf8");let _e;async function Oe(){if(!_e)return!1;const e=await _e.newPage();return await e.setCacheEnabled(!1),await ke(e),function(e){const{debug:t}=le();t.enable&&t.listenToConsole&&e.on("console",(e=>{console.log(`[debug] ${e.text()}`)}));e.on("pageerror",(async t=>{e.isClosed()||await e.$eval("#container",((e,t)=>{window._displayErrors&&(e.innerHTML=t)}),`

Chart input data error:

${t.toString()}`)}))}(e),e}async function Le(e,t){try{for(const e of t)await e.dispose();await e.evaluate((()=>{if("undefined"!=typeof Highcharts){const e=Highcharts.charts;if(Array.isArray(e)&&e.length)for(const t of e)t&&t.destroy(),Highcharts.charts.shift()}const[...e]=document.getElementsByTagName("script"),[,...t]=document.getElementsByTagName("style"),[...r]=document.getElementsByTagName("link");for(const o of[...e,...t,...r])o.remove()}))}catch(e){X(2,e,"[browser] Could not clear page's resources.")}}async function ke(e){await e.setContent(xe,{waitUntil:"domcontentloaded"}),await e.addScriptTag({path:`${Ee()}/sources.js`}),await e.evaluate(Se)}const Ie=async(e,t,r,o)=>{r.export.instr=null,r.export.infile=null;const i=Buffer.byteLength(r.export?.strInj?r.export?.strInj:JSON.stringify(t),"utf-8");if(B(4,`[export] The current total size of data passed to a page is around ${(i/1048576).toFixed(2)} MB`),i>=104857600)throw new ge("[export] The data passed to a page exceeded 100MB.");return e.evaluate(Re,t,r,o)};var Ce=async(e,t,r)=>{let i=[];try{B(4,"[export] Determining export path.");const s=r.export,a=s?.options?.chart?.displayErrors&&me.activeManifest.modules.debugger;let l;if(t.indexOf&&(t.indexOf("=0||t.indexOf("=0)){if(B(4,"[export] Treating as SVG."),"svg"===s.type)return t;l=!0,await e.setContent((e=>`\n\n\n \n \n Highcharts Export\n \n \n \n
\n ${e}\n
\n \n\n\n`)(t),{waitUntil:"domcontentloaded"})}else B(4,"[export] Treating as config."),s.strInj?await Ie(e,{chart:{height:s.height,width:s.width}},r,a):(t.chart.height=s.height,t.chart.width=s.width,await Ie(e,t,r,a));i=await async function(e,t){const r=[],i=t.customLogic.resources;if(i){const s=[];if(i.js&&s.push({content:i.js}),i.files)for(const e of i.files){const t=!e.startsWith("http");s.push(t?{content:o(e,"utf8")}:{url:e})}for(const t of s)try{r.push(await e.addScriptTag(t))}catch(e){X(2,e,"[export] The JS resource cannot be loaded.")}s.length=0;const a=[];if(i.css){let o=i.css.match(/@import\s*([^;]*);/g);if(o)for(let e of o)e&&(e=e.replace("url(","").replace("@import","").replace(/"/g,"").replace(/'/g,"").replace(/;/,"").replace(/\)/g,"").trim(),e.startsWith("http")?a.push({url:e}):t.customLogic.allowFileResources&&a.push({path:n.join(Y,e)}));a.push({content:i.css.replace(/@import\s*([^;]*);/g,"")||" "});for(const t of a)try{r.push(await e.addStyleTag(t))}catch(e){X(2,e,"[export] The CSS resource cannot be loaded.")}a.length=0}}return r}(e,r);const c=l?await e.evaluate((e=>{const t=document.querySelector("#chart-container svg:first-of-type"),r=t.height.baseVal.value*e,o=t.width.baseVal.value*e;return document.body.style.zoom=e,document.body.style.margin="0px",{chartHeight:r,chartWidth:o}}),parseFloat(s.scale)):await e.evaluate((()=>{const{chartHeight:e,chartWidth:t}=window.Highcharts.charts[0];return document.body.style.zoom=1,{chartHeight:e,chartWidth:t}})),p=Math.abs(Math.ceil(c.chartHeight||s.height)),h=Math.abs(Math.ceil(c.chartWidth||s.width)),{x:u,y:d}=await(e=>e.$eval("#chart-container",(e=>{const{x:t,y:r,width:o,height:i}=e.getBoundingClientRect();return{x:t,y:r,width:o,height:Math.trunc(i>1?i:500)}})))(e);let g;if(await e.setViewport({height:p,width:h,deviceScaleFactor:l?1:parseFloat(s.scale)}),"svg"===s.type)g=await(e=>e.$eval("#container svg:first-of-type",(e=>e.outerHTML)))(e);else if(["png","jpeg"].includes(s.type))g=await((e,t,r,o,i)=>Promise.race([e.screenshot({type:t,encoding:r,clip:o,captureBeyondViewport:!0,fullPage:!1,optimizeForSpeed:!0,..."png"!==t?{quality:80}:{},omitBackground:"png"==t}),new Promise(((e,t)=>setTimeout((()=>t(new ge("Rasterization timeout"))),i||1500)))]))(e,s.type,"base64",{width:h,height:p,x:u,y:d},s.rasterizationTimeout);else{if("pdf"!==s.type)throw new ge(`[export] Unsupported output format ${s.type}.`);g=await(async(e,t,r,o,i)=>(await e.emulateMediaType("screen"),e.pdf({height:t+1,width:r,encoding:o,timeout:i||1500})))(e,p,h,"base64",s.rasterizationTimeout)}return await Le(e,i),g}catch(t){return await Le(e,i),t}};let Ne=!1;const Ae={performedExports:0,exportAttempts:0,exportFromSvgAttempts:0,timeSpent:0,droppedExports:0,spentAverage:0};let Pe={};const He={create:async()=>{let e=!1;const t=E(),r=(new Date).getTime();try{if(e=await Oe(),!e||e.isClosed())throw new ge("The page is invalid or closed.");B(3,`[pool] Successfully created a worker ${t} - took ${(new Date).getTime()-r} ms.`)}catch(e){throw new ge("Error encountered when creating a new page.").setError(e)}return{id:t,page:e,workCount:Math.round(Math.random()*(Pe.workLimit/2))}},validate:async e=>!(!e.page||e.page?.isClosed())&&(!(Pe.workLimit&&++e.workCount>Pe.workLimit)||(B(3,`[pool] Worker failed validation: exceeded work limit (limit is ${Pe.workLimit}).`),!1)),destroy:async e=>{B(3,`[pool] Destroying pool entry ${e.id}.`),e.page&&!e.page.isClosed()&&await e.page.close()}},$e=async e=>{if(Pe=e&&e.pool?{...e.pool}:{},await async function(e){const{puppeteer:t,debug:r,other:o}=le(),{enable:i,...s}=r,n={headless:!o.browserShellMode||"shell",userDataDir:t.tempDir||"./tmp/",args:e,handleSIGINT:!1,handleSIGTERM:!1,handleSIGHUP:!1,waitForInitialPage:!1,defaultViewport:null,...i&&s};if(!_e){const e=25;let t=0;const r=async()=>{try{B(3,`[browser] Attempting to get a browser instance (try ${++t}).`),_e=await T.launch(n)}catch(o){if(X(2,o,`[browser] Failed to launch a browser instance - retrying (attempt ${t}/${e}).`),!(t<25))throw o;B(3,`[browser] Retry to open a browser (attempt ${t}/${e}).`),await new Promise((e=>setTimeout(e,4e3))),await r()}};try{await r(),"shell"===n.headless&&B(3,"[browser] Launched browser in shell mode."),i&&B(3,"[browser] Launched browser in debug mode.")}catch(e){throw new ge("[browser] Maximum retries to open a browser instance reached.").setError(e)}if(!_e)throw new ge("[browser] Cannot find a browser to open.")}return _e}(e.puppeteerArgs),B(3,`[pool] Initializing pool with workers: min ${Pe.minWorkers}, max ${Pe.maxWorkers}.`),Ne)return B(4,"[pool] Already initialized, please kill it before creating a new one.");parseInt(Pe.minWorkers)>parseInt(Pe.maxWorkers)&&(Pe.minWorkers=Pe.maxWorkers);try{Ne=new w({...He,min:parseInt(Pe.minWorkers),max:parseInt(Pe.maxWorkers),acquireTimeoutMillis:Pe.acquireTimeout,createTimeoutMillis:Pe.createTimeout,destroyTimeoutMillis:Pe.destroyTimeout,idleTimeoutMillis:Pe.idleTimeout,createRetryIntervalMillis:Pe.createRetryInterval,reapIntervalMillis:Pe.reaperInterval,propagateCreateError:!1}),Ne.on("release",(async e=>{const t=await async function(e,t=!1){try{if(e&&!e.isClosed())return t?(await e.goto("about:blank",{waitUntil:"domcontentloaded"}),await ke(e)):await e.evaluate((()=>{document.body.innerHTML='
'})),!0}catch(e){X(2,e,"[browser] Could not clear the content of the page.")}return!1}(e.page,!1);B(4,`[pool] Releasing a worker with ID ${e.id}. Clear page status: ${t}.`)})),Ne.on("destroySuccess",((e,t)=>{B(4,`[pool] Destroyed a worker with ID ${t.id}.`),t.page=null}));const e=[];for(let t=0;t{Ne.release(e)})),B(3,"[pool] The pool is ready"+(e.length?` with ${e.length} initial resources waiting.`:"."))}catch(e){throw new ge("[pool] Could not create the pool of workers.").setError(e)}};async function De(){if(B(3,"[pool] Killing pool with all workers and closing browser."),Ne){for(const e of Ne.used)Ne.release(e.resource);Ne.destroyed||(await Ne.destroy(),B(4,"[browser] Destroyed the pool of resources."))}await async function(){_e?.connected&&await _e.close(),B(4,"[browser] Closed the browser.")}()}const Ge=async(e,t)=>{let r;try{if(B(4,"[pool] Work received, starting to process."),++Ae.exportAttempts,Pe.benchmarking&&Me(),!Ne)throw new ge("Work received, but pool has not been started.");const o=ne();try{B(4,"[pool] Acquiring a worker handle."),r=await Ne.acquire().promise,t.server.benchmarking&&B(5,t.payload?.requestId?`[benchmark] Request with ID ${t.payload?.requestId} -`:"[benchmark]",`Acquired a worker handle: ${o()}ms.`)}catch(e){throw new ge((t.payload?.requestId?`For request with ID ${t.payload?.requestId} - `:"")+`Error encountered when acquiring an available entry: ${o()}ms.`).setError(e)}if(B(4,"[pool] Acquired a worker handle."),!r.page)throw new ge("Resolved worker page is invalid: the pool setup is wonky.");let i=(new Date).getTime();B(4,`[pool] Starting work on pool entry with ID ${r.id}.`);const s=ne(),n=await Ce(r.page,e,t);if(n instanceof Error)throw"Rasterization timeout"===n.message&&(r.workCount=Pe.workLimit+1,r.page=null),"TimeoutError"===n.name||"Rasterization timeout"===n.message?new ge("Rasterization timeout: your chart may be too complex or large, and failed to render within the allotted time.").setError(n):new ge((t.payload?.requestId?`For request with ID ${t.payload?.requestId} - `:"")+`Error encountered during export: ${s()}ms.`).setError(n);t.server.benchmarking&&B(5,t.payload?.requestId?`[benchmark] Request with ID ${t.payload?.requestId} -`:"[benchmark]",`Exported a chart sucessfully: ${s()}ms.`),Ne.release(r);const a=(new Date).getTime()-i;return Ae.timeSpent+=a,Ae.spentAverage=Ae.timeSpent/++Ae.performedExports,B(4,`[pool] Work completed in ${a} ms.`),{result:n,options:t}}catch(e){throw++Ae.droppedExports,r&&Ne.release(r),new ge(`[pool] In pool.postWork: ${e.message}`).setError(e)}},Ue=()=>({min:Ne.min,max:Ne.max,all:Ne.numFree()+Ne.numUsed(),available:Ne.numFree(),used:Ne.numUsed(),pending:Ne.numPendingAcquires()});function Me(){const{min:e,max:t,all:r,available:o,used:i,pending:s}=Ue();B(5,`[pool] The minimum number of resources allowed by pool: ${e}.`),B(5,`[pool] The maximum number of resources allowed by pool: ${t}.`),B(5,`[pool] The number of all created resources: ${r}.`),B(5,`[pool] The number of available resources: ${o}.`),B(5,`[pool] The number of acquired resources: ${i}.`),B(5,`[pool] The number of resources waiting to be acquired: ${s}.`)}var je=Ue,Fe=()=>Ae;let We=!1;const Ve=async(e,t)=>{B(4,"[chart] Starting the exporting process.");const r=((e,t={})=>{let r={};return e.svg?(r=te(t),r.export.type=e.type||e.export.type,r.export.scale=e.scale||e.export.scale,r.export.outfile=e.outfile||e.export.outfile,r.payload={svg:e.svg}):r=ce(t,e,N),r.export.outfile=r.export?.outfile||`chart.${r.export?.type||"png"}`,r})(e,le()),i=r.export;if(r.payload?.svg&&""!==r.payload.svg)try{B(4,"[chart] Attempting to export from a SVG input.");const e=Ke(function(e){const t=[];F.OTHER_ALLOW_XLINK||t.push("xlink:href");const r=new S("").window;return R(r).sanitize(e,{ADD_TAGS:["foreignObject"],FORBID_ATTR:t})}(r.payload.svg),r,t);return++Ae.exportFromSvgAttempts,e}catch(e){return t(new ge("[chart] Error loading SVG input.").setError(e))}if(i.infile&&i.infile.length)try{return B(4,"[chart] Attempting to export from an input file."),r.export.instr=o(i.infile,"utf8"),Ke(r.export.instr.trim(),r,t)}catch(e){return t(new ge("[chart] Error loading input file.").setError(e))}if(i.instr&&""!==i.instr||i.options&&""!==i.options)try{return B(4,"[chart] Attempting to export from a raw input."),i.instr=i.instr||i.options,ie(r.customLogic?.allowCodeExecution)?Xe(r,t):"string"==typeof i.instr?Ke(i.instr.trim(),r,t):Be(r,i.instr||i.options,t)}catch(e){return t(new ge("[chart] Error loading raw input.").setError(e))}return t(new ge("[chart] No valid input specified. Check if at least one of the following parameters is correctly set: 'infile', 'instr', 'options', or 'svg'."))},qe=e=>{const{chart:t,exporting:r}=e.export?.options||ee(e.export?.instr),o=ee(e.export?.globalOptions);let i=e.export?.scale||r?.scale||o?.exporting?.scale||e.export?.defaultScale||1;i=Math.max(.1,Math.min(i,5)),i=((e,t=1)=>{const r=Math.pow(10,t||0);return Math.round(+e*r)/r})(i,2);const s={height:e.export?.height||r?.sourceHeight||t?.height||o?.exporting?.sourceHeight||o?.chart?.height||e.export?.defaultHeight||400,width:e.export?.width||r?.sourceWidth||t?.width||o?.exporting?.sourceWidth||o?.chart?.width||e.export?.defaultWidth||600,scale:i};for(let[e,t]of Object.entries(s))s[e]="string"==typeof t?+t.replace(/px|%/gi,""):t;return s},Be=async(e,t,r,i)=>{let{export:s,customLogic:n}=e;const a="boolean"==typeof n.allowCodeExecution?n.allowCodeExecution:We;if(n){if(a)if("string"==typeof e.customLogic.resources)e.customLogic.resources=Q(e.customLogic.resources,ie(e.customLogic.allowFileResources));else if(!e.customLogic.resources)try{const t=o("resources.json","utf8");e.customLogic.resources=Q(t,ie(e.customLogic.allowFileResources))}catch(e){B(2,"[chart] Unable to load the default resources.json file.")}}else n=e.customLogic={};if(!a&&n){if(n.callback||n.resources||n.customCode)return r(new ge("[chart] The 'callback', 'resources' and 'customCode' options have been disabled for this server."));n.callback=!1,n.resources=!1,n.customCode=!1}if(t&&(t.chart=t.chart||{},t.exporting=t.exporting||{},t.exporting.enabled=!1),s.constr=s.constr||"chart",s.type=Z(s.type,s.outfile),"svg"===s.type&&(s.width=!1),["globalOptions","themeOptions"].forEach((e=>{try{s&&s[e]&&("string"==typeof s[e]&&s[e].endsWith(".json")?s[e]=ee(o(s[e],"utf8"),!0):s[e]=ee(s[e],!0))}catch(t){s[e]={},X(2,t,`[chart] The '${e}' cannot be loaded.`)}})),n.allowCodeExecution)try{n.customCode=se(n.customCode,n.allowFileResources)}catch(e){X(2,e,"[chart] The 'customCode' cannot be loaded.")}if(n&&n.callback&&n.callback?.indexOf("{")<0)if(n.allowFileResources)try{n.callback=o(n.callback,"utf8")}catch(e){n.callback=!1,X(2,e,"[chart] The 'callback' cannot be loaded.")}else n.callback=!1;e.export={...e.export,...qe(e)};try{return r(!1,await Ge(s.strInj||t||i,e))}catch(e){return r(e)}},Xe=(e,t)=>{try{let r,o=e.export.instr||e.export.options;return"string"!=typeof o&&(r=o=re(o,e.customLogic?.allowCodeExecution)),r=o.replaceAll(/\t|\n|\r/g,"").trim(),";"===r[r.length-1]&&(r=r.substring(0,r.length-1)),e.export.strInj=r,Be(e,!1,t)}catch(r){return t(new ge(`[chart] Malformed input detected for ${e.export?.requestId||"?"}. Please make sure that your JSON/JavaScript options are sent using the "options" attribute, and that if you're using SVG, it is unescaped.`).setError(r))}},Ke=(e,t,r)=>{const{allowCodeExecution:o}=t.customLogic;if(e.indexOf("=0||e.indexOf("=0)return B(4,"[chart] Parsing input as SVG."),Be(t,!1,r,e);try{const o=JSON.parse(e.replaceAll(/\t|\n|\r/g," "));return Be(t,o,r)}catch(e){return ie(o)?Xe(t,r):r(new ge("[chart] Only JSON configurations and SVG are allowed for this server. If this is your server, JavaScript custom code can be enabled by starting the server with the --allowCodeExecution flag.").setError(e))}},ze=[],Je=()=>{B(4,"[server] Clearing all registered intervals.");for(const e of ze)clearInterval(e)},Ye=(e,t,r,o)=>{X(1,e),"development"!==F.OTHER_NODE_ENV&&delete e.stack,o(e)},Ze=(e,t,r,o)=>{const{statusCode:i,status:s,message:n,stack:a}=e,l=i||s||400;r.status(l).json({statusCode:l,message:n,stack:a})};var Qe=(e,t)=>{const r="Too many requests, you have been rate limited. Please try again later.",o={max:t.maxRequests||30,window:t.window||1,delay:t.delay||0,trustProxy:t.trustProxy||!1,skipKey:t.skipKey||!1,skipToken:t.skipToken||!1};o.trustProxy&&e.enable("trust proxy");const i=L({windowMs:60*o.window*1e3,max:o.max,delayMs:o.delay,handler:(e,t)=>{t.format({json:()=>{t.status(429).send({message:r})},default:()=>{t.status(429).send(r)}})},skip:e=>!1!==o.skipKey&&!1!==o.skipToken&&e.query.key===o.skipKey&&e.query.access_token===o.skipToken&&(B(4,"[rate limiting] Skipping rate limiter."),!0)});e.use(i),B(3,`[rate limiting] Enabled rate limiting with ${o.max} requests per ${o.window} minute for each IP, trusting proxy: ${o.trustProxy}.`)};class et extends ge{constructor(e,t){super(e),this.status=this.statusCode=t}setStatus(e){return this.status=e,this}}var tt=e=>!!e&&e.post("/version/change/:newVersion",(async(e,t,r)=>{try{const r=F.HIGHCHARTS_ADMIN_TOKEN;if(!r||!r.length)throw new et("The server is not configured to perform run-time version changes: HIGHCHARTS_ADMIN_TOKEN is not set.",401);const o=e.get("hc-auth");if(!o||o!==r)throw new et("Invalid or missing token: Set the token in the hc-auth header.",401);const i=e.params.newVersion;if(!i||!/^[a-zA-Z0-9.-]+$/.test(i))throw new et("No new version supplied.",400);try{await(async e=>{const t=le();t?.highcharts&&(t.highcharts.version=e),await we(t)})(i)}catch(e){throw new et(`Version change: ${e.message}`,e.statusCode).setError(e)}t.status(200).send({statusCode:200,version:Te(),message:`Successfully updated Highcharts to version: ${i}.`})}catch(e){r(e)}}));const rt={png:"image/png",jpeg:"image/jpeg",gif:"image/gif",pdf:"application/pdf",svg:"image/svg+xml"};let ot=0;const it=[],st=[],nt=(e,t,r,o)=>{let i=!0;const{id:s,uniqueId:n,type:a,body:l}=o;return e.some((e=>{if(e){let o=e(t,r,s,n,a,l);return void 0!==o&&!0!==o&&(i=o),!0}})),i},at=async(e,t,r)=>{try{const r=ne(),i=E().replace(/-/g,""),s=le(),n=e.body,a=++ot;let l=Z(n.type);if(!n||"object"==typeof(o=n)&&!Array.isArray(o)&&null!==o&&0===Object.keys(o).length)throw new et("The request body is required. Please ensure that your Content-Type header is correct (accepted types are application/json and multipart/form-data).",400);let c=ee(n.infile||n.options||n.data);if(!c&&!n.svg)throw B(2,`The request with ID ${i} from ${e.headers["x-forwarded-for"]||e.connection.remoteAddress} was incorrect:\n Content-Type: ${e.headers["content-type"]}. \n Chart constructor: ${n.constr}.\n Dimensions: ${n.width}x${n.height} @ ${n.scale} scale.\n Type: ${l}.\n Is SVG set? ${void 0!==n.svg}.\n B64? ${void 0!==n.b64}.\n No download? ${void 0!==n.noDownload}.\n\n Payload received: ${JSON.stringify(n.infile||n.options||n.data||n.svg)}\n\n `),new et("No correct chart data found. Ensure that you are using either application/json or multipart/form-data headers. If sending JSON, make sure the chart data is in the 'infile', 'options', or 'data' attribute. If sending SVG, ensure it is in the 'svg' attribute.",400);let p=!1;if(p=nt(it,e,t,{id:a,uniqueId:i,type:l,body:n}),!0!==p)return t.send(p);let h=!1;e.socket.on("close",(e=>{e&&(h=!0)})),B(4,`[export] Got an incoming HTTP request with ID ${i}.`),n.constr="string"==typeof n.constr&&n.constr||"chart";const u={export:{instr:c,type:l,constr:n.constr[0].toLowerCase()+n.constr.substr(1),height:n.height,width:n.width,scale:n.scale||s.export.scale,globalOptions:ee(n.globalOptions,!0),themeOptions:ee(n.themeOptions,!0)},customLogic:{allowCodeExecution:We,allowFileResources:!1,resources:ee(n.resources,!0),callback:n.callback,customCode:n.customCode}};c&&(u.export.instr=re(c,u.customLogic.allowCodeExecution));const d=ce(s,u);if(d.export.options=c,d.payload={svg:n.svg||!1,b64:n.b64||!1,noDownload:n.noDownload||!1,requestId:i},n.svg&&(e=>[/xlink:href="(?:http:\/\/|https:\/\/)?localhost\b/,/xlink:href="(?:http:\/\/|https:\/\/)?10\.\d{1,3}\.\d{1,3}\.\d{1,3}\b/,/xlink:href="(?:http:\/\/|https:\/\/)?127\.\d{1,3}\.\d{1,3}\.\d{1,3}\b/,/xlink:href="(?:http:\/\/|https:\/\/)?172\.(1[6-9]|2[0-9]|3[0-1])\.\d{1,3}\.\d{1,3}\b/,/xlink:href="(?:http:\/\/|https:\/\/)?192\.168\.\d{1,3}\.\d{1,3}\b/].some((t=>t.test(e))))(d.payload.svg))throw new et("SVG potentially contain at least one forbidden URL in xlink:href element. Please review the SVG content and ensure that all referenced URLs comply with security policies.",400);await Ve(d,((o,c)=>{if(e.socket.removeAllListeners("close"),s.server.benchmarking&&B(5,`[benchmark] Request with ID ${i} - After the whole exporting process: ${r()}ms.`),h)return B(3,"[export] The client closed the connection before the chart finished processing.");if(o)throw o;if(!c||!c.result)throw new et(`Unexpected return from chart generation. Please check your request data. For the request with ID ${i}, the result is ${c.result}.`,400);return l=c.options.export.type,nt(st,e,t,{id:a,body:c.result}),c.result?n.b64?"pdf"===l||"svg"==l?t.send(Buffer.from(c.result,"utf8").toString("base64")):t.send(c.result):(t.header("Content-Type",rt[l]||"image/png"),n.noDownload||t.attachment(`${e.params.filename||e.body.filename||"chart"}.${l||"png"}`),"svg"===l?t.send(c.result):t.send(Buffer.from(c.result,"base64"))):void 0}))}catch(e){r(e)}var o};const lt=JSON.parse(o(l(Y,"package.json"))),ct=new Date,pt=[];function ht(e){if(!e)return!1;var t;t=setInterval((()=>{const e=Fe(),t=0===e.exportAttempts?1:e.performedExports/e.exportAttempts*100;pt.push(t),pt.length>30&&pt.shift()}),6e4),ze.push(t),e.get("/health",((e,t)=>{const r=Fe(),o=pt.length,i=pt.reduce(((e,t)=>e+t),0)/pt.length;B(4,"[health.js] GET /health [200] - returning server health."),t.send({status:"OK",bootTime:ct,uptime:Math.floor(((new Date).getTime()-ct.getTime())/1e3/60)+" minutes",version:lt.version,highchartsVersion:Te(),averageProcessingTime:r.spentAverage,performedExports:r.performedExports,failedExports:r.droppedExports,exportAttempts:r.exportAttempts,sucessRatio:r.performedExports/r.exportAttempts*100,pool:je(),period:o,movingAverage:i,message:isNaN(i)||!pt.length?"Too early to report. No exports made yet. Please check back soon.":`Last ${o} minutes had a success rate of ${i.toFixed(2)}%.`,svgExportAttempts:r.exportFromSvgAttempts,jsonExportAttempts:r.performedExports-r.exportFromSvgAttempts})}))}const ut=new Map,dt=_();dt.disable("x-powered-by"),dt.use(x()),dt.use(((e,t,r)=>{t.set("Accept-Ranges","none"),r()}));const gt=e=>{e.on("clientError",((e,t)=>{X(1,e,`[server] Client error: ${e.message}, destroying socket.`),t.destroy()})),e.on("error",(e=>{X(1,e,`[server] Server error: ${e.message}`)})),e.on("connection",(e=>{e.on("error",(e=>{X(1,e,`[server] Socket error: ${e.message}`)}))}))},mt=async e=>{try{const t=1024*(e.maxUploadSize||3)*1024,r=O.memoryStorage(),o=O({storage:r,limits:{fieldSize:t}});if(dt.use(_.json({limit:t})),dt.use(_.urlencoded({extended:!0,limit:t})),dt.use(o.none()),!e.enable)return!1;if(!e.ssl.force){const t=y.createServer(dt);gt(t),t.listen(e.port,e.host),ut.set(e.port,t),B(3,`[server] Started HTTP server on ${e.host}:${e.port}.`)}if(e.ssl.enable){let t,r;try{t=await i.readFile(h.join(e.ssl.certPath,"server.key"),"utf8"),r=await i.readFile(h.join(e.ssl.certPath,"server.crt"),"utf8")}catch(t){B(2,`[server] Unable to load key/certificate from the '${e.ssl.certPath}' path. Could not run secured layer server.`)}if(t&&r){const o=b.createServer({key:t,cert:r},dt);gt(o),o.listen(e.ssl.port,e.host),ut.set(e.ssl.port,o),B(3,`[server] Started HTTPS server on ${e.host}:${e.ssl.port}.`)}}e.rateLimiting&&e.rateLimiting.enable&&![0,NaN].includes(e.rateLimiting.maxRequests)&&Qe(dt,e.rateLimiting),dt.use(_.static(h.join(Y,"public"))),ht(dt),(e=>{e.post("/",at),e.post("/:filename",at)})(dt),(e=>{!!e&&e.get("/",((e,t)=>{t.sendFile(l(Y,"public","index.html"),{acceptRanges:!1})}))})(dt),tt(dt),(e=>{e.use(Ye),e.use(Ze)})(dt)}catch(e){throw new ge("[server] Could not configure and start the server.").setError(e)}},ft=()=>{B(4,"[server] Closing all servers.");for(const[e,t]of ut)t.close((()=>{ut.delete(e),B(4,`[server] Closed server on port: ${e}.`)}))};var vt={startServer:mt,closeServers:ft,getServers:()=>ut,enableRateLimiting:e=>Qe(dt,e),getExpress:()=>_,getApp:()=>dt,use:(e,...t)=>{dt.use(e,...t)},get:(e,...t)=>{dt.get(e,...t)},post:(e,...t)=>{dt.post(e,...t)}};const yt=async e=>{await Promise.allSettled([Je(),ft(),De()]),process.exit(e)};var bt={server:vt,startServer:mt,initExport:async e=>{var t;return t=e.customLogic&&e.customLogic.allowCodeExecution,We=ie(t),(e=>{for(const[t,r]of Object.entries(e))V[t]=r;K(e&&parseInt(e.level)),e&&e.dest&&e.toFile&&z(e.dest,e.file||"highcharts-export-server.log")})(e.logging),e.other.listenToProcessExits&&(B(3,"[process] Attaching exit listeners to the process."),process.on("exit",(e=>{B(4,`Process exited with code ${e}.`)})),process.on("SIGINT",(async(e,t)=>{B(4,`The ${e} event with code: ${t}.`),await yt(0)})),process.on("SIGTERM",(async(e,t)=>{B(4,`The ${e} event with code: ${t}.`),await yt(0)})),process.on("SIGHUP",(async(e,t)=>{B(4,`The ${e} event with code: ${t}.`),await yt(0)})),process.on("uncaughtException",(async(e,t)=>{X(1,e,`The ${t} error.`),await yt(1)}))),await we(e),await $e({pool:e.pool||{minWorkers:1,maxWorkers:1},puppeteerArgs:e.puppeteer.args||[]}),e},singleExport:async e=>{e.export.instr=e.export.instr||e.export.options,await Ve(e,(async(e,t)=>{if(e)throw e;const{outfile:r,type:o}=t.options.export;s(r||`chart.${o}`,"svg"!==o?Buffer.from(t.result,"base64"):t.result),await De()}))},batchExport:async e=>{const t=[];for(let r of e.export.batch.split(";"))r=r.split("="),2===r.length&&t.push(Ve({...e,export:{...e.export,infile:r[0],outfile:r[1]}},((e,t)=>{if(e)throw e;s(t.options.export.outfile,"svg"!==t.options.export.type?Buffer.from(t.result,"base64"):t.result)})));try{await Promise.all(t),await De()}catch(e){throw new ge("[chart] Error encountered during batch export.").setError(e)}},startExport:Ve,initPool:$e,killPool:De,setOptions:(e,t)=>(t?.length&&(ae=function(e){const t=e.findIndex((e=>"loadConfig"===e.replace(/-/g,"")));if(t>-1&&e[t+1]){const r=e[t+1];try{if(r&&r.endsWith(".json"))return JSON.parse(o(r))}catch(e){X(2,e,`[config] Unable to load the configuration from the ${r} file.`)}}return{}}(t)),pe(I,ae),ae=he(I),e&&(ae=ce(ae,e,N)),t?.length&&(ae=function(e,t,r){let o=!1;for(let i=0;i(n.length-1===r&&(a=e[t].type),e[t])),r),n.reduce(((e,r,l)=>(n.length-1===l&&void 0!==e[r]&&(t[++i]?"boolean"===a?e[r]=ie(t[i]):"number"===a?e[r]=+t[i]:a.indexOf("]")>=0?e[r]=t[i].split(","):e[r]=t[i]:(B(2,`[config] Missing value for the '${s}' argument. Using the default value.`),o=!0)),e[r])),e)}o&&oe();return e}(ae,t,I)),ae),shutdownCleanUp:yt,log:B,logWithStack:X,setLogLevel:K,enableFileLogging:z,mapToNewConfig:e=>{const t={};for(const[r,o]of Object.entries(e)){const e=A[r]?A[r].split("."):[];e.reduce(((t,r,i)=>t[r]=e.length-1===i?o:t[r]||{}),t)}return t},manualConfig:async t=>{let r={};e(t)&&(r=JSON.parse(o(t,"utf8")));const s=Object.keys(C).map((e=>({title:`${e} options`,value:e})));return d({type:"multiselect",name:"category",message:"Which category do you want to configure?",hint:"Space: Select specific, A: Select all, Enter: Confirm.",instructions:"",choices:s},{onSubmit:async(e,o)=>{let s=0,n=[];for(const e of o)C[e]=C[e].map((t=>({...t,section:e}))),n=[...n,...C[e]];return await d(n,{onSubmit:async(e,o)=>{if("moduleScripts"===e.name?(o=o.length?o.map((t=>e.choices[t])):e.choices,r[e.section][e.name]=o):r[e.section]=ue(Object.assign({},r[e.section]||{}),e.name.split("."),e.choices?e.choices[o]:o),++s===n.length){try{await i.writeFile(t,JSON.stringify(r,null,2),"utf8")}catch(e){X(1,e,`[config] An error occurred while creating the ${t} file.`)}return!0}}}),!0}})},printLogo:e=>{const t=JSON.parse(o(l(Y,"package.json"))).version;e?console.log(`Starting Highcharts Export Server v${t}...`):console.log(o(Y+"/msg/startup.msg").toString().bold.yellow,`v${t}\n`.bold)},printUsage:oe};export{bt as default}; +import"colors";import{existsSync as e,mkdirSync as t,appendFile as r,readFileSync as o,promises as i,writeFileSync as n}from"fs";import s,{dirname as a,join as l,resolve as c,sep as p,posix as u}from"path";import{HttpsProxyAgent as h}from"https-proxy-agent";import d from"prompts";import m from"dotenv";import{z as g}from"zod";import{createRequire as f}from"module";import{fileURLToPath as v}from"url";import y from"http";import b from"https";import{Pool as w}from"tarn";import{v4 as E}from"uuid";import T from"puppeteer";import{JSDOM as S}from"jsdom";import R from"dompurify";import _ from"cors";import x from"express";import O from"multer";import L from"express-rate-limit";const k={core:["highcharts","highcharts-more","highcharts-3d"],modules:["stock","map","gantt","exporting","parallel-coordinates","accessibility","boost-canvas","boost","data","data-tools","draggable-points","static-scale","broken-axis","heatmap","tilemap","tiledwebmap","timeline","treemap","treegraph","item-series","drilldown","histogram-bellcurve","bullet","funnel","funnel3d","geoheatmap","pyramid3d","networkgraph","pareto","pattern-fill","pictorial","price-indicator","sankey","arc-diagram","dependency-wheel","series-label","series-on-point","solid-gauge","sonification","streamgraph","sunburst","variable-pie","variwide","vector","venn","windbarb","wordcloud","xrange","no-data-to-display","drag-panes","debugger","dumbbell","lollipop","cylinder","organization","dotplot","marker-clusters","hollowcandlestick","heikinashi","flowmap","export-data","navigator","textpath"],indicators:["indicators-all"],custom:["https://cdnjs.cloudflare.com/ajax/libs/moment.js/2.30.1/moment.min.js","https://cdnjs.cloudflare.com/ajax/libs/moment-timezone/0.5.45/moment-timezone-with-data.min.js"]},I={puppeteer:{args:{value:["--allow-running-insecure-content","--ash-no-nudges","--autoplay-policy=user-gesture-required","--block-new-web-contents","--disable-accelerated-2d-canvas","--disable-background-networking","--disable-background-timer-throttling","--disable-backgrounding-occluded-windows","--disable-breakpad","--disable-checker-imaging","--disable-client-side-phishing-detection","--disable-component-extensions-with-background-pages","--disable-component-update","--disable-default-apps","--disable-dev-shm-usage","--disable-domain-reliability","--disable-extensions","--disable-features=CalculateNativeWinOcclusion,InterestFeedContentSuggestions,WebOTP","--disable-hang-monitor","--disable-ipc-flooding-protection","--disable-logging","--disable-notifications","--disable-offer-store-unmasked-wallet-cards","--disable-popup-blocking","--disable-print-preview","--disable-prompt-on-repost","--disable-renderer-backgrounding","--disable-search-engine-choice-screen","--disable-session-crashed-bubble","--disable-setuid-sandbox","--disable-site-isolation-trials","--disable-speech-api","--disable-sync","--enable-unsafe-webgpu","--hide-crash-restore-bubble","--hide-scrollbars","--metrics-recording-only","--mute-audio","--no-default-browser-check","--no-first-run","--no-pings","--pipe","--no-startup-window","--password-store=basic","--process-per-tab","--use-mock-keychain"],type:"string[]",description:"Arguments array to send to Puppeteer."},launchRetryWindow:{value:3e4,type:"number",envLink:"PUPPETEER_LAUNCH_RETRY_WINDOW",description:"How long, in milliseconds, to keep retrying a browser launch before giving up. Delays between attempts grow and carry jitter. Keep this below the time an orchestrator waits before replacing an instance that has not become healthy, so that a browser which cannot start is reported rather than silently retried."},tempDir:{value:"./tmp/",type:"string",envLink:"PUPPETEER_TEMP_DIR",description:"The directory for Puppeteer to store temporary files."}},highcharts:{version:{value:"latest",type:"string",envLink:"HIGHCHARTS_VERSION",description:"The Highcharts version to be used."},cdnURL:{value:"https://code.highcharts.com/",type:"string",envLink:"HIGHCHARTS_CDN_URL",description:"The CDN URL for Highcharts scripts to be used."},useNpm:{value:!1,type:"boolean",envLink:"HIGHCHARTS_USE_NPM",description:"Flag to use Highcharts scripts from NPM package"},coreScripts:{value:k.core,type:"string[]",envLink:"HIGHCHARTS_CORE_SCRIPTS",description:"The core Highcharts scripts to fetch."},moduleScripts:{value:k.modules,type:"string[]",envLink:"HIGHCHARTS_MODULE_SCRIPTS",description:"The modules of Highcharts to fetch."},indicatorScripts:{value:k.indicators,type:"string[]",envLink:"HIGHCHARTS_INDICATOR_SCRIPTS",description:"The indicators of Highcharts to fetch."},customScripts:{value:k.custom,type:"string[]",description:"Additional custom scripts or dependencies to fetch."},forceFetch:{value:!1,type:"boolean",envLink:"HIGHCHARTS_FORCE_FETCH",description:"The flag to determine whether to refetch all scripts after each server rerun."},cachePath:{value:".cache",type:"string",envLink:"HIGHCHARTS_CACHE_PATH",description:"The path to the cache directory. It is used to store the Highcharts scripts and custom scripts."}},export:{infile:{value:!1,type:"string",description:"The input file should include a name and a type (json or svg). It must be correctly formatted as a JSON or SVG file."},instr:{value:!1,type:"string",description:"Input, provided in the form of a stringified JSON or SVG file, will override the --infile option."},options:{value:!1,type:"string",description:"An alias for the --instr option."},outfile:{value:!1,type:"string",description:"The output filename along with a type (jpeg, png, pdf, or svg). This will ignore the --type flag."},type:{value:"png",type:"string",envLink:"EXPORT_TYPE",description:"The file export format. It can be jpeg, png, pdf, or svg."},constr:{value:"chart",type:"string",envLink:"EXPORT_CONSTR",description:"The constructor to use. Can be chart, stockChart, mapChart, or ganttChart."},defaultHeight:{value:400,type:"number",envLink:"EXPORT_DEFAULT_HEIGHT",description:"the default height of the exported chart. Used when no value is set."},defaultWidth:{value:600,type:"number",envLink:"EXPORT_DEFAULT_WIDTH",description:"The default width of the exported chart. Used when no value is set."},defaultScale:{value:1,type:"number",envLink:"EXPORT_DEFAULT_SCALE",description:"The default scale of the exported chart. Used when no value is set."},height:{value:!1,type:"number",description:"The height of the exported chart, overriding the option in the chart settings."},width:{value:!1,type:"number",description:"The width of the exported chart, overriding the option in the chart settings."},scale:{value:!1,type:"number",description:"The scale of the exported chart, overriding the option in the chart settings. Ranges between 0.1 and 5.0."},globalOptions:{value:!1,type:"string",description:"Either a stringified JSON or a filename containing options to be passed into the Highcharts.setOptions."},themeOptions:{value:!1,type:"string",description:"Either a stringified JSON or a filename containing theme options to be passed into the Highcharts.setOptions."},batch:{value:!1,type:"string",description:'Initiates a batch job with a string containing input/output pairs: "in=out;in=out;...".'},rasterizationTimeout:{value:1500,type:"number",envLink:"EXPORT_RASTERIZATION_TIMEOUT",description:"The duration in milliseconds to wait for rendering a webpage."}},customLogic:{allowCodeExecution:{value:!1,type:"boolean",envLink:"CUSTOM_LOGIC_ALLOW_CODE_EXECUTION",description:"Controls whether the execution of arbitrary code is allowed during the exporting process."},allowFileResources:{value:!1,type:"boolean",envLink:"CUSTOM_LOGIC_ALLOW_FILE_RESOURCES",description:"Controls the ability to inject resources from the filesystem. This setting has no effect when running as a server."},customCode:{value:!1,type:"string",description:"Custom code to execute before chart initialization. It can be a function, code wrapped within a function, or a filename with the .js extension."},callback:{value:!1,type:"string",description:"JavaScript code to run during construction. It can be a function or a filename with the .js extension."},resources:{value:!1,type:"string",description:"Additional resource in the form of a stringified JSON, which may contain files, js, and css sections."},loadConfig:{value:!1,type:"string",legacyName:"fromFile",description:"A file containing a pre-defined configuration to use."},createConfig:{value:!1,type:"string",description:"Enables setting options through a prompt and saving them in a provided config file."}},server:{maxUploadSize:{value:3,type:"number",envLink:"SERVER_MAX_UPLOAD_SIZE",description:"The maximum upload size, in MB, for the server."},enable:{value:!1,type:"boolean",envLink:"SERVER_ENABLE",cliName:"enableServer",description:"When set to true, the server starts on the local IP address 0.0.0.0."},host:{value:"0.0.0.0",type:"string",envLink:"SERVER_HOST",description:"The hostname of the server. Additionally, it starts a server on the provided hostname."},port:{value:7801,type:"number",envLink:"SERVER_PORT",description:"The server port when enabled."},keepAliveTimeout:{value:65e3,type:"number",envLink:"SERVER_KEEP_ALIVE_TIMEOUT",description:"How long, in milliseconds, an idle keep-alive connection is held open. Node defaults to 5 seconds, which is shorter than the idle timeout of a typical proxy or load balancer, and the mismatch causes sporadic gateway errors when the proxy sends a request into a connection this server has just closed. Keep this above the idle timeout of anything in front of the server. headersTimeout is kept 5 seconds above it automatically."},benchmarking:{value:!1,type:"boolean",envLink:"SERVER_BENCHMARKING",cliName:"serverBenchmarking",description:"Indicates whether to display the duration, in milliseconds, of specific actions that occur on the server while serving a request."},proxy:{host:{value:!1,type:"string",envLink:"SERVER_PROXY_HOST",cliName:"proxyHost",description:"The host of the proxy server to use, if it exists."},port:{value:8080,type:"number",envLink:"SERVER_PROXY_PORT",cliName:"proxyPort",description:"The port of the proxy server to use, if it exists."},username:{value:!1,type:"string",envLink:"SERVER_PROXY_USERNAME",cliName:"proxyUsername",description:"The username for the proxy server, if it exists."},password:{value:!1,type:"string",envLink:"SERVER_PROXY_PASSWORD",cliName:"proxyPassword",description:"The password for the proxy server, if it exists."},timeout:{value:5e3,type:"number",envLink:"SERVER_PROXY_TIMEOUT",cliName:"proxyTimeout",description:"The timeout for the proxy server to use, if it exists."}},rateLimiting:{enable:{value:!1,type:"boolean",envLink:"SERVER_RATE_LIMITING_ENABLE",cliName:"enableRateLimiting",description:"Enables rate limiting for the server."},maxRequests:{value:10,type:"number",envLink:"SERVER_RATE_LIMITING_MAX_REQUESTS",legacyName:"rateLimit",description:"The maximum number of requests allowed in one minute."},window:{value:1,type:"number",envLink:"SERVER_RATE_LIMITING_WINDOW",description:"The time window, in minutes, for the rate limiting."},trustProxy:{value:!1,type:"boolean",envLink:"SERVER_RATE_LIMITING_TRUST_PROXY",description:"Set this to true if the server is behind a load balancer."},skipKey:{value:!1,type:"string",envLink:"SERVER_RATE_LIMITING_SKIP_KEY",description:"Allows bypassing the rate limiter and should be provided with the skipToken argument."},skipToken:{value:!1,type:"string",envLink:"SERVER_RATE_LIMITING_SKIP_TOKEN",description:"Allows bypassing the rate limiter and should be provided with the skipKey argument."}},ssl:{enable:{value:!1,type:"boolean",envLink:"SERVER_SSL_ENABLE",cliName:"enableSsl",description:"Enables or disables the SSL protocol."},force:{value:!1,type:"boolean",envLink:"SERVER_SSL_FORCE",cliName:"sslForce",legacyName:"sslOnly",description:"When set to true, the server is forced to serve only over HTTPS."},port:{value:443,type:"number",envLink:"SERVER_SSL_PORT",cliName:"sslPort",description:"The port on which to run the SSL server."},certPath:{value:!1,type:"string",envLink:"SERVER_SSL_CERT_PATH",legacyName:"sslPath",description:"The path to the SSL certificate/key file."}}},pool:{minWorkers:{value:4,type:"number",envLink:"POOL_MIN_WORKERS",description:"The number of minimum and initial pool workers to spawn."},maxWorkers:{value:8,type:"number",envLink:"POOL_MAX_WORKERS",legacyName:"workers",description:"The number of maximum pool workers to spawn."},workLimit:{value:40,type:"number",envLink:"POOL_WORK_LIMIT",description:"The number of work pieces that can be performed before restarting the worker process."},queueLimit:{value:0,type:"number",envLink:"POOL_QUEUE_LIMIT",description:"The maximum number of exports allowed to wait for a worker. Requests arriving beyond this are refused instead of being queued. Set to 0 to derive it as four times maxWorkers. Throughput does not improve past the pool size, so a deeper queue only adds latency and memory use."},queueRejectDelay:{value:500,type:"number",envLink:"POOL_QUEUE_REJECT_DELAY",description:"The duration, in milliseconds, to wait before answering a request refused because the queue is full. This is deliberate backpressure: answering instantly lets clients that retry immediately consume the whole event loop refusing them, starving the exports already in progress. Set to 0 to answer immediately, only advisable when something upstream is limiting the request rate."},acquireTimeout:{value:5e3,type:"number",envLink:"POOL_ACQUIRE_TIMEOUT",description:"The duration, in milliseconds, to wait for acquiring a resource."},createTimeout:{value:5e3,type:"number",envLink:"POOL_CREATE_TIMEOUT",description:"The duration, in milliseconds, to wait for creating a resource."},destroyTimeout:{value:5e3,type:"number",envLink:"POOL_DESTROY_TIMEOUT",description:"The duration, in milliseconds, to wait for destroying a resource."},idleTimeout:{value:3e4,type:"number",envLink:"POOL_IDLE_TIMEOUT",description:"The duration, in milliseconds, after which an idle resource is destroyed."},createRetryInterval:{value:200,type:"number",envLink:"POOL_CREATE_RETRY_INTERVAL",description:"The duration, in milliseconds, to wait before retrying the create process in case of a failure."},reaperInterval:{value:1e3,type:"number",envLink:"POOL_REAPER_INTERVAL",description:"The duration, in milliseconds, after which the check for idle resources to destroy is triggered."},benchmarking:{value:!1,type:"boolean",envLink:"POOL_BENCHMARKING",cliName:"poolBenchmarking",description:"Indicate whether to show statistics for the pool of resources or not."}},logging:{level:{value:4,type:"number",envLink:"LOGGING_LEVEL",cliName:"logLevel",description:"The logging level to be used."},file:{value:"highcharts-export-server.log",type:"string",envLink:"LOGGING_FILE",cliName:"logFile",description:"The name of a log file. The `logToFile` and `logDest` options also need to be set to enable file logging."},dest:{value:"log/",type:"string",envLink:"LOGGING_DEST",cliName:"logDest",description:"The path to store log files. The `logToFile` option also needs to be set to enable file logging."},toConsole:{value:!0,type:"boolean",envLink:"LOGGING_TO_CONSOLE",cliName:"logToConsole",description:"Enables or disables showing logs in the console."},toFile:{value:!0,type:"boolean",envLink:"LOGGING_TO_FILE",cliName:"logToFile",description:"Enables or disables creation of the log directory and saving the log into a .log file."}},ui:{enable:{value:!1,type:"boolean",envLink:"UI_ENABLE",cliName:"enableUi",description:"Enables or disables the user interface (UI) for the export server."},route:{value:"/",type:"string",envLink:"UI_ROUTE",cliName:"uiRoute",description:"The endpoint route to which the user interface (UI) should be attached."}},other:{nodeEnv:{value:"production",type:"string",envLink:"OTHER_NODE_ENV",description:"The type of Node.js environment."},listenToProcessExits:{value:!0,type:"boolean",envLink:"OTHER_LISTEN_TO_PROCESS_EXITS",description:"Decides whether or not to attach process.exit handlers."},noLogo:{value:!1,type:"boolean",envLink:"OTHER_NO_LOGO",description:"Skip printing the logo on a startup. Will be replaced by a simple text."},hardResetPage:{value:!1,type:"boolean",envLink:"OTHER_HARD_RESET_PAGE",description:"Decides if the page content should be reset entirely."},shutdownDrainTimeout:{value:3e4,type:"number",envLink:"OTHER_SHUTDOWN_DRAIN_TIMEOUT",description:"How long, in milliseconds, to let requests already being served finish during a shutdown, before the worker pool is taken away and the process exits. Set it above the deregistration delay of anything routing traffic to this server, so that it has stopped sending before the server stops answering."},browserShellMode:{value:!0,type:"boolean",envLink:"OTHER_BROWSER_SHELL_MODE",description:"Decides if the browser runs in the shell mode."}},debug:{enable:{value:!1,type:"boolean",envLink:"DEBUG_ENABLE",cliName:"enableDebug",description:"Enables or disables debug mode for the underlying browser."},headless:{value:!0,type:"boolean",envLink:"DEBUG_HEADLESS",description:"Controls the mode in which the browser is launched when in the debug mode."},devtools:{value:!1,type:"boolean",envLink:"DEBUG_DEVTOOLS",description:"Decides whether to enable DevTools when the browser is in a headful state."},listenToConsole:{value:!1,type:"boolean",envLink:"DEBUG_LISTEN_TO_CONSOLE",description:"Decides whether to enable a listener for console messages sent from the browser."},dumpio:{value:!1,type:"boolean",envLink:"DEBUG_DUMPIO",description:"Redirects browser process stdout and stderr to process.stdout and process.stderr."},slowMo:{value:0,type:"number",envLink:"DEBUG_SLOW_MO",description:"Slows down Puppeteer operations by the specified number of milliseconds."},debuggingPort:{value:9222,type:"number",envLink:"DEBUG_DEBUGGING_PORT",description:"Specifies the debugging port."}}},C={puppeteer:[{type:"list",name:"args",message:"Puppeteer arguments",initial:I.puppeteer.args.value.join(","),separator:","}],highcharts:[{type:"text",name:"version",message:"Highcharts version",initial:I.highcharts.version.value},{type:"text",name:"cdnURL",message:"The URL of CDN",initial:I.highcharts.cdnURL.value},{type:"toggle",name:"useNpm",message:"Flag to use Highcharts scripts from NPM package",initial:I.highcharts.useNpm.value},{type:"multiselect",name:"coreScripts",message:"Available core scripts",instructions:"Space: Select specific, A: Select all, Enter: Confirm.",choices:I.highcharts.coreScripts.value},{type:"multiselect",name:"moduleScripts",message:"Available module scripts",instructions:"Space: Select specific, A: Select all, Enter: Confirm.",choices:I.highcharts.moduleScripts.value},{type:"multiselect",name:"indicatorScripts",message:"Available indicator scripts",instructions:"Space: Select specific, A: Select all, Enter: Confirm.",choices:I.highcharts.indicatorScripts.value},{type:"list",name:"customScripts",message:"Custom scripts",initial:I.highcharts.customScripts.value.join(","),separator:","},{type:"toggle",name:"forceFetch",message:"Force re-fetch the scripts",initial:I.highcharts.forceFetch.value},{type:"text",name:"cachePath",message:"The path to the cache directory",initial:I.highcharts.cachePath.value}],export:[{type:"select",name:"type",message:"The default export file type",hint:`Default: ${I.export.type.value}`,initial:0,choices:["png","jpeg","pdf","svg"]},{type:"select",name:"constr",message:"The default constructor for Highcharts",hint:`Default: ${I.export.constr.value}`,initial:0,choices:["chart","stockChart","mapChart","ganttChart"]},{type:"number",name:"defaultHeight",message:"The default fallback height of the exported chart",initial:I.export.defaultHeight.value},{type:"number",name:"defaultWidth",message:"The default fallback width of the exported chart",initial:I.export.defaultWidth.value},{type:"number",name:"defaultScale",message:"The default fallback scale of the exported chart",initial:I.export.defaultScale.value,min:.1,max:5},{type:"number",name:"rasterizationTimeout",message:"The rendering webpage timeout in milliseconds",initial:I.export.rasterizationTimeout.value}],customLogic:[{type:"toggle",name:"allowCodeExecution",message:"Enable execution of custom code",initial:I.customLogic.allowCodeExecution.value},{type:"toggle",name:"allowFileResources",message:"Enable file resources",initial:I.customLogic.allowFileResources.value}],server:[{type:"toggle",name:"enable",message:"Starts the server on 0.0.0.0",initial:I.server.enable.value},{type:"text",name:"host",message:"Server hostname",initial:I.server.host.value},{type:"number",name:"port",message:"Server port",initial:I.server.port.value},{type:"toggle",name:"benchmarking",message:"Enable server benchmarking",initial:I.server.benchmarking.value},{type:"text",name:"proxy.host",message:"The host of the proxy server to use",initial:I.server.proxy.host.value},{type:"number",name:"proxy.port",message:"The port of the proxy server to use",initial:I.server.proxy.port.value},{type:"number",name:"proxy.timeout",message:"The timeout for the proxy server to use",initial:I.server.proxy.timeout.value},{type:"toggle",name:"rateLimiting.enable",message:"Enable rate limiting",initial:I.server.rateLimiting.enable.value},{type:"number",name:"rateLimiting.maxRequests",message:"The maximum requests allowed per minute",initial:I.server.rateLimiting.maxRequests.value},{type:"number",name:"rateLimiting.window",message:"The rate-limiting time window in minutes",initial:I.server.rateLimiting.window.value},{type:"toggle",name:"rateLimiting.trustProxy",message:"Set to true if behind a load balancer",initial:I.server.rateLimiting.trustProxy.value},{type:"text",name:"rateLimiting.skipKey",message:"Allows bypassing the rate limiter when provided with the skipToken argument",initial:I.server.rateLimiting.skipKey.value},{type:"text",name:"rateLimiting.skipToken",message:"Allows bypassing the rate limiter when provided with the skipKey argument",initial:I.server.rateLimiting.skipToken.value},{type:"toggle",name:"ssl.enable",message:"Enable SSL protocol",initial:I.server.ssl.enable.value},{type:"toggle",name:"ssl.force",message:"Force serving only over HTTPS",initial:I.server.ssl.force.value},{type:"number",name:"ssl.port",message:"SSL server port",initial:I.server.ssl.port.value},{type:"text",name:"ssl.certPath",message:"The path to find the SSL certificate/key",initial:I.server.ssl.certPath.value}],pool:[{type:"number",name:"minWorkers",message:"The initial number of workers to spawn",initial:I.pool.minWorkers.value},{type:"number",name:"maxWorkers",message:"The maximum number of workers to spawn",initial:I.pool.maxWorkers.value},{type:"number",name:"workLimit",message:"The pieces of work that can be performed before restarting a Puppeteer process",initial:I.pool.workLimit.value},{type:"number",name:"queueLimit",message:"The maximum number of exports allowed to wait for a worker, or 0 to derive it from maxWorkers",initial:I.pool.queueLimit.value},{type:"number",name:"queueRejectDelay",message:"The number of milliseconds to wait before refusing a request because the queue is full",initial:I.pool.queueRejectDelay.value},{type:"number",name:"acquireTimeout",message:"The number of milliseconds to wait for acquiring a resource",initial:I.pool.acquireTimeout.value},{type:"number",name:"createTimeout",message:"The number of milliseconds to wait for creating a resource",initial:I.pool.createTimeout.value},{type:"number",name:"destroyTimeout",message:"The number of milliseconds to wait for destroying a resource",initial:I.pool.destroyTimeout.value},{type:"number",name:"idleTimeout",message:"The number of milliseconds after an idle resource is destroyed",initial:I.pool.idleTimeout.value},{type:"number",name:"createRetryInterval",message:"The retry interval in milliseconds after a create process fails",initial:I.pool.createRetryInterval.value},{type:"number",name:"reaperInterval",message:"The reaper interval in milliseconds after triggering the check for idle resources to destroy",initial:I.pool.reaperInterval.value},{type:"toggle",name:"benchmarking",message:"Enable benchmarking for a resource pool",initial:I.pool.benchmarking.value}],logging:[{type:"number",name:"level",message:"The log level (0: silent, 1: error, 2: warning, 3: notice, 4: verbose, 5: benchmark)",initial:I.logging.level.value,round:0,min:0,max:5},{type:"text",name:"file",message:"A log file name. Set with --toFile and --logDest to enable file logging",initial:I.logging.file.value},{type:"text",name:"dest",message:"The path to a log file when the file logging is enabled",initial:I.logging.dest.value},{type:"toggle",name:"toConsole",message:"Enable logging to the console",initial:I.logging.toConsole.value},{type:"toggle",name:"toFile",message:"Enables logging to a file",initial:I.logging.toFile.value}],ui:[{type:"toggle",name:"enable",message:"Enable UI for the export server",initial:I.ui.enable.value},{type:"text",name:"route",message:"A route to attach the UI",initial:I.ui.route.value}],other:[{type:"text",name:"nodeEnv",message:"The type of Node.js environment",initial:I.other.nodeEnv.value},{type:"toggle",name:"listenToProcessExits",message:"Set to false to skip attaching process.exit handlers",initial:I.other.listenToProcessExits.value},{type:"toggle",name:"noLogo",message:"Skip printing the logo on startup. Replaced by simple text",initial:I.other.noLogo.value},{type:"toggle",name:"hardResetPage",message:"Decides if the page content should be reset entirely",initial:I.other.hardResetPage.value},{type:"toggle",name:"browserShellMode",message:"Decides if the browser runs in the shell mode",initial:I.other.browserShellMode.value}],debug:[{type:"toggle",name:"enable",message:"Enables debug mode for the browser instance",initial:I.debug.enable.value},{type:"toggle",name:"headless",message:"The mode setting for the browser",initial:I.debug.headless.value},{type:"toggle",name:"devtools",message:"The DevTools for the headful browser",initial:I.debug.devtools.value},{type:"toggle",name:"listenToConsole",message:"The event listener for console messages from the browser",initial:I.debug.listenToConsole.value},{type:"toggle",name:"dumpio",message:"Redirects the browser stdout and stderr to NodeJS process",initial:I.debug.dumpio.value},{type:"number",name:"slowMo",message:"Puppeteer operations slow down in milliseconds",initial:I.debug.slowMo.value},{type:"number",name:"debuggingPort",message:"The port number for debugging",initial:I.debug.debuggingPort.value}]},N=["options","globalOptions","themeOptions","resources","payload"],P={},A=(e,t="")=>{Object.keys(e).forEach((r=>{if(!["puppeteer","highcharts"].includes(r)){const o=e[r];void 0===o.value?A(o,`${t}.${r}`):(P[o.cliName||r]=`${t}.${r}`.substring(1),void 0!==o.legacyName&&(P[o.legacyName]=`${t}.${r}`.substring(1)))}}))};A(I),m.config({quiet:!0});const H=e=>g.string().transform((t=>t.split(",").map((e=>e.trim())).filter((t=>e.includes(t))))).transform((e=>e.length?e:void 0)),$=()=>g.enum(["true","false",""]).transform((e=>""!==e?"true"===e:void 0)),D=e=>g.enum([...e,""]).transform((e=>""!==e?e:void 0)),U=()=>g.string().trim().refine((e=>!["false","undefined","null","NaN"].includes(e)||""===e),{error:e=>`The string contains forbidden values, received '${e.input}'`}).transform((e=>""!==e?e:void 0)),G=()=>g.string().trim().refine((e=>/^(\.\/|\.\.\/|\/|[a-zA-Z]:\\|[a-zA-Z]:\/)?((?:[\w-]+)[\\/]?)+$/.test(e)),{error:"The string is an invalid path directory string."}),M=()=>g.string().trim().refine((e=>""===e||!isNaN(parseFloat(e))&&parseFloat(e)>0),{error:e=>`The value must be numeric and positive, received '${e.input}'`}).transform((e=>""!==e?parseFloat(e):void 0)),j=()=>g.string().trim().refine((e=>""===e||!isNaN(parseFloat(e))&&parseFloat(e)>=0),{error:e=>`The value must be numeric and non-negative, received '${e.input}'`}).transform((e=>""!==e?parseFloat(e):void 0)),F=g.object({PUPPETEER_TEMP_DIR:G(),PUPPETEER_LAUNCH_RETRY_WINDOW:j(),HIGHCHARTS_VERSION:g.string().trim().refine((e=>/^(latest|\d+(\.\d+){0,2})$/.test(e)||""===e),{error:e=>`HIGHCHARTS_VERSION must be 'latest', a major version, or in the form XX.YY.ZZ, received '${e.input}'`}).transform((e=>""!==e?e:void 0)),HIGHCHARTS_CDN_URL:g.string().trim().refine((e=>e.startsWith("https://")||e.startsWith("http://")||""===e),{error:e=>`Invalid value for HIGHCHARTS_CDN_URL. It should start with http:// or https://, received '${e.input}'`}).transform((e=>""!==e?e:void 0)),HIGHCHARTS_USE_NPM:$(),HIGHCHARTS_CORE_SCRIPTS:H(k.core),HIGHCHARTS_MODULE_SCRIPTS:H(k.modules),HIGHCHARTS_INDICATOR_SCRIPTS:H(k.indicators),HIGHCHARTS_FORCE_FETCH:$(),HIGHCHARTS_CACHE_PATH:U(),HIGHCHARTS_ADMIN_TOKEN:U(),EXPORT_TYPE:D(["jpeg","png","pdf","svg"]),EXPORT_CONSTR:D(["chart","stockChart","mapChart","ganttChart"]),EXPORT_DEFAULT_HEIGHT:M(),EXPORT_DEFAULT_WIDTH:M(),EXPORT_DEFAULT_SCALE:M(),EXPORT_RASTERIZATION_TIMEOUT:j(),CUSTOM_LOGIC_ALLOW_CODE_EXECUTION:$(),CUSTOM_LOGIC_ALLOW_FILE_RESOURCES:$(),SERVER_ENABLE:$(),SERVER_HOST:U(),SERVER_PORT:M(),SERVER_MAX_UPLOAD_SIZE:M(),SERVER_KEEP_ALIVE_TIMEOUT:j(),SERVER_BENCHMARKING:$(),SERVER_PROXY_HOST:U(),SERVER_PROXY_PORT:M(),SERVER_PROXY_USERNAME:U(),SERVER_PROXY_PASSWORD:U(),SERVER_PROXY_TIMEOUT:j(),SERVER_RATE_LIMITING_ENABLE:$(),SERVER_RATE_LIMITING_MAX_REQUESTS:j(),SERVER_RATE_LIMITING_WINDOW:j(),SERVER_RATE_LIMITING_TRUST_PROXY:$(),SERVER_RATE_LIMITING_SKIP_KEY:U(),SERVER_RATE_LIMITING_SKIP_TOKEN:U(),SERVER_SSL_ENABLE:$(),SERVER_SSL_FORCE:$(),SERVER_SSL_PORT:M(),SERVER_SSL_CERT_PATH:U(),POOL_MIN_WORKERS:j(),POOL_MAX_WORKERS:j(),POOL_WORK_LIMIT:M(),POOL_QUEUE_LIMIT:j(),POOL_QUEUE_REJECT_DELAY:j(),POOL_ACQUIRE_TIMEOUT:j(),POOL_CREATE_TIMEOUT:j(),POOL_DESTROY_TIMEOUT:j(),POOL_IDLE_TIMEOUT:j(),POOL_CREATE_RETRY_INTERVAL:j(),POOL_REAPER_INTERVAL:j(),POOL_BENCHMARKING:$(),LOGGING_LEVEL:g.string().trim().refine((e=>""===e||!isNaN(parseFloat(e))&&parseFloat(e)>=0&&parseFloat(e)<=5),{error:e=>`Invalid value for LOGGING_LEVEL. We only accept values from 0 to 5 as logging levels, received '${e.input}'`}).transform((e=>""!==e?parseFloat(e):void 0)),LOGGING_FILE:U(),LOGGING_DEST:U(),LOGGING_TO_CONSOLE:$(),LOGGING_TO_FILE:$(),UI_ENABLE:$(),UI_ROUTE:U(),OTHER_SHUTDOWN_DRAIN_TIMEOUT:j(),OTHER_NODE_ENV:D(["development","production","test"]),OTHER_LISTEN_TO_PROCESS_EXITS:$(),OTHER_NO_LOGO:$(),OTHER_HARD_RESET_PAGE:$(),OTHER_BROWSER_SHELL_MODE:$(),OTHER_ALLOW_XLINK:$(),DEBUG_ENABLE:$(),DEBUG_HEADLESS:$(),DEBUG_DEVTOOLS:$(),DEBUG_LISTEN_TO_CONSOLE:$(),DEBUG_DUMPIO:$(),DEBUG_SLOW_MO:j(),DEBUG_DEBUGGING_PORT:M()}).partial().parse(process.env),W=["red","yellow","blue","gray","green"];let q={toConsole:!0,toFile:!1,pathCreated:!1,levelsDesc:[{title:"error",color:W[0]},{title:"warning",color:W[1]},{title:"notice",color:W[2]},{title:"verbose",color:W[3]},{title:"benchmark",color:W[4]}],listeners:[]};const V=(o,i)=>{q.pathCreated||(!e(q.dest)&&t(q.dest),q.pathCreated=!0),r(`${q.dest}${q.file}`,[i].concat(o).join(" ")+"\n",(e=>{e&&(console.log(`[logger] Unable to write to log file: ${e}`),q.toFile=!1)}))},X=(...e)=>{const[t,...r]=e,{levelsDesc:o,level:i}=q;if(5!==t&&(0===t||t>i||i>o.length))return;const n=`${(new Date).toString().split("(")[0].trim()} [${o[t-1].title}] -`;q.listeners.forEach((e=>{e(n,r.join(" "))})),q.toConsole&&console.log.apply(void 0,[n.toString()[q.levelsDesc[t-1].color]].concat(r)),q.toFile&&V(r,n)},B=(e,t,r)=>{const o=r||t.message,{level:i,levelsDesc:n}=q;if(0===e||e>i||i>n.length)return;const s=`${(new Date).toString().split("(")[0].trim()} [${n[e-1].title}] -`,a=t.message!==t.stackMessage||void 0===t.stackMessage?t.stack:t.stack.split("\n").slice(1).join("\n"),l=[o,"\n",a];q.toConsole&&console.log.apply(void 0,[s.toString()[q.levelsDesc[e-1].color]].concat([o[W[e-1]],"\n",a])),q.listeners.forEach((e=>{e(s,l.join(" "))})),q.toFile&&V(l,s)},K=e=>{e>=0&&e<=q.levelsDesc.length&&(q.level=e)},z=(e,t)=>{if(q={...q,dest:e||q.dest,file:t||q.file,toFile:!0},0===q.dest.length)return X(1,"[logger] File logging initialization: no path supplied.");q.dest.endsWith("/")||(q.dest+="/")},J=a(f(import.meta.url).resolve("highcharts/package.json")),Y=v(new URL("../.",import.meta.url)),Q=(e,t)=>{const r=["png","jpeg","pdf","svg"];if(t){const o=t.split(".").pop();"jpg"===o?e="jpeg":r.includes(o)&&e!==o&&(e=o)}return{"image/png":"png","image/jpeg":"jpeg","application/pdf":"pdf","image/svg+xml":"svg"}[e]||r.find((t=>t===e))||"png"},Z=(e=!1,t)=>{const r=["js","css","files"];let i=e,n=!1;if(t&&e.endsWith(".json"))try{i=ee(o(e,"utf8"))}catch(e){return B(2,e,"[cli] No resources found.")}else i=ee(e),i&&!t&&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):X(3,"[cli] No resources found.")};function ee(e,t){try{const r=JSON.parse("string"!=typeof e?JSON.stringify(e):e);return"string"!=typeof r&&t?JSON.stringify(r):r}catch{return!1}}const te=e=>{if(null===e||"object"!=typeof e)return e;const t=Array.isArray(e)?[]:{};for(const r in e)Object.prototype.hasOwnProperty.call(e,r)&&(t[r]=te(e[r]));return t},re=(e,t)=>JSON.stringify(e,((e,r)=>("string"==typeof r&&((r=r.trim()).startsWith("function(")||r.startsWith("function ("))&&r.endsWith("}")&&(r=t?`EXP_FUN${(r+"").replaceAll(/\n|\t|\r/g," ")}EXP_FUN`:void 0),"function"==typeof r?`EXP_FUN${(r+"").replaceAll(/\n|\t|\r/g," ")}EXP_FUN`:r))).replaceAll(/"EXP_FUN|EXP_FUN"/g,"");function oe(){console.log("\nUsage of CLI arguments:".bold,"\n------",`\nFor more detailed information, visit the readme at: ${"https://github.com/highcharts/node-export-server#readme".bold.yellow}.`);const e=t=>{for(const[r,o]of Object.entries(t))if(Object.prototype.hasOwnProperty.call(o,"value")){let e=` --${o.cliName||r} ${("<"+o.type+">").green} `;if(e.length<48)for(let t=e.length;t<48;t++)e+=".";console.log(e,o.description,`[Default: ${o.value.toString().bold}]`.blue)}else e(o)};Object.keys(I).forEach((t=>{["puppeteer","highcharts"].includes(t)||(console.log(`\n${t.toUpperCase()}`.red),e(I[t]))})),console.log("\n")}const ie=e=>!["false","undefined","null","NaN","0",""].includes(e)&&!!e,ne=(e,t)=>{if(e&&"string"==typeof e)return(e=e.trim()).endsWith(".js")?!!t&&ne(o(e,"utf8")):e.startsWith("function()")||e.startsWith("function ()")||e.startsWith("()=>")||e.startsWith("() =>")?`(${e})()`:e.replace(/;$/,"")},se=()=>{const e=process.hrtime.bigint();return()=>Number(process.hrtime.bigint()-e)/1e6};let ae={};const le=()=>ae,ce=(e,t,r=[])=>{const o=te(e);for(const[e,n]of Object.entries(t))o[e]="object"!=typeof(i=n)||Array.isArray(i)||null===i||r.includes(e)||void 0===o[e]?void 0!==n?n:o[e]:ce(o[e],n,r);var i;return o};function pe(e,t={},r=""){Object.keys(e).forEach((o=>{const i=e[o],n=t&&t[o];void 0===i.value?pe(i,n,`${r}.${o}`):(void 0!==n&&(i.value=n),i.envLink in F&&void 0!==F[i.envLink]&&(i.value=F[i.envLink]))}))}function ue(e){let t={};for(const[r,o]of Object.entries(e))t[r]=Object.prototype.hasOwnProperty.call(o,"value")?o.value:ue(o);return t}function he(e,t,r){for(;t.length>1;){const o=t.shift();return Object.prototype.hasOwnProperty.call(e,o)||(e[o]={}),e[o]=he(Object.assign({},e[o]),t,r),e}return e[t[0]]=r,e}async function de(e,t={}){return new Promise(((r,o)=>{const i=(e=>e.startsWith("https")?b:y)(e);i.get(e,Object.assign({headers:{"User-Agent":"highcharts/export",Referer:"highcharts.export"}},t||{}),(e=>{let t="";e.on("data",(e=>{t+=e})),e.on("end",(()=>{t||o("Nothing was fetched from the URL."),e.text=t,r(e)}))})).on("error",(e=>{o(e)}))}))}class me extends Error{constructor(e){super(),this.message=e,this.stackMessage=e}setError(e){return this.error=e,e.name&&(this.name=e.name),e.statusCode&&(this.statusCode=e.statusCode),e.errorCode&&!this.errorCode&&(this.errorCode=e.errorCode),e.stack&&(this.stackMessage=e.message,this.stack=e.stack),this}setCode(e){return this.errorCode=e,this}}const ge={cdnURL:"https://code.highcharts.com/",activeManifest:{},sources:"",hcVersion:""},fe=e=>e.sources.substring(0,e.sources.indexOf("*/")).replace("/*","").replace("*/","").replace(/\n/g,"").trim(),ve=e=>e.replace(/\\/g,"/").split("/").pop().replace(/\.js$/i,""),ye=async(e,t,r,i=!1,n=!1)=>{let s;if(e.endsWith(".js")||(e=`${e}.js`),i)try{X(4,`[cache] Fetching script from NPM - ${l("node_modules","highcharts",e)}`);const t=c(J,e);if(!t.startsWith(c(J)+p))throw new me(`[cache] Invalid script path detected for '${e}'. Directory traversal attempt or bad version input.`,403);return s=o(t,"utf8"),r&&s&&(r[ve(e)]=1),s}catch{}else if(X(4,`[cache] Fetching script from CDN - ${e}`),s=await de(e,t),200===s.statusCode&&"string"==typeof s.text)return r&&(r[ve(e)]=1),s.text;if(n)throw new me(`[cache] Could not fetch the mandatory ${e}. The script might not exist in the requested version.`,404);return X(2,`[cache] Could not fetch the ${e}. The script might not exist in the requested version.`),""},be=async(e,t,r)=>{try{const o={};return ge.sources=await(async(e,t,r)=>{const o=e.version,i="latest"!==o&&o?`${o}/`:"",n=e.cdnURL||ge.cdnURL;X(3,`[cache] Updating cache version to Highcharts: ${i||"latest"}.`);const s=e.useNpm;let a;const{host:c,port:p,username:u,password:d}=t;if(c&&p)try{a=new h({host:c,port:p,...u&&d?{username:u,password:d}:{}})}catch(e){throw new me("[cache] Could not create a Proxy Agent.").setError(e)}const m=a?{agent:a,timeout:F.SERVER_PROXY_TIMEOUT}:{};return(await Promise.all([...e.coreScripts.map((e=>ye(s&&e||`${n}${i}${e}`,m,r,s,!0))),...e.moduleScripts.map((e=>ye(s&&l("modules",e)||("map"===e?`${n}maps/${i}modules/${e}`:`${n}${i}modules/${e}`),m,r,s))),...e.indicatorScripts.map((e=>ye(s&&l("indicators",e)||`${n}stock/${i}indicators/${e}`,m,r,s))),...e.customScripts.map((e=>ye(`${e}`,m)))])).join(";\n")})(e,t,o),ge.hcVersion=fe(ge),n(r,ge.sources),o}catch(e){throw new me("[cache] Unable to update the local Highcharts cache.").setError(e)}},we=async r=>{const{highcharts:i,server:s}=r,a=l(Y,i.cachePath);let c;const p=l(a,"manifest.json"),u=l(a,"sources.js");if(!e(a)&&t(a),!e(p)||i.forceFetch)X(3,"[cache] Fetching and caching Highcharts dependencies."),c=await be(i,s.proxy,u);else{let e=!1;const t=JSON.parse(o(p));if(t.modules&&Array.isArray(t.modules)){const e={};t.modules.forEach((t=>e[t]=1)),t.modules=e}const{coreScripts:r,moduleScripts:n,indicatorScripts:a}=i,l=r.length+n.length+a.length;t.version!==i.version?(X(2,"[cache] A Highcharts version mismatch in the cache, need to re-fetch."),e=!0):Object.keys(t.modules||{}).length!==l?(X(2,"[cache] The cache and the requested modules do not match, need to re-fetch."),e=!0):e=(n||[]).some((e=>{if(!t.modules[e])return X(2,`[cache] The ${e} is missing in the cache, need to re-fetch.`),!0})),e?c=await be(i,s.proxy,u):(X(3,"[cache] Dependency cache is up to date, proceeding."),ge.sources=o(u,"utf8"),c=t.modules,ge.hcVersion=fe(ge))}await(async(e,t)=>{const r={version:e.version,modules:t||{}};ge.activeManifest=r,X(3,"[cache] Writing a new manifest.");try{n(l(Y,e.cachePath,"manifest.json"),JSON.stringify(r),"utf8")}catch(e){throw new me("[cache] Error writing the cache manifest.").setError(e)}})(i,c)},Ee=()=>l(Y,le().highcharts.cachePath),Te=()=>ge.hcVersion;function Se(){Highcharts.animObject=function(){return{duration:0}}}async function Re(e,t,r){window._displayErrors=r;const{getOptions:o,merge:i,setOptions:n,wrap:s}=Highcharts;Highcharts.setOptionsObj=i(!1,{},o());const a={animation:!1};t.export.strInj&&(a.height=e.chart.height,a.width=e.chart.width),window.isRenderComplete=!1,s(Highcharts.Chart.prototype,"init",(function(e,t,r){((t=i(t,{exporting:{enabled:!1},plotOptions:{series:{label:{enabled:!1}}},tooltip:{}})).series||[]).forEach((function(e){e.animation=!1})),window.onHighchartsRender||(window.onHighchartsRender=Highcharts.addEvent(this,"render",(()=>{window.isRenderComplete=!0}))),e.apply(this,[t,r])})),s(Highcharts.Series.prototype,"init",(function(e,t,r){e.apply(this,[t,r])}));const l=t.export.strInj?new Function(`return ${t.export.strInj}`)():e;t.customLogic.customCode&&new Function("options",t.customLogic.customCode)(l);const c=i(!1,JSON.parse(t.export.themeOptions),l,{chart:a}),p=t.customLogic.callback?new Function(`return ${t.customLogic.callback}`)():void 0,u=JSON.parse(t.export.globalOptions);u&&n(u);let h=t.export.constr||"chart";h=void 0!==Highcharts[h]?h:"chart",Highcharts[h]("container",c,p);const d=o();for(const e in d)"function"!=typeof d[e]&&delete d[e];n(Highcharts.setOptionsObj),Highcharts.setOptionsObj={}}const _e=o(Y+"/templates/template.html","utf8");let xe,Oe=0,Le=[],ke=!1,Ie=null;function Ce(){return Oe}function Ne(){ke||(Oe++,xe=void 0,X(1,`[browser] The browser disconnected unexpectedly. Invalidating all workers and relaunching on next use (generation ${Oe}).`))}function Pe(e){if(!e?.pid)return!1;try{return process.kill(e.pid,0),!0}catch(e){return"EPERM"===e.code}}async function Ae(e){const t=e.process();try{await e.close()}catch(e){B(2,e,"[browser] Could not cleanly close the browser.")}if(t){if(!await function(e,t){return new Promise((r=>{if(!Pe(e))return r(!0);const o=()=>{clearTimeout(i),r(!0)},i=setTimeout((()=>{e.removeListener("exit",o),r(!1)}),t);e.once("exit",o)}))}(t,2e3)){X(2,"[browser] The browser process is still running after being closed, killing it.");try{t.kill("SIGKILL")}catch(e){B(2,e,"[browser] Could not kill the browser process.")}Pe(t)&&X(1,`[browser] The browser process ${t.pid} has still not exited after being killed.`)}}else X(2,"[browser] No process handle for the browser, so its exit cannot be confirmed.")}async function He(e){return void 0!==e&&(Le=e),ke=!1,xe?.connected?xe:(Ie||(Ie=async function(e){const{puppeteer:t,debug:r,other:o}=le(),{enable:i,...n}=r,s={headless:!o.browserShellMode||"shell",userDataDir:t.tempDir||"./tmp/",args:e,handleSIGINT:!1,handleSIGTERM:!1,handleSIGHUP:!1,waitForInitialPage:!1,defaultViewport:null,...i&&n},a=function(){const e=parseInt(le().puppeteer?.launchRetryWindow);return isNaN(e)||e<=0?3e4:e}(),l=Date.now()+a;let c=0,p=250;for(;;){c++;try{X(3,`[browser] Attempting to get a browser instance (attempt ${c}).`),xe=await T.launch(s);break}catch(e){const t=l-Date.now();if(t<=0)throw new me(`[browser] Could not launch a browser within ${a}ms, after ${c} attempts.`).setError(e);B(2,e,`[browser] Failed to launch a browser instance, retrying (attempt ${c}, ${Math.ceil(t/1e3)}s of the retry window left).`),await new Promise((e=>setTimeout(e,Math.min(p+Math.round(Math.random()*p),t)))),p=Math.min(2*p,4e3)}}"shell"===s.headless&&X(3,"[browser] Launched browser in shell mode.");i&&X(3,"[browser] Launched browser in debug mode.");if(!xe)throw new me("[browser] Cannot find a browser to open.");return xe.once("disconnected",Ne),xe}(Le).finally((()=>{Ie=null}))),Ie)}async function $e(){if(xe?.connected||ke||(X(3,"[browser] No browser available, attempting to relaunch it."),await He()),!xe)return!1;let e;try{return e=await xe.newPage(),await e.setCacheEnabled(!1),await Ue(e),function(e){const{debug:t}=le();t.enable&&t.listenToConsole&&e.on("console",(e=>{console.log(`[debug] ${e.text()}`)}));e.on("pageerror",(async t=>{e.isClosed()||await e.$eval("#container",((e,t)=>{window._displayErrors&&(e.innerHTML=t)}),`

Chart input data error:

${t.toString()}`)}))}(e),e}catch(t){if(e&&!e.isClosed())try{await e.close()}catch(e){B(2,e,"[browser] Could not close a page that failed to be set up.")}throw t}}async function De(e,t){try{for(const e of t)await e.dispose();await e.evaluate((()=>{if("undefined"!=typeof Highcharts){const e=Highcharts.charts;if(Array.isArray(e)&&e.length)for(const t of e)t&&t.destroy(),Highcharts.charts.shift()}const[...e]=document.getElementsByTagName("script"),[,...t]=document.getElementsByTagName("style"),[...r]=document.getElementsByTagName("link");for(const o of[...e,...t,...r])o.remove()}))}catch(e){B(2,e,"[browser] Could not clear page's resources.")}}async function Ue(e){await e.setContent(_e,{waitUntil:"domcontentloaded"}),await e.addScriptTag({path:`${Ee()}/sources.js`}),await e.evaluate(Se)}const Ge=async(e,t,r,o)=>{r.export.instr=null,r.export.infile=null;const i=Buffer.byteLength(r.export?.strInj?r.export?.strInj:JSON.stringify(t),"utf-8");if(X(4,`[export] The current total size of data passed to a page is around ${(i/1048576).toFixed(2)} MB`),i>=104857600)throw new me("[export] The data passed to a page exceeded 100MB.");return e.evaluate(Re,t,r,o)};var Me=async(e,t,r)=>{let i=[];try{X(4,"[export] Determining export path.");const n=r.export,a=n?.options?.chart?.displayErrors&&ge.activeManifest.modules.debugger;let l;if(t.indexOf&&(t.indexOf("=0||t.indexOf("=0)){if(X(4,"[export] Treating as SVG."),"svg"===n.type)return t;l=!0,await e.setContent((e=>`\n\n\n \n \n Highcharts Export\n \n \n \n
\n ${e}\n
\n \n\n\n`)(t),{waitUntil:"domcontentloaded"})}else X(4,"[export] Treating as config."),n.strInj?await Ge(e,{chart:{height:n.height,width:n.width}},r,a):(t.chart.height=n.height,t.chart.width=n.width,await Ge(e,t,r,a));i=await async function(e,t){const r=[],i=t.customLogic.resources;if(i){const n=[];if(i.js&&n.push({content:i.js}),i.files)for(const e of i.files){const t=!e.startsWith("http");n.push(t?{content:o(e,"utf8")}:{url:e})}for(const t of n)try{r.push(await e.addScriptTag(t))}catch(e){B(2,e,"[export] The JS resource cannot be loaded.")}n.length=0;const a=[];if(i.css){let o=i.css.match(/@import\s*([^;]*);/g);if(o)for(let e of o)e&&(e=e.replace("url(","").replace("@import","").replace(/"/g,"").replace(/'/g,"").replace(/;/,"").replace(/\)/g,"").trim(),e.startsWith("http")?a.push({url:e}):t.customLogic.allowFileResources&&a.push({path:s.join(Y,e)}));a.push({content:i.css.replace(/@import\s*([^;]*);/g,"")||" "});for(const t of a)try{r.push(await e.addStyleTag(t))}catch(e){B(2,e,"[export] The CSS resource cannot be loaded.")}a.length=0}}return r}(e,r);const c=l?await e.evaluate((e=>{const t=document.querySelector("#chart-container svg:first-of-type"),r=t.height.baseVal.value*e,o=t.width.baseVal.value*e;return document.body.style.zoom=e,document.body.style.margin="0px",{chartHeight:r,chartWidth:o}}),parseFloat(n.scale)):await e.evaluate((()=>{const{chartHeight:e,chartWidth:t}=window.Highcharts.charts[0];return document.body.style.zoom=1,{chartHeight:e,chartWidth:t}})),p=Math.abs(Math.ceil(c.chartHeight||n.height)),u=Math.abs(Math.ceil(c.chartWidth||n.width)),{x:h,y:d}=await(e=>e.$eval("#chart-container",(e=>{const{x:t,y:r,width:o,height:i}=e.getBoundingClientRect();return{x:t,y:r,width:o,height:Math.trunc(i>1?i:500)}})))(e);let m;if(await e.setViewport({height:p,width:u,deviceScaleFactor:l?1:parseFloat(n.scale)}),"svg"===n.type)m=await(e=>e.$eval("#container svg:first-of-type",(e=>e.outerHTML)))(e);else if(["png","jpeg"].includes(n.type))m=await(async(e,t,r,o,i)=>{let n;try{return await Promise.race([e.screenshot({type:t,encoding:r,clip:o,captureBeyondViewport:!0,fullPage:!1,optimizeForSpeed:!0,..."png"!==t?{quality:80}:{},omitBackground:"png"==t}),new Promise(((e,t)=>{n=setTimeout((()=>t(new me("Rasterization timeout"))),i||1500)}))])}finally{clearTimeout(n)}})(e,n.type,"base64",{width:u,height:p,x:h,y:d},n.rasterizationTimeout);else{if("pdf"!==n.type)throw new me(`[export] Unsupported output format ${n.type}.`);m=await(async(e,t,r,o,i)=>(await e.emulateMediaType("screen"),e.pdf({height:t+1,width:r,encoding:o,timeout:i||1500})))(e,p,u,"base64",n.rasterizationTimeout)}return await De(e,i),m}catch(t){return await De(e,i),t}};const je="EXPORT_INVALID_REQUEST",Fe="EXPORT_QUEUE_FULL",We="EXPORT_ACQUIRE_TIMEOUT",qe="EXPORT_RASTERIZATION_TIMEOUT",Ve="EXPORT_CLIENT_GONE",Xe="EXPORT_FAILED";let Be=!1;const Ke={performedExports:0,exportAttempts:0,exportFromSvgAttempts:0,timeSpent:0,droppedExports:0,spentAverage:0,rejectedForCapacity:0,abandonedExports:0};let ze={},Je=0,Ye=0;const Qe=()=>Ye,Ze=()=>Je,et={create:async()=>{let e=!1;const t=E(),r=(new Date).getTime();try{if(e=await $e(),!e||e.isClosed())throw new me("The page is invalid or closed.");Ye=0,X(3,`[pool] Successfully created a worker ${t} - took ${(new Date).getTime()-r} ms.`)}catch(e){throw++Ye,new me("Error encountered when creating a new page.").setError(e)}return{id:t,page:e,generation:Ce(),workCount:Math.round(Math.random()*(ze.workLimit/2))}},validate:async e=>{if(!e.page||e.page?.isClosed())return!1;if(e.mustRecycle)return X(3,"[pool] Worker failed validation: its page was left unusable by a previous export."),!1;if(e.generation!==Ce())return X(3,`[pool] Worker failed validation: it belongs to a browser that is gone (worker generation ${e.generation}, current ${Ce()}).`),!1;if(e.cleanPromise){const t=await e.cleanPromise;if(e.cleanPromise=null,!t)return X(3,"[pool] Worker failed validation: the page could not be cleared after its previous export."),!1}return!(ze.workLimit&&++e.workCount>ze.workLimit)||(X(3,`[pool] Worker failed validation: exceeded work limit (limit is ${ze.workLimit}).`),!1)},destroy:async e=>{X(3,`[pool] Destroying pool entry ${e.id}.`),e.page&&!e.page.isClosed()&&await e.page.close()}},tt=async e=>{if(ze=e&&e.pool?{...e.pool}:{},Je=(e=>{const t=parseInt(e.queueLimit),r=parseInt(e.maxWorkers);return!isNaN(t)&&t>0?t:4*(isNaN(r)?8:r)})(ze),await He(e.puppeteerArgs),X(3,`[pool] Initializing pool with workers: min ${ze.minWorkers}, max ${ze.maxWorkers}.`),Be)return X(4,"[pool] Already initialized, please kill it before creating a new one.");parseInt(ze.minWorkers)>parseInt(ze.maxWorkers)&&(ze.minWorkers=ze.maxWorkers);try{Be=new w({...et,min:parseInt(ze.minWorkers),max:parseInt(ze.maxWorkers),acquireTimeoutMillis:ze.acquireTimeout,createTimeoutMillis:ze.createTimeout,destroyTimeoutMillis:ze.destroyTimeout,idleTimeoutMillis:ze.idleTimeout,createRetryIntervalMillis:ze.createRetryInterval,reapIntervalMillis:ze.reaperInterval,propagateCreateError:!1}),Be.on("release",(e=>{e.cleanPromise=async function(e,t=!1){try{if(e&&!e.isClosed())return t?(await e.goto("about:blank",{waitUntil:"domcontentloaded"}),await Ue(e)):await e.evaluate((()=>{document.body.innerHTML='
'})),!0}catch(e){B(2,e,"[browser] Could not clear the content of the page.")}return!1}(e.page,!1).catch((()=>!1)),X(4,`[pool] Releasing a worker with ID ${e.id}.`)})),Be.on("destroySuccess",((e,t)=>{X(4,`[pool] Destroyed a worker with ID ${t.id}.`),t.page=null}));const e=[];for(let t=0;t{Be.release(e)})),X(3,"[pool] The pool is ready"+(e.length?` with ${e.length} initial resources waiting.`:"."))}catch(e){throw new me("[pool] Could not create the pool of workers.").setError(e)}};async function rt(){if(X(3,"[pool] Killing pool with all workers and closing browser."),Be){for(const e of Be.used)Be.release(e.resource);Be.destroyed||(await Be.destroy(),X(4,"[browser] Destroyed the pool of resources."))}await async function(){ke=!0,xe&&await Ae(xe),xe=void 0,X(4,"[browser] Closed the browser.")}()}const ot=async(e,t)=>{let r;try{if(X(4,"[pool] Work received, starting to process."),++Ke.exportAttempts,ze.benchmarking&&st(),!Be)throw new me("Work received, but pool has not been started.");if(Be.numPendingAcquires()>=Je)throw++Ke.rejectedForCapacity,new me((t.payload?.requestId?`For request with ID ${t.payload?.requestId} - `:"")+`The server is at capacity: ${Be.numPendingAcquires()} exports are already waiting for a worker (limit is ${Je}). Please retry shortly.`).setCode(Fe);const o=t.payload?.abortSignal;if(o?.aborted)throw++Ke.abandonedExports,new me((t.payload?.requestId?`For request with ID ${t.payload?.requestId} - `:"")+"The client disconnected before a worker was available.").setCode(Ve);const i=se();try{X(4,"[pool] Acquiring a worker handle."),r=await Be.acquire().promise,t.server.benchmarking&&X(5,t.payload?.requestId?`[benchmark] Request with ID ${t.payload?.requestId} -`:"[benchmark]",`Acquired a worker handle: ${i()}ms.`)}catch(e){throw new me((t.payload?.requestId?`For request with ID ${t.payload?.requestId} - `:"")+`Error encountered when acquiring an available entry: ${i()}ms.`).setCode(We).setError(e)}if(X(4,"[pool] Acquired a worker handle."),o?.aborted)throw++Ke.abandonedExports,Be.release(r),r=null,new me((t.payload?.requestId?`For request with ID ${t.payload?.requestId} - `:"")+"The client disconnected while waiting for a worker.").setCode(Ve);if(!r.page)throw new me("Resolved worker page is invalid: the pool setup is wonky.");let n=(new Date).getTime();X(4,`[pool] Starting work on pool entry with ID ${r.id}.`);const s=se(),a=await Me(r.page,e,t);if(a instanceof Error)throw"Rasterization timeout"===a.message&&(r.workCount=ze.workLimit+1,r.mustRecycle=!0),"TimeoutError"===a.name||"Rasterization timeout"===a.message?new me("Rasterization timeout: your chart may be too complex or large, and failed to render within the allotted time.").setCode(qe).setError(a):new me((t.payload?.requestId?`For request with ID ${t.payload?.requestId} - `:"")+`Error encountered during export: ${s()}ms.`).setCode(Xe).setError(a);t.server.benchmarking&&X(5,t.payload?.requestId?`[benchmark] Request with ID ${t.payload?.requestId} -`:"[benchmark]",`Exported a chart sucessfully: ${s()}ms.`),Be.release(r);const l=(new Date).getTime()-n;return Ke.timeSpent+=l,Ke.spentAverage=Ke.timeSpent/++Ke.performedExports,X(4,`[pool] Work completed in ${l} ms.`),{result:a,options:t}}catch(e){throw e.errorCode!==Ve&&++Ke.droppedExports,r&&Be.release(r),new me(`[pool] In pool.postWork: ${e.message}`).setError(e)}},it=()=>Be,nt=()=>({min:Be.min,max:Be.max,all:Be.numFree()+Be.numUsed(),available:Be.numFree(),used:Be.numUsed(),pending:Be.numPendingAcquires(),queueLimit:Je});function st(){const{min:e,max:t,all:r,available:o,used:i,pending:n}=nt();X(5,`[pool] The minimum number of resources allowed by pool: ${e}.`),X(5,`[pool] The maximum number of resources allowed by pool: ${t}.`),X(5,`[pool] The number of all created resources: ${r}.`),X(5,`[pool] The number of available resources: ${o}.`),X(5,`[pool] The number of acquired resources: ${i}.`),X(5,`[pool] The number of resources waiting to be acquired: ${n}.`)}var at=nt,lt=()=>Ke;let ct;function pt(e){const t=[];return F.OTHER_ALLOW_XLINK||t.push("xlink:href"),(ct||(ct=R(new S("").window)),ct).sanitize(e,{ADD_TAGS:["foreignObject"],FORBID_ATTR:t,HTML_INTEGRATION_POINTS:{foreignobject:!0}})}let ut=!1;const ht=async(e,t)=>{X(4,"[chart] Starting the exporting process.");const r=((e,t={})=>{let r={};return e.svg?(r=te(t),r.export.type=e.type||e.export.type,r.export.scale=e.scale||e.export.scale,r.export.outfile=e.outfile||e.export.outfile,r.payload={svg:e.svg}):r=ce(t,e,N),r.export.outfile=r.export?.outfile||`chart.${r.export?.type||"png"}`,r})(e,le()),i=r.export;if(r.payload?.svg&&""!==r.payload.svg)try{X(4,"[chart] Attempting to export from a SVG input.");const e=ft(pt(r.payload.svg),r,t);return++Ke.exportFromSvgAttempts,e}catch(e){return t(new me("[chart] Error loading SVG input.").setError(e))}if(i.infile&&i.infile.length)try{return X(4,"[chart] Attempting to export from an input file."),r.export.instr=o(i.infile,"utf8"),ft(r.export.instr.trim(),r,t)}catch(e){return t(new me("[chart] Error loading input file.").setError(e))}if(i.instr&&""!==i.instr||i.options&&""!==i.options)try{return X(4,"[chart] Attempting to export from a raw input."),i.instr=i.instr||i.options,ie(r.customLogic?.allowCodeExecution)?gt(r,t):"string"==typeof i.instr?ft(i.instr.trim(),r,t):mt(r,i.instr||i.options,t)}catch(e){return t(new me("[chart] Error loading raw input.").setError(e))}return t(new me("[chart] No valid input specified. Check if at least one of the following parameters is correctly set: 'infile', 'instr', 'options', or 'svg'."))},dt=e=>{const{chart:t,exporting:r}=e.export?.options||ee(e.export?.instr),o=ee(e.export?.globalOptions);let i=e.export?.scale||r?.scale||o?.exporting?.scale||e.export?.defaultScale||1;i=Math.max(.1,Math.min(i,5)),i=((e,t=1)=>{const r=Math.pow(10,t||0);return Math.round(+e*r)/r})(i,2);const n={height:e.export?.height||r?.sourceHeight||t?.height||o?.exporting?.sourceHeight||o?.chart?.height||e.export?.defaultHeight||400,width:e.export?.width||r?.sourceWidth||t?.width||o?.exporting?.sourceWidth||o?.chart?.width||e.export?.defaultWidth||600,scale:i};for(let[e,t]of Object.entries(n))n[e]="string"==typeof t?+t.replace(/px|%/gi,""):t;return n},mt=async(e,t,r,i)=>{let{export:n,customLogic:s}=e;const a="boolean"==typeof s.allowCodeExecution?s.allowCodeExecution:ut;if(s){if(a)if("string"==typeof e.customLogic.resources)e.customLogic.resources=Z(e.customLogic.resources,ie(e.customLogic.allowFileResources));else if(!e.customLogic.resources)try{const t=o("resources.json","utf8");e.customLogic.resources=Z(t,ie(e.customLogic.allowFileResources))}catch(e){X(2,"[chart] Unable to load the default resources.json file.")}}else s=e.customLogic={};if(!a&&s){if(s.callback||s.resources||s.customCode)return r(new me("[chart] The 'callback', 'resources' and 'customCode' options have been disabled for this server."));s.callback=!1,s.resources=!1,s.customCode=!1}if(t&&(t.chart=t.chart||{},t.exporting=t.exporting||{},t.exporting.enabled=!1),n.constr=n.constr||"chart",n.type=Q(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]=ee(o(n[e],"utf8"),!0):n[e]=ee(n[e],!0))}catch(t){n[e]={},B(2,t,`[chart] The '${e}' cannot be loaded.`)}})),s.allowCodeExecution)try{s.customCode=ne(s.customCode,s.allowFileResources)}catch(e){B(2,e,"[chart] The 'customCode' cannot be loaded.")}if(s&&s.callback&&s.callback?.indexOf("{")<0)if(s.allowFileResources)try{s.callback=o(s.callback,"utf8")}catch(e){s.callback=!1,B(2,e,"[chart] The 'callback' cannot be loaded.")}else s.callback=!1;e.export={...e.export,...dt(e)};try{return r(!1,await ot(n.strInj||t||i,e))}catch(e){return r(e)}},gt=(e,t)=>{try{let r,o=e.export.instr||e.export.options;return"string"!=typeof o&&(r=o=re(o,e.customLogic?.allowCodeExecution)),r=o.replaceAll(/\t|\n|\r/g,"").trim(),";"===r[r.length-1]&&(r=r.substring(0,r.length-1)),e.export.strInj=r,mt(e,!1,t)}catch(r){return t(new me(`[chart] Malformed input detected for ${e.export?.requestId||"?"}. Please make sure that your JSON/JavaScript options are sent using the "options" attribute, and that if you're using SVG, it is unescaped.`).setError(r))}},ft=(e,t,r)=>{const{allowCodeExecution:o}=t.customLogic;if(e.indexOf("=0||e.indexOf("=0)return X(4,"[chart] Parsing input as SVG."),mt(t,!1,r,e);try{const o=JSON.parse(e.replaceAll(/\t|\n|\r/g," "));return mt(t,o,r)}catch(e){return ie(o)?gt(t,r):r(new me("[chart] Only JSON configurations and SVG are allowed for this server. If this is your server, JavaScript custom code can be enabled by starting the server with the --allowCodeExecution flag.").setError(e))}},vt=[],yt=(e,t,r,o)=>{B(1,e),"development"!==F.OTHER_NODE_ENV&&delete e.stack,o(e)},bt=(e,t,r,o)=>{if(r.headersSent)return r.end();const{statusCode:i,status:n,message:s,stack:a,errorCode:l}=e;let c=i||n||400;(c>=500||c<100)&&(B(1,e,`[server] An error carried the out-of-contract status ${c}, answering with 400 instead. This server must never return a 5xx.`),c=400),r.status(c).json({statusCode:c,message:s,stack:a,...l?{errorCode:l}:{}})};var wt=(e,t)=>{const r="Too many requests, you have been rate limited. Please try again later.",o={max:t.maxRequests||30,window:t.window||1,trustProxy:t.trustProxy||!1,skipKey:t.skipKey||!1,skipToken:t.skipToken||!1};o.trustProxy&&e.enable("trust proxy");const i=L({windowMs:60*o.window*1e3,limit:o.max,handler:(e,t)=>{t.format({json:()=>{t.status(429).send({message:r})},default:()=>{t.status(429).send(r)}})},skip:e=>!1!==o.skipKey&&!1!==o.skipToken&&e.query.key===o.skipKey&&e.query.access_token===o.skipToken&&(X(4,"[rate limiting] Skipping rate limiter."),!0)});e.use(i),X(3,`[rate limiting] Enabled rate limiting with ${o.max} requests per ${o.window} minute for each IP, trusting proxy: ${o.trustProxy}.`)};class Et extends me{constructor(e,t,r=!1){super(e),this.status=this.statusCode=t,r&&(this.errorCode=r)}setStatus(e){return this.status=e,this}}var Tt=e=>!!e&&e.post("/version/change/:newVersion",(async(e,t,r)=>{try{const r=F.HIGHCHARTS_ADMIN_TOKEN;if(!r||!r.length)throw new Et("The server is not configured to perform run-time version changes: HIGHCHARTS_ADMIN_TOKEN is not set.",401);const o=e.get("hc-auth");if(!o||o!==r)throw new Et("Invalid or missing token: Set the token in the hc-auth header.",401);const i=e.params.newVersion;if(!i||!/^[a-zA-Z0-9.-]+$/.test(i))throw new Et("No new version supplied.",400);try{await(async e=>{const t=le();t?.highcharts&&(t.highcharts.version=e),await we(t)})(i)}catch(e){throw new Et(`Version change: ${e.message}`,e.statusCode).setError(e)}t.status(200).send({statusCode:200,version:Te(),message:`Successfully updated Highcharts to version: ${i}.`})}catch(e){r(e)}}));const St={png:"image/png",jpeg:"image/jpeg",gif:"image/gif",pdf:"application/pdf",svg:"image/svg+xml"};let Rt=0;const _t=[],xt=[],Ot=(e,t,r,o)=>{let i=!0;const{id:n,uniqueId:s,type:a,body:l}=o;return e.some((e=>{if(e){let o=e(t,r,n,s,a,l);return void 0!==o&&!0!==o&&(i=o),!0}})),i},Lt=async(e,t,r)=>{try{const r=se(),i=E().replace(/-/g,""),n=le(),s=e.body,a=++Rt;let l=Q(s.type);if(!s||"object"==typeof(o=s)&&!Array.isArray(o)&&null!==o&&0===Object.keys(o).length)throw new Et("The request body is required. Please ensure that your Content-Type header is correct (accepted types are application/json and multipart/form-data).",400,je);let c=ee(s.infile||s.options||s.data);if(!c&&!s.svg)throw X(2,`The request with ID ${i} from ${e.headers["x-forwarded-for"]||e.connection.remoteAddress} was incorrect:\n Content-Type: ${e.headers["content-type"]}. \n Chart constructor: ${s.constr}.\n Dimensions: ${s.width}x${s.height} @ ${s.scale} scale.\n Type: ${l}.\n Is SVG set? ${void 0!==s.svg}.\n B64? ${void 0!==s.b64}.\n No download? ${void 0!==s.noDownload}.\n\n Payload received: ${JSON.stringify(s.infile||s.options||s.data||s.svg)}\n\n `),new Et("No correct chart data found. Ensure that you are using either application/json or multipart/form-data headers. If sending JSON, make sure the chart data is in the 'infile', 'options', or 'data' attribute. If sending SVG, ensure it is in the 'svg' attribute.",400,je);let p=!1;if(p=Ot(_t,e,t,{id:a,uniqueId:i,type:l,body:s}),!0!==p)return t.send(p);const u=new AbortController;t.once("close",(()=>{t.writableFinished||(u.abort(),X(4,`[export] The client closed the connection for request ${i} before it was answered.`))})),X(4,`[export] Got an incoming HTTP request with ID ${i}.`),s.constr="string"==typeof s.constr&&s.constr||"chart";const h={export:{instr:c,type:l,constr:s.constr[0].toLowerCase()+s.constr.substr(1),height:s.height,width:s.width,scale:s.scale||n.export.scale,globalOptions:ee(s.globalOptions,!0),themeOptions:ee(s.themeOptions,!0)},customLogic:{allowCodeExecution:ut,allowFileResources:!1,resources:ee(s.resources,!0),callback:s.callback,customCode:s.customCode}};c&&(h.export.instr=re(c,h.customLogic.allowCodeExecution));const d=ce(n,h);if(d.export.options=c,d.payload={svg:s.svg||!1,b64:s.b64||!1,noDownload:s.noDownload||!1,requestId:i,abortSignal:u.signal},s.svg&&(e=>[/xlink:href="(?:http:\/\/|https:\/\/)?localhost\b/,/xlink:href="(?:http:\/\/|https:\/\/)?10\.\d{1,3}\.\d{1,3}\.\d{1,3}\b/,/xlink:href="(?:http:\/\/|https:\/\/)?127\.\d{1,3}\.\d{1,3}\.\d{1,3}\b/,/xlink:href="(?:http:\/\/|https:\/\/)?172\.(1[6-9]|2[0-9]|3[0-1])\.\d{1,3}\.\d{1,3}\b/,/xlink:href="(?:http:\/\/|https:\/\/)?192\.168\.\d{1,3}\.\d{1,3}\b/].some((t=>t.test(e))))(d.payload.svg))throw new Et("SVG potentially contain at least one forbidden URL in xlink:href element. Please review the SVG content and ensure that all referenced URLs comply with security policies.",400,je);await ht(d,((o,c)=>{if(u.signal.aborted)return X(4,`[export] Discarding the result for request ${i}: the client is gone.`);if(n.server.benchmarking&&X(5,`[benchmark] Request with ID ${i} - After the whole exporting process: ${r()}ms.`),o)throw o;if(!c||!c.result)throw new Et(`Unexpected return from chart generation. Please check your request data. For the request with ID ${i}, the result is ${c.result}.`,400,Xe);return l=c.options.export.type,Ot(xt,e,t,{id:a,body:c.result}),c.result?s.b64?"pdf"===l||"svg"==l?t.send(Buffer.from(c.result,"utf8").toString("base64")):t.send(c.result):(t.header("Content-Type",St[l]||"image/png"),s.noDownload||t.attachment(`${e.params.filename||e.body.filename||"chart"}.${l||"png"}`),"svg"===l?t.send(c.result):t.send(Buffer.from(c.result,"base64"))):void 0}))}catch(e){r(e)}var o};const kt=JSON.parse(o(l(Y,"package.json"))),It=new Date,Ct=[];function Nt(e){const t=e.exportAttempts-e.abandonedExports;return t<=0?null:e.performedExports/t*100}function Pt(e){if(!e)return!1;var t;t=setInterval((()=>{const e=Nt(lt());Ct.push(null===e?1:e),Ct.length>30&&Ct.shift()}),6e4),vt.push(t),e.get("/health",((e,t)=>{const r=lt(),o=Ct.length,i=Ct.reduce(((e,t)=>e+t),0)/Ct.length;X(4,"[health.js] GET /health [200] - returning server health."),t.send({status:"OK",bootTime:It,uptime:Math.floor(((new Date).getTime()-It.getTime())/1e3/60)+" minutes",version:kt.version,highchartsVersion:Te(),averageProcessingTime:r.spentAverage,performedExports:r.performedExports,failedExports:r.droppedExports,abandonedExports:r.abandonedExports,rejectedForCapacity:r.rejectedForCapacity,consecutiveCreateFailures:Qe(),browserConnected:!!xe?.connected,exportAttempts:r.exportAttempts,sucessRatio:Nt(r),pool:at(),period:o,movingAverage:i,message:isNaN(i)||!Ct.length?"Too early to report. No exports made yet. Please check back soon.":`Last ${o} minutes had a success rate of ${i.toFixed(2)}%.`,svgExportAttempts:r.exportFromSvgAttempts,jsonExportAttempts:r.performedExports-r.exportFromSvgAttempts})}))}const At=new Map,Ht=x();Ht.disable("x-powered-by"),Ht.use(_()),Ht.use(((e,t,r)=>{t.set("Accept-Ranges","none"),r()}));const $t=e=>{e.on("clientError",((e,t)=>{B(1,e,`[server] Client error: ${e.message}, destroying socket.`),t.destroy()})),e.on("error",(e=>{B(1,e,`[server] Server error: ${e.message}`)})),e.on("connection",(e=>{e.on("error",(e=>{B(1,e,`[server] Socket error: ${e.message}`)}))}))},Dt=(e,t)=>{const r=parseInt(t.keepAliveTimeout),o=isNaN(r)||r<0?65e3:r;e.keepAliveTimeout=o,e.headersTimeout=o+5e3,X(4,`[server] Set keepAliveTimeout to ${e.keepAliveTimeout}ms and headersTimeout to ${e.headersTimeout}ms.`)},Ut=async e=>{try{const t=1024*(e.maxUploadSize||3)*1024,r=O.memoryStorage(),o=O({storage:r,limits:{fieldSize:t}});if(Ht.use(((e,t,r)=>{if("POST"!==e.method||e.path.startsWith("/version/"))return r();const o=it();if(o&&o.numPendingAcquires()>=Ze()){++Ke.rejectedForCapacity;const e=new Et(`The server is at capacity: ${o.numPendingAcquires()} exports are already waiting for a worker (limit is ${Ze()}). Please retry shortly.`,400,Fe),i=(()=>{const e=parseInt(ze.queueRejectDelay);return isNaN(e)||e<0?0:e})();if(!i)return r(e);const n=()=>clearTimeout(s),s=setTimeout((()=>{t.removeListener("close",n),r(e)}),i);t.once("close",n)}else r()})),Ht.use(x.json({limit:t})),Ht.use(x.urlencoded({extended:!0,limit:t})),Ht.use(o.none()),!e.enable)return!1;if(!e.ssl.force){const t=y.createServer(Ht);$t(t),Dt(t,e),t.listen(e.port,e.host),At.set(e.port,t),X(3,`[server] Started HTTP server on ${e.host}:${e.port}.`)}if(e.ssl.enable){let t,r;try{t=await i.readFile(u.join(e.ssl.certPath,"server.key"),"utf8"),r=await i.readFile(u.join(e.ssl.certPath,"server.crt"),"utf8")}catch(t){X(2,`[server] Unable to load key/certificate from the '${e.ssl.certPath}' path. Could not run secured layer server.`)}if(t&&r){const o=b.createServer({key:t,cert:r},Ht);$t(o),Dt(o,e),o.listen(e.ssl.port,e.host),At.set(e.ssl.port,o),X(3,`[server] Started HTTPS server on ${e.host}:${e.ssl.port}.`)}}e.rateLimiting&&e.rateLimiting.enable&&![0,NaN].includes(e.rateLimiting.maxRequests)&&wt(Ht,e.rateLimiting),Ht.use(x.static(u.join(Y,"public"))),Pt(Ht),(e=>{e.post("/",Lt),e.post("/:filename",Lt)})(Ht),(e=>{!!e&&e.get("/",((e,t)=>{t.sendFile(l(Y,"public","index.html"),{acceptRanges:!1})}))})(Ht),Tt(Ht),(e=>{e.use(yt),e.use(bt)})(Ht)}catch(e){throw new me("[server] Could not configure and start the server.").setError(e)}},Gt=()=>(X(4,"[server] Closing all servers."),Promise.all([...At].map((([e,t])=>new Promise((r=>{t.close((()=>{At.delete(e),X(4,`[server] Closed server on port: ${e}.`),r()})),t.closeIdleConnections?.()}))))));var Mt={startServer:Ut,closeServers:Gt,getServers:()=>At,enableRateLimiting:e=>wt(Ht,e),getExpress:()=>x,getApp:()=>Ht,use:(e,...t)=>{Ht.use(e,...t)},get:(e,...t)=>{Ht.get(e,...t)},post:(e,...t)=>{Ht.post(e,...t)}};const jt=async e=>{(()=>{X(4,"[server] Clearing all registered intervals.");for(const e of vt)clearInterval(e)})();const t=(()=>{const e=parseInt(le()?.other?.shutdownDrainTimeout);return isNaN(e)||e<0?3e4:e})();await Promise.race([Gt(),new Promise((e=>setTimeout(e,t)))]),await rt(),process.exit(e)};var Ft={server:Mt,startServer:Ut,initExport:async e=>{var t;return t=e.customLogic&&e.customLogic.allowCodeExecution,ut=ie(t),(e=>{for(const[t,r]of Object.entries(e))q[t]=r;K(e&&parseInt(e.level)),e&&e.dest&&e.toFile&&z(e.dest,e.file||"highcharts-export-server.log")})(e.logging),e.other.listenToProcessExits&&(X(3,"[process] Attaching exit listeners to the process."),process.on("exit",(e=>{X(4,`Process exited with code ${e}.`)})),process.on("SIGINT",(async(e,t)=>{X(4,`The ${e} event with code: ${t}.`),await jt(0)})),process.on("SIGTERM",(async(e,t)=>{X(4,`The ${e} event with code: ${t}.`),await jt(0)})),process.on("SIGHUP",(async(e,t)=>{X(4,`The ${e} event with code: ${t}.`),await jt(0)})),process.on("uncaughtException",(async(e,t)=>{B(1,e,`The ${t} error.`),await jt(1)}))),await we(e),await tt({pool:e.pool||{minWorkers:1,maxWorkers:1},puppeteerArgs:e.puppeteer.args||[]}),e},singleExport:async e=>{e.export.instr=e.export.instr||e.export.options,await ht(e,(async(e,t)=>{if(e)throw e;const{outfile:r,type:o}=t.options.export;n(r||`chart.${o}`,"svg"!==o?Buffer.from(t.result,"base64"):t.result),await rt()}))},batchExport:async e=>{const t=[];for(let r of e.export.batch.split(";"))r=r.split("="),2===r.length&&t.push(ht({...e,export:{...e.export,infile:r[0],outfile:r[1]}},((e,t)=>{if(e)throw e;n(t.options.export.outfile,"svg"!==t.options.export.type?Buffer.from(t.result,"base64"):t.result)})));try{await Promise.all(t),await rt()}catch(e){throw new me("[chart] Error encountered during batch export.").setError(e)}},startExport:ht,initPool:tt,killPool:rt,setOptions:(e,t)=>(t?.length&&(ae=function(e){const t=e.findIndex((e=>"loadConfig"===e.replace(/-/g,"")));if(t>-1&&e[t+1]){const r=e[t+1];try{if(r&&r.endsWith(".json"))return JSON.parse(o(r))}catch(e){B(2,e,`[config] Unable to load the configuration from the ${r} file.`)}}return{}}(t)),pe(I,ae),ae=ue(I),e&&(ae=ce(ae,e,N)),t?.length&&(ae=function(e,t,r){let o=!1;for(let i=0;i(s.length-1===r&&(a=e[t].type),e[t])),r),s.reduce(((e,r,l)=>(s.length-1===l&&void 0!==e[r]&&(t[++i]?"boolean"===a?e[r]=ie(t[i]):"number"===a?e[r]=+t[i]:a.indexOf("]")>=0?e[r]=t[i].split(","):e[r]=t[i]:(X(2,`[config] Missing value for the '${n}' argument. Using the default value.`),o=!0)),e[r])),e)}o&&oe();return e}(ae,t,I)),ae),shutdownCleanUp:jt,log:X,logWithStack:B,setLogLevel:K,enableFileLogging:z,mapToNewConfig:e=>{const t={};for(const[r,o]of Object.entries(e)){const e=P[r]?P[r].split("."):[];e.reduce(((t,r,i)=>t[r]=e.length-1===i?o:t[r]||{}),t)}return t},manualConfig:async t=>{let r={};e(t)&&(r=JSON.parse(o(t,"utf8")));const n=Object.keys(C).map((e=>({title:`${e} options`,value:e})));return d({type:"multiselect",name:"category",message:"Which category do you want to configure?",hint:"Space: Select specific, A: Select all, Enter: Confirm.",instructions:"",choices:n},{onSubmit:async(e,o)=>{let n=0,s=[];for(const e of o)C[e]=C[e].map((t=>({...t,section:e}))),s=[...s,...C[e]];return await d(s,{onSubmit:async(e,o)=>{if("moduleScripts"===e.name?(o=o.length?o.map((t=>e.choices[t])):e.choices,r[e.section][e.name]=o):r[e.section]=he(Object.assign({},r[e.section]||{}),e.name.split("."),e.choices?e.choices[o]:o),++n===s.length){try{await i.writeFile(t,JSON.stringify(r,null,2),"utf8")}catch(e){B(1,e,`[config] An error occurred while creating the ${t} file.`)}return!0}}}),!0}})},printLogo:e=>{const t=JSON.parse(o(l(Y,"package.json"))).version;e?console.log(`Starting Highcharts Export Server v${t}...`):console.log(o(Y+"/msg/startup.msg").toString().bold.yellow,`v${t}\n`.bold)},printUsage:oe};export{Ft as default}; //# sourceMappingURL=index.esm.js.map diff --git a/dist/index.esm.js.map b/dist/index.esm.js.map index 8a6ed55a..d60a0e6e 100644 --- a/dist/index.esm.js.map +++ b/dist/index.esm.js.map @@ -1 +1 @@ -{"version":3,"file":"index.esm.js","sources":["../lib/schemas/config.js","../lib/envs.js","../lib/logger.js","../lib/utils.js","../lib/config.js","../lib/fetch.js","../lib/errors/ExportError.js","../lib/cache.js","../lib/highcharts.js","../lib/browser.js","../lib/export.js","../templates/svg_export/svg_export.js","../lib/pool.js","../lib/chart.js","../lib/sanitize.js","../lib/intervals.js","../lib/server/error.js","../lib/server/rate_limit.js","../lib/errors/HttpError.js","../lib/server/routes/change_hc_version.js","../lib/server/routes/export.js","../lib/server/routes/health.js","../lib/server/server.js","../lib/server/routes/ui.js","../lib/resource_release.js","../lib/index.js"],"sourcesContent":["/*******************************************************************************\r\n\r\nHighcharts Export Server\r\n\r\nCopyright (c) 2016-2024, Highsoft\r\n\r\nLicenced under the MIT licence.\r\n\r\nAdditionally a valid Highcharts license is required for use.\r\n\r\nSee LICENSE file in root for details.\r\n\r\n*******************************************************************************/\r\n\r\n// Possible names for Highcharts scripts\r\nexport const scriptsNames = {\r\n core: ['highcharts', 'highcharts-more', 'highcharts-3d'],\r\n modules: [\r\n 'stock',\r\n 'map',\r\n 'gantt',\r\n 'exporting',\r\n 'parallel-coordinates',\r\n 'accessibility',\r\n // 'annotations-advanced',\r\n 'boost-canvas',\r\n 'boost',\r\n 'data',\r\n 'data-tools',\r\n 'draggable-points',\r\n 'static-scale',\r\n 'broken-axis',\r\n 'heatmap',\r\n 'tilemap',\r\n 'tiledwebmap',\r\n 'timeline',\r\n 'treemap',\r\n 'treegraph',\r\n 'item-series',\r\n 'drilldown',\r\n 'histogram-bellcurve',\r\n 'bullet',\r\n 'funnel',\r\n 'funnel3d',\r\n 'geoheatmap',\r\n 'pyramid3d',\r\n 'networkgraph',\r\n // 'overlapping-datalabels',\r\n 'pareto',\r\n 'pattern-fill',\r\n 'pictorial',\r\n 'price-indicator',\r\n 'sankey',\r\n 'arc-diagram',\r\n 'dependency-wheel',\r\n 'series-label',\r\n 'series-on-point',\r\n 'solid-gauge',\r\n 'sonification',\r\n // 'stock-tools',\r\n 'streamgraph',\r\n 'sunburst',\r\n 'variable-pie',\r\n 'variwide',\r\n 'vector',\r\n 'venn',\r\n 'windbarb',\r\n 'wordcloud',\r\n 'xrange',\r\n 'no-data-to-display',\r\n 'drag-panes',\r\n 'debugger',\r\n 'dumbbell',\r\n 'lollipop',\r\n 'cylinder',\r\n 'organization',\r\n 'dotplot',\r\n 'marker-clusters',\r\n 'hollowcandlestick',\r\n 'heikinashi',\r\n 'flowmap',\r\n 'export-data',\r\n 'navigator',\r\n 'textpath'\r\n ],\r\n indicators: ['indicators-all'],\r\n custom: [\r\n 'https://cdnjs.cloudflare.com/ajax/libs/moment.js/2.30.1/moment.min.js',\r\n 'https://cdnjs.cloudflare.com/ajax/libs/moment-timezone/0.5.45/moment-timezone-with-data.min.js'\r\n ]\r\n};\r\n\r\n// This is the configuration object with all options and their default values,\r\n// also from the .env file if one exists\r\nexport const defaultConfig = {\r\n puppeteer: {\r\n args: {\r\n value: [\r\n '--allow-running-insecure-content',\r\n '--ash-no-nudges',\r\n '--autoplay-policy=user-gesture-required',\r\n '--block-new-web-contents',\r\n '--disable-accelerated-2d-canvas',\r\n '--disable-background-networking',\r\n '--disable-background-timer-throttling',\r\n '--disable-backgrounding-occluded-windows',\r\n '--disable-breakpad',\r\n '--disable-checker-imaging',\r\n '--disable-client-side-phishing-detection',\r\n '--disable-component-extensions-with-background-pages',\r\n '--disable-component-update',\r\n '--disable-default-apps',\r\n '--disable-dev-shm-usage',\r\n '--disable-domain-reliability',\r\n '--disable-extensions',\r\n '--disable-features=CalculateNativeWinOcclusion,InterestFeedContentSuggestions,WebOTP',\r\n '--disable-hang-monitor',\r\n '--disable-ipc-flooding-protection',\r\n '--disable-logging',\r\n '--disable-notifications',\r\n '--disable-offer-store-unmasked-wallet-cards',\r\n '--disable-popup-blocking',\r\n '--disable-print-preview',\r\n '--disable-prompt-on-repost',\r\n '--disable-renderer-backgrounding',\r\n '--disable-search-engine-choice-screen',\r\n '--disable-session-crashed-bubble',\r\n '--disable-setuid-sandbox',\r\n '--disable-site-isolation-trials',\r\n '--disable-speech-api',\r\n '--disable-sync',\r\n '--enable-unsafe-webgpu',\r\n '--hide-crash-restore-bubble',\r\n '--hide-scrollbars',\r\n '--metrics-recording-only',\r\n '--mute-audio',\r\n '--no-default-browser-check',\r\n '--no-first-run',\r\n '--no-pings',\r\n '--pipe',\r\n // '--no-sandbox',\r\n '--no-startup-window',\r\n // '--no-zygote',\r\n '--password-store=basic',\r\n '--process-per-tab',\r\n '--use-mock-keychain'\r\n ],\r\n type: 'string[]',\r\n description: 'Arguments array to send to Puppeteer.'\r\n },\r\n tempDir: {\r\n value: './tmp/',\r\n type: 'string',\r\n envLink: 'PUPPETEER_TEMP_DIR',\r\n description: 'The directory for Puppeteer to store temporary files.'\r\n }\r\n },\r\n highcharts: {\r\n version: {\r\n value: 'latest',\r\n type: 'string',\r\n envLink: 'HIGHCHARTS_VERSION',\r\n description: 'The Highcharts version to be used.'\r\n },\r\n cdnURL: {\r\n value: 'https://code.highcharts.com/',\r\n type: 'string',\r\n envLink: 'HIGHCHARTS_CDN_URL',\r\n description: 'The CDN URL for Highcharts scripts to be used.'\r\n },\r\n useNpm: {\r\n value: false,\r\n type: 'boolean',\r\n envLink: 'HIGHCHARTS_USE_NPM',\r\n description: 'Flag to use Highcharts scripts from NPM package'\r\n },\r\n coreScripts: {\r\n value: scriptsNames.core,\r\n type: 'string[]',\r\n envLink: 'HIGHCHARTS_CORE_SCRIPTS',\r\n description: 'The core Highcharts scripts to fetch.'\r\n },\r\n moduleScripts: {\r\n value: scriptsNames.modules,\r\n type: 'string[]',\r\n envLink: 'HIGHCHARTS_MODULE_SCRIPTS',\r\n description: 'The modules of Highcharts to fetch.'\r\n },\r\n indicatorScripts: {\r\n value: scriptsNames.indicators,\r\n type: 'string[]',\r\n envLink: 'HIGHCHARTS_INDICATOR_SCRIPTS',\r\n description: 'The indicators of Highcharts to fetch.'\r\n },\r\n customScripts: {\r\n value: scriptsNames.custom,\r\n type: 'string[]',\r\n description: 'Additional custom scripts or dependencies to fetch.'\r\n },\r\n forceFetch: {\r\n value: false,\r\n type: 'boolean',\r\n envLink: 'HIGHCHARTS_FORCE_FETCH',\r\n description:\r\n 'The flag to determine whether to refetch all scripts after each server rerun.'\r\n },\r\n cachePath: {\r\n value: '.cache',\r\n type: 'string',\r\n envLink: 'HIGHCHARTS_CACHE_PATH',\r\n description:\r\n 'The path to the cache directory. It is used to store the Highcharts scripts and custom scripts.'\r\n }\r\n },\r\n export: {\r\n infile: {\r\n value: false,\r\n type: 'string',\r\n description:\r\n 'The input file should include a name and a type (json or svg). It must be correctly formatted as a JSON or SVG file.'\r\n },\r\n instr: {\r\n value: false,\r\n type: 'string',\r\n description:\r\n 'Input, provided in the form of a stringified JSON or SVG file, will override the --infile option.'\r\n },\r\n options: {\r\n value: false,\r\n type: 'string',\r\n description: 'An alias for the --instr option.'\r\n },\r\n outfile: {\r\n value: false,\r\n type: 'string',\r\n description:\r\n 'The output filename along with a type (jpeg, png, pdf, or svg). This will ignore the --type flag.'\r\n },\r\n type: {\r\n value: 'png',\r\n type: 'string',\r\n envLink: 'EXPORT_TYPE',\r\n description: 'The file export format. It can be jpeg, png, pdf, or svg.'\r\n },\r\n constr: {\r\n value: 'chart',\r\n type: 'string',\r\n envLink: 'EXPORT_CONSTR',\r\n description:\r\n 'The constructor to use. Can be chart, stockChart, mapChart, or ganttChart.'\r\n },\r\n defaultHeight: {\r\n value: 400,\r\n type: 'number',\r\n envLink: 'EXPORT_DEFAULT_HEIGHT',\r\n description:\r\n 'the default height of the exported chart. Used when no value is set.'\r\n },\r\n defaultWidth: {\r\n value: 600,\r\n type: 'number',\r\n envLink: 'EXPORT_DEFAULT_WIDTH',\r\n description:\r\n 'The default width of the exported chart. Used when no value is set.'\r\n },\r\n defaultScale: {\r\n value: 1,\r\n type: 'number',\r\n envLink: 'EXPORT_DEFAULT_SCALE',\r\n description:\r\n 'The default scale of the exported chart. Used when no value is set.'\r\n },\r\n height: {\r\n value: false,\r\n type: 'number',\r\n description:\r\n 'The height of the exported chart, overriding the option in the chart settings.'\r\n },\r\n width: {\r\n value: false,\r\n type: 'number',\r\n description:\r\n 'The width of the exported chart, overriding the option in the chart settings.'\r\n },\r\n scale: {\r\n value: false,\r\n type: 'number',\r\n description:\r\n 'The scale of the exported chart, overriding the option in the chart settings. Ranges between 0.1 and 5.0.'\r\n },\r\n globalOptions: {\r\n value: false,\r\n type: 'string',\r\n description:\r\n 'Either a stringified JSON or a filename containing options to be passed into the Highcharts.setOptions.'\r\n },\r\n themeOptions: {\r\n value: false,\r\n type: 'string',\r\n description:\r\n 'Either a stringified JSON or a filename containing theme options to be passed into the Highcharts.setOptions.'\r\n },\r\n batch: {\r\n value: false,\r\n type: 'string',\r\n description:\r\n 'Initiates a batch job with a string containing input/output pairs: \"in=out;in=out;...\".'\r\n },\r\n rasterizationTimeout: {\r\n value: 1500,\r\n type: 'number',\r\n envLink: 'EXPORT_RASTERIZATION_TIMEOUT',\r\n description:\r\n 'The duration in milliseconds to wait for rendering a webpage.'\r\n }\r\n },\r\n customLogic: {\r\n allowCodeExecution: {\r\n value: false,\r\n type: 'boolean',\r\n envLink: 'CUSTOM_LOGIC_ALLOW_CODE_EXECUTION',\r\n description:\r\n 'Controls whether the execution of arbitrary code is allowed during the exporting process.'\r\n },\r\n allowFileResources: {\r\n value: false,\r\n type: 'boolean',\r\n envLink: 'CUSTOM_LOGIC_ALLOW_FILE_RESOURCES',\r\n description:\r\n 'Controls the ability to inject resources from the filesystem. This setting has no effect when running as a server.'\r\n },\r\n customCode: {\r\n value: false,\r\n type: 'string',\r\n description:\r\n 'Custom code to execute before chart initialization. It can be a function, code wrapped within a function, or a filename with the .js extension.'\r\n },\r\n callback: {\r\n value: false,\r\n type: 'string',\r\n description:\r\n 'JavaScript code to run during construction. It can be a function or a filename with the .js extension.'\r\n },\r\n resources: {\r\n value: false,\r\n type: 'string',\r\n description:\r\n 'Additional resource in the form of a stringified JSON, which may contain files, js, and css sections.'\r\n },\r\n loadConfig: {\r\n value: false,\r\n type: 'string',\r\n legacyName: 'fromFile',\r\n description: 'A file containing a pre-defined configuration to use.'\r\n },\r\n createConfig: {\r\n value: false,\r\n type: 'string',\r\n description:\r\n 'Enables setting options through a prompt and saving them in a provided config file.'\r\n }\r\n },\r\n server: {\r\n maxUploadSize: {\r\n value: 3,\r\n type: 'number',\r\n envLink: 'SERVER_MAX_UPLOAD_SIZE',\r\n description: 'The maximum upload size, in MB, for the server.'\r\n },\r\n enable: {\r\n value: false,\r\n type: 'boolean',\r\n envLink: 'SERVER_ENABLE',\r\n cliName: 'enableServer',\r\n description:\r\n 'When set to true, the server starts on the local IP address 0.0.0.0.'\r\n },\r\n host: {\r\n value: '0.0.0.0',\r\n type: 'string',\r\n envLink: 'SERVER_HOST',\r\n description:\r\n 'The hostname of the server. Additionally, it starts a server on the provided hostname.'\r\n },\r\n port: {\r\n value: 7801,\r\n type: 'number',\r\n envLink: 'SERVER_PORT',\r\n description: 'The server port when enabled.'\r\n },\r\n benchmarking: {\r\n value: false,\r\n type: 'boolean',\r\n envLink: 'SERVER_BENCHMARKING',\r\n cliName: 'serverBenchmarking',\r\n description:\r\n 'Indicates whether to display the duration, in milliseconds, of specific actions that occur on the server while serving a request.'\r\n },\r\n proxy: {\r\n host: {\r\n value: false,\r\n type: 'string',\r\n envLink: 'SERVER_PROXY_HOST',\r\n cliName: 'proxyHost',\r\n description: 'The host of the proxy server to use, if it exists.'\r\n },\r\n port: {\r\n value: 8080,\r\n type: 'number',\r\n envLink: 'SERVER_PROXY_PORT',\r\n cliName: 'proxyPort',\r\n description: 'The port of the proxy server to use, if it exists.'\r\n },\r\n username: {\r\n value: false,\r\n type: 'string',\r\n envLink: 'SERVER_PROXY_USERNAME',\r\n cliName: 'proxyUsername',\r\n description: 'The username for the proxy server, if it exists.'\r\n },\r\n password: {\r\n value: false,\r\n type: 'string',\r\n envLink: 'SERVER_PROXY_PASSWORD',\r\n cliName: 'proxyPassword',\r\n description: 'The password for the proxy server, if it exists.'\r\n },\r\n timeout: {\r\n value: 5000,\r\n type: 'number',\r\n envLink: 'SERVER_PROXY_TIMEOUT',\r\n cliName: 'proxyTimeout',\r\n description: 'The timeout for the proxy server to use, if it exists.'\r\n }\r\n },\r\n rateLimiting: {\r\n enable: {\r\n value: false,\r\n type: 'boolean',\r\n envLink: 'SERVER_RATE_LIMITING_ENABLE',\r\n cliName: 'enableRateLimiting',\r\n description: 'Enables rate limiting for the server.'\r\n },\r\n maxRequests: {\r\n value: 10,\r\n type: 'number',\r\n envLink: 'SERVER_RATE_LIMITING_MAX_REQUESTS',\r\n legacyName: 'rateLimit',\r\n description: 'The maximum number of requests allowed in one minute.'\r\n },\r\n window: {\r\n value: 1,\r\n type: 'number',\r\n envLink: 'SERVER_RATE_LIMITING_WINDOW',\r\n description: 'The time window, in minutes, for the rate limiting.'\r\n },\r\n delay: {\r\n value: 0,\r\n type: 'number',\r\n envLink: 'SERVER_RATE_LIMITING_DELAY',\r\n description:\r\n 'The delay duration for each successive request before reaching the maximum limit.'\r\n },\r\n trustProxy: {\r\n value: false,\r\n type: 'boolean',\r\n envLink: 'SERVER_RATE_LIMITING_TRUST_PROXY',\r\n description: 'Set this to true if the server is behind a load balancer.'\r\n },\r\n skipKey: {\r\n value: false,\r\n type: 'string',\r\n envLink: 'SERVER_RATE_LIMITING_SKIP_KEY',\r\n description:\r\n 'Allows bypassing the rate limiter and should be provided with the skipToken argument.'\r\n },\r\n skipToken: {\r\n value: false,\r\n type: 'string',\r\n envLink: 'SERVER_RATE_LIMITING_SKIP_TOKEN',\r\n description:\r\n 'Allows bypassing the rate limiter and should be provided with the skipKey argument.'\r\n }\r\n },\r\n ssl: {\r\n enable: {\r\n value: false,\r\n type: 'boolean',\r\n envLink: 'SERVER_SSL_ENABLE',\r\n cliName: 'enableSsl',\r\n description: 'Enables or disables the SSL protocol.'\r\n },\r\n force: {\r\n value: false,\r\n type: 'boolean',\r\n envLink: 'SERVER_SSL_FORCE',\r\n cliName: 'sslForce',\r\n legacyName: 'sslOnly',\r\n description:\r\n 'When set to true, the server is forced to serve only over HTTPS.'\r\n },\r\n port: {\r\n value: 443,\r\n type: 'number',\r\n envLink: 'SERVER_SSL_PORT',\r\n cliName: 'sslPort',\r\n description: 'The port on which to run the SSL server.'\r\n },\r\n certPath: {\r\n value: false,\r\n type: 'string',\r\n envLink: 'SERVER_SSL_CERT_PATH',\r\n legacyName: 'sslPath',\r\n description: 'The path to the SSL certificate/key file.'\r\n }\r\n }\r\n },\r\n pool: {\r\n minWorkers: {\r\n value: 4,\r\n type: 'number',\r\n envLink: 'POOL_MIN_WORKERS',\r\n description: 'The number of minimum and initial pool workers to spawn.'\r\n },\r\n maxWorkers: {\r\n value: 8,\r\n type: 'number',\r\n envLink: 'POOL_MAX_WORKERS',\r\n legacyName: 'workers',\r\n description: 'The number of maximum pool workers to spawn.'\r\n },\r\n workLimit: {\r\n value: 40,\r\n type: 'number',\r\n envLink: 'POOL_WORK_LIMIT',\r\n description:\r\n 'The number of work pieces that can be performed before restarting the worker process.'\r\n },\r\n acquireTimeout: {\r\n value: 5000,\r\n type: 'number',\r\n envLink: 'POOL_ACQUIRE_TIMEOUT',\r\n description:\r\n 'The duration, in milliseconds, to wait for acquiring a resource.'\r\n },\r\n createTimeout: {\r\n value: 5000,\r\n type: 'number',\r\n envLink: 'POOL_CREATE_TIMEOUT',\r\n description:\r\n 'The duration, in milliseconds, to wait for creating a resource.'\r\n },\r\n destroyTimeout: {\r\n value: 5000,\r\n type: 'number',\r\n envLink: 'POOL_DESTROY_TIMEOUT',\r\n description:\r\n 'The duration, in milliseconds, to wait for destroying a resource.'\r\n },\r\n idleTimeout: {\r\n value: 30000,\r\n type: 'number',\r\n envLink: 'POOL_IDLE_TIMEOUT',\r\n description:\r\n 'The duration, in milliseconds, after which an idle resource is destroyed.'\r\n },\r\n createRetryInterval: {\r\n value: 200,\r\n type: 'number',\r\n envLink: 'POOL_CREATE_RETRY_INTERVAL',\r\n description:\r\n 'The duration, in milliseconds, to wait before retrying the create process in case of a failure.'\r\n },\r\n reaperInterval: {\r\n value: 1000,\r\n type: 'number',\r\n envLink: 'POOL_REAPER_INTERVAL',\r\n description:\r\n 'The duration, in milliseconds, after which the check for idle resources to destroy is triggered.'\r\n },\r\n benchmarking: {\r\n value: false,\r\n type: 'boolean',\r\n envLink: 'POOL_BENCHMARKING',\r\n cliName: 'poolBenchmarking',\r\n description:\r\n 'Indicate whether to show statistics for the pool of resources or not.'\r\n }\r\n },\r\n logging: {\r\n level: {\r\n value: 4,\r\n type: 'number',\r\n envLink: 'LOGGING_LEVEL',\r\n cliName: 'logLevel',\r\n description: 'The logging level to be used.'\r\n },\r\n file: {\r\n value: 'highcharts-export-server.log',\r\n type: 'string',\r\n envLink: 'LOGGING_FILE',\r\n cliName: 'logFile',\r\n description:\r\n 'The name of a log file. The `logToFile` and `logDest` options also need to be set to enable file logging.'\r\n },\r\n dest: {\r\n value: 'log/',\r\n type: 'string',\r\n envLink: 'LOGGING_DEST',\r\n cliName: 'logDest',\r\n description:\r\n 'The path to store log files. The `logToFile` option also needs to be set to enable file logging.'\r\n },\r\n toConsole: {\r\n value: true,\r\n type: 'boolean',\r\n envLink: 'LOGGING_TO_CONSOLE',\r\n cliName: 'logToConsole',\r\n description: 'Enables or disables showing logs in the console.'\r\n },\r\n toFile: {\r\n value: true,\r\n type: 'boolean',\r\n envLink: 'LOGGING_TO_FILE',\r\n cliName: 'logToFile',\r\n description:\r\n 'Enables or disables creation of the log directory and saving the log into a .log file.'\r\n }\r\n },\r\n ui: {\r\n enable: {\r\n value: false,\r\n type: 'boolean',\r\n envLink: 'UI_ENABLE',\r\n cliName: 'enableUi',\r\n description:\r\n 'Enables or disables the user interface (UI) for the export server.'\r\n },\r\n route: {\r\n value: '/',\r\n type: 'string',\r\n envLink: 'UI_ROUTE',\r\n cliName: 'uiRoute',\r\n description:\r\n 'The endpoint route to which the user interface (UI) should be attached.'\r\n }\r\n },\r\n other: {\r\n nodeEnv: {\r\n value: 'production',\r\n type: 'string',\r\n envLink: 'OTHER_NODE_ENV',\r\n description: 'The type of Node.js environment.'\r\n },\r\n listenToProcessExits: {\r\n value: true,\r\n type: 'boolean',\r\n envLink: 'OTHER_LISTEN_TO_PROCESS_EXITS',\r\n description: 'Decides whether or not to attach process.exit handlers.'\r\n },\r\n noLogo: {\r\n value: false,\r\n type: 'boolean',\r\n envLink: 'OTHER_NO_LOGO',\r\n description:\r\n 'Skip printing the logo on a startup. Will be replaced by a simple text.'\r\n },\r\n hardResetPage: {\r\n value: false,\r\n type: 'boolean',\r\n envLink: 'OTHER_HARD_RESET_PAGE',\r\n description: 'Decides if the page content should be reset entirely.'\r\n },\r\n browserShellMode: {\r\n value: true,\r\n type: 'boolean',\r\n envLink: 'OTHER_BROWSER_SHELL_MODE',\r\n description: 'Decides if the browser runs in the shell mode.'\r\n }\r\n },\r\n debug: {\r\n enable: {\r\n value: false,\r\n type: 'boolean',\r\n envLink: 'DEBUG_ENABLE',\r\n cliName: 'enableDebug',\r\n description: 'Enables or disables debug mode for the underlying browser.'\r\n },\r\n headless: {\r\n value: true,\r\n type: 'boolean',\r\n envLink: 'DEBUG_HEADLESS',\r\n description:\r\n 'Controls the mode in which the browser is launched when in the debug mode.'\r\n },\r\n devtools: {\r\n value: false,\r\n type: 'boolean',\r\n envLink: 'DEBUG_DEVTOOLS',\r\n description:\r\n 'Decides whether to enable DevTools when the browser is in a headful state.'\r\n },\r\n listenToConsole: {\r\n value: false,\r\n type: 'boolean',\r\n envLink: 'DEBUG_LISTEN_TO_CONSOLE',\r\n description:\r\n 'Decides whether to enable a listener for console messages sent from the browser.'\r\n },\r\n dumpio: {\r\n value: false,\r\n type: 'boolean',\r\n envLink: 'DEBUG_DUMPIO',\r\n description:\r\n 'Redirects browser process stdout and stderr to process.stdout and process.stderr.'\r\n },\r\n slowMo: {\r\n value: 0,\r\n type: 'number',\r\n envLink: 'DEBUG_SLOW_MO',\r\n description:\r\n 'Slows down Puppeteer operations by the specified number of milliseconds.'\r\n },\r\n debuggingPort: {\r\n value: 9222,\r\n type: 'number',\r\n envLink: 'DEBUG_DEBUGGING_PORT',\r\n description: 'Specifies the debugging port.'\r\n }\r\n }\r\n};\r\n\r\n// The config descriptions object for the prompts functionality. It contains\r\n// information like:\r\n// * Type of a prompt\r\n// * Name of an option\r\n// * Short description of a chosen option\r\n// * Initial value\r\nexport const promptsConfig = {\r\n puppeteer: [\r\n {\r\n type: 'list',\r\n name: 'args',\r\n message: 'Puppeteer arguments',\r\n initial: defaultConfig.puppeteer.args.value.join(','),\r\n separator: ','\r\n }\r\n ],\r\n highcharts: [\r\n {\r\n type: 'text',\r\n name: 'version',\r\n message: 'Highcharts version',\r\n initial: defaultConfig.highcharts.version.value\r\n },\r\n {\r\n type: 'text',\r\n name: 'cdnURL',\r\n message: 'The URL of CDN',\r\n initial: defaultConfig.highcharts.cdnURL.value\r\n },\r\n {\r\n type: 'toggle',\r\n name: 'useNpm',\r\n message: 'Flag to use Highcharts scripts from NPM package',\r\n initial: defaultConfig.highcharts.useNpm.value\r\n },\r\n {\r\n type: 'multiselect',\r\n name: 'coreScripts',\r\n message: 'Available core scripts',\r\n instructions: 'Space: Select specific, A: Select all, Enter: Confirm.',\r\n choices: defaultConfig.highcharts.coreScripts.value\r\n },\r\n {\r\n type: 'multiselect',\r\n name: 'moduleScripts',\r\n message: 'Available module scripts',\r\n instructions: 'Space: Select specific, A: Select all, Enter: Confirm.',\r\n choices: defaultConfig.highcharts.moduleScripts.value\r\n },\r\n {\r\n type: 'multiselect',\r\n name: 'indicatorScripts',\r\n message: 'Available indicator scripts',\r\n instructions: 'Space: Select specific, A: Select all, Enter: Confirm.',\r\n choices: defaultConfig.highcharts.indicatorScripts.value\r\n },\r\n {\r\n type: 'list',\r\n name: 'customScripts',\r\n message: 'Custom scripts',\r\n initial: defaultConfig.highcharts.customScripts.value.join(','),\r\n separator: ','\r\n },\r\n {\r\n type: 'toggle',\r\n name: 'forceFetch',\r\n message: 'Force re-fetch the scripts',\r\n initial: defaultConfig.highcharts.forceFetch.value\r\n },\r\n {\r\n type: 'text',\r\n name: 'cachePath',\r\n message: 'The path to the cache directory',\r\n initial: defaultConfig.highcharts.cachePath.value\r\n }\r\n ],\r\n export: [\r\n {\r\n type: 'select',\r\n name: 'type',\r\n message: 'The default export file type',\r\n hint: `Default: ${defaultConfig.export.type.value}`,\r\n initial: 0,\r\n choices: ['png', 'jpeg', 'pdf', 'svg']\r\n },\r\n {\r\n type: 'select',\r\n name: 'constr',\r\n message: 'The default constructor for Highcharts',\r\n hint: `Default: ${defaultConfig.export.constr.value}`,\r\n initial: 0,\r\n choices: ['chart', 'stockChart', 'mapChart', 'ganttChart']\r\n },\r\n {\r\n type: 'number',\r\n name: 'defaultHeight',\r\n message: 'The default fallback height of the exported chart',\r\n initial: defaultConfig.export.defaultHeight.value\r\n },\r\n {\r\n type: 'number',\r\n name: 'defaultWidth',\r\n message: 'The default fallback width of the exported chart',\r\n initial: defaultConfig.export.defaultWidth.value\r\n },\r\n {\r\n type: 'number',\r\n name: 'defaultScale',\r\n message: 'The default fallback scale of the exported chart',\r\n initial: defaultConfig.export.defaultScale.value,\r\n min: 0.1,\r\n max: 5\r\n },\r\n {\r\n type: 'number',\r\n name: 'rasterizationTimeout',\r\n message: 'The rendering webpage timeout in milliseconds',\r\n initial: defaultConfig.export.rasterizationTimeout.value\r\n }\r\n ],\r\n customLogic: [\r\n {\r\n type: 'toggle',\r\n name: 'allowCodeExecution',\r\n message: 'Enable execution of custom code',\r\n initial: defaultConfig.customLogic.allowCodeExecution.value\r\n },\r\n {\r\n type: 'toggle',\r\n name: 'allowFileResources',\r\n message: 'Enable file resources',\r\n initial: defaultConfig.customLogic.allowFileResources.value\r\n }\r\n ],\r\n server: [\r\n {\r\n type: 'toggle',\r\n name: 'enable',\r\n message: 'Starts the server on 0.0.0.0',\r\n initial: defaultConfig.server.enable.value\r\n },\r\n {\r\n type: 'text',\r\n name: 'host',\r\n message: 'Server hostname',\r\n initial: defaultConfig.server.host.value\r\n },\r\n {\r\n type: 'number',\r\n name: 'port',\r\n message: 'Server port',\r\n initial: defaultConfig.server.port.value\r\n },\r\n {\r\n type: 'toggle',\r\n name: 'benchmarking',\r\n message: 'Enable server benchmarking',\r\n initial: defaultConfig.server.benchmarking.value\r\n },\r\n {\r\n type: 'text',\r\n name: 'proxy.host',\r\n message: 'The host of the proxy server to use',\r\n initial: defaultConfig.server.proxy.host.value\r\n },\r\n {\r\n type: 'number',\r\n name: 'proxy.port',\r\n message: 'The port of the proxy server to use',\r\n initial: defaultConfig.server.proxy.port.value\r\n },\r\n {\r\n type: 'number',\r\n name: 'proxy.timeout',\r\n message: 'The timeout for the proxy server to use',\r\n initial: defaultConfig.server.proxy.timeout.value\r\n },\r\n {\r\n type: 'toggle',\r\n name: 'rateLimiting.enable',\r\n message: 'Enable rate limiting',\r\n initial: defaultConfig.server.rateLimiting.enable.value\r\n },\r\n {\r\n type: 'number',\r\n name: 'rateLimiting.maxRequests',\r\n message: 'The maximum requests allowed per minute',\r\n initial: defaultConfig.server.rateLimiting.maxRequests.value\r\n },\r\n {\r\n type: 'number',\r\n name: 'rateLimiting.window',\r\n message: 'The rate-limiting time window in minutes',\r\n initial: defaultConfig.server.rateLimiting.window.value\r\n },\r\n {\r\n type: 'number',\r\n name: 'rateLimiting.delay',\r\n message:\r\n 'The delay for each successive request before reaching the maximum',\r\n initial: defaultConfig.server.rateLimiting.delay.value\r\n },\r\n {\r\n type: 'toggle',\r\n name: 'rateLimiting.trustProxy',\r\n message: 'Set to true if behind a load balancer',\r\n initial: defaultConfig.server.rateLimiting.trustProxy.value\r\n },\r\n {\r\n type: 'text',\r\n name: 'rateLimiting.skipKey',\r\n message:\r\n 'Allows bypassing the rate limiter when provided with the skipToken argument',\r\n initial: defaultConfig.server.rateLimiting.skipKey.value\r\n },\r\n {\r\n type: 'text',\r\n name: 'rateLimiting.skipToken',\r\n message:\r\n 'Allows bypassing the rate limiter when provided with the skipKey argument',\r\n initial: defaultConfig.server.rateLimiting.skipToken.value\r\n },\r\n {\r\n type: 'toggle',\r\n name: 'ssl.enable',\r\n message: 'Enable SSL protocol',\r\n initial: defaultConfig.server.ssl.enable.value\r\n },\r\n {\r\n type: 'toggle',\r\n name: 'ssl.force',\r\n message: 'Force serving only over HTTPS',\r\n initial: defaultConfig.server.ssl.force.value\r\n },\r\n {\r\n type: 'number',\r\n name: 'ssl.port',\r\n message: 'SSL server port',\r\n initial: defaultConfig.server.ssl.port.value\r\n },\r\n {\r\n type: 'text',\r\n name: 'ssl.certPath',\r\n message: 'The path to find the SSL certificate/key',\r\n initial: defaultConfig.server.ssl.certPath.value\r\n }\r\n ],\r\n pool: [\r\n {\r\n type: 'number',\r\n name: 'minWorkers',\r\n message: 'The initial number of workers to spawn',\r\n initial: defaultConfig.pool.minWorkers.value\r\n },\r\n {\r\n type: 'number',\r\n name: 'maxWorkers',\r\n message: 'The maximum number of workers to spawn',\r\n initial: defaultConfig.pool.maxWorkers.value\r\n },\r\n {\r\n type: 'number',\r\n name: 'workLimit',\r\n message:\r\n 'The pieces of work that can be performed before restarting a Puppeteer process',\r\n initial: defaultConfig.pool.workLimit.value\r\n },\r\n {\r\n type: 'number',\r\n name: 'acquireTimeout',\r\n message: 'The number of milliseconds to wait for acquiring a resource',\r\n initial: defaultConfig.pool.acquireTimeout.value\r\n },\r\n {\r\n type: 'number',\r\n name: 'createTimeout',\r\n message: 'The number of milliseconds to wait for creating a resource',\r\n initial: defaultConfig.pool.createTimeout.value\r\n },\r\n {\r\n type: 'number',\r\n name: 'destroyTimeout',\r\n message: 'The number of milliseconds to wait for destroying a resource',\r\n initial: defaultConfig.pool.destroyTimeout.value\r\n },\r\n {\r\n type: 'number',\r\n name: 'idleTimeout',\r\n message: 'The number of milliseconds after an idle resource is destroyed',\r\n initial: defaultConfig.pool.idleTimeout.value\r\n },\r\n {\r\n type: 'number',\r\n name: 'createRetryInterval',\r\n message:\r\n 'The retry interval in milliseconds after a create process fails',\r\n initial: defaultConfig.pool.createRetryInterval.value\r\n },\r\n {\r\n type: 'number',\r\n name: 'reaperInterval',\r\n message:\r\n 'The reaper interval in milliseconds after triggering the check for idle resources to destroy',\r\n initial: defaultConfig.pool.reaperInterval.value\r\n },\r\n {\r\n type: 'toggle',\r\n name: 'benchmarking',\r\n message: 'Enable benchmarking for a resource pool',\r\n initial: defaultConfig.pool.benchmarking.value\r\n }\r\n ],\r\n logging: [\r\n {\r\n type: 'number',\r\n name: 'level',\r\n message:\r\n 'The log level (0: silent, 1: error, 2: warning, 3: notice, 4: verbose, 5: benchmark)',\r\n initial: defaultConfig.logging.level.value,\r\n round: 0,\r\n min: 0,\r\n max: 5\r\n },\r\n {\r\n type: 'text',\r\n name: 'file',\r\n message:\r\n 'A log file name. Set with --toFile and --logDest to enable file logging',\r\n initial: defaultConfig.logging.file.value\r\n },\r\n {\r\n type: 'text',\r\n name: 'dest',\r\n message: 'The path to a log file when the file logging is enabled',\r\n initial: defaultConfig.logging.dest.value\r\n },\r\n {\r\n type: 'toggle',\r\n name: 'toConsole',\r\n message: 'Enable logging to the console',\r\n initial: defaultConfig.logging.toConsole.value\r\n },\r\n {\r\n type: 'toggle',\r\n name: 'toFile',\r\n message: 'Enables logging to a file',\r\n initial: defaultConfig.logging.toFile.value\r\n }\r\n ],\r\n ui: [\r\n {\r\n type: 'toggle',\r\n name: 'enable',\r\n message: 'Enable UI for the export server',\r\n initial: defaultConfig.ui.enable.value\r\n },\r\n {\r\n type: 'text',\r\n name: 'route',\r\n message: 'A route to attach the UI',\r\n initial: defaultConfig.ui.route.value\r\n }\r\n ],\r\n other: [\r\n {\r\n type: 'text',\r\n name: 'nodeEnv',\r\n message: 'The type of Node.js environment',\r\n initial: defaultConfig.other.nodeEnv.value\r\n },\r\n {\r\n type: 'toggle',\r\n name: 'listenToProcessExits',\r\n message: 'Set to false to skip attaching process.exit handlers',\r\n initial: defaultConfig.other.listenToProcessExits.value\r\n },\r\n {\r\n type: 'toggle',\r\n name: 'noLogo',\r\n message: 'Skip printing the logo on startup. Replaced by simple text',\r\n initial: defaultConfig.other.noLogo.value\r\n },\r\n {\r\n type: 'toggle',\r\n name: 'hardResetPage',\r\n message: 'Decides if the page content should be reset entirely',\r\n initial: defaultConfig.other.hardResetPage.value\r\n },\r\n {\r\n type: 'toggle',\r\n name: 'browserShellMode',\r\n message: 'Decides if the browser runs in the shell mode',\r\n initial: defaultConfig.other.browserShellMode.value\r\n }\r\n ],\r\n debug: [\r\n {\r\n type: 'toggle',\r\n name: 'enable',\r\n message: 'Enables debug mode for the browser instance',\r\n initial: defaultConfig.debug.enable.value\r\n },\r\n {\r\n type: 'toggle',\r\n name: 'headless',\r\n message: 'The mode setting for the browser',\r\n initial: defaultConfig.debug.headless.value\r\n },\r\n {\r\n type: 'toggle',\r\n name: 'devtools',\r\n message: 'The DevTools for the headful browser',\r\n initial: defaultConfig.debug.devtools.value\r\n },\r\n {\r\n type: 'toggle',\r\n name: 'listenToConsole',\r\n message: 'The event listener for console messages from the browser',\r\n initial: defaultConfig.debug.listenToConsole.value\r\n },\r\n {\r\n type: 'toggle',\r\n name: 'dumpio',\r\n message: 'Redirects the browser stdout and stderr to NodeJS process',\r\n initial: defaultConfig.debug.dumpio.value\r\n },\r\n {\r\n type: 'number',\r\n name: 'slowMo',\r\n message: 'Puppeteer operations slow down in milliseconds',\r\n initial: defaultConfig.debug.slowMo.value\r\n },\r\n {\r\n type: 'number',\r\n name: 'debuggingPort',\r\n message: 'The port number for debugging',\r\n initial: defaultConfig.debug.debuggingPort.value\r\n }\r\n ]\r\n};\r\n\r\n// Absolute props that, in case of merging recursively, need to be force merged\r\nexport const absoluteProps = [\r\n 'options',\r\n 'globalOptions',\r\n 'themeOptions',\r\n 'resources',\r\n 'payload'\r\n];\r\n\r\n// Argument nesting level of all export server options\r\nexport const nestedArgs = {};\r\n\r\n/**\r\n * Recursively creates a chain of nested arguments from an object.\r\n *\r\n * @param {Object} obj - The object containing nested arguments.\r\n * @param {string} propChain - The current chain of nested properties\r\n * (used internally during recursion).\r\n */\r\nconst createNestedArgs = (obj, propChain = '') => {\r\n Object.keys(obj).forEach((k) => {\r\n if (!['puppeteer', 'highcharts'].includes(k)) {\r\n const entry = obj[k];\r\n if (typeof entry.value === 'undefined') {\r\n // Go deeper in the nested arguments\r\n createNestedArgs(entry, `${propChain}.${k}`);\r\n } else {\r\n // Create the chain of nested arguments\r\n nestedArgs[entry.cliName || k] = `${propChain}.${k}`.substring(1);\r\n\r\n // Support for the legacy, PhantomJS properties names\r\n if (entry.legacyName !== undefined) {\r\n nestedArgs[entry.legacyName] = `${propChain}.${k}`.substring(1);\r\n }\r\n }\r\n }\r\n });\r\n};\r\n\r\ncreateNestedArgs(defaultConfig);\r\n","/**\r\n * @fileoverview\r\n * This file is responsible for parsing the environment variables with the 'zod'\r\n * library. The parsed environment variables are then exported to be used\r\n * in the application as \"envs\". We should not use process.env directly\r\n * in the application as these would not be parsed properly.\r\n *\r\n * The environment variables are parsed and validated only once when\r\n * the application starts. We should write a custom validator or a transformer\r\n * for each of the options.\r\n */\r\n\r\nimport dotenv from 'dotenv';\r\nimport { z } from 'zod';\r\n\r\nimport { scriptsNames } from './schemas/config.js';\r\n\r\n// Load .env into environment variables\r\ndotenv.config();\r\n\r\n// Object with custom validators and transformers, to avoid repetition\r\n// in the Config object\r\nconst v = {\r\n // Splits string value into elements in an array, trims every element, checks\r\n // if an array is correct, if it is empty, and if it is, returns undefined\r\n array: (filterArray) =>\r\n z\r\n .string()\r\n .transform((value) =>\r\n value\r\n .split(',')\r\n .map((value) => value.trim())\r\n .filter((value) => filterArray.includes(value))\r\n )\r\n .transform((value) => (value.length ? value : undefined)),\r\n\r\n // Allows only true, false and correctly parse the value to boolean\r\n // or no value in which case the returned value will be undefined\r\n boolean: () =>\r\n z\r\n .enum(['true', 'false', ''])\r\n .transform((value) => (value !== '' ? value === 'true' : undefined)),\r\n\r\n // Allows passed values or no value in which case the returned value will\r\n // be undefined\r\n enum: (values) =>\r\n z\r\n .enum([...values, ''])\r\n .transform((value) => (value !== '' ? value : undefined)),\r\n\r\n // Trims the string value and checks if it is empty or contains stringified\r\n // values such as false, undefined, null, NaN, if it does, returns undefined\r\n string: () =>\r\n z\r\n .string()\r\n .trim()\r\n .refine(\r\n (value) =>\r\n !['false', 'undefined', 'null', 'NaN'].includes(value) ||\r\n value === '',\r\n (value) => ({\r\n message: `The string contains forbidden values, received '${value}'`\r\n })\r\n )\r\n .transform((value) => (value !== '' ? value : undefined)),\r\n\r\n // Checks if the string is a valid path directory (path format)\r\n path: () =>\r\n z\r\n .string()\r\n .trim()\r\n .refine(\r\n (value) => {\r\n // Simplified regex to match both absolute and relative paths\r\n return /^(\\.\\/|\\.\\.\\/|\\/|[a-zA-Z]:\\\\|[a-zA-Z]:\\/)?((?:[\\w-]+)[\\\\/]?)+$/.test(\r\n value\r\n );\r\n },\r\n {},\r\n {\r\n message: 'The string is an invalid path directory string.'\r\n }\r\n ),\r\n\r\n // Allows positive numbers or no value in which case the returned value will\r\n // be undefined\r\n positiveNum: () =>\r\n z\r\n .string()\r\n .trim()\r\n .refine(\r\n (value) =>\r\n value === '' || (!isNaN(parseFloat(value)) && parseFloat(value) > 0),\r\n (value) => ({\r\n message: `The value must be numeric and positive, received '${value}'`\r\n })\r\n )\r\n .transform((value) => (value !== '' ? parseFloat(value) : undefined)),\r\n\r\n // Allows non-negative numbers or no value in which case the returned value\r\n // will be undefined\r\n nonNegativeNum: () =>\r\n z\r\n .string()\r\n .trim()\r\n .refine(\r\n (value) =>\r\n value === '' || (!isNaN(parseFloat(value)) && parseFloat(value) >= 0),\r\n (value) => ({\r\n message: `The value must be numeric and non-negative, received '${value}'`\r\n })\r\n )\r\n .transform((value) => (value !== '' ? parseFloat(value) : undefined))\r\n};\r\n\r\nexport const Config = z.object({\r\n // puppeteer\r\n PUPPETEER_TEMP_DIR: v.path(),\r\n\r\n // highcharts\r\n HIGHCHARTS_VERSION: z\r\n .string()\r\n .trim()\r\n .refine(\r\n (value) => /^(latest|\\d+(\\.\\d+){0,2})$/.test(value) || value === '',\r\n (value) => ({\r\n message: `HIGHCHARTS_VERSION must be 'latest', a major version, or in the form XX.YY.ZZ, received '${value}'`\r\n })\r\n )\r\n .transform((value) => (value !== '' ? value : undefined)),\r\n HIGHCHARTS_CDN_URL: z\r\n .string()\r\n .trim()\r\n .refine(\r\n (value) =>\r\n value.startsWith('https://') ||\r\n value.startsWith('http://') ||\r\n value === '',\r\n (value) => ({\r\n message: `Invalid value for HIGHCHARTS_CDN_URL. It should start with http:// or https://, received '${value}'`\r\n })\r\n )\r\n .transform((value) => (value !== '' ? value : undefined)),\r\n HIGHCHARTS_USE_NPM: v.boolean(),\r\n HIGHCHARTS_CORE_SCRIPTS: v.array(scriptsNames.core),\r\n HIGHCHARTS_MODULE_SCRIPTS: v.array(scriptsNames.modules),\r\n HIGHCHARTS_INDICATOR_SCRIPTS: v.array(scriptsNames.indicators),\r\n HIGHCHARTS_FORCE_FETCH: v.boolean(),\r\n HIGHCHARTS_CACHE_PATH: v.string(),\r\n HIGHCHARTS_ADMIN_TOKEN: v.string(),\r\n\r\n // export\r\n EXPORT_TYPE: v.enum(['jpeg', 'png', 'pdf', 'svg']),\r\n EXPORT_CONSTR: v.enum(['chart', 'stockChart', 'mapChart', 'ganttChart']),\r\n EXPORT_DEFAULT_HEIGHT: v.positiveNum(),\r\n EXPORT_DEFAULT_WIDTH: v.positiveNum(),\r\n EXPORT_DEFAULT_SCALE: v.positiveNum(),\r\n EXPORT_RASTERIZATION_TIMEOUT: v.nonNegativeNum(),\r\n\r\n // custom\r\n CUSTOM_LOGIC_ALLOW_CODE_EXECUTION: v.boolean(),\r\n CUSTOM_LOGIC_ALLOW_FILE_RESOURCES: v.boolean(),\r\n\r\n // server\r\n SERVER_ENABLE: v.boolean(),\r\n SERVER_HOST: v.string(),\r\n SERVER_PORT: v.positiveNum(),\r\n SERVER_MAX_UPLOAD_SIZE: v.positiveNum(),\r\n SERVER_BENCHMARKING: v.boolean(),\r\n\r\n // server proxy\r\n SERVER_PROXY_HOST: v.string(),\r\n SERVER_PROXY_PORT: v.positiveNum(),\r\n SERVER_PROXY_USERNAME: v.string(),\r\n SERVER_PROXY_PASSWORD: v.string(),\r\n SERVER_PROXY_TIMEOUT: v.nonNegativeNum(),\r\n\r\n // server rate limiting\r\n SERVER_RATE_LIMITING_ENABLE: v.boolean(),\r\n SERVER_RATE_LIMITING_MAX_REQUESTS: v.nonNegativeNum(),\r\n SERVER_RATE_LIMITING_WINDOW: v.nonNegativeNum(),\r\n SERVER_RATE_LIMITING_DELAY: v.nonNegativeNum(),\r\n SERVER_RATE_LIMITING_TRUST_PROXY: v.boolean(),\r\n SERVER_RATE_LIMITING_SKIP_KEY: v.string(),\r\n SERVER_RATE_LIMITING_SKIP_TOKEN: v.string(),\r\n\r\n // server ssl\r\n SERVER_SSL_ENABLE: v.boolean(),\r\n SERVER_SSL_FORCE: v.boolean(),\r\n SERVER_SSL_PORT: v.positiveNum(),\r\n SERVER_SSL_CERT_PATH: v.string(),\r\n\r\n // pool\r\n POOL_MIN_WORKERS: v.nonNegativeNum(),\r\n POOL_MAX_WORKERS: v.nonNegativeNum(),\r\n POOL_WORK_LIMIT: v.positiveNum(),\r\n POOL_ACQUIRE_TIMEOUT: v.nonNegativeNum(),\r\n POOL_CREATE_TIMEOUT: v.nonNegativeNum(),\r\n POOL_DESTROY_TIMEOUT: v.nonNegativeNum(),\r\n POOL_IDLE_TIMEOUT: v.nonNegativeNum(),\r\n POOL_CREATE_RETRY_INTERVAL: v.nonNegativeNum(),\r\n POOL_REAPER_INTERVAL: v.nonNegativeNum(),\r\n POOL_BENCHMARKING: v.boolean(),\r\n\r\n // logger\r\n LOGGING_LEVEL: z\r\n .string()\r\n .trim()\r\n .refine(\r\n (value) =>\r\n value === '' ||\r\n (!isNaN(parseFloat(value)) &&\r\n parseFloat(value) >= 0 &&\r\n parseFloat(value) <= 5),\r\n (value) => ({\r\n message: `Invalid value for LOGGING_LEVEL. We only accept values from 0 to 5 as logging levels, received '${value}'`\r\n })\r\n )\r\n .transform((value) => (value !== '' ? parseFloat(value) : undefined)),\r\n LOGGING_FILE: v.string(),\r\n LOGGING_DEST: v.string(),\r\n LOGGING_TO_CONSOLE: v.boolean(),\r\n LOGGING_TO_FILE: v.boolean(),\r\n\r\n // ui\r\n UI_ENABLE: v.boolean(),\r\n UI_ROUTE: v.string(),\r\n\r\n // other\r\n OTHER_NODE_ENV: v.enum(['development', 'production', 'test']),\r\n OTHER_LISTEN_TO_PROCESS_EXITS: v.boolean(),\r\n OTHER_NO_LOGO: v.boolean(),\r\n OTHER_HARD_RESET_PAGE: v.boolean(),\r\n OTHER_BROWSER_SHELL_MODE: v.boolean(),\r\n OTHER_ALLOW_XLINK: v.boolean(),\r\n\r\n // debugger\r\n DEBUG_ENABLE: v.boolean(),\r\n DEBUG_HEADLESS: v.boolean(),\r\n DEBUG_DEVTOOLS: v.boolean(),\r\n DEBUG_LISTEN_TO_CONSOLE: v.boolean(),\r\n DEBUG_DUMPIO: v.boolean(),\r\n DEBUG_SLOW_MO: v.nonNegativeNum(),\r\n DEBUG_DEBUGGING_PORT: v.positiveNum()\r\n});\r\n\r\nexport const envs = Config.partial().parse(process.env);\r\n","/*******************************************************************************\r\n\r\nHighcharts Export Server\r\n\r\nCopyright (c) 2016-2024, Highsoft\r\n\r\nLicenced under the MIT licence.\r\n\r\nAdditionally a valid Highcharts license is required for use.\r\n\r\nSee LICENSE file in root for details.\r\n\r\n*******************************************************************************/\r\n\r\nimport { appendFile, existsSync, mkdirSync } from 'fs';\r\n\r\n// The available colors\r\nconst colors = ['red', 'yellow', 'blue', 'gray', 'green'];\r\n\r\n// The default logging config\r\nlet logging = {\r\n // Flags for logging status\r\n toConsole: true,\r\n toFile: false,\r\n pathCreated: false,\r\n // Log levels\r\n levelsDesc: [\r\n {\r\n title: 'error',\r\n color: colors[0]\r\n },\r\n {\r\n title: 'warning',\r\n color: colors[1]\r\n },\r\n {\r\n title: 'notice',\r\n color: colors[2]\r\n },\r\n {\r\n title: 'verbose',\r\n color: colors[3]\r\n },\r\n {\r\n title: 'benchmark',\r\n color: colors[4]\r\n }\r\n ],\r\n // Log listeners\r\n listeners: []\r\n};\r\n\r\n/**\r\n * Logs the provided texts to a file, if file logging is enabled. It creates\r\n * the necessary directory structure if not already created and appends the\r\n * content, including an optional prefix, to the specified log file.\r\n *\r\n * @param {string[]} texts - An array of texts to be logged.\r\n * @param {string} prefix - An optional prefix to be added to each log entry.\r\n */\r\nconst logToFile = (texts, prefix) => {\r\n if (!logging.pathCreated) {\r\n // Create if does not exist\r\n !existsSync(logging.dest) && mkdirSync(logging.dest);\r\n\r\n // We now assume the path is available, e.g. it's the responsibility\r\n // of the user to create the path with the correct access rights.\r\n logging.pathCreated = true;\r\n }\r\n\r\n // Add the content to a file\r\n appendFile(\r\n `${logging.dest}${logging.file}`,\r\n [prefix].concat(texts).join(' ') + '\\n',\r\n (error) => {\r\n if (error) {\r\n console.log(`[logger] Unable to write to log file: ${error}`);\r\n logging.toFile = false;\r\n }\r\n }\r\n );\r\n};\r\n\r\n/**\r\n * 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 { levelsDesc, level } = logging;\r\n\r\n // Check if log level is within a correct range or is a benchmark log\r\n if (\r\n newLevel !== 5 &&\r\n (newLevel === 0 || newLevel > level || level > levelsDesc.length)\r\n ) {\r\n return;\r\n }\r\n\r\n // Get rid of the GMT text information\r\n const newDate = new Date().toString().split('(')[0].trim();\r\n\r\n // Create a message's prefix\r\n const prefix = `${newDate} [${levelsDesc[newLevel - 1].title}] -`;\r\n\r\n // Call available log listeners\r\n logging.listeners.forEach((fn) => {\r\n fn(prefix, texts.join(' '));\r\n });\r\n\r\n // Log to console\r\n if (logging.toConsole) {\r\n console.log.apply(\r\n undefined,\r\n [prefix.toString()[logging.levelsDesc[newLevel - 1].color]].concat(texts)\r\n );\r\n }\r\n\r\n // Log to file\r\n if (logging.toFile) {\r\n logToFile(texts, prefix);\r\n }\r\n};\r\n\r\n/**\r\n * Logs an error message with its stack trace. Optionally, a custom message\r\n * can be provided.\r\n *\r\n * @param {number} level - The log level.\r\n * @param {Error} error - The error object.\r\n * @param {string} customMessage - An optional custom message to be logged along\r\n * with the error.\r\n */\r\nexport const logWithStack = (newLevel, error, customMessage) => {\r\n // Get the main message\r\n const mainMessage = customMessage || error.message;\r\n\r\n // Current logging options\r\n const { level, levelsDesc } = logging;\r\n\r\n // Check if log level is within a correct range\r\n if (newLevel === 0 || newLevel > level || level > levelsDesc.length) {\r\n return;\r\n }\r\n\r\n // Get rid of the GMT text information\r\n const newDate = new Date().toString().split('(')[0].trim();\r\n\r\n // Create a message's prefix\r\n const prefix = `${newDate} [${levelsDesc[newLevel - 1].title}] -`;\r\n\r\n // If the customMessage exists, we want to display the whole stack message\r\n const stackMessage =\r\n error.message !== error.stackMessage || error.stackMessage === undefined\r\n ? error.stack\r\n : error.stack.split('\\n').slice(1).join('\\n');\r\n\r\n // Combine custom message or error message with error stack message\r\n const texts = [mainMessage, '\\n', stackMessage];\r\n\r\n // Log to console\r\n if (logging.toConsole) {\r\n console.log.apply(\r\n undefined,\r\n [prefix.toString()[logging.levelsDesc[newLevel - 1].color]].concat([\r\n mainMessage[colors[newLevel - 1]],\r\n '\\n',\r\n stackMessage\r\n ])\r\n );\r\n }\r\n\r\n // Call available log listeners\r\n logging.listeners.forEach((fn) => {\r\n fn(prefix, texts.join(' '));\r\n });\r\n\r\n // Log to file\r\n if (logging.toFile) {\r\n logToFile(texts, prefix);\r\n }\r\n};\r\n\r\n/**\r\n * Sets the log level to the specified value. Log levels are (0 = no logging,\r\n * 1 = error, 2 = warning, 3 = notice, 4 = verbose or 5 = benchmark)\r\n *\r\n * @param {number} newLevel - The new log level to be set.\r\n */\r\nexport const setLogLevel = (newLevel) => {\r\n if (newLevel >= 0 && newLevel <= logging.levelsDesc.length) {\r\n logging.level = newLevel;\r\n }\r\n};\r\n\r\n/**\r\n * Enables file logging with the specified destination and log file.\r\n *\r\n * @param {string} logDest - The destination path for log files.\r\n * @param {string} logFile - The log file name.\r\n */\r\nexport const enableFileLogging = (logDest, logFile) => {\r\n // Update logging options\r\n logging = {\r\n ...logging,\r\n dest: logDest || logging.dest,\r\n file: logFile || logging.file,\r\n toFile: true\r\n };\r\n\r\n if (logging.dest.length === 0) {\r\n return log(1, '[logger] File logging initialization: no path supplied.');\r\n }\r\n\r\n if (!logging.dest.endsWith('/')) {\r\n logging.dest += '/';\r\n }\r\n};\r\n\r\n/**\r\n * Initializes logging with the specified logging configuration.\r\n *\r\n * @param {Object} loggingOptions - The logging configuration object.\r\n */\r\nexport const initLogging = (loggingOptions) => {\r\n // Set all the logging options on our logging module object\r\n for (const [key, value] of Object.entries(loggingOptions)) {\r\n logging[key] = value;\r\n }\r\n\r\n // Set the log level\r\n setLogLevel(loggingOptions && parseInt(loggingOptions.level));\r\n\r\n // Set the log file path and name\r\n if (loggingOptions && loggingOptions.dest && loggingOptions.toFile) {\r\n enableFileLogging(\r\n loggingOptions.dest,\r\n loggingOptions.file || 'highcharts-export-server.log'\r\n );\r\n }\r\n};\r\n\r\n/**\r\n * Adds a listener function to the logging system.\r\n *\r\n * @param {function} fn - The listener function to be added.\r\n */\r\nexport const listen = (fn) => {\r\n logging.listeners.push(fn);\r\n};\r\n\r\nexport default {\r\n log,\r\n logWithStack,\r\n setLogLevel,\r\n enableFileLogging,\r\n initLogging,\r\n listen\r\n};\r\n","/*******************************************************************************\r\n\r\nHighcharts Export Server\r\n\r\nCopyright (c) 2016-2024, Highsoft\r\n\r\nLicenced under the MIT licence.\r\n\r\nAdditionally a valid Highcharts license is required for use.\r\n\r\nSee LICENSE file in root for details.\r\n\r\n*******************************************************************************/\r\n\r\nimport { readFileSync } from 'fs';\r\nimport { createRequire } from 'module';\r\nimport { dirname, join } from 'path';\r\nimport { fileURLToPath } from 'url';\r\n\r\nimport { defaultConfig } from '../lib/schemas/config.js';\r\nimport { log, logWithStack } from './logger.js';\r\n\r\nconst MAX_BACKOFF_ATTEMPTS = 6;\r\n\r\n// The highcharts dependency directory path\r\nexport const __highchartsDir = dirname(\r\n createRequire(import.meta.url).resolve('highcharts/package.json')\r\n);\r\n\r\nexport const __dirname = fileURLToPath(new URL('../.', import.meta.url));\r\n\r\n/**\r\n * Clears and standardizes text by replacing multiple consecutive whitespace\r\n * characters with a single space and trimming any leading or trailing\r\n * whitespace.\r\n *\r\n * @param {string} text - The input text to be cleared.\r\n * @param {RegExp} [rule=/\\s\\s+/g] - The regular expression rule to match\r\n * multiple consecutive whitespace characters.\r\n * @param {string} [replacer=' '] - The string used to replace multiple\r\n * consecutive whitespace characters.\r\n *\r\n * @returns {string} - The cleared and standardized text.\r\n */\r\nexport const clearText = (text, rule = /\\s\\s+/g, replacer = ' ') =>\r\n text.replaceAll(rule, replacer).trim();\r\n\r\n/**\r\n * Implements an exponential backoff strategy for retrying a function until\r\n * a certain number of attempts are reached.\r\n *\r\n * @param {Function} fn - The function to be retried.\r\n * @param {number} [attempt=0] - The current attempt number.\r\n * @param {...any} args - Arguments to be passed to the function.\r\n *\r\n * @returns {Promise} - A promise that resolves to the result of the function\r\n * if successful.\r\n *\r\n * @throws {Error} - Throws an error if the maximum number of attempts\r\n * is reached.\r\n */\r\nexport const expBackoff = async (fn, attempt = 0, ...args) => {\r\n try {\r\n // Try to call the function\r\n return await fn(...args);\r\n } catch (error) {\r\n // Calculate delay in ms\r\n const delayInMs = 2 ** attempt * 1000;\r\n\r\n // If the attempt exceeds the maximum attempts of reapeat, throw an error\r\n if (++attempt >= MAX_BACKOFF_ATTEMPTS) {\r\n throw error;\r\n }\r\n\r\n // Wait given amount of time\r\n await new Promise((response) => setTimeout(response, delayInMs));\r\n log(\r\n 3,\r\n `[pool] Waited ${delayInMs}ms until next call for the resource id: ${args[0]}.`\r\n );\r\n\r\n // Try again\r\n return expBackoff(fn, attempt, ...args);\r\n }\r\n};\r\n\r\n/**\r\n * Fixes the export type based on MIME types and file extensions.\r\n *\r\n * @param {string} type - The original export type.\r\n * @param {string} outfile - The file path or name.\r\n *\r\n * @returns {string} - The corrected export type.\r\n */\r\nexport const fixType = (type, outfile) => {\r\n // MIME types\r\n const mimeTypes = {\r\n 'image/png': 'png',\r\n 'image/jpeg': 'jpeg',\r\n 'application/pdf': 'pdf',\r\n 'image/svg+xml': 'svg'\r\n };\r\n\r\n // Formats\r\n const formats = ['png', 'jpeg', 'pdf', 'svg'];\r\n\r\n // Check if type and outfile's extensions are the same\r\n if (outfile) {\r\n const outType = outfile.split('.').pop();\r\n\r\n if (outType === 'jpg') {\r\n type = 'jpeg';\r\n } else if (formats.includes(outType) && type !== outType) {\r\n type = outType;\r\n }\r\n }\r\n\r\n // Return a correct type\r\n return mimeTypes[type] || formats.find((t) => t === type) || 'png';\r\n};\r\n\r\n/**\r\n * Handles and validates resources for export.\r\n *\r\n * @param {Object|string} resources - The resources to be handled. Can be either\r\n * a JSON object, stringified JSON or a path to a JSON file.\r\n * @param {boolean} allowFileResources - Whether to allow loading resources from\r\n * files.\r\n *\r\n * @returns {Object|undefined} - The handled resources or undefined if no valid\r\n * resources are found.\r\n */\r\nexport const handleResources = (resources = false, allowFileResources) => {\r\n const allowedProps = ['js', 'css', 'files'];\r\n\r\n let handledResources = resources;\r\n let correctResources = false;\r\n\r\n // Try to load resources from a file\r\n if (allowFileResources && resources.endsWith('.json')) {\r\n try {\r\n handledResources = isCorrectJSON(readFileSync(resources, 'utf8'));\r\n } catch (error) {\r\n return logWithStack(2, error, `[cli] No resources found.`);\r\n }\r\n } else {\r\n // Try to get JSON\r\n handledResources = isCorrectJSON(resources);\r\n\r\n // Get rid of the files section\r\n if (handledResources && !allowFileResources) {\r\n delete handledResources.files;\r\n }\r\n }\r\n\r\n // Filter from unnecessary properties\r\n for (const propName in handledResources) {\r\n if (!allowedProps.includes(propName)) {\r\n delete handledResources[propName];\r\n } else if (!correctResources) {\r\n correctResources = true;\r\n }\r\n }\r\n\r\n // Check if at least one of allowed properties is present\r\n if (!correctResources) {\r\n return log(3, `[cli] No resources found.`);\r\n }\r\n\r\n // Handle files section\r\n if (handledResources.files) {\r\n handledResources.files = handledResources.files.map((item) => item.trim());\r\n if (!handledResources.files || handledResources.files.length <= 0) {\r\n delete handledResources.files;\r\n }\r\n }\r\n\r\n // Return resources\r\n return handledResources;\r\n};\r\n\r\n/**\r\n * Validates and parses JSON data. Checks if provided data is or can\r\n * be a correct JSON. If a primitive is provided, it is stringified and returned.\r\n *\r\n * @param {Object|string} data - The JSON data to be validated and parsed.\r\n * @param {boolean} toString - Whether to return a stringified representation\r\n * of the parsed JSON.\r\n *\r\n * @returns {Object|string|boolean} - The parsed JSON object, stringified JSON,\r\n * or false if validation fails.\r\n */\r\nexport function isCorrectJSON(data, toString) {\r\n try {\r\n // Get the string representation if not already before parsing\r\n const parsedData = JSON.parse(\r\n typeof data !== 'string' ? JSON.stringify(data) : data\r\n );\r\n\r\n // Return a stringified representation of a JSON if required\r\n if (typeof parsedData !== 'string' && toString) {\r\n return JSON.stringify(parsedData);\r\n }\r\n\r\n // Return a JSON\r\n return parsedData;\r\n } catch {\r\n return false;\r\n }\r\n}\r\n\r\n/**\r\n * Checks if the given item is an object.\r\n *\r\n * @param {any} item - The item to be checked.\r\n *\r\n * @returns {boolean} - True if the item is an object, false otherwise.\r\n */\r\nexport const isObject = (item) =>\r\n typeof item === 'object' && !Array.isArray(item) && item !== null;\r\n\r\n/**\r\n * Checks if the given object is empty.\r\n *\r\n * @param {Object} item - The object to be checked.\r\n *\r\n * @returns {boolean} - True if the object is empty, false otherwise.\r\n */\r\nexport const isObjectEmpty = (item) =>\r\n typeof item === 'object' &&\r\n !Array.isArray(item) &&\r\n item !== null &&\r\n Object.keys(item).length === 0;\r\n\r\n/**\r\n * Checks if a private IP range URL is found in the given string.\r\n *\r\n * @param {string} item - The string to be checked for a private IP range URL.\r\n *\r\n * @returns {boolean} - True if a private IP range URL is found, false\r\n * otherwise.\r\n */\r\nexport const isPrivateRangeUrlFound = (item) => {\r\n const regexPatterns = [\r\n /xlink:href=\"(?:http:\\/\\/|https:\\/\\/)?localhost\\b/,\r\n /xlink:href=\"(?:http:\\/\\/|https:\\/\\/)?10\\.\\d{1,3}\\.\\d{1,3}\\.\\d{1,3}\\b/,\r\n /xlink:href=\"(?:http:\\/\\/|https:\\/\\/)?127\\.\\d{1,3}\\.\\d{1,3}\\.\\d{1,3}\\b/,\r\n /xlink:href=\"(?:http:\\/\\/|https:\\/\\/)?172\\.(1[6-9]|2[0-9]|3[0-1])\\.\\d{1,3}\\.\\d{1,3}\\b/,\r\n /xlink:href=\"(?:http:\\/\\/|https:\\/\\/)?192\\.168\\.\\d{1,3}\\.\\d{1,3}\\b/\r\n ];\r\n\r\n return regexPatterns.some((pattern) => pattern.test(item));\r\n};\r\n\r\n/**\r\n * Creates a deep copy of the given object or array.\r\n *\r\n * @param {Object|Array} obj - The object or array to be deeply copied.\r\n *\r\n * @returns {Object|Array} - The deep copy of the provided object or array.\r\n */\r\nexport const deepCopy = (obj) => {\r\n if (obj === null || typeof obj !== 'object') {\r\n return obj;\r\n }\r\n\r\n const copy = Array.isArray(obj) ? [] : {};\r\n\r\n for (const key in obj) {\r\n if (Object.prototype.hasOwnProperty.call(obj, key)) {\r\n copy[key] = deepCopy(obj[key]);\r\n }\r\n }\r\n\r\n return copy;\r\n};\r\n\r\n/**\r\n * Converts the provided options object to a JSON-formatted string with the\r\n * option to preserve functions.\r\n *\r\n * @param {Object} options - The options object to be converted to a string.\r\n * @param {boolean} allowFunctions - If set to true, functions are preserved\r\n * in the output.\r\n *\r\n * @returns {string} - The JSON-formatted string representing the options.\r\n */\r\nexport const optionsStringify = (options, allowFunctions) => {\r\n const replacerCallback = (name, value) => {\r\n if (typeof value === 'string') {\r\n value = value.trim();\r\n\r\n // If allowFunctions is set to true, preserve functions\r\n if (\r\n (value.startsWith('function(') || value.startsWith('function (')) &&\r\n value.endsWith('}')\r\n ) {\r\n value = allowFunctions\r\n ? `EXP_FUN${(value + '').replaceAll(/\\n|\\t|\\r/g, ' ')}EXP_FUN`\r\n : undefined;\r\n }\r\n }\r\n\r\n return typeof value === 'function'\r\n ? `EXP_FUN${(value + '').replaceAll(/\\n|\\t|\\r/g, ' ')}EXP_FUN`\r\n : value;\r\n };\r\n\r\n // Stringify options and if required, replace special functions marks\r\n return JSON.stringify(options, replacerCallback).replaceAll(\r\n /\"EXP_FUN|EXP_FUN\"/g,\r\n ''\r\n );\r\n};\r\n\r\n/**\r\n * Prints the Highcharts Export Server logo and version information.\r\n *\r\n * @param {boolean} noLogo - If true, only prints version information without\r\n * the logo.\r\n */\r\nexport const printLogo = (noLogo) => {\r\n // Get package version either from env or from package.json\r\n const packageVersion = JSON.parse(\r\n readFileSync(join(__dirname, 'package.json'))\r\n ).version;\r\n\r\n // Print text only\r\n if (noLogo) {\r\n console.log(`Starting Highcharts Export Server v${packageVersion}...`);\r\n return;\r\n }\r\n\r\n // Print the logo\r\n console.log(\r\n readFileSync(__dirname + '/msg/startup.msg').toString().bold.yellow,\r\n `v${packageVersion}\\n`.bold\r\n );\r\n};\r\n\r\n/**\r\n * Prints the usage information for CLI arguments. If required, it can list\r\n * properties recursively\r\n */\r\nexport function printUsage() {\r\n const pad = 48;\r\n const readme = 'https://github.com/highcharts/node-export-server#readme';\r\n\r\n // Display readme information\r\n console.log(\r\n '\\nUsage of CLI arguments:'.bold,\r\n '\\n------',\r\n `\\nFor more detailed information, visit the readme at: ${readme.bold.yellow}.`\r\n );\r\n\r\n const cycleCategories = (options) => {\r\n for (const [name, option] of Object.entries(options)) {\r\n // If category has more levels, go further\r\n if (!Object.prototype.hasOwnProperty.call(option, 'value')) {\r\n cycleCategories(option);\r\n } else {\r\n let descName = ` --${option.cliName || name} ${\r\n ('<' + option.type + '>').green\r\n } `;\r\n if (descName.length < pad) {\r\n for (let i = descName.length; i < pad; i++) {\r\n descName += '.';\r\n }\r\n }\r\n\r\n // Display correctly aligned messages\r\n console.log(\r\n descName,\r\n option.description,\r\n `[Default: ${option.value.toString().bold}]`.blue\r\n );\r\n }\r\n }\r\n };\r\n\r\n // Cycle through options of each categories and display the usage info\r\n Object.keys(defaultConfig).forEach((category) => {\r\n // Only puppeteer and highcharts categories cannot be configured through CLI\r\n if (!['puppeteer', 'highcharts'].includes(category)) {\r\n console.log(`\\n${category.toUpperCase()}`.red);\r\n cycleCategories(defaultConfig[category]);\r\n }\r\n });\r\n console.log('\\n');\r\n}\r\n\r\n/**\r\n * Rounds a number to the specified precision.\r\n *\r\n * @param {number} value - The number to be rounded.\r\n * @param {number} precision - The number of decimal places to round to.\r\n *\r\n * @returns {number} - The rounded number.\r\n */\r\nexport const roundNumber = (value, precision = 1) => {\r\n const multiplier = Math.pow(10, precision || 0);\r\n return Math.round(+value * multiplier) / multiplier;\r\n};\r\n\r\n/**\r\n * Converts a value to a boolean.\r\n *\r\n * @param {any} item - The value to be converted to a boolean.\r\n *\r\n * @returns {boolean} - The boolean representation of the input value.\r\n */\r\nexport const toBoolean = (item) =>\r\n ['false', 'undefined', 'null', 'NaN', '0', ''].includes(item)\r\n ? false\r\n : !!item;\r\n\r\n/**\r\n * Wraps custom code to execute it safely.\r\n *\r\n * @param {string} customCode - The custom code to be wrapped.\r\n * @param {boolean} allowFileResources - Flag to allow loading code from a file.\r\n *\r\n * @returns {string|boolean} - The wrapped custom code or false if wrapping\r\n * fails.\r\n */\r\nexport const wrapAround = (customCode, allowFileResources) => {\r\n if (customCode && typeof customCode === 'string') {\r\n customCode = customCode.trim();\r\n\r\n if (customCode.endsWith('.js')) {\r\n return allowFileResources\r\n ? wrapAround(readFileSync(customCode, 'utf8'))\r\n : false;\r\n } else if (\r\n customCode.startsWith('function()') ||\r\n customCode.startsWith('function ()') ||\r\n customCode.startsWith('()=>') ||\r\n customCode.startsWith('() =>')\r\n ) {\r\n return `(${customCode})()`;\r\n }\r\n return customCode.replace(/;$/, '');\r\n }\r\n};\r\n\r\n/**\r\n * Utility to measure elapsed time using the Node.js process.hrtime() method.\r\n *\r\n * @returns {function(): number} - A function to calculate the elapsed time\r\n * in milliseconds.\r\n */\r\nexport const measureTime = () => {\r\n const start = process.hrtime.bigint();\r\n return () => Number(process.hrtime.bigint() - start) / 1000000;\r\n};\r\n\r\nexport default {\r\n __highchartsDir,\r\n __dirname,\r\n clearText,\r\n expBackoff,\r\n fixType,\r\n handleResources,\r\n isCorrectJSON,\r\n isObject,\r\n isObjectEmpty,\r\n isPrivateRangeUrlFound,\r\n optionsStringify,\r\n printLogo,\r\n printUsage,\r\n roundNumber,\r\n toBoolean,\r\n wrapAround,\r\n measureTime\r\n};\r\n","/*******************************************************************************\r\n\r\nHighcharts Export Server\r\n\r\nCopyright (c) 2016-2024, Highsoft\r\n\r\nLicenced under the MIT licence.\r\n\r\nAdditionally a valid Highcharts license is required for use.\r\n\r\nSee LICENSE file in root for details.\r\n\r\n*******************************************************************************/\r\n\r\nimport { existsSync, readFileSync, promises as fsPromises } from 'fs';\r\n\r\nimport prompts from 'prompts';\r\n\r\nimport {\r\n absoluteProps,\r\n defaultConfig,\r\n nestedArgs,\r\n promptsConfig\r\n} from './schemas/config.js';\r\nimport { envs } from './envs.js';\r\nimport { log, logWithStack } from './logger.js';\r\nimport { deepCopy, isObject, printUsage, toBoolean } from './utils.js';\r\n\r\nlet generalOptions = {};\r\n\r\n/**\r\n * Retrieves and returns the general options for the export process.\r\n *\r\n * @returns {Object} The general options object.\r\n */\r\nexport const getOptions = () => generalOptions;\r\n\r\n/**\r\n * Initializes and sets the general options for the server instace, keeping\r\n * the principle of the options load priority. It accepts optional userOptions\r\n * and args from the CLI.\r\n *\r\n * @param {Object} userOptions - User-provided options for customization.\r\n * @param {Array} args - Command-line arguments for additional configuration\r\n * (CLI usage).\r\n *\r\n * @returns {Object} The updated general options object.\r\n */\r\nexport const setOptions = (userOptions, args) => {\r\n // Only for the CLI usage\r\n if (args?.length) {\r\n // Get the additional options from the custom JSON file\r\n generalOptions = loadConfigFile(args);\r\n }\r\n\r\n // Update the default config with a correct option values\r\n updateDefaultConfig(defaultConfig, generalOptions);\r\n\r\n // Set values for server's options and returns them\r\n generalOptions = initOptions(defaultConfig);\r\n\r\n // Apply user options if there are any\r\n if (userOptions) {\r\n // Merge user options\r\n generalOptions = mergeConfigOptions(\r\n generalOptions,\r\n userOptions,\r\n absoluteProps\r\n );\r\n }\r\n\r\n // Only for the CLI usage\r\n if (args?.length) {\r\n // Pair provided arguments\r\n generalOptions = pairArgumentValue(generalOptions, args, defaultConfig);\r\n }\r\n\r\n // Return final general options\r\n return generalOptions;\r\n};\r\n\r\n/**\r\n * Allows manual configuration based on specified prompts and saves\r\n * the configuration to a file.\r\n *\r\n * @param {string} configFileName - The name of the configuration file.\r\n *\r\n * @returns {Promise} A Promise that resolves to true once the manual\r\n * configuration is completed and saved.\r\n */\r\nexport const manualConfig = async (configFileName) => {\r\n // Prepare a config object\r\n let configFile = {};\r\n\r\n // Check if provided config file exists\r\n if (existsSync(configFileName)) {\r\n configFile = JSON.parse(readFileSync(configFileName, 'utf8'));\r\n }\r\n\r\n // Question about a configuration category\r\n const onSubmit = async (p, categories) => {\r\n let questionsCounter = 0;\r\n let allQuestions = [];\r\n\r\n // Create a corresponding property in the manualConfig object\r\n for (const section of categories) {\r\n // Mark each option with a section\r\n promptsConfig[section] = promptsConfig[section].map((option) => ({\r\n ...option,\r\n section\r\n }));\r\n\r\n // Collect the questions\r\n allQuestions = [...allQuestions, ...promptsConfig[section]];\r\n }\r\n\r\n await prompts(allQuestions, {\r\n onSubmit: async (prompt, answer) => {\r\n // Get the default module scripts\r\n if (prompt.name === 'moduleScripts') {\r\n answer = answer.length\r\n ? answer.map((module) => prompt.choices[module])\r\n : prompt.choices;\r\n\r\n configFile[prompt.section][prompt.name] = answer;\r\n } else {\r\n configFile[prompt.section] = recursiveProps(\r\n Object.assign({}, configFile[prompt.section] || {}),\r\n prompt.name.split('.'),\r\n prompt.choices ? prompt.choices[answer] : answer\r\n );\r\n }\r\n\r\n if (++questionsCounter === allQuestions.length) {\r\n try {\r\n await fsPromises.writeFile(\r\n configFileName,\r\n JSON.stringify(configFile, null, 2),\r\n 'utf8'\r\n );\r\n } catch (error) {\r\n logWithStack(\r\n 1,\r\n error,\r\n `[config] An error occurred while creating the ${configFileName} file.`\r\n );\r\n }\r\n return true;\r\n }\r\n }\r\n });\r\n\r\n return true;\r\n };\r\n\r\n // Find the categories\r\n const choices = Object.keys(promptsConfig).map((choice) => ({\r\n title: `${choice} options`,\r\n value: choice\r\n }));\r\n\r\n // Category prompt\r\n return prompts(\r\n {\r\n type: 'multiselect',\r\n name: 'category',\r\n message: 'Which category do you want to configure?',\r\n hint: 'Space: Select specific, A: Select all, Enter: Confirm.',\r\n instructions: '',\r\n choices\r\n },\r\n { onSubmit }\r\n );\r\n};\r\n\r\n/**\r\n * Maps old-structured (PhantomJS) options to a new configuration format\r\n * (Puppeteer).\r\n *\r\n * @param {Object} oldOptions - Old-structured options to be mapped.\r\n *\r\n * @returns {Object} New options structured based on the defined nestedArgs\r\n * mapping.\r\n */\r\nexport const mapToNewConfig = (oldOptions) => {\r\n const newOptions = {};\r\n // Cycle through old-structured options\r\n for (const [key, value] of Object.entries(oldOptions)) {\r\n const propertiesChain = nestedArgs[key] ? nestedArgs[key].split('.') : [];\r\n\r\n // Populate object in correct properties levels\r\n propertiesChain.reduce(\r\n (obj, prop, index) =>\r\n (obj[prop] =\r\n propertiesChain.length - 1 === index ? value : obj[prop] || {}),\r\n newOptions\r\n );\r\n }\r\n return newOptions;\r\n};\r\n\r\n/**\r\n * Merges two sets of configuration options, considering absolute properties.\r\n *\r\n * @param {Object} options - Original configuration options.\r\n * @param {Object} newOptions - New configuration options to be merged.\r\n * @param {Array} absoluteProps - List of properties that should\r\n * not be recursively merged.\r\n *\r\n * @returns {Object} Merged configuration options.\r\n */\r\nexport const mergeConfigOptions = (options, newOptions, absoluteProps = []) => {\r\n const mergedOptions = deepCopy(options);\r\n\r\n for (const [key, value] of Object.entries(newOptions)) {\r\n mergedOptions[key] =\r\n isObject(value) &&\r\n !absoluteProps.includes(key) &&\r\n mergedOptions[key] !== undefined\r\n ? mergeConfigOptions(mergedOptions[key], value, absoluteProps)\r\n : value !== undefined\r\n ? value\r\n : mergedOptions[key];\r\n }\r\n\r\n return mergedOptions;\r\n};\r\n\r\n/**\r\n * Initializes export settings based on provided exportOptions\r\n * and generalOptions.\r\n *\r\n * @param {Object} exportOptions - Options specific to the export process.\r\n * @param {Object} generalOptions - General configuration options.\r\n *\r\n * @returns {Object} Initialized export settings.\r\n */\r\nexport const initExportSettings = (exportOptions, generalOptions = {}) => {\r\n let options = {};\r\n\r\n if (exportOptions.svg) {\r\n options = deepCopy(generalOptions);\r\n options.export.type = exportOptions.type || exportOptions.export.type;\r\n options.export.scale = exportOptions.scale || exportOptions.export.scale;\r\n options.export.outfile =\r\n exportOptions.outfile || exportOptions.export.outfile;\r\n options.payload = {\r\n svg: exportOptions.svg\r\n };\r\n } else {\r\n options = mergeConfigOptions(\r\n generalOptions,\r\n exportOptions,\r\n // Omit going down recursively with the belows\r\n absoluteProps\r\n );\r\n }\r\n\r\n options.export.outfile =\r\n options.export?.outfile || `chart.${options.export?.type || 'png'}`;\r\n return options;\r\n};\r\n\r\n/**\r\n * Loads additional configuration from a specified file using\r\n * the --loadConfig option.\r\n *\r\n * @param {Array} args - Command-line arguments to check for\r\n * the --loadConfig option.\r\n *\r\n * @returns {Object} Additional configuration loaded from the specified file,\r\n * or an empty object if not found or invalid.\r\n */\r\nfunction loadConfigFile(args) {\r\n // Check if the --loadConfig option was used\r\n const configIndex = args.findIndex(\r\n (arg) => arg.replace(/-/g, '') === 'loadConfig'\r\n );\r\n\r\n // Check if the --loadConfig has a value\r\n if (configIndex > -1 && args[configIndex + 1]) {\r\n const fileName = args[configIndex + 1];\r\n try {\r\n // Check if an additional config file is a correct JSON file\r\n if (fileName && fileName.endsWith('.json')) {\r\n // Load an optional custom JSON config file\r\n return JSON.parse(readFileSync(fileName));\r\n }\r\n } catch (error) {\r\n logWithStack(\r\n 2,\r\n error,\r\n `[config] Unable to load the configuration from the ${fileName} file.`\r\n );\r\n }\r\n }\r\n\r\n // No additional options to return\r\n return {};\r\n}\r\n\r\n/**\r\n * Updates the default configuration object with values from a custom object\r\n * and environment variables.\r\n *\r\n * @param {Object} configObj - The default configuration object.\r\n * @param {Object} customObj - Custom configuration object to override defaults.\r\n * @param {string} propChain - Property chain for tracking nested properties\r\n * during recursion.\r\n */\r\nfunction updateDefaultConfig(configObj, customObj = {}, propChain = '') {\r\n Object.keys(configObj).forEach((key) => {\r\n const entry = configObj[key];\r\n const customValue = customObj && customObj[key];\r\n\r\n if (typeof entry.value === 'undefined') {\r\n updateDefaultConfig(entry, customValue, `${propChain}.${key}`);\r\n } else {\r\n // If a value from a custom JSON exists, it take precedence\r\n if (customValue !== undefined) {\r\n entry.value = customValue;\r\n }\r\n\r\n // If a value from an env variable exists, it take precedence\r\n if (entry.envLink in envs && envs[entry.envLink] !== undefined) {\r\n entry.value = envs[entry.envLink];\r\n }\r\n }\r\n });\r\n}\r\n\r\n/**\r\n * Initializes options object based on provided items, setting values from\r\n * nested properties recursively.\r\n *\r\n * @param {Object} items - Configuration items to be used for initializing\r\n * options.\r\n *\r\n * @returns {Object} Initialized options object.\r\n */\r\nfunction initOptions(items) {\r\n let options = {};\r\n for (const [name, item] of Object.entries(items)) {\r\n options[name] = Object.prototype.hasOwnProperty.call(item, 'value')\r\n ? item.value\r\n : initOptions(item);\r\n }\r\n return options;\r\n}\r\n\r\n/**\r\n * Pairs argument values with corresponding options in the configuration,\r\n * updating the options object.\r\n *\r\n * @param {Object} options - Configuration options object to be updated.\r\n * @param {Array} args - Command-line arguments containing values for specific\r\n * options.\r\n * @param {Object} defaultConfig - Default configuration object for reference.\r\n *\r\n * @returns {Object} Updated options object.\r\n */\r\nfunction pairArgumentValue(options, args, defaultConfig) {\r\n let showUsage = false;\r\n for (let i = 0; i < args.length; i++) {\r\n const option = args[i].replace(/-/g, '');\r\n\r\n // Find the right place for property's value\r\n const propertiesChain = nestedArgs[option]\r\n ? nestedArgs[option].split('.')\r\n : [];\r\n\r\n // Get the correct type for CLI args which are passed as strings\r\n let argumentType;\r\n propertiesChain.reduce((obj, prop, index) => {\r\n if (propertiesChain.length - 1 === index) {\r\n argumentType = obj[prop].type;\r\n }\r\n return obj[prop];\r\n }, defaultConfig);\r\n\r\n propertiesChain.reduce((obj, prop, index) => {\r\n if (propertiesChain.length - 1 === index) {\r\n // Finds an option and set a corresponding value\r\n if (typeof obj[prop] !== 'undefined') {\r\n if (args[++i]) {\r\n if (argumentType === 'boolean') {\r\n obj[prop] = toBoolean(args[i]);\r\n } else if (argumentType === 'number') {\r\n obj[prop] = +args[i];\r\n } else if (argumentType.indexOf(']') >= 0) {\r\n obj[prop] = args[i].split(',');\r\n } else {\r\n obj[prop] = args[i];\r\n }\r\n } else {\r\n log(\r\n 2,\r\n `[config] Missing value for the '${option}' argument. Using the default value.`\r\n );\r\n showUsage = true;\r\n }\r\n }\r\n }\r\n return obj[prop];\r\n }, options);\r\n }\r\n\r\n // Display the usage for the reference if needed\r\n if (showUsage) {\r\n printUsage(defaultConfig);\r\n }\r\n\r\n return options;\r\n}\r\n\r\n/**\r\n * Recursively updates properties in an object based on nested names and assigns\r\n * the final value.\r\n *\r\n * @param {Object} objectToUpdate - The object to be updated.\r\n * @param {Array} nestedNames - Array of nested property names.\r\n * @param {any} value - The final value to be assigned.\r\n *\r\n * @returns {Object} Updated object with assigned values.\r\n */\r\nfunction recursiveProps(objectToUpdate, nestedNames, value) {\r\n while (nestedNames.length > 1) {\r\n const propName = nestedNames.shift();\r\n\r\n // Create a property in object if it doesn't exist\r\n if (!Object.prototype.hasOwnProperty.call(objectToUpdate, propName)) {\r\n objectToUpdate[propName] = {};\r\n }\r\n\r\n // Call function again if there still names to go\r\n objectToUpdate[propName] = recursiveProps(\r\n Object.assign({}, objectToUpdate[propName]),\r\n nestedNames,\r\n value\r\n );\r\n\r\n return objectToUpdate;\r\n }\r\n\r\n // Assign the final value\r\n objectToUpdate[nestedNames[0]] = value;\r\n return objectToUpdate;\r\n}\r\n\r\nexport default {\r\n getOptions,\r\n setOptions,\r\n manualConfig,\r\n mapToNewConfig,\r\n mergeConfigOptions,\r\n initExportSettings\r\n};\r\n","/**\r\n * This module exports two functions: fetch (for GET requests) and post (for POST requests).\r\n */\r\n\r\nimport http from 'http';\r\nimport https from 'https';\r\n\r\n/**\r\n * Returns the HTTP or HTTPS protocol module based on the provided URL.\r\n *\r\n * @param {string} url - The URL to determine the protocol.\r\n *\r\n * @returns {Object} The HTTP or HTTPS protocol module (http or https).\r\n */\r\nconst getProtocol = (url) => (url.startsWith('https') ? https : http);\r\n\r\n/**\r\n * Fetches data from the specified URL using either HTTP or HTTPS protocol.\r\n *\r\n * @param {string} url - The URL to fetch data from.\r\n * @param {Object} requestOptions - Options for the HTTP request (optional).\r\n *\r\n * @returns {Promise} Promise resolving to the HTTP response object\r\n * with added 'text' property or rejecting with an error.\r\n */\r\nasync function fetch(url, requestOptions = {}) {\r\n return new Promise((resolve, reject) => {\r\n const protocol = getProtocol(url);\r\n\r\n protocol\r\n .get(\r\n url,\r\n Object.assign(\r\n {\r\n headers: {\r\n 'User-Agent': 'highcharts/export',\r\n Referer: 'highcharts.export'\r\n }\r\n },\r\n requestOptions || {}\r\n ),\r\n (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 )\r\n .on('error', (error) => {\r\n reject(error);\r\n });\r\n });\r\n}\r\n\r\n/**\r\n * Sends a POST request to the specified URL with the provided JSON body using\r\n * either HTTP or HTTPS protocol.\r\n *\r\n * @param {string} url - The URL to send the POST request to.\r\n * @param {Object} body - The JSON body to include in the POST request\r\n * (optional, default is an empty object).\r\n * @param {Object} requestOptions - Options for the HTTP request (optional).\r\n *\r\n * @returns {Promise} Promise resolving to the HTTP response object with\r\n * added 'text' property or rejecting with an error.\r\n */\r\nasync function post(url, body = {}, requestOptions = {}) {\r\n return new Promise((resolve, reject) => {\r\n const protocol = getProtocol(url);\r\n const data = JSON.stringify(body);\r\n\r\n // Set default headers and merge with requestOptions\r\n const options = Object.assign(\r\n {\r\n method: 'POST',\r\n headers: {\r\n 'Content-Type': 'application/json',\r\n 'Content-Length': data.length\r\n }\r\n },\r\n requestOptions\r\n );\r\n\r\n const req = protocol\r\n .request(url, options, (res) => {\r\n let responseData = '';\r\n\r\n // A chunk of data has been received.\r\n res.on('data', (chunk) => {\r\n responseData += chunk;\r\n });\r\n\r\n // The whole response has been received.\r\n res.on('end', () => {\r\n try {\r\n res.text = responseData;\r\n resolve(res);\r\n } catch (error) {\r\n reject(error);\r\n }\r\n });\r\n })\r\n .on('error', (error) => {\r\n reject(error);\r\n });\r\n\r\n // Write the request body and end the request.\r\n req.write(data);\r\n req.end();\r\n });\r\n}\r\n\r\nexport default fetch;\r\nexport { fetch, post };\r\n","class ExportError extends Error {\r\n constructor(message) {\r\n super();\r\n this.message = message;\r\n this.stackMessage = message;\r\n }\r\n\r\n setError(error) {\r\n this.error = error;\r\n if (error.name) {\r\n this.name = error.name;\r\n }\r\n if (error.statusCode) {\r\n this.statusCode = error.statusCode;\r\n }\r\n if (error.stack) {\r\n this.stackMessage = error.message;\r\n this.stack = error.stack;\r\n }\r\n return this;\r\n }\r\n}\r\n\r\nexport default ExportError;\r\n","/*******************************************************************************\r\n\r\nHighcharts Export Server\r\n\r\nCopyright (c) 2016-2024, Highsoft\r\n\r\nLicenced under the MIT licence.\r\n\r\nAdditionally a valid Highcharts license is required for use.\r\n\r\nSee LICENSE file in root for details.\r\n\r\n*******************************************************************************/\r\n\r\n// The cache manager manages the Highcharts library and its dependencies.\r\n// The cache itself is stored in .cache, and is checked by the config system\r\n// before starting the service\r\n\r\nimport { existsSync, mkdirSync, readFileSync, writeFileSync } from 'fs';\r\nimport { join, resolve, sep } from 'path';\r\n\r\nimport { HttpsProxyAgent } from 'https-proxy-agent';\r\n\r\nimport { getOptions } from './config.js';\r\nimport { envs } from './envs.js';\r\nimport { fetch } from './fetch.js';\r\nimport { log } from './logger.js';\r\nimport { __dirname, __highchartsDir } from './utils.js';\r\n\r\nimport ExportError from './errors/ExportError.js';\r\n\r\nconst cache = {\r\n cdnURL: 'https://code.highcharts.com/',\r\n activeManifest: {},\r\n sources: '',\r\n hcVersion: ''\r\n};\r\n\r\n/**\r\n * Extracts and caches the Highcharts version from the sources string.\r\n *\r\n * @returns {string} The extracted Highcharts version.\r\n */\r\nexport const extractVersion = (cache) => {\r\n return cache.sources\r\n .substring(0, cache.sources.indexOf('*/'))\r\n .replace('/*', '')\r\n .replace('*/', '')\r\n .replace(/\\n/g, '')\r\n .trim();\r\n};\r\n\r\n/**\r\n * Extracts the Highcharts module name based on the scriptPath.\r\n *\r\n * @param {string} scriptPath - The path to the module.\r\n *\r\n * @returns {string} The name of a module.\r\n */\r\nexport const extractModuleName = (scriptPath) => {\r\n // Normalize slashes, get part after the last '/' and remove .js extension\r\n return scriptPath.replace(/\\\\/g, '/').split('/').pop().replace(/\\.js$/i, '');\r\n};\r\n\r\n/**\r\n * Saves the provided configuration and fetched modules to the cache manifest\r\n * file.\r\n *\r\n * @param {object} config - Highcharts-related configuration object.\r\n * @param {object} fetchedModules - An object that contains mapped names of\r\n * fetched Highcharts modules to use.\r\n *\r\n * @throws {ExportError} Throws an ExportError if an error occurs while writing\r\n * the cache manifest.\r\n */\r\nexport const saveConfigToManifest = async (config, fetchedModules) => {\r\n const newManifest = {\r\n version: config.version,\r\n modules: fetchedModules || {}\r\n };\r\n\r\n // Update cache object with the current modules\r\n cache.activeManifest = newManifest;\r\n\r\n log(3, '[cache] Writing a new manifest.');\r\n try {\r\n writeFileSync(\r\n join(__dirname, config.cachePath, 'manifest.json'),\r\n JSON.stringify(newManifest),\r\n 'utf8'\r\n );\r\n } catch (error) {\r\n throw new ExportError('[cache] Error writing the cache manifest.').setError(\r\n error\r\n );\r\n }\r\n};\r\n\r\n/**\r\n * Fetches a single script and updates the fetchedModules accordingly.\r\n *\r\n * @param {string} script - A path to script to get.\r\n * @param {Object} requestOptions - Additional options for the proxy agent\r\n * to use for a request.\r\n * @param {Object} fetchedModules - An object which tracks which Highcharts\r\n * modules have been fetched.\r\n * @param {boolean} [useNpm=false] - A flag to indicate if the script should be\r\n * get from NPM package or fetched from CDN. The default value is `false`.\r\n * @param {boolean} [shouldThrowError=false] - A flag to indicate if the error\r\n * should be thrown. This should be used only for the core scripts.\r\n *\r\n * @returns {Promise} A Promise resolving to the text representation\r\n * of the fetched script.\r\n *\r\n * @throws {ExportError} Throws an ExportError if there is a problem with\r\n * fetching the script.\r\n */\r\nexport const fetchAndProcessScript = async (\r\n script,\r\n requestOptions,\r\n fetchedModules,\r\n useNpm = false,\r\n shouldThrowError = false\r\n) => {\r\n let response;\r\n\r\n // Add the missing .js to the strings\r\n if (!script.endsWith('.js')) {\r\n script = `${script}.js`;\r\n }\r\n\r\n // Whether to use NPM package scripts or fetch it from CDN\r\n if (useNpm) {\r\n try {\r\n // Log fetched script\r\n log(\r\n 4,\r\n `[cache] Fetching script from NPM - ${join('node_modules', 'highcharts', script)}`\r\n );\r\n\r\n // Sanitize and validate path\r\n const resolvedScriptPath = resolve(__highchartsDir, script);\r\n if (!resolvedScriptPath.startsWith(resolve(__highchartsDir) + sep)) {\r\n throw new ExportError(\r\n `[cache] Invalid script path detected for '${script}'. Directory traversal attempt or bad version input.`,\r\n 403\r\n );\r\n }\r\n\r\n // Fetch the script from NPM\r\n response = readFileSync(resolvedScriptPath, 'utf8');\r\n\r\n // If OK, return its text representation\r\n if (fetchedModules && response) {\r\n fetchedModules[extractModuleName(script)] = 1;\r\n }\r\n return response;\r\n } catch {\r\n // Proceed\r\n }\r\n } else {\r\n // Log fetched script\r\n log(4, `[cache] Fetching script from CDN - ${script}`);\r\n\r\n // Fetch the script from CDN\r\n response = await fetch(script, requestOptions);\r\n\r\n // If OK, return its text representation\r\n if (response.statusCode === 200 && typeof response.text == 'string') {\r\n if (fetchedModules) {\r\n fetchedModules[extractModuleName(script)] = 1;\r\n }\r\n return response.text;\r\n }\r\n }\r\n\r\n // Based on the `shouldThrowError` flag, decide how to serve error message\r\n if (shouldThrowError) {\r\n throw new ExportError(\r\n `[cache] Could not fetch the mandatory ${script}. The script might not exist in the requested version.`,\r\n 404\r\n );\r\n } else {\r\n log(\r\n 2,\r\n `[cache] Could not fetch the ${script}. The script might not exist in the requested version.`\r\n );\r\n }\r\n\r\n return '';\r\n};\r\n\r\n/**\r\n * Fetches Highcharts scripts and customScripts from the given CDNs.\r\n *\r\n * @param {Object} highchartsOptions - Object containing all highcharts options.\r\n * @param {object} proxyOptions - Options for the proxy agent to use for\r\n * a request.\r\n * @param {object} fetchedModules - An object which tracks which Highcharts\r\n * modules have been fetched.\r\n *\r\n * @returns {Promise} The fetched scripts content joined.\r\n */\r\nexport const fetchScripts = async (\r\n highchartsOptions,\r\n proxyOptions,\r\n fetchedModules\r\n) => {\r\n const version = highchartsOptions.version;\r\n const hcVersion = version === 'latest' || !version ? '' : `${version}/`;\r\n const cdnURL = highchartsOptions.cdnURL || cache.cdnURL;\r\n\r\n log(\r\n 3,\r\n `[cache] Updating cache version to Highcharts: ${hcVersion || 'latest'}.`\r\n );\r\n\r\n // Whether to use NPM or CDN\r\n const useNpm = highchartsOptions.useNpm;\r\n\r\n // Configure proxy if exists\r\n let proxyAgent;\r\n const { host, port, username, password } = proxyOptions;\r\n\r\n // Try to create a Proxy Agent\r\n if (host && port) {\r\n try {\r\n proxyAgent = new HttpsProxyAgent({\r\n host,\r\n port,\r\n ...(username && password ? { username, password } : {})\r\n });\r\n } catch (error) {\r\n throw new ExportError('[cache] Could not create a Proxy Agent.').setError(\r\n error\r\n );\r\n }\r\n }\r\n\r\n // If exists, add proxy agent to request options\r\n const requestOptions = proxyAgent\r\n ? {\r\n agent: proxyAgent,\r\n timeout: envs.SERVER_PROXY_TIMEOUT\r\n }\r\n : {};\r\n\r\n const fetchedScripts = await Promise.all([\r\n ...highchartsOptions.coreScripts.map((c) =>\r\n fetchAndProcessScript(\r\n (useNpm && c) || `${cdnURL}${hcVersion}${c}`,\r\n requestOptions,\r\n fetchedModules,\r\n useNpm,\r\n true\r\n )\r\n ),\r\n ...highchartsOptions.moduleScripts.map((m) =>\r\n fetchAndProcessScript(\r\n (useNpm && join('modules', m)) ||\r\n (m === 'map'\r\n ? `${cdnURL}maps/${hcVersion}modules/${m}`\r\n : `${cdnURL}${hcVersion}modules/${m}`),\r\n requestOptions,\r\n fetchedModules,\r\n useNpm\r\n )\r\n ),\r\n ...highchartsOptions.indicatorScripts.map((i) =>\r\n fetchAndProcessScript(\r\n (useNpm && join('indicators', i)) ||\r\n `${cdnURL}stock/${hcVersion}indicators/${i}`,\r\n requestOptions,\r\n fetchedModules,\r\n useNpm\r\n )\r\n ),\r\n ...highchartsOptions.customScripts.map((c) =>\r\n fetchAndProcessScript(`${c}`, requestOptions)\r\n )\r\n ]);\r\n\r\n return fetchedScripts.join(';\\n');\r\n};\r\n\r\n/**\r\n * Updates the local cache with Highcharts scripts and their versions.\r\n *\r\n * @param {Object} highchartsOptions - Object containing all options from\r\n * the highcharts section.\r\n * @param {string} sourcePath - The path to the source file in the cache.\r\n *\r\n * @returns {Promise} A Promise resolving to an object representing\r\n * the fetched modules.\r\n *\r\n * @throws {ExportError} Throws an ExportError if there is an issue updating\r\n * the local Highcharts cache.\r\n */\r\nexport const updateCache = async (\r\n highchartsOptions,\r\n proxyOptions,\r\n sourcePath\r\n) => {\r\n try {\r\n const fetchedModules = {};\r\n\r\n // Get sources\r\n cache.sources = await fetchScripts(\r\n highchartsOptions,\r\n proxyOptions,\r\n fetchedModules\r\n );\r\n\r\n // Get sources version\r\n cache.hcVersion = extractVersion(cache);\r\n\r\n // Save the fetched modules into caches' source JSON\r\n writeFileSync(sourcePath, cache.sources);\r\n\r\n return fetchedModules;\r\n } catch (error) {\r\n throw new ExportError(\r\n '[cache] Unable to update the local Highcharts cache.'\r\n ).setError(error);\r\n }\r\n};\r\n\r\n/**\r\n * Updates the Highcharts version in the applied configuration and checks\r\n * the cache for the new version.\r\n *\r\n * @param {string} newVersion - The new Highcharts version to be applied.\r\n *\r\n * @returns {Promise<(object|boolean)>} A Promise resolving to the updated\r\n * configuration with the new version, or false if no applied configuration\r\n * exists.\r\n */\r\nexport const updateVersion = async (newVersion) => {\r\n const options = getOptions();\r\n if (options?.highcharts) {\r\n options.highcharts.version = newVersion;\r\n }\r\n await checkAndUpdateCache(options);\r\n};\r\n\r\n/**\r\n * Checks the cache for Highcharts dependencies, updates the cache if needed,\r\n * and loads the sources.\r\n *\r\n * @param {Object} options - Object containing all options.\r\n *\r\n * @returns {Promise} A Promise that resolves once the cache is checked\r\n * and updated.\r\n *\r\n * @throws {ExportError} Throws an ExportError if there is an issue updating\r\n * or reading the cache.\r\n */\r\nexport const checkAndUpdateCache = async (options) => {\r\n const { highcharts, server } = options;\r\n const cachePath = join(__dirname, highcharts.cachePath);\r\n\r\n let fetchedModules;\r\n // Prepare paths to manifest and sources from the .cache folder\r\n const manifestPath = join(cachePath, 'manifest.json');\r\n const sourcePath = join(cachePath, 'sources.js');\r\n\r\n // Create the cache destination if it doesn't exist already\r\n !existsSync(cachePath) && mkdirSync(cachePath);\r\n\r\n // Fetch all the scripts either if manifest.json does not exist\r\n // or if the forceFetch option is enabled\r\n if (!existsSync(manifestPath) || highcharts.forceFetch) {\r\n log(3, '[cache] Fetching and caching Highcharts dependencies.');\r\n fetchedModules = await updateCache(highcharts, server.proxy, sourcePath);\r\n } else {\r\n let requestUpdate = false;\r\n\r\n // Read the manifest JSON\r\n const manifest = JSON.parse(readFileSync(manifestPath));\r\n\r\n // Check if the modules is an array, if so, we rewrite it to a map to make\r\n // it easier to resolve modules.\r\n if (manifest.modules && Array.isArray(manifest.modules)) {\r\n const moduleMap = {};\r\n manifest.modules.forEach((m) => (moduleMap[m] = 1));\r\n manifest.modules = moduleMap;\r\n }\r\n\r\n const { coreScripts, moduleScripts, indicatorScripts } = highcharts;\r\n const numberOfModules =\r\n coreScripts.length + moduleScripts.length + indicatorScripts.length;\r\n\r\n // Compare the loaded highcharts config with the contents in cache.\r\n // If there are changes, fetch requested modules and products,\r\n // and bake them into a giant blob. Save the blob.\r\n if (manifest.version !== highcharts.version) {\r\n log(\r\n 2,\r\n '[cache] A Highcharts version mismatch in the cache, need to re-fetch.'\r\n );\r\n requestUpdate = true;\r\n } else if (Object.keys(manifest.modules || {}).length !== numberOfModules) {\r\n log(\r\n 2,\r\n '[cache] The cache and the requested modules do not match, need to re-fetch.'\r\n );\r\n requestUpdate = true;\r\n } else {\r\n // Check each module, if anything is missing refetch everything\r\n requestUpdate = (moduleScripts || []).some((moduleName) => {\r\n if (!manifest.modules[moduleName]) {\r\n log(\r\n 2,\r\n `[cache] The ${moduleName} is missing in the cache, need to re-fetch.`\r\n );\r\n return true;\r\n }\r\n });\r\n }\r\n\r\n if (requestUpdate) {\r\n fetchedModules = await updateCache(highcharts, server.proxy, sourcePath);\r\n } else {\r\n log(3, '[cache] Dependency cache is up to date, proceeding.');\r\n\r\n // Load the sources\r\n cache.sources = readFileSync(sourcePath, 'utf8');\r\n\r\n // Get current modules map\r\n fetchedModules = manifest.modules;\r\n\r\n cache.hcVersion = extractVersion(cache);\r\n }\r\n }\r\n\r\n // Finally, save the new manifest, which is basically our current config\r\n // in a slightly different format\r\n await saveConfigToManifest(highcharts, fetchedModules);\r\n};\r\n\r\nexport const getCachePath = () =>\r\n join(__dirname, getOptions().highcharts.cachePath);\r\n\r\nexport const getCache = () => cache;\r\n\r\nexport const highcharts = () => cache.sources;\r\n\r\nexport const version = () => cache.hcVersion;\r\n\r\nexport default {\r\n checkAndUpdateCache,\r\n getCachePath,\r\n updateVersion,\r\n getCache,\r\n highcharts,\r\n version\r\n};\r\n","/*******************************************************************************\r\n\r\nHighcharts Export Server\r\n\r\nCopyright (c) 2016-2024, Highsoft\r\n\r\nLicenced under the MIT licence.\r\n\r\nAdditionally a valid Highcharts license is required for use.\r\n\r\nSee LICENSE file in root for details.\r\n\r\n*******************************************************************************/\r\n\r\n/* eslint-disable no-undef */\r\n\r\n/**\r\n * Setting the animObject. Called when initing the page.\r\n */\r\nexport function setupHighcharts() {\r\n Highcharts.animObject = function () {\r\n return { duration: 0 };\r\n };\r\n}\r\n\r\n/**\r\n * Creates the actual chart.\r\n *\r\n * @param {object} chartOptions - The options for the Highcharts chart.\r\n * @param {object} options - The export options.\r\n * @param {boolean} displayErrors - A flag indicating whether to display errors.\r\n */\r\nexport async function triggerExport(chartOptions, options, displayErrors) {\r\n // Display errors flag taken from chart options nad debugger module\r\n window._displayErrors = displayErrors;\r\n\r\n // Get required functions\r\n const { getOptions, merge, setOptions, wrap } = Highcharts;\r\n\r\n // Create a separate object for a potential setOptions usages in order to\r\n // prevent from polluting other exports that can happen on the same page\r\n Highcharts.setOptionsObj = merge(false, {}, getOptions());\r\n\r\n // By default animation is disabled\r\n const chart = {\r\n animation: false\r\n };\r\n\r\n // When straight inject, the size is set through CSS only\r\n if (options.export.strInj) {\r\n chart.height = chartOptions.chart.height;\r\n chart.width = chartOptions.chart.width;\r\n }\r\n\r\n // NOTE: Is this used for anything useful?\r\n window.isRenderComplete = false;\r\n wrap(Highcharts.Chart.prototype, 'init', function (proceed, userOptions, cb) {\r\n // Override userOptions with image friendly options\r\n userOptions = merge(userOptions, {\r\n exporting: {\r\n enabled: false\r\n },\r\n plotOptions: {\r\n series: {\r\n label: {\r\n enabled: false\r\n }\r\n }\r\n },\r\n /* Expects tooltip in userOptions when forExport is true.\r\n https://github.com/highcharts/highcharts/blob/3ad430a353b8056b9e764aa4e5cd6828aa479db2/js/parts/Chart.js#L241\r\n */\r\n tooltip: {}\r\n });\r\n\r\n (userOptions.series || []).forEach(function (series) {\r\n series.animation = false;\r\n });\r\n\r\n // Add flag to know if chart render has been called.\r\n if (!window.onHighchartsRender) {\r\n window.onHighchartsRender = Highcharts.addEvent(this, 'render', () => {\r\n window.isRenderComplete = true;\r\n });\r\n }\r\n\r\n proceed.apply(this, [userOptions, cb]);\r\n });\r\n\r\n wrap(Highcharts.Series.prototype, 'init', function (proceed, chart, options) {\r\n proceed.apply(this, [chart, options]);\r\n });\r\n\r\n // Get the user options\r\n const userOptions = options.export.strInj\r\n ? new Function(`return ${options.export.strInj}`)()\r\n : chartOptions;\r\n\r\n // Trigger custom code\r\n if (options.customLogic.customCode) {\r\n new Function('options', options.customLogic.customCode)(userOptions);\r\n }\r\n\r\n // Merge the globalOptions, themeOptions, options from the wrapped\r\n // setOptions function and user options to create the final options object\r\n const finalOptions = merge(\r\n false,\r\n JSON.parse(options.export.themeOptions),\r\n userOptions,\r\n // Placed it here instead in the init because of the size issues\r\n { chart }\r\n );\r\n\r\n const finalCallback = options.customLogic.callback\r\n ? new Function(`return ${options.customLogic.callback}`)()\r\n : undefined;\r\n\r\n // Set the global options if exist\r\n const globalOptions = JSON.parse(options.export.globalOptions);\r\n if (globalOptions) {\r\n setOptions(globalOptions);\r\n }\r\n\r\n let constr = options.export.constr || 'chart';\r\n constr = typeof Highcharts[constr] !== 'undefined' ? constr : 'chart';\r\n\r\n Highcharts[constr]('container', finalOptions, finalCallback);\r\n\r\n // Get the current global options\r\n const defaultOptions = getOptions();\r\n\r\n // Clear it just in case (e.g. the setOptions was used in the customCode)\r\n for (const prop in defaultOptions) {\r\n if (typeof defaultOptions[prop] !== 'function') {\r\n delete defaultOptions[prop];\r\n }\r\n }\r\n\r\n // Set the default options back\r\n setOptions(Highcharts.setOptionsObj);\r\n\r\n // Empty the custom global options object\r\n Highcharts.setOptionsObj = {};\r\n}\r\n","/*******************************************************************************\r\n\r\nHighcharts Export Server\r\n\r\nCopyright (c) 2016-2024, Highsoft\r\n\r\nLicenced under the MIT licence.\r\n\r\nAdditionally a valid Highcharts license is required for use.\r\n\r\nSee LICENSE file in root for details.\r\n\r\n*******************************************************************************/\r\n\r\nimport { readFileSync } from 'fs';\r\nimport path from 'path';\r\n\r\nimport puppeteer from 'puppeteer';\r\n\r\nimport { getCachePath } from './cache.js';\r\nimport { getOptions } from './config.js';\r\nimport { setupHighcharts } from './highcharts.js';\r\nimport { log, logWithStack } from './logger.js';\r\nimport { __dirname } from './utils.js';\r\n\r\nimport ExportError from './errors/ExportError.js';\r\n\r\n// Get the template for the page\r\nconst template = readFileSync(__dirname + '/templates/template.html', 'utf8');\r\n\r\nlet browser;\r\n\r\n/**\r\n * Retrieves the existing Puppeteer browser instance.\r\n *\r\n * @returns {Promise} A Promise resolving to the Puppeteer browser\r\n * instance.\r\n *\r\n * @throws {ExportError} Throws an ExportError if no valid browser has been\r\n * created.\r\n */\r\nexport function get() {\r\n if (!browser) {\r\n throw new ExportError('[browser] No valid browser has been created.');\r\n }\r\n return browser;\r\n}\r\n\r\n/**\r\n * Creates a Puppeteer browser instance with the specified arguments.\r\n *\r\n * @param {Array} puppeteerArgs - Additional arguments for Puppeteer launch.\r\n *\r\n * @returns {Promise} A Promise resolving to the Puppeteer browser\r\n * instance.\r\n *\r\n * @throws {ExportError} Throws an ExportError if max retries to open a browser\r\n * instance are reached, or if no browser instance is found after retries.\r\n */\r\nexport async function create(puppeteerArgs) {\r\n // Get debug and other options\r\n const { puppeteer: puppeteerOptions, debug, other } = getOptions();\r\n\r\n // Get the debug options\r\n const { enable: enabledDebug, ...debugOptions } = debug;\r\n\r\n const launchOptions = {\r\n headless: other.browserShellMode ? 'shell' : true,\r\n userDataDir: puppeteerOptions.tempDir || './tmp/',\r\n args: puppeteerArgs,\r\n handleSIGINT: false,\r\n handleSIGTERM: false,\r\n handleSIGHUP: false,\r\n waitForInitialPage: false,\r\n defaultViewport: null,\r\n ...(enabledDebug && debugOptions)\r\n };\r\n\r\n // Create a browser\r\n if (!browser) {\r\n const maxTries = 25;\r\n let tryCount = 0;\r\n\r\n const open = async () => {\r\n try {\r\n log(\r\n 3,\r\n `[browser] Attempting to get a browser instance (try ${++tryCount}).`\r\n );\r\n browser = await puppeteer.launch(launchOptions);\r\n } catch (error) {\r\n // This isn't a full error yet as puppeteer sometimes takes time to\r\n // initialize properly.\r\n logWithStack(\r\n 2,\r\n error,\r\n `[browser] Failed to launch a browser instance - retrying (attempt ${tryCount}/${maxTries}).`\r\n );\r\n\r\n // Retry to launch browser until reaching max attempts\r\n if (tryCount < 25) {\r\n log(\r\n 3,\r\n `[browser] Retry to open a browser (attempt ${tryCount}/${maxTries}).`\r\n );\r\n await new Promise((response) => setTimeout(response, 4000));\r\n await open();\r\n } else {\r\n //... now it's an error, which is caught by the caller\r\n throw error;\r\n }\r\n }\r\n };\r\n\r\n try {\r\n await open();\r\n\r\n // Shell mode inform\r\n if (launchOptions.headless === 'shell') {\r\n log(3, `[browser] Launched browser in shell mode.`);\r\n }\r\n\r\n // Debug mode inform\r\n if (enabledDebug) {\r\n log(3, `[browser] Launched browser in debug mode.`);\r\n }\r\n } catch (error) {\r\n throw new ExportError(\r\n '[browser] Maximum retries to open a browser instance reached.'\r\n ).setError(error);\r\n }\r\n\r\n if (!browser) {\r\n throw new ExportError('[browser] Cannot find a browser to open.');\r\n }\r\n }\r\n\r\n // Return a browser promise\r\n return browser;\r\n}\r\n\r\n/**\r\n * Closes the Puppeteer browser instance if it is connected.\r\n *\r\n * @returns {Promise} A Promise resolving to true after the browser\r\n * is closed.\r\n */\r\nexport async function close() {\r\n // Close the browser when connnected\r\n if (browser?.connected) {\r\n await browser.close();\r\n }\r\n log(4, '[browser] Closed the browser.');\r\n}\r\n\r\n/**\r\n * Creates a new Puppeteer Page within an existing browser instance.\r\n *\r\n * If the browser instance is not available, returns false.\r\n *\r\n * The function creates a new page, disables caching, sets content using\r\n * setPageContent(), and returns the created Puppeteer Page.\r\n *\r\n * @returns {(boolean|object)} Returns false if the browser instance is not\r\n * available, or a Puppeteer Page object representing the newly created page.\r\n */\r\nexport async function newPage() {\r\n if (!browser) {\r\n return false;\r\n }\r\n\r\n // Create a page\r\n const page = await browser.newPage();\r\n\r\n // Disable cache\r\n await page.setCacheEnabled(false);\r\n\r\n // Set the content\r\n await setPageContent(page);\r\n\r\n // Set page events\r\n setPageEvents(page);\r\n\r\n return page;\r\n}\r\n\r\n/**\r\n * Clears the content of a Puppeteer Page based on the specified mode.\r\n *\r\n * @param {Object} page - The Puppeteer Page object to be cleared.\r\n * @param {boolean} hardReset - A flag indicating the type of clearing\r\n * to be performed. If true, navigates to 'about:blank' and resets content\r\n * and scripts. If false, clears the body content by setting a predefined HTML\r\n * structure.\r\n *\r\n * @throws {Error} Logs thrown error if clearing the page content fails.\r\n */\r\nexport async function clearPage(page, hardReset = false) {\r\n try {\r\n if (page && !page.isClosed()) {\r\n if (hardReset) {\r\n // Navigate to about:blank\r\n await page.goto('about:blank', { waitUntil: 'domcontentloaded' });\r\n\r\n // Set the content and and scripts again\r\n await setPageContent(page);\r\n } else {\r\n // Clear body content\r\n await page.evaluate(() => {\r\n document.body.innerHTML =\r\n '
';\r\n });\r\n }\r\n return true;\r\n }\r\n } catch (error) {\r\n logWithStack(\r\n 2,\r\n error,\r\n '[browser] Could not clear the content of the page.'\r\n );\r\n }\r\n\r\n return false;\r\n}\r\n\r\n/**\r\n * Adds custom JS and CSS resources to a Puppeteer Page based on the specified\r\n * options.\r\n *\r\n * @param {Object} page - The Puppeteer Page object to which resources will be\r\n * added.\r\n * @param {Object} options - All options and configuration.\r\n *\r\n * @returns {Promise>} - Promise resolving to an array of injected\r\n * resources.\r\n */\r\nexport async function addPageResources(page, options) {\r\n // Injected resources array\r\n const injectedResources = [];\r\n\r\n // Use resources\r\n const resources = options.customLogic.resources;\r\n if (resources) {\r\n const injectedJs = [];\r\n\r\n // Load custom JS code\r\n if (resources.js) {\r\n injectedJs.push({\r\n content: resources.js\r\n });\r\n }\r\n\r\n // Load scripts from all custom files\r\n if (resources.files) {\r\n for (const file of resources.files) {\r\n const isLocal = !file.startsWith('http') ? true : false;\r\n\r\n // Add each custom script from resources' files\r\n injectedJs.push(\r\n isLocal\r\n ? {\r\n content: readFileSync(file, 'utf8')\r\n }\r\n : {\r\n url: file\r\n }\r\n );\r\n }\r\n }\r\n\r\n for (const jsResource of injectedJs) {\r\n try {\r\n injectedResources.push(await page.addScriptTag(jsResource));\r\n } catch (error) {\r\n logWithStack(2, error, `[export] The JS resource cannot be loaded.`);\r\n }\r\n }\r\n injectedJs.length = 0;\r\n\r\n // Load CSS\r\n const injectedCss = [];\r\n if (resources.css) {\r\n let cssImports = resources.css.match(/@import\\s*([^;]*);/g);\r\n if (cssImports) {\r\n // Handle css section\r\n for (let cssImportPath of cssImports) {\r\n if (cssImportPath) {\r\n cssImportPath = cssImportPath\r\n .replace('url(', '')\r\n .replace('@import', '')\r\n .replace(/\"/g, '')\r\n .replace(/'/g, '')\r\n .replace(/;/, '')\r\n .replace(/\\)/g, '')\r\n .trim();\r\n\r\n // Add each custom css from resources\r\n if (cssImportPath.startsWith('http')) {\r\n injectedCss.push({\r\n url: cssImportPath\r\n });\r\n } else if (options.customLogic.allowFileResources) {\r\n injectedCss.push({\r\n path: path.join(__dirname, cssImportPath)\r\n });\r\n }\r\n }\r\n }\r\n }\r\n\r\n // The rest of the CSS section will be content by now\r\n injectedCss.push({\r\n content: resources.css.replace(/@import\\s*([^;]*);/g, '') || ' '\r\n });\r\n\r\n for (const cssResource of injectedCss) {\r\n try {\r\n injectedResources.push(await page.addStyleTag(cssResource));\r\n } catch (error) {\r\n logWithStack(2, error, `[export] The CSS resource cannot be loaded.`);\r\n }\r\n }\r\n injectedCss.length = 0;\r\n }\r\n }\r\n return injectedResources;\r\n}\r\n\r\n/**\r\n * Clears out all state set on the page with addScriptTag/addStyleTag. Removes\r\n * injected resources and resets CSS and script tags on the page. Additionally,\r\n * it destroys previously existing charts.\r\n *\r\n * @param {Object} page - The Puppeteer Page object from which resources will\r\n * be cleared.\r\n * @param {Array} injectedResources - Array of injected resources\r\n * to be cleared.\r\n */\r\nexport async function clearPageResources(page, injectedResources) {\r\n try {\r\n for (const resource of injectedResources) {\r\n await resource.dispose();\r\n }\r\n\r\n // Destroy old charts after export is done and reset all CSS and script tags\r\n await page.evaluate(() => {\r\n // We are not guaranteed that Highcharts is loaded, e,g, when doing SVG\r\n // exports\r\n if (typeof Highcharts !== 'undefined') {\r\n // eslint-disable-next-line no-undef\r\n const oldCharts = Highcharts.charts;\r\n\r\n // Check in any already existing charts\r\n if (Array.isArray(oldCharts) && oldCharts.length) {\r\n // Destroy old charts\r\n for (const oldChart of oldCharts) {\r\n oldChart && oldChart.destroy();\r\n // eslint-disable-next-line no-undef\r\n Highcharts.charts.shift();\r\n }\r\n }\r\n }\r\n\r\n // eslint-disable-next-line no-undef\r\n const [...scriptsToRemove] = document.getElementsByTagName('script');\r\n // eslint-disable-next-line no-undef\r\n const [, ...stylesToRemove] = document.getElementsByTagName('style');\r\n // eslint-disable-next-line no-undef\r\n const [...linksToRemove] = document.getElementsByTagName('link');\r\n\r\n // Remove tags\r\n for (const element of [\r\n ...scriptsToRemove,\r\n ...stylesToRemove,\r\n ...linksToRemove\r\n ]) {\r\n element.remove();\r\n }\r\n });\r\n } catch (error) {\r\n logWithStack(2, error, `[browser] Could not clear page's resources.`);\r\n }\r\n}\r\n\r\n/**\r\n * Sets the content for a Puppeteer Page using a predefined template\r\n * and additional scripts. Also, sets the pageerror in order to catch\r\n * and display errors from the window context.\r\n *\r\n * @param {Object} page - The Puppeteer Page object for which the content\r\n * is being set.\r\n */\r\nasync function setPageContent(page) {\r\n await page.setContent(template, { waitUntil: 'domcontentloaded' });\r\n\r\n // Add all registered Higcharts scripts, quite demanding\r\n await page.addScriptTag({ path: `${getCachePath()}/sources.js` });\r\n\r\n // Set the initial animObject\r\n await page.evaluate(setupHighcharts);\r\n}\r\n\r\n/**\r\n * Set events for a Puppeteer Page.\r\n *\r\n * @param {Object} page - The Puppeteer Page object to set events to.\r\n */\r\nfunction setPageEvents(page) {\r\n // Get debug options\r\n const { debug } = getOptions();\r\n\r\n // Set the console listener, if needed\r\n if (debug.enable && debug.listenToConsole) {\r\n page.on('console', (message) => {\r\n console.log(`[debug] ${message.text()}`);\r\n });\r\n }\r\n\r\n // Set the pageerror listener\r\n page.on('pageerror', async (error) => {\r\n // It would seem like this may fire at the same time or shortly before\r\n // a page is closed.\r\n if (page.isClosed()) {\r\n return;\r\n }\r\n\r\n // TODO: Consider adding a switch here that turns on log(0) logging\r\n // on page errors.\r\n await page.$eval(\r\n '#container',\r\n (element, errorMessage) => {\r\n // eslint-disable-next-line no-undef\r\n if (window._displayErrors) {\r\n element.innerHTML = errorMessage;\r\n }\r\n },\r\n `

Chart input data error:

${error.toString()}`\r\n );\r\n });\r\n}\r\n\r\nexport default {\r\n get,\r\n create,\r\n close,\r\n newPage,\r\n clearPage,\r\n addPageResources,\r\n clearPageResources\r\n};\r\n","/*******************************************************************************\r\n\r\nHighcharts Export Server\r\n\r\nCopyright (c) 2016-2024, Highsoft\r\n\r\nLicenced under the MIT licence.\r\n\r\nAdditionally a valid Highcharts license is required for use.\r\n\r\nSee LICENSE file in root for details.\r\n\r\n*******************************************************************************/\r\n\r\nimport { addPageResources, clearPageResources } from './browser.js';\r\nimport { getCache } from './cache.js';\r\nimport { triggerExport } from './highcharts.js';\r\nimport { log } from './logger.js';\r\n\r\nimport svgTemplate from './../templates/svg_export/svg_export.js';\r\n\r\nimport ExportError from './errors/ExportError.js';\r\n\r\n/**\r\n * Retrieves the clipping region coordinates of the specified page element with\r\n * the id 'chart-container'.\r\n *\r\n * @param {Object} page - Puppeteer page object.\r\n *\r\n * @returns {Promise} Promise resolving to an object containing\r\n * x, y, width, and height properties.\r\n */\r\nconst getClipRegion = (page) =>\r\n page.$eval('#chart-container', (element) => {\r\n const { x, y, width, height } = element.getBoundingClientRect();\r\n return {\r\n x,\r\n y,\r\n width,\r\n height: Math.trunc(height > 1 ? height : 500)\r\n };\r\n });\r\n\r\n/**\r\n * Creates an image using Puppeteer's page screenshot functionality with\r\n * specified options.\r\n *\r\n * @param {Object} page - Puppeteer page object.\r\n * @param {string} type - Image type.\r\n * @param {string} encoding - Image encoding.\r\n * @param {Object} clip - Clipping region coordinates.\r\n * @param {number} rasterizationTimeout - Timeout for rasterization\r\n * in milliseconds.\r\n *\r\n * @returns {Promise} Promise resolving to the image buffer or rejecting\r\n * with an ExportError for timeout.\r\n */\r\nconst createImage = (page, type, encoding, clip, rasterizationTimeout) =>\r\n Promise.race([\r\n page.screenshot({\r\n type,\r\n encoding,\r\n clip,\r\n captureBeyondViewport: true,\r\n fullPage: false,\r\n optimizeForSpeed: true,\r\n ...(type !== 'png' ? { quality: 80 } : {}),\r\n\r\n // #447, #463 - always render on a transparent page if the expected type\r\n // format is PNG\r\n omitBackground: type == 'png'\r\n }),\r\n new Promise((_resolve, reject) =>\r\n setTimeout(\r\n () => reject(new ExportError('Rasterization timeout')),\r\n rasterizationTimeout || 1500\r\n )\r\n )\r\n ]);\r\n\r\n/**\r\n * Creates a PDF using Puppeteer's page pdf functionality with specified\r\n * options.\r\n *\r\n * @param {Object} page - Puppeteer page object.\r\n * @param {number} height - PDF height.\r\n * @param {number} width - PDF width.\r\n * @param {string} encoding - PDF encoding.\r\n *\r\n * @returns {Promise} Promise resolving to the PDF buffer.\r\n */\r\nconst createPDF = async (\r\n page,\r\n height,\r\n width,\r\n encoding,\r\n rasterizationTimeout\r\n) => {\r\n await page.emulateMediaType('screen');\r\n\r\n return 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 timeout: rasterizationTimeout || 1500\r\n });\r\n};\r\n\r\n/**\r\n * Creates an SVG string by evaluating the outerHTML of the first 'svg' element\r\n * inside an element with the id 'container'.\r\n *\r\n * @param {Object} page - Puppeteer page object.\r\n *\r\n * @returns {Promise} Promise resolving to the SVG string.\r\n */\r\nconst createSVG = (page) =>\r\n page.$eval('#container svg:first-of-type', (element) => element.outerHTML);\r\n\r\n/**\r\n * Sets the specified chart and options as configuration into the triggerExport\r\n * function within the window context using page.evaluate.\r\n *\r\n * @param {Object} page - Puppeteer page object.\r\n * @param {any} chart - The chart object to be configured.\r\n * @param {Object} options - Configuration options for the chart.\r\n *\r\n * @returns {Promise} Promise resolving after the configuration is set.\r\n */\r\nconst setAsConfig = async (page, chart, options, displayErrors) => {\r\n // Get rid of the redunant string data\r\n options.export.instr = null;\r\n options.export.infile = null;\r\n\r\n // Get the size of the export input\r\n const totalSize = Buffer.byteLength(\r\n options.export?.strInj ? options.export?.strInj : JSON.stringify(chart),\r\n 'utf-8'\r\n );\r\n\r\n // Log the size in MB\r\n log(\r\n 4,\r\n `[export] The current total size of data passed to a page is around ${(\r\n totalSize /\r\n (1024 * 1024)\r\n ).toFixed(2)} MB`\r\n );\r\n\r\n // Check the size of data passed to the page\r\n if (totalSize >= 100 * 1024 * 1024) {\r\n throw new ExportError(`[export] The data passed to a page exceeded 100MB.`);\r\n }\r\n\r\n // Trigger the Highcharts chart creation\r\n return page.evaluate(triggerExport, chart, options, displayErrors);\r\n};\r\n\r\n/**\r\n * Exports to a chart from a page using Puppeteer.\r\n *\r\n * @param {Object} page - Puppeteer page object.\r\n * @param {any} chart - The chart object or SVG configuration to be exported.\r\n * @param {Object} options - Export options and configuration.\r\n *\r\n * @returns {Promise} Promise resolving to\r\n * the exported data or rejecting with an ExportError.\r\n */\r\nexport default async (page, chart, options) => {\r\n // Injected resources array (additional JS and CSS)\r\n let injectedResources = [];\r\n\r\n try {\r\n log(4, '[export] Determining export path.');\r\n\r\n const exportOptions = options.export;\r\n\r\n // Decide whether display error or debbuger wrapper around it\r\n const displayErrors =\r\n exportOptions?.options?.chart?.displayErrors &&\r\n getCache().activeManifest.modules.debugger;\r\n\r\n let isSVG;\r\n if (\r\n chart.indexOf &&\r\n (chart.indexOf('= 0 || chart.indexOf('= 0)\r\n ) {\r\n // SVG input handling\r\n log(4, '[export] Treating as SVG.');\r\n\r\n // If input is also SVG, just return it\r\n if (exportOptions.type === 'svg') {\r\n return chart;\r\n }\r\n\r\n isSVG = true;\r\n await page.setContent(svgTemplate(chart), {\r\n waitUntil: 'domcontentloaded'\r\n });\r\n } else {\r\n // JSON config handling\r\n log(4, '[export] Treating as config.');\r\n\r\n // Need to perform straight inject\r\n if (exportOptions.strInj) {\r\n // Injection based configuration export\r\n await setAsConfig(\r\n page,\r\n {\r\n chart: {\r\n height: exportOptions.height,\r\n width: exportOptions.width\r\n }\r\n },\r\n options,\r\n displayErrors\r\n );\r\n } else {\r\n // Basic configuration export\r\n chart.chart.height = exportOptions.height;\r\n chart.chart.width = exportOptions.width;\r\n\r\n await setAsConfig(page, chart, options, displayErrors);\r\n }\r\n }\r\n\r\n // Keeps track of all resources added on the page with addXXXTag. etc\r\n // It's VITAL that all added resources ends up here so we can clear things\r\n // out when doing a new export in the same page!\r\n injectedResources = await addPageResources(page, options);\r\n\r\n // Get the real chart size and set the zoom accordingly\r\n const size = isSVG\r\n ? await page.evaluate((scale) => {\r\n const svgElement = document.querySelector(\r\n '#chart-container svg:first-of-type'\r\n );\r\n\r\n // Get the values correctly scaled\r\n const chartHeight = svgElement.height.baseVal.value * scale;\r\n const chartWidth = svgElement.width.baseVal.value * scale;\r\n\r\n // In case of SVG the zoom must be set directly for body\r\n // Set the zoom as scale\r\n // eslint-disable-next-line no-undef\r\n document.body.style.zoom = scale;\r\n\r\n // Set the margin to 0px\r\n // eslint-disable-next-line no-undef\r\n document.body.style.margin = '0px';\r\n\r\n return {\r\n chartHeight,\r\n chartWidth\r\n };\r\n }, parseFloat(exportOptions.scale))\r\n : await page.evaluate(() => {\r\n // eslint-disable-next-line no-undef\r\n const { chartHeight, chartWidth } = window.Highcharts.charts[0];\r\n\r\n // No need for such scale manipulation in case of other types of exports\r\n // Reset the zoom for other exports than to SVGs\r\n // eslint-disable-next-line no-undef\r\n document.body.style.zoom = 1;\r\n\r\n return {\r\n chartHeight,\r\n chartWidth\r\n };\r\n });\r\n\r\n // Set final height and width for viewport\r\n const viewportHeight = Math.abs(\r\n Math.ceil(size.chartHeight || exportOptions.height)\r\n );\r\n const viewportWidth = Math.abs(\r\n Math.ceil(size.chartWidth || exportOptions.width)\r\n );\r\n\r\n // Get the clip region for the page\r\n const { x, y } = await getClipRegion(page);\r\n\r\n // Set the final viewport now that we have the real height\r\n await page.setViewport({\r\n height: viewportHeight,\r\n width: viewportWidth,\r\n deviceScaleFactor: isSVG ? 1 : parseFloat(exportOptions.scale)\r\n });\r\n\r\n let data;\r\n // Rasterization process\r\n if (exportOptions.type === 'svg') {\r\n // SVG\r\n data = await createSVG(page);\r\n } else if (['png', 'jpeg'].includes(exportOptions.type)) {\r\n // PNG or JPEG\r\n data = await createImage(\r\n page,\r\n exportOptions.type,\r\n 'base64',\r\n {\r\n width: viewportWidth,\r\n height: viewportHeight,\r\n x,\r\n y\r\n },\r\n exportOptions.rasterizationTimeout\r\n );\r\n } else if (exportOptions.type === 'pdf') {\r\n // PDF\r\n data = await createPDF(\r\n page,\r\n viewportHeight,\r\n viewportWidth,\r\n 'base64',\r\n exportOptions.rasterizationTimeout\r\n );\r\n } else {\r\n throw new ExportError(\r\n `[export] Unsupported output format ${exportOptions.type}.`\r\n );\r\n }\r\n\r\n // Clear previously injected JS and CSS resources\r\n await clearPageResources(page, injectedResources);\r\n return data;\r\n } catch (error) {\r\n await clearPageResources(page, injectedResources);\r\n return error;\r\n }\r\n};\r\n","/*******************************************************************************\r\n\r\nHighcharts Export Server\r\n\r\nCopyright (c) 2016-2024, Highsoft\r\n\r\nLicenced under the MIT licence.\r\n\r\nAdditionally a valid Highcharts license is required for use.\r\n\r\nSee LICENSE file in root for details.\r\n\r\n*******************************************************************************/\r\n\r\nimport cssTemplate from './css.js';\r\n\r\nexport default (chart) => `\r\n\r\n\r\n \r\n \r\n Highcharts Export\r\n \r\n \r\n \r\n
\r\n ${chart}\r\n
\r\n \r\n\r\n\r\n`;\r\n","/*******************************************************************************\r\n\r\nHighcharts Export Server\r\n\r\nCopyright (c) 2016-2024, Highsoft\r\n\r\nLicenced under the MIT licence.\r\n\r\nAdditionally a valid Highcharts license is required for use.\r\n\r\nSee LICENSE file in root for details.\r\n\r\n*******************************************************************************/\r\n\r\nimport { Pool } from 'tarn';\r\nimport { v4 as uuid } from 'uuid';\r\n\r\nimport {\r\n create as createBrowser,\r\n close as closeBrowser,\r\n newPage,\r\n clearPage\r\n} from './browser.js';\r\nimport puppeteerExport from './export.js';\r\nimport { log, logWithStack } from './logger.js';\r\nimport { measureTime } from './utils.js';\r\n\r\nimport ExportError from './errors/ExportError.js';\r\n\r\n// The pool instance\r\nlet pool = false;\r\n\r\n// Pool statistics\r\nexport const stats = {\r\n performedExports: 0,\r\n exportAttempts: 0,\r\n exportFromSvgAttempts: 0,\r\n timeSpent: 0,\r\n droppedExports: 0,\r\n spentAverage: 0\r\n};\r\n\r\nlet poolConfig = {};\r\n\r\nconst factory = {\r\n /**\r\n * Creates a new worker page for the export pool.\r\n *\r\n * @returns {Object} - An object containing the worker ID, a reference to the\r\n * browser page, and initial work count.\r\n *\r\n * @throws {ExportError} - If there's an error during the creation of the new\r\n * page.\r\n */\r\n create: async () => {\r\n let page = false;\r\n\r\n const id = uuid();\r\n const startDate = new Date().getTime();\r\n\r\n try {\r\n page = await newPage();\r\n\r\n if (!page || page.isClosed()) {\r\n throw new ExportError('The page is invalid or closed.');\r\n }\r\n\r\n log(\r\n 3,\r\n `[pool] Successfully created a worker ${id} - took ${\r\n new Date().getTime() - startDate\r\n } ms.`\r\n );\r\n } catch (error) {\r\n throw new ExportError(\r\n 'Error encountered when creating a new page.'\r\n ).setError(error);\r\n }\r\n\r\n return {\r\n id,\r\n page,\r\n // Try to distribute the initial work count\r\n workCount: Math.round(Math.random() * (poolConfig.workLimit / 2))\r\n };\r\n },\r\n\r\n /**\r\n * Validates a worker page in the export pool, checking if it has exceeded\r\n * the work limit.\r\n *\r\n * @param {Object} workerHandle - The handle to the worker, containing the\r\n * worker's ID, a reference to the browser page, and work count.\r\n *\r\n * @returns {boolean} - Returns true if the worker is valid and within\r\n * the work limit; otherwise, returns false.\r\n */\r\n validate: async (workerHandle) => {\r\n // NOTE: In certain cases acquiring throws a TargetCloseError, which may\r\n // be caused by two things:\r\n // - The page is closed and attempted to be reused.\r\n // - Lost contact with the browser\r\n // What we're seeing in logs is that successive exports typically\r\n // succeeds, and the server recovers, indicating that it's likely\r\n // the first case. This is an attempt at allievating the issue by\r\n // simply not validating the worker if the page is null or closed.\r\n //\r\n // The actual result from when this happened, was that a worker would\r\n // be completely locked, stopping it from being acquired until\r\n // its work count reached the limit.\r\n if (!workerHandle.page || workerHandle.page?.isClosed()) {\r\n return false;\r\n }\r\n\r\n if (\r\n poolConfig.workLimit &&\r\n ++workerHandle.workCount > poolConfig.workLimit\r\n ) {\r\n log(\r\n 3,\r\n `[pool] Worker failed validation: exceeded work limit (limit is ${poolConfig.workLimit}).`\r\n );\r\n return false;\r\n }\r\n return true;\r\n },\r\n\r\n /**\r\n * Destroys a worker entry in the export pool, closing its associated page.\r\n *\r\n * @param {Object} workerHandle - The handle to the worker, containing\r\n * the worker's ID and a reference to the browser page.\r\n */\r\n destroy: async (workerHandle) => {\r\n log(3, `[pool] Destroying pool entry ${workerHandle.id}.`);\r\n\r\n if (workerHandle.page && !workerHandle.page.isClosed()) {\r\n await workerHandle.page.close();\r\n }\r\n }\r\n\r\n // log: (message, level) => log(1, '[tarn] ' + message)\r\n};\r\n\r\n/**\r\n * Initializes the export pool with the provided configuration, creating\r\n * a browser instance and setting up worker resources.\r\n *\r\n * @param {Object} config - Configuration options for the export pool along\r\n * with custom puppeteer arguments for the puppeteer.launch function.\r\n */\r\nexport const initPool = async (config) => {\r\n // For the module scope usage\r\n poolConfig = config && config.pool ? { ...config.pool } : {};\r\n\r\n // Create a browser instance with the puppeteer arguments\r\n await createBrowser(config.puppeteerArgs);\r\n\r\n log(\r\n 3,\r\n `[pool] Initializing pool with workers: min ${poolConfig.minWorkers}, max ${poolConfig.maxWorkers}.`\r\n );\r\n\r\n if (pool) {\r\n return log(\r\n 4,\r\n '[pool] Already initialized, please kill it before creating a new one.'\r\n );\r\n }\r\n\r\n if (parseInt(poolConfig.minWorkers) > parseInt(poolConfig.maxWorkers)) {\r\n poolConfig.minWorkers = poolConfig.maxWorkers;\r\n }\r\n\r\n try {\r\n // Create a pool along with a minimal number of resources\r\n pool = new Pool({\r\n // Get the create/validate/destroy/log functions\r\n ...factory,\r\n min: parseInt(poolConfig.minWorkers),\r\n max: parseInt(poolConfig.maxWorkers),\r\n acquireTimeoutMillis: poolConfig.acquireTimeout,\r\n createTimeoutMillis: poolConfig.createTimeout,\r\n destroyTimeoutMillis: poolConfig.destroyTimeout,\r\n idleTimeoutMillis: poolConfig.idleTimeout,\r\n createRetryIntervalMillis: poolConfig.createRetryInterval,\r\n reapIntervalMillis: poolConfig.reaperInterval,\r\n propagateCreateError: false\r\n });\r\n\r\n // Set events\r\n pool.on('release', async (resource) => {\r\n // Clear page\r\n const r = await clearPage(resource.page, false);\r\n log(\r\n 4,\r\n `[pool] Releasing a worker with ID ${resource.id}. Clear page status: ${r}.`\r\n );\r\n });\r\n\r\n pool.on('destroySuccess', (eventId, resource) => {\r\n log(4, `[pool] Destroyed a worker with ID ${resource.id}.`);\r\n resource.page = null;\r\n });\r\n\r\n const initialResources = [];\r\n // Create an initial number of resources\r\n for (let i = 0; i < poolConfig.minWorkers; i++) {\r\n try {\r\n const resource = await pool.acquire().promise;\r\n initialResources.push(resource);\r\n } catch (error) {\r\n logWithStack(2, error, '[pool] Could not create an initial resource.');\r\n }\r\n }\r\n\r\n // Release the initial number of resources back to the pool\r\n initialResources.forEach((resource) => {\r\n pool.release(resource);\r\n });\r\n\r\n log(\r\n 3,\r\n `[pool] The pool is ready${initialResources.length ? ` with ${initialResources.length} initial resources waiting.` : '.'}`\r\n );\r\n } catch (error) {\r\n throw new ExportError(\r\n '[pool] Could not create the pool of workers.'\r\n ).setError(error);\r\n }\r\n};\r\n\r\n/**\r\n * Kills all workers in the pool, destroys the pool, and closes the browser\r\n * instance.\r\n *\r\n * @returns {Promise} A promise that resolves after the workers are\r\n * killed, the pool is destroyed, and the browser is closed.\r\n */\r\nexport async function killPool() {\r\n log(3, '[pool] Killing pool with all workers and closing browser.');\r\n\r\n // If still alive, destroy the pool of pages before closing a browser\r\n if (pool) {\r\n // Free up not released workers\r\n for (const worker of pool.used) {\r\n pool.release(worker.resource);\r\n }\r\n\r\n // Destroy the pool if it is still available\r\n if (!pool.destroyed) {\r\n await pool.destroy();\r\n log(4, '[browser] Destroyed the pool of resources.');\r\n }\r\n }\r\n\r\n // Close the browser instance\r\n await closeBrowser();\r\n}\r\n\r\n/**\r\n * Processes the export work using a worker from the pool. Acquires a worker\r\n * handle from the pool, performs the export using puppeteer, and releases\r\n * the worker handle back to the pool.\r\n *\r\n * @param {string} chart - The chart data or configuration to be exported.\r\n * @param {Object} options - Export options and configuration.\r\n *\r\n * @returns {Promise} A promise that resolves with the export resultand\r\n * options.\r\n *\r\n * @throws {ExportError} If an error occurs during the export process.\r\n */\r\nexport const postWork = async (chart, options) => {\r\n let workerHandle;\r\n\r\n try {\r\n log(4, '[pool] Work received, starting to process.');\r\n\r\n ++stats.exportAttempts;\r\n if (poolConfig.benchmarking) {\r\n getPoolInfo();\r\n }\r\n\r\n if (!pool) {\r\n throw new ExportError('Work received, but pool has not been started.');\r\n }\r\n\r\n // Acquire the worker along with the id of resource and work count\r\n const acquireCounter = measureTime();\r\n try {\r\n log(4, '[pool] Acquiring a worker handle.');\r\n workerHandle = await pool.acquire().promise;\r\n\r\n // Check the page acquire time\r\n if (options.server.benchmarking) {\r\n log(\r\n 5,\r\n options.payload?.requestId\r\n ? `[benchmark] Request with ID ${options.payload?.requestId} -`\r\n : '[benchmark]',\r\n `Acquired a worker handle: ${acquireCounter()}ms.`\r\n );\r\n }\r\n } catch (error) {\r\n throw new ExportError(\r\n (options.payload?.requestId\r\n ? `For request with ID ${options.payload?.requestId} - `\r\n : '') +\r\n `Error encountered when acquiring an available entry: ${acquireCounter()}ms.`\r\n ).setError(error);\r\n }\r\n log(4, '[pool] Acquired a worker handle.');\r\n\r\n if (!workerHandle.page) {\r\n throw new ExportError(\r\n 'Resolved worker page is invalid: the pool setup is wonky.'\r\n );\r\n }\r\n\r\n // Save the start time\r\n let workStart = new Date().getTime();\r\n\r\n log(4, `[pool] Starting work on pool entry with ID ${workerHandle.id}.`);\r\n\r\n // Perform an export on a puppeteer level\r\n const exportCounter = measureTime();\r\n const result = await puppeteerExport(workerHandle.page, chart, options);\r\n\r\n // Check if it's an error\r\n if (result instanceof Error) {\r\n // NOTE: If there's a rasterization timeout, we want need to flush the page.\r\n // This is because the page may be in a state where it's waiting for\r\n // the screenshot to finish even though the timeout has occured.\r\n // Which of course causes a lot of issues with the event system,\r\n // and page consistency.\r\n //\r\n // NOTE: Only page.screenshot will throw this, timeouts for PDF's are\r\n // handled by the page.pdf function itself.\r\n //\r\n // ...yes, this is ugly.\r\n if (result.message === 'Rasterization timeout') {\r\n workerHandle.workCount = poolConfig.workLimit + 1;\r\n workerHandle.page = null;\r\n }\r\n\r\n if (\r\n result.name === 'TimeoutError' ||\r\n result.message === 'Rasterization timeout'\r\n ) {\r\n throw new ExportError(\r\n 'Rasterization timeout: your chart may be too complex or large, and failed to render within the allotted time.'\r\n ).setError(result);\r\n } else {\r\n throw new ExportError(\r\n (options.payload?.requestId\r\n ? `For request with ID ${options.payload?.requestId} - `\r\n : '') + `Error encountered during export: ${exportCounter()}ms.`\r\n ).setError(result);\r\n }\r\n }\r\n\r\n // Check the Puppeteer export time\r\n if (options.server.benchmarking) {\r\n log(\r\n 5,\r\n options.payload?.requestId\r\n ? `[benchmark] Request with ID ${options.payload?.requestId} -`\r\n : '[benchmark]',\r\n `Exported a chart sucessfully: ${exportCounter()}ms.`\r\n );\r\n }\r\n\r\n // Release the resource back to the pool\r\n pool.release(workerHandle);\r\n\r\n // Used for statistics in averageTime and processedWorkCount, which\r\n // in turn is used by the /health route.\r\n const workEnd = new Date().getTime();\r\n const exportTime = workEnd - workStart;\r\n stats.timeSpent += exportTime;\r\n stats.spentAverage = stats.timeSpent / ++stats.performedExports;\r\n\r\n log(4, `[pool] Work completed in ${exportTime} ms.`);\r\n\r\n // Otherwise return the result\r\n return {\r\n result,\r\n options\r\n };\r\n } catch (error) {\r\n ++stats.droppedExports;\r\n\r\n if (workerHandle) {\r\n pool.release(workerHandle);\r\n }\r\n\r\n throw new ExportError(`[pool] In pool.postWork: ${error.message}`).setError(\r\n error\r\n );\r\n }\r\n};\r\n\r\n/**\r\n * Retrieves the current pool instance.\r\n *\r\n * @returns {Object|null} The current pool instance if initialized, or null\r\n * if the pool has not been created.\r\n */\r\nexport const getPool = () => pool;\r\n\r\n/**\r\n * Retrieves pool information in JSON format, including minimum and maximum\r\n * workers, available workers, workers in use, and pending acquire requests.\r\n *\r\n * @returns {Object} Pool information in JSON format.\r\n */\r\nexport const getPoolInfoJSON = () => ({\r\n min: pool.min,\r\n max: pool.max,\r\n all: pool.numFree() + pool.numUsed(),\r\n available: pool.numFree(),\r\n used: pool.numUsed(),\r\n pending: pool.numPendingAcquires()\r\n});\r\n\r\n/**\r\n * Logs information about the current state of the pool, including the minimum\r\n * and maximum workers, available workers, workers in use, and pending acquire\r\n * requests.\r\n */\r\nexport function getPoolInfo() {\r\n const { min, max, all, available, used, pending } = getPoolInfoJSON();\r\n\r\n log(5, `[pool] The minimum number of resources allowed by pool: ${min}.`);\r\n log(5, `[pool] The maximum number of resources allowed by pool: ${max}.`);\r\n log(5, `[pool] The number of all created resources: ${all}.`);\r\n log(5, `[pool] The number of available resources: ${available}.`);\r\n log(5, `[pool] The number of acquired resources: ${used}.`);\r\n log(5, `[pool] The number of resources waiting to be acquired: ${pending}.`);\r\n}\r\n\r\nexport default {\r\n initPool,\r\n killPool,\r\n postWork,\r\n getPool,\r\n getPoolInfo,\r\n getPoolInfoJSON,\r\n getStats: () => stats\r\n};\r\n","/*******************************************************************************\r\n\r\nHighcharts Export Server\r\n\r\nCopyright (c) 2016-2024, Highsoft\r\n\r\nLicenced under the MIT licence.\r\n\r\nAdditionally a valid Highcharts license is required for use.\r\n\r\nSee LICENSE file in root for details.\r\n\r\n*******************************************************************************/\r\n\r\nimport { readFileSync, writeFileSync } from 'fs';\r\n\r\nimport { getOptions, initExportSettings } from './config.js';\r\nimport { log, logWithStack } from './logger.js';\r\nimport { killPool, postWork, stats } from './pool.js';\r\nimport {\r\n fixType,\r\n handleResources,\r\n isCorrectJSON,\r\n optionsStringify,\r\n roundNumber,\r\n toBoolean,\r\n wrapAround\r\n} from './utils.js';\r\nimport { sanitize } from './sanitize.js';\r\nimport ExportError from './errors/ExportError.js';\r\n\r\nlet allowCodeExecution = false;\r\n\r\n/**\r\n * Starts an export process. The `settings` contains final options gathered\r\n * from all possible sources (config, env, cli, json). The `endCallback` is\r\n * called when the export is completed, with an error object as the first\r\n * argument and the second containing the base64 respresentation of a chart.\r\n *\r\n * @param {Object} settings - The settings object containing export\r\n * configuration.\r\n * @param {function} endCallback - The callback function to be invoked upon\r\n * finalizing work or upon error occurance of the exporting process.\r\n *\r\n * @returns {void} This function does not return a value directly; instead,\r\n * it communicates results via the endCallback.\r\n */\r\nexport const startExport = async (settings, endCallback) => {\r\n // Starting exporting process message\r\n log(4, '[chart] Starting the exporting process.');\r\n\r\n // Initialize options\r\n const options = initExportSettings(settings, getOptions());\r\n\r\n // Get the export options\r\n const exportOptions = options.export;\r\n\r\n // If SVG is an input (argument can be sent only by the request)\r\n if (options.payload?.svg && options.payload.svg !== '') {\r\n try {\r\n log(4, '[chart] Attempting to export from a SVG input.');\r\n\r\n const result = exportAsString(\r\n sanitize(options.payload.svg), // #209\r\n options,\r\n endCallback\r\n );\r\n\r\n ++stats.exportFromSvgAttempts;\r\n return result;\r\n } catch (error) {\r\n return endCallback(\r\n new ExportError('[chart] Error loading SVG input.').setError(error)\r\n );\r\n }\r\n }\r\n\r\n // Export using options from the file\r\n if (exportOptions.infile && exportOptions.infile.length) {\r\n // Try to read the file to get the string representation\r\n try {\r\n log(4, '[chart] Attempting to export from an input file.');\r\n options.export.instr = readFileSync(exportOptions.infile, 'utf8');\r\n return exportAsString(options.export.instr.trim(), options, endCallback);\r\n } catch (error) {\r\n return endCallback(\r\n new ExportError('[chart] Error loading input file.').setError(error)\r\n );\r\n }\r\n }\r\n\r\n // Export with options from the raw representation\r\n if (\r\n (exportOptions.instr && exportOptions.instr !== '') ||\r\n (exportOptions.options && exportOptions.options !== '')\r\n ) {\r\n try {\r\n log(4, '[chart] Attempting to export from a raw input.');\r\n\r\n // Use whichever one is available\r\n exportOptions.instr = exportOptions.instr || exportOptions.options;\r\n\r\n // Perform a direct inject when forced\r\n if (toBoolean(options.customLogic?.allowCodeExecution)) {\r\n return doStraightInject(options, endCallback);\r\n }\r\n\r\n // Either try to parse to JSON first or do the direct export\r\n return typeof exportOptions.instr === 'string'\r\n ? exportAsString(exportOptions.instr.trim(), options, endCallback)\r\n : doExport(\r\n options,\r\n exportOptions.instr || exportOptions.options,\r\n endCallback\r\n );\r\n } catch (error) {\r\n return endCallback(\r\n new ExportError('[chart] Error loading raw input.').setError(error)\r\n );\r\n }\r\n }\r\n\r\n // No input specified, pass an error message to the callback\r\n return endCallback(\r\n new ExportError(\r\n `[chart] No valid input specified. Check if at least one of the following parameters is correctly set: 'infile', 'instr', 'options', or 'svg'.`\r\n )\r\n );\r\n};\r\n\r\n/**\r\n * Starts a batch export process for multiple charts based on the information\r\n * in the batch option. The batch is a string in the following format:\r\n * \"infile1.json=outfile1.png;infile2.json=outfile2.png;...\"\r\n *\r\n * @param {Object} options - The options object containing configuration for\r\n * a batch export.\r\n *\r\n * @returns {Promise} A Promise that resolves once the batch export\r\n * process is completed.\r\n *\r\n * @throws {ExportError} Throws an ExportError if an error occurs during\r\n * any of the batch export process.\r\n */\r\nexport const batchExport = async (options) => {\r\n const batchFunctions = [];\r\n\r\n // Split and pair the --batch arguments\r\n for (let pair of options.export.batch.split(';')) {\r\n pair = pair.split('=');\r\n if (pair.length === 2) {\r\n batchFunctions.push(\r\n startExport(\r\n {\r\n ...options,\r\n export: {\r\n ...options.export,\r\n infile: pair[0],\r\n outfile: pair[1]\r\n }\r\n },\r\n (error, info) => {\r\n // Throw an error\r\n if (error) {\r\n throw error;\r\n }\r\n\r\n // Save the base64 from a buffer to a correct image file\r\n writeFileSync(\r\n info.options.export.outfile,\r\n info.options.export.type !== 'svg'\r\n ? Buffer.from(info.result, 'base64')\r\n : info.result\r\n );\r\n }\r\n )\r\n );\r\n }\r\n }\r\n\r\n try {\r\n // Await all exports are done\r\n await Promise.all(batchFunctions);\r\n\r\n // Kill pool and close browser after finishing batch export\r\n await killPool();\r\n } catch (error) {\r\n throw new ExportError(\r\n '[chart] Error encountered during batch export.'\r\n ).setError(error);\r\n }\r\n};\r\n\r\n/**\r\n * Starts a single export process based on the specified options.\r\n *\r\n * @param {Object} options - The options object containing configuration for\r\n * a single export.\r\n *\r\n * @returns {Promise} A Promise that resolves once the single export\r\n * process is completed.\r\n *\r\n * @throws {ExportError} Throws an ExportError if an error occurs during\r\n * the single export process.\r\n */\r\nexport const singleExport = async (options) => {\r\n // Use instr or its alias, options\r\n options.export.instr = options.export.instr || options.export.options;\r\n\r\n // Perform an export\r\n await startExport(options, async (error, info) => {\r\n // Exit process when error\r\n if (error) {\r\n throw error;\r\n }\r\n\r\n const { outfile, type } = info.options.export;\r\n\r\n // Save the base64 from a buffer to a correct image file\r\n writeFileSync(\r\n outfile || `chart.${type}`,\r\n type !== 'svg' ? Buffer.from(info.result, 'base64') : info.result\r\n );\r\n\r\n // Kill pool and close browser after finishing single export\r\n await killPool();\r\n });\r\n};\r\n\r\n/**\r\n * Determines the size and scale for chart export based on the provided options.\r\n *\r\n * @param {Object} options - The options object containing configuration for\r\n * chart export.\r\n *\r\n * @returns {Object} An object containing the calculated height, width,\r\n * and scale for the chart export.\r\n */\r\nexport const findChartSize = (options) => {\r\n const { chart, exporting } =\r\n options.export?.options || isCorrectJSON(options.export?.instr);\r\n\r\n // See if globalOptions holds chart or exporting size\r\n const globalOptions = isCorrectJSON(options.export?.globalOptions);\r\n\r\n // Secure scale value\r\n let scale =\r\n options.export?.scale ||\r\n exporting?.scale ||\r\n globalOptions?.exporting?.scale ||\r\n options.export?.defaultScale ||\r\n 1;\r\n\r\n // the scale cannot be lower than 0.1 and cannot be higher than 5.0\r\n scale = Math.max(0.1, Math.min(scale, 5.0));\r\n\r\n // we want to round the numbers like 0.23234 -> 0.23\r\n scale = roundNumber(scale, 2);\r\n\r\n // Find chart size and scale\r\n const size = {\r\n height:\r\n options.export?.height ||\r\n exporting?.sourceHeight ||\r\n chart?.height ||\r\n globalOptions?.exporting?.sourceHeight ||\r\n globalOptions?.chart?.height ||\r\n options.export?.defaultHeight ||\r\n 400,\r\n width:\r\n options.export?.width ||\r\n exporting?.sourceWidth ||\r\n chart?.width ||\r\n globalOptions?.exporting?.sourceWidth ||\r\n globalOptions?.chart?.width ||\r\n options.export?.defaultWidth ||\r\n 600,\r\n scale\r\n };\r\n\r\n // Get rid of potential px and %\r\n for (let [param, value] of Object.entries(size)) {\r\n size[param] =\r\n typeof value === 'string' ? +value.replace(/px|%/gi, '') : value;\r\n }\r\n return size;\r\n};\r\n\r\n/**\r\n * Function for finalizing options before export.\r\n *\r\n * @param {Object} options - The options object containing configuration for\r\n * the export process.\r\n * @param {Object} chartJson - The JSON representation of the chart.\r\n * @param {Function} endCallback - The callback function to be called upon\r\n * completion or error.\r\n * @param {string} svg - The SVG representation of the chart.\r\n *\r\n * @returns {Promise} A Promise that resolves once the export process\r\n * is completed.\r\n */\r\nconst doExport = async (options, chartJson, endCallback, svg) => {\r\n let { export: exportOptions, customLogic: customLogicOptions } = options;\r\n\r\n const allowCodeExecutionScoped =\r\n typeof customLogicOptions.allowCodeExecution === 'boolean'\r\n ? customLogicOptions.allowCodeExecution\r\n : allowCodeExecution;\r\n\r\n if (!customLogicOptions) {\r\n customLogicOptions = options.customLogic = {};\r\n } else if (allowCodeExecutionScoped) {\r\n if (typeof options.customLogic.resources === 'string') {\r\n // Process resources\r\n options.customLogic.resources = handleResources(\r\n options.customLogic.resources,\r\n toBoolean(options.customLogic.allowFileResources)\r\n );\r\n } else if (!options.customLogic.resources) {\r\n try {\r\n const resources = readFileSync('resources.json', 'utf8');\r\n options.customLogic.resources = handleResources(\r\n resources,\r\n toBoolean(options.customLogic.allowFileResources)\r\n );\r\n } catch (error) {\r\n log(2, `[chart] Unable to load the default resources.json file.`);\r\n }\r\n }\r\n }\r\n\r\n // If the allowCodeExecution flag isn't set, we should refuse the usage\r\n // of callback, resources, and custom code. Additionally, the worker will\r\n // refuse to run arbitrary JavaScript. Prioritized should be the scoped\r\n // option, then we should take a look at the overall pool option.\r\n if (!allowCodeExecutionScoped && customLogicOptions) {\r\n if (\r\n customLogicOptions.callback ||\r\n customLogicOptions.resources ||\r\n customLogicOptions.customCode\r\n ) {\r\n // Send back a friendly message saying that the exporter does not support\r\n // these settings.\r\n return endCallback(\r\n new ExportError(\r\n `[chart] The 'callback', 'resources' and 'customCode' options have been disabled for this server.`\r\n )\r\n );\r\n }\r\n\r\n // Reset all additional custom code\r\n customLogicOptions.callback = false;\r\n customLogicOptions.resources = false;\r\n customLogicOptions.customCode = false;\r\n }\r\n\r\n // Clean properties to keep it lean and mean\r\n if (chartJson) {\r\n chartJson.chart = chartJson.chart || {};\r\n chartJson.exporting = chartJson.exporting || {};\r\n chartJson.exporting.enabled = false;\r\n }\r\n\r\n exportOptions.constr = exportOptions.constr || 'chart';\r\n exportOptions.type = fixType(exportOptions.type, exportOptions.outfile);\r\n if (exportOptions.type === 'svg') {\r\n exportOptions.width = false;\r\n }\r\n\r\n // Prepare global and theme options\r\n ['globalOptions', 'themeOptions'].forEach((optionsName) => {\r\n try {\r\n if (exportOptions && exportOptions[optionsName]) {\r\n if (\r\n typeof exportOptions[optionsName] === 'string' &&\r\n exportOptions[optionsName].endsWith('.json')\r\n ) {\r\n exportOptions[optionsName] = isCorrectJSON(\r\n readFileSync(exportOptions[optionsName], 'utf8'),\r\n true\r\n );\r\n } else {\r\n exportOptions[optionsName] = isCorrectJSON(\r\n exportOptions[optionsName],\r\n true\r\n );\r\n }\r\n }\r\n } catch (error) {\r\n exportOptions[optionsName] = {};\r\n logWithStack(2, error, `[chart] The '${optionsName}' cannot be loaded.`);\r\n }\r\n });\r\n\r\n // Prepare the customCode\r\n if (customLogicOptions.allowCodeExecution) {\r\n try {\r\n customLogicOptions.customCode = wrapAround(\r\n customLogicOptions.customCode,\r\n customLogicOptions.allowFileResources\r\n );\r\n } catch (error) {\r\n logWithStack(2, error, `[chart] The 'customCode' cannot be loaded.`);\r\n }\r\n }\r\n\r\n // Get the callback\r\n if (\r\n customLogicOptions &&\r\n customLogicOptions.callback &&\r\n customLogicOptions.callback?.indexOf('{') < 0\r\n ) {\r\n // The allowFileResources is always set to false for HTTP requests to avoid\r\n // injecting arbitrary files from the fs\r\n if (customLogicOptions.allowFileResources) {\r\n try {\r\n customLogicOptions.callback = readFileSync(\r\n customLogicOptions.callback,\r\n 'utf8'\r\n );\r\n } catch (error) {\r\n customLogicOptions.callback = false;\r\n logWithStack(2, error, `[chart] The 'callback' cannot be loaded.`);\r\n }\r\n } else {\r\n customLogicOptions.callback = false;\r\n }\r\n }\r\n\r\n // Size search\r\n options.export = {\r\n ...options.export,\r\n ...findChartSize(options)\r\n };\r\n\r\n // Post the work to the pool\r\n try {\r\n const result = await postWork(\r\n exportOptions.strInj || chartJson || svg,\r\n options\r\n );\r\n return endCallback(false, result);\r\n } catch (error) {\r\n return endCallback(error);\r\n }\r\n};\r\n\r\n/**\r\n * Performs a direct inject of options before export. The function attempts\r\n * to stringify the provided options and removes unnecessary characters,\r\n * ensuring a clean and formatted input. The resulting string is saved as\r\n * a \"stright inject\" string in the export options. It then invokes the\r\n * doExport function with the updated options.\r\n *\r\n * IMPORTANT: Dangerous and must be used deliberately by someone who sets up\r\n * a server (see the --allowCodeExecution option).\r\n *\r\n * @param {Object} options - The export options containing the input\r\n * to be injected.\r\n * @param {function} endCallback - The callback function to be invoked\r\n * at the end of the process.\r\n *\r\n * @returns {Promise} A Promise that resolves with the result of the export\r\n * operation or rejects with an error if any issues occur during the process.\r\n */\r\nconst doStraightInject = (options, endCallback) => {\r\n try {\r\n let strInj;\r\n let instr = options.export.instr || options.export.options;\r\n\r\n if (typeof instr !== 'string') {\r\n // Try to stringify options\r\n strInj = instr = optionsStringify(\r\n instr,\r\n options.customLogic?.allowCodeExecution\r\n );\r\n }\r\n strInj = instr.replaceAll(/\\t|\\n|\\r/g, '').trim();\r\n\r\n // Get rid of the ;\r\n if (strInj[strInj.length - 1] === ';') {\r\n strInj = strInj.substring(0, strInj.length - 1);\r\n }\r\n\r\n // Save as stright inject string\r\n options.export.strInj = strInj;\r\n return doExport(options, false, endCallback);\r\n } catch (error) {\r\n return endCallback(\r\n new ExportError(\r\n `[chart] Malformed input detected for ${options.export?.requestId || '?'}. Please make sure that your JSON/JavaScript options are sent using the \"options\" attribute, and that if you're using SVG, it is unescaped.`\r\n ).setError(error)\r\n );\r\n }\r\n};\r\n\r\n/**\r\n * Exports a string based on the provided options and invokes an end callback.\r\n *\r\n * @param {string} stringToExport - The string content to be exported.\r\n * @param {Object} options - Export options, including customLogic with\r\n * allowCodeExecution flag.\r\n * @param {Function} endCallback - Callback function to be invoked at the end\r\n * of the export process.\r\n *\r\n * @returns {any} Result of the export process or an error if encountered.\r\n */\r\nconst exportAsString = (stringToExport, options, endCallback) => {\r\n const { allowCodeExecution } = options.customLogic;\r\n\r\n // Check if it is SVG\r\n if (\r\n stringToExport.indexOf('= 0 ||\r\n stringToExport.indexOf('= 0\r\n ) {\r\n log(4, '[chart] Parsing input as SVG.');\r\n return doExport(options, false, endCallback, stringToExport);\r\n }\r\n\r\n try {\r\n // Try to parse to JSON and call the doExport function\r\n const chartJSON = JSON.parse(stringToExport.replaceAll(/\\t|\\n|\\r/g, ' '));\r\n\r\n // If a correct JSON, do the export\r\n return doExport(options, chartJSON, endCallback);\r\n } catch (error) {\r\n // Not a valid JSON\r\n if (toBoolean(allowCodeExecution)) {\r\n return doStraightInject(options, endCallback);\r\n } else {\r\n // Do not allow straight injection without the allowCodeExecution flag\r\n return endCallback(\r\n new ExportError(\r\n '[chart] Only JSON configurations and SVG are allowed for this server. If this is your server, JavaScript custom code can be enabled by starting the server with the --allowCodeExecution flag.'\r\n ).setError(error)\r\n );\r\n }\r\n }\r\n};\r\n\r\n/**\r\n * Retrieves and returns the current status of code execution permission.\r\n *\r\n * @returns {any} The value of allowCodeExecution.\r\n */\r\nexport const getAllowCodeExecution = () => allowCodeExecution;\r\n\r\n/**\r\n * Sets the code execution permission based on the provided boolean value.\r\n *\r\n * @param {any} value - The value to be converted and assigned\r\n * to allowCodeExecution.\r\n */\r\nexport const setAllowCodeExecution = (value) => {\r\n allowCodeExecution = toBoolean(value);\r\n};\r\n\r\nexport default {\r\n batchExport,\r\n singleExport,\r\n getAllowCodeExecution,\r\n setAllowCodeExecution,\r\n startExport,\r\n findChartSize\r\n};\r\n","/*******************************************************************************\r\n\r\nHighcharts Export Server\r\n\r\nCopyright (c) 2016-2024, Highsoft\r\n\r\nLicenced under the MIT licence.\r\n\r\nAdditionally a valid Highcharts license is required for use.\r\n\r\nSee LICENSE file in root for details.\r\n\r\n*******************************************************************************/\r\n\r\n/**\r\n * @overview Used to sanitize the strings coming from the exporting module\r\n * to prevent XSS attacks (with the DOMPurify library).\r\n **/\r\n\r\nimport { JSDOM } from 'jsdom';\r\nimport DOMPurify from 'dompurify';\r\n\r\nimport { envs } from './envs.js';\r\n/**\r\n * Sanitizes a given HTML string by removing tags and any content within them.\r\n *\r\n * @param {string} input The HTML string to be sanitized.\r\n * @returns {string} The sanitized HTML string.\r\n */\r\nexport function sanitize(input) {\r\n const forbidden = [];\r\n\r\n if (!envs.OTHER_ALLOW_XLINK) {\r\n forbidden.push('xlink:href');\r\n }\r\n\r\n const window = new JSDOM('').window;\r\n const purify = DOMPurify(window);\r\n return purify.sanitize(input, {\r\n ADD_TAGS: ['foreignObject'],\r\n FORBID_ATTR: forbidden\r\n });\r\n}\r\n\r\nexport default sanitize;\r\n","/*******************************************************************************\r\n\r\nHighcharts Export Server\r\n\r\nCopyright (c) 2016-2024, Highsoft\r\n\r\nLicenced under the MIT licence.\r\n\r\nAdditionally a valid Highcharts license is required for use.\r\n\r\nSee LICENSE file in root for details.\r\n\r\n*******************************************************************************/\r\n\r\nimport { log } from './logger.js';\r\n\r\n// Array that contains ids of all ongoing intervals\r\nconst intervalIds = [];\r\n\r\n/**\r\n * Adds id of a setInterval to the intervalIds array.\r\n *\r\n * @param {NodeJS.Timeout} id - Id of an interval.\r\n */\r\nexport const addInterval = (id) => {\r\n intervalIds.push(id);\r\n};\r\n\r\n/**\r\n * Clears all of ongoing intervals by ids gathered in the intervalIds array.\r\n */\r\nexport const clearAllIntervals = () => {\r\n log(4, `[server] Clearing all registered intervals.`);\r\n for (const id of intervalIds) {\r\n clearInterval(id);\r\n }\r\n};\r\n\r\nexport default {\r\n addInterval,\r\n clearAllIntervals\r\n};\r\n","import { envs } from '../envs.js';\r\nimport { logWithStack } from '../logger.js';\r\n\r\n/**\r\n * Middleware for logging errors with stack trace and handling error response.\r\n *\r\n * @param {Error} error - The error object.\r\n * @param {Express.Request} req - The Express request object.\r\n * @param {Express.Response} res - The Express response object.\r\n * @param {Function} next - The next middleware function.\r\n */\r\nconst logErrorMiddleware = (error, req, res, next) => {\r\n // Display the error with stack in a correct format\r\n logWithStack(1, error);\r\n\r\n // Delete the stack for the environment other than the development\r\n if (envs.OTHER_NODE_ENV !== 'development') {\r\n delete error.stack;\r\n }\r\n\r\n // Call the returnErrorMiddleware\r\n next(error);\r\n};\r\n\r\n/**\r\n * Middleware for returning error response.\r\n *\r\n * @param {Error} error - The error object.\r\n * @param {Express.Request} req - The Express request object.\r\n * @param {Express.Response} res - The Express response object.\r\n * @param {Function} next - The next middleware function.\r\n */\r\nconst returnErrorMiddleware = (error, req, res, next) => {\r\n // Gather all requied information for the response\r\n const { statusCode: stCode, status, message, stack } = error;\r\n const statusCode = stCode || status || 400;\r\n\r\n // Set and return response\r\n res.status(statusCode).json({ statusCode, message, stack });\r\n};\r\n\r\nexport default (app) => {\r\n // Add log error middleware\r\n app.use(logErrorMiddleware);\r\n\r\n // Add set status and return error middleware\r\n app.use(returnErrorMiddleware);\r\n};\r\n","/*******************************************************************************\r\n\r\nHighcharts Export Server\r\n\r\nCopyright (c) 2016-2024, Highsoft\r\n\r\nLicenced under the MIT licence.\r\n\r\nAdditionally a valid Highcharts license is required for use.\r\n\r\nSee LICENSE file in root for details.\r\n\r\n*******************************************************************************/\r\n\r\nimport rateLimit from 'express-rate-limit';\r\n\r\nimport { log } from '../logger.js';\r\n\r\n/**\r\n * Middleware for enabling rate limiting on the specified Express app.\r\n *\r\n * @param {Express} app - The Express app instance.\r\n * @param {Object} limitConfig - Configuration options for rate limiting.\r\n */\r\nexport default (app, limitConfig) => {\r\n const msg =\r\n 'Too many requests, you have been rate limited. Please try again later.';\r\n\r\n // Options for the rate limiter\r\n const rateOptions = {\r\n max: limitConfig.maxRequests || 30,\r\n window: limitConfig.window || 1,\r\n delay: limitConfig.delay || 0,\r\n trustProxy: limitConfig.trustProxy || false,\r\n skipKey: limitConfig.skipKey || false,\r\n skipToken: limitConfig.skipToken || false\r\n };\r\n\r\n // Set if behind a proxy\r\n if (rateOptions.trustProxy) {\r\n app.enable('trust proxy');\r\n }\r\n\r\n // Create a limiter\r\n const limiter = rateLimit({\r\n windowMs: rateOptions.window * 60 * 1000,\r\n // Limit each IP to 100 requests per windowMs\r\n max: rateOptions.max,\r\n // Disable delaying, full speed until the max limit is reached\r\n delayMs: rateOptions.delay,\r\n handler: (request, response) => {\r\n response.format({\r\n json: () => {\r\n response.status(429).send({ message: msg });\r\n },\r\n default: () => {\r\n response.status(429).send(msg);\r\n }\r\n });\r\n },\r\n skip: (request) => {\r\n // Allow bypassing the limiter if a valid key/token has been sent\r\n if (\r\n rateOptions.skipKey !== false &&\r\n rateOptions.skipToken !== false &&\r\n request.query.key === rateOptions.skipKey &&\r\n request.query.access_token === rateOptions.skipToken\r\n ) {\r\n log(4, '[rate limiting] Skipping rate limiter.');\r\n return true;\r\n }\r\n return false;\r\n }\r\n });\r\n\r\n // Use a limiter as a middleware\r\n app.use(limiter);\r\n\r\n log(\r\n 3,\r\n `[rate limiting] Enabled rate limiting with ${rateOptions.max} requests per ${rateOptions.window} minute for each IP, trusting proxy: ${rateOptions.trustProxy}.`\r\n );\r\n};\r\n","import ExportError from './ExportError.js';\r\n\r\nclass HttpError extends ExportError {\r\n constructor(message, status) {\r\n super(message);\r\n this.status = this.statusCode = status;\r\n }\r\n\r\n setStatus(status) {\r\n this.status = status;\r\n return this;\r\n }\r\n}\r\n\r\nexport default HttpError;\r\n","/*******************************************************************************\r\n\r\nHighcharts Export Server\r\n\r\nCopyright (c) 2016-2024, Highsoft\r\n\r\nLicenced under the MIT licence.\r\n\r\nAdditionally a valid Highcharts license is required for use.\r\n\r\nSee LICENSE file in root for details.\r\n\r\n*******************************************************************************/\r\n\r\nimport { updateVersion, version } from '../../cache.js';\r\nimport { envs } from '../../envs.js';\r\n\r\nimport HttpError from '../../errors/HttpError.js';\r\n\r\n/**\r\n * Adds the POST /change_hc_version/:newVersion route that can be utilized to modify\r\n * the Highcharts version on the server.\r\n *\r\n * TODO: Add auth token and connect to API\r\n */\r\nexport default (app) =>\r\n !app\r\n ? false\r\n : app.post(\r\n '/version/change/:newVersion',\r\n async (request, response, next) => {\r\n try {\r\n const adminToken = envs.HIGHCHARTS_ADMIN_TOKEN;\r\n\r\n // Check the existence of the token\r\n if (!adminToken || !adminToken.length) {\r\n throw new HttpError(\r\n 'The server is not configured to perform run-time version changes: HIGHCHARTS_ADMIN_TOKEN is not set.',\r\n 401\r\n );\r\n }\r\n\r\n // Check if the hc-auth header contain a correct token\r\n const token = request.get('hc-auth');\r\n if (!token || token !== adminToken) {\r\n throw new HttpError(\r\n 'Invalid or missing token: Set the token in the hc-auth header.',\r\n 401\r\n );\r\n }\r\n\r\n // Compare versions\r\n const newVersion = request.params.newVersion;\r\n\r\n // Accept only version strings containing digits, letters, dots, hyphens\r\n if (newVersion && /^[a-zA-Z0-9.-]+$/.test(newVersion)) {\r\n try {\r\n // eslint-disable-next-line import/no-named-as-default-member\r\n await updateVersion(newVersion);\r\n } catch (error) {\r\n throw new HttpError(\r\n `Version change: ${error.message}`,\r\n error.statusCode\r\n ).setError(error);\r\n }\r\n\r\n // Success\r\n response.status(200).send({\r\n statusCode: 200,\r\n version: version(),\r\n message: `Successfully updated Highcharts to version: ${newVersion}.`\r\n });\r\n } else {\r\n // No version specified\r\n throw new HttpError('No new version supplied.', 400);\r\n }\r\n } catch (error) {\r\n next(error);\r\n }\r\n }\r\n );\r\n","/*******************************************************************************\r\n\r\nHighcharts Export Server\r\n\r\nCopyright (c) 2016-2024, Highsoft\r\n\r\nLicenced under the MIT licence.\r\n\r\nAdditionally a valid Highcharts license is required for use.\r\n\r\nSee LICENSE file in root for details.\r\n\r\n*******************************************************************************/\r\n\r\nimport { v4 as uuid } from 'uuid';\r\n\r\nimport { getAllowCodeExecution, startExport } from '../../chart.js';\r\nimport { getOptions, mergeConfigOptions } from '../../config.js';\r\nimport { log } from '../../logger.js';\r\nimport {\r\n fixType,\r\n isCorrectJSON,\r\n isObjectEmpty,\r\n isPrivateRangeUrlFound,\r\n optionsStringify,\r\n measureTime\r\n} from '../../utils.js';\r\n\r\nimport HttpError from '../../errors/HttpError.js';\r\n\r\n// Reversed MIME types\r\nconst reversedMime = {\r\n png: 'image/png',\r\n jpeg: 'image/jpeg',\r\n gif: 'image/gif',\r\n pdf: 'application/pdf',\r\n svg: 'image/svg+xml'\r\n};\r\n\r\n// The requests counter\r\nlet requestsCounter = 0;\r\n\r\n// The array of callbacks to call before a request\r\nconst beforeRequest = [];\r\n\r\n// The array of callbacks to call after a request\r\nconst afterRequest = [];\r\n\r\n/**\r\n * Invokes an array of callback functions with specified parameters, allowing\r\n * customization of request handling.\r\n *\r\n * @param {Function[]} callbacks - An array of callback functions\r\n * to be executed.\r\n * @param {Express.Request} request - The Express request object.\r\n * @param {Express.Response} response - The Express response object.\r\n * @param {Object} data - An object containing parameters like id, uniqueId,\r\n * type, and body.\r\n *\r\n * @returns {boolean} - Returns a boolean indicating the overall result\r\n * of the callback invocations.\r\n */\r\nconst doCallbacks = (callbacks, request, response, data) => {\r\n let result = true;\r\n const { id, uniqueId, type, body } = data;\r\n\r\n callbacks.some((callback) => {\r\n if (callback) {\r\n let callResponse = callback(request, response, id, uniqueId, type, body);\r\n\r\n if (callResponse !== undefined && callResponse !== true) {\r\n result = callResponse;\r\n }\r\n\r\n return true;\r\n }\r\n });\r\n\r\n return result;\r\n};\r\n\r\n/**\r\n * Handles the export requests from the client.\r\n *\r\n * @param {Express.Request} request - The Express request object.\r\n * @param {Express.Response} response - The Express response object.\r\n * @param {Function} next - The next middleware function.\r\n *\r\n * @returns {Promise} - A promise that resolves once the export process\r\n * is complete.\r\n */\r\nconst exportHandler = async (request, response, next) => {\r\n try {\r\n // Start counting time\r\n const stopCounter = measureTime();\r\n\r\n // Create a unique ID for a request\r\n const uniqueId = uuid().replace(/-/g, '');\r\n\r\n // Get the current server's general options\r\n const defaultOptions = getOptions();\r\n\r\n const body = request.body;\r\n const id = ++requestsCounter;\r\n\r\n let type = fixType(body.type);\r\n\r\n // Throw 'Bad Request' if there's no body\r\n if (!body || isObjectEmpty(body)) {\r\n throw new HttpError(\r\n 'The request body is required. Please ensure that your Content-Type header is correct (accepted types are application/json and multipart/form-data).',\r\n 400\r\n );\r\n }\r\n\r\n // All of the below can be used\r\n let instr = isCorrectJSON(body.infile || body.options || body.data);\r\n\r\n // Throw 'Bad Request' if there's no JSON or SVG to export\r\n if (!instr && !body.svg) {\r\n log(\r\n 2,\r\n `The request with ID ${uniqueId} from ${\r\n request.headers['x-forwarded-for'] || request.connection.remoteAddress\r\n } was incorrect:\r\n Content-Type: ${request.headers['content-type']}. \r\n Chart constructor: ${body.constr}.\r\n Dimensions: ${body.width}x${body.height} @ ${body.scale} scale.\r\n Type: ${type}.\r\n Is SVG set? ${typeof body.svg !== 'undefined'}.\r\n B64? ${typeof body.b64 !== 'undefined'}.\r\n No download? ${typeof body.noDownload !== 'undefined'}.\r\n\r\n Payload received: ${JSON.stringify(body.infile || body.options || body.data || body.svg)}\r\n\r\n `\r\n );\r\n\r\n throw new HttpError(\r\n \"No correct chart data found. Ensure that you are using either application/json or multipart/form-data headers. If sending JSON, make sure the chart data is in the 'infile', 'options', or 'data' attribute. If sending SVG, ensure it is in the 'svg' attribute.\",\r\n 400\r\n );\r\n }\r\n\r\n let callResponse = false;\r\n\r\n // Call the before request functions\r\n callResponse = doCallbacks(beforeRequest, request, response, {\r\n id,\r\n uniqueId,\r\n type,\r\n body\r\n });\r\n\r\n // Block the request if one of a callbacks failed\r\n if (callResponse !== true) {\r\n return response.send(callResponse);\r\n }\r\n\r\n let connectionAborted = false;\r\n\r\n // In case the connection is closed, force to abort further actions\r\n request.socket.on('close', (hadErrors) => {\r\n if (hadErrors) {\r\n connectionAborted = true;\r\n }\r\n });\r\n\r\n log(4, `[export] Got an incoming HTTP request with ID ${uniqueId}.`);\r\n\r\n body.constr = (typeof body.constr === 'string' && body.constr) || 'chart';\r\n\r\n // Gather and organize options from the payload\r\n const requestOptions = {\r\n export: {\r\n instr,\r\n type,\r\n constr: body.constr[0].toLowerCase() + body.constr.substr(1),\r\n height: body.height,\r\n width: body.width,\r\n scale: body.scale || defaultOptions.export.scale,\r\n globalOptions: isCorrectJSON(body.globalOptions, true),\r\n themeOptions: isCorrectJSON(body.themeOptions, true)\r\n },\r\n customLogic: {\r\n allowCodeExecution: getAllowCodeExecution(),\r\n allowFileResources: false,\r\n resources: isCorrectJSON(body.resources, true),\r\n callback: body.callback,\r\n customCode: body.customCode\r\n }\r\n };\r\n\r\n if (instr) {\r\n // Stringify JSON with options\r\n requestOptions.export.instr = optionsStringify(\r\n instr,\r\n requestOptions.customLogic.allowCodeExecution\r\n );\r\n }\r\n\r\n // Merge the request options into default ones\r\n const options = mergeConfigOptions(defaultOptions, requestOptions);\r\n\r\n // Save the JSON if exists\r\n options.export.options = instr;\r\n\r\n // Lastly, add the server specific arguments into options as payload\r\n options.payload = {\r\n svg: body.svg || false,\r\n b64: body.b64 || false,\r\n noDownload: body.noDownload || false,\r\n requestId: uniqueId\r\n };\r\n\r\n // Test xlink:href elements from payload's SVG\r\n if (body.svg && isPrivateRangeUrlFound(options.payload.svg)) {\r\n throw new HttpError(\r\n 'SVG potentially contain at least one forbidden URL in xlink:href element. Please review the SVG content and ensure that all referenced URLs comply with security policies.',\r\n 400\r\n );\r\n }\r\n\r\n // Start the export process\r\n await startExport(options, (error, info) => {\r\n // Remove the close event from the socket\r\n request.socket.removeAllListeners('close');\r\n\r\n // After the whole exporting process\r\n if (defaultOptions.server.benchmarking) {\r\n log(\r\n 5,\r\n `[benchmark] Request with ID ${uniqueId} - After the whole exporting process: ${stopCounter()}ms.`\r\n );\r\n }\r\n\r\n // If the connection was closed, do nothing\r\n if (connectionAborted) {\r\n return log(\r\n 3,\r\n `[export] The client closed the connection before the chart finished processing.`\r\n );\r\n }\r\n\r\n // If error, log it and send it to the error middleware\r\n if (error) {\r\n throw error;\r\n }\r\n\r\n // If data is missing, log the message and send it to the error middleware\r\n if (!info || !info.result) {\r\n throw new HttpError(\r\n `Unexpected return from chart generation. Please check your request data. For the request with ID ${uniqueId}, the result is ${info.result}.`,\r\n 400\r\n );\r\n }\r\n\r\n // Get the type from options\r\n type = info.options.export.type;\r\n\r\n // The after request callbacks\r\n doCallbacks(afterRequest, request, response, { id, body: info.result });\r\n\r\n if (info.result) {\r\n // If only base64 is required, return it\r\n if (body.b64) {\r\n // SVG Exception for the Highcharts 11.3.0 version\r\n if (type === 'pdf' || type == 'svg') {\r\n return response.send(\r\n Buffer.from(info.result, 'utf8').toString('base64')\r\n );\r\n }\r\n\r\n return response.send(info.result);\r\n }\r\n\r\n // Set correct content type\r\n response.header('Content-Type', reversedMime[type] || 'image/png');\r\n\r\n // Decide whether to download or not chart file\r\n if (!body.noDownload) {\r\n response.attachment(\r\n `${request.params.filename || request.body.filename || 'chart'}.${\r\n type || 'png'\r\n }`\r\n );\r\n }\r\n\r\n // If SVG, return plain content\r\n return type === 'svg'\r\n ? response.send(info.result)\r\n : response.send(Buffer.from(info.result, 'base64'));\r\n }\r\n });\r\n } catch (error) {\r\n next(error);\r\n }\r\n};\r\n\r\nexport default (app) => {\r\n /**\r\n * Adds the POST / a route for handling POST requests at the root endpoint.\r\n */\r\n app.post('/', exportHandler);\r\n\r\n /**\r\n * Adds the POST /:filename a route for handling POST requests with\r\n * a specified filename parameter.\r\n */\r\n app.post('/:filename', exportHandler);\r\n};\r\n","/*******************************************************************************\r\n\r\nHighcharts Export Server\r\n\r\nCopyright (c) 2016-2024, Highsoft\r\n\r\nLicenced under the MIT licence.\r\n\r\nAdditionally a valid Highcharts license is required for use.\r\n\r\nSee LICENSE file in root for details.\r\n\r\n*******************************************************************************/\r\n\r\nimport { readFileSync } from 'fs';\r\nimport { join as pather } from 'path';\r\nimport { log } from '../../logger.js';\r\n\r\nimport { version } from '../../cache.js';\r\nimport { addInterval } from '../../intervals.js';\r\nimport pool from '../../pool.js';\r\nimport { __dirname } from '../../utils.js';\r\n\r\nconst pkgFile = JSON.parse(readFileSync(pather(__dirname, 'package.json')));\r\n\r\nconst serverStartTime = new Date();\r\n\r\nconst successRates = [];\r\nconst recordInterval = 60 * 1000; // record every minute\r\nconst windowSize = 30; // 30 minutes\r\n\r\n/**\r\n * Calculates moving average indicator based on the data from the successRates\r\n * array.\r\n *\r\n * @returns {number} - A moving average for success ratio of the server exports.\r\n */\r\nfunction calculateMovingAverage() {\r\n const sum = successRates.reduce((a, b) => a + b, 0);\r\n return sum / successRates.length;\r\n}\r\n\r\n/**\r\n * Starts the interval responsible for calculating current success rate ratio\r\n * and gathers\r\n *\r\n * @returns {NodeJS.Timeout} id - Id of an interval.\r\n */\r\nexport const startSuccessRate = () =>\r\n setInterval(() => {\r\n const stats = pool.getStats();\r\n const successRatio =\r\n stats.exportAttempts === 0\r\n ? 1\r\n : (stats.performedExports / stats.exportAttempts) * 100;\r\n\r\n successRates.push(successRatio);\r\n if (successRates.length > windowSize) {\r\n successRates.shift();\r\n }\r\n }, recordInterval);\r\n\r\n/**\r\n * Adds the /health and /success-moving-average routes\r\n * which output basic stats for the server.\r\n */\r\nexport default function addHealthRoutes(app) {\r\n if (!app) {\r\n return false;\r\n }\r\n\r\n // Start processing success rate ratio interval and save its id to the array\r\n // for the graceful clearing on shutdown with injected addInterval funtion\r\n addInterval(startSuccessRate());\r\n\r\n app.get('/health', (_, res) => {\r\n const stats = pool.getStats();\r\n const period = successRates.length;\r\n const movingAverage = calculateMovingAverage();\r\n\r\n log(4, '[health.js] GET /health [200] - returning server health.');\r\n\r\n res.send({\r\n status: 'OK',\r\n bootTime: serverStartTime,\r\n uptime:\r\n Math.floor(\r\n (new Date().getTime() - serverStartTime.getTime()) / 1000 / 60\r\n ) + ' minutes',\r\n version: pkgFile.version,\r\n highchartsVersion: version(),\r\n averageProcessingTime: stats.spentAverage,\r\n performedExports: stats.performedExports,\r\n failedExports: stats.droppedExports,\r\n exportAttempts: stats.exportAttempts,\r\n sucessRatio: (stats.performedExports / stats.exportAttempts) * 100,\r\n // eslint-disable-next-line import/no-named-as-default-member\r\n pool: pool.getPoolInfoJSON(),\r\n\r\n // Moving average\r\n period,\r\n movingAverage,\r\n message:\r\n isNaN(movingAverage) || !successRates.length\r\n ? 'Too early to report. No exports made yet. Please check back soon.'\r\n : `Last ${period} minutes had a success rate of ${movingAverage.toFixed(2)}%.`,\r\n\r\n // SVG/JSON attempts\r\n svgExportAttempts: stats.exportFromSvgAttempts,\r\n jsonExportAttempts: stats.performedExports - stats.exportFromSvgAttempts\r\n });\r\n });\r\n}\r\n","/*******************************************************************************\r\n\r\nHighcharts Export Server\r\n\r\nCopyright (c) 2016-2024, Highsoft\r\n\r\nLicenced under the MIT licence.\r\n\r\nAdditionally a valid Highcharts license is required for use.\r\n\r\nSee LICENSE file in root for details.\r\n\r\n*******************************************************************************/\r\n\r\nimport { promises as fsPromises } from 'fs';\r\nimport { posix } from 'path';\r\n\r\nimport cors from 'cors';\r\nimport express from 'express';\r\nimport http from 'http';\r\nimport https from 'https';\r\nimport multer from 'multer';\r\n\r\nimport errorHandler from './error.js';\r\nimport rateLimit from './rate_limit.js';\r\nimport { log, logWithStack } from '../logger.js';\r\nimport { __dirname } from '../utils.js';\r\n\r\nimport vSwitchRoute from './routes/change_hc_version.js';\r\nimport exportRoutes from './routes/export.js';\r\nimport healthRoute from './routes/health.js';\r\nimport uiRoute from './routes/ui.js';\r\n\r\nimport ExportError from '../errors/ExportError.js';\r\n\r\n// Array of an active servers\r\nconst activeServers = new Map();\r\n\r\n// Create express app\r\nconst app = express();\r\n\r\n// Disable the X-Powered-By header\r\napp.disable('x-powered-by');\r\n\r\n// Enable CORS support\r\napp.use(cors());\r\n\r\n// Getting a lot of RangeNotSatisfiableError exception.\r\n// Even though this is a deprecated options, let's try to set it to false.\r\napp.use((_req, res, next) => {\r\n res.set('Accept-Ranges', 'none');\r\n next();\r\n});\r\n\r\n/**\r\n * Attach error handlers to the server.\r\n *\r\n * @param {http.Server} server - The HTTP/HTTPS server instance.\r\n */\r\nconst attachServerErrorHandlers = (server) => {\r\n server.on('clientError', (error, socket) => {\r\n logWithStack(\r\n 1,\r\n error,\r\n `[server] Client error: ${error.message}, destroying socket.`\r\n );\r\n socket.destroy();\r\n });\r\n\r\n server.on('error', (error) => {\r\n logWithStack(1, error, `[server] Server error: ${error.message}`);\r\n });\r\n\r\n server.on('connection', (socket) => {\r\n socket.on('error', (error) => {\r\n logWithStack(1, error, `[server] Socket error: ${error.message}`);\r\n });\r\n });\r\n};\r\n\r\n/**\r\n * Starts an HTTP server based on the provided configuration. The `serverConfig`\r\n * object contains all server related properties (see the `server` section\r\n * in the `lib/schemas/config.js` file for a reference).\r\n *\r\n * @param {Object} serverConfig - The server configuration object.\r\n *\r\n * @throws {ExportError} - Throws an error if the server cannot be configured\r\n * and started.\r\n */\r\nexport const startServer = async (serverConfig) => {\r\n try {\r\n // TODO: Read from config/env\r\n // NOTE:\r\n // Too big limits lead to timeouts in the export process when the\r\n // rasterization timeout is set too low.\r\n const uploadLimitMiB = serverConfig.maxUploadSize || 3;\r\n const uploadLimitBytes = uploadLimitMiB * 1024 * 1024;\r\n\r\n // Enable parsing of form data (files) with Multer package\r\n const storage = multer.memoryStorage();\r\n const upload = multer({\r\n storage,\r\n limits: {\r\n fieldSize: uploadLimitBytes\r\n }\r\n });\r\n\r\n // Enable body parser\r\n app.use(express.json({ limit: uploadLimitBytes }));\r\n app.use(express.urlencoded({ extended: true, limit: uploadLimitBytes }));\r\n\r\n // Use only non-file multipart form fields\r\n app.use(upload.none());\r\n\r\n // Stop if not enabled\r\n if (!serverConfig.enable) {\r\n return false;\r\n }\r\n\r\n // Listen HTTP server\r\n if (!serverConfig.ssl.force) {\r\n // Main server instance (HTTP)\r\n const httpServer = http.createServer(app);\r\n\r\n // Attach error handlers and listen to the server\r\n attachServerErrorHandlers(httpServer);\r\n\r\n // Listen\r\n httpServer.listen(serverConfig.port, serverConfig.host);\r\n\r\n // Save the reference to HTTP server\r\n activeServers.set(serverConfig.port, httpServer);\r\n\r\n log(\r\n 3,\r\n `[server] Started HTTP server on ${serverConfig.host}:${serverConfig.port}.`\r\n );\r\n }\r\n\r\n // Listen HTTPS server\r\n if (serverConfig.ssl.enable) {\r\n // Set up an SSL server also\r\n let key, cert;\r\n\r\n try {\r\n // Get the SSL key\r\n key = await fsPromises.readFile(\r\n posix.join(serverConfig.ssl.certPath, 'server.key'),\r\n 'utf8'\r\n );\r\n\r\n // Get the SSL certificate\r\n cert = await fsPromises.readFile(\r\n posix.join(serverConfig.ssl.certPath, 'server.crt'),\r\n 'utf8'\r\n );\r\n } catch (error) {\r\n log(\r\n 2,\r\n `[server] Unable to load key/certificate from the '${serverConfig.ssl.certPath}' path. Could not run secured layer server.`\r\n );\r\n }\r\n\r\n if (key && cert) {\r\n // Main server instance (HTTPS)\r\n const httpsServer = https.createServer({ key, cert }, app);\r\n\r\n // Attach error handlers and listen to the server\r\n attachServerErrorHandlers(httpsServer);\r\n\r\n // Listen\r\n httpsServer.listen(serverConfig.ssl.port, serverConfig.host);\r\n\r\n // Save the reference to HTTPS server\r\n activeServers.set(serverConfig.ssl.port, httpsServer);\r\n\r\n log(\r\n 3,\r\n `[server] Started HTTPS server on ${serverConfig.host}:${serverConfig.ssl.port}.`\r\n );\r\n }\r\n }\r\n\r\n // Enable the rate limiter if config says so\r\n if (\r\n serverConfig.rateLimiting &&\r\n serverConfig.rateLimiting.enable &&\r\n ![0, NaN].includes(serverConfig.rateLimiting.maxRequests)\r\n ) {\r\n rateLimit(app, serverConfig.rateLimiting);\r\n }\r\n\r\n // Set up static folder's route\r\n app.use(express.static(posix.join(__dirname, 'public')));\r\n\r\n // Set up routes\r\n healthRoute(app);\r\n exportRoutes(app);\r\n uiRoute(app);\r\n vSwitchRoute(app);\r\n\r\n // Set up centralized error handler\r\n errorHandler(app);\r\n } catch (error) {\r\n throw new ExportError(\r\n '[server] Could not configure and start the server.'\r\n ).setError(error);\r\n }\r\n};\r\n\r\n/**\r\n * Closes all servers associated with Express app instance.\r\n */\r\nexport const closeServers = () => {\r\n log(4, `[server] Closing all servers.`);\r\n for (const [port, server] of activeServers) {\r\n server.close(() => {\r\n activeServers.delete(port);\r\n log(4, `[server] Closed server on port: ${port}.`);\r\n });\r\n }\r\n};\r\n\r\n/**\r\n * Get all servers associated with Express app instance.\r\n *\r\n * @returns {Array} - Servers associated with Express app instance.\r\n */\r\nexport const getServers = () => activeServers;\r\n\r\n/**\r\n * Enable rate limiting for the server.\r\n *\r\n * @param {Object} limitConfig - Configuration object for rate limiting.\r\n */\r\nexport const enableRateLimiting = (limitConfig) => rateLimit(app, limitConfig);\r\n\r\n/**\r\n * Get the Express instance.\r\n *\r\n * @returns {Object} - The Express instance.\r\n */\r\nexport const getExpress = () => express;\r\n\r\n/**\r\n * Get the Express app instance.\r\n *\r\n * @returns {Object} - The Express app instance.\r\n */\r\nexport const getApp = () => app;\r\n\r\n/**\r\n * Apply middleware(s) to a specific path.\r\n *\r\n * @param {string} path - The path to which the middleware(s) should be applied.\r\n * @param {...Function} middlewares - The middleware functions to be applied.\r\n */\r\nexport const use = (path, ...middlewares) => {\r\n app.use(path, ...middlewares);\r\n};\r\n\r\n/**\r\n * Set up a route with GET method and apply middleware(s).\r\n *\r\n * @param {string} path - The route path.\r\n * @param {...Function} middlewares - The middleware functions to be applied.\r\n */\r\nexport const get = (path, ...middlewares) => {\r\n app.get(path, ...middlewares);\r\n};\r\n\r\n/**\r\n * Set up a route with POST method and apply middleware(s).\r\n *\r\n * @param {string} path - The route path.\r\n * @param {...Function} middlewares - The middleware functions to be applied.\r\n */\r\nexport const post = (path, ...middlewares) => {\r\n app.post(path, ...middlewares);\r\n};\r\n\r\nexport default {\r\n startServer,\r\n closeServers,\r\n getServers,\r\n enableRateLimiting,\r\n getExpress,\r\n getApp,\r\n use,\r\n get,\r\n post\r\n};\r\n","/*******************************************************************************\r\n\r\nHighcharts Export Server\r\n\r\nCopyright (c) 2016-2024, Highsoft\r\n\r\nLicenced under the MIT licence.\r\n\r\nAdditionally a valid Highcharts license is required for use.\r\n\r\nSee LICENSE file in root for details.\r\n\r\n*******************************************************************************/\r\n\r\nimport { join } from 'path';\r\n\r\nimport { __dirname } from '../../utils.js';\r\n\r\n/**\r\n * Adds the GET / route for a UI when enabled on the export server.\r\n */\r\nexport default (app) =>\r\n !app\r\n ? false\r\n : app.get('/', (_request, response) => {\r\n response.sendFile(join(__dirname, 'public', 'index.html'), {\r\n acceptRanges: false\r\n });\r\n });\r\n","/*******************************************************************************\r\n\r\nHighcharts Export Server\r\n\r\nCopyright (c) 2016-2024, Highsoft\r\n\r\nLicenced under the MIT licence.\r\n\r\nAdditionally a valid Highcharts license is required for use.\r\n\r\nSee LICENSE file in root for details.\r\n\r\n*******************************************************************************/\r\n\r\nimport { clearAllIntervals } from './intervals.js';\r\nimport { killPool } from './pool.js';\r\nimport { closeServers } from './server/server.js';\r\n\r\n/**\r\n * Clean up function to trigger before ending process for the graceful shutdown.\r\n *\r\n * @param {number} exitCode - An exit code for the process.exit() function.\r\n */\r\nexport const shutdownCleanUp = async (exitCode) => {\r\n // Await freeing all resources\r\n await Promise.allSettled([\r\n // Clear all ongoing intervals\r\n clearAllIntervals(),\r\n\r\n // Get available server instances (HTTP/HTTPS) and close them\r\n closeServers(),\r\n\r\n // Close pool along with its workers and the browser instance, if exists\r\n killPool()\r\n ]);\r\n\r\n // Exit process with a correct code\r\n process.exit(exitCode);\r\n};\r\n\r\nexport default {\r\n shutdownCleanUp\r\n};\r\n","/*******************************************************************************\r\n\r\nHighcharts Export Server\r\n\r\nCopyright (c) 2016-2024, Highsoft\r\n\r\nLicenced under the MIT licence.\r\n\r\nAdditionally a valid Highcharts license is required for use.\r\n\r\nSee LICENSE file in root for details.\r\n\r\n*******************************************************************************/\r\n\r\nimport 'colors';\r\n\r\nimport { checkAndUpdateCache } from './cache.js';\r\nimport {\r\n batchExport,\r\n setAllowCodeExecution,\r\n singleExport,\r\n startExport\r\n} from './chart.js';\r\nimport { mapToNewConfig, manualConfig, setOptions } from './config.js';\r\nimport {\r\n initLogging,\r\n log,\r\n logWithStack,\r\n setLogLevel,\r\n enableFileLogging\r\n} from './logger.js';\r\nimport { initPool, killPool } from './pool.js';\r\nimport { shutdownCleanUp } from './resource_release.js';\r\nimport server, { startServer } from './server/server.js';\r\nimport { printLogo, printUsage } from './utils.js';\r\n\r\n/**\r\n * Attaches exit listeners to the process, ensuring proper cleanup of resources\r\n * and termination on exit signals. Handles 'exit', 'SIGINT', 'SIGTERM', and\r\n * 'uncaughtException' events.\r\n */\r\nconst attachProcessExitListeners = () => {\r\n log(3, '[process] Attaching exit listeners to the process.');\r\n\r\n // Handler for the 'exit'\r\n process.on('exit', (code) => {\r\n log(4, `Process exited with code ${code}.`);\r\n });\r\n\r\n // Handler for the 'SIGINT'\r\n process.on('SIGINT', async (name, code) => {\r\n log(4, `The ${name} event with code: ${code}.`);\r\n await shutdownCleanUp(0);\r\n });\r\n\r\n // Handler for the 'SIGTERM'\r\n process.on('SIGTERM', async (name, code) => {\r\n log(4, `The ${name} event with code: ${code}.`);\r\n await shutdownCleanUp(0);\r\n });\r\n\r\n // Handler for the 'SIGHUP'\r\n process.on('SIGHUP', async (name, code) => {\r\n log(4, `The ${name} event with code: ${code}.`);\r\n await shutdownCleanUp(0);\r\n });\r\n\r\n // Handler for the 'uncaughtException'\r\n process.on('uncaughtException', async (error, name) => {\r\n logWithStack(1, error, `The ${name} error.`);\r\n await shutdownCleanUp(1);\r\n });\r\n};\r\n\r\n/**\r\n * Initializes the export process. Tasks such as configuring logging, checking\r\n * cache and sources, and initializing the pool of resources happen during\r\n * this stage. Function that is required to be called before trying to export charts or setting a server. The `options` is an object that contains all options.\r\n *\r\n * @param {Object} options - All export options.\r\n *\r\n * @returns {Promise} Promise resolving to the updated export options.\r\n */\r\nconst initExport = async (options) => {\r\n // Set the allowCodeExecution per export module scope\r\n setAllowCodeExecution(\r\n options.customLogic && options.customLogic.allowCodeExecution\r\n );\r\n\r\n // Init the logging\r\n initLogging(options.logging);\r\n\r\n // Attach process' exit listeners\r\n if (options.other.listenToProcessExits) {\r\n attachProcessExitListeners();\r\n }\r\n\r\n // Check if cache needs to be updated\r\n await checkAndUpdateCache(options);\r\n\r\n // Init the pool\r\n await initPool({\r\n pool: options.pool || {\r\n minWorkers: 1,\r\n maxWorkers: 1\r\n },\r\n puppeteerArgs: options.puppeteer.args || []\r\n });\r\n\r\n // Return updated options\r\n return options;\r\n};\r\n\r\nexport default {\r\n // Server\r\n server,\r\n startServer,\r\n\r\n // Exporting\r\n initExport,\r\n singleExport,\r\n batchExport,\r\n startExport,\r\n\r\n // Pool\r\n initPool,\r\n killPool,\r\n\r\n // Other\r\n setOptions,\r\n shutdownCleanUp,\r\n\r\n // Logs\r\n log,\r\n logWithStack,\r\n setLogLevel,\r\n enableFileLogging,\r\n\r\n // Utils\r\n mapToNewConfig,\r\n manualConfig,\r\n printLogo,\r\n printUsage\r\n};\r\n"],"names":["scriptsNames","core","modules","indicators","custom","defaultConfig","puppeteer","args","value","type","description","tempDir","envLink","highcharts","version","cdnURL","useNpm","coreScripts","moduleScripts","indicatorScripts","customScripts","forceFetch","cachePath","export","infile","instr","options","outfile","constr","defaultHeight","defaultWidth","defaultScale","height","width","scale","globalOptions","themeOptions","batch","rasterizationTimeout","customLogic","allowCodeExecution","allowFileResources","customCode","callback","resources","loadConfig","legacyName","createConfig","server","maxUploadSize","enable","cliName","host","port","benchmarking","proxy","username","password","timeout","rateLimiting","maxRequests","window","delay","trustProxy","skipKey","skipToken","ssl","force","certPath","pool","minWorkers","maxWorkers","workLimit","acquireTimeout","createTimeout","destroyTimeout","idleTimeout","createRetryInterval","reaperInterval","logging","level","file","dest","toConsole","toFile","ui","route","other","nodeEnv","listenToProcessExits","noLogo","hardResetPage","browserShellMode","debug","headless","devtools","listenToConsole","dumpio","slowMo","debuggingPort","promptsConfig","name","message","initial","join","separator","instructions","choices","hint","min","max","round","absoluteProps","nestedArgs","createNestedArgs","obj","propChain","Object","keys","forEach","k","includes","entry","substring","undefined","dotenv","config","v","filterArray","z","string","transform","split","map","trim","filter","length","enum","values","refine","test","isNaN","parseFloat","envs","object","PUPPETEER_TEMP_DIR","HIGHCHARTS_VERSION","HIGHCHARTS_CDN_URL","startsWith","HIGHCHARTS_USE_NPM","HIGHCHARTS_CORE_SCRIPTS","HIGHCHARTS_MODULE_SCRIPTS","HIGHCHARTS_INDICATOR_SCRIPTS","HIGHCHARTS_FORCE_FETCH","HIGHCHARTS_CACHE_PATH","HIGHCHARTS_ADMIN_TOKEN","EXPORT_TYPE","EXPORT_CONSTR","EXPORT_DEFAULT_HEIGHT","EXPORT_DEFAULT_WIDTH","EXPORT_DEFAULT_SCALE","EXPORT_RASTERIZATION_TIMEOUT","CUSTOM_LOGIC_ALLOW_CODE_EXECUTION","CUSTOM_LOGIC_ALLOW_FILE_RESOURCES","SERVER_ENABLE","SERVER_HOST","SERVER_PORT","SERVER_MAX_UPLOAD_SIZE","SERVER_BENCHMARKING","SERVER_PROXY_HOST","SERVER_PROXY_PORT","SERVER_PROXY_USERNAME","SERVER_PROXY_PASSWORD","SERVER_PROXY_TIMEOUT","SERVER_RATE_LIMITING_ENABLE","SERVER_RATE_LIMITING_MAX_REQUESTS","SERVER_RATE_LIMITING_WINDOW","SERVER_RATE_LIMITING_DELAY","SERVER_RATE_LIMITING_TRUST_PROXY","SERVER_RATE_LIMITING_SKIP_KEY","SERVER_RATE_LIMITING_SKIP_TOKEN","SERVER_SSL_ENABLE","SERVER_SSL_FORCE","SERVER_SSL_PORT","SERVER_SSL_CERT_PATH","POOL_MIN_WORKERS","POOL_MAX_WORKERS","POOL_WORK_LIMIT","POOL_ACQUIRE_TIMEOUT","POOL_CREATE_TIMEOUT","POOL_DESTROY_TIMEOUT","POOL_IDLE_TIMEOUT","POOL_CREATE_RETRY_INTERVAL","POOL_REAPER_INTERVAL","POOL_BENCHMARKING","LOGGING_LEVEL","LOGGING_FILE","LOGGING_DEST","LOGGING_TO_CONSOLE","LOGGING_TO_FILE","UI_ENABLE","UI_ROUTE","OTHER_NODE_ENV","OTHER_LISTEN_TO_PROCESS_EXITS","OTHER_NO_LOGO","OTHER_HARD_RESET_PAGE","OTHER_BROWSER_SHELL_MODE","OTHER_ALLOW_XLINK","DEBUG_ENABLE","DEBUG_HEADLESS","DEBUG_DEVTOOLS","DEBUG_LISTEN_TO_CONSOLE","DEBUG_DUMPIO","DEBUG_SLOW_MO","DEBUG_DEBUGGING_PORT","partial","parse","process","env","colors","pathCreated","levelsDesc","title","color","listeners","logToFile","texts","prefix","existsSync","mkdirSync","appendFile","concat","error","console","log","newLevel","Date","toString","fn","apply","logWithStack","customMessage","mainMessage","stackMessage","stack","slice","setLogLevel","enableFileLogging","logDest","logFile","endsWith","__highchartsDir","dirname","createRequire","url","resolve","__dirname","fileURLToPath","URL","fixType","formats","outType","pop","find","t","handleResources","allowedProps","handledResources","correctResources","isCorrectJSON","readFileSync","files","propName","item","data","parsedData","JSON","stringify","deepCopy","copy","Array","isArray","key","prototype","hasOwnProperty","call","optionsStringify","allowFunctions","replaceAll","printUsage","bold","yellow","cycleCategories","option","entries","descName","green","i","blue","category","toUpperCase","red","toBoolean","wrapAround","replace","measureTime","start","hrtime","bigint","Number","generalOptions","getOptions","mergeConfigOptions","newOptions","mergedOptions","updateDefaultConfig","configObj","customObj","customValue","initOptions","items","recursiveProps","objectToUpdate","nestedNames","shift","assign","async","fetch","requestOptions","Promise","reject","protocol","https","http","getProtocol","get","headers","Referer","res","on","chunk","text","ExportError","Error","constructor","super","this","setError","statusCode","cache","activeManifest","sources","hcVersion","extractVersion","indexOf","extractModuleName","scriptPath","fetchAndProcessScript","script","fetchedModules","shouldThrowError","response","resolvedScriptPath","sep","updateCache","highchartsOptions","proxyOptions","sourcePath","proxyAgent","HttpsProxyAgent","agent","all","c","m","fetchScripts","writeFileSync","checkAndUpdateCache","manifestPath","requestUpdate","manifest","moduleMap","numberOfModules","some","moduleName","newManifest","saveConfigToManifest","getCachePath","setupHighcharts","Highcharts","animObject","duration","triggerExport","chartOptions","displayErrors","_displayErrors","merge","setOptions","wrap","setOptionsObj","chart","animation","strInj","isRenderComplete","Chart","proceed","userOptions","cb","exporting","enabled","plotOptions","series","label","tooltip","onHighchartsRender","addEvent","Series","Function","finalOptions","finalCallback","defaultOptions","prop","template","browser","newPage","page","setCacheEnabled","setPageContent","isClosed","$eval","element","errorMessage","innerHTML","setPageEvents","clearPageResources","injectedResources","resource","dispose","evaluate","oldCharts","charts","oldChart","destroy","scriptsToRemove","document","getElementsByTagName","stylesToRemove","linksToRemove","remove","setContent","waitUntil","addScriptTag","path","setAsConfig","totalSize","Buffer","byteLength","toFixed","puppeteerExport","exportOptions","debugger","isSVG","svgTemplate","injectedJs","js","push","content","isLocal","jsResource","injectedCss","css","cssImports","match","cssImportPath","cssResource","addStyleTag","addPageResources","size","svgElement","querySelector","chartHeight","baseVal","chartWidth","body","style","zoom","margin","viewportHeight","Math","abs","ceil","viewportWidth","x","y","getBoundingClientRect","trunc","getClipRegion","setViewport","deviceScaleFactor","outerHTML","createSVG","encoding","clip","race","screenshot","captureBeyondViewport","fullPage","optimizeForSpeed","quality","omitBackground","_resolve","setTimeout","createImage","emulateMediaType","pdf","createPDF","stats","performedExports","exportAttempts","exportFromSvgAttempts","timeSpent","droppedExports","spentAverage","poolConfig","factory","create","id","uuid","startDate","getTime","workCount","random","validate","workerHandle","close","initPool","puppeteerArgs","puppeteerOptions","enabledDebug","debugOptions","launchOptions","userDataDir","handleSIGINT","handleSIGTERM","handleSIGHUP","waitForInitialPage","defaultViewport","maxTries","tryCount","open","launch","createBrowser","parseInt","Pool","acquireTimeoutMillis","createTimeoutMillis","destroyTimeoutMillis","idleTimeoutMillis","createRetryIntervalMillis","reapIntervalMillis","propagateCreateError","r","hardReset","goto","clearPage","eventId","initialResources","acquire","promise","release","killPool","worker","used","destroyed","connected","closeBrowser","postWork","getPoolInfo","acquireCounter","payload","requestId","workStart","exportCounter","result","exportTime","getPoolInfoJSON","numFree","numUsed","available","pending","numPendingAcquires","pool$1","startExport","settings","endCallback","svg","initExportSettings","exportAsString","input","forbidden","JSDOM","DOMPurify","sanitize","ADD_TAGS","FORBID_ATTR","doStraightInject","doExport","findChartSize","precision","multiplier","pow","roundNumber","sourceHeight","sourceWidth","param","chartJson","customLogicOptions","allowCodeExecutionScoped","optionsName","stringToExport","chartJSON","intervalIds","clearAllIntervals","clearInterval","logErrorMiddleware","req","next","returnErrorMiddleware","stCode","status","json","rateLimit","app","limitConfig","msg","rateOptions","limiter","windowMs","delayMs","handler","request","format","send","default","skip","query","access_token","use","HttpError","setStatus","vSwitchRoute","post","adminToken","token","newVersion","params","updateVersion","reversedMime","png","jpeg","gif","requestsCounter","beforeRequest","afterRequest","doCallbacks","callbacks","uniqueId","callResponse","exportHandler","stopCounter","connection","remoteAddress","b64","noDownload","connectionAborted","socket","hadErrors","toLowerCase","substr","pattern","isPrivateRangeUrlFound","info","removeAllListeners","from","header","attachment","filename","pkgFile","pather","serverStartTime","successRates","addHealthRoutes","setInterval","successRatio","_","period","movingAverage","reduce","a","b","bootTime","uptime","floor","highchartsVersion","averageProcessingTime","failedExports","sucessRatio","svgExportAttempts","jsonExportAttempts","activeServers","Map","express","disable","cors","_req","set","attachServerErrorHandlers","startServer","serverConfig","uploadLimitBytes","storage","multer","memoryStorage","upload","limits","fieldSize","limit","urlencoded","extended","none","httpServer","createServer","listen","cert","fsPromises","readFile","posix","httpsServer","NaN","static","healthRoute","exportRoutes","_request","sendFile","acceptRanges","uiRoute","errorHandler","closeServers","delete","getServers","enableRateLimiting","getExpress","getApp","middlewares","shutdownCleanUp","exitCode","allSettled","exit","index","initExport","loggingOptions","initLogging","code","singleExport","batchExport","batchFunctions","pair","configIndex","findIndex","arg","fileName","loadConfigFile","showUsage","propertiesChain","argumentType","pairArgumentValue","mapToNewConfig","oldOptions","manualConfig","configFileName","configFile","choice","prompts","onSubmit","p","categories","questionsCounter","allQuestions","section","prompt","answer","module","writeFile","printLogo","packageVersion"],"mappings":"oqBAeO,MAAMA,EAAe,CAC1BC,KAAM,CAAC,aAAc,kBAAmB,iBACxCC,QAAS,CACP,QACA,MACA,QACA,YACA,uBACA,gBAEA,eACA,QACA,OACA,aACA,mBACA,eACA,cACA,UACA,UACA,cACA,WACA,UACA,YACA,cACA,YACA,sBACA,SACA,SACA,WACA,aACA,YACA,eAEA,SACA,eACA,YACA,kBACA,SACA,cACA,mBACA,eACA,kBACA,cACA,eAEA,cACA,WACA,eACA,WACA,SACA,OACA,WACA,YACA,SACA,qBACA,aACA,WACA,WACA,WACA,WACA,eACA,UACA,kBACA,oBACA,aACA,UACA,cACA,YACA,YAEFC,WAAY,CAAC,kBACbC,OAAQ,CACN,wEACA,mGAMSC,EAAgB,CAC3BC,UAAW,CACTC,KAAM,CACJC,MAAO,CACL,mCACA,kBACA,0CACA,2BACA,kCACA,kCACA,wCACA,2CACA,qBACA,4BACA,2CACA,uDACA,6BACA,yBACA,0BACA,+BACA,uBACA,uFACA,yBACA,oCACA,oBACA,0BACA,8CACA,2BACA,0BACA,6BACA,mCACA,wCACA,mCACA,2BACA,kCACA,uBACA,iBACA,yBACA,8BACA,oBACA,2BACA,eACA,6BACA,iBACA,aACA,SAEA,sBAEA,yBACA,oBACA,uBAEFC,KAAM,WACNC,YAAa,yCAEfC,QAAS,CACPH,MAAO,SACPC,KAAM,SACNG,QAAS,qBACTF,YAAa,0DAGjBG,WAAY,CACVC,QAAS,CACPN,MAAO,SACPC,KAAM,SACNG,QAAS,qBACTF,YAAa,sCAEfK,OAAQ,CACNP,MAAO,+BACPC,KAAM,SACNG,QAAS,qBACTF,YAAa,kDAEfM,OAAQ,CACNR,OAAO,EACPC,KAAM,UACNG,QAAS,qBACTF,YAAa,mDAEfO,YAAa,CACXT,MAAOR,EAAaC,KACpBQ,KAAM,WACNG,QAAS,0BACTF,YAAa,yCAEfQ,cAAe,CACbV,MAAOR,EAAaE,QACpBO,KAAM,WACNG,QAAS,4BACTF,YAAa,uCAEfS,iBAAkB,CAChBX,MAAOR,EAAaG,WACpBM,KAAM,WACNG,QAAS,+BACTF,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"} \ No newline at end of file +{"version":3,"file":"index.esm.js","sources":["../lib/schemas/config.js","../lib/envs.js","../lib/logger.js","../lib/utils.js","../lib/config.js","../lib/fetch.js","../lib/errors/ExportError.js","../lib/cache.js","../lib/highcharts.js","../lib/browser.js","../lib/export.js","../templates/svg_export/svg_export.js","../lib/errors/codes.js","../lib/pool.js","../lib/sanitize.js","../lib/chart.js","../lib/intervals.js","../lib/server/error.js","../lib/server/rate_limit.js","../lib/errors/HttpError.js","../lib/server/routes/change_hc_version.js","../lib/server/routes/export.js","../lib/server/routes/health.js","../lib/server/server.js","../lib/server/routes/ui.js","../lib/resource_release.js","../lib/index.js"],"sourcesContent":["/*******************************************************************************\n\nHighcharts Export Server\n\nCopyright (c) 2016-2024, 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// Possible names for Highcharts scripts\nexport const scriptsNames = {\n core: ['highcharts', 'highcharts-more', 'highcharts-3d'],\n modules: [\n 'stock',\n 'map',\n 'gantt',\n 'exporting',\n 'parallel-coordinates',\n 'accessibility',\n // 'annotations-advanced',\n 'boost-canvas',\n 'boost',\n 'data',\n 'data-tools',\n 'draggable-points',\n 'static-scale',\n 'broken-axis',\n 'heatmap',\n 'tilemap',\n 'tiledwebmap',\n 'timeline',\n 'treemap',\n 'treegraph',\n 'item-series',\n 'drilldown',\n 'histogram-bellcurve',\n 'bullet',\n 'funnel',\n 'funnel3d',\n 'geoheatmap',\n 'pyramid3d',\n 'networkgraph',\n // 'overlapping-datalabels',\n 'pareto',\n 'pattern-fill',\n 'pictorial',\n 'price-indicator',\n 'sankey',\n 'arc-diagram',\n 'dependency-wheel',\n 'series-label',\n 'series-on-point',\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 'flowmap',\n 'export-data',\n 'navigator',\n 'textpath'\n ],\n indicators: ['indicators-all'],\n custom: [\n 'https://cdnjs.cloudflare.com/ajax/libs/moment.js/2.30.1/moment.min.js',\n 'https://cdnjs.cloudflare.com/ajax/libs/moment-timezone/0.5.45/moment-timezone-with-data.min.js'\n ]\n};\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 '--allow-running-insecure-content',\n '--ash-no-nudges',\n '--autoplay-policy=user-gesture-required',\n '--block-new-web-contents',\n '--disable-accelerated-2d-canvas',\n '--disable-background-networking',\n '--disable-background-timer-throttling',\n '--disable-backgrounding-occluded-windows',\n '--disable-breakpad',\n '--disable-checker-imaging',\n '--disable-client-side-phishing-detection',\n '--disable-component-extensions-with-background-pages',\n '--disable-component-update',\n '--disable-default-apps',\n '--disable-dev-shm-usage',\n '--disable-domain-reliability',\n '--disable-extensions',\n '--disable-features=CalculateNativeWinOcclusion,InterestFeedContentSuggestions,WebOTP',\n '--disable-hang-monitor',\n '--disable-ipc-flooding-protection',\n '--disable-logging',\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-search-engine-choice-screen',\n '--disable-session-crashed-bubble',\n '--disable-setuid-sandbox',\n '--disable-site-isolation-trials',\n '--disable-speech-api',\n '--disable-sync',\n '--enable-unsafe-webgpu',\n '--hide-crash-restore-bubble',\n '--hide-scrollbars',\n '--metrics-recording-only',\n '--mute-audio',\n '--no-default-browser-check',\n '--no-first-run',\n '--no-pings',\n '--pipe',\n // '--no-sandbox',\n '--no-startup-window',\n // '--no-zygote',\n '--password-store=basic',\n '--process-per-tab',\n '--use-mock-keychain'\n ],\n type: 'string[]',\n description: 'Arguments array to send to Puppeteer.'\n },\n launchRetryWindow: {\n value: 30000,\n type: 'number',\n envLink: 'PUPPETEER_LAUNCH_RETRY_WINDOW',\n description:\n 'How long, in milliseconds, to keep retrying a browser launch before giving up. Delays between attempts grow and carry jitter. Keep this below the time an orchestrator waits before replacing an instance that has not become healthy, so that a browser which cannot start is reported rather than silently retried.'\n },\n tempDir: {\n value: './tmp/',\n type: 'string',\n envLink: 'PUPPETEER_TEMP_DIR',\n description: 'The directory for Puppeteer to store temporary files.'\n }\n },\n highcharts: {\n version: {\n value: 'latest',\n type: 'string',\n envLink: 'HIGHCHARTS_VERSION',\n description: 'The Highcharts version to be used.'\n },\n cdnURL: {\n value: 'https://code.highcharts.com/',\n type: 'string',\n envLink: 'HIGHCHARTS_CDN_URL',\n description: 'The CDN URL for Highcharts scripts to be used.'\n },\n useNpm: {\n value: false,\n type: 'boolean',\n envLink: 'HIGHCHARTS_USE_NPM',\n description: 'Flag to use Highcharts scripts from NPM package'\n },\n coreScripts: {\n value: scriptsNames.core,\n type: 'string[]',\n envLink: 'HIGHCHARTS_CORE_SCRIPTS',\n description: 'The core Highcharts scripts to fetch.'\n },\n moduleScripts: {\n value: scriptsNames.modules,\n type: 'string[]',\n envLink: 'HIGHCHARTS_MODULE_SCRIPTS',\n description: 'The modules of Highcharts to fetch.'\n },\n indicatorScripts: {\n value: scriptsNames.indicators,\n type: 'string[]',\n envLink: 'HIGHCHARTS_INDICATOR_SCRIPTS',\n description: 'The indicators of Highcharts to fetch.'\n },\n customScripts: {\n value: scriptsNames.custom,\n type: 'string[]',\n description: 'Additional custom scripts or dependencies to fetch.'\n },\n forceFetch: {\n value: false,\n type: 'boolean',\n envLink: 'HIGHCHARTS_FORCE_FETCH',\n description:\n 'The flag to determine whether to refetch all scripts after each server rerun.'\n },\n cachePath: {\n value: '.cache',\n type: 'string',\n envLink: 'HIGHCHARTS_CACHE_PATH',\n description:\n 'The path to the cache directory. It is used to store the Highcharts scripts and custom scripts.'\n }\n },\n export: {\n infile: {\n value: false,\n type: 'string',\n description:\n 'The input file should include a name and a type (json or svg). It must be correctly formatted as a JSON or SVG file.'\n },\n instr: {\n value: false,\n type: 'string',\n description:\n 'Input, provided in the form of a stringified JSON or SVG file, will override the --infile option.'\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). This will ignore the --type flag.'\n },\n type: {\n value: 'png',\n type: 'string',\n envLink: 'EXPORT_TYPE',\n description: 'The file export format. It can be jpeg, png, pdf, or svg.'\n },\n constr: {\n value: 'chart',\n type: 'string',\n envLink: 'EXPORT_CONSTR',\n description:\n 'The constructor to use. Can be chart, stockChart, mapChart, or ganttChart.'\n },\n defaultHeight: {\n value: 400,\n type: 'number',\n envLink: 'EXPORT_DEFAULT_HEIGHT',\n description:\n 'the default height of the exported chart. Used when no value is set.'\n },\n defaultWidth: {\n value: 600,\n type: 'number',\n envLink: 'EXPORT_DEFAULT_WIDTH',\n description:\n 'The default width of the exported chart. Used when no value is set.'\n },\n defaultScale: {\n value: 1,\n type: 'number',\n envLink: 'EXPORT_DEFAULT_SCALE',\n description:\n 'The default scale of the exported chart. Used when no value is set.'\n },\n height: {\n value: false,\n type: 'number',\n description:\n 'The height of the exported chart, overriding the option in the chart settings.'\n },\n width: {\n value: false,\n type: 'number',\n description:\n 'The width of the exported chart, overriding the option in the chart settings.'\n },\n scale: {\n value: false,\n type: 'number',\n description:\n 'The scale of the exported chart, overriding the option in the chart settings. Ranges between 0.1 and 5.0.'\n },\n globalOptions: {\n value: false,\n type: 'string',\n description:\n 'Either a stringified JSON or a filename containing options to be passed into the Highcharts.setOptions.'\n },\n themeOptions: {\n value: false,\n type: 'string',\n description:\n 'Either a stringified JSON or a filename containing theme options to be passed into the Highcharts.setOptions.'\n },\n batch: {\n value: false,\n type: 'string',\n description:\n 'Initiates a batch job with a string containing input/output pairs: \"in=out;in=out;...\".'\n },\n rasterizationTimeout: {\n value: 1500,\n type: 'number',\n envLink: 'EXPORT_RASTERIZATION_TIMEOUT',\n description:\n 'The duration in milliseconds to wait for rendering a webpage.'\n }\n },\n customLogic: {\n allowCodeExecution: {\n value: false,\n type: 'boolean',\n envLink: 'CUSTOM_LOGIC_ALLOW_CODE_EXECUTION',\n description:\n 'Controls whether the execution of arbitrary code is allowed during the exporting process.'\n },\n allowFileResources: {\n value: false,\n type: 'boolean',\n envLink: 'CUSTOM_LOGIC_ALLOW_FILE_RESOURCES',\n description:\n 'Controls the ability to inject resources from the filesystem. This setting has no effect when running as a server.'\n },\n customCode: {\n value: false,\n type: 'string',\n description:\n 'Custom code to execute before chart initialization. It can be a function, code wrapped within a function, or a filename with the .js extension.'\n },\n callback: {\n value: false,\n type: 'string',\n description:\n 'JavaScript code to run during construction. It can be a function or a filename with the .js extension.'\n },\n resources: {\n value: false,\n type: 'string',\n description:\n 'Additional resource in the form of a stringified JSON, which may contain files, js, and css sections.'\n },\n loadConfig: {\n value: false,\n type: 'string',\n legacyName: 'fromFile',\n description: 'A file containing a pre-defined configuration to use.'\n },\n createConfig: {\n value: false,\n type: 'string',\n description:\n 'Enables setting options through a prompt and saving them in a provided config file.'\n }\n },\n server: {\n maxUploadSize: {\n value: 3,\n type: 'number',\n envLink: 'SERVER_MAX_UPLOAD_SIZE',\n description: 'The maximum upload size, in MB, for the server.'\n },\n enable: {\n value: false,\n type: 'boolean',\n envLink: 'SERVER_ENABLE',\n cliName: 'enableServer',\n description:\n 'When set to true, the server starts on the local IP address 0.0.0.0.'\n },\n host: {\n value: '0.0.0.0',\n type: 'string',\n envLink: 'SERVER_HOST',\n description:\n 'The hostname of the server. Additionally, it starts a server on the provided hostname.'\n },\n port: {\n value: 7801,\n type: 'number',\n envLink: 'SERVER_PORT',\n description: 'The server port when enabled.'\n },\n keepAliveTimeout: {\n value: 65000,\n type: 'number',\n envLink: 'SERVER_KEEP_ALIVE_TIMEOUT',\n description:\n 'How long, in milliseconds, an idle keep-alive connection is held open. Node defaults to 5 seconds, which is shorter than the idle timeout of a typical proxy or load balancer, and the mismatch causes sporadic gateway errors when the proxy sends a request into a connection this server has just closed. Keep this above the idle timeout of anything in front of the server. headersTimeout is kept 5 seconds above it automatically.'\n },\n benchmarking: {\n value: false,\n type: 'boolean',\n envLink: 'SERVER_BENCHMARKING',\n cliName: 'serverBenchmarking',\n description:\n 'Indicates whether to display the duration, in milliseconds, of specific actions that occur on the server while serving a request.'\n },\n proxy: {\n host: {\n value: false,\n type: 'string',\n envLink: 'SERVER_PROXY_HOST',\n cliName: 'proxyHost',\n description: 'The host of the proxy server to use, if it exists.'\n },\n port: {\n value: 8080,\n type: 'number',\n envLink: 'SERVER_PROXY_PORT',\n cliName: 'proxyPort',\n description: 'The port of the proxy server to use, if it exists.'\n },\n username: {\n value: false,\n type: 'string',\n envLink: 'SERVER_PROXY_USERNAME',\n cliName: 'proxyUsername',\n description: 'The username for the proxy server, if it exists.'\n },\n password: {\n value: false,\n type: 'string',\n envLink: 'SERVER_PROXY_PASSWORD',\n cliName: 'proxyPassword',\n description: 'The password for the proxy server, if it exists.'\n },\n timeout: {\n value: 5000,\n type: 'number',\n envLink: 'SERVER_PROXY_TIMEOUT',\n cliName: 'proxyTimeout',\n description: 'The timeout for the proxy server to use, if it exists.'\n }\n },\n rateLimiting: {\n enable: {\n value: false,\n type: 'boolean',\n envLink: 'SERVER_RATE_LIMITING_ENABLE',\n cliName: 'enableRateLimiting',\n description: 'Enables rate limiting for the server.'\n },\n maxRequests: {\n value: 10,\n type: 'number',\n envLink: 'SERVER_RATE_LIMITING_MAX_REQUESTS',\n legacyName: 'rateLimit',\n description: 'The maximum number of requests allowed in one minute.'\n },\n window: {\n value: 1,\n type: 'number',\n envLink: 'SERVER_RATE_LIMITING_WINDOW',\n description: 'The time window, in minutes, for the rate limiting.'\n },\n trustProxy: {\n value: false,\n type: 'boolean',\n envLink: 'SERVER_RATE_LIMITING_TRUST_PROXY',\n description: 'Set this to true if the server is behind a load balancer.'\n },\n skipKey: {\n value: false,\n type: 'string',\n envLink: 'SERVER_RATE_LIMITING_SKIP_KEY',\n description:\n 'Allows bypassing the rate limiter and should be provided with the skipToken argument.'\n },\n skipToken: {\n value: false,\n type: 'string',\n envLink: 'SERVER_RATE_LIMITING_SKIP_TOKEN',\n description:\n 'Allows bypassing the rate limiter and should be provided with the skipKey argument.'\n }\n },\n ssl: {\n enable: {\n value: false,\n type: 'boolean',\n envLink: 'SERVER_SSL_ENABLE',\n cliName: 'enableSsl',\n description: 'Enables or disables the SSL protocol.'\n },\n force: {\n value: false,\n type: 'boolean',\n envLink: 'SERVER_SSL_FORCE',\n cliName: 'sslForce',\n legacyName: 'sslOnly',\n description:\n 'When set to true, the server is forced to serve only over HTTPS.'\n },\n port: {\n value: 443,\n type: 'number',\n envLink: 'SERVER_SSL_PORT',\n cliName: 'sslPort',\n description: 'The port on which to run the SSL server.'\n },\n certPath: {\n value: false,\n type: 'string',\n envLink: 'SERVER_SSL_CERT_PATH',\n legacyName: 'sslPath',\n description: 'The path to the SSL certificate/key file.'\n }\n }\n },\n pool: {\n minWorkers: {\n value: 4,\n type: 'number',\n envLink: 'POOL_MIN_WORKERS',\n description: 'The number of minimum and initial pool workers to spawn.'\n },\n maxWorkers: {\n value: 8,\n type: 'number',\n envLink: 'POOL_MAX_WORKERS',\n legacyName: 'workers',\n description: 'The number of maximum pool workers to spawn.'\n },\n workLimit: {\n value: 40,\n type: 'number',\n envLink: 'POOL_WORK_LIMIT',\n description:\n 'The number of work pieces that can be performed before restarting the worker process.'\n },\n queueLimit: {\n value: 0,\n type: 'number',\n envLink: 'POOL_QUEUE_LIMIT',\n description:\n 'The maximum number of exports allowed to wait for a worker. Requests arriving beyond this are refused instead of being queued. Set to 0 to derive it as four times maxWorkers. Throughput does not improve past the pool size, so a deeper queue only adds latency and memory use.'\n },\n queueRejectDelay: {\n value: 500,\n type: 'number',\n envLink: 'POOL_QUEUE_REJECT_DELAY',\n description:\n 'The duration, in milliseconds, to wait before answering a request refused because the queue is full. This is deliberate backpressure: answering instantly lets clients that retry immediately consume the whole event loop refusing them, starving the exports already in progress. Set to 0 to answer immediately, only advisable when something upstream is limiting the request rate.'\n },\n acquireTimeout: {\n value: 5000,\n type: 'number',\n envLink: 'POOL_ACQUIRE_TIMEOUT',\n description:\n 'The duration, in milliseconds, to wait for acquiring a resource.'\n },\n createTimeout: {\n value: 5000,\n type: 'number',\n envLink: 'POOL_CREATE_TIMEOUT',\n description:\n 'The duration, in milliseconds, to wait for creating a resource.'\n },\n destroyTimeout: {\n value: 5000,\n type: 'number',\n envLink: 'POOL_DESTROY_TIMEOUT',\n description:\n 'The duration, in milliseconds, to wait for destroying a resource.'\n },\n idleTimeout: {\n value: 30000,\n type: 'number',\n envLink: 'POOL_IDLE_TIMEOUT',\n description:\n 'The duration, in milliseconds, after which an idle resource is destroyed.'\n },\n createRetryInterval: {\n value: 200,\n type: 'number',\n envLink: 'POOL_CREATE_RETRY_INTERVAL',\n description:\n 'The duration, in milliseconds, to wait before retrying the create process in case of a failure.'\n },\n reaperInterval: {\n value: 1000,\n type: 'number',\n envLink: 'POOL_REAPER_INTERVAL',\n description:\n 'The duration, in milliseconds, after which the check for idle resources to destroy is triggered.'\n },\n benchmarking: {\n value: false,\n type: 'boolean',\n envLink: 'POOL_BENCHMARKING',\n cliName: 'poolBenchmarking',\n description:\n 'Indicate whether to show statistics for the pool of resources or not.'\n }\n },\n logging: {\n level: {\n value: 4,\n type: 'number',\n envLink: 'LOGGING_LEVEL',\n cliName: 'logLevel',\n description: 'The logging level to be used.'\n },\n file: {\n value: 'highcharts-export-server.log',\n type: 'string',\n envLink: 'LOGGING_FILE',\n cliName: 'logFile',\n description:\n 'The name of a log file. The `logToFile` and `logDest` options also need to be set to enable file logging.'\n },\n dest: {\n value: 'log/',\n type: 'string',\n envLink: 'LOGGING_DEST',\n cliName: 'logDest',\n description:\n 'The path to store log files. The `logToFile` option also needs to be set to enable file logging.'\n },\n toConsole: {\n value: true,\n type: 'boolean',\n envLink: 'LOGGING_TO_CONSOLE',\n cliName: 'logToConsole',\n description: 'Enables or disables showing logs in the console.'\n },\n toFile: {\n value: true,\n type: 'boolean',\n envLink: 'LOGGING_TO_FILE',\n cliName: 'logToFile',\n description:\n 'Enables or disables creation of the log directory and saving the log into a .log file.'\n }\n },\n ui: {\n enable: {\n value: false,\n type: 'boolean',\n envLink: 'UI_ENABLE',\n cliName: 'enableUi',\n description:\n 'Enables or disables the user interface (UI) for the export server.'\n },\n route: {\n value: '/',\n type: 'string',\n envLink: 'UI_ROUTE',\n cliName: 'uiRoute',\n description:\n 'The endpoint route to which the user interface (UI) should be attached.'\n }\n },\n other: {\n nodeEnv: {\n value: 'production',\n type: 'string',\n envLink: 'OTHER_NODE_ENV',\n description: 'The type of Node.js environment.'\n },\n listenToProcessExits: {\n value: true,\n type: 'boolean',\n envLink: 'OTHER_LISTEN_TO_PROCESS_EXITS',\n description: 'Decides whether or not to attach process.exit handlers.'\n },\n noLogo: {\n value: false,\n type: 'boolean',\n envLink: 'OTHER_NO_LOGO',\n description:\n 'Skip printing the logo on a startup. Will be replaced by a simple text.'\n },\n hardResetPage: {\n value: false,\n type: 'boolean',\n envLink: 'OTHER_HARD_RESET_PAGE',\n description: 'Decides if the page content should be reset entirely.'\n },\n shutdownDrainTimeout: {\n value: 30000,\n type: 'number',\n envLink: 'OTHER_SHUTDOWN_DRAIN_TIMEOUT',\n description:\n 'How long, in milliseconds, to let requests already being served finish during a shutdown, before the worker pool is taken away and the process exits. Set it above the deregistration delay of anything routing traffic to this server, so that it has stopped sending before the server stops answering.'\n },\n browserShellMode: {\n value: true,\n type: 'boolean',\n envLink: 'OTHER_BROWSER_SHELL_MODE',\n description: 'Decides if the browser runs in the shell mode.'\n }\n },\n debug: {\n enable: {\n value: false,\n type: 'boolean',\n envLink: 'DEBUG_ENABLE',\n cliName: 'enableDebug',\n description: 'Enables or disables debug mode for the underlying browser.'\n },\n headless: {\n value: true,\n type: 'boolean',\n envLink: 'DEBUG_HEADLESS',\n description:\n 'Controls the mode in which the browser is launched when in the debug mode.'\n },\n devtools: {\n value: false,\n type: 'boolean',\n envLink: 'DEBUG_DEVTOOLS',\n description:\n 'Decides whether to enable DevTools when the browser is in a headful state.'\n },\n listenToConsole: {\n value: false,\n type: 'boolean',\n envLink: 'DEBUG_LISTEN_TO_CONSOLE',\n description:\n 'Decides whether to enable a listener for console messages sent from the browser.'\n },\n dumpio: {\n value: false,\n type: 'boolean',\n envLink: 'DEBUG_DUMPIO',\n description:\n 'Redirects browser process stdout and stderr to process.stdout and process.stderr.'\n },\n slowMo: {\n value: 0,\n type: 'number',\n envLink: 'DEBUG_SLOW_MO',\n description:\n 'Slows down Puppeteer operations by the specified number of milliseconds.'\n },\n debuggingPort: {\n value: 9222,\n type: 'number',\n envLink: 'DEBUG_DEBUGGING_PORT',\n description: 'Specifies the debugging port.'\n }\n }\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: 'toggle',\n name: 'useNpm',\n message: 'Flag to use Highcharts scripts from NPM package',\n initial: defaultConfig.highcharts.useNpm.value\n },\n {\n type: 'multiselect',\n name: 'coreScripts',\n message: 'Available core scripts',\n instructions: 'Space: Select specific, A: Select all, Enter: Confirm.',\n choices: defaultConfig.highcharts.coreScripts.value\n },\n {\n type: 'multiselect',\n name: 'moduleScripts',\n message: 'Available module scripts',\n instructions: 'Space: Select specific, A: Select all, Enter: Confirm.',\n choices: defaultConfig.highcharts.moduleScripts.value\n },\n {\n type: 'multiselect',\n name: 'indicatorScripts',\n message: 'Available indicator scripts',\n instructions: 'Space: Select specific, A: Select all, Enter: Confirm.',\n choices: defaultConfig.highcharts.indicatorScripts.value\n },\n {\n type: 'list',\n name: 'customScripts',\n message: 'Custom scripts',\n initial: defaultConfig.highcharts.customScripts.value.join(','),\n separator: ','\n },\n {\n type: 'toggle',\n name: 'forceFetch',\n message: 'Force re-fetch the scripts',\n initial: defaultConfig.highcharts.forceFetch.value\n },\n {\n type: 'text',\n name: 'cachePath',\n message: 'The path to the cache directory',\n initial: defaultConfig.highcharts.cachePath.value\n }\n ],\n export: [\n {\n type: 'select',\n name: 'type',\n message: 'The default export file type',\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',\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 type: 'number',\n name: 'rasterizationTimeout',\n message: 'The rendering webpage timeout in milliseconds',\n initial: defaultConfig.export.rasterizationTimeout.value\n }\n ],\n customLogic: [\n {\n type: 'toggle',\n name: 'allowCodeExecution',\n message: 'Enable execution of custom code',\n initial: defaultConfig.customLogic.allowCodeExecution.value\n },\n {\n type: 'toggle',\n name: 'allowFileResources',\n message: 'Enable file resources',\n initial: defaultConfig.customLogic.allowFileResources.value\n }\n ],\n server: [\n {\n type: 'toggle',\n name: 'enable',\n message: 'Starts the server on 0.0.0.0',\n initial: defaultConfig.server.enable.value\n },\n {\n type: 'text',\n name: 'host',\n message: 'Server hostname',\n initial: defaultConfig.server.host.value\n },\n {\n type: 'number',\n name: 'port',\n message: 'Server port',\n initial: defaultConfig.server.port.value\n },\n {\n type: 'toggle',\n name: 'benchmarking',\n message: 'Enable server benchmarking',\n initial: defaultConfig.server.benchmarking.value\n },\n {\n type: 'text',\n name: 'proxy.host',\n message: 'The host of the proxy server to use',\n initial: defaultConfig.server.proxy.host.value\n },\n {\n type: 'number',\n name: 'proxy.port',\n message: 'The port of the proxy server to use',\n initial: defaultConfig.server.proxy.port.value\n },\n {\n type: 'number',\n name: 'proxy.timeout',\n message: 'The timeout for the proxy server to use',\n initial: defaultConfig.server.proxy.timeout.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: 'The maximum requests allowed per minute',\n initial: defaultConfig.server.rateLimiting.maxRequests.value\n },\n {\n type: 'number',\n name: 'rateLimiting.window',\n message: 'The rate-limiting time window in minutes',\n initial: defaultConfig.server.rateLimiting.window.value\n },\n {\n type: 'toggle',\n name: 'rateLimiting.trustProxy',\n message: 'Set 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 when provided with the 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 when provided with the skipKey argument',\n initial: defaultConfig.server.rateLimiting.skipToken.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 serving only over HTTPS',\n initial: defaultConfig.server.ssl.force.value\n },\n {\n type: 'number',\n name: 'ssl.port',\n message: 'SSL server port',\n initial: defaultConfig.server.ssl.port.value\n },\n {\n type: 'text',\n name: 'ssl.certPath',\n message: 'The path to find the SSL certificate/key',\n initial: defaultConfig.server.ssl.certPath.value\n }\n ],\n pool: [\n {\n type: 'number',\n name: 'minWorkers',\n message: 'The initial number of workers to spawn',\n initial: defaultConfig.pool.minWorkers.value\n },\n {\n type: 'number',\n name: 'maxWorkers',\n message: 'The maximum number of 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: 'queueLimit',\n message:\n 'The maximum number of exports allowed to wait for a worker, or 0 to derive it from maxWorkers',\n initial: defaultConfig.pool.queueLimit.value\n },\n {\n type: 'number',\n name: 'queueRejectDelay',\n message:\n 'The number of milliseconds to wait before refusing a request because the queue is full',\n initial: defaultConfig.pool.queueRejectDelay.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: 'number',\n name: 'createTimeout',\n message: 'The number of milliseconds to wait for creating a resource',\n initial: defaultConfig.pool.createTimeout.value\n },\n {\n type: 'number',\n name: 'destroyTimeout',\n message: 'The number of milliseconds to wait for destroying a resource',\n initial: defaultConfig.pool.destroyTimeout.value\n },\n {\n type: 'number',\n name: 'idleTimeout',\n message: 'The number of milliseconds after an idle resource is destroyed',\n initial: defaultConfig.pool.idleTimeout.value\n },\n {\n type: 'number',\n name: 'createRetryInterval',\n message:\n 'The retry interval in milliseconds after a create process fails',\n initial: defaultConfig.pool.createRetryInterval.value\n },\n {\n type: 'number',\n name: 'reaperInterval',\n message:\n 'The reaper interval in milliseconds after triggering the check for idle resources to destroy',\n initial: defaultConfig.pool.reaperInterval.value\n },\n {\n type: 'toggle',\n name: 'benchmarking',\n message: 'Enable benchmarking for a resource pool',\n initial: defaultConfig.pool.benchmarking.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, 5: benchmark)',\n initial: defaultConfig.logging.level.value,\n round: 0,\n min: 0,\n max: 5\n },\n {\n type: 'text',\n name: 'file',\n message:\n 'A log file name. Set with --toFile and --logDest to enable file logging',\n initial: defaultConfig.logging.file.value\n },\n {\n type: 'text',\n name: 'dest',\n message: 'The path to a log file when the file logging is enabled',\n initial: defaultConfig.logging.dest.value\n },\n {\n type: 'toggle',\n name: 'toConsole',\n message: 'Enable logging to the console',\n initial: defaultConfig.logging.toConsole.value\n },\n {\n type: 'toggle',\n name: 'toFile',\n message: 'Enables logging to a file',\n initial: defaultConfig.logging.toFile.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',\n initial: defaultConfig.ui.route.value\n }\n ],\n other: [\n {\n type: 'text',\n name: 'nodeEnv',\n message: 'The type of Node.js environment',\n initial: defaultConfig.other.nodeEnv.value\n },\n {\n type: 'toggle',\n name: 'listenToProcessExits',\n message: 'Set to false to skip attaching process.exit handlers',\n initial: defaultConfig.other.listenToProcessExits.value\n },\n {\n type: 'toggle',\n name: 'noLogo',\n message: 'Skip printing the logo on startup. Replaced by simple text',\n initial: defaultConfig.other.noLogo.value\n },\n {\n type: 'toggle',\n name: 'hardResetPage',\n message: 'Decides if the page content should be reset entirely',\n initial: defaultConfig.other.hardResetPage.value\n },\n {\n type: 'toggle',\n name: 'browserShellMode',\n message: 'Decides if the browser runs in the shell mode',\n initial: defaultConfig.other.browserShellMode.value\n }\n ],\n debug: [\n {\n type: 'toggle',\n name: 'enable',\n message: 'Enables debug mode for the browser instance',\n initial: defaultConfig.debug.enable.value\n },\n {\n type: 'toggle',\n name: 'headless',\n message: 'The mode setting for the browser',\n initial: defaultConfig.debug.headless.value\n },\n {\n type: 'toggle',\n name: 'devtools',\n message: 'The DevTools for the headful browser',\n initial: defaultConfig.debug.devtools.value\n },\n {\n type: 'toggle',\n name: 'listenToConsole',\n message: 'The event listener for console messages from the browser',\n initial: defaultConfig.debug.listenToConsole.value\n },\n {\n type: 'toggle',\n name: 'dumpio',\n message: 'Redirects the browser stdout and stderr to NodeJS process',\n initial: defaultConfig.debug.dumpio.value\n },\n {\n type: 'number',\n name: 'slowMo',\n message: 'Puppeteer operations slow down in milliseconds',\n initial: defaultConfig.debug.slowMo.value\n },\n {\n type: 'number',\n name: 'debuggingPort',\n message: 'The port number for debugging',\n initial: defaultConfig.debug.debuggingPort.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 * Recursively creates a chain of nested arguments from an object.\n *\n * @param {Object} obj - The object containing nested arguments.\n * @param {string} propChain - The current chain of nested properties\n * (used internally during recursion).\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 // Support for the legacy, PhantomJS properties names\n if (entry.legacyName !== undefined) {\n nestedArgs[entry.legacyName] = `${propChain}.${k}`.substring(1);\n }\n }\n }\n });\n};\n\ncreateNestedArgs(defaultConfig);\n","/**\n * @fileoverview\n * This file is responsible for parsing the environment variables with the 'zod'\n * library. The parsed environment variables are then exported to be used\n * in the application as \"envs\". We should not use process.env directly\n * in the application as these would not be parsed properly.\n *\n * The environment variables are parsed and validated only once when\n * the application starts. We should write a custom validator or a transformer\n * for each of the options.\n */\n\nimport dotenv from 'dotenv';\nimport { z } from 'zod';\n\nimport { scriptsNames } from './schemas/config.js';\n\n// Load .env into environment variables.\n//\n// quiet: true suppresses the summary dotenv prints from v17 onwards. This process\n// writes structured log lines and its output is read by other tooling, so an\n// unsolicited banner on stdout is noise.\ndotenv.config({ quiet: true });\n\n// Object with custom validators and transformers, to avoid repetition\n// in the Config object\nconst v = {\n // Splits string value into elements in an array, trims every element, checks\n // if an array is correct, if it is empty, and if it is, returns undefined\n array: (filterArray) =>\n z\n .string()\n .transform((value) =>\n value\n .split(',')\n .map((value) => value.trim())\n .filter((value) => filterArray.includes(value))\n )\n .transform((value) => (value.length ? value : undefined)),\n\n // Allows only true, false and correctly parse the value to boolean\n // or no value in which case the returned value will be undefined\n boolean: () =>\n z\n .enum(['true', 'false', ''])\n .transform((value) => (value !== '' ? value === 'true' : undefined)),\n\n // Allows passed values or no value in which case the returned value will\n // be undefined\n enum: (values) =>\n z\n .enum([...values, ''])\n .transform((value) => (value !== '' ? value : undefined)),\n\n // Trims the string value and checks if it is empty or contains stringified\n // values such as false, undefined, null, NaN, if it does, returns undefined\n string: () =>\n z\n .string()\n .trim()\n .refine(\n (value) =>\n !['false', 'undefined', 'null', 'NaN'].includes(value) ||\n value === '',\n {\n error: (issue) =>\n `The string contains forbidden values, received '${issue.input}'`\n }\n )\n .transform((value) => (value !== '' ? value : undefined)),\n\n // Checks if the string is a valid path directory (path format)\n path: () =>\n z\n .string()\n .trim()\n .refine(\n (value) => {\n // Simplified regex to match both absolute and relative paths\n return /^(\\.\\/|\\.\\.\\/|\\/|[a-zA-Z]:\\\\|[a-zA-Z]:\\/)?((?:[\\w-]+)[\\\\/]?)+$/.test(\n value\n );\n },\n { error: 'The string is an invalid path directory string.' }\n ),\n\n // Allows positive numbers or no value in which case the returned value will\n // be undefined\n positiveNum: () =>\n z\n .string()\n .trim()\n .refine(\n (value) =>\n value === '' || (!isNaN(parseFloat(value)) && parseFloat(value) > 0),\n {\n error: (issue) =>\n `The value must be numeric and positive, received '${issue.input}'`\n }\n )\n .transform((value) => (value !== '' ? parseFloat(value) : undefined)),\n\n // Allows non-negative numbers or no value in which case the returned value\n // will be undefined\n nonNegativeNum: () =>\n z\n .string()\n .trim()\n .refine(\n (value) =>\n value === '' || (!isNaN(parseFloat(value)) && parseFloat(value) >= 0),\n {\n error: (issue) =>\n `The value must be numeric and non-negative, received '${issue.input}'`\n }\n )\n .transform((value) => (value !== '' ? parseFloat(value) : undefined))\n};\n\nexport const Config = z.object({\n // puppeteer\n PUPPETEER_TEMP_DIR: v.path(),\n PUPPETEER_LAUNCH_RETRY_WINDOW: v.nonNegativeNum(),\n\n // highcharts\n HIGHCHARTS_VERSION: z\n .string()\n .trim()\n .refine(\n (value) => /^(latest|\\d+(\\.\\d+){0,2})$/.test(value) || value === '',\n {\n error: (issue) =>\n `HIGHCHARTS_VERSION must be 'latest', a major version, or in the form XX.YY.ZZ, received '${issue.input}'`\n }\n )\n .transform((value) => (value !== '' ? value : undefined)),\n HIGHCHARTS_CDN_URL: z\n .string()\n .trim()\n .refine(\n (value) =>\n value.startsWith('https://') ||\n value.startsWith('http://') ||\n value === '',\n {\n error: (issue) =>\n `Invalid value for HIGHCHARTS_CDN_URL. It should start with http:// or https://, received '${issue.input}'`\n }\n )\n .transform((value) => (value !== '' ? value : undefined)),\n HIGHCHARTS_USE_NPM: v.boolean(),\n HIGHCHARTS_CORE_SCRIPTS: v.array(scriptsNames.core),\n HIGHCHARTS_MODULE_SCRIPTS: v.array(scriptsNames.modules),\n HIGHCHARTS_INDICATOR_SCRIPTS: v.array(scriptsNames.indicators),\n HIGHCHARTS_FORCE_FETCH: v.boolean(),\n HIGHCHARTS_CACHE_PATH: v.string(),\n HIGHCHARTS_ADMIN_TOKEN: v.string(),\n\n // export\n EXPORT_TYPE: v.enum(['jpeg', 'png', 'pdf', 'svg']),\n EXPORT_CONSTR: v.enum(['chart', 'stockChart', 'mapChart', 'ganttChart']),\n EXPORT_DEFAULT_HEIGHT: v.positiveNum(),\n EXPORT_DEFAULT_WIDTH: v.positiveNum(),\n EXPORT_DEFAULT_SCALE: v.positiveNum(),\n EXPORT_RASTERIZATION_TIMEOUT: v.nonNegativeNum(),\n\n // custom\n CUSTOM_LOGIC_ALLOW_CODE_EXECUTION: v.boolean(),\n CUSTOM_LOGIC_ALLOW_FILE_RESOURCES: v.boolean(),\n\n // server\n SERVER_ENABLE: v.boolean(),\n SERVER_HOST: v.string(),\n SERVER_PORT: v.positiveNum(),\n SERVER_MAX_UPLOAD_SIZE: v.positiveNum(),\n SERVER_KEEP_ALIVE_TIMEOUT: v.nonNegativeNum(),\n SERVER_BENCHMARKING: v.boolean(),\n\n // server proxy\n SERVER_PROXY_HOST: v.string(),\n SERVER_PROXY_PORT: v.positiveNum(),\n SERVER_PROXY_USERNAME: v.string(),\n SERVER_PROXY_PASSWORD: v.string(),\n SERVER_PROXY_TIMEOUT: v.nonNegativeNum(),\n\n // server rate limiting\n SERVER_RATE_LIMITING_ENABLE: v.boolean(),\n SERVER_RATE_LIMITING_MAX_REQUESTS: v.nonNegativeNum(),\n SERVER_RATE_LIMITING_WINDOW: v.nonNegativeNum(),\n SERVER_RATE_LIMITING_TRUST_PROXY: v.boolean(),\n SERVER_RATE_LIMITING_SKIP_KEY: v.string(),\n SERVER_RATE_LIMITING_SKIP_TOKEN: v.string(),\n\n // server ssl\n SERVER_SSL_ENABLE: v.boolean(),\n SERVER_SSL_FORCE: v.boolean(),\n SERVER_SSL_PORT: v.positiveNum(),\n SERVER_SSL_CERT_PATH: v.string(),\n\n // pool\n POOL_MIN_WORKERS: v.nonNegativeNum(),\n POOL_MAX_WORKERS: v.nonNegativeNum(),\n POOL_WORK_LIMIT: v.positiveNum(),\n POOL_QUEUE_LIMIT: v.nonNegativeNum(),\n POOL_QUEUE_REJECT_DELAY: v.nonNegativeNum(),\n POOL_ACQUIRE_TIMEOUT: v.nonNegativeNum(),\n POOL_CREATE_TIMEOUT: v.nonNegativeNum(),\n POOL_DESTROY_TIMEOUT: v.nonNegativeNum(),\n POOL_IDLE_TIMEOUT: v.nonNegativeNum(),\n POOL_CREATE_RETRY_INTERVAL: v.nonNegativeNum(),\n POOL_REAPER_INTERVAL: v.nonNegativeNum(),\n POOL_BENCHMARKING: v.boolean(),\n\n // logger\n LOGGING_LEVEL: z\n .string()\n .trim()\n .refine(\n (value) =>\n value === '' ||\n (!isNaN(parseFloat(value)) &&\n parseFloat(value) >= 0 &&\n parseFloat(value) <= 5),\n {\n error: (issue) =>\n `Invalid value for LOGGING_LEVEL. We only accept values from 0 to 5 as logging levels, received '${issue.input}'`\n }\n )\n .transform((value) => (value !== '' ? parseFloat(value) : undefined)),\n LOGGING_FILE: v.string(),\n LOGGING_DEST: v.string(),\n LOGGING_TO_CONSOLE: v.boolean(),\n LOGGING_TO_FILE: v.boolean(),\n\n // ui\n UI_ENABLE: v.boolean(),\n UI_ROUTE: v.string(),\n\n // other\n OTHER_SHUTDOWN_DRAIN_TIMEOUT: v.nonNegativeNum(),\n OTHER_NODE_ENV: v.enum(['development', 'production', 'test']),\n OTHER_LISTEN_TO_PROCESS_EXITS: v.boolean(),\n OTHER_NO_LOGO: v.boolean(),\n OTHER_HARD_RESET_PAGE: v.boolean(),\n OTHER_BROWSER_SHELL_MODE: v.boolean(),\n OTHER_ALLOW_XLINK: v.boolean(),\n\n // debugger\n DEBUG_ENABLE: v.boolean(),\n DEBUG_HEADLESS: v.boolean(),\n DEBUG_DEVTOOLS: v.boolean(),\n DEBUG_LISTEN_TO_CONSOLE: v.boolean(),\n DEBUG_DUMPIO: v.boolean(),\n DEBUG_SLOW_MO: v.nonNegativeNum(),\n DEBUG_DEBUGGING_PORT: v.positiveNum()\n});\n\nexport const envs = Config.partial().parse(process.env);\n","/*******************************************************************************\n\nHighcharts Export Server\n\nCopyright (c) 2016-2024, 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\n// The available colors\nconst colors = ['red', 'yellow', 'blue', 'gray', 'green'];\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: colors[0]\n },\n {\n title: 'warning',\n color: colors[1]\n },\n {\n title: 'notice',\n color: colors[2]\n },\n {\n title: 'verbose',\n color: colors[3]\n },\n {\n title: 'benchmark',\n color: colors[4]\n }\n ],\n // Log listeners\n listeners: []\n};\n\n/**\n * Logs the provided texts to a file, if file logging is enabled. It creates\n * the necessary directory structure if not already created and appends the\n * content, including an optional prefix, to the specified log file.\n *\n * @param {string[]} texts - An array of texts to be logged.\n * @param {string} prefix - An optional prefix to be added to each log entry.\n */\nconst logToFile = (texts, prefix) => {\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/**\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 { levelsDesc, level } = logging;\n\n // Check if log level is within a correct range or is a benchmark log\n if (\n newLevel !== 5 &&\n (newLevel === 0 || newLevel > level || level > levelsDesc.length)\n ) {\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 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 // Log to file\n if (logging.toFile) {\n logToFile(texts, prefix);\n }\n};\n\n/**\n * Logs an error message with its stack trace. Optionally, a custom message\n * can be provided.\n *\n * @param {number} level - The log level.\n * @param {Error} error - The error object.\n * @param {string} customMessage - An optional custom message to be logged along\n * with the error.\n */\nexport const logWithStack = (newLevel, error, customMessage) => {\n // Get the main message\n const mainMessage = customMessage || error.message;\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 // If the customMessage exists, we want to display the whole stack message\n const stackMessage =\n error.message !== error.stackMessage || error.stackMessage === undefined\n ? error.stack\n : error.stack.split('\\n').slice(1).join('\\n');\n\n // Combine custom message or error message with error stack message\n const texts = [mainMessage, '\\n', stackMessage];\n\n // Log to console\n if (logging.toConsole) {\n console.log.apply(\n undefined,\n [prefix.toString()[logging.levelsDesc[newLevel - 1].color]].concat([\n mainMessage[colors[newLevel - 1]],\n '\\n',\n stackMessage\n ])\n );\n }\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 logToFile(texts, prefix);\n }\n};\n\n/**\n * Sets the log level to the specified value. Log levels are (0 = no logging,\n * 1 = error, 2 = warning, 3 = notice, 4 = verbose or 5 = benchmark)\n *\n * @param {number} newLevel - The new log level to be set.\n */\nexport const setLogLevel = (newLevel) => {\n if (newLevel >= 0 && newLevel <= logging.levelsDesc.length) {\n logging.level = newLevel;\n }\n};\n\n/**\n * Enables file logging with the specified destination and log file.\n *\n * @param {string} logDest - The destination path for log files.\n * @param {string} logFile - The log file name.\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 initialization: no path supplied.');\n }\n\n if (!logging.dest.endsWith('/')) {\n logging.dest += '/';\n }\n};\n\n/**\n * Initializes logging with the specified logging configuration.\n *\n * @param {Object} loggingOptions - The logging configuration object.\n */\nexport const initLogging = (loggingOptions) => {\n // Set all the logging options on our logging module object\n for (const [key, value] of Object.entries(loggingOptions)) {\n logging[key] = value;\n }\n\n // Set the log level\n setLogLevel(loggingOptions && parseInt(loggingOptions.level));\n\n // Set the log file path and name\n if (loggingOptions && loggingOptions.dest && loggingOptions.toFile) {\n enableFileLogging(\n loggingOptions.dest,\n loggingOptions.file || 'highcharts-export-server.log'\n );\n }\n};\n\n/**\n * Adds a listener function to the logging system.\n *\n * @param {function} fn - The listener function to be added.\n */\nexport const listen = (fn) => {\n logging.listeners.push(fn);\n};\n\nexport default {\n log,\n logWithStack,\n setLogLevel,\n enableFileLogging,\n initLogging,\n listen\n};\n","/*******************************************************************************\n\nHighcharts Export Server\n\nCopyright (c) 2016-2024, 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 { createRequire } from 'module';\nimport { dirname, join } from 'path';\nimport { fileURLToPath } from 'url';\n\nimport { defaultConfig } from '../lib/schemas/config.js';\nimport { log, logWithStack } from './logger.js';\n\nconst MAX_BACKOFF_ATTEMPTS = 6;\n\n// The highcharts dependency directory path\nexport const __highchartsDir = dirname(\n createRequire(import.meta.url).resolve('highcharts/package.json')\n);\n\nexport const __dirname = fileURLToPath(new URL('../.', import.meta.url));\n\n/**\n * Clears and standardizes text by replacing multiple consecutive whitespace\n * characters with a single space and trimming any leading or trailing\n * whitespace.\n *\n * @param {string} text - The input text to be cleared.\n * @param {RegExp} [rule=/\\s\\s+/g] - The regular expression rule to match\n * multiple consecutive whitespace characters.\n * @param {string} [replacer=' '] - The string used to replace multiple\n * consecutive whitespace characters.\n *\n * @returns {string} - The cleared and standardized text.\n */\nexport const clearText = (text, rule = /\\s\\s+/g, replacer = ' ') =>\n text.replaceAll(rule, replacer).trim();\n\n/**\n * Implements an exponential backoff strategy for retrying a function until\n * a certain number of attempts are reached.\n *\n * @param {Function} fn - The function to be retried.\n * @param {number} [attempt=0] - The current attempt number.\n * @param {...any} args - Arguments to be passed to the function.\n *\n * @returns {Promise} - A promise that resolves to the result of the function\n * if successful.\n *\n * @throws {Error} - Throws an error if the maximum number of attempts\n * is reached.\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 the export type based on MIME types and file extensions.\n *\n * @param {string} type - The original export type.\n * @param {string} outfile - The file path or name.\n *\n * @returns {string} - The corrected export type.\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 if (outType === 'jpg') {\n type = 'jpeg';\n } else 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 and validates resources for export.\n *\n * @param {Object|string} resources - The resources to be handled. Can be either\n * a JSON object, stringified JSON or a path to a JSON file.\n * @param {boolean} allowFileResources - Whether to allow loading resources from\n * files.\n *\n * @returns {Object|undefined} - The handled resources or undefined if no valid\n * resources are found.\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 handledResources = isCorrectJSON(readFileSync(resources, 'utf8'));\n } catch (error) {\n return logWithStack(2, error, `[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 (handledResources && !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 * Validates and parses JSON data. Checks if provided data is or can\n * be a correct JSON. If a primitive is provided, it is stringified and returned.\n *\n * @param {Object|string} data - The JSON data to be validated and parsed.\n * @param {boolean} toString - Whether to return a stringified representation\n * of the parsed JSON.\n *\n * @returns {Object|string|boolean} - The parsed JSON object, stringified JSON,\n * or false if validation fails.\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 {\n return false;\n }\n}\n\n/**\n * Checks if the given item is an object.\n *\n * @param {any} item - The item to be checked.\n *\n * @returns {boolean} - True if the item is an object, false otherwise.\n */\nexport const isObject = (item) =>\n typeof item === 'object' && !Array.isArray(item) && item !== null;\n\n/**\n * Checks if the given object is empty.\n *\n * @param {Object} item - The object to be checked.\n *\n * @returns {boolean} - True if the object is empty, false otherwise.\n */\nexport const isObjectEmpty = (item) =>\n typeof item === 'object' &&\n !Array.isArray(item) &&\n item !== null &&\n Object.keys(item).length === 0;\n\n/**\n * Checks if a private IP range URL is found in the given string.\n *\n * @param {string} item - The string to be checked for a private IP range URL.\n *\n * @returns {boolean} - True if a private IP range URL is found, false\n * otherwise.\n */\nexport const isPrivateRangeUrlFound = (item) => {\n const regexPatterns = [\n /xlink:href=\"(?:http:\\/\\/|https:\\/\\/)?localhost\\b/,\n /xlink:href=\"(?:http:\\/\\/|https:\\/\\/)?10\\.\\d{1,3}\\.\\d{1,3}\\.\\d{1,3}\\b/,\n /xlink:href=\"(?:http:\\/\\/|https:\\/\\/)?127\\.\\d{1,3}\\.\\d{1,3}\\.\\d{1,3}\\b/,\n /xlink:href=\"(?:http:\\/\\/|https:\\/\\/)?172\\.(1[6-9]|2[0-9]|3[0-1])\\.\\d{1,3}\\.\\d{1,3}\\b/,\n /xlink:href=\"(?:http:\\/\\/|https:\\/\\/)?192\\.168\\.\\d{1,3}\\.\\d{1,3}\\b/\n ];\n\n return regexPatterns.some((pattern) => pattern.test(item));\n};\n\n/**\n * Creates a deep copy of the given object or array.\n *\n * @param {Object|Array} obj - The object or array to be deeply copied.\n *\n * @returns {Object|Array} - The deep copy of the provided object or array.\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 * Converts the provided options object to a JSON-formatted string with the\n * option to preserve functions.\n *\n * @param {Object} options - The options object to be converted to a string.\n * @param {boolean} allowFunctions - If set to true, functions are preserved\n * in the output.\n *\n * @returns {string} - The JSON-formatted string representing the options.\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 Highcharts Export Server logo and version information.\n *\n * @param {boolean} noLogo - If true, only prints version information without\n * the logo.\n */\nexport const printLogo = (noLogo) => {\n // Get package version either from env or from package.json\n const packageVersion = JSON.parse(\n readFileSync(join(__dirname, 'package.json'))\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`.bold\n );\n};\n\n/**\n * Prints the usage information for CLI arguments. If required, it can list\n * 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 '\\nUsage of CLI arguments:'.bold,\n '\\n------',\n `\\nFor more detailed information, visit the readme at: ${readme.bold.yellow}.`\n );\n\n const cycleCategories = (options) => {\n for (const [name, option] of Object.entries(options)) {\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 a number to the specified precision.\n *\n * @param {number} value - The number to be rounded.\n * @param {number} precision - The number of decimal places to round to.\n *\n * @returns {number} - The rounded number.\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 * Converts a value to a boolean.\n *\n * @param {any} item - The value to be converted to a boolean.\n *\n * @returns {boolean} - The boolean representation of the input value.\n */\nexport const toBoolean = (item) =>\n ['false', 'undefined', 'null', 'NaN', '0', ''].includes(item)\n ? false\n : !!item;\n\n/**\n * Wraps custom code to execute it safely.\n *\n * @param {string} customCode - The custom code to be wrapped.\n * @param {boolean} allowFileResources - Flag to allow loading code from a file.\n *\n * @returns {string|boolean} - The wrapped custom code or false if wrapping\n * fails.\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 elapsed time using the Node.js process.hrtime() method.\n *\n * @returns {function(): number} - A function to calculate the elapsed time\n * in milliseconds.\n */\nexport const measureTime = () => {\n const start = process.hrtime.bigint();\n return () => Number(process.hrtime.bigint() - start) / 1000000;\n};\n\nexport default {\n __highchartsDir,\n __dirname,\n clearText,\n expBackoff,\n fixType,\n handleResources,\n isCorrectJSON,\n isObject,\n isObjectEmpty,\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-2024, 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 {\n absoluteProps,\n defaultConfig,\n nestedArgs,\n promptsConfig\n} from './schemas/config.js';\nimport { envs } from './envs.js';\nimport { log, logWithStack } from './logger.js';\nimport { deepCopy, isObject, printUsage, toBoolean } from './utils.js';\n\nlet generalOptions = {};\n\n/**\n * Retrieves and returns the general options for the export process.\n *\n * @returns {Object} The general options object.\n */\nexport const getOptions = () => generalOptions;\n\n/**\n * Initializes and sets the general options for the server instace, keeping\n * the principle of the options load priority. It accepts optional userOptions\n * and args from the CLI.\n *\n * @param {Object} userOptions - User-provided options for customization.\n * @param {Array} args - Command-line arguments for additional configuration\n * (CLI usage).\n *\n * @returns {Object} The updated 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 * Allows manual configuration based on specified prompts and saves\n * the configuration to a file.\n *\n * @param {string} configFileName - The name of the configuration file.\n *\n * @returns {Promise} A Promise that resolves to true once the manual\n * configuration is completed and saved.\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 module scripts\n if (prompt.name === 'moduleScripts') {\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 prompt.choices ? prompt.choices[answer] : 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 logWithStack(\n 1,\n error,\n `[config] An error occurred while creating the ${configFileName} file.`\n );\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 old-structured (PhantomJS) options to a new configuration format\n * (Puppeteer).\n *\n * @param {Object} oldOptions - Old-structured options to be mapped.\n *\n * @returns {Object} New options structured based on the defined nestedArgs\n * mapping.\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 two sets of configuration options, considering absolute properties.\n *\n * @param {Object} options - Original configuration options.\n * @param {Object} newOptions - New configuration options to be merged.\n * @param {Array} absoluteProps - List of properties that should\n * not be recursively merged.\n *\n * @returns {Object} Merged configuration options.\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 export settings based on provided exportOptions\n * and generalOptions.\n *\n * @param {Object} exportOptions - Options specific to the export process.\n * @param {Object} generalOptions - General configuration options.\n *\n * @returns {Object} Initialized export settings.\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 additional configuration from a specified file using\n * the --loadConfig option.\n *\n * @param {Array} args - Command-line arguments to check for\n * the --loadConfig option.\n *\n * @returns {Object} Additional configuration loaded from the specified file,\n * or an empty object if not found or invalid.\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 logWithStack(\n 2,\n error,\n `[config] Unable to load the configuration from the ${fileName} file.`\n );\n }\n }\n\n // No additional options to return\n return {};\n}\n\n/**\n * Updates the default configuration object with values from a custom object\n * and environment variables.\n *\n * @param {Object} configObj - The default configuration object.\n * @param {Object} customObj - Custom configuration object to override defaults.\n * @param {string} propChain - Property chain for tracking nested properties\n * during recursion.\n */\nfunction updateDefaultConfig(configObj, customObj = {}, propChain = '') {\n Object.keys(configObj).forEach((key) => {\n const entry = configObj[key];\n const customValue = customObj && customObj[key];\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 in envs && envs[entry.envLink] !== undefined) {\n entry.value = envs[entry.envLink];\n }\n }\n });\n}\n\n/**\n * Initializes options object based on provided items, setting values from\n * nested properties recursively.\n *\n * @param {Object} items - Configuration items to be used for initializing\n * options.\n *\n * @returns {Object} Initialized 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 values with corresponding options in the configuration,\n * updating the options object.\n *\n * @param {Object} options - Configuration options object to be updated.\n * @param {Array} args - Command-line arguments containing values for specific\n * options.\n * @param {Object} defaultConfig - Default configuration object for reference.\n *\n * @returns {Object} Updated options object.\n */\nfunction pairArgumentValue(options, args, defaultConfig) {\n let showUsage = false;\n for (let i = 0; i < args.length; i++) {\n const 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 // Get the correct type for CLI args which are passed as strings\n let argumentType;\n propertiesChain.reduce((obj, prop, index) => {\n if (propertiesChain.length - 1 === index) {\n argumentType = obj[prop].type;\n }\n return obj[prop];\n }, defaultConfig);\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 if (argumentType === 'boolean') {\n obj[prop] = toBoolean(args[i]);\n } else if (argumentType === 'number') {\n obj[prop] = +args[i];\n } else if (argumentType.indexOf(']') >= 0) {\n obj[prop] = args[i].split(',');\n } else {\n obj[prop] = args[i];\n }\n } else {\n log(\n 2,\n `[config] Missing value for the '${option}' argument. Using the default value.`\n );\n showUsage = true;\n }\n }\n }\n return obj[prop];\n }, options);\n }\n\n // Display the usage for the reference if needed\n if (showUsage) {\n printUsage(defaultConfig);\n }\n\n return options;\n}\n\n/**\n * Recursively updates properties in an object based on nested names and assigns\n * the final value.\n *\n * @param {Object} objectToUpdate - The object to be updated.\n * @param {Array} nestedNames - Array of nested property names.\n * @param {any} value - The final value to be assigned.\n *\n * @returns {Object} Updated object with assigned values.\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 * 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 * Returns the HTTP or HTTPS protocol module based on the provided URL.\n *\n * @param {string} url - The URL to determine the protocol.\n *\n * @returns {Object} The HTTP or HTTPS protocol module (http or https).\n */\nconst getProtocol = (url) => (url.startsWith('https') ? https : http);\n\n/**\n * Fetches data from the specified URL using either HTTP or HTTPS protocol.\n *\n * @param {string} url - The URL to fetch data from.\n * @param {Object} requestOptions - Options for the HTTP request (optional).\n *\n * @returns {Promise} Promise resolving to the HTTP response object\n * with added 'text' property or rejecting with an error.\n */\nasync function fetch(url, requestOptions = {}) {\n return new Promise((resolve, reject) => {\n const protocol = getProtocol(url);\n\n protocol\n .get(\n url,\n Object.assign(\n {\n headers: {\n 'User-Agent': 'highcharts/export',\n Referer: 'highcharts.export'\n }\n },\n requestOptions || {}\n ),\n (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 )\n .on('error', (error) => {\n reject(error);\n });\n });\n}\n\n/**\n * Sends a POST request to the specified URL with the provided JSON body using\n * either HTTP or HTTPS protocol.\n *\n * @param {string} url - The URL to send the POST request to.\n * @param {Object} body - The JSON body to include in the POST request\n * (optional, default is an empty object).\n * @param {Object} requestOptions - Options for the HTTP request (optional).\n *\n * @returns {Promise} Promise resolving to the HTTP response object with\n * added 'text' property or rejecting with an error.\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","class ExportError extends Error {\n // NOTE: Deliberately takes only a message. Two existing call sites in cache.js\n // pass a number as a second argument, intending a status code, which\n // this constructor has always ignored. Accepting a second parameter here\n // would silently give those a meaning. Use setCode() instead, which is\n // explicit.\n constructor(message) {\n super();\n this.message = message;\n this.stackMessage = message;\n }\n\n setError(error) {\n this.error = error;\n if (error.name) {\n this.name = error.name;\n }\n if (error.statusCode) {\n this.statusCode = error.statusCode;\n }\n // NOTE: Carry a machine readable code up from the wrapped error. Errors are\n // wrapped as they travel up the stack, and without this the reason a\n // request failed would be lost at the first wrap, leaving only the\n // message to tell a capacity problem from a bad request.\n if (error.errorCode && !this.errorCode) {\n this.errorCode = error.errorCode;\n }\n if (error.stack) {\n this.stackMessage = error.message;\n this.stack = error.stack;\n }\n return this;\n }\n\n setCode(errorCode) {\n this.errorCode = errorCode;\n return this;\n }\n}\n\nexport default ExportError;\n","/*******************************************************************************\n\nHighcharts Export Server\n\nCopyright (c) 2016-2024, 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, resolve, sep } from 'path';\n\nimport { HttpsProxyAgent } from 'https-proxy-agent';\n\nimport { getOptions } from './config.js';\nimport { envs } from './envs.js';\nimport { fetch } from './fetch.js';\nimport { log } from './logger.js';\nimport { __dirname, __highchartsDir } from './utils.js';\n\nimport ExportError from './errors/ExportError.js';\n\nconst cache = {\n cdnURL: 'https://code.highcharts.com/',\n activeManifest: {},\n sources: '',\n hcVersion: ''\n};\n\n/**\n * Extracts and caches the Highcharts version from the sources string.\n *\n * @returns {string} The extracted Highcharts version.\n */\nexport const extractVersion = (cache) => {\n return cache.sources\n .substring(0, cache.sources.indexOf('*/'))\n .replace('/*', '')\n .replace('*/', '')\n .replace(/\\n/g, '')\n .trim();\n};\n\n/**\n * Extracts the Highcharts module name based on the scriptPath.\n *\n * @param {string} scriptPath - The path to the module.\n *\n * @returns {string} The name of a module.\n */\nexport const extractModuleName = (scriptPath) => {\n // Normalize slashes, get part after the last '/' and remove .js extension\n return scriptPath.replace(/\\\\/g, '/').split('/').pop().replace(/\\.js$/i, '');\n};\n\n/**\n * Saves the provided configuration and fetched modules to the cache manifest\n * file.\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 *\n * @throws {ExportError} Throws an ExportError if an error occurs while writing\n * the cache manifest.\n */\nexport const 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(3, '[cache] Writing a new manifest.');\n try {\n writeFileSync(\n join(__dirname, config.cachePath, 'manifest.json'),\n JSON.stringify(newManifest),\n 'utf8'\n );\n } catch (error) {\n throw new ExportError('[cache] Error writing the cache manifest.').setError(\n error\n );\n }\n};\n\n/**\n * Fetches a single script and updates the fetchedModules accordingly.\n *\n * @param {string} script - A path to script to get.\n * @param {Object} requestOptions - Additional options for the proxy agent\n * to use for a request.\n * @param {Object} fetchedModules - An object which tracks which Highcharts\n * modules have been fetched.\n * @param {boolean} [useNpm=false] - A flag to indicate if the script should be\n * get from NPM package or fetched from CDN. The default value is `false`.\n * @param {boolean} [shouldThrowError=false] - A flag to indicate if the error\n * should be thrown. This should be used only for the core scripts.\n *\n * @returns {Promise} A Promise resolving to the text representation\n * of the fetched script.\n *\n * @throws {ExportError} Throws an ExportError if there is a problem with\n * fetching the script.\n */\nexport const fetchAndProcessScript = async (\n script,\n requestOptions,\n fetchedModules,\n useNpm = false,\n shouldThrowError = false\n) => {\n let response;\n\n // Add the missing .js to the strings\n if (!script.endsWith('.js')) {\n script = `${script}.js`;\n }\n\n // Whether to use NPM package scripts or fetch it from CDN\n if (useNpm) {\n try {\n // Log fetched script\n log(\n 4,\n `[cache] Fetching script from NPM - ${join('node_modules', 'highcharts', script)}`\n );\n\n // Sanitize and validate path\n const resolvedScriptPath = resolve(__highchartsDir, script);\n if (!resolvedScriptPath.startsWith(resolve(__highchartsDir) + sep)) {\n throw new ExportError(\n `[cache] Invalid script path detected for '${script}'. Directory traversal attempt or bad version input.`,\n 403\n );\n }\n\n // Fetch the script from NPM\n response = readFileSync(resolvedScriptPath, 'utf8');\n\n // If OK, return its text representation\n if (fetchedModules && response) {\n fetchedModules[extractModuleName(script)] = 1;\n }\n return response;\n } catch {\n // Proceed\n }\n } else {\n // Log fetched script\n log(4, `[cache] Fetching script from CDN - ${script}`);\n\n // Fetch the script from CDN\n response = await fetch(script, requestOptions);\n\n // If OK, return its text representation\n if (response.statusCode === 200 && typeof response.text == 'string') {\n if (fetchedModules) {\n fetchedModules[extractModuleName(script)] = 1;\n }\n return response.text;\n }\n }\n\n // Based on the `shouldThrowError` flag, decide how to serve error message\n if (shouldThrowError) {\n throw new ExportError(\n `[cache] Could not fetch the mandatory ${script}. The script might not exist in the requested version.`,\n 404\n );\n } else {\n log(\n 2,\n `[cache] Could not fetch the ${script}. The script might not exist in the requested version.`\n );\n }\n\n return '';\n};\n\n/**\n * Fetches Highcharts scripts and customScripts from the given CDNs.\n *\n * @param {Object} highchartsOptions - Object containing all highcharts options.\n * @param {object} proxyOptions - Options for the proxy agent to use for\n * a request.\n * @param {object} fetchedModules - An object which tracks which Highcharts\n * modules have been fetched.\n *\n * @returns {Promise} The fetched scripts content joined.\n */\nexport const fetchScripts = async (\n highchartsOptions,\n proxyOptions,\n fetchedModules\n) => {\n const version = highchartsOptions.version;\n const hcVersion = version === 'latest' || !version ? '' : `${version}/`;\n const cdnURL = highchartsOptions.cdnURL || cache.cdnURL;\n\n log(\n 3,\n `[cache] Updating cache version to Highcharts: ${hcVersion || 'latest'}.`\n );\n\n // Whether to use NPM or CDN\n const useNpm = highchartsOptions.useNpm;\n\n // Configure proxy if exists\n let proxyAgent;\n const { host, port, username, password } = proxyOptions;\n\n // Try to create a Proxy Agent\n if (host && port) {\n try {\n proxyAgent = new HttpsProxyAgent({\n host,\n port,\n ...(username && password ? { username, password } : {})\n });\n } catch (error) {\n throw new ExportError('[cache] Could not create a Proxy Agent.').setError(\n error\n );\n }\n }\n\n // If exists, add proxy agent to request options\n const requestOptions = proxyAgent\n ? {\n agent: proxyAgent,\n timeout: envs.SERVER_PROXY_TIMEOUT\n }\n : {};\n\n const fetchedScripts = await Promise.all([\n ...highchartsOptions.coreScripts.map((c) =>\n fetchAndProcessScript(\n (useNpm && c) || `${cdnURL}${hcVersion}${c}`,\n requestOptions,\n fetchedModules,\n useNpm,\n true\n )\n ),\n ...highchartsOptions.moduleScripts.map((m) =>\n fetchAndProcessScript(\n (useNpm && join('modules', m)) ||\n (m === 'map'\n ? `${cdnURL}maps/${hcVersion}modules/${m}`\n : `${cdnURL}${hcVersion}modules/${m}`),\n requestOptions,\n fetchedModules,\n useNpm\n )\n ),\n ...highchartsOptions.indicatorScripts.map((i) =>\n fetchAndProcessScript(\n (useNpm && join('indicators', i)) ||\n `${cdnURL}stock/${hcVersion}indicators/${i}`,\n requestOptions,\n fetchedModules,\n useNpm\n )\n ),\n ...highchartsOptions.customScripts.map((c) =>\n fetchAndProcessScript(`${c}`, requestOptions)\n )\n ]);\n\n return fetchedScripts.join(';\\n');\n};\n\n/**\n * Updates the local cache with Highcharts scripts and their versions.\n *\n * @param {Object} highchartsOptions - Object containing all options from\n * the highcharts section.\n * @param {string} sourcePath - The path to the source file in the cache.\n *\n * @returns {Promise} A Promise resolving to an object representing\n * the fetched modules.\n *\n * @throws {ExportError} Throws an ExportError if there is an issue updating\n * the local Highcharts cache.\n */\nexport const updateCache = async (\n highchartsOptions,\n proxyOptions,\n sourcePath\n) => {\n try {\n const fetchedModules = {};\n\n // Get sources\n cache.sources = await fetchScripts(\n highchartsOptions,\n proxyOptions,\n fetchedModules\n );\n\n // Get sources version\n cache.hcVersion = extractVersion(cache);\n\n // Save the fetched modules into caches' source JSON\n writeFileSync(sourcePath, cache.sources);\n\n return fetchedModules;\n } catch (error) {\n throw new ExportError(\n '[cache] Unable to update the local Highcharts cache.'\n ).setError(error);\n }\n};\n\n/**\n * Updates the Highcharts version in the applied configuration and checks\n * the cache for the new version.\n *\n * @param {string} newVersion - The new Highcharts version to be applied.\n *\n * @returns {Promise<(object|boolean)>} A Promise resolving to the updated\n * configuration with the new version, or false if no applied configuration\n * exists.\n */\nexport const updateVersion = async (newVersion) => {\n const options = getOptions();\n if (options?.highcharts) {\n options.highcharts.version = newVersion;\n }\n await checkAndUpdateCache(options);\n};\n\n/**\n * Checks the cache for Highcharts dependencies, updates the cache if needed,\n * and loads the sources.\n *\n * @param {Object} options - Object containing all options.\n *\n * @returns {Promise} A Promise that resolves once the cache is checked\n * and updated.\n *\n * @throws {ExportError} Throws an ExportError if there is an issue updating\n * or reading the cache.\n */\nexport const checkAndUpdateCache = async (options) => {\n const { highcharts, server } = options;\n const cachePath = join(__dirname, highcharts.cachePath);\n\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 // 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) || highcharts.forceFetch) {\n log(3, '[cache] Fetching and caching Highcharts dependencies.');\n fetchedModules = await updateCache(highcharts, server.proxy, 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 { coreScripts, moduleScripts, indicatorScripts } = highcharts;\n const numberOfModules =\n coreScripts.length + moduleScripts.length + indicatorScripts.length;\n\n // Compare the loaded highcharts 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 !== highcharts.version) {\n log(\n 2,\n '[cache] A Highcharts version mismatch in the cache, need to re-fetch.'\n );\n requestUpdate = true;\n } else if (Object.keys(manifest.modules || {}).length !== numberOfModules) {\n log(\n 2,\n '[cache] The cache and the requested modules do not match, need to re-fetch.'\n );\n requestUpdate = true;\n } else {\n // Check each module, if anything is missing refetch everything\n requestUpdate = (moduleScripts || []).some((moduleName) => {\n if (!manifest.modules[moduleName]) {\n log(\n 2,\n `[cache] The ${moduleName} is missing in the cache, need to re-fetch.`\n );\n return true;\n }\n });\n }\n\n if (requestUpdate) {\n fetchedModules = await updateCache(highcharts, server.proxy, 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\n cache.hcVersion = extractVersion(cache);\n }\n }\n\n // Finally, save the new manifest, which is basically our current config\n // in a slightly different format\n await saveConfigToManifest(highcharts, fetchedModules);\n};\n\nexport const getCachePath = () =>\n join(__dirname, getOptions().highcharts.cachePath);\n\nexport const getCache = () => cache;\n\nexport const highcharts = () => cache.sources;\n\nexport const version = () => cache.hcVersion;\n\nexport default {\n checkAndUpdateCache,\n getCachePath,\n updateVersion,\n getCache,\n highcharts,\n version\n};\n","/*******************************************************************************\n\nHighcharts Export Server\n\nCopyright (c) 2016-2024, 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/* eslint-disable no-undef */\n\n/**\n * Setting the animObject. Called when initing the page.\n */\nexport function setupHighcharts() {\n Highcharts.animObject = function () {\n return { duration: 0 };\n };\n}\n\n/**\n * Creates the actual chart.\n *\n * @param {object} chartOptions - The options for the Highcharts chart.\n * @param {object} options - The export options.\n * @param {boolean} displayErrors - A flag indicating whether to display errors.\n */\nexport async function triggerExport(chartOptions, options, displayErrors) {\n // Display errors flag taken from chart options nad debugger module\n window._displayErrors = displayErrors;\n\n // Get required functions\n const { getOptions, merge, setOptions, wrap } = Highcharts;\n\n // Create a separate object for a potential setOptions usages in order to\n // prevent from polluting other exports that can happen on the same page\n Highcharts.setOptionsObj = merge(false, {}, getOptions());\n\n // By default animation is disabled\n const chart = {\n animation: false\n };\n\n // When straight inject, the size is set through CSS only\n if (options.export.strInj) {\n chart.height = chartOptions.chart.height;\n chart.width = chartOptions.chart.width;\n }\n\n // NOTE: Is this used for anything useful?\n window.isRenderComplete = false;\n wrap(Highcharts.Chart.prototype, 'init', function (proceed, userOptions, cb) {\n // Override userOptions with image friendly options\n userOptions = merge(userOptions, {\n exporting: {\n enabled: false\n },\n plotOptions: {\n series: {\n label: {\n enabled: false\n }\n }\n },\n /* Expects tooltip in userOptions when forExport is true.\n https://github.com/highcharts/highcharts/blob/3ad430a353b8056b9e764aa4e5cd6828aa479db2/js/parts/Chart.js#L241\n */\n tooltip: {}\n });\n\n (userOptions.series || []).forEach(function (series) {\n series.animation = false;\n });\n\n // Add flag to know if chart render has been called.\n if (!window.onHighchartsRender) {\n window.onHighchartsRender = Highcharts.addEvent(this, 'render', () => {\n window.isRenderComplete = true;\n });\n }\n\n proceed.apply(this, [userOptions, cb]);\n });\n\n wrap(Highcharts.Series.prototype, 'init', function (proceed, chart, options) {\n proceed.apply(this, [chart, options]);\n });\n\n // Get the user options\n const userOptions = options.export.strInj\n ? new Function(`return ${options.export.strInj}`)()\n : chartOptions;\n\n // Trigger custom code\n if (options.customLogic.customCode) {\n new Function('options', options.customLogic.customCode)(userOptions);\n }\n\n // Merge the globalOptions, themeOptions, options from the wrapped\n // setOptions function and user options to create the final options object\n const finalOptions = merge(\n false,\n JSON.parse(options.export.themeOptions),\n userOptions,\n // Placed it here instead in the init because of the size issues\n { chart }\n );\n\n const finalCallback = options.customLogic.callback\n ? new Function(`return ${options.customLogic.callback}`)()\n : undefined;\n\n // Set the global options if exist\n const globalOptions = JSON.parse(options.export.globalOptions);\n if (globalOptions) {\n setOptions(globalOptions);\n }\n\n let constr = options.export.constr || 'chart';\n constr = typeof Highcharts[constr] !== 'undefined' ? constr : 'chart';\n\n Highcharts[constr]('container', finalOptions, finalCallback);\n\n // Get the current global options\n const defaultOptions = getOptions();\n\n // Clear it just in case (e.g. the setOptions was used in the customCode)\n for (const prop in defaultOptions) {\n if (typeof defaultOptions[prop] !== 'function') {\n delete defaultOptions[prop];\n }\n }\n\n // Set the default options back\n setOptions(Highcharts.setOptionsObj);\n\n // Empty the custom global options object\n Highcharts.setOptionsObj = {};\n}\n","/*******************************************************************************\n\nHighcharts Export Server\n\nCopyright (c) 2016-2024, 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 path from 'path';\n\nimport puppeteer from 'puppeteer';\n\nimport { getCachePath } from './cache.js';\nimport { getOptions } from './config.js';\nimport { setupHighcharts } from './highcharts.js';\nimport { log, logWithStack } from './logger.js';\nimport { __dirname } from './utils.js';\n\nimport ExportError from './errors/ExportError.js';\n\n// Get the template for the page\nconst template = readFileSync(__dirname + '/templates/template.html', 'utf8');\n\nlet browser;\n\n// Incremented every time the browser is lost. Pool workers are stamped with the\n// value current when they were created, which lets the pool tell that a worker\n// belongs to a browser that no longer exists. This is necessary because a page\n// belonging to a dead browser still reports isClosed() === false, so the page\n// itself cannot be asked whether it is usable.\nlet browserGeneration = 0;\n\n// The arguments the browser was last launched with, kept so that it can be\n// relaunched on the same terms after an unexpected disconnect.\nlet lastPuppeteerArgs = [];\n\n// Set while the browser is being deliberately closed, so that the resulting\n// disconnect is not mistaken for a crash.\nlet closingOnPurpose = false;\n\n// Shared promise for an in-flight launch, so that concurrent callers - the pool\n// creating several workers at once, typically - trigger a single launch rather\n// than one each.\nlet launchPromise = null;\n\n/**\n * Returns how long to keep retrying a browser launch before giving up.\n *\n * @returns {number} The retry window in milliseconds.\n */\nfunction getLaunchRetryWindow() {\n const configured = parseInt(getOptions().puppeteer?.launchRetryWindow);\n return isNaN(configured) || configured <= 0 ? 30000 : configured;\n}\n\n/**\n * Retrieves the existing Puppeteer browser instance.\n *\n * @returns {Promise} A Promise resolving to the Puppeteer browser\n * instance.\n *\n * @throws {ExportError} Throws an ExportError if no valid browser has been\n * created.\n */\nexport function get() {\n if (!browser) {\n throw new ExportError('[browser] No valid browser has been created.');\n }\n return browser;\n}\n\n/**\n * Returns the current browser generation. Pool workers are stamped with this\n * value on creation and compared against it on validation, so that workers\n * holding a page from a previous, now dead, browser can be identified and\n * replaced.\n *\n * @returns {number} The current browser generation.\n */\nexport function getGeneration() {\n return browserGeneration;\n}\n\n/**\n * Reports whether a usable browser is currently connected.\n *\n * @returns {boolean} True when a browser exists and is connected.\n */\nexport function isConnected() {\n return !!browser?.connected;\n}\n\n/**\n * Handles the browser disconnecting. Puppeteer emits this when the browser\n * process goes away for any reason, including being killed by an out of memory\n * reaper, which is the case this exists for.\n *\n * The browser reference is cleared so that the guard in create() will actually\n * relaunch it, and the generation is advanced so that every pool worker holding\n * a page from the dead browser fails validation and is replaced.\n */\nfunction handleDisconnect() {\n if (closingOnPurpose) {\n return;\n }\n\n browserGeneration++;\n browser = undefined;\n\n log(\n 1,\n `[browser] The browser disconnected unexpectedly. Invalidating all workers and relaunching on next use (generation ${browserGeneration}).`\n );\n}\n\n/**\n * Reports whether a process is still running, by asking the operating system\n * rather than trusting the child process object.\n *\n * NOTE: exitCode and signalCode are not dependable here. Both are null for a\n * running process, but either can be undefined depending on how the\n * process object was produced, and `undefined !== null` reads as exited -\n * which silently skips both the wait and the kill below, leaving the\n * process alive. Signal 0 performs the permission and existence checks\n * without delivering anything.\n *\n * @param {Object} proc - The child process to check.\n *\n * @returns {boolean} True while the process still exists.\n */\nfunction isAlive(proc) {\n if (!proc?.pid) {\n return false;\n }\n\n try {\n process.kill(proc.pid, 0);\n return true;\n } catch (error) {\n // ESRCH means no such process; EPERM means it exists but is not ours\n return error.code === 'EPERM';\n }\n}\n\n/**\n * Waits for a child process to exit, up to a limit.\n *\n * @param {Object} proc - The child process to wait for.\n * @param {number} timeout - How long to wait, in milliseconds.\n *\n * @returns {Promise} True if the process exited within the limit.\n */\nfunction waitForExit(proc, timeout) {\n return new Promise((resolve) => {\n if (!isAlive(proc)) {\n return resolve(true);\n }\n\n const onExit = () => {\n clearTimeout(timer);\n resolve(true);\n };\n\n const timer = setTimeout(() => {\n proc.removeListener('exit', onExit);\n resolve(false);\n }, timeout);\n\n proc.once('exit', onExit);\n });\n}\n\n/**\n * Closes a browser and makes certain its process has actually gone.\n *\n * @param {Object} instance - The Puppeteer browser instance to end.\n *\n * @returns {Promise} Resolves once the process has exited, or once giving\n * up waiting for it.\n */\nasync function terminate(instance) {\n // Take the process reference before closing, as it is not reachable afterwards\n const proc = instance.process();\n\n try {\n await instance.close();\n } catch (error) {\n logWithStack(2, error, '[browser] Could not cleanly close the browser.');\n }\n\n if (!proc) {\n log(\n 2,\n '[browser] No process handle for the browser, so its exit cannot be confirmed.'\n );\n return;\n }\n\n // NOTE: close() resolving does not mean the process has gone. It can resolve\n // when the connection drops, and the launch options deliberately disable\n // Puppeteer's signal handling, so nothing will clean up on our behalf.\n //\n // A surviving process keeps Chrome's lock on the user data directory,\n // which is shared by every launch here, so leaving one behind stops any\n // later browser from starting at all - and on shutdown it leaks a browser\n // process outright.\n if (await waitForExit(proc, 2000)) {\n return;\n }\n\n log(\n 2,\n '[browser] The browser process is still running after being closed, killing it.'\n );\n\n try {\n proc.kill('SIGKILL');\n } catch (error) {\n logWithStack(2, error, '[browser] Could not kill the browser process.');\n }\n\n if (isAlive(proc)) {\n log(\n 1,\n `[browser] The browser process ${proc.pid} has still not exited after being killed.`\n );\n }\n}\n\n/**\n * Creates a Puppeteer browser instance with the specified arguments.\n *\n * @param {Array} puppeteerArgs - Additional arguments for Puppeteer launch.\n *\n * @returns {Promise} A Promise resolving to the Puppeteer browser\n * instance.\n *\n * @throws {ExportError} Throws an ExportError if max retries to open a browser\n * instance are reached, or if no browser instance is found after retries.\n */\nexport async function create(puppeteerArgs) {\n // Remember the arguments so that an unexpected disconnect can relaunch the\n // browser on the same terms, and clear any deliberate-close state left from a\n // previous cycle\n if (puppeteerArgs !== undefined) {\n lastPuppeteerArgs = puppeteerArgs;\n }\n closingOnPurpose = false;\n\n if (browser?.connected) {\n return browser;\n }\n\n // NOTE: Concurrent callers must share a single launch. The pool creates\n // several workers at once, and after a disconnect each of them finds\n // no browser at the same moment - without this they would launch a\n // browser each.\n if (!launchPromise) {\n launchPromise = launchBrowser(lastPuppeteerArgs).finally(() => {\n launchPromise = null;\n });\n }\n\n return launchPromise;\n}\n\n/**\n * Launches a Puppeteer browser instance, retrying on failure.\n *\n * @param {Array} puppeteerArgs - Additional arguments for Puppeteer launch.\n *\n * @returns {Promise} A Promise resolving to the Puppeteer browser\n * instance.\n *\n * @throws {ExportError} Throws an ExportError if max retries to open a browser\n * instance are reached, or if no browser instance is found after retries.\n */\nasync function launchBrowser(puppeteerArgs) {\n // Get debug and other options\n const { puppeteer: puppeteerOptions, debug, other } = getOptions();\n\n // Get the debug options\n const { enable: enabledDebug, ...debugOptions } = debug;\n\n const launchOptions = {\n headless: other.browserShellMode ? 'shell' : true,\n userDataDir: puppeteerOptions.tempDir || './tmp/',\n args: puppeteerArgs,\n handleSIGINT: false,\n handleSIGTERM: false,\n handleSIGHUP: false,\n waitForInitialPage: false,\n defaultViewport: null,\n ...(enabledDebug && debugOptions)\n };\n\n // NOTE: Retry within a time budget rather than for a fixed number of attempts.\n // The previous 25 attempts four seconds apart meant a browser that could\n // never start took 100 seconds to say so, which is longer than an\n // orchestrator will usually wait before deciding the instance is\n // unhealthy - so it would be replaced while still reporting that it was\n // starting, and the real reason never surfaced.\n //\n // The delay grows and carries jitter, so that a fleet restarting together\n // does not retry in lockstep against whatever they are all contending for.\n const retryWindow = getLaunchRetryWindow();\n const deadline = Date.now() + retryWindow;\n\n let attempt = 0;\n let delay = 250;\n\n for (;;) {\n attempt++;\n\n try {\n log(\n 3,\n `[browser] Attempting to get a browser instance (attempt ${attempt}).`\n );\n browser = await puppeteer.launch(launchOptions);\n break;\n } catch (error) {\n const remaining = deadline - Date.now();\n\n if (remaining <= 0) {\n throw new ExportError(\n `[browser] Could not launch a browser within ${retryWindow}ms, after ${attempt} attempts.`\n ).setError(error);\n }\n\n // Not a full error yet, as Puppeteer sometimes needs a moment to settle\n logWithStack(\n 2,\n error,\n `[browser] Failed to launch a browser instance, retrying (attempt ${attempt}, ${Math.ceil(remaining / 1000)}s of the retry window left).`\n );\n\n await new Promise((resolve) =>\n setTimeout(\n resolve,\n Math.min(delay + Math.round(Math.random() * delay), remaining)\n )\n );\n\n delay = Math.min(delay * 2, 4000);\n }\n }\n\n // Shell mode inform\n if (launchOptions.headless === 'shell') {\n log(3, `[browser] Launched browser in shell mode.`);\n }\n\n // Debug mode inform\n if (enabledDebug) {\n log(3, `[browser] Launched browser in debug mode.`);\n }\n\n if (!browser) {\n throw new ExportError('[browser] Cannot find a browser to open.');\n }\n\n // Notice the browser going away, so that the pool's workers can be\n // invalidated and the browser relaunched, rather than the pool handing out\n // pages belonging to a process that no longer exists\n browser.once('disconnected', handleDisconnect);\n\n return browser;\n}\n\n/**\n * Closes the Puppeteer browser instance if it is connected.\n *\n * @returns {Promise} A Promise resolving to true after the browser\n * is closed.\n */\nexport async function close() {\n // Mark this as intentional so the resulting disconnect is not treated as a\n // crash and does not trigger a relaunch\n closingOnPurpose = true;\n\n // Close the browser and make sure its process has gone, so that shutdown does\n // not leave one behind holding the user data directory\n if (browser) {\n await terminate(browser);\n }\n\n browser = undefined;\n\n log(4, '[browser] Closed the browser.');\n}\n\n/**\n * Creates a new Puppeteer Page within an existing browser instance.\n *\n * If the browser instance is not available, returns false.\n *\n * The function creates a new page, disables caching, sets content using\n * setPageContent(), and returns the created Puppeteer Page.\n *\n * If the browser is not currently available it is relaunched first, so that the\n * pool can recover by itself after the browser process has gone away.\n *\n * @returns {(boolean|object)} Returns false if the browser instance is not\n * available, or a Puppeteer Page object representing the newly created page.\n */\nexport async function newPage() {\n // The browser may have gone away since the last page was made. Bring it back\n // rather than failing every export from here on - create() is guarded, so\n // concurrent callers share the one relaunch.\n if (!browser?.connected && !closingOnPurpose) {\n log(3, '[browser] No browser available, attempting to relaunch it.');\n await create();\n }\n\n if (!browser) {\n return false;\n }\n\n let page;\n\n try {\n // Create a page\n page = await browser.newPage();\n\n // Disable cache\n await page.setCacheEnabled(false);\n\n // Set the content\n await setPageContent(page);\n\n // Set page events\n setPageEvents(page);\n\n return page;\n } catch (error) {\n // NOTE: Without this, a page created above but failing any of the\n // subsequent steps is left open forever - the caller only receives\n // the error and never had a reference to close it. setPageContent is\n // the likely thrower, as it injects the entire Highcharts bundle and\n // so is sensitive to a CPU starved instance. That matters because on\n // a sustained create failure tarn retries every\n // createRetryInterval (200ms by default), which would leak another\n // browser tab on every attempt.\n if (page && !page.isClosed()) {\n try {\n await page.close();\n } catch (closeError) {\n logWithStack(\n 2,\n closeError,\n '[browser] Could not close a page that failed to be set up.'\n );\n }\n }\n\n throw error;\n }\n}\n\n/**\n * Clears the content of a Puppeteer Page based on the specified mode.\n *\n * @param {Object} page - The Puppeteer Page object to be cleared.\n * @param {boolean} hardReset - A flag indicating the type of clearing\n * to be performed. If true, navigates to 'about:blank' and resets content\n * and scripts. If false, clears the body content by setting a predefined HTML\n * structure.\n *\n * @throws {Error} Logs thrown error if clearing the page content fails.\n */\nexport async function clearPage(page, hardReset = false) {\n try {\n if (page && !page.isClosed()) {\n if (hardReset) {\n // Navigate to about:blank\n await page.goto('about:blank', { waitUntil: 'domcontentloaded' });\n\n // Set the content and and scripts again\n await setPageContent(page);\n } else {\n // Clear body content\n await page.evaluate(() => {\n document.body.innerHTML =\n '
';\n });\n }\n return true;\n }\n } catch (error) {\n logWithStack(\n 2,\n error,\n '[browser] Could not clear the content of the page.'\n );\n }\n\n return false;\n}\n\n/**\n * Adds custom JS and CSS resources to a Puppeteer Page based on the specified\n * options.\n *\n * @param {Object} page - The Puppeteer Page object to which resources will be\n * added.\n * @param {Object} options - All options and configuration.\n *\n * @returns {Promise>} - Promise resolving to an array of injected\n * resources.\n */\nexport async function addPageResources(page, options) {\n // Injected resources array\n const injectedResources = [];\n\n // Use resources\n const resources = options.customLogic.resources;\n if (resources) {\n const injectedJs = [];\n\n // Load custom JS code\n if (resources.js) {\n injectedJs.push({\n content: resources.js\n });\n }\n\n // Load scripts from all custom files\n if (resources.files) {\n for (const file of resources.files) {\n const isLocal = !file.startsWith('http') ? true : false;\n\n // Add each custom script from resources' files\n injectedJs.push(\n isLocal\n ? {\n content: readFileSync(file, 'utf8')\n }\n : {\n url: file\n }\n );\n }\n }\n\n for (const jsResource of injectedJs) {\n try {\n injectedResources.push(await page.addScriptTag(jsResource));\n } catch (error) {\n logWithStack(2, error, `[export] The JS resource cannot be loaded.`);\n }\n }\n injectedJs.length = 0;\n\n // Load CSS\n const injectedCss = [];\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 injectedCss.push({\n url: cssImportPath\n });\n } else if (options.customLogic.allowFileResources) {\n injectedCss.push({\n path: path.join(__dirname, cssImportPath)\n });\n }\n }\n }\n }\n\n // The rest of the CSS section will be content by now\n injectedCss.push({\n content: resources.css.replace(/@import\\s*([^;]*);/g, '') || ' '\n });\n\n for (const cssResource of injectedCss) {\n try {\n injectedResources.push(await page.addStyleTag(cssResource));\n } catch (error) {\n logWithStack(2, error, `[export] The CSS resource cannot be loaded.`);\n }\n }\n injectedCss.length = 0;\n }\n }\n return injectedResources;\n}\n\n/**\n * Clears out all state set on the page with addScriptTag/addStyleTag. Removes\n * injected resources and resets CSS and script tags on the page. Additionally,\n * it destroys previously existing charts.\n *\n * @param {Object} page - The Puppeteer Page object from which resources will\n * be cleared.\n * @param {Array} injectedResources - Array of injected resources\n * to be cleared.\n */\nexport async function clearPageResources(page, injectedResources) {\n try {\n for (const resource of injectedResources) {\n await resource.dispose();\n }\n\n // Destroy old charts after export is done and reset all CSS and script tags\n await page.evaluate(() => {\n // We are not guaranteed that Highcharts is loaded, e,g, when doing SVG\n // exports\n if (typeof Highcharts !== 'undefined') {\n // eslint-disable-next-line no-undef\n const oldCharts = Highcharts.charts;\n\n // Check in any already existing charts\n if (Array.isArray(oldCharts) && 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 const [...scriptsToRemove] = document.getElementsByTagName('script');\n const [, ...stylesToRemove] = document.getElementsByTagName('style');\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 } catch (error) {\n logWithStack(2, error, `[browser] Could not clear page's resources.`);\n }\n}\n\n/**\n * Sets the content for a Puppeteer Page using a predefined template\n * and additional scripts. Also, sets the pageerror in order to catch\n * and display errors from the window context.\n *\n * @param {Object} page - The Puppeteer Page object for which the content\n * is being set.\n */\nasync function setPageContent(page) {\n await page.setContent(template, { waitUntil: 'domcontentloaded' });\n\n // Add all registered Higcharts scripts, quite demanding\n await page.addScriptTag({ path: `${getCachePath()}/sources.js` });\n\n // Set the initial animObject\n await page.evaluate(setupHighcharts);\n}\n\n/**\n * Set events for a Puppeteer Page.\n *\n * @param {Object} page - The Puppeteer Page object to set events to.\n */\nfunction setPageEvents(page) {\n // Get debug options\n const { debug } = getOptions();\n\n // Set the console listener, if needed\n if (debug.enable && debug.listenToConsole) {\n page.on('console', (message) => {\n console.log(`[debug] ${message.text()}`);\n });\n }\n\n // Set the pageerror listener\n page.on('pageerror', async (error) => {\n // It would seem like this may fire at the same time or shortly before\n // a page is closed.\n if (page.isClosed()) {\n return;\n }\n\n // TODO: Consider adding a switch here that turns on log(0) logging\n // on page errors.\n await page.$eval(\n '#container',\n (element, errorMessage) => {\n if (window._displayErrors) {\n element.innerHTML = errorMessage;\n }\n },\n `

Chart input data error:

${error.toString()}`\n );\n });\n}\n\nexport default {\n get,\n getGeneration,\n isConnected,\n create,\n close,\n newPage,\n clearPage,\n addPageResources,\n clearPageResources\n};\n","/*******************************************************************************\n\nHighcharts Export Server\n\nCopyright (c) 2016-2024, 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 { addPageResources, clearPageResources } from './browser.js';\nimport { getCache } from './cache.js';\nimport { triggerExport } from './highcharts.js';\nimport { log } from './logger.js';\n\nimport svgTemplate from './../templates/svg_export/svg_export.js';\n\nimport ExportError from './errors/ExportError.js';\n\n/**\n * Retrieves the clipping region coordinates of the specified page element with\n * the id 'chart-container'.\n *\n * @param {Object} page - Puppeteer page object.\n *\n * @returns {Promise} Promise resolving to an object containing\n * x, y, width, and height properties.\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 * Creates an image using Puppeteer's page screenshot functionality with\n * specified options.\n *\n * @param {Object} page - Puppeteer page object.\n * @param {string} type - Image type.\n * @param {string} encoding - Image encoding.\n * @param {Object} clip - Clipping region coordinates.\n * @param {number} rasterizationTimeout - Timeout for rasterization\n * in milliseconds.\n *\n * @returns {Promise} Promise resolving to the image buffer or rejecting\n * with an ExportError for timeout.\n */\nconst createImage = async (\n page,\n type,\n encoding,\n clip,\n rasterizationTimeout\n) => {\n let timer;\n\n try {\n return await Promise.race([\n page.screenshot({\n type,\n encoding,\n clip,\n captureBeyondViewport: true,\n fullPage: false,\n optimizeForSpeed: true,\n ...(type !== 'png' ? { quality: 80 } : {}),\n\n // #447, #463 - always render on a transparent page if the expected type\n // format is PNG\n omitBackground: type == 'png'\n }),\n new Promise((_resolve, reject) => {\n timer = setTimeout(\n () => reject(new ExportError('Rasterization timeout')),\n rasterizationTimeout || 1500\n );\n })\n ]);\n } finally {\n // The timer outlives the race otherwise, keeping itself and everything its\n // callback closes over alive for the rest of the timeout on every successful\n // export\n clearTimeout(timer);\n }\n};\n\n/**\n * Creates a PDF using Puppeteer's page pdf functionality with specified\n * options.\n *\n * @param {Object} page - Puppeteer page object.\n * @param {number} height - PDF height.\n * @param {number} width - PDF width.\n * @param {string} encoding - PDF encoding.\n *\n * @returns {Promise} Promise resolving to the PDF buffer.\n */\nconst createPDF = async (\n page,\n height,\n width,\n encoding,\n rasterizationTimeout\n) => {\n await page.emulateMediaType('screen');\n\n return page.pdf({\n // This will remove an extra empty page in PDF exports\n height: height + 1,\n width,\n encoding,\n timeout: rasterizationTimeout || 1500\n });\n};\n\n/**\n * Creates an SVG string by evaluating the outerHTML of the first 'svg' element\n * inside an element with the id 'container'.\n *\n * @param {Object} page - Puppeteer page object.\n *\n * @returns {Promise} Promise resolving to the SVG string.\n */\nconst createSVG = (page) =>\n page.$eval('#container svg:first-of-type', (element) => element.outerHTML);\n\n/**\n * Sets the specified chart and options as configuration into the triggerExport\n * function within the window context using page.evaluate.\n *\n * @param {Object} page - Puppeteer page object.\n * @param {any} chart - The chart object to be configured.\n * @param {Object} options - Configuration options for the chart.\n *\n * @returns {Promise} Promise resolving after the configuration is set.\n */\nconst setAsConfig = async (page, chart, options, displayErrors) => {\n // Get rid of the redunant string data\n options.export.instr = null;\n options.export.infile = null;\n\n // Get the size of the export input\n const totalSize = Buffer.byteLength(\n options.export?.strInj ? options.export?.strInj : JSON.stringify(chart),\n 'utf-8'\n );\n\n // Log the size in MB\n log(\n 4,\n `[export] The current total size of data passed to a page is around ${(\n totalSize /\n (1024 * 1024)\n ).toFixed(2)} MB`\n );\n\n // Check the size of data passed to the page\n if (totalSize >= 100 * 1024 * 1024) {\n throw new ExportError(`[export] The data passed to a page exceeded 100MB.`);\n }\n\n // Trigger the Highcharts chart creation\n return page.evaluate(triggerExport, chart, options, displayErrors);\n};\n\n/**\n * Exports to a chart from a page using Puppeteer.\n *\n * @param {Object} page - Puppeteer page object.\n * @param {any} chart - The chart object or SVG configuration to be exported.\n * @param {Object} options - Export options and configuration.\n *\n * @returns {Promise} Promise resolving to\n * the exported data or rejecting with an ExportError.\n */\nexport default async (page, chart, options) => {\n // Injected resources array (additional JS and CSS)\n let injectedResources = [];\n\n try {\n log(4, '[export] Determining export path.');\n\n const exportOptions = options.export;\n\n // Decide whether display error or debbuger wrapper around it\n const displayErrors =\n exportOptions?.options?.chart?.displayErrors &&\n getCache().activeManifest.modules.debugger;\n\n let isSVG;\n if (\n chart.indexOf &&\n (chart.indexOf('= 0 || chart.indexOf('= 0)\n ) {\n // SVG input handling\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 await page.setContent(svgTemplate(chart), {\n waitUntil: 'domcontentloaded'\n });\n } else {\n // JSON config handling\n log(4, '[export] Treating as config.');\n\n // Need to perform straight inject\n if (exportOptions.strInj) {\n // Injection based configuration export\n await setAsConfig(\n page,\n {\n chart: {\n height: exportOptions.height,\n width: exportOptions.width\n }\n },\n options,\n displayErrors\n );\n } else {\n // Basic configuration export\n chart.chart.height = exportOptions.height;\n chart.chart.width = exportOptions.width;\n\n await setAsConfig(page, chart, options, displayErrors);\n }\n }\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 injectedResources = await addPageResources(page, options);\n\n // Get the real chart size and set the zoom accordingly\n const size = isSVG\n ? await page.evaluate((scale) => {\n const svgElement = document.querySelector(\n '#chart-container svg:first-of-type'\n );\n\n // Get the values correctly scaled\n const chartHeight = svgElement.height.baseVal.value * scale;\n const chartWidth = svgElement.width.baseVal.value * scale;\n\n // In case of SVG the zoom must be set directly for body\n // Set the zoom as scale\n document.body.style.zoom = scale;\n\n // Set the margin to 0px\n document.body.style.margin = '0px';\n\n return {\n chartHeight,\n chartWidth\n };\n }, parseFloat(exportOptions.scale))\n : await page.evaluate(() => {\n const { chartHeight, chartWidth } = window.Highcharts.charts[0];\n\n // No need for such scale manipulation in case of other types of exports\n // Reset the zoom for other exports than to SVGs\n document.body.style.zoom = 1;\n\n return {\n chartHeight,\n chartWidth\n };\n });\n\n // Set final height and width for viewport\n const viewportHeight = Math.abs(\n Math.ceil(size.chartHeight || exportOptions.height)\n );\n const viewportWidth = Math.abs(\n Math.ceil(size.chartWidth || exportOptions.width)\n );\n\n // Get the clip region for the page\n const { x, y } = await getClipRegion(page);\n\n // Set the final viewport now that we have the real height\n await page.setViewport({\n height: viewportHeight,\n width: viewportWidth,\n deviceScaleFactor: isSVG ? 1 : parseFloat(exportOptions.scale)\n });\n\n let data;\n // Rasterization process\n if (exportOptions.type === 'svg') {\n // SVG\n data = await createSVG(page);\n } else if (['png', 'jpeg'].includes(exportOptions.type)) {\n // PNG or JPEG\n data = await createImage(\n page,\n exportOptions.type,\n 'base64',\n {\n width: viewportWidth,\n height: viewportHeight,\n x,\n y\n },\n exportOptions.rasterizationTimeout\n );\n } else if (exportOptions.type === 'pdf') {\n // PDF\n data = await createPDF(\n page,\n viewportHeight,\n viewportWidth,\n 'base64',\n exportOptions.rasterizationTimeout\n );\n } else {\n throw new ExportError(\n `[export] Unsupported output format ${exportOptions.type}.`\n );\n }\n\n // Clear previously injected JS and CSS resources\n await clearPageResources(page, injectedResources);\n return data;\n } catch (error) {\n await clearPageResources(page, injectedResources);\n return error;\n }\n};\n","/*******************************************************************************\n\nHighcharts Export Server\n\nCopyright (c) 2016-2024, 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 Highcharts Export\n \n \n \n
\n ${chart}\n
\n \n\n\n`;\n","/*******************************************************************************\n\nHighcharts Export Server\n\nCopyright (c) 2016-2024, 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/**\n * Machine readable error codes, returned as the `errorCode` property of an\n * error response.\n *\n * These exist because the HTTP status code cannot carry the distinction that\n * matters most in production: a request refused because the server was busy and\n * a request refused because it was malformed are both reported as 400. Without a\n * code, a dashboard cannot separate a capacity problem from callers sending bad\n * data, and a client cannot tell whether retrying is worthwhile.\n *\n * Treat these as a stable contract - callers may branch on them.\n */\nexport const errorCodes = {\n // The request itself was not usable: missing body, no chart data, or content\n // that is not allowed. Retrying without changing the request will not help.\n INVALID_REQUEST: 'EXPORT_INVALID_REQUEST',\n\n // The server was already holding as many queued exports as it is willing to,\n // and refused this one without starting work on it. Retrying later, ideally\n // with backoff, is appropriate.\n QUEUE_FULL: 'EXPORT_QUEUE_FULL',\n\n // No worker became available within the acquire timeout. Same meaning for a\n // caller as QUEUE_FULL, but reached by waiting rather than by being refused\n // up front.\n ACQUIRE_TIMEOUT: 'EXPORT_ACQUIRE_TIMEOUT',\n\n // The chart was too large or complex to render within the allotted time.\n RASTERIZATION_TIMEOUT: 'EXPORT_RASTERIZATION_TIMEOUT',\n\n // The client disconnected before the export could be served, so the work was\n // discarded. Never reaches a caller by definition - it exists so that\n // abandoned work is distinguishable in logs and counters from work that\n // genuinely failed.\n CLIENT_GONE: 'EXPORT_CLIENT_GONE',\n\n // The export failed for a reason that is not one of the above.\n EXPORT_FAILED: 'EXPORT_FAILED'\n};\n\nexport default errorCodes;\n","/*******************************************************************************\n\nHighcharts Export Server\n\nCopyright (c) 2016-2024, 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 { Pool } from 'tarn';\nimport { v4 as uuid } from 'uuid';\n\nimport {\n create as createBrowser,\n close as closeBrowser,\n newPage,\n clearPage,\n getGeneration as getBrowserGeneration\n} from './browser.js';\nimport puppeteerExport from './export.js';\nimport { log, logWithStack } from './logger.js';\nimport { measureTime } from './utils.js';\n\nimport ExportError from './errors/ExportError.js';\nimport { errorCodes } from './errors/codes.js';\n\n// The pool instance\nlet pool = false;\n\n// Pool statistics\nexport const stats = {\n performedExports: 0,\n exportAttempts: 0,\n exportFromSvgAttempts: 0,\n timeSpent: 0,\n droppedExports: 0,\n spentAverage: 0,\n rejectedForCapacity: 0,\n abandonedExports: 0\n};\n\nlet poolConfig = {};\n\n// The resolved maximum number of exports allowed to wait for a worker\nlet queueLimit = 0;\n\n// Worker creations that have failed in a row, reset by any success. Used to stop\n// the pool retrying against a browser that is not going to start working again.\nlet consecutiveCreateFailures = 0;\n\n/**\n * Returns the number of worker creations that have failed in a row.\n *\n * @returns {number} The current consecutive failure count.\n */\nexport const getConsecutiveCreateFailures = () => consecutiveCreateFailures;\n\n/**\n * Resolves the queue limit from the pool configuration.\n *\n * A limit of 0 means derive it from the pool size. Four times maxWorkers keeps\n * the worst case wait at roughly four exports' worth of time, which is a\n * meaningful bound, while leaving enough slack to absorb normal bursts.\n *\n * @param {Object} config - The pool section of the configuration.\n *\n * @returns {number} The resolved queue limit.\n */\nconst resolveQueueLimit = (config) => {\n const configured = parseInt(config.queueLimit);\n const maxWorkers = parseInt(config.maxWorkers);\n\n if (!isNaN(configured) && configured > 0) {\n return configured;\n }\n\n return (isNaN(maxWorkers) ? 8 : maxWorkers) * 4;\n};\n\n/**\n * Returns the number of exports currently allowed to wait for a worker.\n *\n * @returns {number} The active queue limit.\n */\nexport const getQueueLimit = () => queueLimit;\n\n/**\n * Returns how long to wait before answering a request refused because the queue\n * was full.\n *\n * @returns {number} The delay in milliseconds.\n */\nexport const getQueueRejectDelay = () => {\n const delay = parseInt(poolConfig.queueRejectDelay);\n return isNaN(delay) || delay < 0 ? 0 : delay;\n};\n\nconst factory = {\n /**\n * Creates a new worker page for the export pool.\n *\n * @returns {Object} - An object containing the worker ID, a reference to the\n * browser page, and initial work count.\n *\n * @throws {ExportError} - If there's an error during the creation of the new\n * page.\n */\n create: async () => {\n let page = false;\n\n const id = uuid();\n const startDate = new Date().getTime();\n\n try {\n page = await newPage();\n\n if (!page || page.isClosed()) {\n throw new ExportError('The page is invalid or closed.');\n }\n\n consecutiveCreateFailures = 0;\n\n log(\n 3,\n `[pool] Successfully created a worker ${id} - took ${\n new Date().getTime() - startDate\n } ms.`\n );\n } catch (error) {\n ++consecutiveCreateFailures;\n\n throw new ExportError(\n 'Error encountered when creating a new page.'\n ).setError(error);\n }\n\n return {\n id,\n page,\n // Which browser this page belongs to, so that it can be recognised as\n // stale if that browser goes away - see factory.validate\n generation: getBrowserGeneration(),\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 page in the export pool, checking if it has exceeded\n * the work limit.\n *\n * @param {Object} workerHandle - The handle to the worker, containing the\n * worker's ID, a reference to the browser page, and work count.\n *\n * @returns {boolean} - Returns true if the worker is valid and within\n * the work limit; otherwise, returns false.\n */\n validate: async (workerHandle) => {\n // NOTE: In certain cases acquiring throws a TargetCloseError, which may\n // be caused by two things:\n // - The page is closed and attempted to be reused.\n // - Lost contact with the browser\n // What we're seeing in logs is that successive exports typically\n // succeeds, and the server recovers, indicating that it's likely\n // the first case. This is an attempt at allievating the issue by\n // simply not validating the worker if the page is null or closed.\n //\n // The actual result from when this happened, was that a worker would\n // be completely locked, stopping it from being acquired until\n // its work count reached the limit.\n if (!workerHandle.page || workerHandle.page?.isClosed()) {\n return false;\n }\n\n // Set when an export left the page in a state it cannot be reused from - see\n // the rasterization timeout handling in postWork\n if (workerHandle.mustRecycle) {\n log(\n 3,\n `[pool] Worker failed validation: its page was left unusable by a previous export.`\n );\n return false;\n }\n\n // NOTE: A page whose browser has gone away still reports\n // isClosed() === false, so the check above cannot detect it and the\n // pool would keep handing out pages belonging to a dead process,\n // failing every export while continuing to report healthy workers.\n //\n // The generation is the reliable signal: it advances whenever the\n // browser is lost, so any worker created against an earlier one is\n // stale. Returning false here makes tarn destroy the worker and\n // create a replacement, which relaunches the browser via newPage.\n if (workerHandle.generation !== getBrowserGeneration()) {\n log(\n 3,\n `[pool] Worker failed validation: it belongs to a browser that is gone (worker generation ${workerHandle.generation}, current ${getBrowserGeneration()}).`\n );\n return false;\n }\n\n // NOTE: Wait for the page clearing started when this worker was released\n // to complete before handing it out again.\n //\n // Tarn's release() is synchronous: it invokes the 'release' event\n // handlers and then returns the resource to the free list within the\n // same tick, without awaiting anything. Clearing the page in that\n // handler therefore overlaps the next export whenever an acquire is\n // already waiting, which is exactly the case under saturation.\n //\n // In practice the visible damage is currently limited, because\n // export.js already calls clearPageResources at the end of every\n // export, which destroys the old charts and removes the injected\n // tags. The innerHTML reset done here is a second pass. But that\n // makes correctness depend on the ordering of two independent cleanup\n // paths, and an innerHTML reset landing part way through the next\n // render would wipe its container.\n //\n // Tarn does await validate, so this is the point where the clearing\n // is guaranteed to have finished. Doing it here rather than in the\n // handler keeps the work overlapped with the worker's idle time, and\n // lets a page that could not be cleared recycle the worker instead of\n // being exported onto.\n if (workerHandle.cleanPromise) {\n const cleared = await workerHandle.cleanPromise;\n workerHandle.cleanPromise = null;\n\n if (!cleared) {\n log(\n 3,\n `[pool] Worker failed validation: the page could not be cleared after its previous export.`\n );\n return false;\n }\n }\n\n if (\n poolConfig.workLimit &&\n ++workerHandle.workCount > poolConfig.workLimit\n ) {\n log(\n 3,\n `[pool] Worker failed validation: exceeded work limit (limit is ${poolConfig.workLimit}).`\n );\n return false;\n }\n return true;\n },\n\n /**\n * Destroys a worker entry in the export pool, closing its associated page.\n *\n * @param {Object} workerHandle - The handle to the worker, containing\n * the worker's ID and a reference to the browser page.\n */\n destroy: async (workerHandle) => {\n log(3, `[pool] Destroying pool entry ${workerHandle.id}.`);\n\n if (workerHandle.page && !workerHandle.page.isClosed()) {\n await workerHandle.page.close();\n }\n }\n\n // log: (message, level) => log(1, '[tarn] ' + message)\n};\n\n/**\n * Initializes the export pool with the provided configuration, creating\n * a browser instance and setting up worker resources.\n *\n * @param {Object} config - Configuration options for the export pool along\n * with custom puppeteer arguments for the puppeteer.launch function.\n */\nexport const initPool = async (config) => {\n // For the module scope usage\n poolConfig = config && config.pool ? { ...config.pool } : {};\n\n // Work out how deep the queue of waiting exports is allowed to get\n queueLimit = resolveQueueLimit(poolConfig);\n\n // Create a browser instance with the puppeteer arguments\n await createBrowser(config.puppeteerArgs);\n\n log(\n 3,\n `[pool] Initializing pool with workers: min ${poolConfig.minWorkers}, 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 if (parseInt(poolConfig.minWorkers) > parseInt(poolConfig.maxWorkers)) {\n poolConfig.minWorkers = poolConfig.maxWorkers;\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: parseInt(poolConfig.minWorkers),\n max: parseInt(poolConfig.maxWorkers),\n acquireTimeoutMillis: poolConfig.acquireTimeout,\n createTimeoutMillis: poolConfig.createTimeout,\n destroyTimeoutMillis: poolConfig.destroyTimeout,\n idleTimeoutMillis: poolConfig.idleTimeout,\n createRetryIntervalMillis: poolConfig.createRetryInterval,\n reapIntervalMillis: poolConfig.reaperInterval,\n propagateCreateError: false\n });\n\n // Set events\n pool.on('release', (resource) => {\n // Start clearing the page, but deliberately do not await it here - see\n // the note in factory.validate, which is where the result is awaited\n // before the worker can be handed out again. The catch is belt and braces:\n // clearPage resolves false rather than rejecting, and nothing must be\n // able to turn this into an unhandled rejection.\n resource.cleanPromise = clearPage(resource.page, false).catch(\n () => false\n );\n\n log(4, `[pool] Releasing a worker with ID ${resource.id}.`);\n });\n\n pool.on('destroySuccess', (eventId, resource) => {\n log(4, `[pool] Destroyed a worker with ID ${resource.id}.`);\n resource.page = null;\n });\n\n const initialResources = [];\n // Create an initial number of resources\n for (let i = 0; i < poolConfig.minWorkers; i++) {\n try {\n const resource = await pool.acquire().promise;\n initialResources.push(resource);\n } catch (error) {\n logWithStack(2, error, '[pool] Could not create an initial resource.');\n }\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${initialResources.length ? ` with ${initialResources.length} initial resources waiting.` : '.'}`\n );\n } catch (error) {\n throw new ExportError(\n '[pool] Could not create the pool of workers.'\n ).setError(error);\n }\n};\n\n/**\n * Kills all workers in the pool, destroys the pool, and closes the browser\n * instance.\n *\n * @returns {Promise} A promise that resolves after the workers are\n * killed, the pool is destroyed, and the browser is closed.\n */\nexport async function killPool() {\n log(3, '[pool] Killing pool with all workers and closing browser.');\n\n // If still alive, destroy the pool of pages before closing a browser\n if (pool) {\n // Free up not released workers\n for (const worker of pool.used) {\n pool.release(worker.resource);\n }\n\n // Destroy the pool if it is still available\n if (!pool.destroyed) {\n await pool.destroy();\n log(4, '[browser] Destroyed the pool of resources.');\n }\n }\n\n // Close the browser instance\n await closeBrowser();\n}\n\n/**\n * Processes the export work using a worker from the pool. Acquires a worker\n * handle from the pool, performs the export using puppeteer, and releases\n * the worker handle back to the pool.\n *\n * @param {string} chart - The chart data or configuration to be exported.\n * @param {Object} options - Export options and configuration.\n *\n * @returns {Promise} A promise that resolves with the export resultand\n * options.\n *\n * @throws {ExportError} If an error occurs during the export process.\n */\nexport const postWork = async (chart, options) => {\n let workerHandle;\n\n try {\n log(4, '[pool] Work received, starting to process.');\n\n ++stats.exportAttempts;\n if (poolConfig.benchmarking) {\n getPoolInfo();\n }\n\n if (!pool) {\n throw new ExportError('Work received, but pool has not been started.');\n }\n\n // NOTE: Refuse the work rather than queue it when the queue is already at\n // its limit.\n //\n // Throughput does not improve past the pool size - measured, it peaks\n // at roughly the number of workers and then falls - so an unbounded\n // queue cannot buy capacity. What it does buy is latency and memory:\n // every waiting request holds its parsed body until it is served or\n // times out, and a saturated server was observed accepting more than\n // twenty times the work it could complete, queueing thousands of\n // requests and then failing most of them on timeout.\n //\n // Failing here is cheap and immediate, and carries a distinct error\n // code so that a caller and a dashboard can tell this apart from a\n // malformed request.\n if (pool.numPendingAcquires() >= queueLimit) {\n ++stats.rejectedForCapacity;\n\n throw new ExportError(\n (options.payload?.requestId\n ? `For request with ID ${options.payload?.requestId} - `\n : '') +\n `The server is at capacity: ${pool.numPendingAcquires()} exports are already waiting for a worker (limit is ${queueLimit}). Please retry shortly.`\n ).setCode(errorCodes.QUEUE_FULL);\n }\n\n // NOTE: Drop work whose client has already gone, before it takes a queue\n // slot or a worker.\n //\n // Measured on the unbounded queue: 60 clients that each gave up after\n // 150ms left 1367 exports still queued a second after the last client\n // had disconnected, and the pool spent a further five seconds\n // rendering charts nobody would receive. Under a retry storm that\n // compounds - every retry adds work while the abandoned original is\n // still being rendered - so capacity falls with each round.\n const abortSignal = options.payload?.abortSignal;\n\n if (abortSignal?.aborted) {\n ++stats.abandonedExports;\n\n throw new ExportError(\n (options.payload?.requestId\n ? `For request with ID ${options.payload?.requestId} - `\n : '') + 'The client disconnected before a worker was available.'\n ).setCode(errorCodes.CLIENT_GONE);\n }\n\n // Acquire the worker along with the id of resource and work count\n const acquireCounter = measureTime();\n try {\n log(4, '[pool] Acquiring a worker handle.');\n workerHandle = await pool.acquire().promise;\n\n // Check the page acquire time\n if (options.server.benchmarking) {\n log(\n 5,\n options.payload?.requestId\n ? `[benchmark] Request with ID ${options.payload?.requestId} -`\n : '[benchmark]',\n `Acquired a worker handle: ${acquireCounter()}ms.`\n );\n }\n } catch (error) {\n throw new ExportError(\n (options.payload?.requestId\n ? `For request with ID ${options.payload?.requestId} - `\n : '') +\n `Error encountered when acquiring an available entry: ${acquireCounter()}ms.`\n )\n .setCode(errorCodes.ACQUIRE_TIMEOUT)\n .setError(error);\n }\n log(4, '[pool] Acquired a worker handle.');\n\n // The client may have gone while this request was queued. Hand the worker\n // straight back rather than spending it on a result nobody will read.\n if (abortSignal?.aborted) {\n ++stats.abandonedExports;\n pool.release(workerHandle);\n workerHandle = null;\n\n throw new ExportError(\n (options.payload?.requestId\n ? `For request with ID ${options.payload?.requestId} - `\n : '') + 'The client disconnected while waiting for a worker.'\n ).setCode(errorCodes.CLIENT_GONE);\n }\n\n if (!workerHandle.page) {\n throw new ExportError(\n 'Resolved worker page is invalid: the pool setup is wonky.'\n );\n }\n\n // Save the start time\n let workStart = new Date().getTime();\n\n log(4, `[pool] Starting work on pool entry with ID ${workerHandle.id}.`);\n\n // Perform an export on a puppeteer level\n const exportCounter = measureTime();\n const result = await puppeteerExport(workerHandle.page, chart, options);\n\n // Check if it's an error\n if (result instanceof Error) {\n // NOTE: If there's a rasterization timeout, we want need to flush the page.\n // This is because the page may be in a state where it's waiting for\n // the screenshot to finish even though the timeout has occured.\n // Which of course causes a lot of issues with the event system,\n // and page consistency.\n //\n // NOTE: Only page.screenshot will throw this, timeouts for PDF's are\n // handled by the page.pdf function itself.\n //\n // ...yes, this is ugly.\n if (result.message === 'Rasterization timeout') {\n // NOTE: This worker must not serve another export. Its page may still be\n // waiting on the screenshot that timed out, so its state is unknown.\n //\n // Marking it rather than discarding the page reference is the point\n // here. Setting page to null used to be how this was done, but\n // factory.destroy only closes a page it can still see, so nulling\n // the reference meant the page was never closed - and with a process\n // per tab, every timeout leaked a renderer process. Under load that\n // compounds: the leaked processes take CPU from the exports still\n // running, which makes more of them time out.\n workerHandle.workCount = poolConfig.workLimit + 1;\n workerHandle.mustRecycle = true;\n }\n\n if (\n result.name === 'TimeoutError' ||\n result.message === 'Rasterization timeout'\n ) {\n throw new ExportError(\n 'Rasterization timeout: your chart may be too complex or large, and failed to render within the allotted time.'\n )\n .setCode(errorCodes.RASTERIZATION_TIMEOUT)\n .setError(result);\n } else {\n throw new ExportError(\n (options.payload?.requestId\n ? `For request with ID ${options.payload?.requestId} - `\n : '') + `Error encountered during export: ${exportCounter()}ms.`\n )\n .setCode(errorCodes.EXPORT_FAILED)\n .setError(result);\n }\n }\n\n // Check the Puppeteer export time\n if (options.server.benchmarking) {\n log(\n 5,\n options.payload?.requestId\n ? `[benchmark] Request with ID ${options.payload?.requestId} -`\n : '[benchmark]',\n `Exported a chart sucessfully: ${exportCounter()}ms.`\n );\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 stats.timeSpent += exportTime;\n stats.spentAverage = stats.timeSpent / ++stats.performedExports;\n\n log(4, `[pool] Work completed in ${exportTime} ms.`);\n\n // Otherwise return the result\n return {\n result,\n options\n };\n } catch (error) {\n // NOTE: Work discarded because its client left is not a failure of the\n // server, so it is counted separately rather than inflating\n // droppedExports and depressing the success ratio that /health\n // reports. It has its own counter, incremented where it is detected.\n if (error.errorCode !== errorCodes.CLIENT_GONE) {\n ++stats.droppedExports;\n }\n\n if (workerHandle) {\n pool.release(workerHandle);\n }\n\n throw new ExportError(`[pool] In pool.postWork: ${error.message}`).setError(\n error\n );\n }\n};\n\n/**\n * Retrieves the current pool instance.\n *\n * @returns {Object|null} The current pool instance if initialized, or null\n * if the pool has not been created.\n */\nexport const getPool = () => pool;\n\n/**\n * Retrieves pool information in JSON format, including minimum and maximum\n * workers, available workers, workers in use, and pending acquire requests.\n *\n * @returns {Object} Pool information in JSON format.\n */\nexport const getPoolInfoJSON = () => ({\n min: pool.min,\n max: pool.max,\n all: pool.numFree() + pool.numUsed(),\n available: pool.numFree(),\n used: pool.numUsed(),\n pending: pool.numPendingAcquires(),\n queueLimit\n});\n\n/**\n * Logs information about the current state of the pool, including the minimum\n * and maximum workers, available workers, workers in use, and pending acquire\n * requests.\n */\nexport function getPoolInfo() {\n const { min, max, all, available, used, pending } = getPoolInfoJSON();\n\n log(5, `[pool] The minimum number of resources allowed by pool: ${min}.`);\n log(5, `[pool] The maximum number of resources allowed by pool: ${max}.`);\n log(5, `[pool] The number of all created resources: ${all}.`);\n log(5, `[pool] The number of available resources: ${available}.`);\n log(5, `[pool] The number of acquired resources: ${used}.`);\n log(5, `[pool] The number of resources waiting to be acquired: ${pending}.`);\n}\n\nexport default {\n initPool,\n killPool,\n postWork,\n getPool,\n getPoolInfo,\n getPoolInfoJSON,\n getQueueLimit,\n getConsecutiveCreateFailures,\n getStats: () => stats\n};\n","/*******************************************************************************\n\nHighcharts Export Server\n\nCopyright (c) 2016-2024, 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/**\n * @overview Used to sanitize the strings coming from the exporting module\n * to prevent XSS attacks (with the DOMPurify library).\n **/\n\nimport { JSDOM } from 'jsdom';\nimport DOMPurify from 'dompurify';\n\nimport { envs } from './envs.js';\n\n// The purifier, built on first use and then reused.\n//\n// NOTE: Building a DOM and a purifier per call is by far the most expensive part\n// of sanitizing, and this runs on every SVG export. It is also synchronous,\n// so the cost is paid on the event loop and delays every other request in\n// flight, not just this one.\n//\n// Only the instance is shared. The options stay per call, since FORBID_ATTR\n// depends on configuration that can be read at any time, and DOMPurify\n// applies the options it is given on each call.\nlet purifier;\n\n/**\n * Returns the shared purifier, building it if this is the first call.\n *\n * @returns {Object} The DOMPurify instance.\n */\nfunction getPurifier() {\n if (!purifier) {\n purifier = DOMPurify(new JSDOM('').window);\n }\n\n return purifier;\n}\n\n/**\n * Sanitizes a given HTML string by removing tags and any content within them.\n *\n * @param {string} input The HTML string to be sanitized.\n * @returns {string} The sanitized HTML string.\n */\nexport function sanitize(input) {\n const forbidden = [];\n\n if (!envs.OTHER_ALLOW_XLINK) {\n forbidden.push('xlink:href');\n }\n\n return getPurifier().sanitize(input, {\n ADD_TAGS: ['foreignObject'],\n FORBID_ATTR: forbidden,\n HTML_INTEGRATION_POINTS: { foreignobject: true }\n });\n}\n\nexport default sanitize;\n","/*******************************************************************************\n\nHighcharts Export Server\n\nCopyright (c) 2016-2024, 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, writeFileSync } from 'fs';\n\nimport { getOptions, initExportSettings } from './config.js';\nimport { log, logWithStack } from './logger.js';\nimport { killPool, postWork, stats } from './pool.js';\nimport {\n fixType,\n handleResources,\n isCorrectJSON,\n optionsStringify,\n roundNumber,\n toBoolean,\n wrapAround\n} from './utils.js';\nimport { sanitize } from './sanitize.js';\nimport ExportError from './errors/ExportError.js';\n\nlet allowCodeExecution = false;\n\n/**\n * Starts an export process. The `settings` contains final options gathered\n * from all possible sources (config, env, cli, json). The `endCallback` is\n * called when the export is completed, with an error object as the first\n * argument and the second containing the base64 respresentation of a chart.\n *\n * @param {Object} settings - The settings object containing export\n * configuration.\n * @param {function} endCallback - The callback function to be invoked upon\n * finalizing work or upon error occurance of the exporting process.\n *\n * @returns {void} This function does not return a value directly; instead,\n * it communicates results via the endCallback.\n */\nexport const startExport = async (settings, endCallback) => {\n // Starting exporting process message\n log(4, '[chart] Starting the 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 try {\n log(4, '[chart] Attempting to export from a SVG input.');\n\n const result = exportAsString(\n sanitize(options.payload.svg), // #209\n options,\n endCallback\n );\n\n ++stats.exportFromSvgAttempts;\n return result;\n } catch (error) {\n return endCallback(\n new ExportError('[chart] Error loading SVG input.').setError(error)\n );\n }\n }\n\n // Export using options from the file\n if (exportOptions.infile && exportOptions.infile.length) {\n // Try to read the file to get the string representation\n try {\n log(4, '[chart] Attempting to export from an input file.');\n options.export.instr = readFileSync(exportOptions.infile, 'utf8');\n return exportAsString(options.export.instr.trim(), options, endCallback);\n } catch (error) {\n return endCallback(\n new ExportError('[chart] Error loading input file.').setError(error)\n );\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 try {\n log(4, '[chart] Attempting to export from a raw input.');\n\n // Use whichever one is available\n exportOptions.instr = exportOptions.instr || exportOptions.options;\n\n // Perform a direct inject when forced\n if (toBoolean(options.customLogic?.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 } catch (error) {\n return endCallback(\n new ExportError('[chart] Error loading raw input.').setError(error)\n );\n }\n }\n\n // No input specified, pass an error message to the callback\n return endCallback(\n new ExportError(\n `[chart] No valid input specified. Check if at least one of the following parameters is correctly set: 'infile', 'instr', 'options', or 'svg'.`\n )\n );\n};\n\n/**\n * Starts a batch export process for multiple charts based on the information\n * in the batch option. The batch is a string in the following format:\n * \"infile1.json=outfile1.png;infile2.json=outfile2.png;...\"\n *\n * @param {Object} options - The options object containing configuration for\n * a batch export.\n *\n * @returns {Promise} A Promise that resolves once the batch export\n * process is completed.\n *\n * @throws {ExportError} Throws an ExportError if an error occurs during\n * any of the batch export process.\n */\nexport const batchExport = async (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 startExport(\n {\n ...options,\n export: {\n ...options.export,\n infile: pair[0],\n outfile: pair[1]\n }\n },\n (error, info) => {\n // Throw an error\n if (error) {\n throw error;\n }\n\n // Save the base64 from a buffer to a correct image file\n writeFileSync(\n info.options.export.outfile,\n info.options.export.type !== 'svg'\n ? Buffer.from(info.result, 'base64')\n : info.result\n );\n }\n )\n );\n }\n }\n\n try {\n // Await all exports are done\n await Promise.all(batchFunctions);\n\n // Kill pool and close browser after finishing batch export\n await killPool();\n } catch (error) {\n throw new ExportError(\n '[chart] Error encountered during batch export.'\n ).setError(error);\n }\n};\n\n/**\n * Starts a single export process based on the specified options.\n *\n * @param {Object} options - The options object containing configuration for\n * a single export.\n *\n * @returns {Promise} A Promise that resolves once the single export\n * process is completed.\n *\n * @throws {ExportError} Throws an ExportError if an error occurs during\n * the single export process.\n */\nexport const singleExport = async (options) => {\n // Use instr or its alias, options\n options.export.instr = options.export.instr || options.export.options;\n\n // Perform an export\n await startExport(options, async (error, info) => {\n // Exit process when error\n if (error) {\n throw error;\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.result, 'base64') : info.result\n );\n\n // Kill pool and close browser after finishing single export\n await killPool();\n });\n};\n\n/**\n * Determines the size and scale for chart export based on the provided options.\n *\n * @param {Object} options - The options object containing configuration for\n * chart export.\n *\n * @returns {Object} An object containing the calculated height, width,\n * and scale for the chart export.\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 const size = {\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 // Get rid of potential px and %\n for (let [param, value] of Object.entries(size)) {\n size[param] =\n typeof value === 'string' ? +value.replace(/px|%/gi, '') : value;\n }\n return size;\n};\n\n/**\n * Function for finalizing options before export.\n *\n * @param {Object} options - The options object containing configuration for\n * the export process.\n * @param {Object} chartJson - The JSON representation of the chart.\n * @param {Function} endCallback - The callback function to be called upon\n * completion or error.\n * @param {string} svg - The SVG representation of the chart.\n *\n * @returns {Promise} A Promise that resolves once the export process\n * is completed.\n */\nconst doExport = async (options, chartJson, endCallback, svg) => {\n let { export: exportOptions, customLogic: customLogicOptions } = options;\n\n const allowCodeExecutionScoped =\n typeof customLogicOptions.allowCodeExecution === 'boolean'\n ? customLogicOptions.allowCodeExecution\n : allowCodeExecution;\n\n if (!customLogicOptions) {\n customLogicOptions = options.customLogic = {};\n } else if (allowCodeExecutionScoped) {\n if (typeof options.customLogic.resources === 'string') {\n // Process resources\n options.customLogic.resources = handleResources(\n options.customLogic.resources,\n toBoolean(options.customLogic.allowFileResources)\n );\n } else if (!options.customLogic.resources) {\n try {\n const resources = readFileSync('resources.json', 'utf8');\n options.customLogic.resources = handleResources(\n resources,\n toBoolean(options.customLogic.allowFileResources)\n );\n } catch (error) {\n log(2, `[chart] Unable to load the default resources.json file.`);\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 && customLogicOptions) {\n if (\n customLogicOptions.callback ||\n customLogicOptions.resources ||\n customLogicOptions.customCode\n ) {\n // Send back a friendly message saying that the exporter does not support\n // these settings.\n return endCallback(\n new ExportError(\n `[chart] The 'callback', 'resources' and 'customCode' options have been disabled for this server.`\n )\n );\n }\n\n // Reset all additional custom code\n customLogicOptions.callback = false;\n customLogicOptions.resources = false;\n customLogicOptions.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 logWithStack(2, error, `[chart] The '${optionsName}' cannot be loaded.`);\n }\n });\n\n // Prepare the customCode\n if (customLogicOptions.allowCodeExecution) {\n try {\n customLogicOptions.customCode = wrapAround(\n customLogicOptions.customCode,\n customLogicOptions.allowFileResources\n );\n } catch (error) {\n logWithStack(2, error, `[chart] The 'customCode' cannot be loaded.`);\n }\n }\n\n // Get the callback\n if (\n customLogicOptions &&\n customLogicOptions.callback &&\n customLogicOptions.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 (customLogicOptions.allowFileResources) {\n try {\n customLogicOptions.callback = readFileSync(\n customLogicOptions.callback,\n 'utf8'\n );\n } catch (error) {\n customLogicOptions.callback = false;\n logWithStack(2, error, `[chart] The 'callback' cannot be loaded.`);\n }\n } else {\n customLogicOptions.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 try {\n const result = await postWork(\n exportOptions.strInj || chartJson || svg,\n options\n );\n return endCallback(false, result);\n } catch (error) {\n return endCallback(error);\n }\n};\n\n/**\n * Performs a direct inject of options before export. The function attempts\n * to stringify the provided options and removes unnecessary characters,\n * ensuring a clean and formatted input. The resulting string is saved as\n * a \"stright inject\" string in the export options. It then invokes the\n * doExport function with the updated options.\n *\n * IMPORTANT: Dangerous and must be used deliberately by someone who sets up\n * a server (see the --allowCodeExecution option).\n *\n * @param {Object} options - The export options containing the input\n * to be injected.\n * @param {function} endCallback - The callback function to be invoked\n * at the end of the process.\n *\n * @returns {Promise} A Promise that resolves with the result of the export\n * operation or rejects with an error if any issues occur during the process.\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.customLogic?.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 return endCallback(\n new ExportError(\n `[chart] Malformed input detected for ${options.export?.requestId || '?'}. Please make sure that your JSON/JavaScript options are sent using the \"options\" attribute, and that if you're using SVG, it is unescaped.`\n ).setError(error)\n );\n }\n};\n\n/**\n * Exports a string based on the provided options and invokes an end callback.\n *\n * @param {string} stringToExport - The string content to be exported.\n * @param {Object} options - Export options, including customLogic with\n * allowCodeExecution flag.\n * @param {Function} endCallback - Callback function to be invoked at the end\n * of the export process.\n *\n * @returns {any} Result of the export process or an error if encountered.\n */\nconst exportAsString = (stringToExport, options, endCallback) => {\n const { allowCodeExecution } = options.customLogic;\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 endCallback(\n new ExportError(\n '[chart] Only JSON configurations and SVG are allowed for this server. If this is your server, JavaScript custom code can be enabled by starting the server with the --allowCodeExecution flag.'\n ).setError(error)\n );\n }\n }\n};\n\n/**\n * Retrieves and returns the current status of code execution permission.\n *\n * @returns {any} The value of allowCodeExecution.\n */\nexport const getAllowCodeExecution = () => allowCodeExecution;\n\n/**\n * Sets the code execution permission based on the provided boolean value.\n *\n * @param {any} value - The value to be converted and assigned\n * to allowCodeExecution.\n */\nexport const setAllowCodeExecution = (value) => {\n allowCodeExecution = toBoolean(value);\n};\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-2024, 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';\n\n// Array that contains ids of all ongoing intervals\nconst intervalIds = [];\n\n/**\n * Adds id of a setInterval to the intervalIds array.\n *\n * @param {NodeJS.Timeout} id - Id of an interval.\n */\nexport const addInterval = (id) => {\n intervalIds.push(id);\n};\n\n/**\n * Clears all of ongoing intervals by ids gathered in the intervalIds array.\n */\nexport const clearAllIntervals = () => {\n log(4, `[server] Clearing all registered intervals.`);\n for (const id of intervalIds) {\n clearInterval(id);\n }\n};\n\nexport default {\n addInterval,\n clearAllIntervals\n};\n","import { envs } from '../envs.js';\nimport { logWithStack } from '../logger.js';\n\n/**\n * Middleware for logging errors with stack trace and handling error response.\n *\n * @param {Error} error - The error object.\n * @param {Express.Request} req - The Express request object.\n * @param {Express.Response} res - The Express response object.\n * @param {Function} next - The next middleware function.\n */\nconst logErrorMiddleware = (error, req, res, next) => {\n // Display the error with stack in a correct format\n logWithStack(1, error);\n\n // Delete the stack for the environment other than the development\n if (envs.OTHER_NODE_ENV !== 'development') {\n delete error.stack;\n }\n\n // Call the returnErrorMiddleware\n next(error);\n};\n\n/**\n * Middleware for returning error response.\n *\n * @param {Error} error - The error object.\n * @param {Express.Request} req - The Express request object.\n * @param {Express.Response} res - The Express response object.\n * @param {Function} next - The next middleware function.\n */\nconst returnErrorMiddleware = (error, req, res, next) => {\n // NOTE: Once the response has started there is no status left to set, and\n // handing the error onwards would let Express's default handler take\n // over, which answers 500. Ending the response is the only action here\n // that cannot produce one.\n if (res.headersSent) {\n return res.end();\n }\n\n // Gather all requied information for the response\n const { statusCode: stCode, status, message, stack, errorCode } = error;\n let statusCode = stCode || status || 400;\n\n // NOTE: This server must never answer with a 5xx. Treat that as an absolute\n // rule, not a preference.\n //\n // Nothing in this codebase sets a 5xx deliberately, but the status is not\n // always ours: setError copies statusCode up from a wrapped error, and\n // wrapped errors include ones from outbound HTTP calls, which can carry\n // any status a remote gave us. Clamping at the one place every error\n // response passes through makes a 5xx structurally impossible instead of\n // something to be careful about.\n //\n // It is logged loudly because reaching here means an error carried a\n // status it should not have, which is worth knowing about even though the\n // response is safe.\n if (statusCode >= 500 || statusCode < 100) {\n logWithStack(\n 1,\n error,\n `[server] An error carried the out-of-contract status ${statusCode}, answering with 400 instead. This server must never return a 5xx.`\n );\n\n statusCode = 400;\n }\n\n // Set and return response\n //\n // NOTE: The errorCode is only included when the error carries one, so that\n // the response shape is unchanged for errors that do not. It exists\n // because the status code alone cannot distinguish a request the server\n // refused because it was busy from one it refused because it was\n // malformed - both are reported as 400.\n res.status(statusCode).json({\n statusCode,\n message,\n stack,\n ...(errorCode ? { errorCode } : {})\n });\n};\n\nexport default (app) => {\n // Add log error middleware\n app.use(logErrorMiddleware);\n\n // Add set status and return error middleware\n app.use(returnErrorMiddleware);\n};\n","/*******************************************************************************\n\nHighcharts Export Server\n\nCopyright (c) 2016-2024, 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 { log } from '../logger.js';\n\n/**\n * Middleware for enabling rate limiting on the specified Express app.\n *\n * @param {Express} app - The Express app instance.\n * @param {Object} limitConfig - Configuration options for 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 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 // The number of requests each IP may make per window. Named `max` before\n // express-rate-limit v7.\n limit: rateOptions.max,\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 `[rate limiting] Enabled rate limiting with ${rateOptions.max} requests per ${rateOptions.window} minute for each IP, trusting proxy: ${rateOptions.trustProxy}.`\n );\n};\n","import ExportError from './ExportError.js';\n\nclass HttpError extends ExportError {\n constructor(message, status, errorCode = false) {\n super(message);\n this.status = this.statusCode = status;\n\n if (errorCode) {\n this.errorCode = errorCode;\n }\n }\n\n setStatus(status) {\n this.status = status;\n return this;\n }\n}\n\nexport default HttpError;\n","/*******************************************************************************\n\nHighcharts Export Server\n\nCopyright (c) 2016-2024, 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 { updateVersion, version } from '../../cache.js';\nimport { envs } from '../../envs.js';\n\nimport HttpError from '../../errors/HttpError.js';\n\n/**\n * Adds the POST /change_hc_version/:newVersion route that can be utilized to modify\n * the Highcharts version on the server.\n *\n * TODO: Add auth token and connect to API\n */\nexport default (app) =>\n !app\n ? false\n : app.post(\n '/version/change/:newVersion',\n async (request, response, next) => {\n try {\n const adminToken = envs.HIGHCHARTS_ADMIN_TOKEN;\n\n // Check the existence of the token\n if (!adminToken || !adminToken.length) {\n throw new HttpError(\n 'The server is not configured to perform run-time version changes: HIGHCHARTS_ADMIN_TOKEN is not set.',\n 401\n );\n }\n\n // Check if the hc-auth header contain a correct token\n const token = request.get('hc-auth');\n if (!token || token !== adminToken) {\n throw new HttpError(\n 'Invalid or missing token: Set the token in the hc-auth header.',\n 401\n );\n }\n\n // Compare versions\n const newVersion = request.params.newVersion;\n\n // Accept only version strings containing digits, letters, dots, hyphens\n if (newVersion && /^[a-zA-Z0-9.-]+$/.test(newVersion)) {\n try {\n await updateVersion(newVersion);\n } catch (error) {\n throw new HttpError(\n `Version change: ${error.message}`,\n error.statusCode\n ).setError(error);\n }\n\n // Success\n response.status(200).send({\n statusCode: 200,\n version: version(),\n message: `Successfully updated Highcharts to version: ${newVersion}.`\n });\n } else {\n // No version specified\n throw new HttpError('No new version supplied.', 400);\n }\n } catch (error) {\n next(error);\n }\n }\n );\n","/*******************************************************************************\n\nHighcharts Export Server\n\nCopyright (c) 2016-2024, 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 fixType,\n isCorrectJSON,\n isObjectEmpty,\n isPrivateRangeUrlFound,\n optionsStringify,\n measureTime\n} from '../../utils.js';\n\nimport HttpError from '../../errors/HttpError.js';\nimport { errorCodes } from '../../errors/codes.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\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 * Invokes an array of callback functions with specified parameters, allowing\n * customization of request handling.\n *\n * @param {Function[]} callbacks - An array of callback functions\n * to be executed.\n * @param {Express.Request} request - The Express request object.\n * @param {Express.Response} response - The Express response object.\n * @param {Object} data - An object containing parameters like id, uniqueId,\n * type, and body.\n *\n * @returns {boolean} - Returns a boolean indicating the overall result\n * of the callback invocations.\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 the export requests from the client.\n *\n * @param {Express.Request} request - The Express request object.\n * @param {Express.Response} response - The Express response object.\n * @param {Function} next - The next middleware function.\n *\n * @returns {Promise} - A promise that resolves once the export process\n * is complete.\n */\nconst exportHandler = async (request, response, next) => {\n try {\n // Start counting time\n const stopCounter = measureTime();\n\n // Create a unique ID for a request\n const uniqueId = uuid().replace(/-/g, '');\n\n // Get the current server's general options\n const defaultOptions = getOptions();\n\n const body = request.body;\n const id = ++requestsCounter;\n\n let type = fixType(body.type);\n\n // Throw 'Bad Request' if there's no body\n if (!body || isObjectEmpty(body)) {\n throw new HttpError(\n 'The request body is required. Please ensure that your Content-Type header is correct (accepted types are application/json and multipart/form-data).',\n 400,\n errorCodes.INVALID_REQUEST\n );\n }\n\n // All of the below can be used\n let instr = isCorrectJSON(body.infile || body.options || body.data);\n\n // Throw 'Bad Request' if there's no JSON or SVG to export\n if (!instr && !body.svg) {\n log(\n 2,\n `The request with ID ${uniqueId} from ${\n request.headers['x-forwarded-for'] || request.connection.remoteAddress\n } was incorrect:\n Content-Type: ${request.headers['content-type']}. \n Chart constructor: ${body.constr}.\n Dimensions: ${body.width}x${body.height} @ ${body.scale} scale.\n Type: ${type}.\n Is SVG set? ${typeof body.svg !== 'undefined'}.\n B64? ${typeof body.b64 !== 'undefined'}.\n No download? ${typeof body.noDownload !== 'undefined'}.\n\n Payload received: ${JSON.stringify(body.infile || body.options || body.data || body.svg)}\n\n `\n );\n\n throw new HttpError(\n \"No correct chart data found. Ensure that you are using either application/json or multipart/form-data headers. If sending JSON, make sure the chart data is in the 'infile', 'options', or 'data' attribute. If sending SVG, ensure it is in the 'svg' attribute.\",\n 400,\n errorCodes.INVALID_REQUEST\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 // Block the request if one of a callbacks failed\n if (callResponse !== true) {\n return response.send(callResponse);\n }\n\n // NOTE: Notice the client going away, so that work nobody is waiting for can\n // be dropped rather than occupying the queue and then a worker.\n //\n // This listens on the response rather than the socket, and does not\n // require the close to have carried an error. The previous check only\n // set the flag when the socket closed with `hadErrors`, so a clean\n // disconnect - a proxy idle timeout, or a caller cancelling - was not\n // detected at all. writableFinished distinguishes the client leaving\n // early from the normal close after a completed response.\n const abortController = new AbortController();\n\n response.once('close', () => {\n if (!response.writableFinished) {\n abortController.abort();\n\n log(\n 4,\n `[export] The client closed the connection for request ${uniqueId} before it was answered.`\n );\n }\n });\n\n log(4, `[export] Got an incoming HTTP request with ID ${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 customLogic: {\n allowCodeExecution: getAllowCodeExecution(),\n allowFileResources: false,\n resources: isCorrectJSON(body.resources, true),\n callback: body.callback,\n customCode: body.customCode\n }\n };\n\n if (instr) {\n // Stringify JSON with options\n requestOptions.export.instr = optionsStringify(\n instr,\n requestOptions.customLogic.allowCodeExecution\n );\n }\n\n // Merge the request options into default ones\n const options = mergeConfigOptions(defaultOptions, requestOptions);\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 noDownload: body.noDownload || false,\n requestId: uniqueId,\n // Lets the pool drop this export if the client gives up before a worker\n // becomes free\n abortSignal: abortController.signal\n };\n\n // Test xlink:href elements from payload's SVG\n if (body.svg && isPrivateRangeUrlFound(options.payload.svg)) {\n throw new HttpError(\n 'SVG potentially contain at least one forbidden URL in xlink:href element. Please review the SVG content and ensure that all referenced URLs comply with security policies.',\n 400,\n errorCodes.INVALID_REQUEST\n );\n }\n\n // Start the export process\n await startExport(options, (error, info) => {\n // NOTE: Check this before looking at the error. An abandoned export is\n // reported as an error by design, and there is nobody left to answer,\n // so raising it here would only produce a failed write to a closed\n // socket and a misleading error in the log.\n if (abortController.signal.aborted) {\n return log(\n 4,\n `[export] Discarding the result for request ${uniqueId}: the client is gone.`\n );\n }\n\n // After the whole exporting process\n if (defaultOptions.server.benchmarking) {\n log(\n 5,\n `[benchmark] Request with ID ${uniqueId} - After the whole exporting process: ${stopCounter()}ms.`\n );\n }\n\n // If error, log it and send it to the error middleware\n if (error) {\n throw error;\n }\n\n // If data is missing, log the message and send it to the error middleware\n if (!info || !info.result) {\n throw new HttpError(\n `Unexpected return from chart generation. Please check your request data. For the request with ID ${uniqueId}, the result is ${info.result}.`,\n 400,\n errorCodes.EXPORT_FAILED\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.result });\n\n if (info.result) {\n // If only base64 is required, return it\n if (body.b64) {\n // SVG Exception for the Highcharts 11.3.0 version\n if (type === 'pdf' || type == 'svg') {\n return response.send(\n Buffer.from(info.result, 'utf8').toString('base64')\n );\n }\n\n return response.send(info.result);\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.result)\n : response.send(Buffer.from(info.result, 'base64'));\n }\n });\n } catch (error) {\n next(error);\n }\n};\n\nexport default (app) => {\n /**\n * Adds the POST / a route for handling POST requests at the root endpoint.\n */\n app.post('/', exportHandler);\n\n /**\n * Adds the POST /:filename a route for handling POST requests with\n * a specified filename parameter.\n */\n app.post('/:filename', exportHandler);\n};\n","/*******************************************************************************\n\nHighcharts Export Server\n\nCopyright (c) 2016-2024, 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 { join as pather } from 'path';\nimport { log } from '../../logger.js';\n\nimport { version } from '../../cache.js';\nimport { addInterval } from '../../intervals.js';\nimport pool, { getConsecutiveCreateFailures } from '../../pool.js';\nimport { isConnected as browserIsConnected } from '../../browser.js';\nimport { __dirname } from '../../utils.js';\n\nconst pkgFile = JSON.parse(readFileSync(pather(__dirname, 'package.json')));\n\nconst serverStartTime = new Date();\n\nconst successRates = [];\nconst recordInterval = 60 * 1000; // record every minute\nconst windowSize = 30; // 30 minutes\n\n/**\n * Calculates moving average indicator based on the data from the successRates\n * array.\n *\n * @returns {number} - A moving average for success ratio of the server exports.\n */\nfunction calculateMovingAverage() {\n const sum = successRates.reduce((a, b) => a + b, 0);\n return sum / successRates.length;\n}\n\n/**\n * Starts the interval responsible for calculating current success rate ratio\n * and gathers\n *\n * @returns {NodeJS.Timeout} id - Id of an interval.\n */\nexport const startSuccessRate = () =>\n setInterval(() => {\n const successRatio = calculateSuccessRatio(pool.getStats());\n\n successRates.push(successRatio === null ? 1 : successRatio);\n if (successRates.length > windowSize) {\n successRates.shift();\n }\n }, recordInterval);\n\n/**\n * Calculates the ratio of exports that succeeded, as a percentage.\n *\n * Exports abandoned by their client are excluded from the denominator. They are\n * not failures of the server - the caller went away - and counting them would\n * report a healthy server as failing whenever callers time out or cancel, which\n * is exactly when this figure is most likely to be looked at.\n *\n * @param {Object} stats - The pool statistics.\n *\n * @returns {number|null} The success ratio as a percentage, or null when no\n * export has been attempted yet.\n */\nfunction calculateSuccessRatio(stats) {\n const attempts = stats.exportAttempts - stats.abandonedExports;\n\n if (attempts <= 0) {\n return null;\n }\n\n return (stats.performedExports / attempts) * 100;\n}\n\n/**\n * Adds the /health and /success-moving-average routes\n * which output basic stats for the server.\n */\nexport default function addHealthRoutes(app) {\n if (!app) {\n return false;\n }\n\n // Start processing success rate ratio interval and save its id to the array\n // for the graceful clearing on shutdown with injected addInterval funtion\n addInterval(startSuccessRate());\n\n app.get('/health', (_, res) => {\n const stats = pool.getStats();\n const period = successRates.length;\n const movingAverage = calculateMovingAverage();\n\n log(4, '[health.js] GET /health [200] - returning server health.');\n\n res.send({\n status: 'OK',\n bootTime: serverStartTime,\n uptime:\n Math.floor(\n (new Date().getTime() - serverStartTime.getTime()) / 1000 / 60\n ) + ' minutes',\n version: pkgFile.version,\n highchartsVersion: version(),\n averageProcessingTime: stats.spentAverage,\n performedExports: stats.performedExports,\n failedExports: stats.droppedExports,\n abandonedExports: stats.abandonedExports,\n rejectedForCapacity: stats.rejectedForCapacity,\n consecutiveCreateFailures: getConsecutiveCreateFailures(),\n browserConnected: browserIsConnected(),\n exportAttempts: stats.exportAttempts,\n sucessRatio: calculateSuccessRatio(stats),\n // eslint-disable-next-line import/no-named-as-default-member\n pool: pool.getPoolInfoJSON(),\n\n // Moving average\n period,\n movingAverage,\n message:\n isNaN(movingAverage) || !successRates.length\n ? 'Too early to report. No exports made yet. Please check back soon.'\n : `Last ${period} minutes had a success rate of ${movingAverage.toFixed(2)}%.`,\n\n // SVG/JSON attempts\n svgExportAttempts: stats.exportFromSvgAttempts,\n jsonExportAttempts: stats.performedExports - stats.exportFromSvgAttempts\n });\n });\n}\n","/*******************************************************************************\n\nHighcharts Export Server\n\nCopyright (c) 2016-2024, 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 cors from 'cors';\nimport express from 'express';\nimport http from 'http';\nimport https from 'https';\nimport multer from 'multer';\n\nimport errorHandler from './error.js';\nimport rateLimit from './rate_limit.js';\nimport { log, logWithStack } from '../logger.js';\nimport {\n getPool,\n getQueueLimit,\n getQueueRejectDelay,\n stats as poolStats\n} from '../pool.js';\nimport { __dirname } from '../utils.js';\n\nimport { errorCodes } from '../errors/codes.js';\nimport HttpError from '../errors/HttpError.js';\n\nimport vSwitchRoute from './routes/change_hc_version.js';\nimport exportRoutes from './routes/export.js';\nimport healthRoute from './routes/health.js';\nimport uiRoute from './routes/ui.js';\n\nimport ExportError from '../errors/ExportError.js';\n\n// Array of an active servers\nconst activeServers = new Map();\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// Getting a lot of RangeNotSatisfiableError exception.\n// Even though this is a deprecated options, let's try to set it to false.\napp.use((_req, res, next) => {\n res.set('Accept-Ranges', 'none');\n next();\n});\n\n/**\n * Attach error handlers to the server.\n *\n * @param {http.Server} server - The HTTP/HTTPS server instance.\n */\nconst attachServerErrorHandlers = (server) => {\n server.on('clientError', (error, socket) => {\n logWithStack(\n 1,\n error,\n `[server] Client error: ${error.message}, destroying socket.`\n );\n socket.destroy();\n });\n\n server.on('error', (error) => {\n logWithStack(1, error, `[server] Server error: ${error.message}`);\n });\n\n server.on('connection', (socket) => {\n socket.on('error', (error) => {\n logWithStack(1, error, `[server] Socket error: ${error.message}`);\n });\n });\n};\n\n/**\n * Applies the connection timeouts to a server.\n *\n * NOTE: Node's default keepAliveTimeout is 5 seconds, which is shorter than the\n * idle timeout of a typical proxy or load balancer sitting in front of this\n * server - commonly 60. When the shorter side closes an idle connection the\n * other side does not know, so it can send a request into a connection that\n * is already going away, and the caller sees a gateway error that has\n * nothing to do with the request. Keeping ours longer than theirs leaves the\n * closing to them.\n *\n * headersTimeout is kept above keepAliveTimeout deliberately: if it were\n * shorter it would fire while a kept-alive connection was still legitimately\n * idle between requests.\n *\n * @param {http.Server} server - The HTTP/HTTPS server instance.\n * @param {Object} serverConfig - The server configuration object.\n */\nconst applyServerTimeouts = (server, serverConfig) => {\n const keepAlive = parseInt(serverConfig.keepAliveTimeout);\n const keepAliveTimeout =\n isNaN(keepAlive) || keepAlive < 0 ? 65000 : keepAlive;\n\n server.keepAliveTimeout = keepAliveTimeout;\n server.headersTimeout = keepAliveTimeout + 5000;\n\n log(\n 4,\n `[server] Set keepAliveTimeout to ${server.keepAliveTimeout}ms and headersTimeout to ${server.headersTimeout}ms.`\n );\n};\n\n/**\n * Starts an HTTP server based on the provided configuration. The `serverConfig`\n * object contains all server related properties (see the `server` section\n * in the `lib/schemas/config.js` file for a reference).\n *\n * @param {Object} serverConfig - The server configuration object.\n *\n * @throws {ExportError} - Throws an error if the server cannot be configured\n * and started.\n */\nexport const startServer = async (serverConfig) => {\n try {\n // TODO: Read from config/env\n // NOTE:\n // Too big limits lead to timeouts in the export process when the\n // rasterization timeout is set too low.\n const uploadLimitMiB = serverConfig.maxUploadSize || 3;\n const uploadLimitBytes = uploadLimitMiB * 1024 * 1024;\n\n // Enable parsing of form data (files) with Multer package\n const storage = multer.memoryStorage();\n const upload = multer({\n storage,\n limits: {\n fieldSize: uploadLimitBytes\n }\n });\n\n // NOTE: Refuse work before the body is parsed when the queue is already\n // full. Checking only inside the pool would mean a request that is\n // going to be refused anyway has its body - up to maxUploadSize, 3MiB\n // by default - read and held in memory first. With a deep queue that\n // is exactly the memory pressure a saturated server cannot afford,\n // and it is what eventually gets the browser killed.\n app.use((request, response, next) => {\n // Only export requests are worth gating; the admin version route is not\n // pool work and must stay reachable when the server is busy\n if (request.method !== 'POST' || request.path.startsWith('/version/')) {\n return next();\n }\n\n const pool = getPool();\n\n if (pool && pool.numPendingAcquires() >= getQueueLimit()) {\n ++poolStats.rejectedForCapacity;\n\n const error = new HttpError(\n `The server is at capacity: ${pool.numPendingAcquires()} exports are already waiting for a worker (limit is ${getQueueLimit()}). Please retry shortly.`,\n 400,\n errorCodes.QUEUE_FULL\n );\n\n // NOTE: Do not answer immediately by default. Measured: with an instant\n // refusal, clients that retry as soon as they are refused drive\n // the request rate up by orders of magnitude - 150 concurrent\n // clients reached 11000 requests per second - and the server then\n // spends its entire event loop refusing them, starving the exports\n // already in progress. Goodput fell from ~19 exports per second to\n // under 1.\n //\n // The 5 second acquire timeout used to provide this backpressure\n // accidentally, by making every client wait before it could retry.\n // Bounding the queue removes that, so the delay puts it back\n // deliberately, and far more cheaply: no body has been parsed and\n // no worker is held, only a socket and a timer.\n const delay = getQueueRejectDelay();\n\n if (!delay) {\n return next(error);\n }\n\n const onClose = () => clearTimeout(timer);\n\n const timer = setTimeout(() => {\n response.removeListener('close', onClose);\n next(error);\n }, delay);\n\n // Do not keep a timer alive for a client that has already gone\n response.once('close', onClose);\n\n return;\n }\n\n next();\n });\n\n // Enable body parser\n app.use(express.json({ limit: uploadLimitBytes }));\n app.use(express.urlencoded({ extended: true, limit: uploadLimitBytes }));\n\n // Use only non-file multipart form fields\n app.use(upload.none());\n\n // Stop if not enabled\n if (!serverConfig.enable) {\n return false;\n }\n\n // Listen HTTP server\n if (!serverConfig.ssl.force) {\n // Main server instance (HTTP)\n const httpServer = http.createServer(app);\n\n // Attach error handlers and listen to the server\n attachServerErrorHandlers(httpServer);\n applyServerTimeouts(httpServer, serverConfig);\n\n // Listen\n httpServer.listen(serverConfig.port, serverConfig.host);\n\n // Save the reference to HTTP server\n activeServers.set(serverConfig.port, httpServer);\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 2,\n `[server] Unable to load key/certificate from the '${serverConfig.ssl.certPath}' path. Could not run secured layer server.`\n );\n }\n\n if (key && cert) {\n // Main server instance (HTTPS)\n const httpsServer = https.createServer({ key, cert }, app);\n\n // Attach error handlers and listen to the server\n attachServerErrorHandlers(httpsServer);\n applyServerTimeouts(httpsServer, serverConfig);\n\n // Listen\n httpsServer.listen(serverConfig.ssl.port, serverConfig.host);\n\n // Save the reference to HTTPS server\n activeServers.set(serverConfig.ssl.port, httpsServer);\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 // Set up centralized error handler\n errorHandler(app);\n } catch (error) {\n throw new ExportError(\n '[server] Could not configure and start the server.'\n ).setError(error);\n }\n};\n\n/**\n * Closes all servers associated with Express app instance, resolving once the\n * requests already in flight have been served.\n *\n * @returns {Promise} Resolves when every server has closed.\n */\nexport const closeServers = () => {\n log(4, `[server] Closing all servers.`);\n\n return Promise.all(\n [...activeServers].map(\n ([port, server]) =>\n new Promise((resolve) => {\n // close() stops new connections being accepted and calls back once the\n // ones in flight have finished, which is what makes a graceful shutdown\n // possible - the previous version discarded that callback entirely\n server.close(() => {\n activeServers.delete(port);\n log(4, `[server] Closed server on port: ${port}.`);\n resolve();\n });\n\n // NOTE: Without this, close() also waits for connections that are merely\n // idle, which now means up to keepAliveTimeout - 65 seconds. Idle\n // connections have no work worth waiting for, so end them and let\n // close() wait only on requests actually being served.\n server.closeIdleConnections?.();\n })\n )\n );\n};\n\n/**\n * Get all servers associated with Express app instance.\n *\n * @returns {Array} - Servers associated with Express app instance.\n */\nexport const getServers = () => activeServers;\n\n/**\n * Enable rate limiting for the server.\n *\n * @param {Object} limitConfig - Configuration object for rate limiting.\n */\nexport const enableRateLimiting = (limitConfig) => rateLimit(app, limitConfig);\n\n/**\n * Get the Express instance.\n *\n * @returns {Object} - The Express instance.\n */\nexport const getExpress = () => express;\n\n/**\n * Get the Express app instance.\n *\n * @returns {Object} - The Express app instance.\n */\nexport const getApp = () => app;\n\n/**\n * Apply middleware(s) to a specific path.\n *\n * @param {string} path - The path to which the middleware(s) should be applied.\n * @param {...Function} middlewares - The middleware functions to be applied.\n */\nexport const use = (path, ...middlewares) => {\n app.use(path, ...middlewares);\n};\n\n/**\n * Set up a route with GET method and apply middleware(s).\n *\n * @param {string} path - The route path.\n * @param {...Function} middlewares - The middleware functions to be applied.\n */\nexport const get = (path, ...middlewares) => {\n app.get(path, ...middlewares);\n};\n\n/**\n * Set up a route with POST method and apply middleware(s).\n *\n * @param {string} path - The route path.\n * @param {...Function} middlewares - The middleware functions to be applied.\n */\nexport const post = (path, ...middlewares) => {\n app.post(path, ...middlewares);\n};\n\nexport default {\n startServer,\n closeServers,\n getServers,\n enableRateLimiting,\n getExpress,\n getApp,\n use,\n get,\n post\n};\n","/*******************************************************************************\n\nHighcharts Export Server\n\nCopyright (c) 2016-2024, 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/**\n * Adds the GET / route for a UI when enabled on 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 acceptRanges: false\n });\n });\n","/*******************************************************************************\n\nHighcharts Export Server\n\nCopyright (c) 2016-2024, 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 { getOptions } from './config.js';\nimport { clearAllIntervals } from './intervals.js';\nimport { killPool } from './pool.js';\nimport { closeServers } from './server/server.js';\n\n/**\n * Returns how long to let in-flight requests finish during a shutdown.\n *\n * @returns {number} The drain timeout in milliseconds.\n */\nconst getDrainTimeout = () => {\n const configured = parseInt(getOptions()?.other?.shutdownDrainTimeout);\n return isNaN(configured) || configured < 0 ? 30000 : configured;\n};\n\n/**\n * Clean up function to trigger before ending process for the graceful shutdown.\n *\n * @param {number} exitCode - An exit code for the process.exit() function.\n */\nexport const shutdownCleanUp = async (exitCode) => {\n // Stop the background intervals; nothing depends on their timing from here\n clearAllIntervals();\n\n // NOTE: These have to happen in order, not together. Previously all three were\n // started at once and closeServers() was not even awaitable - it is not\n // async and returned undefined, so Promise.allSettled treated it as\n // already done. The process therefore exited as soon as the pool had been\n // destroyed, cutting off every export still being served. That happens on\n // each restart, deploy and scale-in, so the dropped requests were not rare.\n //\n // Closing the servers first stops new work arriving and resolves once the\n // requests in flight have been answered, which is the drain. It is raced\n // against a timeout so that one request which never completes cannot hold\n // the shutdown open indefinitely.\n const drainTimeout = getDrainTimeout();\n\n await Promise.race([\n closeServers(),\n new Promise((resolve) => setTimeout(resolve, drainTimeout))\n ]);\n\n // Only once nothing is being served is it safe to take the workers away\n await killPool();\n\n // Exit process with a correct code\n process.exit(exitCode);\n};\n\nexport default {\n shutdownCleanUp\n};\n","/*******************************************************************************\n\nHighcharts Export Server\n\nCopyright (c) 2016-2024, 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 'colors';\n\nimport { checkAndUpdateCache } from './cache.js';\nimport {\n batchExport,\n setAllowCodeExecution,\n singleExport,\n startExport\n} from './chart.js';\nimport { mapToNewConfig, manualConfig, setOptions } from './config.js';\nimport {\n initLogging,\n log,\n logWithStack,\n setLogLevel,\n enableFileLogging\n} from './logger.js';\nimport { initPool, killPool } from './pool.js';\nimport { shutdownCleanUp } from './resource_release.js';\nimport server, { startServer } from './server/server.js';\nimport { printLogo, printUsage } from './utils.js';\n\n/**\n * Attaches exit listeners to the process, ensuring proper cleanup of resources\n * and termination on exit signals. Handles 'exit', 'SIGINT', 'SIGTERM', and\n * 'uncaughtException' events.\n */\nconst attachProcessExitListeners = () => {\n log(3, '[process] Attaching exit listeners to the process.');\n\n // Handler for the 'exit'\n process.on('exit', (code) => {\n log(4, `Process exited with code ${code}.`);\n });\n\n // Handler for the 'SIGINT'\n process.on('SIGINT', async (name, code) => {\n log(4, `The ${name} event with code: ${code}.`);\n await shutdownCleanUp(0);\n });\n\n // Handler for the 'SIGTERM'\n process.on('SIGTERM', async (name, code) => {\n log(4, `The ${name} event with code: ${code}.`);\n await shutdownCleanUp(0);\n });\n\n // Handler for the 'SIGHUP'\n process.on('SIGHUP', async (name, code) => {\n log(4, `The ${name} event with code: ${code}.`);\n await shutdownCleanUp(0);\n });\n\n // Handler for the 'uncaughtException'\n process.on('uncaughtException', async (error, name) => {\n logWithStack(1, error, `The ${name} error.`);\n await shutdownCleanUp(1);\n });\n};\n\n/**\n * Initializes the export process. Tasks such as configuring logging, checking\n * cache and sources, and initializing the pool of resources happen during\n * this stage. Function that is required to be called before trying to export charts or setting a server. The `options` is an object that contains all options.\n *\n * @param {Object} options - All export options.\n *\n * @returns {Promise} Promise resolving to the updated export options.\n */\nconst initExport = async (options) => {\n // Set the allowCodeExecution per export module scope\n setAllowCodeExecution(\n options.customLogic && options.customLogic.allowCodeExecution\n );\n\n // Init the logging\n initLogging(options.logging);\n\n // Attach process' exit listeners\n if (options.other.listenToProcessExits) {\n attachProcessExitListeners();\n }\n\n // Check if cache needs to be updated\n await checkAndUpdateCache(options);\n\n // Init the pool\n await initPool({\n pool: options.pool || {\n minWorkers: 1,\n maxWorkers: 1\n },\n puppeteerArgs: options.puppeteer.args || []\n });\n\n // Return updated options\n return options;\n};\n\nexport default {\n // Server\n server,\n startServer,\n\n // Exporting\n initExport,\n singleExport,\n batchExport,\n startExport,\n\n // Pool\n initPool,\n killPool,\n\n // Other\n setOptions,\n shutdownCleanUp,\n\n // Logs\n log,\n logWithStack,\n setLogLevel,\n enableFileLogging,\n\n // Utils\n mapToNewConfig,\n manualConfig,\n printLogo,\n printUsage\n};\n"],"names":["scriptsNames","core","modules","indicators","custom","defaultConfig","puppeteer","args","value","type","description","launchRetryWindow","envLink","tempDir","highcharts","version","cdnURL","useNpm","coreScripts","moduleScripts","indicatorScripts","customScripts","forceFetch","cachePath","export","infile","instr","options","outfile","constr","defaultHeight","defaultWidth","defaultScale","height","width","scale","globalOptions","themeOptions","batch","rasterizationTimeout","customLogic","allowCodeExecution","allowFileResources","customCode","callback","resources","loadConfig","legacyName","createConfig","server","maxUploadSize","enable","cliName","host","port","keepAliveTimeout","benchmarking","proxy","username","password","timeout","rateLimiting","maxRequests","window","trustProxy","skipKey","skipToken","ssl","force","certPath","pool","minWorkers","maxWorkers","workLimit","queueLimit","queueRejectDelay","acquireTimeout","createTimeout","destroyTimeout","idleTimeout","createRetryInterval","reaperInterval","logging","level","file","dest","toConsole","toFile","ui","route","other","nodeEnv","listenToProcessExits","noLogo","hardResetPage","shutdownDrainTimeout","browserShellMode","debug","headless","devtools","listenToConsole","dumpio","slowMo","debuggingPort","promptsConfig","name","message","initial","join","separator","instructions","choices","hint","min","max","round","absoluteProps","nestedArgs","createNestedArgs","obj","propChain","Object","keys","forEach","k","includes","entry","substring","undefined","dotenv","config","quiet","v","filterArray","z","string","transform","split","map","trim","filter","length","enum","values","refine","error","issue","input","test","isNaN","parseFloat","envs","object","PUPPETEER_TEMP_DIR","PUPPETEER_LAUNCH_RETRY_WINDOW","HIGHCHARTS_VERSION","HIGHCHARTS_CDN_URL","startsWith","HIGHCHARTS_USE_NPM","HIGHCHARTS_CORE_SCRIPTS","HIGHCHARTS_MODULE_SCRIPTS","HIGHCHARTS_INDICATOR_SCRIPTS","HIGHCHARTS_FORCE_FETCH","HIGHCHARTS_CACHE_PATH","HIGHCHARTS_ADMIN_TOKEN","EXPORT_TYPE","EXPORT_CONSTR","EXPORT_DEFAULT_HEIGHT","EXPORT_DEFAULT_WIDTH","EXPORT_DEFAULT_SCALE","EXPORT_RASTERIZATION_TIMEOUT","CUSTOM_LOGIC_ALLOW_CODE_EXECUTION","CUSTOM_LOGIC_ALLOW_FILE_RESOURCES","SERVER_ENABLE","SERVER_HOST","SERVER_PORT","SERVER_MAX_UPLOAD_SIZE","SERVER_KEEP_ALIVE_TIMEOUT","SERVER_BENCHMARKING","SERVER_PROXY_HOST","SERVER_PROXY_PORT","SERVER_PROXY_USERNAME","SERVER_PROXY_PASSWORD","SERVER_PROXY_TIMEOUT","SERVER_RATE_LIMITING_ENABLE","SERVER_RATE_LIMITING_MAX_REQUESTS","SERVER_RATE_LIMITING_WINDOW","SERVER_RATE_LIMITING_TRUST_PROXY","SERVER_RATE_LIMITING_SKIP_KEY","SERVER_RATE_LIMITING_SKIP_TOKEN","SERVER_SSL_ENABLE","SERVER_SSL_FORCE","SERVER_SSL_PORT","SERVER_SSL_CERT_PATH","POOL_MIN_WORKERS","POOL_MAX_WORKERS","POOL_WORK_LIMIT","POOL_QUEUE_LIMIT","POOL_QUEUE_REJECT_DELAY","POOL_ACQUIRE_TIMEOUT","POOL_CREATE_TIMEOUT","POOL_DESTROY_TIMEOUT","POOL_IDLE_TIMEOUT","POOL_CREATE_RETRY_INTERVAL","POOL_REAPER_INTERVAL","POOL_BENCHMARKING","LOGGING_LEVEL","LOGGING_FILE","LOGGING_DEST","LOGGING_TO_CONSOLE","LOGGING_TO_FILE","UI_ENABLE","UI_ROUTE","OTHER_SHUTDOWN_DRAIN_TIMEOUT","OTHER_NODE_ENV","OTHER_LISTEN_TO_PROCESS_EXITS","OTHER_NO_LOGO","OTHER_HARD_RESET_PAGE","OTHER_BROWSER_SHELL_MODE","OTHER_ALLOW_XLINK","DEBUG_ENABLE","DEBUG_HEADLESS","DEBUG_DEVTOOLS","DEBUG_LISTEN_TO_CONSOLE","DEBUG_DUMPIO","DEBUG_SLOW_MO","DEBUG_DEBUGGING_PORT","partial","parse","process","env","colors","pathCreated","levelsDesc","title","color","listeners","logToFile","texts","prefix","existsSync","mkdirSync","appendFile","concat","console","log","newLevel","Date","toString","fn","apply","logWithStack","customMessage","mainMessage","stackMessage","stack","slice","setLogLevel","enableFileLogging","logDest","logFile","endsWith","__highchartsDir","dirname","createRequire","url","resolve","__dirname","fileURLToPath","URL","fixType","formats","outType","pop","find","t","handleResources","allowedProps","handledResources","correctResources","isCorrectJSON","readFileSync","files","propName","item","data","parsedData","JSON","stringify","deepCopy","copy","Array","isArray","key","prototype","hasOwnProperty","call","optionsStringify","allowFunctions","replaceAll","printUsage","bold","yellow","cycleCategories","option","entries","descName","green","i","blue","category","toUpperCase","red","toBoolean","wrapAround","replace","measureTime","start","hrtime","bigint","Number","generalOptions","getOptions","mergeConfigOptions","newOptions","mergedOptions","updateDefaultConfig","configObj","customObj","customValue","initOptions","items","recursiveProps","objectToUpdate","nestedNames","shift","assign","async","fetch","requestOptions","Promise","reject","protocol","https","http","getProtocol","get","headers","Referer","res","on","chunk","text","ExportError","Error","constructor","super","this","setError","statusCode","errorCode","setCode","cache","activeManifest","sources","hcVersion","extractVersion","indexOf","extractModuleName","scriptPath","fetchAndProcessScript","script","fetchedModules","shouldThrowError","response","resolvedScriptPath","sep","updateCache","highchartsOptions","proxyOptions","sourcePath","proxyAgent","HttpsProxyAgent","agent","all","c","m","fetchScripts","writeFileSync","checkAndUpdateCache","manifestPath","requestUpdate","manifest","moduleMap","numberOfModules","some","moduleName","newManifest","saveConfigToManifest","getCachePath","setupHighcharts","Highcharts","animObject","duration","triggerExport","chartOptions","displayErrors","_displayErrors","merge","setOptions","wrap","setOptionsObj","chart","animation","strInj","isRenderComplete","Chart","proceed","userOptions","cb","exporting","enabled","plotOptions","series","label","tooltip","onHighchartsRender","addEvent","Series","Function","finalOptions","finalCallback","defaultOptions","prop","template","browser","browserGeneration","lastPuppeteerArgs","closingOnPurpose","launchPromise","getGeneration","handleDisconnect","isAlive","proc","pid","kill","code","terminate","instance","close","onExit","clearTimeout","timer","setTimeout","removeListener","once","waitForExit","create","puppeteerArgs","connected","puppeteerOptions","enabledDebug","debugOptions","launchOptions","userDataDir","handleSIGINT","handleSIGTERM","handleSIGHUP","waitForInitialPage","defaultViewport","retryWindow","configured","parseInt","getLaunchRetryWindow","deadline","now","attempt","delay","launch","remaining","Math","ceil","random","launchBrowser","finally","newPage","page","setCacheEnabled","setPageContent","isClosed","$eval","element","errorMessage","innerHTML","setPageEvents","closeError","clearPageResources","injectedResources","resource","dispose","evaluate","oldCharts","charts","oldChart","destroy","scriptsToRemove","document","getElementsByTagName","stylesToRemove","linksToRemove","remove","setContent","waitUntil","addScriptTag","path","setAsConfig","totalSize","Buffer","byteLength","toFixed","puppeteerExport","exportOptions","debugger","isSVG","svgTemplate","injectedJs","js","push","content","isLocal","jsResource","injectedCss","css","cssImports","match","cssImportPath","cssResource","addStyleTag","addPageResources","size","svgElement","querySelector","chartHeight","baseVal","chartWidth","body","style","zoom","margin","viewportHeight","abs","viewportWidth","x","y","getBoundingClientRect","trunc","getClipRegion","setViewport","deviceScaleFactor","outerHTML","createSVG","encoding","clip","race","screenshot","captureBeyondViewport","fullPage","optimizeForSpeed","quality","omitBackground","_resolve","createImage","emulateMediaType","pdf","createPDF","errorCodes","stats","performedExports","exportAttempts","exportFromSvgAttempts","timeSpent","droppedExports","spentAverage","rejectedForCapacity","abandonedExports","poolConfig","consecutiveCreateFailures","getConsecutiveCreateFailures","getQueueLimit","factory","id","uuid","startDate","getTime","generation","getBrowserGeneration","workCount","validate","workerHandle","mustRecycle","cleanPromise","cleared","initPool","resolveQueueLimit","createBrowser","Pool","acquireTimeoutMillis","createTimeoutMillis","destroyTimeoutMillis","idleTimeoutMillis","createRetryIntervalMillis","reapIntervalMillis","propagateCreateError","hardReset","goto","clearPage","catch","eventId","initialResources","acquire","promise","release","killPool","worker","used","destroyed","closeBrowser","postWork","getPoolInfo","numPendingAcquires","payload","requestId","abortSignal","aborted","acquireCounter","workStart","exportCounter","result","exportTime","getPool","getPoolInfoJSON","numFree","numUsed","available","pending","pool$1","purifier","sanitize","forbidden","DOMPurify","JSDOM","ADD_TAGS","FORBID_ATTR","HTML_INTEGRATION_POINTS","foreignobject","startExport","settings","endCallback","svg","initExportSettings","exportAsString","doStraightInject","doExport","findChartSize","precision","multiplier","pow","roundNumber","sourceHeight","sourceWidth","param","chartJson","customLogicOptions","allowCodeExecutionScoped","optionsName","stringToExport","chartJSON","intervalIds","logErrorMiddleware","req","next","returnErrorMiddleware","headersSent","end","stCode","status","json","rateLimit","app","limitConfig","msg","rateOptions","limiter","windowMs","limit","handler","request","format","send","default","skip","query","access_token","use","HttpError","setStatus","vSwitchRoute","post","adminToken","token","newVersion","params","updateVersion","reversedMime","png","jpeg","gif","requestsCounter","beforeRequest","afterRequest","doCallbacks","callbacks","uniqueId","callResponse","exportHandler","stopCounter","connection","remoteAddress","b64","noDownload","abortController","AbortController","writableFinished","abort","toLowerCase","substr","signal","pattern","isPrivateRangeUrlFound","info","from","header","attachment","filename","pkgFile","pather","serverStartTime","successRates","calculateSuccessRatio","attempts","addHealthRoutes","setInterval","successRatio","_","period","movingAverage","reduce","a","b","bootTime","uptime","floor","highchartsVersion","averageProcessingTime","failedExports","browserConnected","sucessRatio","svgExportAttempts","jsonExportAttempts","activeServers","Map","express","disable","cors","_req","set","attachServerErrorHandlers","socket","applyServerTimeouts","serverConfig","keepAlive","headersTimeout","startServer","uploadLimitBytes","storage","multer","memoryStorage","upload","limits","fieldSize","method","poolStats","getQueueRejectDelay","onClose","urlencoded","extended","none","httpServer","createServer","listen","cert","fsPromises","readFile","posix","httpsServer","NaN","static","healthRoute","exportRoutes","_request","sendFile","acceptRanges","uiRoute","errorHandler","closeServers","delete","closeIdleConnections","getServers","enableRateLimiting","getExpress","getApp","middlewares","shutdownCleanUp","exitCode","clearInterval","clearAllIntervals","drainTimeout","getDrainTimeout","exit","index","initExport","loggingOptions","initLogging","singleExport","batchExport","batchFunctions","pair","configIndex","findIndex","arg","fileName","loadConfigFile","showUsage","propertiesChain","argumentType","pairArgumentValue","mapToNewConfig","oldOptions","manualConfig","configFileName","configFile","choice","prompts","onSubmit","p","categories","questionsCounter","allQuestions","section","prompt","answer","module","writeFile","printLogo","packageVersion"],"mappings":"oqBAeO,MAAMA,EAAe,CAC1BC,KAAM,CAAC,aAAc,kBAAmB,iBACxCC,QAAS,CACP,QACA,MACA,QACA,YACA,uBACA,gBAEA,eACA,QACA,OACA,aACA,mBACA,eACA,cACA,UACA,UACA,cACA,WACA,UACA,YACA,cACA,YACA,sBACA,SACA,SACA,WACA,aACA,YACA,eAEA,SACA,eACA,YACA,kBACA,SACA,cACA,mBACA,eACA,kBACA,cACA,eAEA,cACA,WACA,eACA,WACA,SACA,OACA,WACA,YACA,SACA,qBACA,aACA,WACA,WACA,WACA,WACA,eACA,UACA,kBACA,oBACA,aACA,UACA,cACA,YACA,YAEFC,WAAY,CAAC,kBACbC,OAAQ,CACN,wEACA,mGAMSC,EAAgB,CAC3BC,UAAW,CACTC,KAAM,CACJC,MAAO,CACL,mCACA,kBACA,0CACA,2BACA,kCACA,kCACA,wCACA,2CACA,qBACA,4BACA,2CACA,uDACA,6BACA,yBACA,0BACA,+BACA,uBACA,uFACA,yBACA,oCACA,oBACA,0BACA,8CACA,2BACA,0BACA,6BACA,mCACA,wCACA,mCACA,2BACA,kCACA,uBACA,iBACA,yBACA,8BACA,oBACA,2BACA,eACA,6BACA,iBACA,aACA,SAEA,sBAEA,yBACA,oBACA,uBAEFC,KAAM,WACNC,YAAa,yCAEfC,kBAAmB,CACjBH,MAAO,IACPC,KAAM,SACNG,QAAS,gCACTF,YACE,yTAEJG,QAAS,CACPL,MAAO,SACPC,KAAM,SACNG,QAAS,qBACTF,YAAa,0DAGjBI,WAAY,CACVC,QAAS,CACPP,MAAO,SACPC,KAAM,SACNG,QAAS,qBACTF,YAAa,sCAEfM,OAAQ,CACNR,MAAO,+BACPC,KAAM,SACNG,QAAS,qBACTF,YAAa,kDAEfO,OAAQ,CACNT,OAAO,EACPC,KAAM,UACNG,QAAS,qBACTF,YAAa,mDAEfQ,YAAa,CACXV,MAAOR,EAAaC,KACpBQ,KAAM,WACNG,QAAS,0BACTF,YAAa,yCAEfS,cAAe,CACbX,MAAOR,EAAaE,QACpBO,KAAM,WACNG,QAAS,4BACTF,YAAa,uCA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"} \ No newline at end of file diff --git a/eslint.config.js b/eslint.config.js new file mode 100644 index 00000000..a81cab3f --- /dev/null +++ b/eslint.config.js @@ -0,0 +1,67 @@ +import js from '@eslint/js'; +import importPlugin from 'eslint-plugin-import'; +import prettierRecommended from 'eslint-plugin-prettier/recommended'; +import globals from 'globals'; +import { EOL } from 'os'; + +export default [ + // Replaces .eslintignore, which flat config does not read + { + ignores: [ + 'dist/**', + '.cache/**', + 'tmp/**', + 'log/**', + 'tests/**/_results/**' + ] + }, + + js.configs.recommended, + importPlugin.flatConfigs.recommended, + prettierRecommended, + + { + languageOptions: { + ecmaVersion: 'latest', + sourceType: 'module', + globals: { + ...globals.browser, + ...globals.node + } + }, + + settings: { + // NOTE: import/no-unresolved cannot see these. Both declare their entry + // points through a package.json "exports" map with conditional keys, + // and the resolver eslint-plugin-import uses does not implement + // "exports" - Node resolves them without trouble, and they work at + // runtime and under test. + // + // Listing them here keeps the rule doing its job everywhere else, + // rather than turning it off or taking on a resolver that is still + // pre-release. Add to this only for packages verified to resolve + // correctly at runtime. + 'import/core-modules': ['uuid', 'https-proxy-agent'] + }, + + rules: { + 'no-unused-vars': 0, + 'import/no-cycle': 2, + 'prettier/prettier': [ + 'error', + { + endOfLine: EOL === '\r\n' ? 'crlf' : 'lf' + } + ] + } + }, + + { + files: ['**/*.test.js', '**/*.spec.js'], + languageOptions: { + globals: { + ...globals.jest + } + } + } +]; diff --git a/lib/browser.js b/lib/browser.js index f393359a..3fabc252 100644 --- a/lib/browser.js +++ b/lib/browser.js @@ -30,6 +30,36 @@ const template = readFileSync(__dirname + '/templates/template.html', 'utf8'); let browser; +// Incremented every time the browser is lost. Pool workers are stamped with the +// value current when they were created, which lets the pool tell that a worker +// belongs to a browser that no longer exists. This is necessary because a page +// belonging to a dead browser still reports isClosed() === false, so the page +// itself cannot be asked whether it is usable. +let browserGeneration = 0; + +// The arguments the browser was last launched with, kept so that it can be +// relaunched on the same terms after an unexpected disconnect. +let lastPuppeteerArgs = []; + +// Set while the browser is being deliberately closed, so that the resulting +// disconnect is not mistaken for a crash. +let closingOnPurpose = false; + +// Shared promise for an in-flight launch, so that concurrent callers - the pool +// creating several workers at once, typically - trigger a single launch rather +// than one each. +let launchPromise = null; + +/** + * Returns how long to keep retrying a browser launch before giving up. + * + * @returns {number} The retry window in milliseconds. + */ +function getLaunchRetryWindow() { + const configured = parseInt(getOptions().puppeteer?.launchRetryWindow); + return isNaN(configured) || configured <= 0 ? 30000 : configured; +} + /** * Retrieves the existing Puppeteer browser instance. * @@ -46,6 +76,164 @@ export function get() { return browser; } +/** + * Returns the current browser generation. Pool workers are stamped with this + * value on creation and compared against it on validation, so that workers + * holding a page from a previous, now dead, browser can be identified and + * replaced. + * + * @returns {number} The current browser generation. + */ +export function getGeneration() { + return browserGeneration; +} + +/** + * Reports whether a usable browser is currently connected. + * + * @returns {boolean} True when a browser exists and is connected. + */ +export function isConnected() { + return !!browser?.connected; +} + +/** + * Handles the browser disconnecting. Puppeteer emits this when the browser + * process goes away for any reason, including being killed by an out of memory + * reaper, which is the case this exists for. + * + * The browser reference is cleared so that the guard in create() will actually + * relaunch it, and the generation is advanced so that every pool worker holding + * a page from the dead browser fails validation and is replaced. + */ +function handleDisconnect() { + if (closingOnPurpose) { + return; + } + + browserGeneration++; + browser = undefined; + + log( + 1, + `[browser] The browser disconnected unexpectedly. Invalidating all workers and relaunching on next use (generation ${browserGeneration}).` + ); +} + +/** + * Reports whether a process is still running, by asking the operating system + * rather than trusting the child process object. + * + * NOTE: exitCode and signalCode are not dependable here. Both are null for a + * running process, but either can be undefined depending on how the + * process object was produced, and `undefined !== null` reads as exited - + * which silently skips both the wait and the kill below, leaving the + * process alive. Signal 0 performs the permission and existence checks + * without delivering anything. + * + * @param {Object} proc - The child process to check. + * + * @returns {boolean} True while the process still exists. + */ +function isAlive(proc) { + if (!proc?.pid) { + return false; + } + + try { + process.kill(proc.pid, 0); + return true; + } catch (error) { + // ESRCH means no such process; EPERM means it exists but is not ours + return error.code === 'EPERM'; + } +} + +/** + * Waits for a child process to exit, up to a limit. + * + * @param {Object} proc - The child process to wait for. + * @param {number} timeout - How long to wait, in milliseconds. + * + * @returns {Promise} True if the process exited within the limit. + */ +function waitForExit(proc, timeout) { + return new Promise((resolve) => { + if (!isAlive(proc)) { + return resolve(true); + } + + const onExit = () => { + clearTimeout(timer); + resolve(true); + }; + + const timer = setTimeout(() => { + proc.removeListener('exit', onExit); + resolve(false); + }, timeout); + + proc.once('exit', onExit); + }); +} + +/** + * Closes a browser and makes certain its process has actually gone. + * + * @param {Object} instance - The Puppeteer browser instance to end. + * + * @returns {Promise} Resolves once the process has exited, or once giving + * up waiting for it. + */ +async function terminate(instance) { + // Take the process reference before closing, as it is not reachable afterwards + const proc = instance.process(); + + try { + await instance.close(); + } catch (error) { + logWithStack(2, error, '[browser] Could not cleanly close the browser.'); + } + + if (!proc) { + log( + 2, + '[browser] No process handle for the browser, so its exit cannot be confirmed.' + ); + return; + } + + // NOTE: close() resolving does not mean the process has gone. It can resolve + // when the connection drops, and the launch options deliberately disable + // Puppeteer's signal handling, so nothing will clean up on our behalf. + // + // A surviving process keeps Chrome's lock on the user data directory, + // which is shared by every launch here, so leaving one behind stops any + // later browser from starting at all - and on shutdown it leaks a browser + // process outright. + if (await waitForExit(proc, 2000)) { + return; + } + + log( + 2, + '[browser] The browser process is still running after being closed, killing it.' + ); + + try { + proc.kill('SIGKILL'); + } catch (error) { + logWithStack(2, error, '[browser] Could not kill the browser process.'); + } + + if (isAlive(proc)) { + log( + 1, + `[browser] The browser process ${proc.pid} has still not exited after being killed.` + ); + } +} + /** * Creates a Puppeteer browser instance with the specified arguments. * @@ -58,6 +246,43 @@ export function get() { * instance are reached, or if no browser instance is found after retries. */ export async function create(puppeteerArgs) { + // Remember the arguments so that an unexpected disconnect can relaunch the + // browser on the same terms, and clear any deliberate-close state left from a + // previous cycle + if (puppeteerArgs !== undefined) { + lastPuppeteerArgs = puppeteerArgs; + } + closingOnPurpose = false; + + if (browser?.connected) { + return browser; + } + + // NOTE: Concurrent callers must share a single launch. The pool creates + // several workers at once, and after a disconnect each of them finds + // no browser at the same moment - without this they would launch a + // browser each. + if (!launchPromise) { + launchPromise = launchBrowser(lastPuppeteerArgs).finally(() => { + launchPromise = null; + }); + } + + return launchPromise; +} + +/** + * Launches a Puppeteer browser instance, retrying on failure. + * + * @param {Array} puppeteerArgs - Additional arguments for Puppeteer launch. + * + * @returns {Promise} A Promise resolving to the Puppeteer browser + * instance. + * + * @throws {ExportError} Throws an ExportError if max retries to open a browser + * instance are reached, or if no browser instance is found after retries. + */ +async function launchBrowser(puppeteerArgs) { // Get debug and other options const { puppeteer: puppeteerOptions, debug, other } = getOptions(); @@ -76,66 +301,77 @@ export async function create(puppeteerArgs) { ...(enabledDebug && debugOptions) }; - // Create a browser - if (!browser) { - const maxTries = 25; - let tryCount = 0; + // NOTE: Retry within a time budget rather than for a fixed number of attempts. + // The previous 25 attempts four seconds apart meant a browser that could + // never start took 100 seconds to say so, which is longer than an + // orchestrator will usually wait before deciding the instance is + // unhealthy - so it would be replaced while still reporting that it was + // starting, and the real reason never surfaced. + // + // The delay grows and carries jitter, so that a fleet restarting together + // does not retry in lockstep against whatever they are all contending for. + const retryWindow = getLaunchRetryWindow(); + const deadline = Date.now() + retryWindow; - const open = async () => { - try { - log( - 3, - `[browser] Attempting to get a browser instance (try ${++tryCount}).` - ); - browser = await puppeteer.launch(launchOptions); - } catch (error) { - // This isn't a full error yet as puppeteer sometimes takes time to - // initialize properly. - logWithStack( - 2, - error, - `[browser] Failed to launch a browser instance - retrying (attempt ${tryCount}/${maxTries}).` - ); + let attempt = 0; + let delay = 250; - // Retry to launch browser until reaching max attempts - if (tryCount < 25) { - log( - 3, - `[browser] Retry to open a browser (attempt ${tryCount}/${maxTries}).` - ); - await new Promise((response) => setTimeout(response, 4000)); - await open(); - } else { - //... now it's an error, which is caught by the caller - throw error; - } - } - }; + for (;;) { + attempt++; try { - await open(); + log( + 3, + `[browser] Attempting to get a browser instance (attempt ${attempt}).` + ); + browser = await puppeteer.launch(launchOptions); + break; + } catch (error) { + const remaining = deadline - Date.now(); - // Shell mode inform - if (launchOptions.headless === 'shell') { - log(3, `[browser] Launched browser in shell mode.`); + if (remaining <= 0) { + throw new ExportError( + `[browser] Could not launch a browser within ${retryWindow}ms, after ${attempt} attempts.` + ).setError(error); } - // Debug mode inform - if (enabledDebug) { - log(3, `[browser] Launched browser in debug mode.`); - } - } catch (error) { - throw new ExportError( - '[browser] Maximum retries to open a browser instance reached.' - ).setError(error); + // Not a full error yet, as Puppeteer sometimes needs a moment to settle + logWithStack( + 2, + error, + `[browser] Failed to launch a browser instance, retrying (attempt ${attempt}, ${Math.ceil(remaining / 1000)}s of the retry window left).` + ); + + await new Promise((resolve) => + setTimeout( + resolve, + Math.min(delay + Math.round(Math.random() * delay), remaining) + ) + ); + + delay = Math.min(delay * 2, 4000); } + } - if (!browser) { - throw new ExportError('[browser] Cannot find a browser to open.'); - } + // Shell mode inform + if (launchOptions.headless === 'shell') { + log(3, `[browser] Launched browser in shell mode.`); } - // Return a browser promise + // Debug mode inform + if (enabledDebug) { + log(3, `[browser] Launched browser in debug mode.`); + } + + if (!browser) { + throw new ExportError('[browser] Cannot find a browser to open.'); + } + + // Notice the browser going away, so that the pool's workers can be + // invalidated and the browser relaunched, rather than the pool handing out + // pages belonging to a process that no longer exists + browser.once('disconnected', handleDisconnect); + return browser; } @@ -146,10 +382,18 @@ export async function create(puppeteerArgs) { * is closed. */ export async function close() { - // Close the browser when connnected - if (browser?.connected) { - await browser.close(); + // Mark this as intentional so the resulting disconnect is not treated as a + // crash and does not trigger a relaunch + closingOnPurpose = true; + + // Close the browser and make sure its process has gone, so that shutdown does + // not leave one behind holding the user data directory + if (browser) { + await terminate(browser); } + + browser = undefined; + log(4, '[browser] Closed the browser.'); } @@ -161,27 +405,64 @@ export async function close() { * The function creates a new page, disables caching, sets content using * setPageContent(), and returns the created Puppeteer Page. * + * If the browser is not currently available it is relaunched first, so that the + * pool can recover by itself after the browser process has gone away. + * * @returns {(boolean|object)} Returns false if the browser instance is not * available, or a Puppeteer Page object representing the newly created page. */ export async function newPage() { + // The browser may have gone away since the last page was made. Bring it back + // rather than failing every export from here on - create() is guarded, so + // concurrent callers share the one relaunch. + if (!browser?.connected && !closingOnPurpose) { + log(3, '[browser] No browser available, attempting to relaunch it.'); + await create(); + } + if (!browser) { return false; } - // Create a page - const page = await browser.newPage(); + let page; + + try { + // Create a page + page = await browser.newPage(); + + // Disable cache + await page.setCacheEnabled(false); - // Disable cache - await page.setCacheEnabled(false); + // Set the content + await setPageContent(page); - // Set the content - await setPageContent(page); + // Set page events + setPageEvents(page); - // Set page events - setPageEvents(page); + return page; + } catch (error) { + // NOTE: Without this, a page created above but failing any of the + // subsequent steps is left open forever - the caller only receives + // the error and never had a reference to close it. setPageContent is + // the likely thrower, as it injects the entire Highcharts bundle and + // so is sensitive to a CPU starved instance. That matters because on + // a sustained create failure tarn retries every + // createRetryInterval (200ms by default), which would leak another + // browser tab on every attempt. + if (page && !page.isClosed()) { + try { + await page.close(); + } catch (closeError) { + logWithStack( + 2, + closeError, + '[browser] Could not close a page that failed to be set up.' + ); + } + } - return page; + throw error; + } } /** @@ -362,11 +643,8 @@ export async function clearPageResources(page, injectedResources) { } } - // eslint-disable-next-line no-undef const [...scriptsToRemove] = document.getElementsByTagName('script'); - // eslint-disable-next-line no-undef const [, ...stylesToRemove] = document.getElementsByTagName('style'); - // eslint-disable-next-line no-undef const [...linksToRemove] = document.getElementsByTagName('link'); // Remove tags @@ -430,7 +708,6 @@ function setPageEvents(page) { await page.$eval( '#container', (element, errorMessage) => { - // eslint-disable-next-line no-undef if (window._displayErrors) { element.innerHTML = errorMessage; } @@ -442,6 +719,8 @@ function setPageEvents(page) { export default { get, + getGeneration, + isConnected, create, close, newPage, diff --git a/lib/envs.js b/lib/envs.js index ed8c3d23..b4d86eb5 100644 --- a/lib/envs.js +++ b/lib/envs.js @@ -15,8 +15,12 @@ import { z } from 'zod'; import { scriptsNames } from './schemas/config.js'; -// Load .env into environment variables -dotenv.config(); +// Load .env into environment variables. +// +// quiet: true suppresses the summary dotenv prints from v17 onwards. This process +// writes structured log lines and its output is read by other tooling, so an +// unsolicited banner on stdout is noise. +dotenv.config({ quiet: true }); // Object with custom validators and transformers, to avoid repetition // in the Config object @@ -58,9 +62,10 @@ const v = { (value) => !['false', 'undefined', 'null', 'NaN'].includes(value) || value === '', - (value) => ({ - message: `The string contains forbidden values, received '${value}'` - }) + { + error: (issue) => + `The string contains forbidden values, received '${issue.input}'` + } ) .transform((value) => (value !== '' ? value : undefined)), @@ -76,10 +81,7 @@ const v = { value ); }, - {}, - { - message: 'The string is an invalid path directory string.' - } + { error: 'The string is an invalid path directory string.' } ), // Allows positive numbers or no value in which case the returned value will @@ -91,9 +93,10 @@ const v = { .refine( (value) => value === '' || (!isNaN(parseFloat(value)) && parseFloat(value) > 0), - (value) => ({ - message: `The value must be numeric and positive, received '${value}'` - }) + { + error: (issue) => + `The value must be numeric and positive, received '${issue.input}'` + } ) .transform((value) => (value !== '' ? parseFloat(value) : undefined)), @@ -106,9 +109,10 @@ const v = { .refine( (value) => value === '' || (!isNaN(parseFloat(value)) && parseFloat(value) >= 0), - (value) => ({ - message: `The value must be numeric and non-negative, received '${value}'` - }) + { + error: (issue) => + `The value must be numeric and non-negative, received '${issue.input}'` + } ) .transform((value) => (value !== '' ? parseFloat(value) : undefined)) }; @@ -116,6 +120,7 @@ const v = { export const Config = z.object({ // puppeteer PUPPETEER_TEMP_DIR: v.path(), + PUPPETEER_LAUNCH_RETRY_WINDOW: v.nonNegativeNum(), // highcharts HIGHCHARTS_VERSION: z @@ -123,9 +128,10 @@ export const Config = z.object({ .trim() .refine( (value) => /^(latest|\d+(\.\d+){0,2})$/.test(value) || value === '', - (value) => ({ - message: `HIGHCHARTS_VERSION must be 'latest', a major version, or in the form XX.YY.ZZ, received '${value}'` - }) + { + error: (issue) => + `HIGHCHARTS_VERSION must be 'latest', a major version, or in the form XX.YY.ZZ, received '${issue.input}'` + } ) .transform((value) => (value !== '' ? value : undefined)), HIGHCHARTS_CDN_URL: z @@ -136,9 +142,10 @@ export const Config = z.object({ value.startsWith('https://') || value.startsWith('http://') || value === '', - (value) => ({ - message: `Invalid value for HIGHCHARTS_CDN_URL. It should start with http:// or https://, received '${value}'` - }) + { + error: (issue) => + `Invalid value for HIGHCHARTS_CDN_URL. It should start with http:// or https://, received '${issue.input}'` + } ) .transform((value) => (value !== '' ? value : undefined)), HIGHCHARTS_USE_NPM: v.boolean(), @@ -166,6 +173,7 @@ export const Config = z.object({ SERVER_HOST: v.string(), SERVER_PORT: v.positiveNum(), SERVER_MAX_UPLOAD_SIZE: v.positiveNum(), + SERVER_KEEP_ALIVE_TIMEOUT: v.nonNegativeNum(), SERVER_BENCHMARKING: v.boolean(), // server proxy @@ -179,7 +187,6 @@ export const Config = z.object({ SERVER_RATE_LIMITING_ENABLE: v.boolean(), SERVER_RATE_LIMITING_MAX_REQUESTS: v.nonNegativeNum(), SERVER_RATE_LIMITING_WINDOW: v.nonNegativeNum(), - SERVER_RATE_LIMITING_DELAY: v.nonNegativeNum(), SERVER_RATE_LIMITING_TRUST_PROXY: v.boolean(), SERVER_RATE_LIMITING_SKIP_KEY: v.string(), SERVER_RATE_LIMITING_SKIP_TOKEN: v.string(), @@ -194,6 +201,8 @@ export const Config = z.object({ POOL_MIN_WORKERS: v.nonNegativeNum(), POOL_MAX_WORKERS: v.nonNegativeNum(), POOL_WORK_LIMIT: v.positiveNum(), + POOL_QUEUE_LIMIT: v.nonNegativeNum(), + POOL_QUEUE_REJECT_DELAY: v.nonNegativeNum(), POOL_ACQUIRE_TIMEOUT: v.nonNegativeNum(), POOL_CREATE_TIMEOUT: v.nonNegativeNum(), POOL_DESTROY_TIMEOUT: v.nonNegativeNum(), @@ -212,9 +221,10 @@ export const Config = z.object({ (!isNaN(parseFloat(value)) && parseFloat(value) >= 0 && parseFloat(value) <= 5), - (value) => ({ - message: `Invalid value for LOGGING_LEVEL. We only accept values from 0 to 5 as logging levels, received '${value}'` - }) + { + error: (issue) => + `Invalid value for LOGGING_LEVEL. We only accept values from 0 to 5 as logging levels, received '${issue.input}'` + } ) .transform((value) => (value !== '' ? parseFloat(value) : undefined)), LOGGING_FILE: v.string(), @@ -227,6 +237,7 @@ export const Config = z.object({ UI_ROUTE: v.string(), // other + OTHER_SHUTDOWN_DRAIN_TIMEOUT: v.nonNegativeNum(), OTHER_NODE_ENV: v.enum(['development', 'production', 'test']), OTHER_LISTEN_TO_PROCESS_EXITS: v.boolean(), OTHER_NO_LOGO: v.boolean(), diff --git a/lib/errors/ExportError.js b/lib/errors/ExportError.js index be659551..7853c3b8 100644 --- a/lib/errors/ExportError.js +++ b/lib/errors/ExportError.js @@ -1,4 +1,9 @@ class ExportError extends Error { + // NOTE: Deliberately takes only a message. Two existing call sites in cache.js + // pass a number as a second argument, intending a status code, which + // this constructor has always ignored. Accepting a second parameter here + // would silently give those a meaning. Use setCode() instead, which is + // explicit. constructor(message) { super(); this.message = message; @@ -13,12 +18,24 @@ class ExportError extends Error { if (error.statusCode) { this.statusCode = error.statusCode; } + // NOTE: Carry a machine readable code up from the wrapped error. Errors are + // wrapped as they travel up the stack, and without this the reason a + // request failed would be lost at the first wrap, leaving only the + // message to tell a capacity problem from a bad request. + if (error.errorCode && !this.errorCode) { + this.errorCode = error.errorCode; + } if (error.stack) { this.stackMessage = error.message; this.stack = error.stack; } return this; } + + setCode(errorCode) { + this.errorCode = errorCode; + return this; + } } export default ExportError; diff --git a/lib/errors/HttpError.js b/lib/errors/HttpError.js index b995a4d2..01f89e7c 100644 --- a/lib/errors/HttpError.js +++ b/lib/errors/HttpError.js @@ -1,9 +1,13 @@ import ExportError from './ExportError.js'; class HttpError extends ExportError { - constructor(message, status) { + constructor(message, status, errorCode = false) { super(message); this.status = this.statusCode = status; + + if (errorCode) { + this.errorCode = errorCode; + } } setStatus(status) { diff --git a/lib/errors/codes.js b/lib/errors/codes.js new file mode 100644 index 00000000..aa62c8ca --- /dev/null +++ b/lib/errors/codes.js @@ -0,0 +1,55 @@ +/******************************************************************************* + +Highcharts Export Server + +Copyright (c) 2016-2024, Highsoft + +Licenced under the MIT licence. + +Additionally a valid Highcharts license is required for use. + +See LICENSE file in root for details. + +*******************************************************************************/ + +/** + * Machine readable error codes, returned as the `errorCode` property of an + * error response. + * + * These exist because the HTTP status code cannot carry the distinction that + * matters most in production: a request refused because the server was busy and + * a request refused because it was malformed are both reported as 400. Without a + * code, a dashboard cannot separate a capacity problem from callers sending bad + * data, and a client cannot tell whether retrying is worthwhile. + * + * Treat these as a stable contract - callers may branch on them. + */ +export const errorCodes = { + // The request itself was not usable: missing body, no chart data, or content + // that is not allowed. Retrying without changing the request will not help. + INVALID_REQUEST: 'EXPORT_INVALID_REQUEST', + + // The server was already holding as many queued exports as it is willing to, + // and refused this one without starting work on it. Retrying later, ideally + // with backoff, is appropriate. + QUEUE_FULL: 'EXPORT_QUEUE_FULL', + + // No worker became available within the acquire timeout. Same meaning for a + // caller as QUEUE_FULL, but reached by waiting rather than by being refused + // up front. + ACQUIRE_TIMEOUT: 'EXPORT_ACQUIRE_TIMEOUT', + + // The chart was too large or complex to render within the allotted time. + RASTERIZATION_TIMEOUT: 'EXPORT_RASTERIZATION_TIMEOUT', + + // The client disconnected before the export could be served, so the work was + // discarded. Never reaches a caller by definition - it exists so that + // abandoned work is distinguishable in logs and counters from work that + // genuinely failed. + CLIENT_GONE: 'EXPORT_CLIENT_GONE', + + // The export failed for a reason that is not one of the above. + EXPORT_FAILED: 'EXPORT_FAILED' +}; + +export default errorCodes; diff --git a/lib/export.js b/lib/export.js index ed14bd80..8aba206d 100644 --- a/lib/export.js +++ b/lib/export.js @@ -55,28 +55,44 @@ const getClipRegion = (page) => * @returns {Promise} Promise resolving to the image buffer or rejecting * with an ExportError for timeout. */ -const createImage = (page, type, encoding, clip, rasterizationTimeout) => - Promise.race([ - page.screenshot({ - type, - encoding, - clip, - captureBeyondViewport: true, - fullPage: false, - optimizeForSpeed: true, - ...(type !== 'png' ? { quality: 80 } : {}), - - // #447, #463 - always render on a transparent page if the expected type - // format is PNG - omitBackground: type == 'png' - }), - new Promise((_resolve, reject) => - setTimeout( - () => reject(new ExportError('Rasterization timeout')), - rasterizationTimeout || 1500 - ) - ) - ]); +const createImage = async ( + page, + type, + encoding, + clip, + rasterizationTimeout +) => { + let timer; + + try { + return await Promise.race([ + page.screenshot({ + type, + encoding, + clip, + captureBeyondViewport: true, + fullPage: false, + optimizeForSpeed: true, + ...(type !== 'png' ? { quality: 80 } : {}), + + // #447, #463 - always render on a transparent page if the expected type + // format is PNG + omitBackground: type == 'png' + }), + new Promise((_resolve, reject) => { + timer = setTimeout( + () => reject(new ExportError('Rasterization timeout')), + rasterizationTimeout || 1500 + ); + }) + ]); + } finally { + // The timer outlives the race otherwise, keeping itself and everything its + // callback closes over alive for the rest of the timeout on every successful + // export + clearTimeout(timer); + } +}; /** * Creates a PDF using Puppeteer's page pdf functionality with specified @@ -243,11 +259,9 @@ export default async (page, chart, options) => { // In case of SVG the zoom must be set directly for body // Set the zoom as scale - // eslint-disable-next-line no-undef document.body.style.zoom = scale; // Set the margin to 0px - // eslint-disable-next-line no-undef document.body.style.margin = '0px'; return { @@ -256,12 +270,10 @@ export default async (page, chart, options) => { }; }, parseFloat(exportOptions.scale)) : await page.evaluate(() => { - // eslint-disable-next-line no-undef const { chartHeight, chartWidth } = window.Highcharts.charts[0]; // No need for such scale manipulation in case of other types of exports // Reset the zoom for other exports than to SVGs - // eslint-disable-next-line no-undef document.body.style.zoom = 1; return { diff --git a/lib/pool.js b/lib/pool.js index 5b5f09e5..5a615995 100644 --- a/lib/pool.js +++ b/lib/pool.js @@ -19,13 +19,15 @@ import { create as createBrowser, close as closeBrowser, newPage, - clearPage + clearPage, + getGeneration as getBrowserGeneration } from './browser.js'; import puppeteerExport from './export.js'; import { log, logWithStack } from './logger.js'; import { measureTime } from './utils.js'; import ExportError from './errors/ExportError.js'; +import { errorCodes } from './errors/codes.js'; // The pool instance let pool = false; @@ -37,11 +39,67 @@ export const stats = { exportFromSvgAttempts: 0, timeSpent: 0, droppedExports: 0, - spentAverage: 0 + spentAverage: 0, + rejectedForCapacity: 0, + abandonedExports: 0 }; let poolConfig = {}; +// The resolved maximum number of exports allowed to wait for a worker +let queueLimit = 0; + +// Worker creations that have failed in a row, reset by any success. Used to stop +// the pool retrying against a browser that is not going to start working again. +let consecutiveCreateFailures = 0; + +/** + * Returns the number of worker creations that have failed in a row. + * + * @returns {number} The current consecutive failure count. + */ +export const getConsecutiveCreateFailures = () => consecutiveCreateFailures; + +/** + * Resolves the queue limit from the pool configuration. + * + * A limit of 0 means derive it from the pool size. Four times maxWorkers keeps + * the worst case wait at roughly four exports' worth of time, which is a + * meaningful bound, while leaving enough slack to absorb normal bursts. + * + * @param {Object} config - The pool section of the configuration. + * + * @returns {number} The resolved queue limit. + */ +const resolveQueueLimit = (config) => { + const configured = parseInt(config.queueLimit); + const maxWorkers = parseInt(config.maxWorkers); + + if (!isNaN(configured) && configured > 0) { + return configured; + } + + return (isNaN(maxWorkers) ? 8 : maxWorkers) * 4; +}; + +/** + * Returns the number of exports currently allowed to wait for a worker. + * + * @returns {number} The active queue limit. + */ +export const getQueueLimit = () => queueLimit; + +/** + * Returns how long to wait before answering a request refused because the queue + * was full. + * + * @returns {number} The delay in milliseconds. + */ +export const getQueueRejectDelay = () => { + const delay = parseInt(poolConfig.queueRejectDelay); + return isNaN(delay) || delay < 0 ? 0 : delay; +}; + const factory = { /** * Creates a new worker page for the export pool. @@ -65,6 +123,8 @@ const factory = { throw new ExportError('The page is invalid or closed.'); } + consecutiveCreateFailures = 0; + log( 3, `[pool] Successfully created a worker ${id} - took ${ @@ -72,6 +132,8 @@ const factory = { } ms.` ); } catch (error) { + ++consecutiveCreateFailures; + throw new ExportError( 'Error encountered when creating a new page.' ).setError(error); @@ -80,6 +142,9 @@ const factory = { return { id, page, + // Which browser this page belongs to, so that it can be recognised as + // stale if that browser goes away - see factory.validate + generation: getBrowserGeneration(), // Try to distribute the initial work count workCount: Math.round(Math.random() * (poolConfig.workLimit / 2)) }; @@ -112,6 +177,68 @@ const factory = { return false; } + // Set when an export left the page in a state it cannot be reused from - see + // the rasterization timeout handling in postWork + if (workerHandle.mustRecycle) { + log( + 3, + `[pool] Worker failed validation: its page was left unusable by a previous export.` + ); + return false; + } + + // NOTE: A page whose browser has gone away still reports + // isClosed() === false, so the check above cannot detect it and the + // pool would keep handing out pages belonging to a dead process, + // failing every export while continuing to report healthy workers. + // + // The generation is the reliable signal: it advances whenever the + // browser is lost, so any worker created against an earlier one is + // stale. Returning false here makes tarn destroy the worker and + // create a replacement, which relaunches the browser via newPage. + if (workerHandle.generation !== getBrowserGeneration()) { + log( + 3, + `[pool] Worker failed validation: it belongs to a browser that is gone (worker generation ${workerHandle.generation}, current ${getBrowserGeneration()}).` + ); + return false; + } + + // NOTE: Wait for the page clearing started when this worker was released + // to complete before handing it out again. + // + // Tarn's release() is synchronous: it invokes the 'release' event + // handlers and then returns the resource to the free list within the + // same tick, without awaiting anything. Clearing the page in that + // handler therefore overlaps the next export whenever an acquire is + // already waiting, which is exactly the case under saturation. + // + // In practice the visible damage is currently limited, because + // export.js already calls clearPageResources at the end of every + // export, which destroys the old charts and removes the injected + // tags. The innerHTML reset done here is a second pass. But that + // makes correctness depend on the ordering of two independent cleanup + // paths, and an innerHTML reset landing part way through the next + // render would wipe its container. + // + // Tarn does await validate, so this is the point where the clearing + // is guaranteed to have finished. Doing it here rather than in the + // handler keeps the work overlapped with the worker's idle time, and + // lets a page that could not be cleared recycle the worker instead of + // being exported onto. + if (workerHandle.cleanPromise) { + const cleared = await workerHandle.cleanPromise; + workerHandle.cleanPromise = null; + + if (!cleared) { + log( + 3, + `[pool] Worker failed validation: the page could not be cleared after its previous export.` + ); + return false; + } + } + if ( poolConfig.workLimit && ++workerHandle.workCount > poolConfig.workLimit @@ -153,6 +280,9 @@ export const initPool = async (config) => { // For the module scope usage poolConfig = config && config.pool ? { ...config.pool } : {}; + // Work out how deep the queue of waiting exports is allowed to get + queueLimit = resolveQueueLimit(poolConfig); + // Create a browser instance with the puppeteer arguments await createBrowser(config.puppeteerArgs); @@ -189,13 +319,17 @@ export const initPool = async (config) => { }); // Set events - pool.on('release', async (resource) => { - // Clear page - const r = await clearPage(resource.page, false); - log( - 4, - `[pool] Releasing a worker with ID ${resource.id}. Clear page status: ${r}.` + pool.on('release', (resource) => { + // Start clearing the page, but deliberately do not await it here - see + // the note in factory.validate, which is where the result is awaited + // before the worker can be handed out again. The catch is belt and braces: + // clearPage resolves false rather than rejecting, and nothing must be + // able to turn this into an unhandled rejection. + resource.cleanPromise = clearPage(resource.page, false).catch( + () => false ); + + log(4, `[pool] Releasing a worker with ID ${resource.id}.`); }); pool.on('destroySuccess', (eventId, resource) => { @@ -286,6 +420,52 @@ export const postWork = async (chart, options) => { throw new ExportError('Work received, but pool has not been started.'); } + // NOTE: Refuse the work rather than queue it when the queue is already at + // its limit. + // + // Throughput does not improve past the pool size - measured, it peaks + // at roughly the number of workers and then falls - so an unbounded + // queue cannot buy capacity. What it does buy is latency and memory: + // every waiting request holds its parsed body until it is served or + // times out, and a saturated server was observed accepting more than + // twenty times the work it could complete, queueing thousands of + // requests and then failing most of them on timeout. + // + // Failing here is cheap and immediate, and carries a distinct error + // code so that a caller and a dashboard can tell this apart from a + // malformed request. + if (pool.numPendingAcquires() >= queueLimit) { + ++stats.rejectedForCapacity; + + throw new ExportError( + (options.payload?.requestId + ? `For request with ID ${options.payload?.requestId} - ` + : '') + + `The server is at capacity: ${pool.numPendingAcquires()} exports are already waiting for a worker (limit is ${queueLimit}). Please retry shortly.` + ).setCode(errorCodes.QUEUE_FULL); + } + + // NOTE: Drop work whose client has already gone, before it takes a queue + // slot or a worker. + // + // Measured on the unbounded queue: 60 clients that each gave up after + // 150ms left 1367 exports still queued a second after the last client + // had disconnected, and the pool spent a further five seconds + // rendering charts nobody would receive. Under a retry storm that + // compounds - every retry adds work while the abandoned original is + // still being rendered - so capacity falls with each round. + const abortSignal = options.payload?.abortSignal; + + if (abortSignal?.aborted) { + ++stats.abandonedExports; + + throw new ExportError( + (options.payload?.requestId + ? `For request with ID ${options.payload?.requestId} - ` + : '') + 'The client disconnected before a worker was available.' + ).setCode(errorCodes.CLIENT_GONE); + } + // Acquire the worker along with the id of resource and work count const acquireCounter = measureTime(); try { @@ -308,10 +488,26 @@ export const postWork = async (chart, options) => { ? `For request with ID ${options.payload?.requestId} - ` : '') + `Error encountered when acquiring an available entry: ${acquireCounter()}ms.` - ).setError(error); + ) + .setCode(errorCodes.ACQUIRE_TIMEOUT) + .setError(error); } log(4, '[pool] Acquired a worker handle.'); + // The client may have gone while this request was queued. Hand the worker + // straight back rather than spending it on a result nobody will read. + if (abortSignal?.aborted) { + ++stats.abandonedExports; + pool.release(workerHandle); + workerHandle = null; + + throw new ExportError( + (options.payload?.requestId + ? `For request with ID ${options.payload?.requestId} - ` + : '') + 'The client disconnected while waiting for a worker.' + ).setCode(errorCodes.CLIENT_GONE); + } + if (!workerHandle.page) { throw new ExportError( 'Resolved worker page is invalid: the pool setup is wonky.' @@ -340,8 +536,18 @@ export const postWork = async (chart, options) => { // // ...yes, this is ugly. if (result.message === 'Rasterization timeout') { + // NOTE: This worker must not serve another export. Its page may still be + // waiting on the screenshot that timed out, so its state is unknown. + // + // Marking it rather than discarding the page reference is the point + // here. Setting page to null used to be how this was done, but + // factory.destroy only closes a page it can still see, so nulling + // the reference meant the page was never closed - and with a process + // per tab, every timeout leaked a renderer process. Under load that + // compounds: the leaked processes take CPU from the exports still + // running, which makes more of them time out. workerHandle.workCount = poolConfig.workLimit + 1; - workerHandle.page = null; + workerHandle.mustRecycle = true; } if ( @@ -350,13 +556,17 @@ export const postWork = async (chart, options) => { ) { throw new ExportError( 'Rasterization timeout: your chart may be too complex or large, and failed to render within the allotted time.' - ).setError(result); + ) + .setCode(errorCodes.RASTERIZATION_TIMEOUT) + .setError(result); } else { throw new ExportError( (options.payload?.requestId ? `For request with ID ${options.payload?.requestId} - ` : '') + `Error encountered during export: ${exportCounter()}ms.` - ).setError(result); + ) + .setCode(errorCodes.EXPORT_FAILED) + .setError(result); } } @@ -389,7 +599,13 @@ export const postWork = async (chart, options) => { options }; } catch (error) { - ++stats.droppedExports; + // NOTE: Work discarded because its client left is not a failure of the + // server, so it is counted separately rather than inflating + // droppedExports and depressing the success ratio that /health + // reports. It has its own counter, incremented where it is detected. + if (error.errorCode !== errorCodes.CLIENT_GONE) { + ++stats.droppedExports; + } if (workerHandle) { pool.release(workerHandle); @@ -421,7 +637,8 @@ export const getPoolInfoJSON = () => ({ all: pool.numFree() + pool.numUsed(), available: pool.numFree(), used: pool.numUsed(), - pending: pool.numPendingAcquires() + pending: pool.numPendingAcquires(), + queueLimit }); /** @@ -447,5 +664,7 @@ export default { getPool, getPoolInfo, getPoolInfoJSON, + getQueueLimit, + getConsecutiveCreateFailures, getStats: () => stats }; diff --git a/lib/resource_release.js b/lib/resource_release.js index 1d8e85ce..1003d811 100644 --- a/lib/resource_release.js +++ b/lib/resource_release.js @@ -12,28 +12,51 @@ See LICENSE file in root for details. *******************************************************************************/ +import { getOptions } from './config.js'; import { clearAllIntervals } from './intervals.js'; import { killPool } from './pool.js'; import { closeServers } from './server/server.js'; +/** + * Returns how long to let in-flight requests finish during a shutdown. + * + * @returns {number} The drain timeout in milliseconds. + */ +const getDrainTimeout = () => { + const configured = parseInt(getOptions()?.other?.shutdownDrainTimeout); + return isNaN(configured) || configured < 0 ? 30000 : configured; +}; + /** * Clean up function to trigger before ending process for the graceful shutdown. * * @param {number} exitCode - An exit code for the process.exit() function. */ export const shutdownCleanUp = async (exitCode) => { - // Await freeing all resources - await Promise.allSettled([ - // Clear all ongoing intervals - clearAllIntervals(), + // Stop the background intervals; nothing depends on their timing from here + clearAllIntervals(); - // Get available server instances (HTTP/HTTPS) and close them - closeServers(), + // NOTE: These have to happen in order, not together. Previously all three were + // started at once and closeServers() was not even awaitable - it is not + // async and returned undefined, so Promise.allSettled treated it as + // already done. The process therefore exited as soon as the pool had been + // destroyed, cutting off every export still being served. That happens on + // each restart, deploy and scale-in, so the dropped requests were not rare. + // + // Closing the servers first stops new work arriving and resolves once the + // requests in flight have been answered, which is the drain. It is raced + // against a timeout so that one request which never completes cannot hold + // the shutdown open indefinitely. + const drainTimeout = getDrainTimeout(); - // Close pool along with its workers and the browser instance, if exists - killPool() + await Promise.race([ + closeServers(), + new Promise((resolve) => setTimeout(resolve, drainTimeout)) ]); + // Only once nothing is being served is it safe to take the workers away + await killPool(); + // Exit process with a correct code process.exit(exitCode); }; diff --git a/lib/sanitize.js b/lib/sanitize.js index dd95095a..e9df8a68 100644 --- a/lib/sanitize.js +++ b/lib/sanitize.js @@ -21,6 +21,32 @@ import { JSDOM } from 'jsdom'; import DOMPurify from 'dompurify'; import { envs } from './envs.js'; + +// The purifier, built on first use and then reused. +// +// NOTE: Building a DOM and a purifier per call is by far the most expensive part +// of sanitizing, and this runs on every SVG export. It is also synchronous, +// so the cost is paid on the event loop and delays every other request in +// flight, not just this one. +// +// Only the instance is shared. The options stay per call, since FORBID_ATTR +// depends on configuration that can be read at any time, and DOMPurify +// applies the options it is given on each call. +let purifier; + +/** + * Returns the shared purifier, building it if this is the first call. + * + * @returns {Object} The DOMPurify instance. + */ +function getPurifier() { + if (!purifier) { + purifier = DOMPurify(new JSDOM('').window); + } + + return purifier; +} + /** * Sanitizes a given HTML string by removing